| Index: src/x64/stub-cache-x64.cc
|
| ===================================================================
|
| --- src/x64/stub-cache-x64.cc (revision 4738)
|
| +++ src/x64/stub-cache-x64.cc (working copy)
|
| @@ -375,189 +375,6 @@
|
| }
|
|
|
|
|
| -template <class Compiler>
|
| -static void CompileLoadInterceptor(Compiler* compiler,
|
| - StubCompiler* stub_compiler,
|
| - MacroAssembler* masm,
|
| - JSObject* object,
|
| - JSObject* holder,
|
| - String* name,
|
| - LookupResult* lookup,
|
| - Register receiver,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Label* miss) {
|
| - ASSERT(holder->HasNamedInterceptor());
|
| - ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
|
| -
|
| - // Check that the receiver isn't a smi.
|
| - __ JumpIfSmi(receiver, miss);
|
| -
|
| - // Check that the maps haven't changed.
|
| - Register reg =
|
| - stub_compiler->CheckPrototypes(object, receiver, holder,
|
| - scratch1, scratch2, name, miss);
|
| -
|
| - if (lookup->IsProperty() && lookup->IsCacheable()) {
|
| - compiler->CompileCacheable(masm,
|
| - stub_compiler,
|
| - receiver,
|
| - reg,
|
| - scratch1,
|
| - scratch2,
|
| - holder,
|
| - lookup,
|
| - name,
|
| - miss);
|
| - } else {
|
| - compiler->CompileRegular(masm,
|
| - receiver,
|
| - reg,
|
| - scratch2,
|
| - holder,
|
| - miss);
|
| - }
|
| -}
|
| -
|
| -
|
| -class LoadInterceptorCompiler BASE_EMBEDDED {
|
| - public:
|
| - explicit LoadInterceptorCompiler(Register name) : name_(name) {}
|
| -
|
| - void CompileCacheable(MacroAssembler* masm,
|
| - StubCompiler* stub_compiler,
|
| - Register receiver,
|
| - Register holder,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - JSObject* interceptor_holder,
|
| - LookupResult* lookup,
|
| - String* name,
|
| - Label* miss_label) {
|
| - AccessorInfo* callback = NULL;
|
| - bool optimize = false;
|
| - // So far the most popular follow ups for interceptor loads are FIELD
|
| - // and CALLBACKS, so inline only them, other cases may be added
|
| - // later.
|
| - if (lookup->type() == FIELD) {
|
| - optimize = true;
|
| - } else if (lookup->type() == CALLBACKS) {
|
| - Object* callback_object = lookup->GetCallbackObject();
|
| - if (callback_object->IsAccessorInfo()) {
|
| - callback = AccessorInfo::cast(callback_object);
|
| - optimize = callback->getter() != NULL;
|
| - }
|
| - }
|
| -
|
| - if (!optimize) {
|
| - CompileRegular(masm, receiver, holder, scratch2, interceptor_holder,
|
| - miss_label);
|
| - return;
|
| - }
|
| -
|
| - // Save necessary data before invoking an interceptor.
|
| - // Requires a frame to make GC aware of pushed pointers.
|
| - __ EnterInternalFrame();
|
| -
|
| - if (lookup->type() == CALLBACKS && !receiver.is(holder)) {
|
| - // CALLBACKS case needs a receiver to be passed into C++ callback.
|
| - __ push(receiver);
|
| - }
|
| - __ push(holder);
|
| - __ push(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,
|
| - name_,
|
| - interceptor_holder);
|
| -
|
| - // 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);
|
| - __ LeaveInternalFrame();
|
| - __ ret(0);
|
| -
|
| - __ bind(&interceptor_failed);
|
| - __ pop(name_);
|
| - __ pop(holder);
|
| - if (lookup->type() == CALLBACKS && !receiver.is(holder)) {
|
| - __ pop(receiver);
|
| - }
|
| -
|
| - __ LeaveInternalFrame();
|
| -
|
| - // Check that the maps from interceptor's holder to lookup's holder
|
| - // haven't changed. And load lookup's holder into |holder| register.
|
| - if (interceptor_holder != lookup->holder()) {
|
| - holder = stub_compiler->CheckPrototypes(interceptor_holder, holder,
|
| - lookup->holder(), scratch1,
|
| - scratch2,
|
| - name,
|
| - miss_label);
|
| - }
|
| -
|
| - if (lookup->type() == FIELD) {
|
| - // We found FIELD property in prototype chain of interceptor's holder.
|
| - // Retrieve a field from field's holder.
|
| - stub_compiler->GenerateFastPropertyLoad(masm,
|
| - rax,
|
| - holder,
|
| - lookup->holder(),
|
| - lookup->GetFieldIndex());
|
| - __ ret(0);
|
| - } else {
|
| - // We found CALLBACKS property in prototype chain of interceptor's
|
| - // holder.
|
| - ASSERT(lookup->type() == CALLBACKS);
|
| - ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
|
| - ASSERT(callback != NULL);
|
| - ASSERT(callback->getter() != NULL);
|
| -
|
| - // Tail call to runtime.
|
| - // Important invariant in CALLBACKS case: the code above must be
|
| - // structured to never clobber |receiver| register.
|
| - __ pop(scratch2); // return address
|
| - __ push(receiver);
|
| - __ push(holder);
|
| - __ Move(holder, Handle<AccessorInfo>(callback));
|
| - __ push(holder);
|
| - __ push(FieldOperand(holder, AccessorInfo::kDataOffset));
|
| - __ push(name_);
|
| - __ push(scratch2); // restore return address
|
| -
|
| - ExternalReference ref =
|
| - ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
|
| - __ TailCallExternalReference(ref, 5, 1);
|
| - }
|
| - }
|
| -
|
| -
|
| - void CompileRegular(MacroAssembler* masm,
|
| - Register receiver,
|
| - Register holder,
|
| - Register scratch,
|
| - JSObject* interceptor_holder,
|
| - Label* miss_label) {
|
| - __ pop(scratch); // save old return address
|
| - PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
|
| - __ push(scratch); // restore old return address
|
| -
|
| - ExternalReference ref = ExternalReference(
|
| - IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
|
| - __ TailCallExternalReference(ref, 5, 1);
|
| - }
|
| -
|
| - private:
|
| - Register name_;
|
| -};
|
| -
|
| -
|
| // Reserves space for the extra arguments to FastHandleApiCall in the
|
| // caller's frame.
|
| //
|
| @@ -2125,9 +1942,8 @@
|
| }
|
|
|
|
|
| -
|
| void StubCompiler::GenerateLoadInterceptor(JSObject* object,
|
| - JSObject* holder,
|
| + JSObject* interceptor_holder,
|
| LookupResult* lookup,
|
| Register receiver,
|
| Register name_reg,
|
| @@ -2135,18 +1951,128 @@
|
| Register scratch2,
|
| String* name,
|
| Label* miss) {
|
| - LoadInterceptorCompiler compiler(name_reg);
|
| - CompileLoadInterceptor(&compiler,
|
| - this,
|
| - masm(),
|
| - object,
|
| - holder,
|
| - name,
|
| - lookup,
|
| - receiver,
|
| - scratch1,
|
| - scratch2,
|
| - miss);
|
| + ASSERT(interceptor_holder->HasNamedInterceptor());
|
| + ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
|
| +
|
| + // Check that the receiver isn't a smi.
|
| + __ JumpIfSmi(receiver, miss);
|
| +
|
| + // 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->IsProperty() && lookup->IsCacheable()) {
|
| + if (lookup->type() == FIELD) {
|
| + compile_followup_inline = true;
|
| + } else if (lookup->type() == CALLBACKS &&
|
| + lookup->GetCallbackObject()->IsAccessorInfo() &&
|
| + AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) {
|
| + compile_followup_inline = true;
|
| + }
|
| + }
|
| +
|
| + 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.
|
| + Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
|
| + scratch1, scratch2, name, miss);
|
| + ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
|
| +
|
| + // Save necessary data before invoking an interceptor.
|
| + // Requires a frame to make GC aware of pushed pointers.
|
| + __ EnterInternalFrame();
|
| +
|
| + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
|
| + // CALLBACKS case needs a receiver to be passed into C++ callback.
|
| + __ push(receiver);
|
| + }
|
| + __ push(holder_reg);
|
| + __ push(name_reg);
|
| +
|
| + // 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,
|
| + name_reg,
|
| + interceptor_holder);
|
| +
|
| + // 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);
|
| + __ LeaveInternalFrame();
|
| + __ ret(0);
|
| +
|
| + __ bind(&interceptor_failed);
|
| + __ pop(name_reg);
|
| + __ pop(holder_reg);
|
| + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
|
| + __ pop(receiver);
|
| + }
|
| +
|
| + __ LeaveInternalFrame();
|
| +
|
| + // Check that the maps from interceptor's holder to lookup's holder
|
| + // haven't changed. And load lookup's holder into |holder| register.
|
| + if (interceptor_holder != lookup->holder()) {
|
| + holder_reg = CheckPrototypes(interceptor_holder,
|
| + holder_reg,
|
| + lookup->holder(),
|
| + scratch1,
|
| + scratch2,
|
| + name,
|
| + miss);
|
| + }
|
| +
|
| + if (lookup->type() == FIELD) {
|
| + // We found FIELD property in prototype chain of interceptor's holder.
|
| + // Retrieve a field from field's holder.
|
| + GenerateFastPropertyLoad(masm(), rax, holder_reg,
|
| + lookup->holder(), lookup->GetFieldIndex());
|
| + __ ret(0);
|
| + } else {
|
| + // We found CALLBACKS property in prototype chain of interceptor's
|
| + // holder.
|
| + ASSERT(lookup->type() == CALLBACKS);
|
| + ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
|
| + AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
|
| + ASSERT(callback != NULL);
|
| + ASSERT(callback->getter() != NULL);
|
| +
|
| + // Tail call to runtime.
|
| + // Important invariant in CALLBACKS case: the code above must be
|
| + // structured to never clobber |receiver| register.
|
| + __ pop(scratch2); // return address
|
| + __ push(receiver);
|
| + __ push(holder_reg);
|
| + __ Move(holder_reg, Handle<AccessorInfo>(callback));
|
| + __ push(holder_reg);
|
| + __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset));
|
| + __ push(name_reg);
|
| + __ push(scratch2); // restore return address
|
| +
|
| + ExternalReference ref =
|
| + ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
|
| + __ TailCallExternalReference(ref, 5, 1);
|
| + }
|
| + } else { // !compile_followup_inline
|
| + // Call the runtime system to load the interceptor.
|
| + // Check that the maps haven't changed.
|
| + Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
|
| + scratch1, scratch2, name, miss);
|
| + __ pop(scratch2); // save old return address
|
| + PushInterceptorArguments(masm(), receiver, holder_reg,
|
| + name_reg, interceptor_holder);
|
| + __ push(scratch2); // restore old return address
|
| +
|
| + ExternalReference ref = ExternalReference(
|
| + IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
|
| + __ TailCallExternalReference(ref, 5, 1);
|
| + }
|
| }
|
|
|
|
|
|
|