| Index: test/cctest/test-api.cc
|
| ===================================================================
|
| --- test/cctest/test-api.cc (revision 2479)
|
| +++ test/cctest/test-api.cc (working copy)
|
| @@ -5024,6 +5024,236 @@
|
| }
|
|
|
|
|
| +// 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);
|
| +}
|
| +
|
| +
|
| +// 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 v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
|
| + ApiTestFuzzer::Fuzz();
|
| + return v8_num(239);
|
| +}
|
| +
|
| +
|
| +static void SetOnThis(Local<String> name,
|
| + Local<Value> value,
|
| + const AccessorInfo& info) {
|
| + info.This()->ForceSet(name, value);
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
|
| + v8::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
|
| + templ->SetAccessor(v8_str("y"), Return239);
|
| + LocalContext context;
|
| + context->Global()->Set(v8_str("o"), templ->NewInstance());
|
| + v8::Handle<Value> value = CompileRun(
|
| + "var result = 0;"
|
| + "for (var i = 0; i < 7; i++) {"
|
| + " result = o.y;"
|
| + "}");
|
| + CHECK_EQ(239, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
|
| + v8::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
| + templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
|
| + templ_p->SetAccessor(v8_str("y"), Return239);
|
| +
|
| + 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;"
|
| + "var result = 0;"
|
| + "for (var i = 0; i < 7; i++) {"
|
| + " result = o.x + o.y;"
|
| + "}");
|
| + CHECK_EQ(239 + 42, value->Int32Value());
|
| +}
|
| +
|
| +
|
| +THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
|
| + v8::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
|
| + templ->SetAccessor(v8_str("y"), Return239);
|
| +
|
| + 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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
| + templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
|
| + templ_p->SetAccessor(v8_str("y"), Return239);
|
| +
|
| + 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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
| + templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
|
| + templ_p->SetAccessor(v8_str("y"), Return239, 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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
| + templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
|
| + v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
|
| + templ_p->SetAccessor(v8_str("y"), Return239, 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 v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
|
| const AccessorInfo& info) {
|
| ApiTestFuzzer::Fuzz();
|
| @@ -5108,6 +5338,192 @@
|
| 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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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 v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
|
| + const AccessorInfo& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + CHECK(v8_str("x")->Equals(name));
|
| + return 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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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(
|
| + "o.__proto__.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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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());
|
| +}
|
| +
|
| +
|
| +static v8::Handle<Value> call_ic_function5;
|
| +static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
|
| + const AccessorInfo& info) {
|
| + ApiTestFuzzer::Fuzz();
|
| + if (v8_str("x")->Equals(name))
|
| + return call_ic_function5;
|
| + else
|
| + return Local<Value>();
|
| +}
|
| +
|
| +
|
| +// This test checks that if interceptor doesn't provide a function,
|
| +// cached constant function is used
|
| +THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
|
| + v8::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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());
|
| +}
|
| +
|
| +
|
| +// This test checks that if interceptor provides a function,
|
| +// even if we cached constant function, interceptor's function
|
| +// is invoked
|
| +THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
|
| + v8::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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());
|
| +}
|
| +
|
| +
|
| +// Test the case when we stored constant function into
|
| +// a stub, but it got invalidated later on
|
| +THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
|
| + v8::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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::HandleScope scope;
|
| + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
| + templ->SetNamedPropertyHandler(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());
|
| +}
|
| +
|
| +
|
| static int interceptor_call_count = 0;
|
|
|
| static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
|
|
|