From 73529a7d14153bd57e4d555829cb72d3b4cff3f6 Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Thu, 30 Jan 2014 11:30:38 +0000 Subject: [PATCH] Support loads from primitive values. This also changes load computation to use HeapTypes rather than Maps. TODO: move conversion between maps and heaptypes earlier in the process, already in the oracle. BUG= R=dcarney@chromium.org Review URL: https://codereview.chromium.org/147763006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18938 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/accessors.cc | 25 ++-- src/accessors.h | 3 +- src/hydrogen-instructions.h | 4 + src/hydrogen.cc | 226 +++++++++++++++++++++++---------- src/hydrogen.h | 62 +++++---- src/ic.cc | 4 +- test/mjsunit/value-wrapper-accessor.js | 99 +++++++++++++++ 7 files changed, 319 insertions(+), 104 deletions(-) create mode 100644 test/mjsunit/value-wrapper-accessor.js diff --git a/src/accessors.cc b/src/accessors.cc index cd41564..25ee305 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -91,10 +91,19 @@ static V8_INLINE bool CheckForName(Handle name, } -bool Accessors::IsJSObjectFieldAccessor( - Handle map, Handle name, - int* object_offset) { - Isolate* isolate = map->GetIsolate(); +bool Accessors::IsJSObjectFieldAccessor(Handle type, + Handle name, + int* object_offset) { + Isolate* isolate = name->GetIsolate(); + + if (type->Is(HeapType::String())) { + return CheckForName(name, isolate->heap()->length_string(), + String::kLengthOffset, object_offset); + } + + if (!type->IsClass()) return false; + Handle map = type->AsClass(); + switch (map->instance_type()) { case JS_ARRAY_TYPE: return @@ -122,14 +131,8 @@ bool Accessors::IsJSObjectFieldAccessor( JSDataView::kByteOffsetOffset, object_offset) || CheckForName(name, isolate->heap()->buffer_string(), JSDataView::kBufferOffset, object_offset); - default: { - if (map->instance_type() < FIRST_NONSTRING_TYPE) { - return - CheckForName(name, isolate->heap()->length_string(), - String::kLengthOffset, object_offset); - } + default: return false; - } } } diff --git a/src/accessors.h b/src/accessors.h index 723abd2..dedde32 100644 --- a/src/accessors.h +++ b/src/accessors.h @@ -89,8 +89,7 @@ class Accessors : public AllStatic { // Returns true for properties that are accessors to object fields. // If true, *object_offset contains offset of object field. static bool IsJSObjectFieldAccessor( - Handle map, Handle name, - int* object_offset); + Handle map, Handle name, int* object_offset); private: diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 93e4908..345c6b9 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -4269,6 +4269,9 @@ class HIsStringAndBranch V8_FINAL : public HUnaryControlInstruction { DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch) + protected: + virtual int RedefinedOperandIndex() { return 0; } + private: HIsStringAndBranch(HValue* value, HBasicBlock* true_target = NULL, @@ -4291,6 +4294,7 @@ class HIsSmiAndBranch V8_FINAL : public HUnaryControlInstruction { protected: virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; } + virtual int RedefinedOperandIndex() { return 0; } private: HIsSmiAndBranch(HValue* value, diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 74262b9..3c7bf50 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -4924,10 +4924,13 @@ void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { } -static bool CanInlinePropertyAccess(Map* type) { - return type->IsJSObjectMap() && - !type->is_dictionary_map() && - !type->has_named_interceptor(); +static bool CanInlinePropertyAccess(Handle type) { + if (type->Is(HeapType::NumberOrString())) return true; + if (!type->IsClass()) return false; + Handle map = type->AsClass(); + return map->IsJSObjectMap() && + !map->is_dictionary_map() && + !map->has_named_interceptor(); } @@ -4936,8 +4939,8 @@ static void LookupInPrototypes(Handle map, LookupResult* lookup) { while (map->prototype()->IsJSObject()) { Handle holder(JSObject::cast(map->prototype())); - map = Handle(holder->map()); - if (!CanInlinePropertyAccess(*map)) break; + map = handle(holder->map()); + if (!CanInlinePropertyAccess(IC::MapToType(map))) break; map->LookupDescriptor(*holder, *name, lookup); if (lookup->IsFound()) return; } @@ -5438,7 +5441,7 @@ static bool ComputeStoreField(Handle type, LookupResult* lookup, bool lookup_transition = true) { ASSERT(!type->is_observed()); - if (!CanInlinePropertyAccess(*type)) { + if (!CanInlinePropertyAccess(IC::MapToType(type))) { lookup->NotFound(); return false; } @@ -5473,13 +5476,26 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic( bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad( PropertyAccessInfo* info) { - if (!CanInlinePropertyAccess(*map_)) return false; + if (!CanInlinePropertyAccess(type_)) return false; + + // Currently only handle HeapType::Number as a polymorphic case. + // TODO(verwaest): Support monomorphic handling of numbers with a HCheckNumber + // instruction. + if (type_->Is(HeapType::Number())) return false; + + // Values are only compatible for monomorphic load if they all behave the same + // regarding value wrappers. + if (type_->Is(HeapType::NumberOrString())) { + if (!info->type_->Is(HeapType::NumberOrString())) return false; + } else { + if (info->type_->Is(HeapType::NumberOrString())) return false; + } if (!LookupDescriptor()) return false; if (!lookup_.IsFound()) { return (!info->lookup_.IsFound() || info->has_holder()) && - map_->prototype() == info->map_->prototype(); + map()->prototype() == info->map()->prototype(); } // Mismatch if the other access info found the property in the prototype @@ -5507,8 +5523,9 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad( bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() { - map_->LookupDescriptor(NULL, *name_, &lookup_); - return LoadResult(map_); + if (!type_->IsClass()) return true; + map()->LookupDescriptor(NULL, *name_, &lookup_); + return LoadResult(map()); } @@ -5534,14 +5551,15 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle map) { bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() { - Handle map = map_; + Handle map = this->map(); + while (map->prototype()->IsJSObject()) { holder_ = handle(JSObject::cast(map->prototype())); if (holder_->map()->is_deprecated()) { JSObject::TryMigrateInstance(holder_); } map = Handle(holder_->map()); - if (!CanInlinePropertyAccess(*map)) { + if (!CanInlinePropertyAccess(IC::MapToType(map))) { lookup_.NotFound(); return false; } @@ -5554,7 +5572,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() { bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() { - if (!CanInlinePropertyAccess(*map_)) return IsStringLength(); + if (!CanInlinePropertyAccess(type_)) return false; if (IsJSObjectFieldAccessor()) return true; if (!LookupDescriptor()) return false; if (lookup_.IsFound()) return true; @@ -5564,19 +5582,12 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() { bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( SmallMapList* types) { - ASSERT(map_.is_identical_to(types->first())); + ASSERT(type_->Is(IC::MapToType(types->first()))); if (!CanLoadMonomorphic()) return false; if (types->length() > kMaxLoadPolymorphism) return false; - if (IsStringLength()) { - for (int i = 1; i < types->length(); ++i) { - if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false; - } - return true; - } - if (IsArrayLength()) { - bool is_fast = IsFastElementsKind(map_->elements_kind()); + bool is_fast = IsFastElementsKind(map()->elements_kind()); for (int i = 1; i < types->length(); ++i) { Handle test_map = types->at(i); if (test_map->instance_type() != JS_ARRAY_TYPE) return false; @@ -5587,16 +5598,25 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( return true; } - if (IsJSObjectFieldAccessor()) { - InstanceType instance_type = map_->instance_type(); + HObjectAccess access = HObjectAccess::ForMap(); // bogus default + if (GetJSObjectFieldAccess(&access)) { for (int i = 1; i < types->length(); ++i) { - if (types->at(i)->instance_type() != instance_type) return false; + PropertyAccessInfo test_info( + builder_, IC::MapToType(types->at(i)), name_); + HObjectAccess test_access = HObjectAccess::ForMap(); // bogus default + if (!test_info.GetJSObjectFieldAccess(&test_access)) return false; + if (!access.Equals(test_access)) return false; } return true; } + // Currently only handle HeapType::Number as a polymorphic case. + // TODO(verwaest): Support monomorphic handling of numbers with a HCheckNumber + // instruction. + if (type_->Is(HeapType::Number())) return false; + for (int i = 1; i < types->length(); ++i) { - PropertyAccessInfo test_info(isolate(), types->at(i), name_); + PropertyAccessInfo test_info(builder_, IC::MapToType(types->at(i)), name_); if (!test_info.IsCompatibleForLoad(this)) return false; } @@ -5604,10 +5624,17 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic( } +static bool NeedsWrappingFor(Handle type, Handle target) { + return type->Is(HeapType::NumberOrString()) && + target->shared()->is_classic_mode() && + !target->shared()->native(); +} + + HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic( PropertyAccessInfo* info, HValue* object, - HInstruction* checked_object, + HValue* checked_object, BailoutId ast_id, BailoutId return_id, bool can_inline_accessor) { @@ -5631,14 +5658,21 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic( } if (info->lookup()->IsPropertyCallbacks()) { - Push(checked_object); - if (FLAG_inline_accessors && - can_inline_accessor && - TryInlineGetter(info->accessor(), ast_id, return_id)) { - return NULL; + if (NeedsWrappingFor(info->type(), info->accessor())) { + return New(checked_object, info->name()); + // HValue* function = Add(info->accessor()); + // Add(checked_object); + // return New(function, 1, WRAP_AND_CALL); + } else { + Push(checked_object); + if (FLAG_inline_accessors && + can_inline_accessor && + TryInlineGetter(info->accessor(), ast_id, return_id)) { + return NULL; + } + Add(Pop()); + return BuildCallConstantFunction(info->accessor(), 1); } - Add(Pop()); - return BuildCallConstantFunction(info->accessor(), 1); } ASSERT(info->lookup()->IsConstant()); @@ -5655,36 +5689,88 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( // Something did not match; must use a polymorphic load. int count = 0; HBasicBlock* join = NULL; + HBasicBlock* number_block = NULL; + + bool handle_smi = false; for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { - PropertyAccessInfo info(isolate(), types->at(i), name); + PropertyAccessInfo info(this, IC::MapToType(types->at(i)), name); if (info.CanLoadMonomorphic()) { - if (count == 0) { - BuildCheckHeapObject(object); - join = graph()->CreateBasicBlock(); + count++; + if (info.type()->Is(HeapType::Number())) { + handle_smi = true; + break; } - ++count; - HBasicBlock* if_true = graph()->CreateBasicBlock(); - HBasicBlock* if_false = graph()->CreateBasicBlock(); - HCompareMap* compare = New( - object, info.map(), if_true, if_false); - FinishCurrentBlock(compare); + } + } - set_current_block(if_true); + count = 0; + bool handled_string = false; + HControlInstruction* smi_check = NULL; + + for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { + PropertyAccessInfo info(this, IC::MapToType(types->at(i)), name); + if (info.type()->Is(HeapType::String())) { + if (handled_string) continue; + handled_string = true; + } + if (!info.CanLoadMonomorphic()) continue; - HInstruction* load = BuildLoadMonomorphic( - &info, object, compare, ast_id, return_id, FLAG_polymorphic_inlining); - if (load == NULL) { - if (HasStackOverflow()) return; + if (count == 0) { + join = graph()->CreateBasicBlock(); + if (handle_smi) { + HBasicBlock* empty_smi_block = graph()->CreateBasicBlock(); + HBasicBlock* not_smi_block = graph()->CreateBasicBlock(); + number_block = graph()->CreateBasicBlock(); + smi_check = New( + object, empty_smi_block, not_smi_block); + FinishCurrentBlock(smi_check); + Goto(empty_smi_block, number_block); + set_current_block(not_smi_block); } else { - if (!load->IsLinked()) { - AddInstruction(load); - } - if (!ast_context()->IsEffect()) Push(load); + BuildCheckHeapObject(object); } + } + ++count; + HBasicBlock* if_true = graph()->CreateBasicBlock(); + HBasicBlock* if_false = graph()->CreateBasicBlock(); + HUnaryControlInstruction* compare; - if (current_block() != NULL) Goto(join); - set_current_block(if_false); + HValue* dependency; + if (info.type()->Is(HeapType::Number())) { + Handle heap_number_map = isolate()->factory()->heap_number_map(); + compare = New(object, heap_number_map, if_true, if_false); + dependency = smi_check; + } else if (info.type()->Is(HeapType::String())) { + compare = New(object, if_true, if_false); + dependency = compare; + } else { + compare = New(object, info.map(), if_true, if_false); + dependency = compare; } + FinishCurrentBlock(compare); + + if (info.type()->Is(HeapType::Number())) { + Goto(if_true, number_block); + if_true = number_block; + number_block->SetJoinId(ast_id); + } + + set_current_block(if_true); + + HInstruction* load = BuildLoadMonomorphic( + &info, object, dependency, ast_id, + return_id, FLAG_polymorphic_inlining); + if (load == NULL) { + if (HasStackOverflow()) return; + } else { + if (!load->IsLinked()) { + AddInstruction(load); + } + if (!ast_context()->IsEffect()) Push(load); + } + + if (current_block() != NULL) Goto(join); + set_current_block(if_false); } // Finish up. Unconditionally deoptimize if we've handled all the maps we @@ -5868,7 +5954,7 @@ static bool ComputeReceiverTypes(Expression* expr, types->FilterForPossibleTransitions(root_map); monomorphic = types->length() == 1; } - return monomorphic && CanInlinePropertyAccess(*types->first()); + return monomorphic && CanInlinePropertyAccess(IC::MapToType(types->first())); } @@ -5915,15 +6001,21 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr, Handle setter; Handle holder; if (LookupSetter(map, name, &setter, &holder)) { - AddCheckConstantFunction(holder, object, map); - if (FLAG_inline_accessors && - TryInlineSetter(setter, ast_id, return_id, value)) { + AddCheckMap(object, map); + AddCheckPrototypeMaps(holder, map); + bool needs_wrapping = NeedsWrappingFor(IC::MapToType(map), setter); + bool try_inline = FLAG_inline_accessors && !needs_wrapping; + if (try_inline && TryInlineSetter(setter, ast_id, return_id, value)) { return; } Drop(2); - Add(object); - Add(value); - instr = BuildCallConstantFunction(setter, 2); + if (needs_wrapping) { + instr = BuildStoreNamedGeneric(object, name, value); + } else { + Add(object); + Add(value); + instr = BuildCallConstantFunction(setter, 2); + } } else { Drop(2); CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object, @@ -6776,14 +6868,16 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr, ASSERT(types != NULL); if (types->length() > 0) { - PropertyAccessInfo info(isolate(), types->first(), name); + PropertyAccessInfo info(this, IC::MapToType(types->first()), name); if (!info.CanLoadAsMonomorphic(types)) { return HandlePolymorphicLoadNamedField( ast_id, expr->LoadId(), object, types, name); } + HValue* checked_object; + // HeapType::Number() is only supported by polymorphic load/call handling. + ASSERT(!info.type()->Is(HeapType::Number())); BuildCheckHeapObject(object); - HInstruction* checked_object; if (AreStringTypes(types)) { checked_object = Add(object, HCheckInstanceType::IS_STRING); @@ -7010,7 +7104,7 @@ bool HOptimizedGraphBuilder::TryCallPolymorphicAsMonomorphic( Handle name) { if (types->length() > kMaxCallPolymorphism) return false; - PropertyAccessInfo info(isolate(), types->at(0), name); + PropertyAccessInfo info(this, IC::MapToType(types->at(0)), name); if (!info.CanLoadAsMonomorphic(types)) return false; if (!expr->ComputeTarget(info.map(), name)) return false; diff --git a/src/hydrogen.h b/src/hydrogen.h index 55e7274..a9c0d32 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2256,9 +2256,12 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { class PropertyAccessInfo { public: - PropertyAccessInfo(Isolate* isolate, Handle map, Handle name) - : lookup_(isolate), - map_(map), + PropertyAccessInfo(HOptimizedGraphBuilder* builder, + Handle type, + Handle name) + : lookup_(builder->isolate()), + builder_(builder), + type_(type), name_(name), access_(HObjectAccess::ForMap()) { } @@ -2275,32 +2278,48 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { // PropertyAccessInfo is built for types->first(). bool CanLoadAsMonomorphic(SmallMapList* types); + Handle map() { + if (type_->Is(HeapType::Number())) { + Context* context = current_info()->closure()->context(); + context = context->native_context(); + return handle(context->number_function()->initial_map()); + } else if (type_->Is(HeapType::String())) { + Context* context = current_info()->closure()->context(); + context = context->native_context(); + return handle(context->string_function()->initial_map()); + } else { + return type_->AsClass(); + } + } + Handle type() const { return type_; } + Handle name() const { return name_; } + bool IsJSObjectFieldAccessor() { int offset; // unused - return Accessors::IsJSObjectFieldAccessor(map_, name_, &offset); + return Accessors::IsJSObjectFieldAccessor(type_, name_, &offset); } bool GetJSObjectFieldAccess(HObjectAccess* access) { - if (IsStringLength()) { - *access = HObjectAccess::ForStringLength(); - return true; - } else if (IsArrayLength()) { - *access = HObjectAccess::ForArrayLength(map_->elements_kind()); + if (IsArrayLength()) { + *access = HObjectAccess::ForArrayLength(map()->elements_kind()); return true; - } else { - int offset; - if (Accessors::IsJSObjectFieldAccessor(map_, name_, &offset)) { + } + int offset; + if (Accessors::IsJSObjectFieldAccessor(type_, name_, &offset)) { + if (type_->Is(HeapType::String())) { + ASSERT(name_->Equals(isolate()->heap()->length_string())); + *access = HObjectAccess::ForStringLength(); + } else { *access = HObjectAccess::ForJSObjectOffset(offset); - return true; } - return false; + return true; } + return false; } bool has_holder() { return !holder_.is_null(); } LookupResult* lookup() { return &lookup_; } - Handle map() { return map_; } Handle holder() { return holder_; } Handle accessor() { return accessor_; } Handle constant() { return constant_; } @@ -2308,14 +2327,10 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { private: Isolate* isolate() { return lookup_.isolate(); } - - bool IsStringLength() { - return map_->instance_type() < FIRST_NONSTRING_TYPE && - name_->Equals(isolate()->heap()->length_string()); - } + CompilationInfo* current_info() { return builder_->current_info(); } bool IsArrayLength() { - return map_->instance_type() == JS_ARRAY_TYPE && + return map()->instance_type() == JS_ARRAY_TYPE && name_->Equals(isolate()->heap()->length_string()); } @@ -2330,7 +2345,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { } LookupResult lookup_; - Handle map_; + HOptimizedGraphBuilder* builder_; + Handle type_; Handle name_; Handle holder_; Handle accessor_; @@ -2340,7 +2356,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { HInstruction* BuildLoadMonomorphic(PropertyAccessInfo* info, HValue* object, - HInstruction* checked_object, + HValue* checked_object, BailoutId ast_id, BailoutId return_id, bool can_inline_accessor = true); diff --git a/src/ic.cc b/src/ic.cc index b465757..fd86f1e 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1208,9 +1208,9 @@ Handle LoadIC::CompileHandler(LookupResult* lookup, // Use simple field loads for some well-known callback properties. if (object->IsJSObject()) { Handle receiver = Handle::cast(object); - Handle map(receiver->map()); + Handle type = IC::MapToType(handle(receiver->map())); int object_offset; - if (Accessors::IsJSObjectFieldAccessor(map, name, &object_offset)) { + if (Accessors::IsJSObjectFieldAccessor(type, name, &object_offset)) { return SimpleFieldLoad(object_offset / kPointerSize); } } diff --git a/test/mjsunit/value-wrapper-accessor.js b/test/mjsunit/value-wrapper-accessor.js new file mode 100644 index 0000000..2a51fee --- /dev/null +++ b/test/mjsunit/value-wrapper-accessor.js @@ -0,0 +1,99 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// When calling user-defined accessors on strings, booleans or +// numbers, we should create a wrapper object in classic-mode. + +// Flags: --allow-natives-syntax + +function test(object, prototype) { + var result; + Object.defineProperty(prototype, "nonstrict", { + get: function() { result = this; }, + set: function(v) { result = this; } + }); + Object.defineProperty(prototype, "strict", { + get: function() { "use strict"; result = this; }, + set: function(v) { "use strict"; result = this; } + }); + + (function() { + function nonstrict(s) { + return s.nonstrict; + } + function strict(s) { + return s.strict; + } + + nonstrict(object); + nonstrict(object); + %OptimizeFunctionOnNextCall(nonstrict); + result = undefined; + nonstrict(object); + assertEquals("object", typeof result); + + strict(object); + strict(object); + %OptimizeFunctionOnNextCall(strict); + result = undefined; + strict(object); + assertEquals(typeof object, typeof result); + })(); + + (function() { + function nonstrict(s) { + return s.nonstrict = 10; + } + function strict(s) { + return s.strict = 10; + } + + nonstrict(object); + nonstrict(object); + %OptimizeFunctionOnNextCall(nonstrict); + result = undefined; + nonstrict(object); + // TODO(1475): Support storing to primitive values. + // This should return "object" once storing to primitive values is + // supported. + assertEquals("undefined", typeof result); + + strict(object); + strict(object); + %OptimizeFunctionOnNextCall(strict); + result = undefined; + strict(object); + // TODO(1475): Support storing to primitive values. + // This should return "object" once storing to primitive values is + // supported. + assertEquals("undefined", typeof result); + })(); +} + +test(1, Number.prototype); +test("string", String.prototype); +test(true, Boolean.prototype); -- 2.7.4