| // Copyright 2012 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 "src/init/v8.h" |
| |
| #include "src/api/api-inl.h" |
| #include "src/execution/frames-inl.h" |
| #include "src/strings/string-stream.h" |
| #include "test/cctest/cctest.h" |
| |
| using ::v8::ObjectTemplate; |
| using ::v8::Value; |
| using ::v8::Context; |
| using ::v8::Local; |
| using ::v8::Name; |
| using ::v8::String; |
| using ::v8::Script; |
| using ::v8::Function; |
| using ::v8::Extension; |
| |
| static void handle_property(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(900)); |
| } |
| |
| static void handle_property_2(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(902)); |
| } |
| |
| |
| static void handle_property(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(0, info.Length()); |
| info.GetReturnValue().Set(v8_num(907)); |
| } |
| |
| |
| THREADED_TEST(PropertyHandler) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); |
| fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property); |
| Local<v8::FunctionTemplate> getter_templ = |
| v8::FunctionTemplate::New(isolate, handle_property); |
| getter_templ->SetLength(0); |
| fun_templ-> |
| InstanceTemplate()->SetAccessorProperty(v8_str("bar"), getter_templ); |
| fun_templ->InstanceTemplate()-> |
| SetNativeDataProperty(v8_str("instance_foo"), handle_property); |
| fun_templ->SetNativeDataProperty(v8_str("object_foo"), handle_property_2); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("Fun"), fun).FromJust()); |
| Local<Script> getter; |
| Local<Script> setter; |
| // check function instance accessors |
| getter = v8_compile("var obj = new Fun(); obj.instance_foo;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(900, getter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| setter = v8_compile("obj.instance_foo = 901;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(901, setter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| getter = v8_compile("obj.bar;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(907, getter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| setter = v8_compile("obj.bar = 908;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(908, setter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| // check function static accessors |
| getter = v8_compile("Fun.object_foo;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(902, getter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| setter = v8_compile("Fun.object_foo = 903;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(903, setter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| |
| // And now with null prototype. |
| CompileRun(env.local(), "obj.__proto__ = null;"); |
| getter = v8_compile("obj.bar;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(907, getter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| setter = v8_compile("obj.bar = 908;"); |
| for (int i = 0; i < 4; i++) { |
| CHECK_EQ(908, setter->Run(env.local()) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| } |
| |
| |
| static void GetIntValue(Local<String> property, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| int* value = |
| static_cast<int*>(v8::Local<v8::External>::Cast(info.Data())->Value()); |
| info.GetReturnValue().Set(v8_num(*value)); |
| } |
| |
| |
| static void SetIntValue(Local<String> property, |
| Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| int* field = |
| static_cast<int*>(v8::Local<v8::External>::Cast(info.Data())->Value()); |
| *field = value->Int32Value(info.GetIsolate()->GetCurrentContext()).FromJust(); |
| } |
| |
| int foo, bar, baz; |
| |
| THREADED_TEST(GlobalVariableAccess) { |
| foo = 0; |
| bar = -4; |
| baz = 10; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->InstanceTemplate()->SetAccessor( |
| v8_str("foo"), GetIntValue, SetIntValue, |
| v8::External::New(isolate, &foo)); |
| templ->InstanceTemplate()->SetAccessor( |
| v8_str("bar"), GetIntValue, SetIntValue, |
| v8::External::New(isolate, &bar)); |
| templ->InstanceTemplate()->SetAccessor( |
| v8_str("baz"), GetIntValue, SetIntValue, |
| v8::External::New(isolate, &baz)); |
| LocalContext env(nullptr, templ->InstanceTemplate()); |
| v8_compile("foo = (++bar) + baz")->Run(env.local()).ToLocalChecked(); |
| CHECK_EQ(-3, bar); |
| CHECK_EQ(7, foo); |
| } |
| |
| |
| static int x_register[2] = {0, 0}; |
| static v8::Local<v8::Object> x_receiver; |
| static v8::Local<v8::Object> x_holder; |
| |
| template<class Info> |
| static void XGetter(const Info& info, int offset) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = CcTest::isolate(); |
| CHECK_EQ(isolate, info.GetIsolate()); |
| CHECK( |
| x_receiver->Equals(isolate->GetCurrentContext(), info.This()).FromJust()); |
| info.GetReturnValue().Set(v8_num(x_register[offset])); |
| } |
| |
| |
| static void XGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(x_holder->Equals(info.GetIsolate()->GetCurrentContext(), info.Holder()) |
| .FromJust()); |
| XGetter(info, 0); |
| } |
| |
| |
| static void XGetter(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CHECK( |
| x_receiver->Equals(info.GetIsolate()->GetCurrentContext(), info.Holder()) |
| .FromJust()); |
| XGetter(info, 1); |
| } |
| |
| |
| template<class Info> |
| static void XSetter(Local<Value> value, const Info& info, int offset) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| CHECK_EQ(isolate, info.GetIsolate()); |
| CHECK(x_holder->Equals(info.GetIsolate()->GetCurrentContext(), info.This()) |
| .FromJust()); |
| CHECK(x_holder->Equals(info.GetIsolate()->GetCurrentContext(), info.Holder()) |
| .FromJust()); |
| x_register[offset] = |
| value->Int32Value(info.GetIsolate()->GetCurrentContext()).FromJust(); |
| info.GetReturnValue().Set(v8_num(-1)); |
| } |
| |
| |
| static void XSetter(Local<String> name, |
| Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| XSetter(value, info, 0); |
| } |
| |
| |
| static void XSetter(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CHECK_EQ(1, info.Length()); |
| XSetter(info[0], info, 1); |
| } |
| |
| |
| THREADED_TEST(AccessorIC) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("x0"), XGetter, XSetter); |
| obj->SetAccessorProperty(v8_str("x1"), |
| v8::FunctionTemplate::New(isolate, XGetter), |
| v8::FunctionTemplate::New(isolate, XSetter)); |
| x_holder = obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("holder"), x_holder) |
| .FromJust()); |
| x_receiver = v8::Object::New(isolate); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), x_receiver) |
| .FromJust()); |
| v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast( |
| CompileRun("obj.__proto__ = holder;" |
| "var result = [];" |
| "var key_0 = 'x0';" |
| "var key_1 = 'x1';" |
| "for (var j = 0; j < 10; j++) {" |
| " var i = 4*j;" |
| " result.push(holder.x0 = i);" |
| " result.push(obj.x0);" |
| " result.push(holder.x1 = i + 1);" |
| " result.push(obj.x1);" |
| " result.push(holder[key_0] = i + 2);" |
| " result.push(obj[key_0]);" |
| " result.push(holder[key_1] = i + 3);" |
| " result.push(obj[key_1]);" |
| "}" |
| "result")); |
| CHECK_EQ(80u, array->Length()); |
| for (int i = 0; i < 80; i++) { |
| v8::Local<Value> entry = |
| array->Get(context.local(), v8::Integer::New(isolate, i)) |
| .ToLocalChecked(); |
| CHECK(v8::Integer::New(isolate, i / 2) |
| ->Equals(context.local(), entry) |
| .FromJust()); |
| } |
| } |
| |
| |
| template <int C> |
| static void HandleAllocatingGetter( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| for (int i = 0; i < C; i++) { |
| v8::String::NewFromUtf8(info.GetIsolate(), "foo", |
| v8::NewStringType::kNormal) |
| .ToLocalChecked(); |
| } |
| info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), "foo", |
| v8::NewStringType::kNormal) |
| .ToLocalChecked()); |
| } |
| |
| |
| THREADED_TEST(HandleScopePop) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>); |
| obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>); |
| v8::Local<v8::Object> inst = |
| obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("obj"), inst).FromJust()); |
| int count_before = |
| i::HandleScope::NumberOfHandles(reinterpret_cast<i::Isolate*>(isolate)); |
| { |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "for (var i = 0; i < 1000; i++) {" |
| " obj.one;" |
| " obj.many;" |
| "}"); |
| } |
| int count_after = |
| i::HandleScope::NumberOfHandles(reinterpret_cast<i::Isolate*>(isolate)); |
| CHECK_EQ(count_before, count_after); |
| } |
| |
| static void CheckAccessorArgsCorrect( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(info.GetIsolate() == CcTest::isolate()); |
| CHECK(info.This() == info.Holder()); |
| CHECK(info.Data() |
| ->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("data")) |
| .FromJust()); |
| ApiTestFuzzer::Fuzz(); |
| CHECK(info.GetIsolate() == CcTest::isolate()); |
| CHECK(info.This() == info.Holder()); |
| CHECK(info.Data() |
| ->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("data")) |
| .FromJust()); |
| CcTest::CollectAllGarbage(); |
| CHECK(info.GetIsolate() == CcTest::isolate()); |
| CHECK(info.This() == info.Holder()); |
| CHECK(info.Data() |
| ->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("data")) |
| .FromJust()); |
| info.GetReturnValue().Set(17); |
| } |
| |
| |
| THREADED_TEST(DirectCall) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("xxx"), CheckAccessorArgsCorrect, nullptr, |
| v8_str("data")); |
| v8::Local<v8::Object> inst = |
| obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("obj"), inst).FromJust()); |
| Local<Script> scr = |
| v8::Script::Compile(context.local(), v8_str("obj.xxx")).ToLocalChecked(); |
| for (int i = 0; i < 10; i++) { |
| Local<Value> result = scr->Run(context.local()).ToLocalChecked(); |
| CHECK(!result.IsEmpty()); |
| CHECK_EQ(17, result->Int32Value(context.local()).FromJust()); |
| } |
| } |
| |
| static void EmptyGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CheckAccessorArgsCorrect(name, info); |
| ApiTestFuzzer::Fuzz(); |
| CheckAccessorArgsCorrect(name, info); |
| info.GetReturnValue().Set(v8::Local<v8::Value>()); |
| } |
| |
| |
| THREADED_TEST(EmptyResult) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("xxx"), EmptyGetter, nullptr, v8_str("data")); |
| v8::Local<v8::Object> inst = |
| obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("obj"), inst).FromJust()); |
| Local<Script> scr = |
| v8::Script::Compile(context.local(), v8_str("obj.xxx")).ToLocalChecked(); |
| for (int i = 0; i < 10; i++) { |
| Local<Value> result = scr->Run(context.local()).ToLocalChecked(); |
| CHECK(result == v8::Undefined(isolate)); |
| } |
| } |
| |
| |
| THREADED_TEST(NoReuseRegress) { |
| // Check that the IC generated for the one test doesn't get reused |
| // for the other. |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| { |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("xxx"), EmptyGetter, nullptr, v8_str("data")); |
| LocalContext context; |
| v8::Local<v8::Object> inst = |
| obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), inst) |
| .FromJust()); |
| Local<Script> scr = v8::Script::Compile(context.local(), v8_str("obj.xxx")) |
| .ToLocalChecked(); |
| for (int i = 0; i < 2; i++) { |
| Local<Value> result = scr->Run(context.local()).ToLocalChecked(); |
| CHECK(result == v8::Undefined(isolate)); |
| } |
| } |
| { |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("xxx"), CheckAccessorArgsCorrect, nullptr, |
| v8_str("data")); |
| LocalContext context; |
| v8::Local<v8::Object> inst = |
| obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), inst) |
| .FromJust()); |
| Local<Script> scr = v8::Script::Compile(context.local(), v8_str("obj.xxx")) |
| .ToLocalChecked(); |
| for (int i = 0; i < 10; i++) { |
| Local<Value> result = scr->Run(context.local()).ToLocalChecked(); |
| CHECK(!result.IsEmpty()); |
| CHECK_EQ(17, result->Int32Value(context.local()).FromJust()); |
| } |
| } |
| } |
| |
| static void ThrowingGetAccessor( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetIsolate()->ThrowException(v8_str("g")); |
| } |
| |
| |
| static void ThrowingSetAccessor(Local<String> name, |
| Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| info.GetIsolate()->ThrowException(value); |
| } |
| |
| |
| THREADED_TEST(Regress1054726) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("x"), |
| ThrowingGetAccessor, |
| ThrowingSetAccessor, |
| Local<Value>()); |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| // Use the throwing property setter/getter in a loop to force |
| // the accessor ICs to be initialized. |
| v8::Local<Value> result; |
| result = Script::Compile(env.local(), |
| v8_str("var result = '';" |
| "for (var i = 0; i < 5; i++) {" |
| " try { obj.x; } catch (e) { result += e; }" |
| "}; result")) |
| .ToLocalChecked() |
| ->Run(env.local()) |
| .ToLocalChecked(); |
| CHECK(v8_str("ggggg")->Equals(env.local(), result).FromJust()); |
| |
| result = |
| Script::Compile(env.local(), |
| v8_str("var result = '';" |
| "for (var i = 0; i < 5; i++) {" |
| " try { obj.x = i; } catch (e) { result += e; }" |
| "}; result")) |
| .ToLocalChecked() |
| ->Run(env.local()) |
| .ToLocalChecked(); |
| CHECK(v8_str("01234")->Equals(env.local(), result).FromJust()); |
| } |
| |
| |
| static void AllocGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8::Array::New(info.GetIsolate(), 1000)); |
| } |
| |
| |
| THREADED_TEST(Gc) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("xxx"), AllocGetter); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| Script::Compile(env.local(), v8_str("var last = [];" |
| "for (var i = 0; i < 2048; i++) {" |
| " var result = obj.xxx;" |
| " result[0] = last;" |
| " last = result;" |
| "}")) |
| .ToLocalChecked() |
| ->Run(env.local()) |
| .ToLocalChecked(); |
| } |
| |
| |
| static void StackCheck(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| i::StackFrameIterator iter(reinterpret_cast<i::Isolate*>(info.GetIsolate())); |
| for (int i = 0; !iter.done(); i++) { |
| i::StackFrame* frame = iter.frame(); |
| CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT)); |
| i::Code code = frame->LookupCode(); |
| CHECK(code.IsCode()); |
| CHECK(code.contains(frame->pc())); |
| iter.Advance(); |
| } |
| } |
| |
| |
| THREADED_TEST(StackIteration) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| i::StringStream::ClearMentionedObjectCache( |
| reinterpret_cast<i::Isolate*>(isolate)); |
| obj->SetAccessor(v8_str("xxx"), StackCheck); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| Script::Compile(env.local(), v8_str("function foo() {" |
| " return obj.xxx;" |
| "}" |
| "for (var i = 0; i < 100; i++) {" |
| " foo();" |
| "}")) |
| .ToLocalChecked() |
| ->Run(env.local()) |
| .ToLocalChecked(); |
| } |
| |
| |
| static void AllocateHandles(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| for (int i = 0; i < i::kHandleBlockSize + 1; i++) { |
| v8::Local<v8::Value>::New(info.GetIsolate(), name); |
| } |
| info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 100)); |
| } |
| |
| |
| THREADED_TEST(HandleScopeSegment) { |
| // Check that we can return values past popping of handle scope |
| // segments. |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("xxx"), AllocateHandles); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::Local<v8::Value> result = |
| Script::Compile(env.local(), v8_str("var result;" |
| "for (var i = 0; i < 4; i++)" |
| " result = obj.xxx;" |
| "result;")) |
| .ToLocalChecked() |
| ->Run(env.local()) |
| .ToLocalChecked(); |
| CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| void JSONStringifyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) { |
| v8::Local<v8::Array> array = v8::Array::New(info.GetIsolate(), 1); |
| CHECK(array->Set(info.GetIsolate()->GetCurrentContext(), 0, v8_str("regress")) |
| .FromJust()); |
| info.GetReturnValue().Set(array); |
| } |
| |
| |
| void JSONStringifyGetter(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(v8_str("crbug-161028")); |
| } |
| |
| |
| THREADED_TEST(JSONStringifyNamedInterceptorObject) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| JSONStringifyGetter, nullptr, nullptr, nullptr, JSONStringifyEnumerator)); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::Local<v8::String> expected = v8_str("{\"regress\":\"crbug-161028\"}"); |
| CHECK(CompileRun("JSON.stringify(obj)") |
| ->Equals(env.local(), expected) |
| .FromJust()); |
| } |
| |
| |
| static v8::Local<v8::Context> expected_current_context; |
| |
| |
| static void check_contexts(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK(expected_current_context == info.GetIsolate()->GetCurrentContext()); |
| } |
| |
| |
| THREADED_TEST(AccessorPropertyCrossContext) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Function> fun = |
| v8::Function::New(env.local(), check_contexts).ToLocalChecked(); |
| LocalContext switch_context; |
| CHECK(switch_context->Global() |
| ->Set(switch_context.local(), v8_str("fun"), fun) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| expected_current_context = env.local(); |
| CompileRun( |
| "var o = Object.create(null, { n: { get:fun } });" |
| "for (var i = 0; i < 10; i++) o.n;"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectAccessor) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "var set_value = 1;" |
| "Object.defineProperty(this.__proto__, 'x', {" |
| " get : function() { return this; }," |
| " set : function() { set_value = this; }" |
| "});" |
| "function getter() { return x; }" |
| "function setter() { x = 1; }"); |
| |
| Local<Script> check_getter = v8_compile("getter()"); |
| Local<Script> check_setter = v8_compile("setter(); set_value"); |
| |
| // Ensure that LoadGlobalICs in getter and StoreGlobalICs setter get |
| // JSGlobalProxy as a receiver regardless of the current IC state and |
| // the order in which ICs are executed. |
| for (int i = 0; i < 10; i++) { |
| CHECK( |
| v8::Utils::OpenHandle(*check_getter->Run(env.local()).ToLocalChecked()) |
| ->IsJSGlobalProxy()); |
| } |
| for (int i = 0; i < 10; i++) { |
| CHECK( |
| v8::Utils::OpenHandle(*check_setter->Run(env.local()).ToLocalChecked()) |
| ->IsJSGlobalProxy()); |
| } |
| for (int i = 0; i < 10; i++) { |
| CHECK( |
| v8::Utils::OpenHandle(*check_getter->Run(env.local()).ToLocalChecked()) |
| ->IsJSGlobalProxy()); |
| CHECK( |
| v8::Utils::OpenHandle(*check_setter->Run(env.local()).ToLocalChecked()) |
| ->IsJSGlobalProxy()); |
| } |
| } |
| |
| |
| static void EmptyGetter(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| } |
| |
| |
| static void OneProperty(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(1)); |
| } |
| |
| |
| THREADED_TEST(Regress433458) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetHandler(v8::NamedPropertyHandlerConfiguration(EmptyGetter)); |
| obj->SetNativeDataProperty(v8_str("prop"), OneProperty); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "Object.defineProperty(obj, 'prop', { writable: false });" |
| "Object.defineProperty(obj, 'prop', { writable: true });"); |
| } |
| |
| |
| static bool security_check_value = false; |
| |
| static bool SecurityTestCallback(Local<v8::Context> accessing_context, |
| Local<v8::Object> accessed_object, |
| Local<v8::Value> data) { |
| return security_check_value; |
| } |
| |
| |
| TEST(PrototypeGetterAccessCheck) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| auto fun_templ = v8::FunctionTemplate::New(isolate); |
| auto getter_templ = v8::FunctionTemplate::New(isolate, handle_property); |
| getter_templ->SetAcceptAnyReceiver(false); |
| fun_templ->InstanceTemplate()->SetAccessorProperty(v8_str("foo"), |
| getter_templ); |
| auto obj_templ = v8::ObjectTemplate::New(isolate); |
| obj_templ->SetAccessCheckCallback(SecurityTestCallback); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("Fun"), |
| fun_templ->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj_templ->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj2"), |
| obj_templ->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| security_check_value = true; |
| CompileRun("var proto = new Fun();"); |
| CompileRun("obj.__proto__ = proto;"); |
| ExpectInt32("proto.foo", 907); |
| |
| // Test direct. |
| security_check_value = true; |
| ExpectInt32("obj.foo", 907); |
| security_check_value = false; |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun("obj.foo"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| // Test through call. |
| security_check_value = true; |
| ExpectInt32("proto.__lookupGetter__('foo').call(obj)", 907); |
| security_check_value = false; |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun("proto.__lookupGetter__('foo').call(obj)"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| // Test ics. |
| CompileRun( |
| "function f() {" |
| " var x;" |
| " for (var i = 0; i < 4; i++) {" |
| " x = obj.foo;" |
| " }" |
| " return x;" |
| "};" |
| "%PrepareFunctionForOptimization(f);"); |
| |
| security_check_value = true; |
| ExpectInt32("f()", 907); |
| security_check_value = false; |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun("f();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| // Test TurboFan. |
| CompileRun("%OptimizeFunctionOnNextCall(f);"); |
| |
| security_check_value = true; |
| ExpectInt32("f()", 907); |
| security_check_value = false; |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun("f();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| static void CheckReceiver(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(info.This()->IsObject()); |
| } |
| |
| TEST(Regress609134) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| auto fun_templ = v8::FunctionTemplate::New(isolate); |
| fun_templ->InstanceTemplate()->SetNativeDataProperty(v8_str("foo"), |
| CheckReceiver); |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("Fun"), |
| fun_templ->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| CompileRun( |
| "var f = new Fun();" |
| "Number.prototype.__proto__ = f;" |
| "var a = 42;" |
| "for (var i = 0; i<3; i++) { a.foo; }"); |
| } |
| |
| TEST(ObjectSetLazyDataProperty) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); |
| |
| // Despite getting the property multiple times, the getter should only be |
| // called once and data property reads should continue to produce the same |
| // value. |
| static int getter_call_count; |
| getter_call_count = 0; |
| auto result = obj->SetLazyDataProperty( |
| env.local(), v8_str("foo"), |
| [](Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| getter_call_count++; |
| info.GetReturnValue().Set(getter_call_count); |
| }); |
| CHECK(result.FromJust()); |
| CHECK_EQ(0, getter_call_count); |
| for (int i = 0; i < 2; i++) { |
| ExpectInt32("obj.foo", 1); |
| CHECK_EQ(1, getter_call_count); |
| } |
| |
| // Setting should overwrite the data property. |
| result = obj->SetLazyDataProperty( |
| env.local(), v8_str("bar"), |
| [](Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(false); |
| }); |
| CHECK(result.FromJust()); |
| ExpectInt32("obj.bar = -1; obj.bar;", -1); |
| } |