Commit 0027c834 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[callbacks] Properly support RCS for ApiAccessors and Callbacks.

Bug: chromium:782550

Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I4426415b55772d82bd16b638c3c533320efa3b72
Reviewed-on: https://chromium-review.googlesource.com/771752
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55138}
parent 546ca37b
......@@ -12084,6 +12084,13 @@ Node* CodeStubAssembler::IsDebugActive() {
return Word32NotEqual(is_debug_active, Int32Constant(0));
}
TNode<BoolT> CodeStubAssembler::IsRuntimeCallStatsEnabled() {
TNode<UintPtrT> flag_value = UncheckedCast<UintPtrT>(Load(
MachineType::IntPtr(),
ExternalConstant(ExternalReference::address_of_runtime_stats_flag())));
return WordNotEqual(flag_value, IntPtrConstant(0));
}
Node* CodeStubAssembler::IsPromiseHookEnabled() {
Node* const promise_hook = Load(
MachineType::Pointer(),
......
......@@ -2686,6 +2686,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Debug helpers
Node* IsDebugActive();
TNode<BoolT> IsRuntimeCallStatsEnabled();
// TypedArray/ArrayBuffer helpers
Node* IsDetachedBuffer(Node* buffer);
TNode<JSArrayBuffer> LoadArrayBufferViewBuffer(
......
......@@ -467,6 +467,10 @@ ExternalReference ExternalReference::address_of_min_int() {
return ExternalReference(reinterpret_cast<Address>(&double_min_int_constant));
}
ExternalReference ExternalReference::address_of_runtime_stats_flag() {
return ExternalReference(&FLAG_runtime_stats);
}
ExternalReference ExternalReference::address_of_one_half() {
return ExternalReference(
reinterpret_cast<Address>(&double_one_half_constant));
......
......@@ -78,6 +78,7 @@ class StatsCounter;
V(address_of_float_neg_constant, "float_negate_constant") \
V(address_of_min_int, "LDoubleConstant::min_int") \
V(address_of_one_half, "LDoubleConstant::one_half") \
V(address_of_runtime_stats_flag, "FLAG_runtime_stats") \
V(address_of_the_hole_nan, "the_hole_nan") \
V(address_of_uint32_bias, "uint32_bias") \
V(bytecode_size_table_address, "Bytecodes::bytecode_size_table_address") \
......
......@@ -207,6 +207,78 @@ void AccessorAssembler::HandleLoadICHandlerCase(
}
}
void AccessorAssembler::HandleLoadCallbackProperty(const LoadICParameters* p,
TNode<JSObject> holder,
TNode<WordT> handler_word,
ExitPoint* exit_point) {
Comment("native_data_property_load");
Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
Label runtime(this, Label::kDeferred);
Callable callable = CodeFactory::ApiGetter(isolate());
TNode<AccessorInfo> accessor_info =
CAST(LoadDescriptorValue(LoadMap(holder), descriptor));
GotoIf(IsRuntimeCallStatsEnabled(), &runtime);
exit_point->ReturnCallStub(callable, p->context, p->receiver, holder,
accessor_info);
BIND(&runtime);
exit_point->ReturnCallRuntime(Runtime::kLoadCallbackProperty, p->context,
p->receiver, holder, accessor_info, p->name);
}
void AccessorAssembler::HandleLoadAccessor(
const LoadICParameters* p, TNode<CallHandlerInfo> call_handler_info,
TNode<WordT> handler_word, TNode<DataHandler> handler,
TNode<IntPtrT> handler_kind, ExitPoint* exit_point) {
Comment("api_getter");
Label runtime(this, Label::kDeferred);
// Context is stored either in data2 or data3 field depending on whether
// the access check is enabled for this handler or not.
TNode<MaybeObject> maybe_context = Select<MaybeObject>(
IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word),
[=] { return LoadHandlerDataField(handler, 3); },
[=] { return LoadHandlerDataField(handler, 2); });
CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_context));
CSA_CHECK(this, IsNotClearedWeakHeapObject(maybe_context));
TNode<Object> context = ToWeakHeapObject(maybe_context);
GotoIf(IsRuntimeCallStatsEnabled(), &runtime);
{
TNode<Foreign> foreign = CAST(
LoadObjectField(call_handler_info, CallHandlerInfo::kJsCallbackOffset));
TNode<WordT> callback = TNode<WordT>::UncheckedCast(LoadObjectField(
foreign, Foreign::kForeignAddressOffset, MachineType::Pointer()));
TNode<Object> data =
LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
VARIABLE(api_holder, MachineRepresentation::kTagged, p->receiver);
Label load(this);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
&load);
CSA_ASSERT(
this,
WordEqual(handler_kind,
IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)));
api_holder.Bind(LoadMapPrototype(LoadMap(p->receiver)));
Goto(&load);
BIND(&load);
Callable callable = CodeFactory::CallApiCallback(isolate(), 0);
exit_point->Return(CallStub(callable, nullptr, context, data,
api_holder.value(), callback, p->receiver));
}
BIND(&runtime);
exit_point->ReturnCallRuntime(Runtime::kLoadAccessorProperty, context,
p->receiver, SmiTag(handler_kind),
call_handler_info);
}
void AccessorAssembler::HandleLoadField(Node* holder, Node* handler_word,
Variable* var_double_value,
Label* rebox_double,
......@@ -250,17 +322,17 @@ void AccessorAssembler::HandleLoadField(Node* holder, Node* handler_word,
}
}
TNode<Object> AccessorAssembler::LoadDescriptorValue(Node* map,
TNode<Object> AccessorAssembler::LoadDescriptorValue(TNode<Map> map,
Node* descriptor) {
return CAST(LoadDescriptorValueOrFieldType(map, descriptor));
}
TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType(
Node* map, Node* descriptor) {
TNode<Map> map, SloppyTNode<IntPtrT> descriptor) {
TNode<DescriptorArray> descriptors = LoadMapDescriptors(map);
Node* scaled_descriptor =
TNode<IntPtrT> scaled_descriptor =
IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize));
Node* value_index = IntPtrAdd(
TNode<IntPtrT> value_index = IntPtrAdd(
scaled_descriptor, IntPtrConstant(DescriptorArray::kFirstIndex +
DescriptorArray::kEntryValueIndex));
CSA_ASSERT(this, UintPtrLessThan(descriptor, LoadAndUntagWeakFixedArrayLength(
......@@ -269,14 +341,15 @@ TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType(
}
void AccessorAssembler::HandleLoadICSmiHandlerCase(
const LoadICParameters* p, Node* holder, Node* smi_handler, Node* handler,
Label* miss, ExitPoint* exit_point, OnNonExistent on_nonexistent,
ElementSupport support_elements) {
const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler,
SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point,
OnNonExistent on_nonexistent, ElementSupport support_elements) {
VARIABLE(var_double_value, MachineRepresentation::kFloat64);
Label rebox_double(this, &var_double_value);
Node* handler_word = SmiUntag(smi_handler);
Node* handler_kind = DecodeWord<LoadHandler::KindBits>(handler_word);
TNode<WordT> handler_word = SmiUntag(smi_handler);
TNode<IntPtrT> handler_kind =
Signed(DecodeWord<LoadHandler::KindBits>(handler_word));
if (support_elements == kSupportElements) {
Label if_element(this), if_indexed_string(this), if_property(this);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)),
......@@ -461,58 +534,11 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
}
BIND(&native_data_property);
{
Comment("native_data_property_load");
Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
Node* accessor_info = LoadDescriptorValue(LoadMap(holder), descriptor);
Callable callable = CodeFactory::ApiGetter(isolate());
exit_point->ReturnCallStub(callable, p->context, p->receiver, holder,
accessor_info);
}
HandleLoadCallbackProperty(p, CAST(holder), handler_word, exit_point);
BIND(&api_getter);
{
Comment("api_getter");
CSA_ASSERT(this, TaggedIsNotSmi(handler));
Node* call_handler_info = holder;
// Context is stored either in data2 or data3 field depending on whether
// the access check is enabled for this handler or not.
TNode<MaybeObject> maybe_context = Select<MaybeObject>(
IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word),
[=] { return LoadHandlerDataField(handler, 3); },
[=] { return LoadHandlerDataField(handler, 2); });
CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_context));
TNode<Object> context = Select<Object>(
IsClearedWeakHeapObject(maybe_context), [=] { return SmiConstant(0); },
[=] { return ToWeakHeapObject(maybe_context); });
Node* foreign =
LoadObjectField(call_handler_info, CallHandlerInfo::kJsCallbackOffset);
Node* callback = LoadObjectField(foreign, Foreign::kForeignAddressOffset,
MachineType::Pointer());
Node* data =
LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
VARIABLE(api_holder, MachineRepresentation::kTagged, p->receiver);
Label load(this);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
&load);
CSA_ASSERT(
this,
WordEqual(handler_kind,
IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)));
api_holder.Bind(LoadMapPrototype(LoadMap(p->receiver)));
Goto(&load);
BIND(&load);
Callable callable = CodeFactory::CallApiCallback(isolate(), 0);
exit_point->Return(CallStub(callable, nullptr, context, data,
api_holder.value(), callback, p->receiver));
}
HandleLoadAccessor(p, CAST(holder), handler_word, CAST(handler), handler_kind,
exit_point);
BIND(&proxy);
{
......@@ -791,6 +817,7 @@ void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable,
void AccessorAssembler::HandleStoreICNativeDataProperty(
const StoreICParameters* p, Node* holder, Node* handler_word) {
Comment("native_data_property_store");
Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word);
Node* accessor_info = LoadDescriptorValue(LoadMap(holder), descriptor);
CSA_CHECK(this, IsAccessorInfo(accessor_info));
......
......@@ -138,9 +138,9 @@ class AccessorAssembler : public CodeStubAssembler {
TVariable<MaybeObject>* var_handler, Label* if_handler,
Label* miss, ExitPoint* exit_point);
TNode<Object> LoadDescriptorValue(Node* map, Node* descriptor);
TNode<MaybeObject> LoadDescriptorValueOrFieldType(Node* map,
Node* descriptor);
TNode<Object> LoadDescriptorValue(TNode<Map> map, Node* descriptor);
TNode<MaybeObject> LoadDescriptorValueOrFieldType(
TNode<Map> map, SloppyTNode<IntPtrT> descriptor);
void LoadIC_Uninitialized(const LoadICParameters* p);
......@@ -174,7 +174,8 @@ class AccessorAssembler : public CodeStubAssembler {
ElementSupport support_elements = kOnlyProperties);
void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder,
Node* smi_handler, Node* handler, Label* miss,
SloppyTNode<Smi> smi_handler,
SloppyTNode<Object> handler, Label* miss,
ExitPoint* exit_point,
OnNonExistent on_nonexistent,
ElementSupport support_elements);
......@@ -184,6 +185,16 @@ class AccessorAssembler : public CodeStubAssembler {
Label* if_smi_handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode);
void HandleLoadCallbackProperty(const LoadICParameters* p,
TNode<JSObject> holder,
TNode<WordT> handler_word,
ExitPoint* exit_point);
void HandleLoadAccessor(const LoadICParameters* p,
TNode<CallHandlerInfo> call_handler_info,
TNode<WordT> handler_word, TNode<DataHandler> handler,
TNode<IntPtrT> handler_kind, ExitPoint* exit_point);
void HandleLoadField(Node* holder, Node* handler_word,
Variable* var_double_value, Label* rebox_double,
ExitPoint* exit_point);
......
......@@ -2587,7 +2587,7 @@ RUNTIME_FUNCTION(Runtime_CloneObjectIC_Slow) {
RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) {
Handle<JSObject> receiver = args.at<JSObject>(0);
Handle<JSObject> holder = args.at<JSObject>(1);
Handle<HeapObject> callback = args.at<HeapObject>(2);
Handle<AccessorInfo> info = args.at<AccessorInfo>(2);
Handle<Name> name = args.at<Name>(3);
Handle<Object> value = args.at(4);
CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 5);
......@@ -2599,7 +2599,6 @@ RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) {
language_mode));
}
Handle<AccessorInfo> info(AccessorInfo::cast(*callback), isolate);
DCHECK(info->IsCompatibleReceiver(*receiver));
ShouldThrow should_throw =
......@@ -2611,6 +2610,45 @@ RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) {
return *value;
}
RUNTIME_FUNCTION(Runtime_LoadCallbackProperty) {
Handle<JSObject> receiver = args.at<JSObject>(0);
Handle<JSObject> holder = args.at<JSObject>(1);
Handle<AccessorInfo> info = args.at<AccessorInfo>(2);
Handle<Name> name = args.at<Name>(3);
HandleScope scope(isolate);
DCHECK(info->IsCompatibleReceiver(*receiver));
PropertyCallbackArguments custom_args(isolate, info->data(), *receiver,
*holder, kThrowOnError);
Handle<Object> result = custom_args.CallAccessorGetter(info, name);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
if (result.is_null()) return ReadOnlyRoots(isolate).undefined_value();
return *result;
}
RUNTIME_FUNCTION(Runtime_LoadAccessorProperty) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 3);
Handle<JSObject> receiver = args.at<JSObject>(0);
int handler_kind = args.smi_at(1);
Handle<CallHandlerInfo> call_handler_info = args.at<CallHandlerInfo>(2);
Object* holder = *receiver;
if (handler_kind == LoadHandler::kApiGetterHolderIsPrototype) {
holder = receiver->map()->prototype();
} else {
DCHECK_EQ(handler_kind, LoadHandler::kApiGetter);
}
// Call the accessor without additional arguments.
FunctionCallbackArguments custom(isolate, call_handler_info->data(),
*receiver, holder, nullptr, nullptr, 0);
Handle<Object> result_handle = custom.Call(*call_handler_info);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
if (result_handle.is_null()) return ReadOnlyRoots(isolate).undefined_value();
return *result_handle;
}
/**
* Loads a property with an interceptor performing post interceptor
......
......@@ -584,6 +584,8 @@ namespace internal {
F(KeyedLoadIC_Miss, 4, 1) \
F(KeyedStoreIC_Miss, 5, 1) \
F(KeyedStoreIC_Slow, 5, 1) \
F(LoadAccessorProperty, 4, 1) \
F(LoadCallbackProperty, 4, 1) \
F(LoadElementWithInterceptor, 2, 1) \
F(LoadGlobalIC_Miss, 3, 1) \
F(LoadGlobalIC_Slow, 3, 1) \
......
......@@ -571,7 +571,7 @@ TEST_F(RuntimeCallStatsTest, BasicJavaScript) {
{
NativeTimeScope native_timer_scope;
RunJS("function f() { return 1; }");
RunJS("function f() { return 1; };");
}
EXPECT_EQ(1, counter->count());
int64_t time = counter->time().InMicroseconds();
......@@ -579,7 +579,7 @@ TEST_F(RuntimeCallStatsTest, BasicJavaScript) {
{
NativeTimeScope native_timer_scope;
RunJS("f()");
RunJS("f();");
}
EXPECT_EQ(2, counter->count());
EXPECT_LE(time, counter->time().InMicroseconds());
......@@ -588,38 +588,43 @@ TEST_F(RuntimeCallStatsTest, BasicJavaScript) {
TEST_F(RuntimeCallStatsTest, FunctionLengthGetter) {
RuntimeCallCounter* getter_counter =
stats()->GetCounter(RuntimeCallCounterId::kFunctionLengthGetter);
RuntimeCallCounter* js_counter =
stats()->GetCounter(RuntimeCallCounterId::kJS_Execution);
EXPECT_EQ(0, getter_counter->count());
EXPECT_EQ(0, js_counter->count());
EXPECT_EQ(0, js_counter()->count());
EXPECT_EQ(0, getter_counter->time().InMicroseconds());
EXPECT_EQ(0, js_counter->time().InMicroseconds());
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
{
NativeTimeScope native_timer_scope;
RunJS("function f(array) { return array.length; }");
RunJS("function f(array) { return array.length; };");
}
EXPECT_EQ(0, getter_counter->count());
EXPECT_EQ(1, js_counter->count());
EXPECT_EQ(1, js_counter()->count());
EXPECT_EQ(0, getter_counter->time().InMicroseconds());
int64_t js_time = js_counter->time().InMicroseconds();
int64_t js_time = js_counter()->time().InMicroseconds();
EXPECT_LT(0, js_time);
{
NativeTimeScope native_timer_scope;
RunJS("f.length");
RunJS("f.length;");
}
EXPECT_EQ(1, getter_counter->count());
EXPECT_EQ(2, js_counter->count());
EXPECT_EQ(2, js_counter()->count());
EXPECT_LE(0, getter_counter->time().InMicroseconds());
EXPECT_LE(js_time, js_counter->time().InMicroseconds());
EXPECT_LE(js_time, js_counter()->time().InMicroseconds());
{
NativeTimeScope native_timer_scope;
RunJS("for (let i = 0; i < 50; i++) { f.length }");
RunJS("for (let i = 0; i < 50; i++) { f.length };");
}
EXPECT_EQ(51, getter_counter->count());
EXPECT_EQ(3, js_counter->count());
EXPECT_EQ(3, js_counter()->count());
{
NativeTimeScope native_timer_scope;
RunJS("for (let i = 0; i < 1000; i++) { f.length; };");
}
EXPECT_EQ(1051, getter_counter->count());
EXPECT_EQ(4, js_counter()->count());
}
namespace {
......@@ -632,7 +637,10 @@ static void CustomCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
}
} // namespace
TEST_F(RuntimeCallStatsTest, CustomCallback) {
TEST_F(RuntimeCallStatsTest, CallbackFunction) {
RuntimeCallCounter* callback_counter =
stats()->GetCounter(RuntimeCallCounterId::kFunctionCallback);
current_test = this;
// Set up a function template with a custom callback.
v8::Isolate* isolate = v8_isolate();
......@@ -646,9 +654,9 @@ TEST_F(RuntimeCallStatsTest, CustomCallback) {
object_template->NewInstance(v8_context()).ToLocalChecked();
SetGlobalProperty("custom_object", object);
// TODO(cbruni): Check api accessor timer (one above the custom callback).
EXPECT_EQ(0, js_counter()->count());
EXPECT_EQ(0, counter()->count());
EXPECT_EQ(0, callback_counter->count());
EXPECT_EQ(0, counter2()->count());
{
RuntimeCallTimerScope scope(stats(), counter_id());
......@@ -656,29 +664,105 @@ TEST_F(RuntimeCallStatsTest, CustomCallback) {
RunJS("custom_object.callback();");
}
EXPECT_EQ(1, js_counter()->count());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(1, callback_counter->count());
EXPECT_EQ(1, counter2()->count());
// Given that no native timers are used, only the two scopes explitly
// mentioned above will track the time.
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(0, callback_counter->time().InMicroseconds());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(1, counter2()->count());
EXPECT_EQ(kCustomCallbackTime, counter2()->time().InMicroseconds());
RunJS("for (let i = 0; i < 9; i++) { custom_object.callback() };");
RunJS("for (let i = 0; i < 9; i++) { custom_object.callback(); };");
EXPECT_EQ(2, js_counter()->count());
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(10, callback_counter->count());
EXPECT_EQ(10, counter2()->count());
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(0, callback_counter->time().InMicroseconds());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(kCustomCallbackTime * 10, counter2()->time().InMicroseconds());
RunJS("for (let i = 0; i < 4000; i++) { custom_object.callback() };");
RunJS("for (let i = 0; i < 4000; i++) { custom_object.callback(); };");
EXPECT_EQ(3, js_counter()->count());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(4010, callback_counter->count());
EXPECT_EQ(4010, counter2()->count());
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(0, callback_counter->time().InMicroseconds());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(kCustomCallbackTime * 4010, counter2()->time().InMicroseconds());
}
TEST_F(RuntimeCallStatsTest, ApiGetter) {
RuntimeCallCounter* callback_counter =
stats()->GetCounter(RuntimeCallCounterId::kFunctionCallback);
current_test = this;
// Set up a function template with an api accessor.
v8::Isolate* isolate = v8_isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetAccessorProperty(
NewString("apiGetter"),
v8::FunctionTemplate::New(isolate, CustomCallback));
v8::Local<v8::Object> object =
object_template->NewInstance(v8_context()).ToLocalChecked();
SetGlobalProperty("custom_object", object);
// TODO(cbruni): Check api accessor timer (one above the custom callback).
EXPECT_EQ(0, js_counter()->count());
EXPECT_EQ(0, counter()->count());
EXPECT_EQ(0, callback_counter->count());
EXPECT_EQ(0, counter2()->count());
{
RuntimeCallTimerScope scope(stats(), counter_id());
Sleep(100);
RunJS("custom_object.apiGetter;");
}
PrintStats();
EXPECT_EQ(1, js_counter()->count());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(1, callback_counter->count());
EXPECT_EQ(1, counter2()->count());
// Given that no native timers are used, only the two scopes explitly
// mentioned above will track the time.
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(0, callback_counter->time().InMicroseconds());
EXPECT_EQ(kCustomCallbackTime, counter2()->time().InMicroseconds());
RunJS("for (let i = 0; i < 9; i++) { custom_object.apiGetter };");
PrintStats();
EXPECT_EQ(2, js_counter()->count());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(10, callback_counter->count());
EXPECT_EQ(10, counter2()->count());
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(0, callback_counter->time().InMicroseconds());
EXPECT_EQ(kCustomCallbackTime * 10, counter2()->time().InMicroseconds());
RunJS("for (let i = 0; i < 4000; i++) { custom_object.apiGetter };");
PrintStats();
EXPECT_EQ(3, js_counter()->count());
EXPECT_EQ(1, counter()->count());
EXPECT_EQ(4010, callback_counter->count());
EXPECT_EQ(4010, counter2()->count());
EXPECT_EQ(0, js_counter()->time().InMicroseconds());
EXPECT_EQ(100, counter()->time().InMicroseconds());
EXPECT_EQ(0, callback_counter->time().InMicroseconds());
EXPECT_EQ(kCustomCallbackTime * 4010, counter2()->time().InMicroseconds());
PrintStats();
}
} // namespace internal
......
......@@ -67,17 +67,19 @@ Local<Value> TestWithIsolate::RunJS(const char* source) {
TestWithContext::TestWithContext()
: context_(Context::New(isolate())), context_scope_(context_) {}
TestWithContext::~TestWithContext() {}
v8::Local<v8::String> TestWithContext::NewString(const char* string) {
return v8::String::NewFromUtf8(v8_isolate(), string,
v8::NewStringType::kNormal)
.ToLocalChecked();
}
void TestWithContext::SetGlobalProperty(const char* name,
v8::Local<v8::Value> value) {
v8::Local<v8::String> property_name =
v8::String::NewFromUtf8(v8_isolate(), name, v8::NewStringType::kNormal)
.ToLocalChecked();
CHECK(v8_context()
->Global()
->Set(v8_context(), property_name, value)
->Set(v8_context(), NewString(name), value)
.FromJust());
}
......
......@@ -60,6 +60,7 @@ class TestWithContext : public virtual v8::TestWithIsolate {
const Local<Context>& context() const { return v8_context(); }
const Local<Context>& v8_context() const { return context_; }
v8::Local<v8::String> NewString(const char* string);
void SetGlobalProperty(const char* name, v8::Local<v8::Value> value);
private:
......
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