diff options
Diffstat (limited to 'deps/v8/test/cctest/test-accessors.cc')
-rw-r--r-- | deps/v8/test/cctest/test-accessors.cc | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/deps/v8/test/cctest/test-accessors.cc b/deps/v8/test/cctest/test-accessors.cc new file mode 100644 index 0000000000..25f5c39518 --- /dev/null +++ b/deps/v8/test/cctest/test-accessors.cc @@ -0,0 +1,450 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <stdlib.h> + +#include "v8.h" + +#include "api.h" +#include "cctest.h" +#include "frames-inl.h" +#include "string-stream.h" + +using ::v8::ObjectTemplate; +using ::v8::Value; +using ::v8::Context; +using ::v8::Local; +using ::v8::String; +using ::v8::Script; +using ::v8::Function; +using ::v8::AccessorInfo; +using ::v8::Extension; + +namespace i = ::v8::internal; + +static v8::Handle<Value> handle_property(Local<String> name, + const AccessorInfo&) { + ApiTestFuzzer::Fuzz(); + return v8_num(900); +} + + +THREADED_TEST(PropertyHandler) { + v8::HandleScope scope; + Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property); + LocalContext env; + Local<Function> fun = fun_templ->GetFunction(); + env->Global()->Set(v8_str("Fun"), fun); + Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;"); + CHECK_EQ(900, getter->Run()->Int32Value()); + Local<Script> setter = v8_compile("obj.foo = 901;"); + CHECK_EQ(901, setter->Run()->Int32Value()); +} + + +static v8::Handle<Value> GetIntValue(Local<String> property, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + int* value = + static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); + return v8_num(*value); +} + + +static void SetIntValue(Local<String> property, + Local<Value> value, + const AccessorInfo& info) { + int* field = + static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); + *field = value->Int32Value(); +} + +int foo, bar, baz; + +THREADED_TEST(GlobalVariableAccess) { + foo = 0; + bar = -4; + baz = 10; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); + templ->InstanceTemplate()->SetAccessor(v8_str("foo"), + GetIntValue, + SetIntValue, + v8::External::New(&foo)); + templ->InstanceTemplate()->SetAccessor(v8_str("bar"), + GetIntValue, + SetIntValue, + v8::External::New(&bar)); + templ->InstanceTemplate()->SetAccessor(v8_str("baz"), + GetIntValue, + SetIntValue, + v8::External::New(&baz)); + LocalContext env(0, templ->InstanceTemplate()); + v8_compile("foo = (++bar) + baz")->Run(); + CHECK_EQ(bar, -3); + CHECK_EQ(foo, 7); +} + + +static int x_register = 0; +static v8::Handle<v8::Object> x_receiver; +static v8::Handle<v8::Object> x_holder; + + +static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + CHECK_EQ(x_receiver, info.This()); + CHECK_EQ(x_holder, info.Holder()); + return v8_num(x_register); +} + + +static void XSetter(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + CHECK_EQ(x_holder, info.This()); + CHECK_EQ(x_holder, info.Holder()); + x_register = value->Int32Value(); +} + + +THREADED_TEST(AccessorIC) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("x"), XGetter, XSetter); + LocalContext context; + x_holder = obj->NewInstance(); + context->Global()->Set(v8_str("holder"), x_holder); + x_receiver = v8::Object::New(); + context->Global()->Set(v8_str("obj"), x_receiver); + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun( + "obj.__proto__ = holder;" + "var result = [];" + "for (var i = 0; i < 10; i++) {" + " holder.x = i;" + " result.push(obj.x);" + "}" + "result")); + CHECK_EQ(10, array->Length()); + for (int i = 0; i < 10; i++) { + v8::Handle<Value> entry = array->Get(v8::Integer::New(i)); + CHECK_EQ(v8::Integer::New(i), entry); + } +} + + +static v8::Handle<Value> AccessorProhibitsOverwritingGetter( + Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8::True(); +} + + +THREADED_TEST(AccessorProhibitsOverwriting) { + v8::HandleScope scope; + LocalContext context; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("x"), + AccessorProhibitsOverwritingGetter, + 0, + v8::Handle<Value>(), + v8::PROHIBITS_OVERWRITING, + v8::ReadOnly); + Local<v8::Object> instance = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), instance); + Local<Value> value = CompileRun( + "obj.__defineGetter__('x', function() { return false; });" + "obj.x"); + CHECK(value->BooleanValue()); + value = CompileRun( + "var setter_called = false;" + "obj.__defineSetter__('x', function() { setter_called = true; });" + "obj.x = 42;" + "setter_called"); + CHECK(!value->BooleanValue()); + value = CompileRun( + "obj2 = {};" + "obj2.__proto__ = obj;" + "obj2.__defineGetter__('x', function() { return false; });" + "obj2.x"); + CHECK(value->BooleanValue()); + value = CompileRun( + "var setter_called = false;" + "obj2 = {};" + "obj2.__proto__ = obj;" + "obj2.__defineSetter__('x', function() { setter_called = true; });" + "obj2.x = 42;" + "setter_called"); + CHECK(!value->BooleanValue()); +} + + +template <int C> +static v8::Handle<Value> HandleAllocatingGetter(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + for (int i = 0; i < C; i++) + v8::String::New("foo"); + return v8::String::New("foo"); +} + + +THREADED_TEST(HandleScopePop) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>); + obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + int count_before = i::HandleScope::NumberOfHandles(); + { + v8::HandleScope scope; + CompileRun( + "for (var i = 0; i < 1000; i++) {" + " obj.one;" + " obj.many;" + "}"); + } + int count_after = i::HandleScope::NumberOfHandles(); + CHECK_EQ(count_before, count_after); +} + +static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name, + const AccessorInfo& info) { + CHECK(info.This() == info.Holder()); + CHECK(info.Data()->Equals(v8::String::New("data"))); + ApiTestFuzzer::Fuzz(); + CHECK(info.This() == info.Holder()); + CHECK(info.Data()->Equals(v8::String::New("data"))); + i::Heap::CollectAllGarbage(true); + CHECK(info.This() == info.Holder()); + CHECK(info.Data()->Equals(v8::String::New("data"))); + return v8::Integer::New(17); +} + +THREADED_TEST(DirectCall) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), + CheckAccessorArgsCorrect, + NULL, + v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 10; i++) { + Local<Value> result = scr->Run(); + CHECK(!result.IsEmpty()); + CHECK_EQ(17, result->Int32Value()); + } +} + +static v8::Handle<Value> EmptyGetter(Local<String> name, + const AccessorInfo& info) { + CheckAccessorArgsCorrect(name, info); + ApiTestFuzzer::Fuzz(); + CheckAccessorArgsCorrect(name, info); + return v8::Handle<v8::Value>(); +} + +THREADED_TEST(EmptyResult) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 10; i++) { + Local<Value> result = scr->Run(); + CHECK(result == v8::Undefined()); + } +} + + +THREADED_TEST(NoReuseRegress) { + // Check that the IC generated for the one test doesn't get reused + // for the other. + v8::HandleScope scope; + { + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 2; i++) { + Local<Value> result = scr->Run(); + CHECK(result == v8::Undefined()); + } + } + { + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), + CheckAccessorArgsCorrect, + NULL, + v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 10; i++) { + Local<Value> result = scr->Run(); + CHECK(!result.IsEmpty()); + CHECK_EQ(17, result->Int32Value()); + } + } +} + +static v8::Handle<Value> ThrowingGetAccessor(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8::ThrowException(v8_str("g")); +} + + +static void ThrowingSetAccessor(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + v8::ThrowException(value); +} + + +THREADED_TEST(Regress1054726) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("x"), + ThrowingGetAccessor, + ThrowingSetAccessor, + Local<Value>()); + + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + + // Use the throwing property setter/getter in a loop to force + // the accessor ICs to be initialized. + v8::Handle<Value> result; + result = Script::Compile(v8_str( + "var result = '';" + "for (var i = 0; i < 5; i++) {" + " try { obj.x; } catch (e) { result += e; }" + "}; result"))->Run(); + CHECK_EQ(v8_str("ggggg"), result); + + result = Script::Compile(String::New( + "var result = '';" + "for (var i = 0; i < 5; i++) {" + " try { obj.x = i; } catch (e) { result += e; }" + "}; result"))->Run(); + CHECK_EQ(v8_str("01234"), result); +} + + +static v8::Handle<Value> AllocGetter(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8::Array::New(1000); +} + + +THREADED_TEST(Gc) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), AllocGetter); + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + Script::Compile(String::New( + "var last = [];" + "for (var i = 0; i < 2048; i++) {" + " var result = obj.xxx;" + " result[0] = last;" + " last = result;" + "}"))->Run(); +} + + +static v8::Handle<Value> StackCheck(Local<String> name, + const AccessorInfo& info) { + i::StackFrameIterator iter; + for (int i = 0; !iter.done(); i++) { + i::StackFrame* frame = iter.frame(); + CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT)); + CHECK(frame->code()->IsCode()); + i::Address pc = frame->pc(); + i::Code* code = frame->code(); + CHECK(code->contains(pc)); + iter.Advance(); + } + return v8::Undefined(); +} + + +THREADED_TEST(StackIteration) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + i::StringStream::ClearMentionedObjectCache(); + obj->SetAccessor(v8_str("xxx"), StackCheck); + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + Script::Compile(String::New( + "function foo() {" + " return obj.xxx;" + "}" + "for (var i = 0; i < 100; i++) {" + " foo();" + "}"))->Run(); +} + + +static v8::Handle<Value> AllocateHandles(Local<String> name, + const AccessorInfo& info) { + for (int i = 0; i < i::kHandleBlockSize + 1; i++) { + v8::Local<v8::Value>::New(name); + } + return v8::Integer::New(100); +} + + +THREADED_TEST(HandleScopeSegment) { + // Check that we can return values past popping of handle scope + // segments. + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), AllocateHandles); + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + v8::Handle<v8::Value> result = Script::Compile(String::New( + "var result;" + "for (var i = 0; i < 4; i++)" + " result = obj.xxx;" + "result;"))->Run(); + CHECK_EQ(100, result->Int32Value()); +} |