ia32: Fuse map and type checks in call ICs for API functions.
This uses the fact that if a map stayed the same then the object still passes the type check. A new builtin is added to handle the API call in this case. Review URL: http://codereview.chromium.org/573003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3825 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ad83e411e1
commit
d1d56d9891
@ -737,7 +737,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
|
||||
Register holder_reg,
|
||||
Register scratch,
|
||||
String* name,
|
||||
int save_at_depth,
|
||||
Label* miss) {
|
||||
// TODO(602): support object saving.
|
||||
ASSERT(save_at_depth == kInvalidProtoDepth);
|
||||
|
||||
// Check that the maps haven't changed.
|
||||
Register result =
|
||||
masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
|
||||
|
@ -474,6 +474,76 @@ BUILTIN(HandleApiCallConstruct) {
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
static void VerifyTypeCheck(Handle<JSObject> object,
|
||||
Handle<JSFunction> function) {
|
||||
FunctionTemplateInfo* info =
|
||||
FunctionTemplateInfo::cast(function->shared()->function_data());
|
||||
if (info->signature()->IsUndefined()) return;
|
||||
SignatureInfo* signature = SignatureInfo::cast(info->signature());
|
||||
Object* receiver_type = signature->receiver();
|
||||
if (receiver_type->IsUndefined()) return;
|
||||
FunctionTemplateInfo* type = FunctionTemplateInfo::cast(receiver_type);
|
||||
ASSERT(object->IsInstanceOf(type));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
BUILTIN(FastHandleApiCall) {
|
||||
ASSERT(!CalledAsConstructor());
|
||||
const bool is_construct = false;
|
||||
|
||||
// We expect four more arguments: function, callback, call data, and holder.
|
||||
const int args_length = args.length() - 4;
|
||||
ASSERT(args_length >= 0);
|
||||
|
||||
Handle<JSFunction> function = args.at<JSFunction>(args_length);
|
||||
Object* callback_obj = args[args_length + 1];
|
||||
Handle<Object> data_handle = args.at<Object>(args_length + 2);
|
||||
Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3);
|
||||
|
||||
#ifdef DEBUG
|
||||
VerifyTypeCheck(checked_holder, function);
|
||||
#endif
|
||||
|
||||
v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder);
|
||||
v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
|
||||
v8::InvocationCallback callback =
|
||||
v8::ToCData<v8::InvocationCallback>(callback_obj);
|
||||
v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
|
||||
|
||||
v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
|
||||
data,
|
||||
holder,
|
||||
callee,
|
||||
is_construct,
|
||||
reinterpret_cast<void**>(&args[0] - 1),
|
||||
args_length - 1);
|
||||
|
||||
HandleScope scope;
|
||||
Object* result;
|
||||
v8::Handle<v8::Value> value;
|
||||
{
|
||||
// Leaving JavaScript.
|
||||
VMState state(EXTERNAL);
|
||||
#ifdef ENABLE_LOGGING_AND_PROFILING
|
||||
state.set_external_callback(v8::ToCData<Address>(callback_obj));
|
||||
#endif
|
||||
value = callback(new_args);
|
||||
}
|
||||
if (value.IsEmpty()) {
|
||||
result = Heap::undefined_value();
|
||||
} else {
|
||||
result = *reinterpret_cast<Object**>(*value);
|
||||
}
|
||||
|
||||
RETURN_IF_SCHEDULED_EXCEPTION();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Helper function to handle calls to non-function objects created through the
|
||||
// API. The object can be called as either a constructor (using new) or just as
|
||||
// a function (without new).
|
||||
|
@ -50,6 +50,7 @@ enum BuiltinExtraArguments {
|
||||
V(ArrayPop, NO_EXTRA_ARGUMENTS) \
|
||||
\
|
||||
V(HandleApiCall, NEEDS_CALLED_FUNCTION) \
|
||||
V(FastHandleApiCall, NO_EXTRA_ARGUMENTS) \
|
||||
V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION) \
|
||||
V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \
|
||||
V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS)
|
||||
|
@ -554,6 +554,7 @@ void MacroAssembler::PopTryHandler() {
|
||||
Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
|
||||
JSObject* holder, Register holder_reg,
|
||||
Register scratch,
|
||||
int save_at_depth,
|
||||
Label* miss) {
|
||||
// Make sure there's no overlap between scratch and the other
|
||||
// registers.
|
||||
@ -561,7 +562,11 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
|
||||
|
||||
// Keep track of the current object in register reg.
|
||||
Register reg = object_reg;
|
||||
int depth = 1;
|
||||
int depth = 0;
|
||||
|
||||
if (save_at_depth == depth) {
|
||||
mov(Operand(esp, kPointerSize), object_reg);
|
||||
}
|
||||
|
||||
// Check the maps in the prototype chain.
|
||||
// Traverse the prototype chain from the object and do map checks.
|
||||
@ -593,7 +598,6 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
|
||||
// to it in the code. Load it from the map.
|
||||
reg = holder_reg; // from now the object is in holder_reg
|
||||
mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
|
||||
|
||||
} else {
|
||||
// Check the map of the current object.
|
||||
cmp(FieldOperand(reg, HeapObject::kMapOffset),
|
||||
@ -611,6 +615,10 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
|
||||
mov(reg, Handle<JSObject>(prototype));
|
||||
}
|
||||
|
||||
if (save_at_depth == depth) {
|
||||
mov(Operand(esp, kPointerSize), reg);
|
||||
}
|
||||
|
||||
// Go to the next object in the prototype chain.
|
||||
object = prototype;
|
||||
}
|
||||
@ -621,7 +629,7 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
|
||||
j(not_equal, miss, not_taken);
|
||||
|
||||
// Log the check depth.
|
||||
LOG(IntEvent("check-maps-depth", depth));
|
||||
LOG(IntEvent("check-maps-depth", depth + 1));
|
||||
|
||||
// Perform security check for access to the global object and return
|
||||
// the holder register.
|
||||
|
@ -194,9 +194,14 @@ class MacroAssembler: public Assembler {
|
||||
// clobbered if it the same as the holder register. The function
|
||||
// returns a register containing the holder - either object_reg or
|
||||
// holder_reg.
|
||||
// The function can optionally (when save_at_depth !=
|
||||
// kInvalidProtoDepth) save the object at the given depth by moving
|
||||
// it to [esp + kPointerSize].
|
||||
Register CheckMaps(JSObject* object, Register object_reg,
|
||||
JSObject* holder, Register holder_reg,
|
||||
Register scratch, Label* miss);
|
||||
Register scratch,
|
||||
int save_at_depth,
|
||||
Label* miss);
|
||||
|
||||
// Generate code for checking access rights - used for security checks
|
||||
// on access to global objects across environments. The holder register
|
||||
|
@ -481,88 +481,353 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
|
||||
};
|
||||
|
||||
|
||||
// Holds information about possible function call optimizations.
|
||||
class CallOptimization BASE_EMBEDDED {
|
||||
public:
|
||||
explicit CallOptimization(LookupResult* lookup)
|
||||
: constant_function_(NULL),
|
||||
is_simple_api_call_(false),
|
||||
expected_receiver_type_(NULL),
|
||||
api_call_info_(NULL) {
|
||||
if (!lookup->IsValid() || !lookup->IsCacheable()) return;
|
||||
|
||||
// We only optimize constant function calls.
|
||||
if (lookup->type() != CONSTANT_FUNCTION) return;
|
||||
|
||||
Initialize(lookup->GetConstantFunction());
|
||||
}
|
||||
|
||||
explicit CallOptimization(JSFunction* function) {
|
||||
Initialize(function);
|
||||
}
|
||||
|
||||
bool is_constant_call() const {
|
||||
return constant_function_ != NULL;
|
||||
}
|
||||
|
||||
JSFunction* constant_function() const {
|
||||
ASSERT(constant_function_ != NULL);
|
||||
return constant_function_;
|
||||
}
|
||||
|
||||
bool is_simple_api_call() const {
|
||||
return is_simple_api_call_;
|
||||
}
|
||||
|
||||
FunctionTemplateInfo* expected_receiver_type() const {
|
||||
ASSERT(is_simple_api_call_);
|
||||
return expected_receiver_type_;
|
||||
}
|
||||
|
||||
CallHandlerInfo* api_call_info() const {
|
||||
ASSERT(is_simple_api_call_);
|
||||
return api_call_info_;
|
||||
}
|
||||
|
||||
// Returns the depth of the object having the expected type in the
|
||||
// prototype chain between the two arguments.
|
||||
int GetPrototypeDepthOfExpectedType(JSObject* object,
|
||||
JSObject* holder) const {
|
||||
ASSERT(is_simple_api_call_);
|
||||
if (expected_receiver_type_ == NULL) return 0;
|
||||
int depth = 0;
|
||||
while (object != holder) {
|
||||
if (object->IsInstanceOf(expected_receiver_type_)) return depth;
|
||||
object = JSObject::cast(object->GetPrototype());
|
||||
++depth;
|
||||
}
|
||||
if (holder->IsInstanceOf(expected_receiver_type_)) return depth;
|
||||
return kInvalidProtoDepth;
|
||||
}
|
||||
|
||||
private:
|
||||
void Initialize(JSFunction* function) {
|
||||
if (!function->is_compiled()) return;
|
||||
|
||||
constant_function_ = function;
|
||||
is_simple_api_call_ = false;
|
||||
|
||||
AnalyzePossibleApiFunction(function);
|
||||
}
|
||||
|
||||
// Determines whether the given function can be called using the
|
||||
// fast api call builtin.
|
||||
void AnalyzePossibleApiFunction(JSFunction* function) {
|
||||
SharedFunctionInfo* sfi = function->shared();
|
||||
if (sfi->function_data()->IsUndefined()) return;
|
||||
FunctionTemplateInfo* info =
|
||||
FunctionTemplateInfo::cast(sfi->function_data());
|
||||
|
||||
// Require a C++ callback.
|
||||
if (info->call_code()->IsUndefined()) return;
|
||||
api_call_info_ = CallHandlerInfo::cast(info->call_code());
|
||||
|
||||
// Accept signatures that either have no restrictions at all or
|
||||
// only have restrictions on the receiver.
|
||||
if (!info->signature()->IsUndefined()) {
|
||||
SignatureInfo* signature = SignatureInfo::cast(info->signature());
|
||||
if (!signature->args()->IsUndefined()) return;
|
||||
if (!signature->receiver()->IsUndefined()) {
|
||||
expected_receiver_type_ =
|
||||
FunctionTemplateInfo::cast(signature->receiver());
|
||||
}
|
||||
}
|
||||
|
||||
is_simple_api_call_ = true;
|
||||
}
|
||||
|
||||
JSFunction* constant_function_;
|
||||
bool is_simple_api_call_;
|
||||
FunctionTemplateInfo* expected_receiver_type_;
|
||||
CallHandlerInfo* api_call_info_;
|
||||
};
|
||||
|
||||
|
||||
// Reserves space for the extra arguments to FastHandleApiCall in the
|
||||
// caller's frame.
|
||||
//
|
||||
// These arguments are set by CheckPrototypes and GenerateFastApiCall.
|
||||
static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- esp[0] : return address
|
||||
// -- esp[4] : last argument in the internal frame of the caller
|
||||
// -----------------------------------
|
||||
__ pop(scratch);
|
||||
__ push(Immediate(Smi::FromInt(0)));
|
||||
__ push(Immediate(Smi::FromInt(0)));
|
||||
__ push(Immediate(Smi::FromInt(0)));
|
||||
__ push(Immediate(Smi::FromInt(0)));
|
||||
__ push(scratch);
|
||||
}
|
||||
|
||||
|
||||
// Undoes the effects of ReserveSpaceForFastApiCall.
|
||||
static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- esp[0] : return address
|
||||
// -- esp[4] : last fast api call extra argument
|
||||
// -- ...
|
||||
// -- esp[16] : first fast api call extra argument
|
||||
// -- esp[20] : last argument in the internal frame
|
||||
// -----------------------------------
|
||||
__ pop(scratch);
|
||||
__ add(Operand(esp), Immediate(kPointerSize * 4));
|
||||
__ push(scratch);
|
||||
}
|
||||
|
||||
|
||||
// Generates call to FastHandleApiCall builtin.
|
||||
static void GenerateFastApiCall(MacroAssembler* masm,
|
||||
const CallOptimization& optimization,
|
||||
int argc) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- esp[0] : return address
|
||||
// -- esp[4] : object passing the type check
|
||||
// (last fast api call extra argument,
|
||||
// set by CheckPrototypes)
|
||||
// -- esp[8] : api call data
|
||||
// -- esp[12] : api callback
|
||||
// -- esp[16] : api function
|
||||
// (first fast api call extra argument)
|
||||
// -- esp[20] : last argument
|
||||
// -- ...
|
||||
// -- esp[(argc + 5) * 4] : first argument
|
||||
// -- esp[(argc + 6) * 4] : receiver
|
||||
// -----------------------------------
|
||||
|
||||
// Get the function and setup the context.
|
||||
JSFunction* function = optimization.constant_function();
|
||||
__ mov(edi, Immediate(Handle<JSFunction>(function)));
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
|
||||
// Pass the additional arguments FastHandleApiCall expects.
|
||||
__ mov(Operand(esp, 4 * kPointerSize), edi);
|
||||
bool info_loaded = false;
|
||||
Object* callback = optimization.api_call_info()->callback();
|
||||
if (Heap::InNewSpace(callback)) {
|
||||
info_loaded = true;
|
||||
__ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
|
||||
__ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kCallbackOffset));
|
||||
__ mov(Operand(esp, 3 * kPointerSize), ebx);
|
||||
} else {
|
||||
__ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(callback)));
|
||||
}
|
||||
Object* call_data = optimization.api_call_info()->data();
|
||||
if (Heap::InNewSpace(call_data)) {
|
||||
if (!info_loaded) {
|
||||
__ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
|
||||
}
|
||||
__ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset));
|
||||
__ mov(Operand(esp, 2 * kPointerSize), ebx);
|
||||
} else {
|
||||
__ mov(Operand(esp, 2 * kPointerSize),
|
||||
Immediate(Handle<Object>(call_data)));
|
||||
}
|
||||
|
||||
// Set the number of arguments.
|
||||
__ mov(eax, Immediate(argc + 4));
|
||||
|
||||
// Jump to the fast api call builtin (tail call).
|
||||
Handle<Code> code = Handle<Code>(
|
||||
Builtins::builtin(Builtins::FastHandleApiCall));
|
||||
ParameterCount expected(0);
|
||||
__ InvokeCode(code, expected, expected,
|
||||
RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
class CallInterceptorCompiler BASE_EMBEDDED {
|
||||
public:
|
||||
CallInterceptorCompiler(const ParameterCount& arguments, Register name)
|
||||
: arguments_(arguments), argc_(arguments.immediate()), name_(name) {}
|
||||
CallInterceptorCompiler(StubCompiler* stub_compiler,
|
||||
const ParameterCount& arguments,
|
||||
Register name)
|
||||
: stub_compiler_(stub_compiler),
|
||||
arguments_(arguments),
|
||||
argc_(arguments.immediate()),
|
||||
name_(name) {}
|
||||
|
||||
void Compile(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.
|
||||
__ test(receiver, Immediate(kSmiTagMask));
|
||||
__ j(zero, miss, not_taken);
|
||||
|
||||
CallOptimization optimization(lookup);
|
||||
|
||||
if (optimization.is_constant_call() &&
|
||||
!Top::CanHaveSpecialFunctions(holder)) {
|
||||
CompileCacheable(masm,
|
||||
object,
|
||||
receiver,
|
||||
scratch1,
|
||||
scratch2,
|
||||
holder,
|
||||
lookup,
|
||||
name,
|
||||
optimization,
|
||||
miss);
|
||||
} else {
|
||||
CompileRegular(masm,
|
||||
object,
|
||||
receiver,
|
||||
scratch1,
|
||||
scratch2,
|
||||
name,
|
||||
holder,
|
||||
miss);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void CompileCacheable(MacroAssembler* masm,
|
||||
StubCompiler* stub_compiler,
|
||||
JSObject* object,
|
||||
Register receiver,
|
||||
Register holder,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
JSObject* holder_obj,
|
||||
LookupResult* lookup,
|
||||
String* name,
|
||||
const CallOptimization& optimization,
|
||||
Label* miss_label) {
|
||||
JSFunction* function = 0;
|
||||
bool optimize = false;
|
||||
// So far the most popular case for failed interceptor is
|
||||
// CONSTANT_FUNCTION sitting below.
|
||||
if (lookup->type() == CONSTANT_FUNCTION) {
|
||||
function = lookup->GetConstantFunction();
|
||||
// JSArray holder is a special case for call constant function
|
||||
// (see the corresponding code).
|
||||
if (function->is_compiled() && !holder_obj->IsJSArray()) {
|
||||
optimize = true;
|
||||
ASSERT(optimization.is_constant_call());
|
||||
|
||||
int depth1 = kInvalidProtoDepth;
|
||||
int depth2 = kInvalidProtoDepth;
|
||||
bool can_do_fast_api_call = false;
|
||||
if (optimization.is_simple_api_call() &&
|
||||
!lookup->holder()->IsGlobalObject()) {
|
||||
depth1 = optimization.GetPrototypeDepthOfExpectedType(object, holder_obj);
|
||||
if (depth1 == kInvalidProtoDepth) {
|
||||
depth2 = optimization.GetPrototypeDepthOfExpectedType(holder_obj,
|
||||
lookup->holder());
|
||||
}
|
||||
can_do_fast_api_call = (depth1 != kInvalidProtoDepth) ||
|
||||
(depth2 != kInvalidProtoDepth);
|
||||
}
|
||||
|
||||
if (!optimize) {
|
||||
CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label);
|
||||
return;
|
||||
__ IncrementCounter(&Counters::call_const_interceptor, 1);
|
||||
|
||||
if (can_do_fast_api_call) {
|
||||
__ IncrementCounter(&Counters::call_const_interceptor_fast_api, 1);
|
||||
ReserveSpaceForFastApiCall(masm, scratch1);
|
||||
}
|
||||
|
||||
__ EnterInternalFrame();
|
||||
__ push(holder); // Save the holder.
|
||||
__ push(name_); // Save the name.
|
||||
Label miss_cleanup;
|
||||
Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
|
||||
Register holder =
|
||||
stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
|
||||
scratch1, scratch2, name,
|
||||
depth1, miss);
|
||||
|
||||
CompileCallLoadPropertyWithInterceptor(masm,
|
||||
receiver,
|
||||
holder,
|
||||
name_,
|
||||
holder_obj);
|
||||
Label regular_invoke;
|
||||
LoadWithInterceptor(masm, receiver, holder, holder_obj, ®ular_invoke);
|
||||
|
||||
__ pop(name_); // Restore the name.
|
||||
__ pop(receiver); // Restore the holder.
|
||||
__ LeaveInternalFrame();
|
||||
// Generate code for the failed interceptor case.
|
||||
|
||||
__ cmp(eax, Factory::no_interceptor_result_sentinel());
|
||||
Label invoke;
|
||||
__ j(not_equal, &invoke);
|
||||
// Check the lookup is still valid.
|
||||
stub_compiler_->CheckPrototypes(holder_obj, receiver,
|
||||
lookup->holder(),
|
||||
scratch1, scratch2, name,
|
||||
depth2, miss);
|
||||
|
||||
stub_compiler->CheckPrototypes(holder_obj, receiver,
|
||||
lookup->holder(), scratch1,
|
||||
scratch2,
|
||||
name,
|
||||
miss_label);
|
||||
if (lookup->holder()->IsGlobalObject()) {
|
||||
ASSERT(!can_do_fast_api_call);
|
||||
__ mov(edx, Operand(esp, (argc_ + 1) * kPointerSize));
|
||||
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx);
|
||||
}
|
||||
|
||||
ASSERT(function->is_compiled());
|
||||
// Get the function and setup the context.
|
||||
__ mov(edi, Immediate(Handle<JSFunction>(function)));
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
if (can_do_fast_api_call) {
|
||||
GenerateFastApiCall(masm, optimization, argc_);
|
||||
} else {
|
||||
// Get the function and setup the context.
|
||||
JSFunction* function = optimization.constant_function();
|
||||
__ mov(edi, Immediate(Handle<JSFunction>(function)));
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
|
||||
// Jump to the cached code (tail call).
|
||||
Handle<Code> code(function->code());
|
||||
ParameterCount expected(function->shared()->formal_parameter_count());
|
||||
__ InvokeCode(code, expected, arguments_,
|
||||
RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
||||
// Jump to the cached code (tail call).
|
||||
ASSERT(function->is_compiled());
|
||||
Handle<Code> code(function->code());
|
||||
ParameterCount expected(function->shared()->formal_parameter_count());
|
||||
__ InvokeCode(code, expected, arguments_,
|
||||
RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
__ bind(&invoke);
|
||||
if (can_do_fast_api_call) {
|
||||
__ bind(&miss_cleanup);
|
||||
FreeSpaceForFastApiCall(masm, scratch1);
|
||||
__ jmp(miss_label);
|
||||
}
|
||||
|
||||
__ bind(®ular_invoke);
|
||||
if (can_do_fast_api_call) {
|
||||
FreeSpaceForFastApiCall(masm, scratch1);
|
||||
}
|
||||
}
|
||||
|
||||
void CompileRegular(MacroAssembler* masm,
|
||||
JSObject* object,
|
||||
Register receiver,
|
||||
Register holder,
|
||||
Register scratch,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
String* name,
|
||||
JSObject* holder_obj,
|
||||
Label* miss_label) {
|
||||
Register holder =
|
||||
stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
|
||||
scratch1, scratch2, name,
|
||||
miss_label);
|
||||
|
||||
__ EnterInternalFrame();
|
||||
// Save the name_ register across the call.
|
||||
__ push(name_);
|
||||
@ -586,9 +851,32 @@ class CallInterceptorCompiler BASE_EMBEDDED {
|
||||
__ LeaveInternalFrame();
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadWithInterceptor(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register holder,
|
||||
JSObject* holder_obj,
|
||||
Label* interceptor_succeeded) {
|
||||
__ EnterInternalFrame();
|
||||
__ push(holder); // Save the holder.
|
||||
__ push(name_); // Save the name.
|
||||
|
||||
CompileCallLoadPropertyWithInterceptor(masm,
|
||||
receiver,
|
||||
holder,
|
||||
name_,
|
||||
holder_obj);
|
||||
|
||||
__ pop(name_); // Restore the name.
|
||||
__ pop(receiver); // Restore the holder.
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
__ cmp(eax, Factory::no_interceptor_result_sentinel());
|
||||
__ j(not_equal, interceptor_succeeded);
|
||||
}
|
||||
|
||||
StubCompiler* stub_compiler_;
|
||||
const ParameterCount& arguments_;
|
||||
int argc_;
|
||||
const int argc_;
|
||||
Register name_;
|
||||
};
|
||||
|
||||
@ -698,10 +986,12 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
|
||||
Register holder_reg,
|
||||
Register scratch,
|
||||
String* name,
|
||||
int push_at_depth,
|
||||
Label* miss) {
|
||||
// Check that the maps haven't changed.
|
||||
Register result =
|
||||
masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
|
||||
masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
|
||||
push_at_depth, miss);
|
||||
|
||||
// If we've skipped any global objects, it's not enough to verify
|
||||
// that their maps haven't changed.
|
||||
@ -723,7 +1013,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
|
||||
object = JSObject::cast(object->GetPrototype());
|
||||
}
|
||||
|
||||
// Return the register containin the holder.
|
||||
// Return the register containing the holder.
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -976,15 +1266,31 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
// unless we're doing a receiver map check.
|
||||
ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
|
||||
|
||||
CallOptimization optimization(function);
|
||||
int depth = kInvalidProtoDepth;
|
||||
|
||||
switch (check) {
|
||||
case RECEIVER_MAP_CHECK:
|
||||
__ IncrementCounter(&Counters::call_const, 1);
|
||||
|
||||
if (optimization.is_simple_api_call() && !object->IsGlobalObject()) {
|
||||
depth = optimization.GetPrototypeDepthOfExpectedType(
|
||||
JSObject::cast(object), holder);
|
||||
}
|
||||
|
||||
if (depth != kInvalidProtoDepth) {
|
||||
__ IncrementCounter(&Counters::call_const_fast_api, 1);
|
||||
ReserveSpaceForFastApiCall(masm(), eax);
|
||||
}
|
||||
|
||||
// Check that the maps haven't changed.
|
||||
CheckPrototypes(JSObject::cast(object), edx, holder,
|
||||
ebx, eax, name, &miss);
|
||||
ebx, eax, name, depth, &miss);
|
||||
|
||||
// Patch the receiver on the stack with the global proxy if
|
||||
// necessary.
|
||||
if (object->IsGlobalObject()) {
|
||||
ASSERT(depth == kInvalidProtoDepth);
|
||||
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
|
||||
}
|
||||
@ -1069,19 +1375,26 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Get the function and setup the context.
|
||||
__ mov(edi, Immediate(Handle<JSFunction>(function)));
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
if (depth != kInvalidProtoDepth) {
|
||||
GenerateFastApiCall(masm(), optimization, argc);
|
||||
} else {
|
||||
// Get the function and setup the context.
|
||||
__ mov(edi, Immediate(Handle<JSFunction>(function)));
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
|
||||
// Jump to the cached code (tail call).
|
||||
ASSERT(function->is_compiled());
|
||||
Handle<Code> code(function->code());
|
||||
ParameterCount expected(function->shared()->formal_parameter_count());
|
||||
__ InvokeCode(code, expected, arguments(),
|
||||
RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
||||
// Jump to the cached code (tail call).
|
||||
ASSERT(function->is_compiled());
|
||||
Handle<Code> code(function->code());
|
||||
ParameterCount expected(function->shared()->formal_parameter_count());
|
||||
__ InvokeCode(code, expected, arguments(),
|
||||
RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
if (depth != kInvalidProtoDepth) {
|
||||
FreeSpaceForFastApiCall(masm(), eax);
|
||||
}
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
|
||||
@ -1115,18 +1428,16 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
|
||||
// Get the receiver from the stack.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
||||
CallInterceptorCompiler compiler(arguments(), ecx);
|
||||
CompileLoadInterceptor(&compiler,
|
||||
this,
|
||||
masm(),
|
||||
JSObject::cast(object),
|
||||
holder,
|
||||
name,
|
||||
&lookup,
|
||||
edx,
|
||||
ebx,
|
||||
edi,
|
||||
&miss);
|
||||
CallInterceptorCompiler compiler(this, arguments(), ecx);
|
||||
compiler.Compile(masm(),
|
||||
JSObject::cast(object),
|
||||
holder,
|
||||
name,
|
||||
&lookup,
|
||||
edx,
|
||||
ebx,
|
||||
edi,
|
||||
&miss);
|
||||
|
||||
// Restore receiver.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
@ -61,6 +61,8 @@ enum AllocationFlags {
|
||||
RESULT_CONTAINS_TOP = 1 << 1
|
||||
};
|
||||
|
||||
// Invalid depth in prototype chain.
|
||||
const int kInvalidProtoDepth = -1;
|
||||
|
||||
#if V8_TARGET_ARCH_IA32
|
||||
#include "assembler.h"
|
||||
|
@ -381,12 +381,25 @@ class StubCompiler BASE_EMBEDDED {
|
||||
|
||||
// Check the integrity of the prototype chain to make sure that the
|
||||
// current IC is still valid.
|
||||
|
||||
Register CheckPrototypes(JSObject* object,
|
||||
Register object_reg,
|
||||
JSObject* holder,
|
||||
Register holder_reg,
|
||||
Register scratch,
|
||||
String* name,
|
||||
Label* miss) {
|
||||
return CheckPrototypes(object, object_reg, holder, holder_reg, scratch,
|
||||
name, kInvalidProtoDepth, miss);
|
||||
}
|
||||
|
||||
Register CheckPrototypes(JSObject* object,
|
||||
Register object_reg,
|
||||
JSObject* holder,
|
||||
Register holder_reg,
|
||||
Register scratch,
|
||||
String* name,
|
||||
int save_at_depth,
|
||||
Label* miss);
|
||||
|
||||
protected:
|
||||
|
@ -949,10 +949,15 @@ Handle<Context> Top::GetCallingGlobalContext() {
|
||||
}
|
||||
|
||||
|
||||
bool Top::CanHaveSpecialFunctions(JSObject* object) {
|
||||
return object->IsJSArray();
|
||||
}
|
||||
|
||||
|
||||
Object* Top::LookupSpecialFunction(JSObject* receiver,
|
||||
JSObject* prototype,
|
||||
JSFunction* function) {
|
||||
if (receiver->IsJSArray()) {
|
||||
if (CanHaveSpecialFunctions(receiver)) {
|
||||
FixedArray* table = context()->global_context()->special_function_table();
|
||||
for (int index = 0; index < table->length(); index +=3) {
|
||||
if ((prototype == table->get(index)) &&
|
||||
|
@ -342,6 +342,7 @@ class Top {
|
||||
return Handle<JSBuiltinsObject>(thread_local_.context_->builtins());
|
||||
}
|
||||
|
||||
static bool CanHaveSpecialFunctions(JSObject* object);
|
||||
static Object* LookupSpecialFunction(JSObject* receiver,
|
||||
JSObject* prototype,
|
||||
JSFunction* value);
|
||||
|
@ -100,69 +100,73 @@ namespace internal {
|
||||
SC(total_full_codegen_source_size, V8.TotalFullCodegenSourceSize)
|
||||
|
||||
|
||||
#define STATS_COUNTER_LIST_2(SC) \
|
||||
/* Number of code stubs. */ \
|
||||
SC(code_stubs, V8.CodeStubs) \
|
||||
/* Amount of stub code. */ \
|
||||
SC(total_stubs_code_size, V8.TotalStubsCodeSize) \
|
||||
/* Amount of (JS) compiled code. */ \
|
||||
SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \
|
||||
SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \
|
||||
SC(gc_compactor_caused_by_promoted_data, \
|
||||
V8.GCCompactorCausedByPromotedData) \
|
||||
SC(gc_compactor_caused_by_oldspace_exhaustion, \
|
||||
V8.GCCompactorCausedByOldspaceExhaustion) \
|
||||
SC(gc_compactor_caused_by_weak_handles, \
|
||||
V8.GCCompactorCausedByWeakHandles) \
|
||||
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
|
||||
SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
|
||||
/* How is the generic keyed-load stub used? */ \
|
||||
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
|
||||
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
|
||||
SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
|
||||
SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
|
||||
/* Count how much the monomorphic keyed-load stubs are hit. */ \
|
||||
SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \
|
||||
SC(keyed_load_string_length, V8.KeyedLoadStringLength) \
|
||||
SC(keyed_load_array_length, V8.KeyedLoadArrayLength) \
|
||||
SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction) \
|
||||
SC(keyed_load_field, V8.KeyedLoadField) \
|
||||
SC(keyed_load_callback, V8.KeyedLoadCallback) \
|
||||
SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \
|
||||
SC(keyed_load_inline, V8.KeyedLoadInline) \
|
||||
SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \
|
||||
SC(named_load_inline, V8.NamedLoadInline) \
|
||||
SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \
|
||||
SC(named_load_global_inline, V8.NamedLoadGlobalInline) \
|
||||
SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss) \
|
||||
SC(keyed_store_field, V8.KeyedStoreField) \
|
||||
SC(keyed_store_inline, V8.KeyedStoreInline) \
|
||||
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
|
||||
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
|
||||
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
|
||||
SC(call_global_inline, V8.CallGlobalInline) \
|
||||
SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
|
||||
SC(constructed_objects, V8.ConstructedObjects) \
|
||||
SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
|
||||
SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
|
||||
SC(array_function_runtime, V8.ArrayFunctionRuntime) \
|
||||
SC(array_function_native, V8.ArrayFunctionNative) \
|
||||
SC(for_in, V8.ForIn) \
|
||||
SC(enum_cache_hits, V8.EnumCacheHits) \
|
||||
SC(enum_cache_misses, V8.EnumCacheMisses) \
|
||||
SC(reloc_info_count, V8.RelocInfoCount) \
|
||||
SC(reloc_info_size, V8.RelocInfoSize) \
|
||||
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
|
||||
SC(compute_entry_frame, V8.ComputeEntryFrame) \
|
||||
SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
|
||||
SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
|
||||
SC(string_add_runtime, V8.StringAddRuntime) \
|
||||
SC(string_add_native, V8.StringAddNative) \
|
||||
SC(sub_string_runtime, V8.SubStringRuntime) \
|
||||
SC(sub_string_native, V8.SubStringNative) \
|
||||
SC(string_compare_native, V8.StringCompareNative) \
|
||||
SC(string_compare_runtime, V8.StringCompareRuntime) \
|
||||
SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
|
||||
#define STATS_COUNTER_LIST_2(SC) \
|
||||
/* Number of code stubs. */ \
|
||||
SC(code_stubs, V8.CodeStubs) \
|
||||
/* Amount of stub code. */ \
|
||||
SC(total_stubs_code_size, V8.TotalStubsCodeSize) \
|
||||
/* Amount of (JS) compiled code. */ \
|
||||
SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \
|
||||
SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \
|
||||
SC(gc_compactor_caused_by_promoted_data, \
|
||||
V8.GCCompactorCausedByPromotedData) \
|
||||
SC(gc_compactor_caused_by_oldspace_exhaustion, \
|
||||
V8.GCCompactorCausedByOldspaceExhaustion) \
|
||||
SC(gc_compactor_caused_by_weak_handles, \
|
||||
V8.GCCompactorCausedByWeakHandles) \
|
||||
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
|
||||
SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
|
||||
/* How is the generic keyed-load stub used? */ \
|
||||
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
|
||||
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
|
||||
SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
|
||||
SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
|
||||
/* Count how much the monomorphic keyed-load stubs are hit. */ \
|
||||
SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \
|
||||
SC(keyed_load_string_length, V8.KeyedLoadStringLength) \
|
||||
SC(keyed_load_array_length, V8.KeyedLoadArrayLength) \
|
||||
SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction) \
|
||||
SC(keyed_load_field, V8.KeyedLoadField) \
|
||||
SC(keyed_load_callback, V8.KeyedLoadCallback) \
|
||||
SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \
|
||||
SC(keyed_load_inline, V8.KeyedLoadInline) \
|
||||
SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \
|
||||
SC(named_load_inline, V8.NamedLoadInline) \
|
||||
SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \
|
||||
SC(named_load_global_inline, V8.NamedLoadGlobalInline) \
|
||||
SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss) \
|
||||
SC(keyed_store_field, V8.KeyedStoreField) \
|
||||
SC(keyed_store_inline, V8.KeyedStoreInline) \
|
||||
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
|
||||
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
|
||||
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
|
||||
SC(call_const, V8.CallConst) \
|
||||
SC(call_const_fast_api, V8.CallConstFastApi) \
|
||||
SC(call_const_interceptor, V8.CallConstInterceptor) \
|
||||
SC(call_const_interceptor_fast_api, V8.CallConstInterceptorFastApi) \
|
||||
SC(call_global_inline, V8.CallGlobalInline) \
|
||||
SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
|
||||
SC(constructed_objects, V8.ConstructedObjects) \
|
||||
SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
|
||||
SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
|
||||
SC(array_function_runtime, V8.ArrayFunctionRuntime) \
|
||||
SC(array_function_native, V8.ArrayFunctionNative) \
|
||||
SC(for_in, V8.ForIn) \
|
||||
SC(enum_cache_hits, V8.EnumCacheHits) \
|
||||
SC(enum_cache_misses, V8.EnumCacheMisses) \
|
||||
SC(reloc_info_count, V8.RelocInfoCount) \
|
||||
SC(reloc_info_size, V8.RelocInfoSize) \
|
||||
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
|
||||
SC(compute_entry_frame, V8.ComputeEntryFrame) \
|
||||
SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
|
||||
SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
|
||||
SC(string_add_runtime, V8.StringAddRuntime) \
|
||||
SC(string_add_native, V8.StringAddNative) \
|
||||
SC(sub_string_runtime, V8.SubStringRuntime) \
|
||||
SC(sub_string_native, V8.SubStringNative) \
|
||||
SC(string_compare_native, V8.StringCompareNative) \
|
||||
SC(string_compare_runtime, V8.StringCompareRuntime) \
|
||||
SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
|
||||
SC(regexp_entry_native, V8.RegExpEntryNative)
|
||||
|
||||
// This file contains all the v8 counters that are in use.
|
||||
|
@ -1669,7 +1669,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
|
||||
Register holder_reg,
|
||||
Register scratch,
|
||||
String* name,
|
||||
int save_at_depth,
|
||||
Label* miss) {
|
||||
// TODO(602): support object saving.
|
||||
ASSERT(save_at_depth == kInvalidProtoDepth);
|
||||
|
||||
// Check that the maps haven't changed.
|
||||
Register result =
|
||||
__ CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
|
||||
|
@ -5890,6 +5890,294 @@ THREADED_TEST(InterceptorCallICCachedFromGlobal) {
|
||||
CHECK_EQ(239 * 10, value->Int32Value());
|
||||
}
|
||||
|
||||
static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
|
||||
const AccessorInfo& info) {
|
||||
ApiTestFuzzer::Fuzz();
|
||||
int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
|
||||
++(*call_count);
|
||||
if ((*call_count) % 20 == 0) {
|
||||
v8::internal::Heap::CollectAllGarbage(true);
|
||||
}
|
||||
return v8::Handle<Value>();
|
||||
}
|
||||
|
||||
static v8::Handle<Value> FastApiCallback_TrivialSignature(
|
||||
const v8::Arguments& args) {
|
||||
ApiTestFuzzer::Fuzz();
|
||||
CHECK_EQ(args.This(), args.Holder());
|
||||
CHECK(args.Data()->Equals(v8_str("method_data")));
|
||||
return v8::Integer::New(args[0]->Int32Value() + 1);
|
||||
}
|
||||
|
||||
static v8::Handle<Value> FastApiCallback_SimpleSignature(
|
||||
const v8::Arguments& args) {
|
||||
ApiTestFuzzer::Fuzz();
|
||||
CHECK_EQ(args.This()->GetPrototype(), args.Holder());
|
||||
CHECK(args.Data()->Equals(v8_str("method_data")));
|
||||
// Note, we're using HasRealNamedProperty instead of Has to avoid
|
||||
// invoking the interceptor again.
|
||||
CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
|
||||
return v8::Integer::New(args[0]->Int32Value() + 1);
|
||||
}
|
||||
|
||||
// Helper to maximize the odds of object moving.
|
||||
static void GenerateSomeGarbage() {
|
||||
CompileRun(
|
||||
"var garbage;"
|
||||
"for (var i = 0; i < 1000; i++) {"
|
||||
" garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
|
||||
"}"
|
||||
"garbage = undefined;");
|
||||
}
|
||||
|
||||
THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
|
||||
int interceptor_call_count = 0;
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Handle<v8::Signature>());
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
|
||||
NULL, NULL, NULL, NULL,
|
||||
v8::External::Wrap(&interceptor_call_count));
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = o.method(41);"
|
||||
"}");
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
CHECK_EQ(100, interceptor_call_count);
|
||||
}
|
||||
|
||||
THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
|
||||
int interceptor_call_count = 0;
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Signature::New(fun_templ));
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
|
||||
NULL, NULL, NULL, NULL,
|
||||
v8::External::Wrap(&interceptor_call_count));
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o.foo = 17;"
|
||||
"var receiver = {};"
|
||||
"receiver.__proto__ = o;"
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = receiver.method(41);"
|
||||
"}");
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
CHECK_EQ(100, interceptor_call_count);
|
||||
}
|
||||
|
||||
THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
|
||||
int interceptor_call_count = 0;
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Signature::New(fun_templ));
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
|
||||
NULL, NULL, NULL, NULL,
|
||||
v8::External::Wrap(&interceptor_call_count));
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o.foo = 17;"
|
||||
"var receiver = {};"
|
||||
"receiver.__proto__ = o;"
|
||||
"var result = 0;"
|
||||
"var saved_result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = receiver.method(41);"
|
||||
" if (i == 50) {"
|
||||
" saved_result = result;"
|
||||
" receiver = {method: function(x) { return x - 1 }};"
|
||||
" }"
|
||||
"}");
|
||||
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
|
||||
CHECK_GE(interceptor_call_count, 50);
|
||||
}
|
||||
|
||||
THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
|
||||
int interceptor_call_count = 0;
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Signature::New(fun_templ));
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
|
||||
NULL, NULL, NULL, NULL,
|
||||
v8::External::Wrap(&interceptor_call_count));
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o.foo = 17;"
|
||||
"var receiver = {};"
|
||||
"receiver.__proto__ = o;"
|
||||
"var result = 0;"
|
||||
"var saved_result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = receiver.method(41);"
|
||||
" if (i == 50) {"
|
||||
" saved_result = result;"
|
||||
" o.method = function(x) { return x - 1 };"
|
||||
" }"
|
||||
"}");
|
||||
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
|
||||
CHECK_GE(interceptor_call_count, 50);
|
||||
}
|
||||
|
||||
THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
|
||||
int interceptor_call_count = 0;
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Signature::New(fun_templ));
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
|
||||
NULL, NULL, NULL, NULL,
|
||||
v8::External::Wrap(&interceptor_call_count));
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::TryCatch try_catch;
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o.foo = 17;"
|
||||
"var receiver = {};"
|
||||
"receiver.__proto__ = o;"
|
||||
"var result = 0;"
|
||||
"var saved_result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = receiver.method(41);"
|
||||
" if (i == 50) {"
|
||||
" saved_result = result;"
|
||||
" receiver = {method: receiver.method};"
|
||||
" }"
|
||||
"}");
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK_EQ(v8_str("TypeError: Illegal invocation"),
|
||||
try_catch.Exception()->ToString());
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
|
||||
CHECK_GE(interceptor_call_count, 50);
|
||||
}
|
||||
|
||||
THREADED_TEST(CallICFastApi_TrivialSignature) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Handle<v8::Signature>());
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = o.method(41);"
|
||||
"}");
|
||||
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
THREADED_TEST(CallICFastApi_SimpleSignature) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Signature::New(fun_templ));
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o.foo = 17;"
|
||||
"var receiver = {};"
|
||||
"receiver.__proto__ = o;"
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = receiver.method(41);"
|
||||
"}");
|
||||
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
THREADED_TEST(CallICFastApi_SimpleSignature_Miss) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
|
||||
v8::Handle<v8::FunctionTemplate> method_templ =
|
||||
v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
|
||||
v8_str("method_data"),
|
||||
v8::Signature::New(fun_templ));
|
||||
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
|
||||
proto_templ->Set(v8_str("method"), method_templ);
|
||||
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
|
||||
LocalContext context;
|
||||
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
|
||||
GenerateSomeGarbage();
|
||||
context->Global()->Set(v8_str("o"), fun->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o.foo = 17;"
|
||||
"var receiver = {};"
|
||||
"receiver.__proto__ = o;"
|
||||
"var result = 0;"
|
||||
"var saved_result = 0;"
|
||||
"for (var i = 0; i < 100; i++) {"
|
||||
" result = receiver.method(41);"
|
||||
" if (i == 50) {"
|
||||
" saved_result = result;"
|
||||
" receiver = {method: function(x) { return x - 1 }};"
|
||||
" }"
|
||||
"}");
|
||||
CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
|
||||
}
|
||||
|
||||
|
||||
static int interceptor_call_count = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user