8439401d2d
We used to intercept function definitions, but not declarations. GenericNamedPropertySetterCallback now also intercepts function declarations. For definitions, we call DeclareGlobal and then InitializeVarGlobal. For declarations, we never call InitializeVarGlobal, thus we must check for interceptors in DeclareGlobal. If the semantics of a redeclaration are wrong, e.g., redeclaring a read-only property, an exception is thrown independent of whether an interceptor is installed. Usually, i.e., not during a declaration, we only throw if the call is not successfully intercepted. BUG=v8:5375 Review-Url: https://codereview.chromium.org/2334733002 Cr-Commit-Position: refs/heads/master@{#39450}
4860 lines
167 KiB
C++
4860 lines
167 KiB
C++
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "test/cctest/test-api.h"
|
|
|
|
#include "include/v8-util.h"
|
|
#include "src/api.h"
|
|
#include "src/arguments.h"
|
|
#include "src/base/platform/platform.h"
|
|
#include "src/compilation-cache.h"
|
|
#include "src/execution.h"
|
|
#include "src/objects.h"
|
|
#include "src/unicode-inl.h"
|
|
#include "src/utils.h"
|
|
|
|
using ::v8::Boolean;
|
|
using ::v8::BooleanObject;
|
|
using ::v8::Context;
|
|
using ::v8::Extension;
|
|
using ::v8::Function;
|
|
using ::v8::FunctionTemplate;
|
|
using ::v8::HandleScope;
|
|
using ::v8::Local;
|
|
using ::v8::Name;
|
|
using ::v8::Message;
|
|
using ::v8::MessageCallback;
|
|
using ::v8::Object;
|
|
using ::v8::ObjectTemplate;
|
|
using ::v8::Persistent;
|
|
using ::v8::Script;
|
|
using ::v8::StackTrace;
|
|
using ::v8::String;
|
|
using ::v8::Symbol;
|
|
using ::v8::TryCatch;
|
|
using ::v8::Undefined;
|
|
using ::v8::UniqueId;
|
|
using ::v8::V8;
|
|
using ::v8::Value;
|
|
|
|
|
|
namespace {
|
|
|
|
void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
info.GetReturnValue().Set(42);
|
|
}
|
|
|
|
void Return239Callback(Local<String> name,
|
|
const v8::PropertyCallbackInfo<Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CheckReturnValue(info, FUNCTION_ADDR(Return239Callback));
|
|
info.GetReturnValue().Set(v8_str("bad value"));
|
|
info.GetReturnValue().Set(v8_num(239));
|
|
}
|
|
|
|
|
|
void EmptyInterceptorGetter(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {}
|
|
|
|
|
|
void EmptyInterceptorSetter(Local<Name> name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {}
|
|
|
|
|
|
void SimpleAccessorGetter(Local<String> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
Local<Object> self = Local<Object>::Cast(info.This());
|
|
info.GetReturnValue().Set(self->Get(info.GetIsolate()->GetCurrentContext(),
|
|
String::Concat(v8_str("accessor_"), name))
|
|
.ToLocalChecked());
|
|
}
|
|
|
|
void SimpleAccessorSetter(Local<String> name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<void>& info) {
|
|
Local<Object> self = Local<Object>::Cast(info.This());
|
|
self->Set(info.GetIsolate()->GetCurrentContext(),
|
|
String::Concat(v8_str("accessor_"), name), value)
|
|
.FromJust();
|
|
}
|
|
|
|
|
|
void SymbolAccessorGetter(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(name->IsSymbol());
|
|
Local<Symbol> sym = Local<Symbol>::Cast(name);
|
|
if (sym->Name()->IsUndefined()) return;
|
|
SimpleAccessorGetter(Local<String>::Cast(sym->Name()), info);
|
|
}
|
|
|
|
void SymbolAccessorSetter(Local<Name> name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<void>& info) {
|
|
CHECK(name->IsSymbol());
|
|
Local<Symbol> sym = Local<Symbol>::Cast(name);
|
|
if (sym->Name()->IsUndefined()) return;
|
|
SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info);
|
|
}
|
|
|
|
void StringInterceptorGetter(
|
|
Local<String> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>&
|
|
info) { // Intercept names that start with 'interceptor_'.
|
|
String::Utf8Value utf8(name);
|
|
char* name_str = *utf8;
|
|
char prefix[] = "interceptor_";
|
|
int i;
|
|
for (i = 0; name_str[i] && prefix[i]; ++i) {
|
|
if (name_str[i] != prefix[i]) return;
|
|
}
|
|
Local<Object> self = Local<Object>::Cast(info.This());
|
|
info.GetReturnValue().Set(
|
|
self->GetPrivate(
|
|
info.GetIsolate()->GetCurrentContext(),
|
|
v8::Private::ForApi(info.GetIsolate(), v8_str(name_str + i)))
|
|
.ToLocalChecked());
|
|
}
|
|
|
|
|
|
void StringInterceptorSetter(Local<String> name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Intercept accesses that set certain integer values, for which the name does
|
|
// not start with 'accessor_'.
|
|
String::Utf8Value utf8(name);
|
|
char* name_str = *utf8;
|
|
char prefix[] = "accessor_";
|
|
int i;
|
|
for (i = 0; name_str[i] && prefix[i]; ++i) {
|
|
if (name_str[i] != prefix[i]) break;
|
|
}
|
|
if (!prefix[i]) return;
|
|
|
|
Local<Context> context = info.GetIsolate()->GetCurrentContext();
|
|
if (value->IsInt32() && value->Int32Value(context).FromJust() < 10000) {
|
|
Local<Object> self = Local<Object>::Cast(info.This());
|
|
Local<v8::Private> symbol = v8::Private::ForApi(info.GetIsolate(), name);
|
|
self->SetPrivate(context, symbol, value).FromJust();
|
|
info.GetReturnValue().Set(value);
|
|
}
|
|
}
|
|
|
|
void InterceptorGetter(Local<Name> generic_name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
if (generic_name->IsSymbol()) return;
|
|
StringInterceptorGetter(Local<String>::Cast(generic_name), info);
|
|
}
|
|
|
|
void InterceptorSetter(Local<Name> generic_name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
if (generic_name->IsSymbol()) return;
|
|
StringInterceptorSetter(Local<String>::Cast(generic_name), value, info);
|
|
}
|
|
|
|
void GenericInterceptorGetter(Local<Name> generic_name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
Local<String> str;
|
|
if (generic_name->IsSymbol()) {
|
|
Local<Value> name = Local<Symbol>::Cast(generic_name)->Name();
|
|
if (name->IsUndefined()) return;
|
|
str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name));
|
|
} else {
|
|
Local<String> name = Local<String>::Cast(generic_name);
|
|
String::Utf8Value utf8(name);
|
|
char* name_str = *utf8;
|
|
if (*name_str == '_') return;
|
|
str = String::Concat(v8_str("_str_"), name);
|
|
}
|
|
|
|
Local<Object> self = Local<Object>::Cast(info.This());
|
|
info.GetReturnValue().Set(
|
|
self->Get(info.GetIsolate()->GetCurrentContext(), str).ToLocalChecked());
|
|
}
|
|
|
|
void GenericInterceptorSetter(Local<Name> generic_name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
Local<String> str;
|
|
if (generic_name->IsSymbol()) {
|
|
Local<Value> name = Local<Symbol>::Cast(generic_name)->Name();
|
|
if (name->IsUndefined()) return;
|
|
str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name));
|
|
} else {
|
|
Local<String> name = Local<String>::Cast(generic_name);
|
|
String::Utf8Value utf8(name);
|
|
char* name_str = *utf8;
|
|
if (*name_str == '_') return;
|
|
str = String::Concat(v8_str("_str_"), name);
|
|
}
|
|
|
|
Local<Object> self = Local<Object>::Cast(info.This());
|
|
self->Set(info.GetIsolate()->GetCurrentContext(), str, value).FromJust();
|
|
info.GetReturnValue().Set(value);
|
|
}
|
|
|
|
void AddAccessor(Local<FunctionTemplate> templ, Local<String> name,
|
|
v8::AccessorGetterCallback getter,
|
|
v8::AccessorSetterCallback setter) {
|
|
templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
|
|
}
|
|
|
|
void AddInterceptor(Local<FunctionTemplate> templ,
|
|
v8::NamedPropertyGetterCallback getter,
|
|
v8::NamedPropertySetterCallback setter) {
|
|
templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter);
|
|
}
|
|
|
|
|
|
void AddAccessor(Local<FunctionTemplate> templ, Local<Name> name,
|
|
v8::AccessorNameGetterCallback getter,
|
|
v8::AccessorNameSetterCallback setter) {
|
|
templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
|
|
}
|
|
|
|
void AddInterceptor(Local<FunctionTemplate> templ,
|
|
v8::GenericNamedPropertyGetterCallback getter,
|
|
v8::GenericNamedPropertySetterCallback setter) {
|
|
templ->InstanceTemplate()->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(getter, setter));
|
|
}
|
|
|
|
|
|
v8::Local<v8::Object> bottom;
|
|
|
|
void CheckThisIndexedPropertyHandler(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisNamedPropertyHandler(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisIndexedPropertyDefiner(
|
|
uint32_t index, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDefiner));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisNamedPropertyDefiner(
|
|
Local<Name> property, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDefiner));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisIndexedPropertySetter(
|
|
uint32_t index, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisIndexedPropertyDescriptor(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDescriptor));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisNamedPropertyDescriptor(
|
|
Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDescriptor));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisNamedPropertySetter(
|
|
Local<Name> property, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
void CheckThisIndexedPropertyQuery(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
void CheckThisNamedPropertyQuery(
|
|
Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
void CheckThisIndexedPropertyDeleter(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
void CheckThisNamedPropertyDeleter(
|
|
Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
void CheckThisIndexedPropertyEnumerator(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
void CheckThisNamedPropertyEnumerator(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator));
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.This()
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
int echo_named_call_count;
|
|
|
|
|
|
void EchoNamedProperty(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(v8_str("data")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), info.Data())
|
|
.FromJust());
|
|
echo_named_call_count++;
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
|
|
void InterceptorHasOwnPropertyGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
}
|
|
|
|
void InterceptorHasOwnPropertyGetterGC(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int query_counter_int = 0;
|
|
|
|
namespace {
|
|
void QueryCallback(Local<Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
|
query_counter_int++;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Examples that show when the query callback is triggered.
|
|
THREADED_TEST(QueryInterceptor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(0, 0, QueryCallback));
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
CHECK_EQ(query_counter_int, 0);
|
|
v8::Local<Value> result =
|
|
v8_compile("Object.getOwnPropertyDescriptor(obj, 'x');")
|
|
->Run(env.local())
|
|
.ToLocalChecked();
|
|
CHECK_EQ(query_counter_int, 1);
|
|
CHECK_EQ(v8::PropertyAttribute::None,
|
|
static_cast<v8::PropertyAttribute>(
|
|
result->Int32Value(env.local()).FromJust()));
|
|
|
|
v8_compile("Object.defineProperty(obj, 'not_enum', {value: 17});")
|
|
->Run(env.local())
|
|
.ToLocalChecked();
|
|
CHECK_EQ(query_counter_int, 2);
|
|
|
|
v8_compile(
|
|
"Object.defineProperty(obj, 'enum', {value: 17, enumerable: true, "
|
|
"writable: true});")
|
|
->Run(env.local())
|
|
.ToLocalChecked();
|
|
CHECK_EQ(query_counter_int, 3);
|
|
|
|
CHECK(v8_compile("obj.propertyIsEnumerable('enum');")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK_EQ(query_counter_int, 4);
|
|
|
|
CHECK(!v8_compile("obj.propertyIsEnumerable('not_enum');")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK_EQ(query_counter_int, 5);
|
|
|
|
CHECK(v8_compile("obj.hasOwnProperty('enum');")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK_EQ(query_counter_int, 5);
|
|
|
|
CHECK(v8_compile("obj.hasOwnProperty('not_enum');")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK_EQ(query_counter_int, 5);
|
|
|
|
CHECK(!v8_compile("obj.hasOwnProperty('x');")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK_EQ(query_counter_int, 6);
|
|
|
|
CHECK(!v8_compile("obj.propertyIsEnumerable('undef');")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK_EQ(query_counter_int, 7);
|
|
|
|
v8_compile("Object.defineProperty(obj, 'enum', {value: 42});")
|
|
->Run(env.local())
|
|
.ToLocalChecked();
|
|
CHECK_EQ(query_counter_int, 8);
|
|
|
|
v8_compile("Object.isFrozen('obj.x');")->Run(env.local()).ToLocalChecked();
|
|
CHECK_EQ(query_counter_int, 8);
|
|
}
|
|
|
|
bool get_was_called = false;
|
|
bool set_was_called = false;
|
|
|
|
int set_was_called_counter = 0;
|
|
|
|
namespace {
|
|
void GetterCallback(Local<Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
get_was_called = true;
|
|
}
|
|
|
|
void SetterCallback(Local<Name> property, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
set_was_called = true;
|
|
set_was_called_counter++;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Check that get callback is called in defineProperty with accessor descriptor.
|
|
THREADED_TEST(DefinerCallbackAccessorInterceptor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(GetterCallback, SetterCallback));
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
|
|
get_was_called = false;
|
|
set_was_called = false;
|
|
|
|
v8_compile("Object.defineProperty(obj, 'x', {set: function() {return 17;}});")
|
|
->Run(env.local())
|
|
.ToLocalChecked();
|
|
CHECK_EQ(get_was_called, true);
|
|
CHECK_EQ(set_was_called, false);
|
|
}
|
|
|
|
// Check that set callback is called for function declarations.
|
|
THREADED_TEST(SetterCallbackFunctionDeclarationInterceptor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
|
|
v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
|
|
object_template->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(nullptr, SetterCallback));
|
|
v8::Local<v8::Context> ctx =
|
|
v8::Context::New(CcTest::isolate(), nullptr, object_template);
|
|
|
|
set_was_called_counter = 0;
|
|
|
|
// Declare function.
|
|
v8::Local<v8::String> code = v8_str("function x() {return 42;}; x();");
|
|
CHECK_EQ(42, v8::Script::Compile(ctx, code)
|
|
.ToLocalChecked()
|
|
->Run(ctx)
|
|
.ToLocalChecked()
|
|
->Int32Value(ctx)
|
|
.FromJust());
|
|
CHECK_EQ(set_was_called_counter, 1);
|
|
|
|
// Redeclare function.
|
|
code = v8_str("function x() {return 43;}; x();");
|
|
CHECK_EQ(43, v8::Script::Compile(ctx, code)
|
|
.ToLocalChecked()
|
|
->Run(ctx)
|
|
.ToLocalChecked()
|
|
->Int32Value(ctx)
|
|
.FromJust());
|
|
CHECK_EQ(set_was_called_counter, 2);
|
|
|
|
// Redefine function.
|
|
code = v8_str("x = function() {return 44;}; x();");
|
|
CHECK_EQ(44, v8::Script::Compile(ctx, code)
|
|
.ToLocalChecked()
|
|
->Run(ctx)
|
|
.ToLocalChecked()
|
|
->Int32Value(ctx)
|
|
.FromJust());
|
|
CHECK_EQ(set_was_called_counter, 3);
|
|
}
|
|
|
|
// Check that function re-declarations throw if they are read-only.
|
|
THREADED_TEST(SetterCallbackFunctionDeclarationInterceptorThrow) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
|
|
v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
|
|
object_template->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(nullptr, SetterCallback));
|
|
v8::Local<v8::Context> ctx =
|
|
v8::Context::New(CcTest::isolate(), nullptr, object_template);
|
|
|
|
set_was_called = false;
|
|
|
|
v8::Local<v8::String> code = v8_str(
|
|
"function x() {return 42;};"
|
|
"Object.defineProperty(this, 'x', {"
|
|
"configurable: false, "
|
|
"writable: false});"
|
|
"x();");
|
|
CHECK_EQ(42, v8::Script::Compile(ctx, code)
|
|
.ToLocalChecked()
|
|
->Run(ctx)
|
|
.ToLocalChecked()
|
|
->Int32Value(ctx)
|
|
.FromJust());
|
|
|
|
CHECK_EQ(set_was_called, true);
|
|
|
|
v8::TryCatch try_catch(CcTest::isolate());
|
|
set_was_called = false;
|
|
|
|
// Redeclare function that is read-only.
|
|
code = v8_str("function x() {return 43;};");
|
|
CHECK(v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty());
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
CHECK_EQ(set_was_called, false);
|
|
}
|
|
|
|
bool get_was_called_in_order = false;
|
|
bool define_was_called_in_order = false;
|
|
|
|
namespace {
|
|
|
|
void GetterCallbackOrder(Local<Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
get_was_called_in_order = true;
|
|
CHECK_EQ(define_was_called_in_order, true);
|
|
info.GetReturnValue().Set(property);
|
|
}
|
|
|
|
void DefinerCallbackOrder(Local<Name> property,
|
|
const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK_EQ(get_was_called_in_order, false); // Define called before get.
|
|
define_was_called_in_order = true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Check that definer callback is called before getter callback.
|
|
THREADED_TEST(DefinerCallbackGetAndDefine) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
GetterCallbackOrder, SetterCallback, 0, 0, 0, DefinerCallbackOrder));
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
|
|
CHECK_EQ(get_was_called_in_order, false);
|
|
CHECK_EQ(define_was_called_in_order, false);
|
|
|
|
v8_compile("Object.defineProperty(obj, 'x', {set: function() {return 17;}});")
|
|
->Run(env.local())
|
|
.ToLocalChecked();
|
|
CHECK_EQ(get_was_called_in_order, true);
|
|
CHECK_EQ(define_was_called_in_order, true);
|
|
}
|
|
|
|
THREADED_TEST(InterceptorHasOwnProperty) {
|
|
LocalContext context;
|
|
v8::Isolate* isolate = context->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
|
|
Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
|
|
instance_templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter));
|
|
Local<Function> function =
|
|
fun_templ->GetFunction(context.local()).ToLocalChecked();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("constructor"), function)
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"var o = new constructor();"
|
|
"o.hasOwnProperty('ostehaps');");
|
|
CHECK_EQ(false, value->BooleanValue(context.local()).FromJust());
|
|
value = CompileRun(
|
|
"o.ostehaps = 42;"
|
|
"o.hasOwnProperty('ostehaps');");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
value = CompileRun(
|
|
"var p = new constructor();"
|
|
"p.hasOwnProperty('ostehaps');");
|
|
CHECK_EQ(false, value->BooleanValue(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
|
|
LocalContext context;
|
|
v8::Isolate* isolate = context->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
|
|
Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
|
|
instance_templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC));
|
|
Local<Function> function =
|
|
fun_templ->GetFunction(context.local()).ToLocalChecked();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("constructor"), function)
|
|
.FromJust();
|
|
// Let's first make some stuff so we can be sure to get a good GC.
|
|
CompileRun(
|
|
"function makestr(size) {"
|
|
" switch (size) {"
|
|
" case 1: return 'f';"
|
|
" case 2: return 'fo';"
|
|
" case 3: return 'foo';"
|
|
" }"
|
|
" return makestr(size >> 1) + makestr((size + 1) >> 1);"
|
|
"}"
|
|
"var x = makestr(12345);"
|
|
"x = makestr(31415);"
|
|
"x = makestr(23456);");
|
|
v8::Local<Value> value = CompileRun(
|
|
"var o = new constructor();"
|
|
"o.__proto__ = new String(x);"
|
|
"o.hasOwnProperty('ostehaps');");
|
|
CHECK_EQ(false, value->BooleanValue(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static void CheckInterceptorLoadIC(
|
|
v8::GenericNamedPropertyGetterCallback getter, const char* source,
|
|
int expected) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(getter, 0, 0, 0, 0,
|
|
v8_str("data")));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(source);
|
|
CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static void InterceptorLoadICGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
CHECK_EQ(isolate, info.GetIsolate());
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
CHECK(v8_str("data")->Equals(context, info.Data()).FromJust());
|
|
CHECK(v8_str("x")->Equals(context, name).FromJust());
|
|
info.GetReturnValue().Set(v8::Integer::New(isolate, 42));
|
|
}
|
|
|
|
|
|
// This test should hit the load IC for the interceptor case.
|
|
THREADED_TEST(InterceptorLoadIC) {
|
|
CheckInterceptorLoadIC(InterceptorLoadICGetter,
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.x;"
|
|
"}",
|
|
42);
|
|
}
|
|
|
|
|
|
// Below go several tests which verify that JITing for various
|
|
// configurations of interceptor and explicit fields works fine
|
|
// (those cases are special cased to get better performance).
|
|
|
|
static void InterceptorLoadXICGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
info.GetReturnValue().Set(
|
|
v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust()
|
|
? v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42))
|
|
: v8::Local<v8::Value>());
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
|
|
CheckInterceptorLoadIC(InterceptorLoadXICGetter,
|
|
"var result = 0;"
|
|
"o.y = 239;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.y;"
|
|
"}",
|
|
239);
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
|
|
CheckInterceptorLoadIC(InterceptorLoadXICGetter,
|
|
"var result = 0;"
|
|
"o.__proto__ = { 'y': 239 };"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.y + o.x;"
|
|
"}",
|
|
239 + 42);
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
|
|
CheckInterceptorLoadIC(InterceptorLoadXICGetter,
|
|
"var result = 0;"
|
|
"o.__proto__.y = 239;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.y + o.x;"
|
|
"}",
|
|
239 + 42);
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICUndefined) {
|
|
CheckInterceptorLoadIC(InterceptorLoadXICGetter,
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = (o.y == undefined) ? 239 : 42;"
|
|
"}",
|
|
239);
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICWithOverride) {
|
|
CheckInterceptorLoadIC(InterceptorLoadXICGetter,
|
|
"fst = new Object(); fst.__proto__ = o;"
|
|
"snd = new Object(); snd.__proto__ = fst;"
|
|
"var result1 = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result1 = snd.x;"
|
|
"}"
|
|
"fst.x = 239;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = snd.x;"
|
|
"}"
|
|
"result + result1",
|
|
239 + 42);
|
|
}
|
|
|
|
|
|
// Test the case when we stored field into
|
|
// a stub, but interceptor produced value on its own.
|
|
THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
|
|
CheckInterceptorLoadIC(
|
|
InterceptorLoadXICGetter,
|
|
"proto = new Object();"
|
|
"o.__proto__ = proto;"
|
|
"proto.x = 239;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" o.x;"
|
|
// Now it should be ICed and keep a reference to x defined on proto
|
|
"}"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result += o.x;"
|
|
"}"
|
|
"result;",
|
|
42 * 1000);
|
|
}
|
|
|
|
|
|
// Test the case when we stored field into
|
|
// a stub, but it got invalidated later on.
|
|
THREADED_TEST(InterceptorLoadICInvalidatedField) {
|
|
CheckInterceptorLoadIC(
|
|
InterceptorLoadXICGetter,
|
|
"proto1 = new Object();"
|
|
"proto2 = new Object();"
|
|
"o.__proto__ = proto1;"
|
|
"proto1.__proto__ = proto2;"
|
|
"proto2.y = 239;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" o.y;"
|
|
// Now it should be ICed and keep a reference to y defined on proto2
|
|
"}"
|
|
"proto1.y = 42;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result += o.y;"
|
|
"}"
|
|
"result;",
|
|
42 * 1000);
|
|
}
|
|
|
|
|
|
static int interceptor_load_not_handled_calls = 0;
|
|
static void InterceptorLoadNotHandled(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
++interceptor_load_not_handled_calls;
|
|
}
|
|
|
|
|
|
// Test how post-interceptor lookups are done in the non-cacheable
|
|
// case: the interceptor should not be invoked during this lookup.
|
|
THREADED_TEST(InterceptorLoadICPostInterceptor) {
|
|
interceptor_load_not_handled_calls = 0;
|
|
CheckInterceptorLoadIC(InterceptorLoadNotHandled,
|
|
"receiver = new Object();"
|
|
"receiver.__proto__ = o;"
|
|
"proto = new Object();"
|
|
"/* Make proto a slow-case object. */"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" proto[\"xxxxxxxx\" + i] = [];"
|
|
"}"
|
|
"proto.x = 17;"
|
|
"o.__proto__ = proto;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result += receiver.x;"
|
|
"}"
|
|
"result;",
|
|
17 * 1000);
|
|
CHECK_EQ(1000, interceptor_load_not_handled_calls);
|
|
}
|
|
|
|
|
|
// Test the case when we stored field into
|
|
// a stub, but it got invalidated later on due to override on
|
|
// global object which is between interceptor and fields' holders.
|
|
THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
|
|
CheckInterceptorLoadIC(
|
|
InterceptorLoadXICGetter,
|
|
"o.__proto__ = this;" // set a global to be a proto of o.
|
|
"this.__proto__.y = 239;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (o.y != 239) throw 'oops: ' + o.y;"
|
|
// Now it should be ICed and keep a reference to y defined on
|
|
// field_holder.
|
|
"}"
|
|
"this.y = 42;" // Assign on a global.
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" result += o.y;"
|
|
"}"
|
|
"result;",
|
|
42 * 10);
|
|
}
|
|
|
|
|
|
static void SetOnThis(Local<String> name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<void>& info) {
|
|
Local<Object>::Cast(info.This())
|
|
->CreateDataProperty(info.GetIsolate()->GetCurrentContext(), name, value)
|
|
.FromJust();
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
templ->SetAccessor(v8_str("y"), Return239Callback);
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
// Check the case when receiver and interceptor's holder
|
|
// are the same objects.
|
|
v8::Local<Value> value = CompileRun(
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result = o.y;"
|
|
"}");
|
|
CHECK_EQ(239, value->Int32Value(context.local()).FromJust());
|
|
|
|
// Check the case when interceptor's holder is in proto chain
|
|
// of receiver.
|
|
value = CompileRun(
|
|
"r = { __proto__: o };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result = r.y;"
|
|
"}");
|
|
CHECK_EQ(239, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
|
templ_p->SetAccessor(v8_str("y"), Return239Callback);
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("p"),
|
|
templ_p->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
// Check the case when receiver and interceptor's holder
|
|
// are the same objects.
|
|
v8::Local<Value> value = CompileRun(
|
|
"o.__proto__ = p;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result = o.x + o.y;"
|
|
"}");
|
|
CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
|
|
|
|
// Check the case when interceptor's holder is in proto chain
|
|
// of receiver.
|
|
value = CompileRun(
|
|
"r = { __proto__: o };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result = r.x + r.y;"
|
|
"}");
|
|
CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
templ->SetAccessor(v8_str("y"), Return239Callback);
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<Value> value = CompileRun(
|
|
"fst = new Object(); fst.__proto__ = o;"
|
|
"snd = new Object(); snd.__proto__ = fst;"
|
|
"var result1 = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result1 = snd.x;"
|
|
"}"
|
|
"fst.x = 239;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result = snd.x;"
|
|
"}"
|
|
"result + result1");
|
|
CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored callback into
|
|
// a stub, but interceptor produced value on its own.
|
|
THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
|
templ_p->SetAccessor(v8_str("y"), Return239Callback);
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("p"),
|
|
templ_p->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<Value> value = CompileRun(
|
|
"o.__proto__ = p;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" o.x;"
|
|
// Now it should be ICed and keep a reference to x defined on p
|
|
"}"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result += o.x;"
|
|
"}"
|
|
"result");
|
|
CHECK_EQ(42 * 7, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored callback into
|
|
// a stub, but it got invalidated later on.
|
|
THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
|
templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("p"),
|
|
templ_p->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<Value> value = CompileRun(
|
|
"inbetween = new Object();"
|
|
"o.__proto__ = inbetween;"
|
|
"inbetween.__proto__ = p;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" o.y;"
|
|
// Now it should be ICed and keep a reference to y defined on p
|
|
"}"
|
|
"inbetween.y = 42;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" result += o.y;"
|
|
"}"
|
|
"result");
|
|
CHECK_EQ(42 * 10, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored callback into
|
|
// a stub, but it got invalidated later on due to override on
|
|
// global object which is between interceptor and callbacks' holders.
|
|
THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
|
templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("p"),
|
|
templ_p->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<Value> value = CompileRun(
|
|
"o.__proto__ = this;"
|
|
"this.__proto__ = p;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (o.y != 239) throw 'oops: ' + o.y;"
|
|
// Now it should be ICed and keep a reference to y defined on p
|
|
"}"
|
|
"this.y = 42;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" result += o.y;"
|
|
"}"
|
|
"result");
|
|
CHECK_EQ(42 * 10, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
// Test load of a non-existing global when a global object has an interceptor.
|
|
THREADED_TEST(InterceptorLoadGlobalICGlobalWithInterceptor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_global = v8::ObjectTemplate::New(isolate);
|
|
templ_global->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
EmptyInterceptorGetter, EmptyInterceptorSetter));
|
|
|
|
LocalContext context(nullptr, templ_global);
|
|
i::Handle<i::JSReceiver> global_proxy =
|
|
v8::Utils::OpenHandle<Object, i::JSReceiver>(context->Global());
|
|
CHECK(global_proxy->IsJSGlobalProxy());
|
|
i::Handle<i::JSGlobalObject> global(
|
|
i::JSGlobalObject::cast(global_proxy->map()->prototype()));
|
|
CHECK(global->map()->has_named_interceptor());
|
|
|
|
v8::Local<Value> value = CompileRun(
|
|
"var f = function() { "
|
|
" try {"
|
|
" x1;"
|
|
" } catch(e) {"
|
|
" }"
|
|
" return typeof x1 === 'undefined';"
|
|
"};"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" f();"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
|
|
value = CompileRun(
|
|
"var f = function() { "
|
|
" try {"
|
|
" x2;"
|
|
" return false;"
|
|
" } catch(e) {"
|
|
" return true;"
|
|
" }"
|
|
"};"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" f();"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
|
|
value = CompileRun(
|
|
"var f = function() { "
|
|
" try {"
|
|
" typeof(x3);"
|
|
" return true;"
|
|
" } catch(e) {"
|
|
" return false;"
|
|
" }"
|
|
"};"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" f();"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
}
|
|
|
|
static void InterceptorLoadICGetter0(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust());
|
|
info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0));
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorReturningZero) {
|
|
CheckInterceptorLoadIC(InterceptorLoadICGetter0, "o.x == undefined ? 1 : 0",
|
|
0);
|
|
}
|
|
|
|
|
|
static void InterceptorStoreICSetter(
|
|
Local<Name> key, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
|
CHECK(v8_str("x")->Equals(context, key).FromJust());
|
|
CHECK_EQ(42, value->Int32Value(context).FromJust());
|
|
info.GetReturnValue().Set(value);
|
|
}
|
|
|
|
|
|
// This test should hit the store IC for the interceptor case.
|
|
THREADED_TEST(InterceptorStoreIC) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
InterceptorLoadICGetter, InterceptorStoreICSetter, 0, 0, 0,
|
|
v8_str("data")));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" o.x = 42;"
|
|
"}");
|
|
}
|
|
|
|
|
|
THREADED_TEST(InterceptorStoreICWithNoSetter) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" o.y = 239;"
|
|
"}"
|
|
"42 + o.y");
|
|
CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
|
child->Inherit(parent);
|
|
AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
|
|
SimpleAccessorSetter);
|
|
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"child.age = 10;");
|
|
ExpectBoolean("child.hasOwnProperty('age')", false);
|
|
ExpectInt32("child.age", 10);
|
|
ExpectInt32("child.accessor_age", 10);
|
|
}
|
|
|
|
|
|
THREADED_TEST(LegacyInterceptorDoesNotSeeSymbols) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
|
v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
|
|
|
|
child->Inherit(parent);
|
|
AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
|
|
AddInterceptor(child, StringInterceptorGetter, StringInterceptorSetter);
|
|
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
env->Global()->Set(env.local(), v8_str("age"), age).FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"child[age] = 10;");
|
|
ExpectInt32("child[age]", 10);
|
|
ExpectBoolean("child.hasOwnProperty('age')", false);
|
|
ExpectBoolean("child.hasOwnProperty('accessor_age')", true);
|
|
}
|
|
|
|
|
|
THREADED_TEST(GenericInterceptorDoesSeeSymbols) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
|
v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
|
|
v8::Local<v8::Symbol> anon = v8::Symbol::New(isolate);
|
|
|
|
child->Inherit(parent);
|
|
AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
|
|
AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter);
|
|
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
env->Global()->Set(env.local(), v8_str("age"), age).FromJust();
|
|
env->Global()->Set(env.local(), v8_str("anon"), anon).FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"child[age] = 10;");
|
|
ExpectInt32("child[age]", 10);
|
|
ExpectInt32("child._sym_age", 10);
|
|
|
|
// Check that it also sees strings.
|
|
CompileRun("child.foo = 47");
|
|
ExpectInt32("child.foo", 47);
|
|
ExpectInt32("child._str_foo", 47);
|
|
|
|
// Check that the interceptor can punt (in this case, on anonymous symbols).
|
|
CompileRun("child[anon] = 31337");
|
|
ExpectInt32("child[anon]", 31337);
|
|
}
|
|
|
|
|
|
THREADED_TEST(NamedPropertyHandlerGetter) {
|
|
echo_named_call_count = 0;
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
EchoNamedProperty, 0, 0, 0, 0, v8_str("data")));
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
CHECK_EQ(echo_named_call_count, 0);
|
|
v8_compile("obj.x")->Run(env.local()).ToLocalChecked();
|
|
CHECK_EQ(echo_named_call_count, 1);
|
|
const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
|
|
v8::Local<Value> str = CompileRun(code);
|
|
String::Utf8Value value(str);
|
|
CHECK_EQ(0, strcmp(*value, "oddlepoddle"));
|
|
// Check default behavior
|
|
CHECK_EQ(10, v8_compile("obj.flob = 10;")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
CHECK(v8_compile("'myProperty' in obj")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
CHECK(v8_compile("delete obj.myProperty")
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
namespace {
|
|
void NotInterceptingPropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Do not intercept by not calling info.GetReturnValue().Set().
|
|
}
|
|
|
|
void InterceptingPropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
|
|
void CheckDescriptorInDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(!desc.has_writable());
|
|
CHECK(!desc.has_value());
|
|
CHECK(!desc.has_enumerable());
|
|
CHECK(desc.has_configurable());
|
|
CHECK(!desc.configurable());
|
|
CHECK(desc.has_get());
|
|
CHECK(desc.get()->IsFunction());
|
|
CHECK(desc.has_set());
|
|
CHECK(desc.set()->IsUndefined());
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
} // namespace
|
|
|
|
THREADED_TEST(PropertyDefinerCallback) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
|
|
{ // Intercept defineProperty()
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, NotInterceptingPropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.defineProperty(obj, 'x', {value: 42});"
|
|
"obj.x;";
|
|
CHECK_EQ(42, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
{ // Intercept defineProperty() for correct accessor descriptor
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckDescriptorInDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.defineProperty(obj, 'x', {"
|
|
"get: function(){ return 42; }, "
|
|
"set: undefined,"
|
|
"configurable: 0"
|
|
"});"
|
|
"obj.x;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
{ // Do not intercept defineProperty()
|
|
v8::Local<v8::FunctionTemplate> templ2 =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ2->InstanceTemplate()->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, InterceptingPropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.defineProperty(obj, 'x', {value: 42});"
|
|
"obj.x;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
void NotInterceptingPropertyDefineCallbackIndexed(
|
|
uint32_t index, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Do not intercept by not calling info.GetReturnValue().Set()
|
|
}
|
|
|
|
void InterceptingPropertyDefineCallbackIndexed(
|
|
uint32_t index, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(index);
|
|
}
|
|
|
|
void CheckDescriptorInDefineCallbackIndexed(
|
|
uint32_t index, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(!desc.has_writable());
|
|
CHECK(!desc.has_value());
|
|
CHECK(desc.has_enumerable());
|
|
CHECK(desc.enumerable());
|
|
CHECK(!desc.has_configurable());
|
|
CHECK(desc.has_get());
|
|
CHECK(desc.get()->IsFunction());
|
|
CHECK(desc.has_set());
|
|
CHECK(desc.set()->IsUndefined());
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(index);
|
|
}
|
|
} // namespace
|
|
|
|
THREADED_TEST(PropertyDefinerCallbackIndexed) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
|
|
{ // Intercept defineProperty()
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, NotInterceptingPropertyDefineCallbackIndexed));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj[2] = 17; "
|
|
"Object.defineProperty(obj, 2, {value: 42});"
|
|
"obj[2];";
|
|
CHECK_EQ(42, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
{ // Intercept defineProperty() for correct accessor descriptor
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckDescriptorInDefineCallbackIndexed));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj[2] = 17; "
|
|
"Object.defineProperty(obj, 2, {"
|
|
"get: function(){ return 42; }, "
|
|
"set: undefined,"
|
|
"enumerable: true"
|
|
"});"
|
|
"obj[2];";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
{ // Do not intercept defineProperty()
|
|
v8::Local<v8::FunctionTemplate> templ2 =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ2->InstanceTemplate()->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, InterceptingPropertyDefineCallbackIndexed));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
|
|
const char* code =
|
|
"obj[2] = 17; "
|
|
"Object.defineProperty(obj, 2, {value: 42});"
|
|
"obj[2];";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
}
|
|
|
|
// Test that freeze() is intercepted.
|
|
THREADED_TEST(PropertyDefinerCallbackForFreeze) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, InterceptingPropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.freeze(obj.x); "
|
|
"Object.isFrozen(obj.x);";
|
|
|
|
CHECK(v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->BooleanValue(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
// Check that the descriptor passed to the callback is enumerable.
|
|
namespace {
|
|
void CheckEnumerablePropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(desc.has_value());
|
|
CHECK_EQ(42, desc.value()
|
|
->Int32Value(info.GetIsolate()->GetCurrentContext())
|
|
.FromJust());
|
|
CHECK(desc.has_enumerable());
|
|
CHECK(desc.enumerable());
|
|
CHECK(!desc.has_writable());
|
|
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
} // namespace
|
|
THREADED_TEST(PropertyDefinerCallbackEnumerable) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckEnumerablePropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.defineProperty(obj, 'x', {value: 42, enumerable: true});"
|
|
"obj.x;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
// Check that the descriptor passed to the callback is configurable.
|
|
namespace {
|
|
void CheckConfigurablePropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(desc.has_value());
|
|
CHECK_EQ(42, desc.value()
|
|
->Int32Value(info.GetIsolate()->GetCurrentContext())
|
|
.FromJust());
|
|
CHECK(desc.has_configurable());
|
|
CHECK(desc.configurable());
|
|
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
} // namespace
|
|
THREADED_TEST(PropertyDefinerCallbackConfigurable) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckConfigurablePropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.defineProperty(obj, 'x', {value: 42, configurable: true});"
|
|
"obj.x;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
// Check that the descriptor passed to the callback is writable.
|
|
namespace {
|
|
void CheckWritablePropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(desc.has_writable());
|
|
CHECK(desc.writable());
|
|
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
} // namespace
|
|
THREADED_TEST(PropertyDefinerCallbackWritable) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckWritablePropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"Object.defineProperty(obj, 'x', {value: 42, writable: true});"
|
|
"obj.x;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
// Check that the descriptor passed to the callback has a getter.
|
|
namespace {
|
|
void CheckGetterPropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(desc.has_get());
|
|
CHECK(!desc.has_set());
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
} // namespace
|
|
THREADED_TEST(PropertyDefinerCallbackWithGetter) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckGetterPropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17;"
|
|
"Object.defineProperty(obj, 'x', {get: function() {return 42;}});"
|
|
"obj.x;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
// Check that the descriptor passed to the callback has a setter.
|
|
namespace {
|
|
void CheckSetterPropertyDefineCallback(
|
|
Local<Name> name, const v8::PropertyDescriptor& desc,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(desc.has_set());
|
|
CHECK(!desc.has_get());
|
|
// intercept the callback by setting a non-empty handle
|
|
info.GetReturnValue().Set(name);
|
|
}
|
|
} // namespace
|
|
THREADED_TEST(PropertyDefinerCallbackWithSetter) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, 0, 0, 0, CheckSetterPropertyDefineCallback));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"Object.defineProperty(obj, 'x', {set: function() {return 42;}});"
|
|
"obj.x = 17;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
namespace {
|
|
void EmptyPropertyDescriptorCallback(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Do not intercept by not calling info.GetReturnValue().Set().
|
|
}
|
|
|
|
void InterceptingPropertyDescriptorCallback(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Intercept the callback by setting a different descriptor.
|
|
const char* code =
|
|
"var desc = {value: 42};"
|
|
"desc;";
|
|
Local<Value> descriptor = v8_compile(code)
|
|
->Run(info.GetIsolate()->GetCurrentContext())
|
|
.ToLocalChecked();
|
|
info.GetReturnValue().Set(descriptor);
|
|
}
|
|
} // namespace
|
|
|
|
THREADED_TEST(PropertyDescriptorCallback) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
LocalContext env;
|
|
|
|
{ // Normal behavior of getOwnPropertyDescriptor() with empty callback.
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, EmptyPropertyDescriptorCallback, 0, 0, 0));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"var desc = Object.getOwnPropertyDescriptor(obj, 'x');"
|
|
"desc.value;";
|
|
CHECK_EQ(17, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
|
|
{ // Intercept getOwnPropertyDescriptor().
|
|
v8::Local<v8::FunctionTemplate> templ =
|
|
v8::FunctionTemplate::New(CcTest::isolate());
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
0, 0, InterceptingPropertyDescriptorCallback, 0, 0, 0));
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
const char* code =
|
|
"obj.x = 17; "
|
|
"var desc = Object.getOwnPropertyDescriptor(obj, 'x');"
|
|
"desc.value;";
|
|
CHECK_EQ(42, v8_compile(code)
|
|
->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust());
|
|
}
|
|
}
|
|
|
|
int echo_indexed_call_count = 0;
|
|
|
|
|
|
static void EchoIndexedProperty(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(v8_num(637)
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), info.Data())
|
|
.FromJust());
|
|
echo_indexed_call_count++;
|
|
info.GetReturnValue().Set(v8_num(index));
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedPropertyHandlerGetter) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
|
|
templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
EchoIndexedProperty, 0, 0, 0, 0, v8_num(637)));
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
Local<Script> script = v8_compile("obj[900]");
|
|
CHECK_EQ(script->Run(env.local())
|
|
.ToLocalChecked()
|
|
->Int32Value(env.local())
|
|
.FromJust(),
|
|
900);
|
|
}
|
|
|
|
|
|
THREADED_TEST(PropertyHandlerInPrototype) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
|
|
templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
|
|
CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter,
|
|
CheckThisIndexedPropertyEnumerator));
|
|
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
|
|
CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter,
|
|
CheckThisNamedPropertyEnumerator));
|
|
|
|
bottom = templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked();
|
|
Local<v8::Object> top = templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked();
|
|
Local<v8::Object> middle = templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked();
|
|
|
|
bottom->SetPrototype(env.local(), middle).FromJust();
|
|
middle->SetPrototype(env.local(), top).FromJust();
|
|
env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust();
|
|
|
|
// Indexed and named get.
|
|
CompileRun("obj[0]");
|
|
CompileRun("obj.x");
|
|
|
|
// Indexed and named set.
|
|
CompileRun("obj[1] = 42");
|
|
CompileRun("obj.y = 42");
|
|
|
|
// Indexed and named query.
|
|
CompileRun("0 in obj");
|
|
CompileRun("'x' in obj");
|
|
|
|
// Indexed and named deleter.
|
|
CompileRun("delete obj[0]");
|
|
CompileRun("delete obj.x");
|
|
|
|
// Enumerators.
|
|
CompileRun("for (var p in obj) ;");
|
|
}
|
|
|
|
TEST(PropertyHandlerInPrototypeWithDefine) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
|
|
templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
|
|
CheckThisIndexedPropertyDescriptor, CheckThisIndexedPropertyDeleter,
|
|
CheckThisIndexedPropertyEnumerator, CheckThisIndexedPropertyDefiner));
|
|
|
|
templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
|
|
CheckThisNamedPropertyDescriptor, CheckThisNamedPropertyDeleter,
|
|
CheckThisNamedPropertyEnumerator, CheckThisNamedPropertyDefiner));
|
|
|
|
bottom = templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked();
|
|
Local<v8::Object> top = templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked();
|
|
Local<v8::Object> middle = templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked();
|
|
|
|
bottom->SetPrototype(env.local(), middle).FromJust();
|
|
middle->SetPrototype(env.local(), top).FromJust();
|
|
env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust();
|
|
|
|
// Indexed and named get.
|
|
CompileRun("obj[0]");
|
|
CompileRun("obj.x");
|
|
|
|
// Indexed and named set.
|
|
CompileRun("obj[1] = 42");
|
|
CompileRun("obj.y = 42");
|
|
|
|
// Indexed and named deleter.
|
|
CompileRun("delete obj[0]");
|
|
CompileRun("delete obj.x");
|
|
|
|
// Enumerators.
|
|
CompileRun("for (var p in obj) ;");
|
|
|
|
// Indexed and named definer.
|
|
CompileRun("Object.defineProperty(obj, 2, {});");
|
|
CompileRun("Object.defineProperty(obj, 'z', {});");
|
|
|
|
// Indexed and named propertyDescriptor.
|
|
CompileRun("Object.getOwnPropertyDescriptor(obj, 2);");
|
|
CompileRun("Object.getOwnPropertyDescriptor(obj, 'z');");
|
|
}
|
|
|
|
|
|
bool is_bootstrapping = false;
|
|
static void PrePropertyHandlerGet(
|
|
Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (!is_bootstrapping &&
|
|
v8_str("pre")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), key)
|
|
.FromJust()) {
|
|
info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
|
|
}
|
|
}
|
|
|
|
|
|
static void PrePropertyHandlerQuery(
|
|
Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
|
if (!is_bootstrapping &&
|
|
v8_str("pre")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), key)
|
|
.FromJust()) {
|
|
info.GetReturnValue().Set(static_cast<int32_t>(v8::None));
|
|
}
|
|
}
|
|
|
|
|
|
THREADED_TEST(PrePropertyHandler) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
|
|
desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
PrePropertyHandlerGet, 0, PrePropertyHandlerQuery));
|
|
is_bootstrapping = true;
|
|
LocalContext env(NULL, desc->InstanceTemplate());
|
|
is_bootstrapping = false;
|
|
CompileRun("var pre = 'Object: pre'; var on = 'Object: on';");
|
|
v8::Local<Value> result_pre = CompileRun("pre");
|
|
CHECK(v8_str("PrePropertyHandler: pre")
|
|
->Equals(env.local(), result_pre)
|
|
.FromJust());
|
|
v8::Local<Value> result_on = CompileRun("on");
|
|
CHECK(v8_str("Object: on")->Equals(env.local(), result_on).FromJust());
|
|
v8::Local<Value> result_post = CompileRun("post");
|
|
CHECK(result_post.IsEmpty());
|
|
}
|
|
|
|
|
|
THREADED_TEST(EmptyInterceptorBreakTransitions) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Constructor"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var o1 = new Constructor;"
|
|
"o1.a = 1;" // Ensure a and x share the descriptor array.
|
|
"Object.defineProperty(o1, 'x', {value: 10});");
|
|
CompileRun(
|
|
"var o2 = new Constructor;"
|
|
"o2.a = 1;"
|
|
"Object.defineProperty(o2, 'x', {value: 10});");
|
|
}
|
|
|
|
|
|
THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
|
child->Inherit(parent);
|
|
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"var parent = child.__proto__;"
|
|
"Object.defineProperty(parent, 'age', "
|
|
" {get: function(){ return this.accessor_age; }, "
|
|
" set: function(v){ this.accessor_age = v; }, "
|
|
" enumerable: true, configurable: true});"
|
|
"child.age = 10;");
|
|
ExpectBoolean("child.hasOwnProperty('age')", false);
|
|
ExpectInt32("child.age", 10);
|
|
ExpectInt32("child.accessor_age", 10);
|
|
}
|
|
|
|
|
|
THREADED_TEST(EmptyInterceptorDoesNotShadowApiAccessors) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
|
auto returns_42 = FunctionTemplate::New(isolate, Returns42);
|
|
parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age"), returns_42);
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
|
child->Inherit(parent);
|
|
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"var parent = child.__proto__;");
|
|
ExpectBoolean("child.hasOwnProperty('age')", false);
|
|
ExpectInt32("child.age", 42);
|
|
// Check interceptor followup.
|
|
ExpectInt32(
|
|
"var result;"
|
|
"for (var i = 0; i < 4; ++i) {"
|
|
" result = child.age;"
|
|
"}"
|
|
"result",
|
|
42);
|
|
}
|
|
|
|
|
|
THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
|
child->Inherit(parent);
|
|
AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"var parent = child.__proto__;"
|
|
"parent.name = 'Alice';");
|
|
ExpectBoolean("child.hasOwnProperty('name')", false);
|
|
ExpectString("child.name", "Alice");
|
|
CompileRun("child.name = 'Bob';");
|
|
ExpectString("child.name", "Bob");
|
|
ExpectBoolean("child.hasOwnProperty('name')", true);
|
|
ExpectString("parent.name", "Alice");
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromInterceptorToAccessor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
"function setAge(i){ obj.age = i; };"
|
|
"for(var i = 0; i <= 10000; i++) setAge(i);");
|
|
// All i < 10000 go to the interceptor.
|
|
ExpectInt32("obj.interceptor_age", 9999);
|
|
// The last i goes to the accessor.
|
|
ExpectInt32("obj.accessor_age", 10000);
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromAccessorToInterceptor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
"function setAge(i){ obj.age = i; };"
|
|
"for(var i = 20000; i >= 9999; i--) setAge(i);");
|
|
// All i >= 10000 go to the accessor.
|
|
ExpectInt32("obj.accessor_age", 10000);
|
|
// The last i goes to the interceptor.
|
|
ExpectInt32("obj.interceptor_age", 9999);
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
|
child->Inherit(parent);
|
|
AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
|
|
SimpleAccessorSetter);
|
|
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"function setAge(i){ child.age = i; };"
|
|
"for(var i = 0; i <= 10000; i++) setAge(i);");
|
|
// All i < 10000 go to the interceptor.
|
|
ExpectInt32("child.interceptor_age", 9999);
|
|
// The last i goes to the accessor.
|
|
ExpectInt32("child.accessor_age", 10000);
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
|
child->Inherit(parent);
|
|
AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
|
|
SimpleAccessorSetter);
|
|
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"function setAge(i){ child.age = i; };"
|
|
"for(var i = 20000; i >= 9999; i--) setAge(i);");
|
|
// All i >= 10000 go to the accessor.
|
|
ExpectInt32("child.accessor_age", 10000);
|
|
// The last i goes to the interceptor.
|
|
ExpectInt32("child.interceptor_age", 9999);
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromInterceptorToJSAccessor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
"function setter(i) { this.accessor_age = i; };"
|
|
"function getter() { return this.accessor_age; };"
|
|
"function setAge(i) { obj.age = i; };"
|
|
"Object.defineProperty(obj, 'age', { get:getter, set:setter });"
|
|
"for(var i = 0; i <= 10000; i++) setAge(i);");
|
|
// All i < 10000 go to the interceptor.
|
|
ExpectInt32("obj.interceptor_age", 9999);
|
|
// The last i goes to the JavaScript accessor.
|
|
ExpectInt32("obj.accessor_age", 10000);
|
|
// The installed JavaScript getter is still intact.
|
|
// This last part is a regression test for issue 1651 and relies on the fact
|
|
// that both interceptor and accessor are being installed on the same object.
|
|
ExpectInt32("obj.age", 10000);
|
|
ExpectBoolean("obj.hasOwnProperty('age')", true);
|
|
ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromJSAccessorToInterceptor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
"function setter(i) { this.accessor_age = i; };"
|
|
"function getter() { return this.accessor_age; };"
|
|
"function setAge(i) { obj.age = i; };"
|
|
"Object.defineProperty(obj, 'age', { get:getter, set:setter });"
|
|
"for(var i = 20000; i >= 9999; i--) setAge(i);");
|
|
// All i >= 10000 go to the accessor.
|
|
ExpectInt32("obj.accessor_age", 10000);
|
|
// The last i goes to the interceptor.
|
|
ExpectInt32("obj.interceptor_age", 9999);
|
|
// The installed JavaScript getter is still intact.
|
|
// This last part is a regression test for issue 1651 and relies on the fact
|
|
// that both interceptor and accessor are being installed on the same object.
|
|
ExpectInt32("obj.age", 10000);
|
|
ExpectBoolean("obj.hasOwnProperty('age')", true);
|
|
ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromInterceptorToProperty) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
|
child->Inherit(parent);
|
|
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"function setAge(i){ child.age = i; };"
|
|
"for(var i = 0; i <= 10000; i++) setAge(i);");
|
|
// All i < 10000 go to the interceptor.
|
|
ExpectInt32("child.interceptor_age", 9999);
|
|
// The last i goes to child's own property.
|
|
ExpectInt32("child.age", 10000);
|
|
}
|
|
|
|
|
|
THREADED_TEST(SwitchFromPropertyToInterceptor) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
|
Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
|
child->Inherit(parent);
|
|
AddInterceptor(child, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Child"),
|
|
child->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var child = new Child;"
|
|
"function setAge(i){ child.age = i; };"
|
|
"for(var i = 20000; i >= 9999; i--) setAge(i);");
|
|
// All i >= 10000 go to child's own property.
|
|
ExpectInt32("child.age", 10000);
|
|
// The last i goes to the interceptor.
|
|
ExpectInt32("child.interceptor_age", 9999);
|
|
}
|
|
|
|
|
|
static bool interceptor_for_hidden_properties_called;
|
|
static void InterceptorForHiddenProperties(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
interceptor_for_hidden_properties_called = true;
|
|
}
|
|
|
|
|
|
THREADED_TEST(HiddenPropertiesWithInterceptors) {
|
|
LocalContext context;
|
|
v8::Isolate* isolate = context->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
|
|
interceptor_for_hidden_properties_called = false;
|
|
|
|
v8::Local<v8::Private> key =
|
|
v8::Private::New(isolate, v8_str("api-test::hidden-key"));
|
|
|
|
// Associate an interceptor with an object and start setting hidden values.
|
|
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
|
|
Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
|
|
instance_templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorForHiddenProperties));
|
|
Local<v8::Function> function =
|
|
fun_templ->GetFunction(context.local()).ToLocalChecked();
|
|
Local<v8::Object> obj =
|
|
function->NewInstance(context.local()).ToLocalChecked();
|
|
CHECK(obj->SetPrivate(context.local(), key, v8::Integer::New(isolate, 2302))
|
|
.FromJust());
|
|
CHECK_EQ(2302, obj->GetPrivate(context.local(), key)
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
CHECK(!interceptor_for_hidden_properties_called);
|
|
}
|
|
|
|
|
|
static void XPropertyGetter(Local<Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(info.Data()->IsUndefined());
|
|
info.GetReturnValue().Set(property);
|
|
}
|
|
|
|
|
|
THREADED_TEST(NamedInterceptorPropertyRead) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
Local<Script> script = v8_compile("obj.x");
|
|
for (int i = 0; i < 10; i++) {
|
|
Local<Value> result = script->Run(context.local()).ToLocalChecked();
|
|
CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
|
|
}
|
|
}
|
|
|
|
|
|
THREADED_TEST(NamedInterceptorDictionaryIC) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
|
|
LocalContext context;
|
|
// Create an object with a named interceptor.
|
|
context->Global()
|
|
->Set(context.local(), v8_str("interceptor_obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
Local<Script> script = v8_compile("interceptor_obj.x");
|
|
for (int i = 0; i < 10; i++) {
|
|
Local<Value> result = script->Run(context.local()).ToLocalChecked();
|
|
CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
|
|
}
|
|
// Create a slow case object and a function accessing a property in
|
|
// that slow case object (with dictionary probing in generated
|
|
// code). Then force object with a named interceptor into slow-case,
|
|
// pass it to the function, and check that the interceptor is called
|
|
// instead of accessing the local property.
|
|
Local<Value> result = CompileRun(
|
|
"function get_x(o) { return o.x; };"
|
|
"var obj = { x : 42, y : 0 };"
|
|
"delete obj.y;"
|
|
"for (var i = 0; i < 10; i++) get_x(obj);"
|
|
"interceptor_obj.x = 42;"
|
|
"interceptor_obj.y = 10;"
|
|
"delete interceptor_obj.y;"
|
|
"get_x(interceptor_obj)");
|
|
CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
|
|
}
|
|
|
|
|
|
THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<Context> context1 = Context::New(isolate);
|
|
|
|
context1->Enter();
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
|
|
// Create an object with a named interceptor.
|
|
v8::Local<v8::Object> object = templ->NewInstance(context1).ToLocalChecked();
|
|
context1->Global()
|
|
->Set(context1, v8_str("interceptor_obj"), object)
|
|
.FromJust();
|
|
|
|
// Force the object into the slow case.
|
|
CompileRun(
|
|
"interceptor_obj.y = 0;"
|
|
"delete interceptor_obj.y;");
|
|
context1->Exit();
|
|
|
|
{
|
|
// Introduce the object into a different context.
|
|
// Repeat named loads to exercise ICs.
|
|
LocalContext context2;
|
|
context2->Global()
|
|
->Set(context2.local(), v8_str("interceptor_obj"), object)
|
|
.FromJust();
|
|
Local<Value> result = CompileRun(
|
|
"function get_x(o) { return o.x; }"
|
|
"interceptor_obj.x = 42;"
|
|
"for (var i=0; i != 10; i++) {"
|
|
" get_x(interceptor_obj);"
|
|
"}"
|
|
"get_x(interceptor_obj)");
|
|
// Check that the interceptor was actually invoked.
|
|
CHECK(result->Equals(context2.local(), v8_str("x")).FromJust());
|
|
}
|
|
|
|
// Return to the original context and force some object to the slow case
|
|
// to cause the NormalizedMapCache to verify.
|
|
context1->Enter();
|
|
CompileRun("var obj = { x : 0 }; delete obj.x;");
|
|
context1->Exit();
|
|
}
|
|
|
|
|
|
static void SetXOnPrototypeGetter(
|
|
Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
// Set x on the prototype object and do not handle the get request.
|
|
v8::Local<v8::Value> proto = info.Holder()->GetPrototype();
|
|
proto.As<v8::Object>()
|
|
->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x"),
|
|
v8::Integer::New(info.GetIsolate(), 23))
|
|
.FromJust();
|
|
}
|
|
|
|
|
|
// This is a regression test for http://crbug.com/20104. Map
|
|
// transitions should not interfere with post interceptor lookup.
|
|
THREADED_TEST(NamedInterceptorMapTransitionRead) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<v8::FunctionTemplate> function_template =
|
|
v8::FunctionTemplate::New(isolate);
|
|
Local<v8::ObjectTemplate> instance_template =
|
|
function_template->InstanceTemplate();
|
|
instance_template->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(SetXOnPrototypeGetter));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("F"),
|
|
function_template->GetFunction(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
// Create an instance of F and introduce a map transition for x.
|
|
CompileRun("var o = new F(); o.x = 23;");
|
|
// Create an instance of F and invoke the getter. The result should be 23.
|
|
Local<Value> result = CompileRun("o = new F(); o.x");
|
|
CHECK_EQ(result->Int32Value(context.local()).FromJust(), 23);
|
|
}
|
|
|
|
|
|
static void IndexedPropertyGetter(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (index == 37) {
|
|
info.GetReturnValue().Set(v8_num(625));
|
|
}
|
|
}
|
|
|
|
|
|
static void IndexedPropertySetter(
|
|
uint32_t index, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (index == 39) {
|
|
info.GetReturnValue().Set(value);
|
|
}
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
IndexedPropertyGetter, IndexedPropertySetter));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
Local<Script> getter_script =
|
|
v8_compile("obj.__defineGetter__(\"3\", function(){return 5;});obj[3];");
|
|
Local<Script> setter_script = v8_compile(
|
|
"obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
|
|
"obj[17] = 23;"
|
|
"obj.foo;");
|
|
Local<Script> interceptor_setter_script = v8_compile(
|
|
"obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
|
|
"obj[39] = 47;"
|
|
"obj.foo;"); // This setter should not run, due to the interceptor.
|
|
Local<Script> interceptor_getter_script = v8_compile("obj[37];");
|
|
Local<Value> result = getter_script->Run(context.local()).ToLocalChecked();
|
|
CHECK(v8_num(5)->Equals(context.local(), result).FromJust());
|
|
result = setter_script->Run(context.local()).ToLocalChecked();
|
|
CHECK(v8_num(23)->Equals(context.local(), result).FromJust());
|
|
result = interceptor_setter_script->Run(context.local()).ToLocalChecked();
|
|
CHECK(v8_num(23)->Equals(context.local(), result).FromJust());
|
|
result = interceptor_getter_script->Run(context.local()).ToLocalChecked();
|
|
CHECK(v8_num(625)->Equals(context.local(), result).FromJust());
|
|
}
|
|
|
|
|
|
static void UnboxedDoubleIndexedPropertyGetter(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (index < 25) {
|
|
info.GetReturnValue().Set(v8_num(index));
|
|
}
|
|
}
|
|
|
|
|
|
static void UnboxedDoubleIndexedPropertySetter(
|
|
uint32_t index, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (index < 25) {
|
|
info.GetReturnValue().Set(v8_num(index));
|
|
}
|
|
}
|
|
|
|
|
|
void UnboxedDoubleIndexedPropertyEnumerator(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
// Force the list of returned keys to be stored in a FastDoubleArray.
|
|
Local<Script> indexed_property_names_script = v8_compile(
|
|
"keys = new Array(); keys[125000] = 1;"
|
|
"for(i = 0; i < 80000; i++) { keys[i] = i; };"
|
|
"keys.length = 25; keys;");
|
|
Local<Value> result =
|
|
indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext())
|
|
.ToLocalChecked();
|
|
info.GetReturnValue().Set(Local<v8::Array>::Cast(result));
|
|
}
|
|
|
|
|
|
// Make sure that the the interceptor code in the runtime properly handles
|
|
// merging property name lists for double-array-backed arrays.
|
|
THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
UnboxedDoubleIndexedPropertyGetter, UnboxedDoubleIndexedPropertySetter, 0,
|
|
0, UnboxedDoubleIndexedPropertyEnumerator));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
// When obj is created, force it to be Stored in a FastDoubleArray.
|
|
Local<Script> create_unboxed_double_script = v8_compile(
|
|
"obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } "
|
|
"key_count = 0; "
|
|
"for (x in obj) {key_count++;};"
|
|
"obj;");
|
|
Local<Value> result =
|
|
create_unboxed_double_script->Run(context.local()).ToLocalChecked();
|
|
CHECK(result->ToObject(context.local())
|
|
.ToLocalChecked()
|
|
->HasRealIndexedProperty(context.local(), 2000)
|
|
.FromJust());
|
|
Local<Script> key_count_check = v8_compile("key_count;");
|
|
result = key_count_check->Run(context.local()).ToLocalChecked();
|
|
CHECK(v8_num(40013)->Equals(context.local(), result).FromJust());
|
|
}
|
|
|
|
|
|
void SloppyArgsIndexedPropertyEnumerator(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
// Force the list of returned keys to be stored in a Arguments object.
|
|
Local<Script> indexed_property_names_script = v8_compile(
|
|
"function f(w,x) {"
|
|
" return arguments;"
|
|
"}"
|
|
"keys = f(0, 1, 2, 3);"
|
|
"keys;");
|
|
Local<Object> result = Local<Object>::Cast(
|
|
indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext())
|
|
.ToLocalChecked());
|
|
// Have to populate the handle manually, as it's not Cast-able.
|
|
i::Handle<i::JSReceiver> o =
|
|
v8::Utils::OpenHandle<Object, i::JSReceiver>(result);
|
|
i::Handle<i::JSArray> array(reinterpret_cast<i::JSArray*>(*o));
|
|
info.GetReturnValue().Set(v8::Utils::ToLocal(array));
|
|
}
|
|
|
|
|
|
static void SloppyIndexedPropertyGetter(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (index < 4) {
|
|
info.GetReturnValue().Set(v8_num(index));
|
|
}
|
|
}
|
|
|
|
|
|
// Make sure that the the interceptor code in the runtime properly handles
|
|
// merging property name lists for non-string arguments arrays.
|
|
THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
SloppyIndexedPropertyGetter, 0, 0, 0,
|
|
SloppyArgsIndexedPropertyEnumerator));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
Local<Script> create_args_script = v8_compile(
|
|
"var key_count = 0;"
|
|
"for (x in obj) {key_count++;} key_count;");
|
|
Local<Value> result =
|
|
create_args_script->Run(context.local()).ToLocalChecked();
|
|
CHECK(v8_num(4)->Equals(context.local(), result).FromJust());
|
|
}
|
|
|
|
|
|
static void IdentityIndexedPropertyGetter(
|
|
uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
info.GetReturnValue().Set(index);
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
// Check fast object case.
|
|
const char* fast_case_code =
|
|
"Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
|
|
ExpectString(fast_case_code, "0");
|
|
|
|
// Check slow case.
|
|
const char* slow_case_code =
|
|
"obj.x = 1; delete obj.x;"
|
|
"Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
|
|
ExpectString(slow_case_code, "1");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithNoSetter) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
const char* code =
|
|
"try {"
|
|
" obj[0] = 239;"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var v = obj[0];"
|
|
" if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context,
|
|
Local<v8::Object> accessed_object,
|
|
Local<v8::Value> data) {
|
|
return false;
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
templ->SetAccessCheckCallback(AccessAlwaysBlocked);
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"var result = 'PASSED';"
|
|
"for (var i = 0; i < 100; i++) {"
|
|
" try {"
|
|
" var v = obj[0];"
|
|
" result = 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" break;"
|
|
" } catch (e) {"
|
|
" /* pass */"
|
|
" }"
|
|
"}"
|
|
"result";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"try {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var v = obj[i];"
|
|
" if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"try {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var expected = i;"
|
|
" var key = i;"
|
|
" if (i == 25) {"
|
|
" key = -1;"
|
|
" expected = undefined;"
|
|
" }"
|
|
" if (i == 50) {"
|
|
" /* probe minimal Smi number on 32-bit platforms */"
|
|
" key = -(1 << 30);"
|
|
" expected = undefined;"
|
|
" }"
|
|
" if (i == 75) {"
|
|
" /* probe minimal Smi number on 64-bit platforms */"
|
|
" key = 1 << 31;"
|
|
" expected = undefined;"
|
|
" }"
|
|
" var v = obj[key];"
|
|
" if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"try {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var expected = i;"
|
|
" var key = i;"
|
|
" if (i == 50) {"
|
|
" key = 'foobar';"
|
|
" expected = undefined;"
|
|
" }"
|
|
" var v = obj[key];"
|
|
" if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"var original = obj;"
|
|
"try {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var expected = i;"
|
|
" if (i == 50) {"
|
|
" obj = {50: 'foobar'};"
|
|
" expected = 'foobar';"
|
|
" }"
|
|
" var v = obj[i];"
|
|
" if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" if (i == 50) obj = original;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"var original = obj;"
|
|
"try {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var expected = i;"
|
|
" if (i == 5) {"
|
|
" obj = 239;"
|
|
" expected = undefined;"
|
|
" }"
|
|
" var v = obj[i];"
|
|
" if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" if (i == 5) obj = original;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
THREADED_TEST(IndexedInterceptorOnProto) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
|
|
|
|
LocalContext context;
|
|
Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
|
|
const char* code =
|
|
"var o = {__proto__: obj};"
|
|
"try {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" var v = o[i];"
|
|
" if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
|
|
" }"
|
|
" 'PASSED'"
|
|
"} catch(e) {"
|
|
" e"
|
|
"}";
|
|
ExpectString(code, "PASSED");
|
|
}
|
|
|
|
|
|
static void NoBlockGetterX(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>&) {}
|
|
|
|
|
|
static void NoBlockGetterI(uint32_t index,
|
|
const v8::PropertyCallbackInfo<v8::Value>&) {}
|
|
|
|
|
|
static void PDeleter(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
|
if (!name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo"))
|
|
.FromJust()) {
|
|
return; // not intercepted
|
|
}
|
|
|
|
info.GetReturnValue().Set(false); // intercepted, don't delete the property
|
|
}
|
|
|
|
|
|
static void IDeleter(uint32_t index,
|
|
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
|
if (index != 2) {
|
|
return; // not intercepted
|
|
}
|
|
|
|
info.GetReturnValue().Set(false); // intercepted, don't delete the property
|
|
}
|
|
|
|
|
|
THREADED_TEST(Deleter) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
|
|
obj->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX, NULL,
|
|
NULL, PDeleter, NULL));
|
|
obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
NoBlockGetterI, NULL, NULL, IDeleter, NULL));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("k"),
|
|
obj->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"k.foo = 'foo';"
|
|
"k.bar = 'bar';"
|
|
"k[2] = 2;"
|
|
"k[4] = 4;");
|
|
CHECK(v8_compile("delete k.foo")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->IsFalse());
|
|
CHECK(v8_compile("delete k.bar")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->IsTrue());
|
|
|
|
CHECK(v8_compile("k.foo")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->Equals(context.local(), v8_str("foo"))
|
|
.FromJust());
|
|
CHECK(v8_compile("k.bar")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->IsUndefined());
|
|
|
|
CHECK(v8_compile("delete k[2]")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->IsFalse());
|
|
CHECK(v8_compile("delete k[4]")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->IsTrue());
|
|
|
|
CHECK(v8_compile("k[2]")
|
|
->Run(context.local())
|
|
.ToLocalChecked()
|
|
->Equals(context.local(), v8_num(2))
|
|
.FromJust());
|
|
CHECK(
|
|
v8_compile("k[4]")->Run(context.local()).ToLocalChecked()->IsUndefined());
|
|
}
|
|
|
|
|
|
static void GetK(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
|
if (name->Equals(context, v8_str("foo")).FromJust() ||
|
|
name->Equals(context, v8_str("bar")).FromJust() ||
|
|
name->Equals(context, v8_str("baz")).FromJust()) {
|
|
info.GetReturnValue().SetUndefined();
|
|
}
|
|
}
|
|
|
|
|
|
static void IndexedGetK(uint32_t index,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (index == 0 || index == 1) info.GetReturnValue().SetUndefined();
|
|
}
|
|
|
|
|
|
static void NamedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 3);
|
|
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
|
result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("foo"))
|
|
.FromJust();
|
|
result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("bar"))
|
|
.FromJust();
|
|
result->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_str("baz"))
|
|
.FromJust();
|
|
info.GetReturnValue().Set(result);
|
|
}
|
|
|
|
|
|
static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
|
|
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
|
result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("0"))
|
|
.FromJust();
|
|
result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("1"))
|
|
.FromJust();
|
|
info.GetReturnValue().Set(result);
|
|
}
|
|
|
|
|
|
THREADED_TEST(Enumerators) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
|
|
obj->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(GetK, NULL, NULL, NULL, NamedEnum));
|
|
obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
IndexedGetK, NULL, NULL, NULL, IndexedEnum));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("k"),
|
|
obj->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<v8::Array> result =
|
|
v8::Local<v8::Array>::Cast(CompileRun("k[10] = 0;"
|
|
"k.a = 0;"
|
|
"k[5] = 0;"
|
|
"k.b = 0;"
|
|
"k[4294967294] = 0;"
|
|
"k.c = 0;"
|
|
"k[4294967295] = 0;"
|
|
"k.d = 0;"
|
|
"k[140000] = 0;"
|
|
"k.e = 0;"
|
|
"k[30000000000] = 0;"
|
|
"k.f = 0;"
|
|
"var result = [];"
|
|
"for (var prop in k) {"
|
|
" result.push(prop);"
|
|
"}"
|
|
"result"));
|
|
// Check that we get all the property names returned including the
|
|
// ones from the enumerators in the right order: indexed properties
|
|
// in numerical order, indexed interceptor properties, named
|
|
// properties in insertion order, named interceptor properties.
|
|
// This order is not mandated by the spec, so this test is just
|
|
// documenting our behavior.
|
|
CHECK_EQ(17u, result->Length());
|
|
// Indexed properties.
|
|
CHECK(v8_str("5")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 0))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("10")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 1))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("140000")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 2))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("4294967294")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 3))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
// Indexed Interceptor properties
|
|
CHECK(v8_str("0")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 4))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("1")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 5))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
// Named properties in insertion order.
|
|
CHECK(v8_str("a")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 6))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("b")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 7))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("c")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 8))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("4294967295")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 9))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("d")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 10))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("e")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 11))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("30000000000")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 12))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("f")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 13))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
// Named interceptor properties.
|
|
CHECK(v8_str("foo")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 14))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("bar")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 15))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("baz")
|
|
->Equals(context.local(),
|
|
result->Get(context.local(), v8::Integer::New(isolate, 16))
|
|
.ToLocalChecked())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
v8::Local<Value> call_ic_function;
|
|
v8::Local<Value> call_ic_function2;
|
|
v8::Local<Value> call_ic_function3;
|
|
|
|
static void InterceptorCallICGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust());
|
|
info.GetReturnValue().Set(call_ic_function);
|
|
}
|
|
|
|
|
|
// This test should hit the call IC for the interceptor case.
|
|
THREADED_TEST(InterceptorCallIC) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
call_ic_function = v8_compile("function f(x) { return x + 1; }; f")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
v8::Local<Value> value = CompileRun(
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.x(41);"
|
|
"}");
|
|
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// This test checks that if interceptor doesn't provide
|
|
// a value, we can fetch regular value.
|
|
THREADED_TEST(InterceptorCallICSeesOthers) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"o.x = function f(x) { return x + 1; };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result = o.x(41);"
|
|
"}");
|
|
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static v8::Local<Value> call_ic_function4;
|
|
static void InterceptorCallICGetter4(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CHECK(v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust());
|
|
info.GetReturnValue().Set(call_ic_function4);
|
|
}
|
|
|
|
|
|
// This test checks that if interceptor provides a function,
|
|
// even if we cached shadowed variant, interceptor's function
|
|
// is invoked
|
|
THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
call_ic_function4 = v8_compile("function f(x) { return x - 1; }; f")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
v8::Local<Value> value = CompileRun(
|
|
"Object.getPrototypeOf(o).x = function(x) { return x + 1; };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.x(42);"
|
|
"}");
|
|
CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored cacheable lookup into
|
|
// a stub, but it got invalidated later on
|
|
THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"proto1 = new Object();"
|
|
"proto2 = new Object();"
|
|
"o.__proto__ = proto1;"
|
|
"proto1.__proto__ = proto2;"
|
|
"proto2.y = function(x) { return x + 1; };"
|
|
// Invoke it many times to compile a stub
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" o.y(42);"
|
|
"}"
|
|
"proto1.y = function(x) { return x - 1; };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result += o.y(42);"
|
|
"}");
|
|
CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// This test checks that if interceptor doesn't provide a function,
|
|
// cached constant function is used
|
|
THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function inc(x) { return x + 1; };"
|
|
"inc(1);"
|
|
"o.x = inc;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.x(42);"
|
|
"}");
|
|
CHECK_EQ(43, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static v8::Local<Value> call_ic_function5;
|
|
static void InterceptorCallICGetter5(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust())
|
|
info.GetReturnValue().Set(call_ic_function5);
|
|
}
|
|
|
|
|
|
// This test checks that if interceptor provides a function,
|
|
// even if we cached constant function, interceptor's function
|
|
// is invoked
|
|
THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
call_ic_function5 = v8_compile("function f(x) { return x - 1; }; f")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function inc(x) { return x + 1; };"
|
|
"inc(1);"
|
|
"o.x = inc;"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 1000; i++) {"
|
|
" result = o.x(42);"
|
|
"}");
|
|
CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static v8::Local<Value> call_ic_function6;
|
|
static void InterceptorCallICGetter6(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust())
|
|
info.GetReturnValue().Set(call_ic_function6);
|
|
}
|
|
|
|
|
|
// Same test as above, except the code is wrapped in a function
|
|
// to test the optimized compiler.
|
|
THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
call_ic_function6 = v8_compile("function f(x) { return x - 1; }; f")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function inc(x) { return x + 1; };"
|
|
"inc(1);"
|
|
"o.x = inc;"
|
|
"function test() {"
|
|
" var result = 0;"
|
|
" for (var i = 0; i < 1000; i++) {"
|
|
" result = o.x(42);"
|
|
" }"
|
|
" return result;"
|
|
"};"
|
|
"test();"
|
|
"test();"
|
|
"test();"
|
|
"%OptimizeFunctionOnNextCall(test);"
|
|
"test()");
|
|
CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored constant function into
|
|
// a stub, but it got invalidated later on
|
|
THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function inc(x) { return x + 1; };"
|
|
"inc(1);"
|
|
"proto1 = new Object();"
|
|
"proto2 = new Object();"
|
|
"o.__proto__ = proto1;"
|
|
"proto1.__proto__ = proto2;"
|
|
"proto2.y = inc;"
|
|
// Invoke it many times to compile a stub
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" o.y(42);"
|
|
"}"
|
|
"proto1.y = function(x) { return x - 1; };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result += o.y(42);"
|
|
"}");
|
|
CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored constant function into
|
|
// a stub, but it got invalidated later on due to override on
|
|
// global object which is between interceptor and constant function' holders.
|
|
THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function inc(x) { return x + 1; };"
|
|
"inc(1);"
|
|
"o.__proto__ = this;"
|
|
"this.__proto__.y = inc;"
|
|
// Invoke it many times to compile a stub
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
|
|
"}"
|
|
"this.y = function(x) { return x - 1; };"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 7; i++) {"
|
|
" result += o.y(42);"
|
|
"}");
|
|
CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when actual function to call sits on global object.
|
|
THREADED_TEST(InterceptorCallICCachedFromGlobal) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<Value> value = CompileRun(
|
|
"try {"
|
|
" o.__proto__ = this;"
|
|
" for (var i = 0; i < 10; i++) {"
|
|
" var v = o.parseFloat('239');"
|
|
" if (v != 239) throw v;"
|
|
// Now it should be ICed and keep a reference to parseFloat.
|
|
" }"
|
|
" var result = 0;"
|
|
" for (var i = 0; i < 10; i++) {"
|
|
" result += o.parseFloat('239');"
|
|
" }"
|
|
" result"
|
|
"} catch(e) {"
|
|
" e"
|
|
"};");
|
|
CHECK_EQ(239 * 10, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
v8::Local<Value> keyed_call_ic_function;
|
|
|
|
static void InterceptorKeyedCallICGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust()) {
|
|
info.GetReturnValue().Set(keyed_call_ic_function);
|
|
}
|
|
}
|
|
|
|
|
|
// Test the case when we stored cacheable lookup into
|
|
// a stub, but the function name changed (to another cacheable function).
|
|
THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"proto = new Object();"
|
|
"proto.y = function(x) { return x + 1; };"
|
|
"proto.z = function(x) { return x - 1; };"
|
|
"o.__proto__ = proto;"
|
|
"var result = 0;"
|
|
"var method = 'y';"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (i == 5) { method = 'z'; };"
|
|
" result += o[method](41);"
|
|
"}");
|
|
CHECK_EQ(42 * 5 + 40 * 5, context->Global()
|
|
->Get(context.local(), v8_str("result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when we stored cacheable lookup into
|
|
// a stub, but the function name changed (and the new function is present
|
|
// both before and after the interceptor in the prototype chain).
|
|
THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("proto1"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
keyed_call_ic_function = v8_compile("function f(x) { return x - 1; }; f")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
CompileRun(
|
|
"o = new Object();"
|
|
"proto2 = new Object();"
|
|
"o.y = function(x) { return x + 1; };"
|
|
"proto2.y = function(x) { return x + 2; };"
|
|
"o.__proto__ = proto1;"
|
|
"proto1.__proto__ = proto2;"
|
|
"var result = 0;"
|
|
"var method = 'x';"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (i == 5) { method = 'y'; };"
|
|
" result += o[method](41);"
|
|
"}");
|
|
CHECK_EQ(42 * 5 + 40 * 5, context->Global()
|
|
->Get(context.local(), v8_str("result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
|
|
// on the global object.
|
|
THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"function inc(x) { return x + 1; };"
|
|
"inc(1);"
|
|
"function dec(x) { return x - 1; };"
|
|
"dec(1);"
|
|
"o.__proto__ = this;"
|
|
"this.__proto__.x = inc;"
|
|
"this.__proto__.y = dec;"
|
|
"var result = 0;"
|
|
"var method = 'x';"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (i == 5) { method = 'y'; };"
|
|
" result += o[method](41);"
|
|
"}");
|
|
CHECK_EQ(42 * 5 + 40 * 5, context->Global()
|
|
->Get(context.local(), v8_str("result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
// Test the case when actual function to call sits on global object.
|
|
THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
CompileRun(
|
|
"function len(x) { return x.length; };"
|
|
"o.__proto__ = this;"
|
|
"var m = 'parseFloat';"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (i == 5) {"
|
|
" m = 'len';"
|
|
" saved_result = result;"
|
|
" };"
|
|
" result = o[m]('239');"
|
|
"}");
|
|
CHECK_EQ(3, context->Global()
|
|
->Get(context.local(), v8_str("result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
CHECK_EQ(239, context->Global()
|
|
->Get(context.local(), v8_str("saved_result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
// Test the map transition before the interceptor.
|
|
THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("proto"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
CompileRun(
|
|
"var o = new Object();"
|
|
"o.__proto__ = proto;"
|
|
"o.method = function(x) { return x + 1; };"
|
|
"var m = 'method';"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (i == 5) { o.method = function(x) { return x - 1; }; };"
|
|
" result += o[m](41);"
|
|
"}");
|
|
CHECK_EQ(42 * 5 + 40 * 5, context->Global()
|
|
->Get(context.local(), v8_str("result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
// Test the map transition after the interceptor.
|
|
THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
|
templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
|
LocalContext context;
|
|
context->Global()
|
|
->Set(context.local(), v8_str("o"),
|
|
templ_o->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
CompileRun(
|
|
"var proto = new Object();"
|
|
"o.__proto__ = proto;"
|
|
"proto.method = function(x) { return x + 1; };"
|
|
"var m = 'method';"
|
|
"var result = 0;"
|
|
"for (var i = 0; i < 10; i++) {"
|
|
" if (i == 5) { proto.method = function(x) { return x - 1; }; };"
|
|
" result += o[m](41);"
|
|
"}");
|
|
CHECK_EQ(42 * 5 + 40 * 5, context->Global()
|
|
->Get(context.local(), v8_str("result"))
|
|
.ToLocalChecked()
|
|
->Int32Value(context.local())
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
static int interceptor_call_count = 0;
|
|
|
|
static void InterceptorICRefErrorGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (!is_bootstrapping &&
|
|
v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust() &&
|
|
interceptor_call_count++ < 20) {
|
|
info.GetReturnValue().Set(call_ic_function2);
|
|
}
|
|
}
|
|
|
|
|
|
// This test should hit load and call ICs for the interceptor case.
|
|
// Once in a while, the interceptor will reply that a property was not
|
|
// found in which case we should get a reference error.
|
|
THREADED_TEST(InterceptorICReferenceErrors) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter));
|
|
is_bootstrapping = true;
|
|
LocalContext context(0, templ, v8::Local<Value>());
|
|
is_bootstrapping = false;
|
|
call_ic_function2 = v8_compile("function h(x) { return x; }; h")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function f() {"
|
|
" for (var i = 0; i < 1000; i++) {"
|
|
" try { x; } catch(e) { return true; }"
|
|
" }"
|
|
" return false;"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
interceptor_call_count = 0;
|
|
value = CompileRun(
|
|
"function g() {"
|
|
" for (var i = 0; i < 1000; i++) {"
|
|
" try { x(42); } catch(e) { return true; }"
|
|
" }"
|
|
" return false;"
|
|
"};"
|
|
"g();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static int interceptor_ic_exception_get_count = 0;
|
|
|
|
static void InterceptorICExceptionGetter(
|
|
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (is_bootstrapping) return;
|
|
if (v8_str("x")
|
|
->Equals(info.GetIsolate()->GetCurrentContext(), name)
|
|
.FromJust() &&
|
|
++interceptor_ic_exception_get_count < 20) {
|
|
info.GetReturnValue().Set(call_ic_function3);
|
|
}
|
|
if (interceptor_ic_exception_get_count == 20) {
|
|
info.GetIsolate()->ThrowException(v8_num(42));
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// Test interceptor load/call IC where the interceptor throws an
|
|
// exception once in a while.
|
|
THREADED_TEST(InterceptorICGetterExceptions) {
|
|
interceptor_ic_exception_get_count = 0;
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter));
|
|
is_bootstrapping = true;
|
|
LocalContext context(0, templ, v8::Local<Value>());
|
|
is_bootstrapping = false;
|
|
call_ic_function3 = v8_compile("function h(x) { return x; }; h")
|
|
->Run(context.local())
|
|
.ToLocalChecked();
|
|
v8::Local<Value> value = CompileRun(
|
|
"function f() {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" try { x; } catch(e) { return true; }"
|
|
" }"
|
|
" return false;"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
interceptor_ic_exception_get_count = 0;
|
|
value = CompileRun(
|
|
"function f() {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" try { x(42); } catch(e) { return true; }"
|
|
" }"
|
|
" return false;"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
static int interceptor_ic_exception_set_count = 0;
|
|
|
|
static void InterceptorICExceptionSetter(
|
|
Local<Name> key, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
if (++interceptor_ic_exception_set_count > 20) {
|
|
info.GetIsolate()->ThrowException(v8_num(42));
|
|
}
|
|
}
|
|
|
|
|
|
// Test interceptor store IC where the interceptor throws an exception
|
|
// once in a while.
|
|
THREADED_TEST(InterceptorICSetterExceptions) {
|
|
interceptor_ic_exception_set_count = 0;
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(0, InterceptorICExceptionSetter));
|
|
LocalContext context(0, templ, v8::Local<Value>());
|
|
v8::Local<Value> value = CompileRun(
|
|
"function f() {"
|
|
" for (var i = 0; i < 100; i++) {"
|
|
" try { x = 42; } catch(e) { return true; }"
|
|
" }"
|
|
" return false;"
|
|
"};"
|
|
"f();");
|
|
CHECK_EQ(true, value->BooleanValue(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test that we ignore null interceptors.
|
|
THREADED_TEST(NullNamedInterceptor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
static_cast<v8::GenericNamedPropertyGetterCallback>(0)));
|
|
LocalContext context;
|
|
templ->Set(CcTest::isolate(), "x", v8_num(42));
|
|
v8::Local<v8::Object> obj =
|
|
templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
v8::Local<Value> value = CompileRun("obj.x");
|
|
CHECK(value->IsInt32());
|
|
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
// Test that we ignore null interceptors.
|
|
THREADED_TEST(NullIndexedInterceptor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
|
templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
static_cast<v8::IndexedPropertyGetterCallback>(0)));
|
|
LocalContext context;
|
|
templ->Set(CcTest::isolate(), "42", v8_num(42));
|
|
v8::Local<v8::Object> obj =
|
|
templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
|
|
v8::Local<Value> value = CompileRun("obj[42]");
|
|
CHECK(value->IsInt32());
|
|
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
|
|
}
|
|
|
|
|
|
THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
|
|
templ->InstanceTemplate()->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
|
|
.ToLocalChecked()
|
|
->NewInstance(env.local())
|
|
.ToLocalChecked())
|
|
.FromJust();
|
|
ExpectTrue("obj.x === 42");
|
|
ExpectTrue("!obj.propertyIsEnumerable('x')");
|
|
}
|
|
|
|
|
|
THREADED_TEST(Regress256330) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
LocalContext context;
|
|
v8::HandleScope scope(context->GetIsolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
context->Global()
|
|
->Set(context.local(), v8_str("Bug"),
|
|
templ->GetFunction(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"\"use strict\"; var o = new Bug;"
|
|
"function f(o) { o.x = 10; };"
|
|
"f(o); f(o); f(o);"
|
|
"%OptimizeFunctionOnNextCall(f);"
|
|
"f(o);");
|
|
ExpectBoolean("%GetOptimizationStatus(f) != 2", true);
|
|
}
|
|
|
|
|
|
THREADED_TEST(CrankshaftInterceptorSetter) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
// Initialize fields to avoid transitions later.
|
|
"obj.age = 0;"
|
|
"obj.accessor_age = 42;"
|
|
"function setter(i) { this.accessor_age = i; };"
|
|
"function getter() { return this.accessor_age; };"
|
|
"function setAge(i) { obj.age = i; };"
|
|
"Object.defineProperty(obj, 'age', { get:getter, set:setter });"
|
|
"setAge(1);"
|
|
"setAge(2);"
|
|
"setAge(3);"
|
|
"%OptimizeFunctionOnNextCall(setAge);"
|
|
"setAge(4);");
|
|
// All stores went through the interceptor.
|
|
ExpectInt32("obj.interceptor_age", 4);
|
|
ExpectInt32("obj.accessor_age", 42);
|
|
}
|
|
|
|
|
|
THREADED_TEST(CrankshaftInterceptorGetter) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
// Initialize fields to avoid transitions later.
|
|
"obj.age = 1;"
|
|
"obj.accessor_age = 42;"
|
|
"function getter() { return this.accessor_age; };"
|
|
"function getAge() { return obj.interceptor_age; };"
|
|
"Object.defineProperty(obj, 'interceptor_age', { get:getter });"
|
|
"getAge();"
|
|
"getAge();"
|
|
"getAge();"
|
|
"%OptimizeFunctionOnNextCall(getAge);");
|
|
// Access through interceptor.
|
|
ExpectInt32("getAge()", 1);
|
|
}
|
|
|
|
|
|
THREADED_TEST(CrankshaftInterceptorFieldRead) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
"obj.__proto__.interceptor_age = 42;"
|
|
"obj.age = 100;"
|
|
"function getAge() { return obj.interceptor_age; };");
|
|
ExpectInt32("getAge();", 100);
|
|
ExpectInt32("getAge();", 100);
|
|
ExpectInt32("getAge();", 100);
|
|
CompileRun("%OptimizeFunctionOnNextCall(getAge);");
|
|
// Access through interceptor.
|
|
ExpectInt32("getAge();", 100);
|
|
}
|
|
|
|
|
|
THREADED_TEST(CrankshaftInterceptorFieldWrite) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Obj"),
|
|
templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var obj = new Obj;"
|
|
"obj.age = 100000;"
|
|
"function setAge(i) { obj.age = i };"
|
|
"setAge(100);"
|
|
"setAge(101);"
|
|
"setAge(102);"
|
|
"%OptimizeFunctionOnNextCall(setAge);"
|
|
"setAge(103);");
|
|
ExpectInt32("obj.age", 100000);
|
|
ExpectInt32("obj.interceptor_age", 103);
|
|
}
|
|
|
|
|
|
THREADED_TEST(Regress149912) {
|
|
LocalContext context;
|
|
v8::HandleScope scope(context->GetIsolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
|
|
AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
context->Global()
|
|
->Set(context.local(), v8_str("Bug"),
|
|
templ->GetFunction(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();");
|
|
}
|
|
|
|
THREADED_TEST(Regress625155) {
|
|
LocalContext context;
|
|
v8::HandleScope scope(context->GetIsolate());
|
|
Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
|
|
AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
context->Global()
|
|
->Set(context.local(), v8_str("Bug"),
|
|
templ->GetFunction(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"Number.prototype.__proto__ = new Bug;"
|
|
"var x;"
|
|
"x = 0xdead;"
|
|
"x.boom = 0;"
|
|
"x = 's';"
|
|
"x.boom = 0;"
|
|
"x = 1.5;"
|
|
"x.boom = 0;");
|
|
}
|
|
|
|
THREADED_TEST(Regress125988) {
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
Local<FunctionTemplate> intercept = FunctionTemplate::New(CcTest::isolate());
|
|
AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
|
LocalContext env;
|
|
env->Global()
|
|
->Set(env.local(), v8_str("Intercept"),
|
|
intercept->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust();
|
|
CompileRun(
|
|
"var a = new Object();"
|
|
"var b = new Intercept();"
|
|
"var c = new Object();"
|
|
"c.__proto__ = b;"
|
|
"b.__proto__ = a;"
|
|
"a.x = 23;"
|
|
"for (var i = 0; i < 3; i++) c.x;");
|
|
ExpectBoolean("c.hasOwnProperty('x')", false);
|
|
ExpectInt32("c.x", 23);
|
|
CompileRun(
|
|
"a.y = 42;"
|
|
"for (var i = 0; i < 3; i++) c.x;");
|
|
ExpectBoolean("c.hasOwnProperty('x')", false);
|
|
ExpectInt32("c.x", 23);
|
|
ExpectBoolean("c.hasOwnProperty('y')", false);
|
|
ExpectInt32("c.y", 42);
|
|
}
|
|
|
|
|
|
static void IndexedPropertyEnumerator(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 1);
|
|
result->Set(info.GetIsolate()->GetCurrentContext(), 0,
|
|
v8::Integer::New(info.GetIsolate(), 7))
|
|
.FromJust();
|
|
info.GetReturnValue().Set(result);
|
|
}
|
|
|
|
|
|
static void NamedPropertyEnumerator(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
|
|
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
|
result->Set(context, 0, v8_str("x")).FromJust();
|
|
result->Set(context, 1, v8::Symbol::GetIterator(info.GetIsolate()))
|
|
.FromJust();
|
|
info.GetReturnValue().Set(result);
|
|
}
|
|
|
|
|
|
THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
|
|
|
|
obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
|
|
obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
|
|
obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
NULL, NULL, NULL, NULL, IndexedPropertyEnumerator));
|
|
obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
NULL, NULL, NULL, NULL, NamedPropertyEnumerator));
|
|
|
|
LocalContext context;
|
|
v8::Local<v8::Object> global = context->Global();
|
|
global->Set(context.local(), v8_str("object"),
|
|
obj_template->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<v8::Value> result =
|
|
CompileRun("Object.getOwnPropertyNames(object)");
|
|
CHECK(result->IsArray());
|
|
v8::Local<v8::Array> result_array = v8::Local<v8::Array>::Cast(result);
|
|
CHECK_EQ(2u, result_array->Length());
|
|
CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString());
|
|
CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString());
|
|
CHECK(v8_str("7")
|
|
->Equals(context.local(),
|
|
result_array->Get(context.local(), 0).ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("x")
|
|
->Equals(context.local(),
|
|
result_array->Get(context.local(), 1).ToLocalChecked())
|
|
.FromJust());
|
|
|
|
result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret");
|
|
CHECK(result->IsArray());
|
|
result_array = v8::Local<v8::Array>::Cast(result);
|
|
CHECK_EQ(2u, result_array->Length());
|
|
CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString());
|
|
CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString());
|
|
CHECK(v8_str("7")
|
|
->Equals(context.local(),
|
|
result_array->Get(context.local(), 0).ToLocalChecked())
|
|
.FromJust());
|
|
CHECK(v8_str("x")
|
|
->Equals(context.local(),
|
|
result_array->Get(context.local(), 1).ToLocalChecked())
|
|
.FromJust());
|
|
|
|
result = CompileRun("Object.getOwnPropertySymbols(object)");
|
|
CHECK(result->IsArray());
|
|
result_array = v8::Local<v8::Array>::Cast(result);
|
|
CHECK_EQ(1u, result_array->Length());
|
|
CHECK(result_array->Get(context.local(), 0)
|
|
.ToLocalChecked()
|
|
->Equals(context.local(), v8::Symbol::GetIterator(isolate))
|
|
.FromJust());
|
|
}
|
|
|
|
|
|
static void IndexedPropertyEnumeratorException(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
info.GetIsolate()->ThrowException(v8_num(42));
|
|
}
|
|
|
|
|
|
THREADED_TEST(GetOwnPropertyNamesWithIndexedInterceptorExceptions_regress4026) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
|
|
|
|
obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
|
|
obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
|
|
// First just try a failing indexed interceptor.
|
|
obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
|
|
NULL, NULL, NULL, NULL, IndexedPropertyEnumeratorException));
|
|
|
|
LocalContext context;
|
|
v8::Local<v8::Object> global = context->Global();
|
|
global->Set(context.local(), v8_str("object"),
|
|
obj_template->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
v8::Local<v8::Value> result = CompileRun(
|
|
"var result = []; "
|
|
"try { "
|
|
" for (var k in object) result .push(k);"
|
|
"} catch (e) {"
|
|
" result = e"
|
|
"}"
|
|
"result ");
|
|
CHECK(!result->IsArray());
|
|
CHECK(v8_num(42)->Equals(context.local(), result).FromJust());
|
|
|
|
result = CompileRun(
|
|
"var result = [];"
|
|
"try { "
|
|
" result = Object.keys(object);"
|
|
"} catch (e) {"
|
|
" result = e;"
|
|
"}"
|
|
"result");
|
|
CHECK(!result->IsArray());
|
|
CHECK(v8_num(42)->Equals(context.local(), result).FromJust());
|
|
}
|
|
|
|
|
|
static void NamedPropertyEnumeratorException(
|
|
const v8::PropertyCallbackInfo<v8::Array>& info) {
|
|
info.GetIsolate()->ThrowException(v8_num(43));
|
|
}
|
|
|
|
|
|
THREADED_TEST(GetOwnPropertyNamesWithNamedInterceptorExceptions_regress4026) {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
|
|
|
|
obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
|
|
obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
|
|
// First just try a failing indexed interceptor.
|
|
obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
NULL, NULL, NULL, NULL, NamedPropertyEnumeratorException));
|
|
|
|
LocalContext context;
|
|
v8::Local<v8::Object> global = context->Global();
|
|
global->Set(context.local(), v8_str("object"),
|
|
obj_template->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
v8::Local<v8::Value> result = CompileRun(
|
|
"var result = []; "
|
|
"try { "
|
|
" for (var k in object) result.push(k);"
|
|
"} catch (e) {"
|
|
" result = e"
|
|
"}"
|
|
"result");
|
|
CHECK(!result->IsArray());
|
|
CHECK(v8_num(43)->Equals(context.local(), result).FromJust());
|
|
|
|
result = CompileRun(
|
|
"var result = [];"
|
|
"try { "
|
|
" result = Object.keys(object);"
|
|
"} catch (e) {"
|
|
" result = e;"
|
|
"}"
|
|
"result");
|
|
CHECK(!result->IsArray());
|
|
CHECK(v8_num(43)->Equals(context.local(), result).FromJust());
|
|
}
|
|
|
|
namespace {
|
|
|
|
template <typename T>
|
|
Local<Object> BuildWrappedObject(v8::Isolate* isolate, T* data) {
|
|
auto templ = v8::ObjectTemplate::New(isolate);
|
|
templ->SetInternalFieldCount(1);
|
|
auto instance =
|
|
templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
|
|
instance->SetAlignedPointerInInternalField(0, data);
|
|
return instance;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
T* GetWrappedObject(Local<Value> data) {
|
|
return reinterpret_cast<T*>(
|
|
Object::Cast(*data)->GetAlignedPointerFromInternalField(0));
|
|
}
|
|
|
|
|
|
struct AccessCheckData {
|
|
int count;
|
|
bool result;
|
|
};
|
|
|
|
AccessCheckData* g_access_check_data = nullptr;
|
|
|
|
bool SimpleAccessChecker(Local<v8::Context> accessing_context,
|
|
Local<v8::Object> access_object,
|
|
Local<v8::Value> data) {
|
|
g_access_check_data->count++;
|
|
return g_access_check_data->result;
|
|
}
|
|
|
|
|
|
struct ShouldInterceptData {
|
|
int value;
|
|
bool should_intercept;
|
|
};
|
|
|
|
|
|
void ShouldNamedInterceptor(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CheckReturnValue(info, FUNCTION_ADDR(ShouldNamedInterceptor));
|
|
auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
|
|
if (!data->should_intercept) return;
|
|
info.GetReturnValue().Set(v8_num(data->value));
|
|
}
|
|
|
|
|
|
void ShouldIndexedInterceptor(uint32_t,
|
|
const v8::PropertyCallbackInfo<Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
CheckReturnValue(info, FUNCTION_ADDR(ShouldIndexedInterceptor));
|
|
auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
|
|
if (!data->should_intercept) return;
|
|
info.GetReturnValue().Set(v8_num(data->value));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
TEST(NamedAllCanReadInterceptor) {
|
|
auto isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
LocalContext context;
|
|
|
|
AccessCheckData access_check_data;
|
|
access_check_data.result = true;
|
|
access_check_data.count = 0;
|
|
|
|
g_access_check_data = &access_check_data;
|
|
|
|
ShouldInterceptData intercept_data_0;
|
|
intercept_data_0.value = 239;
|
|
intercept_data_0.should_intercept = true;
|
|
|
|
ShouldInterceptData intercept_data_1;
|
|
intercept_data_1.value = 165;
|
|
intercept_data_1.should_intercept = false;
|
|
|
|
auto intercepted_0 = v8::ObjectTemplate::New(isolate);
|
|
{
|
|
v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
|
|
conf.data =
|
|
BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0);
|
|
intercepted_0->SetHandler(conf);
|
|
}
|
|
|
|
auto intercepted_1 = v8::ObjectTemplate::New(isolate);
|
|
{
|
|
v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
|
|
conf.data =
|
|
BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1);
|
|
intercepted_1->SetHandler(conf);
|
|
}
|
|
|
|
auto checked = v8::ObjectTemplate::New(isolate);
|
|
checked->SetAccessCheckCallback(SimpleAccessChecker);
|
|
|
|
context->Global()
|
|
->Set(context.local(), v8_str("intercepted_0"),
|
|
intercepted_0->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("intercepted_1"),
|
|
intercepted_1->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
auto checked_instance =
|
|
checked->NewInstance(context.local()).ToLocalChecked();
|
|
checked_instance->Set(context.local(), v8_str("whatever"), v8_num(17))
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("checked"), checked_instance)
|
|
.FromJust();
|
|
CompileRun(
|
|
"checked.__proto__ = intercepted_1;"
|
|
"intercepted_1.__proto__ = intercepted_0;");
|
|
|
|
CHECK_EQ(3, access_check_data.count);
|
|
|
|
ExpectInt32("checked.whatever", 17);
|
|
CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')")
|
|
->IsUndefined());
|
|
CHECK_EQ(5, access_check_data.count);
|
|
|
|
access_check_data.result = false;
|
|
ExpectInt32("checked.whatever", intercept_data_0.value);
|
|
{
|
|
v8::TryCatch try_catch(isolate);
|
|
CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')");
|
|
CHECK(try_catch.HasCaught());
|
|
}
|
|
CHECK_EQ(7, access_check_data.count);
|
|
|
|
intercept_data_1.should_intercept = true;
|
|
ExpectInt32("checked.whatever", intercept_data_1.value);
|
|
{
|
|
v8::TryCatch try_catch(isolate);
|
|
CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')");
|
|
CHECK(try_catch.HasCaught());
|
|
}
|
|
CHECK_EQ(9, access_check_data.count);
|
|
g_access_check_data = nullptr;
|
|
}
|
|
|
|
|
|
TEST(IndexedAllCanReadInterceptor) {
|
|
auto isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
LocalContext context;
|
|
|
|
AccessCheckData access_check_data;
|
|
access_check_data.result = true;
|
|
access_check_data.count = 0;
|
|
|
|
g_access_check_data = &access_check_data;
|
|
|
|
ShouldInterceptData intercept_data_0;
|
|
intercept_data_0.value = 239;
|
|
intercept_data_0.should_intercept = true;
|
|
|
|
ShouldInterceptData intercept_data_1;
|
|
intercept_data_1.value = 165;
|
|
intercept_data_1.should_intercept = false;
|
|
|
|
auto intercepted_0 = v8::ObjectTemplate::New(isolate);
|
|
{
|
|
v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
|
|
conf.data =
|
|
BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0);
|
|
intercepted_0->SetHandler(conf);
|
|
}
|
|
|
|
auto intercepted_1 = v8::ObjectTemplate::New(isolate);
|
|
{
|
|
v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
|
|
conf.data =
|
|
BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1);
|
|
intercepted_1->SetHandler(conf);
|
|
}
|
|
|
|
auto checked = v8::ObjectTemplate::New(isolate);
|
|
checked->SetAccessCheckCallback(SimpleAccessChecker);
|
|
|
|
context->Global()
|
|
->Set(context.local(), v8_str("intercepted_0"),
|
|
intercepted_0->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("intercepted_1"),
|
|
intercepted_1->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
auto checked_instance =
|
|
checked->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("checked"), checked_instance)
|
|
.FromJust();
|
|
checked_instance->Set(context.local(), 15, v8_num(17)).FromJust();
|
|
CompileRun(
|
|
"checked.__proto__ = intercepted_1;"
|
|
"intercepted_1.__proto__ = intercepted_0;");
|
|
|
|
CHECK_EQ(3, access_check_data.count);
|
|
|
|
access_check_data.result = true;
|
|
ExpectInt32("checked[15]", 17);
|
|
CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, '15')")
|
|
->IsUndefined());
|
|
CHECK_EQ(5, access_check_data.count);
|
|
|
|
access_check_data.result = false;
|
|
ExpectInt32("checked[15]", intercept_data_0.value);
|
|
{
|
|
v8::TryCatch try_catch(isolate);
|
|
CompileRun("Object.getOwnPropertyDescriptor(checked, '15')");
|
|
CHECK(try_catch.HasCaught());
|
|
}
|
|
CHECK_EQ(7, access_check_data.count);
|
|
|
|
intercept_data_1.should_intercept = true;
|
|
ExpectInt32("checked[15]", intercept_data_1.value);
|
|
{
|
|
v8::TryCatch try_catch(isolate);
|
|
CompileRun("Object.getOwnPropertyDescriptor(checked, '15')");
|
|
CHECK(try_catch.HasCaught());
|
|
}
|
|
CHECK_EQ(9, access_check_data.count);
|
|
|
|
g_access_check_data = nullptr;
|
|
}
|
|
|
|
|
|
THREADED_TEST(NonMaskingInterceptorOwnProperty) {
|
|
auto isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
LocalContext context;
|
|
|
|
ShouldInterceptData intercept_data;
|
|
intercept_data.value = 239;
|
|
intercept_data.should_intercept = true;
|
|
|
|
auto interceptor_templ = v8::ObjectTemplate::New(isolate);
|
|
v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kNonMasking;
|
|
conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
|
|
interceptor_templ->SetHandler(conf);
|
|
|
|
auto interceptor =
|
|
interceptor_templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"), interceptor)
|
|
.FromJust();
|
|
|
|
ExpectInt32("obj.whatever", 239);
|
|
|
|
CompileRun("obj.whatever = 4;");
|
|
|
|
// obj.whatever exists, thus it is not affected by the non-masking
|
|
// interceptor.
|
|
ExpectInt32("obj.whatever", 4);
|
|
|
|
CompileRun("delete obj.whatever;");
|
|
ExpectInt32("obj.whatever", 239);
|
|
}
|
|
|
|
|
|
THREADED_TEST(NonMaskingInterceptorPrototypeProperty) {
|
|
auto isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
LocalContext context;
|
|
|
|
ShouldInterceptData intercept_data;
|
|
intercept_data.value = 239;
|
|
intercept_data.should_intercept = true;
|
|
|
|
auto interceptor_templ = v8::ObjectTemplate::New(isolate);
|
|
v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kNonMasking;
|
|
conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
|
|
interceptor_templ->SetHandler(conf);
|
|
|
|
auto interceptor =
|
|
interceptor_templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"), interceptor)
|
|
.FromJust();
|
|
|
|
ExpectInt32("obj.whatever", 239);
|
|
|
|
CompileRun("obj.__proto__ = {'whatever': 4};");
|
|
ExpectInt32("obj.whatever", 4);
|
|
|
|
CompileRun("delete obj.__proto__.whatever;");
|
|
ExpectInt32("obj.whatever", 239);
|
|
}
|
|
|
|
|
|
THREADED_TEST(NonMaskingInterceptorPrototypePropertyIC) {
|
|
auto isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
LocalContext context;
|
|
|
|
ShouldInterceptData intercept_data;
|
|
intercept_data.value = 239;
|
|
intercept_data.should_intercept = true;
|
|
|
|
auto interceptor_templ = v8::ObjectTemplate::New(isolate);
|
|
v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
|
|
conf.flags = v8::PropertyHandlerFlags::kNonMasking;
|
|
conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
|
|
interceptor_templ->SetHandler(conf);
|
|
|
|
auto interceptor =
|
|
interceptor_templ->NewInstance(context.local()).ToLocalChecked();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("obj"), interceptor)
|
|
.FromJust();
|
|
|
|
CompileRun(
|
|
"outer = {};"
|
|
"outer.__proto__ = obj;"
|
|
"function f(obj) {"
|
|
" var x;"
|
|
" for (var i = 0; i < 4; i++) {"
|
|
" x = obj.whatever;"
|
|
" }"
|
|
" return x;"
|
|
"}");
|
|
|
|
// Receiver == holder.
|
|
CompileRun("obj.__proto__ = null;");
|
|
ExpectInt32("f(obj)", 239);
|
|
ExpectInt32("f(outer)", 239);
|
|
|
|
// Receiver != holder.
|
|
CompileRun("Object.setPrototypeOf(obj, {});");
|
|
ExpectInt32("f(obj)", 239);
|
|
ExpectInt32("f(outer)", 239);
|
|
|
|
// Masked value on prototype.
|
|
CompileRun("obj.__proto__.whatever = 4;");
|
|
CompileRun("obj.__proto__.__proto__ = { 'whatever' : 5 };");
|
|
ExpectInt32("f(obj)", 4);
|
|
ExpectInt32("f(outer)", 4);
|
|
|
|
// Masked value on prototype prototype.
|
|
CompileRun("delete obj.__proto__.whatever;");
|
|
ExpectInt32("f(obj)", 5);
|
|
ExpectInt32("f(outer)", 5);
|
|
|
|
// Reset.
|
|
CompileRun("delete obj.__proto__.__proto__.whatever;");
|
|
ExpectInt32("f(obj)", 239);
|
|
ExpectInt32("f(outer)", 239);
|
|
|
|
// Masked value on self.
|
|
CompileRun("obj.whatever = 4;");
|
|
ExpectInt32("f(obj)", 4);
|
|
ExpectInt32("f(outer)", 4);
|
|
|
|
// Reset.
|
|
CompileRun("delete obj.whatever;");
|
|
ExpectInt32("f(obj)", 239);
|
|
ExpectInt32("f(outer)", 239);
|
|
|
|
CompileRun("outer.whatever = 4;");
|
|
ExpectInt32("f(obj)", 239);
|
|
ExpectInt32("f(outer)", 4);
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
void DatabaseGetter(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
auto context = info.GetIsolate()->GetCurrentContext();
|
|
Local<v8::Object> db = info.Holder()
|
|
->GetRealNamedProperty(context, v8_str("db"))
|
|
.ToLocalChecked()
|
|
.As<v8::Object>();
|
|
if (!db->Has(context, name).FromJust()) return;
|
|
info.GetReturnValue().Set(db->Get(context, name).ToLocalChecked());
|
|
}
|
|
|
|
|
|
void DatabaseSetter(Local<Name> name, Local<Value> value,
|
|
const v8::PropertyCallbackInfo<Value>& info) {
|
|
ApiTestFuzzer::Fuzz();
|
|
auto context = info.GetIsolate()->GetCurrentContext();
|
|
if (name->Equals(context, v8_str("db")).FromJust()) return;
|
|
Local<v8::Object> db = info.Holder()
|
|
->GetRealNamedProperty(context, v8_str("db"))
|
|
.ToLocalChecked()
|
|
.As<v8::Object>();
|
|
db->Set(context, name, value).FromJust();
|
|
info.GetReturnValue().Set(value);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
THREADED_TEST(NonMaskingInterceptorGlobalEvalRegression) {
|
|
auto isolate = CcTest::isolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
LocalContext context;
|
|
|
|
auto interceptor_templ = v8::ObjectTemplate::New(isolate);
|
|
v8::NamedPropertyHandlerConfiguration conf(DatabaseGetter, DatabaseSetter);
|
|
conf.flags = v8::PropertyHandlerFlags::kNonMasking;
|
|
interceptor_templ->SetHandler(conf);
|
|
|
|
context->Global()
|
|
->Set(context.local(), v8_str("intercepted_1"),
|
|
interceptor_templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
context->Global()
|
|
->Set(context.local(), v8_str("intercepted_2"),
|
|
interceptor_templ->NewInstance(context.local()).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
// Init dbs.
|
|
CompileRun(
|
|
"intercepted_1.db = {};"
|
|
"intercepted_2.db = {};");
|
|
|
|
ExpectInt32(
|
|
"var obj = intercepted_1;"
|
|
"obj.x = 4;"
|
|
"eval('obj.x');"
|
|
"eval('obj.x');"
|
|
"eval('obj.x');"
|
|
"obj = intercepted_2;"
|
|
"obj.x = 9;"
|
|
"eval('obj.x');",
|
|
9);
|
|
}
|
|
|
|
static void CheckReceiver(Local<Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
CHECK(info.This()->IsObject());
|
|
}
|
|
|
|
TEST(Regress609134Interceptor) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
auto fun_templ = v8::FunctionTemplate::New(isolate);
|
|
fun_templ->InstanceTemplate()->SetHandler(
|
|
v8::NamedPropertyHandlerConfiguration(CheckReceiver));
|
|
|
|
CHECK(env->Global()
|
|
->Set(env.local(), v8_str("Fun"),
|
|
fun_templ->GetFunction(env.local()).ToLocalChecked())
|
|
.FromJust());
|
|
|
|
CompileRun(
|
|
"var f = new Fun();"
|
|
"Number.prototype.__proto__ = f;"
|
|
"var a = 42;"
|
|
"for (var i = 0; i<3; i++) { a.foo; }");
|
|
}
|