| Index: test/cctest/test-api-interceptors.cc
|
| diff --git a/test/cctest/test-api-interceptors.cc b/test/cctest/test-api-interceptors.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1e68f944fb09dc6c89aa6a7c424a0525459cbd57
|
| --- /dev/null
|
| +++ b/test/cctest/test-api-interceptors.cc
|
| @@ -0,0 +1,2909 @@
|
| +// 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/parser.h"
|
| +#include "src/smart-pointers.h"
|
| +#include "src/snapshot.h"
|
| +#include "src/unicode-inl.h"
|
| +#include "src/utils.h"
|
| +#include "src/vm-state.h"
|
| +
|
| +using ::v8::Boolean;
|
| +using ::v8::BooleanObject;
|
| +using ::v8::Context;
|
| +using ::v8::Extension;
|
| +using ::v8::Function;
|
| +using ::v8::FunctionTemplate;
|
| +using ::v8::Handle;
|
| +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) {
|
| + Handle<Object> self = Handle<Object>::Cast(info.This());
|
| + info.GetReturnValue().Set(
|
| + self->Get(String::Concat(v8_str("accessor_"), name)));
|
| +}
|
| +
|
| +void SimpleAccessorSetter(Local<String> name, Local<Value> value,
|
| + const v8::PropertyCallbackInfo<void>& info) {
|
| + Handle<Object> self = Handle<Object>::Cast(info.This());
|
| + self->Set(String::Concat(v8_str("accessor_"), name), value);
|
| +}
|
| +
|
| +
|
| +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;
|
| + }
|
| + Handle<Object> self = Handle<Object>::Cast(info.This());
|
| + info.GetReturnValue().Set(self->GetHiddenValue(v8_str(name_str + i)));
|
| +}
|
| +
|
| +
|
| +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;
|
| +
|
| + if (value->IsInt32() && value->Int32Value() < 10000) {
|
| + Handle<Object> self = Handle<Object>::Cast(info.This());
|
| + self->SetHiddenValue(name, value);
|
| + 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);
|
| + }
|
| +
|
| + Handle<Object> self = Handle<Object>::Cast(info.This());
|
| + info.GetReturnValue().Set(self->Get(str));
|
| +}
|
| +
|
| +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);
|
| + }
|
| +
|
| + Handle<Object> self = Handle<Object>::Cast(info.This());
|
| + self->Set(str, value);
|
| + info.GetReturnValue().Set(value);
|
| +}
|
| +
|
| +void AddAccessor(Handle<FunctionTemplate> templ, Handle<String> name,
|
| + v8::AccessorGetterCallback getter,
|
| + v8::AccessorSetterCallback setter) {
|
| + templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
|
| +}
|
| +
|
| +void AddInterceptor(Handle<FunctionTemplate> templ,
|
| + v8::NamedPropertyGetterCallback getter,
|
| + v8::NamedPropertySetterCallback setter) {
|
| + templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter);
|
| +}
|
| +
|
| +
|
| +void AddAccessor(Handle<FunctionTemplate> templ, Handle<Name> name,
|
| + v8::AccessorNameGetterCallback getter,
|
| + v8::AccessorNameSetterCallback setter) {
|
| + templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
|
| +}
|
| +
|
| +void AddInterceptor(Handle<FunctionTemplate> templ,
|
| + v8::GenericNamedPropertyGetterCallback getter,
|
| + v8::GenericNamedPropertySetterCallback setter) {
|
| + templ->InstanceTemplate()->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(getter, setter));
|
| +}
|
| +
|
| +
|
| +v8::Handle<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(bottom));
|
| +}
|
| +
|
| +void CheckThisNamedPropertyHandler(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +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(bottom));
|
| +}
|
| +
|
| +
|
| +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(bottom));
|
| +}
|
| +
|
| +void CheckThisIndexedPropertyQuery(
|
| + uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +
|
| +void CheckThisNamedPropertyQuery(
|
| + Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +
|
| +void CheckThisIndexedPropertyDeleter(
|
| + uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +
|
| +void CheckThisNamedPropertyDeleter(
|
| + Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +
|
| +void CheckThisIndexedPropertyEnumerator(
|
| + const v8::PropertyCallbackInfo<v8::Array>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +
|
| +void CheckThisNamedPropertyEnumerator(
|
| + const v8::PropertyCallbackInfo<v8::Array>& info) {
|
| + CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator));
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(info.This()->Equals(bottom));
|
| +}
|
| +
|
| +
|
| +int echo_named_call_count;
|
| +
|
| +
|
| +void EchoNamedProperty(Local<Name> name,
|
| + const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(v8_str("data")->Equals(info.Data()));
|
| + 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::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +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->Global()->Set(v8_str("constructor"), function);
|
| + v8::Handle<Value> value = CompileRun(
|
| + "var o = new constructor();"
|
| + "o.hasOwnProperty('ostehaps');");
|
| + CHECK_EQ(false, value->BooleanValue());
|
| + value = CompileRun(
|
| + "o.ostehaps = 42;"
|
| + "o.hasOwnProperty('ostehaps');");
|
| + CHECK_EQ(true, value->BooleanValue());
|
| + value = CompileRun(
|
| + "var p = new constructor();"
|
| + "p.hasOwnProperty('ostehaps');");
|
| + CHECK_EQ(false, value->BooleanValue());
|
| +}
|
| +
|
| +
|
| +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->Global()->Set(v8_str("constructor"), function);
|
| + // 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::Handle<Value> value = CompileRun(
|
| + "var o = new constructor();"
|
| + "o.__proto__ = new String(x);"
|
| + "o.hasOwnProperty('ostehaps');");
|
| + CHECK_EQ(false, value->BooleanValue());
|
| +}
|
| +
|
| +
|
| +static void CheckInterceptorLoadIC(
|
| + v8::GenericNamedPropertyGetterCallback getter, const char* source,
|
| + int expected) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(getter, 0, 0, 0, 0,
|
| + v8_str("data")));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<Value> value = CompileRun(source);
|
| + CHECK_EQ(expected, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +static void InterceptorLoadICGetter(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + CHECK_EQ(isolate, info.GetIsolate());
|
| + CHECK(v8_str("data")->Equals(info.Data()));
|
| + CHECK(v8_str("x")->Equals(name));
|
| + 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(name)
|
| + ? v8::Handle<v8::Value>(v8::Integer::New(info.GetIsolate(), 42))
|
| + : v8::Handle<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())->ForceSet(name, value);
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + templ->SetAccessor(v8_str("y"), Return239Callback);
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| +
|
| + // Check the case when receiver and interceptor's holder
|
| + // are the same objects.
|
| + v8::Handle<Value> value = CompileRun(
|
| + "var result = 0;"
|
| + "for (var i = 0; i < 7; i++) {"
|
| + " result = o.y;"
|
| + "}");
|
| + CHECK_EQ(239, value->Int32Value());
|
| +
|
| + // 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());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
| + templ_p->SetAccessor(v8_str("y"), Return239Callback);
|
| +
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| + context->Global()->Set(v8_str("p"), templ_p->NewInstance());
|
| +
|
| + // Check the case when receiver and interceptor's holder
|
| + // are the same objects.
|
| + v8::Handle<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());
|
| +
|
| + // 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());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + templ->SetAccessor(v8_str("y"), Return239Callback);
|
| +
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| +
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
| + templ_p->SetAccessor(v8_str("y"), Return239Callback);
|
| +
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| + context->Global()->Set(v8_str("p"), templ_p->NewInstance());
|
| +
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
| + templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
|
| +
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| + context->Global()->Set(v8_str("p"), templ_p->NewInstance());
|
| +
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
|
| + templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
|
| +
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| + context->Global()->Set(v8_str("p"), templ_p->NewInstance());
|
| +
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +static void InterceptorLoadICGetter0(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(v8_str("x")->Equals(name));
|
| + 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) {
|
| + CHECK(v8_str("x")->Equals(key));
|
| + CHECK_EQ(42, value->Int32Value());
|
| + 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
| + InterceptorLoadICGetter, InterceptorStoreICSetter, 0, 0, 0,
|
| + v8_str("data")));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + CompileRun(
|
| + "for (var i = 0; i < 1000; i++) {"
|
| + " o.x = 42;"
|
| + "}");
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorStoreICWithNoSetter) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<Value> value = CompileRun(
|
| + "for (var i = 0; i < 1000; i++) {"
|
| + " o.y = 239;"
|
| + "}"
|
| + "42 + o.y");
|
| + CHECK_EQ(239 + 42, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
|
| + v8::HandleScope scope(CcTest::isolate());
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
| + Handle<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(v8_str("Child"), child->GetFunction());
|
| + 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);
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
| + Handle<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(v8_str("Child"), child->GetFunction());
|
| + env->Global()->Set(v8_str("age"), age);
|
| + 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);
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
| + Handle<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(v8_str("Child"), child->GetFunction());
|
| + env->Global()->Set(v8_str("age"), age);
|
| + env->Global()->Set(v8_str("anon"), anon);
|
| + 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::Handle<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(v8_str("obj"), templ->GetFunction()->NewInstance());
|
| + CHECK_EQ(echo_named_call_count, 0);
|
| + v8_compile("obj.x")->Run();
|
| + CHECK_EQ(echo_named_call_count, 1);
|
| + const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
|
| + v8::Handle<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()->Int32Value());
|
| + CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
|
| + CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
|
| +}
|
| +
|
| +
|
| +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.Data()));
|
| + echo_indexed_call_count++;
|
| + info.GetReturnValue().Set(v8_num(index));
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(IndexedPropertyHandlerGetter) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<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(v8_str("obj"), templ->GetFunction()->NewInstance());
|
| + Local<Script> script = v8_compile("obj[900]");
|
| + CHECK_EQ(script->Run()->Int32Value(), 900);
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(PropertyHandlerInPrototype) {
|
| + LocalContext env;
|
| + v8::Isolate* isolate = env->GetIsolate();
|
| + v8::HandleScope scope(isolate);
|
| +
|
| + // Set up a prototype chain with three interceptors.
|
| + v8::Handle<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()->NewInstance();
|
| + Local<v8::Object> top = templ->GetFunction()->NewInstance();
|
| + Local<v8::Object> middle = templ->GetFunction()->NewInstance();
|
| +
|
| + bottom->SetPrototype(middle);
|
| + middle->SetPrototype(top);
|
| + env->Global()->Set(v8_str("obj"), bottom);
|
| +
|
| + // 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) ;");
|
| +}
|
| +
|
| +
|
| +static void PrePropertyHandlerGet(
|
| + Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("pre")->Equals(key)) {
|
| + info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
|
| + }
|
| +}
|
| +
|
| +
|
| +static void PrePropertyHandlerQuery(
|
| + Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
|
| + if (v8_str("pre")->Equals(key)) {
|
| + info.GetReturnValue().Set(static_cast<int32_t>(v8::None));
|
| + }
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(PrePropertyHandler) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
|
| + desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
| + PrePropertyHandlerGet, 0, PrePropertyHandlerQuery));
|
| + LocalContext env(NULL, desc->InstanceTemplate());
|
| + CompileRun("var pre = 'Object: pre'; var on = 'Object: on';");
|
| + v8::Handle<Value> result_pre = CompileRun("pre");
|
| + CHECK(v8_str("PrePropertyHandler: pre")->Equals(result_pre));
|
| + v8::Handle<Value> result_on = CompileRun("on");
|
| + CHECK(v8_str("Object: on")->Equals(result_on));
|
| + v8::Handle<Value> result_post = CompileRun("post");
|
| + CHECK(result_post.IsEmpty());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(EmptyInterceptorBreakTransitions) {
|
| + v8::HandleScope scope(CcTest::isolate());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Constructor"), templ->GetFunction());
|
| + 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);
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
| + Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
| + child->Inherit(parent);
|
| + AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Child"), child->GetFunction());
|
| + 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);
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
| + auto returns_42 = FunctionTemplate::New(isolate, Returns42);
|
| + parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age"), returns_42);
|
| + Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
| + child->Inherit(parent);
|
| + AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Child"), child->GetFunction());
|
| + 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);
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
|
| + Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
|
| + child->Inherit(parent);
|
| + AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Child"), child->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
| + Handle<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(v8_str("Child"), child->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
| + Handle<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(v8_str("Child"), child->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
| + Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
| + child->Inherit(parent);
|
| + AddInterceptor(child, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Child"), child->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
|
| + Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
|
| + child->Inherit(parent);
|
| + AddInterceptor(child, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Child"), child->GetFunction());
|
| + 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::String> key = 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();
|
| + Local<v8::Object> obj = function->NewInstance();
|
| + CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2302)));
|
| + CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
|
| + 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(v8_str("obj"), templ->NewInstance());
|
| + Local<Script> script = v8_compile("obj.x");
|
| + for (int i = 0; i < 10; i++) {
|
| + Local<Value> result = script->Run();
|
| + CHECK(result->Equals(v8_str("x")));
|
| + }
|
| +}
|
| +
|
| +
|
| +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(v8_str("interceptor_obj"), templ->NewInstance());
|
| + Local<Script> script = v8_compile("interceptor_obj.x");
|
| + for (int i = 0; i < 10; i++) {
|
| + Local<Value> result = script->Run();
|
| + CHECK(result->Equals(v8_str("x")));
|
| + }
|
| + // 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(v8_str("x")));
|
| +}
|
| +
|
| +
|
| +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->Global()->Set(v8_str("interceptor_obj"), object);
|
| +
|
| + // 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(v8_str("interceptor_obj"), object);
|
| + 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(v8_str("x")));
|
| + }
|
| +
|
| + // 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::Handle<v8::Value> proto = info.Holder()->GetPrototype();
|
| + proto.As<v8::Object>()->Set(v8_str("x"),
|
| + v8::Integer::New(info.GetIsolate(), 23));
|
| +}
|
| +
|
| +
|
| +// 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(v8_str("F"), function_template->GetFunction());
|
| + // 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(), 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(v8_str("obj"), templ->NewInstance());
|
| + 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();
|
| + CHECK(v8_num(5)->Equals(result));
|
| + result = setter_script->Run();
|
| + CHECK(v8_num(23)->Equals(result));
|
| + result = interceptor_setter_script->Run();
|
| + CHECK(v8_num(23)->Equals(result));
|
| + result = interceptor_getter_script->Run();
|
| + CHECK(v8_num(625)->Equals(result));
|
| +}
|
| +
|
| +
|
| +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.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(v8_str("obj"), templ->NewInstance());
|
| + // 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();
|
| + CHECK(result->ToObject(isolate)->HasRealIndexedProperty(2000));
|
| + Local<Script> key_count_check = v8_compile("key_count;");
|
| + result = key_count_check->Run();
|
| + CHECK(v8_num(40013)->Equals(result));
|
| +}
|
| +
|
| +
|
| +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());
|
| + // Have to populate the handle manually, as it's not Cast-able.
|
| + i::Handle<i::JSObject> o = v8::Utils::OpenHandle<Object, i::JSObject>(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(v8_str("obj"), templ->NewInstance());
|
| + 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();
|
| + CHECK(v8_num(4)->Equals(result));
|
| +}
|
| +
|
| +
|
| +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(v8_str("obj"), templ->NewInstance());
|
| +
|
| + // 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(v8_str("obj"), templ->NewInstance());
|
| +
|
| + 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");
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
|
| + 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();
|
| + obj->TurnOnAccessCheck();
|
| + context->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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(IndexedInterceptorWithAccessorCheckSwitchedOn) {
|
| + i::FLAG_allow_natives_syntax = true;
|
| + 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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + const char* code =
|
| + "var result = 'PASSED';"
|
| + "for (var i = 0; i < 100; i++) {"
|
| + " var expected = i;"
|
| + " if (i == 5) {"
|
| + " %EnableAccessChecks(obj);"
|
| + " }"
|
| + " try {"
|
| + " var v = obj[i];"
|
| + " if (i == 5) {"
|
| + " result = 'Should not have reached this!';"
|
| + " break;"
|
| + " } else if (v != expected) {"
|
| + " result = 'Wrong value ' + v + ' at iteration ' + i;"
|
| + " break;"
|
| + " }"
|
| + " } catch (e) {"
|
| + " if (i != 5) {"
|
| + " result = e;"
|
| + " }"
|
| + " }"
|
| + " if (i == 5) %DisableAccessChecks(obj);"
|
| + "}"
|
| + "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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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->Global()->Set(v8_str("obj"), obj);
|
| +
|
| + 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(v8_str("foo"))) {
|
| + 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::Handle<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(v8_str("k"), obj->NewInstance());
|
| + CompileRun(
|
| + "k.foo = 'foo';"
|
| + "k.bar = 'bar';"
|
| + "k[2] = 2;"
|
| + "k[4] = 4;");
|
| + CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
|
| + CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
|
| +
|
| + CHECK(v8_compile("k.foo")->Run()->Equals(v8_str("foo")));
|
| + CHECK(v8_compile("k.bar")->Run()->IsUndefined());
|
| +
|
| + CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
|
| + CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
|
| +
|
| + CHECK(v8_compile("k[2]")->Run()->Equals(v8_num(2)));
|
| + CHECK(v8_compile("k[4]")->Run()->IsUndefined());
|
| +}
|
| +
|
| +
|
| +static void GetK(Local<Name> name,
|
| + const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (name->Equals(v8_str("foo")) || name->Equals(v8_str("bar")) ||
|
| + name->Equals(v8_str("baz"))) {
|
| + 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::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 3);
|
| + result->Set(v8::Integer::New(info.GetIsolate(), 0), v8_str("foo"));
|
| + result->Set(v8::Integer::New(info.GetIsolate(), 1), v8_str("bar"));
|
| + result->Set(v8::Integer::New(info.GetIsolate(), 2), v8_str("baz"));
|
| + info.GetReturnValue().Set(result);
|
| +}
|
| +
|
| +
|
| +static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
|
| + result->Set(v8::Integer::New(info.GetIsolate(), 0), v8_str("0"));
|
| + result->Set(v8::Integer::New(info.GetIsolate(), 1), v8_str("1"));
|
| + info.GetReturnValue().Set(result);
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(Enumerators) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<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(v8_str("k"), obj->NewInstance());
|
| + v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
|
| + "k[10] = 0;"
|
| + "k.a = 0;"
|
| + "k[5] = 0;"
|
| + "k.b = 0;"
|
| + "k[4294967295] = 0;"
|
| + "k.c = 0;"
|
| + "k[4294967296] = 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 in numerical order.
|
| + CHECK(v8_str("5")->Equals(result->Get(v8::Integer::New(isolate, 0))));
|
| + CHECK(v8_str("10")->Equals(result->Get(v8::Integer::New(isolate, 1))));
|
| + CHECK(v8_str("140000")->Equals(result->Get(v8::Integer::New(isolate, 2))));
|
| + CHECK(
|
| + v8_str("4294967295")->Equals(result->Get(v8::Integer::New(isolate, 3))));
|
| + // Indexed interceptor properties in the order they are returned
|
| + // from the enumerator interceptor.
|
| + CHECK(v8_str("0")->Equals(result->Get(v8::Integer::New(isolate, 4))));
|
| + CHECK(v8_str("1")->Equals(result->Get(v8::Integer::New(isolate, 5))));
|
| + // Named properties in insertion order.
|
| + CHECK(v8_str("a")->Equals(result->Get(v8::Integer::New(isolate, 6))));
|
| + CHECK(v8_str("b")->Equals(result->Get(v8::Integer::New(isolate, 7))));
|
| + CHECK(v8_str("c")->Equals(result->Get(v8::Integer::New(isolate, 8))));
|
| + CHECK(
|
| + v8_str("4294967296")->Equals(result->Get(v8::Integer::New(isolate, 9))));
|
| + CHECK(v8_str("d")->Equals(result->Get(v8::Integer::New(isolate, 10))));
|
| + CHECK(v8_str("e")->Equals(result->Get(v8::Integer::New(isolate, 11))));
|
| + CHECK(v8_str("30000000000")
|
| + ->Equals(result->Get(v8::Integer::New(isolate, 12))));
|
| + CHECK(v8_str("f")->Equals(result->Get(v8::Integer::New(isolate, 13))));
|
| + // Named interceptor properties.
|
| + CHECK(v8_str("foo")->Equals(result->Get(v8::Integer::New(isolate, 14))));
|
| + CHECK(v8_str("bar")->Equals(result->Get(v8::Integer::New(isolate, 15))));
|
| + CHECK(v8_str("baz")->Equals(result->Get(v8::Integer::New(isolate, 16))));
|
| +}
|
| +
|
| +
|
| +v8::Handle<Value> call_ic_function;
|
| +v8::Handle<Value> call_ic_function2;
|
| +v8::Handle<Value> call_ic_function3;
|
| +
|
| +static void InterceptorCallICGetter(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(v8_str("x")->Equals(name));
|
| + 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + call_ic_function = v8_compile("function f(x) { return x + 1; }; f")->Run();
|
| + v8::Handle<Value> value = CompileRun(
|
| + "var result = 0;"
|
| + "for (var i = 0; i < 1000; i++) {"
|
| + " result = o.x(41);"
|
| + "}");
|
| + CHECK_EQ(42, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +static v8::Handle<Value> call_ic_function4;
|
| +static void InterceptorCallICGetter4(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(v8_str("x")->Equals(name));
|
| + 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + call_ic_function4 = v8_compile("function f(x) { return x - 1; }; f")->Run();
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +static v8::Handle<Value> call_ic_function5;
|
| +static void InterceptorCallICGetter5(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("x")->Equals(name)) 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + call_ic_function5 = v8_compile("function f(x) { return x - 1; }; f")->Run();
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +static v8::Handle<Value> call_ic_function6;
|
| +static void InterceptorCallICGetter6(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("x")->Equals(name)) 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + call_ic_function6 = v8_compile("function f(x) { return x - 1; }; f")->Run();
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| +
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| +
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +v8::Handle<Value> keyed_call_ic_function;
|
| +
|
| +static void InterceptorKeyedCallICGetter(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("x")->Equals(name)) {
|
| + 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + 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(v8_str("result"))->Int32Value());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("proto1"), templ->NewInstance());
|
| + keyed_call_ic_function =
|
| + v8_compile("function f(x) { return x - 1; }; f")->Run();
|
| + 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(v8_str("result"))->Int32Value());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + 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(v8_str("result"))->Int32Value());
|
| +}
|
| +
|
| +
|
| +// 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::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| +
|
| + 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(v8_str("result"))->Int32Value());
|
| + CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
|
| +}
|
| +
|
| +
|
| +// Test the map transition before the interceptor.
|
| +THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
|
| +
|
| + 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(v8_str("result"))->Int32Value());
|
| +}
|
| +
|
| +
|
| +// Test the map transition after the interceptor.
|
| +THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
|
| + templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
| +
|
| + 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(v8_str("result"))->Int32Value());
|
| +}
|
| +
|
| +
|
| +static int interceptor_call_count = 0;
|
| +
|
| +static void InterceptorICRefErrorGetter(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("x")->Equals(name) && 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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter));
|
| + LocalContext context(0, templ, v8::Handle<Value>());
|
| + call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
|
| + v8::Handle<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());
|
| + 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());
|
| +}
|
| +
|
| +
|
| +static int interceptor_ic_exception_get_count = 0;
|
| +
|
| +static void InterceptorICExceptionGetter(
|
| + Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("x")->Equals(name) && ++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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter));
|
| + LocalContext context(0, templ, v8::Handle<Value>());
|
| + call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
|
| + v8::Handle<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());
|
| + 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());
|
| +}
|
| +
|
| +
|
| +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::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
| + templ->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(0, InterceptorICExceptionSetter));
|
| + LocalContext context(0, templ, v8::Handle<Value>());
|
| + v8::Handle<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());
|
| +}
|
| +
|
| +
|
| +// Test that we ignore null interceptors.
|
| +THREADED_TEST(NullNamedInterceptor) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<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::Handle<v8::Object> obj = templ->NewInstance();
|
| + context->Global()->Set(v8_str("obj"), obj);
|
| + v8::Handle<Value> value = CompileRun("obj.x");
|
| + CHECK(value->IsInt32());
|
| + CHECK_EQ(42, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +// Test that we ignore null interceptors.
|
| +THREADED_TEST(NullIndexedInterceptor) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<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::Handle<v8::Object> obj = templ->NewInstance();
|
| + context->Global()->Set(v8_str("obj"), obj);
|
| + v8::Handle<Value> value = CompileRun("obj[42]");
|
| + CHECK(value->IsInt32());
|
| + CHECK_EQ(42, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope scope(isolate);
|
| + v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
|
| + templ->InstanceTemplate()->SetHandler(
|
| + v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("obj"), templ->GetFunction()->NewInstance());
|
| + ExpectTrue("obj.x === 42");
|
| + ExpectTrue("!obj.propertyIsEnumerable('x')");
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(Regress256330) {
|
| + i::FLAG_allow_natives_syntax = true;
|
| + LocalContext context;
|
| + v8::HandleScope scope(context->GetIsolate());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + context->Global()->Set(v8_str("Bug"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Obj"), templ->GetFunction());
|
| + 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());
|
| + Handle<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
|
| + AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
| + context->Global()->Set(v8_str("Bug"), templ->GetFunction());
|
| + CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();");
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(Regress125988) {
|
| + v8::HandleScope scope(CcTest::isolate());
|
| + Handle<FunctionTemplate> intercept = FunctionTemplate::New(CcTest::isolate());
|
| + AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter);
|
| + LocalContext env;
|
| + env->Global()->Set(v8_str("Intercept"), intercept->GetFunction());
|
| + 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::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 1);
|
| + result->Set(0, v8::Integer::New(info.GetIsolate(), 7));
|
| + info.GetReturnValue().Set(result);
|
| +}
|
| +
|
| +
|
| +static void NamedPropertyEnumerator(
|
| + const v8::PropertyCallbackInfo<v8::Array>& info) {
|
| + v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
|
| + result->Set(0, v8_str("x"));
|
| + result->Set(1, v8::Symbol::GetIterator(info.GetIsolate()));
|
| + info.GetReturnValue().Set(result);
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
|
| + v8::Isolate* isolate = CcTest::isolate();
|
| + v8::HandleScope handle_scope(isolate);
|
| + v8::Handle<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::Handle<v8::Object> global = context->Global();
|
| + global->Set(v8_str("object"), obj_template->NewInstance());
|
| +
|
| + v8::Handle<v8::Value> result =
|
| + CompileRun("Object.getOwnPropertyNames(object)");
|
| + CHECK(result->IsArray());
|
| + v8::Handle<v8::Array> result_array = v8::Handle<v8::Array>::Cast(result);
|
| + CHECK_EQ(2u, result_array->Length());
|
| + CHECK(result_array->Get(0)->IsString());
|
| + CHECK(result_array->Get(1)->IsString());
|
| + CHECK(v8_str("7")->Equals(result_array->Get(0)));
|
| + CHECK(v8_str("x")->Equals(result_array->Get(1)));
|
| +
|
| + result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret");
|
| + CHECK(result->IsArray());
|
| + result_array = v8::Handle<v8::Array>::Cast(result);
|
| + CHECK_EQ(2u, result_array->Length());
|
| + CHECK(result_array->Get(0)->IsString());
|
| + CHECK(result_array->Get(1)->IsString());
|
| + CHECK(v8_str("7")->Equals(result_array->Get(0)));
|
| + CHECK(v8_str("x")->Equals(result_array->Get(1)));
|
| +
|
| + result = CompileRun("Object.getOwnPropertySymbols(object)");
|
| + CHECK(result->IsArray());
|
| + result_array = v8::Handle<v8::Array>::Cast(result);
|
| + CHECK_EQ(1u, result_array->Length());
|
| + CHECK(result_array->Get(0)->Equals(v8::Symbol::GetIterator(isolate)));
|
| +}
|
|
|