Commit 604031af authored by verwaest@chromium.org's avatar verwaest@chromium.org

Use LookupIterator for CompileLoadInterceptor and delete Object::Lookup

BUG=
R=jkummerow@chromium.org

Review URL: https://codereview.chromium.org/466283003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23168 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 109db3ca
......@@ -794,91 +794,76 @@ void NamedLoadHandlerCompiler::GenerateLoadCallback(
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg,
LookupResult* lookup,
Handle<Name> name) {
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
// later.
bool compile_followup_inline = false;
if (lookup->IsFound() && lookup->IsCacheable()) {
if (lookup->IsField()) {
compile_followup_inline = true;
} else if (lookup->type() == CALLBACKS &&
lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
compile_followup_inline =
callback->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), callback,
type());
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameAndConstantPoolScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
}
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ cmp(r0, scratch1());
__ b(eq, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
if (compile_followup_inline) {
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from
// the holder and it is needed should the interceptor return without any
// result. The CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *holder() != lookup->holder();
bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameAndConstantPoolScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ cmp(r0, scratch1());
__ b(eq, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Leave the internal frame.
__ bind(&interceptor_failed);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(holder_reg, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
GenerateLoadPostInterceptor(it, holder_reg);
}
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptor),
isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptor), isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
}
......
......@@ -756,91 +756,77 @@ void NamedLoadHandlerCompiler::GenerateLoadCallback(
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg,
LookupResult* lookup,
Handle<Name> name) {
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(!AreAliased(receiver(), this->name(),
scratch1(), scratch2(), scratch3()));
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added later.
bool compile_followup_inline = false;
if (lookup->IsFound() && lookup->IsCacheable()) {
if (lookup->IsField()) {
compile_followup_inline = true;
} else if (lookup->type() == CALLBACKS &&
lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
compile_followup_inline =
callback->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), callback,
type());
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
}
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ JumpIfRoot(x0, Heap::kNoInterceptorResultSentinelRootIndex,
&interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
if (compile_followup_inline) {
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from
// the holder and it is needed should the interceptor return without any
// result. The CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *holder() != lookup->holder();
bool must_preserve_receiver_reg = !receiver().Is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ JumpIfRoot(x0,
Heap::kNoInterceptorResultSentinelRootIndex,
&interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ Bind(&interceptor_failed);
if (must_preserve_receiver_reg) {
__ Pop(this->name(), holder_reg, receiver());
} else {
__ Pop(this->name(), holder_reg);
}
// Leave the internal frame.
__ Bind(&interceptor_failed);
if (must_preserve_receiver_reg) {
__ Pop(this->name(), holder_reg, receiver());
} else {
__ Pop(this->name(), holder_reg);
}
GenerateLoadPostInterceptor(holder_reg, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptor),
isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptor), isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
}
......
......@@ -779,102 +779,87 @@ void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg,
LookupResult* lookup,
Handle<Name> name) {
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
// later.
bool compile_followup_inline = false;
if (lookup->IsFound() && lookup->IsCacheable()) {
if (lookup->IsField()) {
compile_followup_inline = true;
} else if (lookup->type() == CALLBACKS &&
lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
compile_followup_inline =
callback->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), callback,
type());
}
}
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (compile_followup_inline) {
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from
// the holder and it is needed should the interceptor return without any
// result. The CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *holder() != lookup->holder();
bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ push(receiver());
}
__ push(holder_reg);
__ push(this->name());
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ cmp(eax, factory()->no_interceptor_result_sentinel());
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
// Clobber registers when generating debug-code to provoke errors.
__ bind(&interceptor_failed);
if (FLAG_debug_code) {
__ mov(receiver(), Immediate(BitCast<int32_t>(kZapValue)));
__ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue)));
__ mov(this->name(), Immediate(BitCast<int32_t>(kZapValue)));
}
if (must_preserve_receiver_reg) {
__ push(receiver());
}
__ push(holder_reg);
__ push(this->name());
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ cmp(eax, factory()->no_interceptor_result_sentinel());
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Clobber registers when generating debug-code to provoke errors.
__ bind(&interceptor_failed);
if (FLAG_debug_code) {
__ mov(receiver(), Immediate(BitCast<int32_t>(kZapValue)));
__ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue)));
__ mov(this->name(), Immediate(BitCast<int32_t>(kZapValue)));
}
// Leave the internal frame.
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
GenerateLoadPostInterceptor(holder_reg, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
__ pop(scratch2()); // save old return address
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
__ push(scratch2()); // restore old return address
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptor),
isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
// Call the runtime system to load the interceptor.
__ pop(scratch2()); // save old return address
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
__ push(scratch2()); // restore old return address
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptor), isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
}
......
......@@ -1034,7 +1034,12 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
DCHECK(!holder->GetNamedInterceptor()->getter()->IsUndefined());
NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
cache_holder);
return compiler.CompileLoadInterceptor(name);
// Perform a lookup behind the interceptor. Copy the LookupIterator since
// the original iterator will be used to fetch the value.
LookupIterator it(lookup);
it.Next();
LookupForRead(&it);
return compiler.CompileLoadInterceptor(&it, name);
}
DCHECK(lookup->state() == LookupIterator::PROPERTY);
......
......@@ -48,6 +48,18 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
DESCRIPTOR
};
explicit LookupIterator(const LookupIterator* other)
: configuration_(other->configuration_),
state_(other->state_),
property_kind_(other->property_kind_),
property_encoding_(other->property_encoding_),
property_details_(other->property_details_),
isolate_(other->isolate_),
name_(other->name_),
holder_map_(other->holder_map_),
maybe_receiver_(other->maybe_receiver_),
maybe_holder_(other->maybe_holder_) {}
LookupIterator(Handle<Object> receiver, Handle<Name> name,
Configuration configuration = CHECK_DERIVED)
: configuration_(ComputeConfiguration(configuration, name)),
......
......@@ -104,31 +104,6 @@ bool Object::IsCallable() const {
}
void Object::Lookup(Handle<Name> name, LookupResult* result) {
DisallowHeapAllocation no_gc;
Object* holder = NULL;
if (IsJSReceiver()) {
holder = this;
} else {
Context* native_context = result->isolate()->context()->native_context();
if (IsNumber()) {
holder = native_context->number_function()->instance_prototype();
} else if (IsString()) {
holder = native_context->string_function()->instance_prototype();
} else if (IsSymbol()) {
holder = native_context->symbol_function()->instance_prototype();
} else if (IsBoolean()) {
holder = native_context->boolean_function()->instance_prototype();
} else {
result->isolate()->PushStackTraceAndDie(
0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001);
}
}
DCHECK(holder != NULL); // Cannot handle null or undefined.
JSReceiver::cast(holder)->Lookup(name, result);
}
MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
......
......@@ -1473,8 +1473,6 @@ class Object {
static MUST_USE_RESULT inline MaybeHandle<Smi> ToSmi(Isolate* isolate,
Handle<Object> object);
void Lookup(Handle<Name> name, LookupResult* result);
MUST_USE_RESULT static MaybeHandle<Object> GetProperty(LookupIterator* it);
// Implementation of [[Put]], ECMA-262 5th edition, section 8.12.5.
......
......@@ -893,43 +893,62 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
Handle<Name> name) {
// Perform a lookup after the interceptor.
LookupResult lookup(isolate());
holder()->LookupOwnRealNamedProperty(name, &lookup);
if (!lookup.IsFound()) {
PrototypeIterator iter(holder()->GetIsolate(), holder());
if (!iter.IsAtEnd()) {
PrototypeIterator::GetCurrent(iter)->Lookup(name, &lookup);
LookupIterator* it, Handle<Name> name) {
// So far the most popular follow ups for interceptor loads are FIELD and
// ExecutableAccessorInfo, so inline only them. Other cases may be added
// later.
bool inline_followup = it->state() == LookupIterator::PROPERTY;
if (inline_followup) {
switch (it->property_kind()) {
case LookupIterator::DATA:
inline_followup = it->property_details().type() == FIELD;
break;
case LookupIterator::ACCESSOR: {
Handle<Object> accessors = it->GetAccessors();
inline_followup = accessors->IsExecutableAccessorInfo();
if (!inline_followup) break;
Handle<ExecutableAccessorInfo> info =
Handle<ExecutableAccessorInfo>::cast(accessors);
inline_followup = info->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(
isolate(), info, type());
}
}
}
Register reg = Frontend(receiver(), name);
// TODO(368): Compile in the whole chain: all the interceptors in
// prototypes and ultimate answer.
GenerateLoadInterceptor(reg, &lookup, name);
if (inline_followup) {
// TODO(368): Compile in the whole chain: all the interceptors in
// prototypes and ultimate answer.
GenerateLoadInterceptorWithFollowup(it, reg);
} else {
GenerateLoadInterceptor(reg);
}
return GetCode(kind(), Code::FAST, name);
}
void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
Register interceptor_reg, Handle<Name> name, LookupResult* lookup) {
Handle<JSObject> real_named_property_holder(lookup->holder());
LookupIterator* it, Register interceptor_reg) {
Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
set_type_for_object(holder());
set_holder(real_named_property_holder);
Register reg = Frontend(interceptor_reg, name);
if (lookup->IsField()) {
__ Move(receiver(), reg);
LoadFieldStub stub(isolate(), lookup->GetFieldIndex());
GenerateTailCall(masm(), stub.GetCode());
} else {
DCHECK(lookup->type() == CALLBACKS);
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
DCHECK(callback->getter() != NULL);
GenerateLoadCallback(reg, callback);
Register reg = Frontend(interceptor_reg, it->name());
switch (it->property_kind()) {
case LookupIterator::DATA: {
DCHECK_EQ(FIELD, it->property_details().type());
__ Move(receiver(), reg);
LoadFieldStub stub(isolate(), it->GetFieldIndex());
GenerateTailCall(masm(), stub.GetCode());
break;
}
case LookupIterator::ACCESSOR:
Handle<ExecutableAccessorInfo> info =
Handle<ExecutableAccessorInfo>::cast(it->GetAccessors());
DCHECK_NE(NULL, info->getter());
GenerateLoadCallback(reg, info);
}
}
......
......@@ -473,7 +473,10 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
Handle<Code> CompileLoadConstant(Handle<Name> name, int constant_index);
Handle<Code> CompileLoadInterceptor(Handle<Name> name);
// The LookupIterator is used to perform a lookup behind the interceptor. If
// the iterator points to a LookupIterator::PROPERTY, its access will be
// inlined.
Handle<Code> CompileLoadInterceptor(LookupIterator* it, Handle<Name> name);
Handle<Code> CompileLoadViaGetter(Handle<Name> name,
Handle<JSFunction> getter);
......@@ -523,12 +526,10 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
Handle<ExecutableAccessorInfo> callback);
void GenerateLoadCallback(const CallOptimization& call_optimization,
Handle<Map> receiver_map);
void GenerateLoadInterceptor(Register holder_reg,
LookupResult* lookup,
Handle<Name> name);
void GenerateLoadPostInterceptor(Register reg,
Handle<Name> name,
LookupResult* lookup);
void GenerateLoadInterceptor(Register holder_reg);
void GenerateLoadInterceptorWithFollowup(LookupIterator* it,
Register holder_reg);
void GenerateLoadPostInterceptor(LookupIterator* it, Register reg);
// Generates prototype loading code that uses the objects from the
// context we were in when this function was called. If the context
......
......@@ -727,94 +727,80 @@ void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg,
LookupResult* lookup,
Handle<Name> name) {
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
// later.
bool compile_followup_inline = false;
if (lookup->IsFound() && lookup->IsCacheable()) {
if (lookup->IsField()) {
compile_followup_inline = true;
} else if (lookup->type() == CALLBACKS &&
lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
compile_followup_inline =
callback->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), callback,
type());
}
}
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (compile_followup_inline) {
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from
// the holder and it is needed should the interceptor return without any
// result. The CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *holder() != lookup->holder();
bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver());
}
__ Push(holder_reg);
__ Push(this->name());
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
__ bind(&interceptor_failed);
__ Pop(this->name());
__ Pop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(receiver());
}
if (must_preserve_receiver_reg) {
__ Push(receiver());
}
__ Push(holder_reg);
__ Push(this->name());
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
IC::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
// Leave the internal frame.
__ bind(&interceptor_failed);
__ Pop(this->name());
__ Pop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(receiver());
}
GenerateLoadPostInterceptor(holder_reg, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
__ PopReturnAddressTo(scratch2());
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
__ PushReturnAddressFrom(scratch2());
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptor), isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
__ PopReturnAddressTo(scratch2());
PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
holder());
__ PushReturnAddressFrom(scratch2());
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptor), isolate());
__ TailCallExternalReference(
ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment