91f98a8f7c
... in various components. Bug: v8:11880 Change-Id: I1e4411ec38a4b15e505bda35a92987972e89d9d0 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3777718 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Patrick Thier <pthier@chromium.org> Cr-Commit-Position: refs/heads/main@{#81863}
939 lines
32 KiB
C++
939 lines
32 KiB
C++
// 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 "include/v8-function.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*>(info.Data().As<v8::External>()->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*>(info.Data().As<v8::External>()->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++) {
|
|
USE(v8::String::NewFromUtf8Literal(info.GetIsolate(), "foo"));
|
|
}
|
|
info.GetReturnValue().Set(
|
|
v8::String::NewFromUtf8Literal(info.GetIsolate(), "foo"));
|
|
}
|
|
|
|
|
|
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 inner_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::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
|
|
i::StackFrameIterator iter(isolate);
|
|
for (int i = 0; !iter.done(); i++) {
|
|
i::StackFrame* frame = iter.frame();
|
|
CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
|
|
i::CodeT code = frame->LookupCodeT().ToCodeT();
|
|
CHECK(code.contains(isolate, 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) {
|
|
// The request is not intercepted so don't call ApiTestFuzzer::Fuzz() here.
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(ObjectSetLazyDataPropertyForIndex) {
|
|
// Regression test for crbug.com/1136800 .
|
|
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());
|
|
|
|
static int getter_call_count;
|
|
getter_call_count = 0;
|
|
auto result = obj->SetLazyDataProperty(
|
|
env.local(), v8_str("1"),
|
|
[](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[1]", 1);
|
|
CHECK_EQ(1, getter_call_count);
|
|
}
|
|
}
|
|
|
|
TEST(ObjectTemplateSetLazyPropertySurvivesIC) {
|
|
i::FLAG_allow_natives_syntax = true;
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
|
|
static int getter_call_count = 0;
|
|
templ->SetLazyDataProperty(
|
|
v8_str("foo"), [](v8::Local<v8::Name> name,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
getter_call_count++;
|
|
info.GetReturnValue().Set(getter_call_count);
|
|
});
|
|
|
|
v8::Local<v8::Function> f = CompileRun(
|
|
"function f(obj) {"
|
|
" obj.foo;"
|
|
" obj.foo;"
|
|
"};"
|
|
"%PrepareFunctionForOptimization(f);"
|
|
"f")
|
|
.As<v8::Function>();
|
|
v8::Local<v8::Value> obj = templ->NewInstance(context).ToLocalChecked();
|
|
f->Call(context, context->Global(), 1, &obj).ToLocalChecked();
|
|
CHECK_EQ(getter_call_count, 1);
|
|
|
|
obj = templ->NewInstance(context).ToLocalChecked();
|
|
f->Call(context, context->Global(), 1, &obj).ToLocalChecked();
|
|
CHECK_EQ(getter_call_count, 2);
|
|
}
|