| // 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 <climits> |
| #include <csignal> |
| #include <map> |
| #include <memory> |
| #include <string> |
| |
| #include "test/cctest/test-api.h" |
| |
| #if V8_OS_POSIX |
| #include <unistd.h> // NOLINT |
| #endif |
| |
| #include "include/v8-util.h" |
| #include "src/api.h" |
| #include "src/arguments.h" |
| #include "src/base/platform/platform.h" |
| #include "src/code-stubs.h" |
| #include "src/compilation-cache.h" |
| #include "src/debug/debug.h" |
| #include "src/execution.h" |
| #include "src/futex-emulation.h" |
| #include "src/heap/incremental-marking.h" |
| #include "src/heap/local-allocator.h" |
| #include "src/lookup.h" |
| #include "src/objects-inl.h" |
| #include "src/parsing/preparse-data.h" |
| #include "src/profiler/cpu-profiler.h" |
| #include "src/unicode-inl.h" |
| #include "src/utils.h" |
| #include "src/vm-state.h" |
| #include "test/cctest/heap/heap-tester.h" |
| #include "test/cctest/heap/heap-utils.h" |
| |
| static const bool kLogThreading = false; |
| |
| using ::v8::Array; |
| using ::v8::Boolean; |
| using ::v8::BooleanObject; |
| using ::v8::Context; |
| using ::v8::Extension; |
| using ::v8::Function; |
| using ::v8::FunctionTemplate; |
| using ::v8::HandleScope; |
| using ::v8::Local; |
| using ::v8::Maybe; |
| using ::v8::Message; |
| using ::v8::MessageCallback; |
| using ::v8::Module; |
| using ::v8::Name; |
| using ::v8::None; |
| using ::v8::Object; |
| using ::v8::ObjectTemplate; |
| using ::v8::Persistent; |
| using ::v8::PropertyAttribute; |
| using ::v8::Script; |
| using ::v8::StackTrace; |
| using ::v8::String; |
| using ::v8::Symbol; |
| using ::v8::TryCatch; |
| using ::v8::Undefined; |
| using ::v8::V8; |
| using ::v8::Value; |
| |
| |
| #define THREADED_PROFILED_TEST(Name) \ |
| static void Test##Name(); \ |
| TEST(Name##WithProfiler) { \ |
| RunWithProfiler(&Test##Name); \ |
| } \ |
| THREADED_TEST(Name) |
| |
| |
| void RunWithProfiler(void (*test)()) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<v8::String> profile_name = v8_str("my_profile1"); |
| v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate()); |
| cpu_profiler->StartProfiling(profile_name); |
| (*test)(); |
| reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->DeleteAllProfiles(); |
| cpu_profiler->Dispose(); |
| } |
| |
| |
| static int signature_callback_count; |
| static Local<Value> signature_expected_receiver; |
| static void IncrementingSignatureCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| signature_callback_count++; |
| CHECK(signature_expected_receiver->Equals( |
| args.GetIsolate()->GetCurrentContext(), |
| args.Holder()) |
| .FromJust()); |
| CHECK(signature_expected_receiver->Equals( |
| args.GetIsolate()->GetCurrentContext(), |
| args.This()) |
| .FromJust()); |
| v8::Local<v8::Array> result = |
| v8::Array::New(args.GetIsolate(), args.Length()); |
| for (int i = 0; i < args.Length(); i++) { |
| CHECK(result->Set(args.GetIsolate()->GetCurrentContext(), |
| v8::Integer::New(args.GetIsolate(), i), args[i]) |
| .FromJust()); |
| } |
| args.GetReturnValue().Set(result); |
| } |
| |
| |
| static void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(42); |
| } |
| |
| |
| // Tests that call v8::V8::Dispose() cannot be threaded. |
| UNINITIALIZED_TEST(InitializeAndDisposeOnce) { |
| CHECK(v8::V8::Initialize()); |
| CHECK(v8::V8::Dispose()); |
| } |
| |
| |
| // Tests that call v8::V8::Dispose() cannot be threaded. |
| UNINITIALIZED_TEST(InitializeAndDisposeMultiple) { |
| for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); |
| for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize()); |
| for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); |
| for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize()); |
| for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); |
| } |
| |
| // Tests that Smi::kZero is set up properly. |
| UNINITIALIZED_TEST(SmiZero) { CHECK_EQ(i::Smi::kZero, i::Smi::kZero); } |
| |
| THREADED_TEST(Handles) { |
| v8::HandleScope scope(CcTest::isolate()); |
| Local<Context> local_env; |
| { |
| LocalContext env; |
| local_env = env.local(); |
| } |
| |
| // Local context should still be live. |
| CHECK(!local_env.IsEmpty()); |
| local_env->Enter(); |
| |
| v8::Local<v8::Primitive> undef = v8::Undefined(CcTest::isolate()); |
| CHECK(!undef.IsEmpty()); |
| CHECK(undef->IsUndefined()); |
| |
| const char* source = "1 + 2 + 3"; |
| Local<Script> script = v8_compile(source); |
| CHECK_EQ(6, v8_run_int32value(script)); |
| |
| local_env->Exit(); |
| } |
| |
| |
| THREADED_TEST(IsolateOfContext) { |
| v8::HandleScope scope(CcTest::isolate()); |
| v8::Local<Context> env = Context::New(CcTest::isolate()); |
| |
| CHECK(!env->GetIsolate()->InContext()); |
| CHECK(env->GetIsolate() == CcTest::isolate()); |
| env->Enter(); |
| CHECK(env->GetIsolate()->InContext()); |
| CHECK(env->GetIsolate() == CcTest::isolate()); |
| env->Exit(); |
| CHECK(!env->GetIsolate()->InContext()); |
| CHECK(env->GetIsolate() == CcTest::isolate()); |
| } |
| |
| static void TestSignatureLooped(const char* operation, Local<Value> receiver, |
| v8::Isolate* isolate) { |
| i::ScopedVector<char> source(200); |
| i::SNPrintF(source, |
| "for (var i = 0; i < 10; i++) {" |
| " %s" |
| "}", |
| operation); |
| signature_callback_count = 0; |
| signature_expected_receiver = receiver; |
| bool expected_to_throw = receiver.IsEmpty(); |
| v8::TryCatch try_catch(isolate); |
| CompileRun(source.start()); |
| CHECK_EQ(expected_to_throw, try_catch.HasCaught()); |
| if (!expected_to_throw) { |
| CHECK_EQ(10, signature_callback_count); |
| } else { |
| CHECK(v8_str("TypeError: Illegal invocation") |
| ->Equals(isolate->GetCurrentContext(), |
| try_catch.Exception() |
| ->ToString(isolate->GetCurrentContext()) |
| .ToLocalChecked()) |
| .FromJust()); |
| } |
| } |
| |
| static void TestSignatureOptimized(const char* operation, Local<Value> receiver, |
| v8::Isolate* isolate) { |
| i::ScopedVector<char> source(200); |
| i::SNPrintF(source, |
| "function test() {" |
| " %s" |
| "}" |
| "try { test() } catch(e) {}" |
| "try { test() } catch(e) {}" |
| "%%OptimizeFunctionOnNextCall(test);" |
| "test()", |
| operation); |
| signature_callback_count = 0; |
| signature_expected_receiver = receiver; |
| bool expected_to_throw = receiver.IsEmpty(); |
| v8::TryCatch try_catch(isolate); |
| CompileRun(source.start()); |
| CHECK_EQ(expected_to_throw, try_catch.HasCaught()); |
| if (!expected_to_throw) { |
| CHECK_EQ(3, signature_callback_count); |
| } else { |
| CHECK(v8_str("TypeError: Illegal invocation") |
| ->Equals(isolate->GetCurrentContext(), |
| try_catch.Exception() |
| ->ToString(isolate->GetCurrentContext()) |
| .ToLocalChecked()) |
| .FromJust()); |
| } |
| } |
| |
| static void TestSignature(const char* operation, Local<Value> receiver, |
| v8::Isolate* isolate) { |
| TestSignatureLooped(operation, receiver, isolate); |
| TestSignatureOptimized(operation, receiver, isolate); |
| } |
| |
| THREADED_TEST(ReceiverSignature) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| // Setup templates. |
| v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun); |
| v8::Local<v8::FunctionTemplate> callback_sig = v8::FunctionTemplate::New( |
| isolate, IncrementingSignatureCallback, Local<Value>(), sig); |
| v8::Local<v8::FunctionTemplate> callback = |
| v8::FunctionTemplate::New(isolate, IncrementingSignatureCallback); |
| v8::Local<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(isolate); |
| sub_fun->Inherit(fun); |
| v8::Local<v8::FunctionTemplate> direct_sub_fun = |
| v8::FunctionTemplate::New(isolate); |
| direct_sub_fun->Inherit(fun); |
| v8::Local<v8::FunctionTemplate> unrel_fun = |
| v8::FunctionTemplate::New(isolate); |
| // Install properties. |
| v8::Local<v8::ObjectTemplate> fun_proto = fun->PrototypeTemplate(); |
| fun_proto->Set(v8_str("prop_sig"), callback_sig); |
| fun_proto->Set(v8_str("prop"), callback); |
| fun_proto->SetAccessorProperty( |
| v8_str("accessor_sig"), callback_sig, callback_sig); |
| fun_proto->SetAccessorProperty(v8_str("accessor"), callback, callback); |
| // Instantiate templates. |
| Local<Value> fun_instance = |
| fun->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked(); |
| Local<Value> sub_fun_instance = |
| sub_fun->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked(); |
| // Instance template with properties. |
| v8::Local<v8::ObjectTemplate> direct_instance_templ = |
| direct_sub_fun->InstanceTemplate(); |
| direct_instance_templ->Set(v8_str("prop_sig"), callback_sig); |
| direct_instance_templ->Set(v8_str("prop"), callback); |
| direct_instance_templ->SetAccessorProperty(v8_str("accessor_sig"), |
| callback_sig, callback_sig); |
| direct_instance_templ->SetAccessorProperty(v8_str("accessor"), callback, |
| callback); |
| Local<Value> direct_instance = |
| direct_instance_templ->NewInstance(env.local()).ToLocalChecked(); |
| // Setup global variables. |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("Fun"), |
| fun->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("UnrelFun"), |
| unrel_fun->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("fun_instance"), fun_instance) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("sub_fun_instance"), sub_fun_instance) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("direct_instance"), direct_instance) |
| .FromJust()); |
| CompileRun( |
| "var accessor_sig_key = 'accessor_sig';" |
| "var accessor_key = 'accessor';" |
| "var prop_sig_key = 'prop_sig';" |
| "var prop_key = 'prop';" |
| "" |
| "function copy_props(obj) {" |
| " var keys = [accessor_sig_key, accessor_key, prop_sig_key, prop_key];" |
| " var source = Fun.prototype;" |
| " for (var i in keys) {" |
| " var key = keys[i];" |
| " var desc = Object.getOwnPropertyDescriptor(source, key);" |
| " Object.defineProperty(obj, key, desc);" |
| " }" |
| "}" |
| "" |
| "var plain = {};" |
| "copy_props(plain);" |
| "var unrelated = new UnrelFun();" |
| "copy_props(unrelated);" |
| "var inherited = { __proto__: fun_instance };" |
| "var inherited_direct = { __proto__: direct_instance };"); |
| // Test with and without ICs |
| const char* test_objects[] = { |
| "fun_instance", "sub_fun_instance", "direct_instance", "plain", |
| "unrelated", "inherited", "inherited_direct"}; |
| unsigned bad_signature_start_offset = 3; |
| for (unsigned i = 0; i < arraysize(test_objects); i++) { |
| i::ScopedVector<char> source(200); |
| i::SNPrintF( |
| source, "var test_object = %s; test_object", test_objects[i]); |
| Local<Value> test_object = CompileRun(source.start()); |
| TestSignature("test_object.prop();", test_object, isolate); |
| TestSignature("test_object.accessor;", test_object, isolate); |
| TestSignature("test_object[accessor_key];", test_object, isolate); |
| TestSignature("test_object.accessor = 1;", test_object, isolate); |
| TestSignature("test_object[accessor_key] = 1;", test_object, isolate); |
| if (i >= bad_signature_start_offset) test_object = Local<Value>(); |
| TestSignature("test_object.prop_sig();", test_object, isolate); |
| TestSignature("test_object.accessor_sig;", test_object, isolate); |
| TestSignature("test_object[accessor_sig_key];", test_object, isolate); |
| TestSignature("test_object.accessor_sig = 1;", test_object, isolate); |
| TestSignature("test_object[accessor_sig_key] = 1;", test_object, isolate); |
| } |
| } |
| |
| |
| THREADED_TEST(HulIgennem) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Primitive> undef = v8::Undefined(isolate); |
| Local<String> undef_str = undef->ToString(env.local()).ToLocalChecked(); |
| char* value = i::NewArray<char>(undef_str->Utf8Length() + 1); |
| undef_str->WriteUtf8(value); |
| CHECK_EQ(0, strcmp(value, "undefined")); |
| i::DeleteArray(value); |
| } |
| |
| |
| THREADED_TEST(Access) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::Object> obj = v8::Object::New(isolate); |
| Local<Value> foo_before = |
| obj->Get(env.local(), v8_str("foo")).ToLocalChecked(); |
| CHECK(foo_before->IsUndefined()); |
| Local<String> bar_str = v8_str("bar"); |
| CHECK(obj->Set(env.local(), v8_str("foo"), bar_str).FromJust()); |
| Local<Value> foo_after = |
| obj->Get(env.local(), v8_str("foo")).ToLocalChecked(); |
| CHECK(!foo_after->IsUndefined()); |
| CHECK(foo_after->IsString()); |
| CHECK(bar_str->Equals(env.local(), foo_after).FromJust()); |
| |
| CHECK(obj->Set(env.local(), v8_str("foo"), bar_str).ToChecked()); |
| bool result; |
| CHECK(obj->Set(env.local(), v8_str("foo"), bar_str).To(&result)); |
| CHECK(result); |
| } |
| |
| |
| THREADED_TEST(AccessElement) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| Local<Value> before = obj->Get(env.local(), 1).ToLocalChecked(); |
| CHECK(before->IsUndefined()); |
| Local<String> bar_str = v8_str("bar"); |
| CHECK(obj->Set(env.local(), 1, bar_str).FromJust()); |
| Local<Value> after = obj->Get(env.local(), 1).ToLocalChecked(); |
| CHECK(!after->IsUndefined()); |
| CHECK(after->IsString()); |
| CHECK(bar_str->Equals(env.local(), after).FromJust()); |
| |
| Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); |
| CHECK(v8_str("a") |
| ->Equals(env.local(), value->Get(env.local(), 0).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("b") |
| ->Equals(env.local(), value->Get(env.local(), 1).ToLocalChecked()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(Script) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| const char* source = "1 + 2 + 3"; |
| Local<Script> script = v8_compile(source); |
| CHECK_EQ(6, v8_run_int32value(script)); |
| } |
| |
| |
| class TestResource: public String::ExternalStringResource { |
| public: |
| explicit TestResource(uint16_t* data, int* counter = nullptr, |
| bool owning_data = true) |
| : data_(data), length_(0), counter_(counter), owning_data_(owning_data) { |
| while (data[length_]) ++length_; |
| } |
| |
| ~TestResource() { |
| if (owning_data_) i::DeleteArray(data_); |
| if (counter_ != nullptr) ++*counter_; |
| } |
| |
| const uint16_t* data() const { |
| return data_; |
| } |
| |
| size_t length() const { |
| return length_; |
| } |
| |
| private: |
| uint16_t* data_; |
| size_t length_; |
| int* counter_; |
| bool owning_data_; |
| }; |
| |
| |
| class TestOneByteResource : public String::ExternalOneByteStringResource { |
| public: |
| explicit TestOneByteResource(const char* data, int* counter = nullptr, |
| size_t offset = 0) |
| : orig_data_(data), |
| data_(data + offset), |
| length_(strlen(data) - offset), |
| counter_(counter) {} |
| |
| ~TestOneByteResource() { |
| i::DeleteArray(orig_data_); |
| if (counter_ != nullptr) ++*counter_; |
| } |
| |
| const char* data() const { |
| return data_; |
| } |
| |
| size_t length() const { |
| return length_; |
| } |
| |
| private: |
| const char* orig_data_; |
| const char* data_; |
| size_t length_; |
| int* counter_; |
| }; |
| |
| |
| THREADED_TEST(ScriptUsingStringResource) { |
| int dispose_count = 0; |
| const char* c_source = "1 + 2 * 3"; |
| uint16_t* two_byte_source = AsciiToTwoByteString(c_source); |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| TestResource* resource = new TestResource(two_byte_source, &dispose_count); |
| Local<String> source = |
| String::NewExternalTwoByte(env->GetIsolate(), resource) |
| .ToLocalChecked(); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| CHECK(source->IsExternal()); |
| CHECK_EQ(resource, |
| static_cast<TestResource*>(source->GetExternalStringResource())); |
| String::Encoding encoding = String::UNKNOWN_ENCODING; |
| CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), |
| source->GetExternalStringResourceBase(&encoding)); |
| CHECK_EQ(String::TWO_BYTE_ENCODING, encoding); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllAvailableGarbage(); |
| CHECK_EQ(1, dispose_count); |
| } |
| |
| |
| THREADED_TEST(ScriptUsingOneByteStringResource) { |
| int dispose_count = 0; |
| const char* c_source = "1 + 2 * 3"; |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| TestOneByteResource* resource = |
| new TestOneByteResource(i::StrDup(c_source), &dispose_count); |
| Local<String> source = |
| String::NewExternalOneByte(env->GetIsolate(), resource) |
| .ToLocalChecked(); |
| CHECK(source->IsExternalOneByte()); |
| CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), |
| source->GetExternalOneByteStringResource()); |
| String::Encoding encoding = String::UNKNOWN_ENCODING; |
| CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), |
| source->GetExternalStringResourceBase(&encoding)); |
| CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllAvailableGarbage(); |
| CHECK_EQ(1, dispose_count); |
| } |
| |
| |
| THREADED_TEST(ScriptMakingExternalString) { |
| int dispose_count = 0; |
| uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<String> source = |
| String::NewFromTwoByte(env->GetIsolate(), two_byte_source, |
| v8::NewStringType::kNormal) |
| .ToLocalChecked(); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| CHECK(!source->IsExternal()); |
| CHECK(!source->IsExternalOneByte()); |
| String::Encoding encoding = String::UNKNOWN_ENCODING; |
| CHECK(!source->GetExternalStringResourceBase(&encoding)); |
| CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); |
| bool success = source->MakeExternal(new TestResource(two_byte_source, |
| &dispose_count)); |
| CHECK(success); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(1, dispose_count); |
| } |
| |
| |
| THREADED_TEST(ScriptMakingExternalOneByteString) { |
| int dispose_count = 0; |
| const char* c_source = "1 + 2 * 3"; |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<String> source = v8_str(c_source); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| bool success = source->MakeExternal( |
| new TestOneByteResource(i::StrDup(c_source), &dispose_count)); |
| CHECK(success); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(1, dispose_count); |
| } |
| |
| |
| TEST(MakingExternalStringConditions) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Free some space in the new space so that we can check freshness. |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| |
| uint16_t* two_byte_string = AsciiToTwoByteString("s1"); |
| Local<String> local_string = |
| String::NewFromTwoByte(env->GetIsolate(), two_byte_string, |
| v8::NewStringType::kNormal) |
| .ToLocalChecked(); |
| i::DeleteArray(two_byte_string); |
| |
| // We should refuse to externalize new space strings. |
| CHECK(!local_string->CanMakeExternal()); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| // Old space strings should be accepted. |
| CHECK(local_string->CanMakeExternal()); |
| } |
| |
| |
| TEST(MakingExternalOneByteStringConditions) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Free some space in the new space so that we can check freshness. |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| |
| Local<String> local_string = v8_str("s1"); |
| // We should refuse to externalize new space strings. |
| CHECK(!local_string->CanMakeExternal()); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| // Old space strings should be accepted. |
| CHECK(local_string->CanMakeExternal()); |
| } |
| |
| |
| TEST(MakingExternalUnalignedOneByteString) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| CompileRun("function cons(a, b) { return a + b; }" |
| "function slice(a) { return a.substring(1); }"); |
| // Create a cons string that will land in old pointer space. |
| Local<String> cons = Local<String>::Cast(CompileRun( |
| "cons('abcdefghijklm', 'nopqrstuvwxyz');")); |
| // Create a sliced string that will land in old pointer space. |
| Local<String> slice = Local<String>::Cast(CompileRun( |
| "slice('abcdefghijklmnopqrstuvwxyz');")); |
| |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| i::heap::SimulateFullSpace(CcTest::heap()->old_space()); |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| |
| // Turn into external string with unaligned resource data. |
| const char* c_cons = "_abcdefghijklmnopqrstuvwxyz"; |
| bool success = cons->MakeExternal( |
| new TestOneByteResource(i::StrDup(c_cons), nullptr, 1)); |
| CHECK(success); |
| const char* c_slice = "_bcdefghijklmnopqrstuvwxyz"; |
| success = slice->MakeExternal( |
| new TestOneByteResource(i::StrDup(c_slice), nullptr, 1)); |
| CHECK(success); |
| |
| // Trigger GCs and force evacuation. |
| CcTest::CollectAllGarbage(); |
| CcTest::CollectAllGarbage(i::Heap::kReduceMemoryFootprintMask); |
| } |
| |
| |
| THREADED_TEST(UsingExternalString) { |
| i::Factory* factory = CcTest::i_isolate()->factory(); |
| { |
| v8::HandleScope scope(CcTest::isolate()); |
| uint16_t* two_byte_string = AsciiToTwoByteString("test string"); |
| Local<String> string = |
| String::NewExternalTwoByte(CcTest::isolate(), |
| new TestResource(two_byte_string)) |
| .ToLocalChecked(); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| i::Handle<i::String> isymbol = |
| factory->InternalizeString(istring); |
| CHECK(isymbol->IsInternalizedString()); |
| } |
| CcTest::CollectAllGarbage(); |
| CcTest::CollectAllGarbage(); |
| } |
| |
| |
| THREADED_TEST(UsingExternalOneByteString) { |
| i::Factory* factory = CcTest::i_isolate()->factory(); |
| { |
| v8::HandleScope scope(CcTest::isolate()); |
| const char* one_byte_string = "test string"; |
| Local<String> string = |
| String::NewExternalOneByte( |
| CcTest::isolate(), |
| new TestOneByteResource(i::StrDup(one_byte_string))) |
| .ToLocalChecked(); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| i::Handle<i::String> isymbol = |
| factory->InternalizeString(istring); |
| CHECK(isymbol->IsInternalizedString()); |
| } |
| CcTest::CollectAllGarbage(); |
| CcTest::CollectAllGarbage(); |
| } |
| |
| |
| class RandomLengthResource : public v8::String::ExternalStringResource { |
| public: |
| explicit RandomLengthResource(int length) : length_(length) {} |
| virtual const uint16_t* data() const { return string_; } |
| virtual size_t length() const { return length_; } |
| |
| private: |
| uint16_t string_[10]; |
| int length_; |
| }; |
| |
| |
| class RandomLengthOneByteResource |
| : public v8::String::ExternalOneByteStringResource { |
| public: |
| explicit RandomLengthOneByteResource(int length) : length_(length) {} |
| virtual const char* data() const { return string_; } |
| virtual size_t length() const { return length_; } |
| |
| private: |
| char string_[10]; |
| int length_; |
| }; |
| |
| |
| THREADED_TEST(NewExternalForVeryLongString) { |
| auto isolate = CcTest::isolate(); |
| { |
| v8::HandleScope scope(isolate); |
| v8::TryCatch try_catch(isolate); |
| RandomLengthOneByteResource r(1 << 30); |
| v8::MaybeLocal<v8::String> maybe_str = |
| v8::String::NewExternalOneByte(isolate, &r); |
| CHECK(maybe_str.IsEmpty()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| v8::HandleScope scope(isolate); |
| v8::TryCatch try_catch(isolate); |
| RandomLengthResource r(1 << 30); |
| v8::MaybeLocal<v8::String> maybe_str = |
| v8::String::NewExternalTwoByte(isolate, &r); |
| CHECK(maybe_str.IsEmpty()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| } |
| |
| |
| THREADED_TEST(ScavengeExternalString) { |
| i::FLAG_stress_compaction = false; |
| i::FLAG_gc_global = false; |
| int dispose_count = 0; |
| bool in_new_space = false; |
| { |
| v8::HandleScope scope(CcTest::isolate()); |
| uint16_t* two_byte_string = AsciiToTwoByteString("test string"); |
| Local<String> string = |
| String::NewExternalTwoByte( |
| CcTest::isolate(), |
| new TestResource(two_byte_string, &dispose_count)) |
| .ToLocalChecked(); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| in_new_space = CcTest::heap()->InNewSpace(*istring); |
| CHECK(in_new_space || CcTest::heap()->old_space()->Contains(*istring)); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_SPACE); |
| CHECK_EQ(1, dispose_count); |
| } |
| |
| |
| THREADED_TEST(ScavengeExternalOneByteString) { |
| i::FLAG_stress_compaction = false; |
| i::FLAG_gc_global = false; |
| int dispose_count = 0; |
| bool in_new_space = false; |
| { |
| v8::HandleScope scope(CcTest::isolate()); |
| const char* one_byte_string = "test string"; |
| Local<String> string = |
| String::NewExternalOneByte( |
| CcTest::isolate(), |
| new TestOneByteResource(i::StrDup(one_byte_string), &dispose_count)) |
| .ToLocalChecked(); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| in_new_space = CcTest::heap()->InNewSpace(*istring); |
| CHECK(in_new_space || CcTest::heap()->old_space()->Contains(*istring)); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_SPACE); |
| CHECK_EQ(1, dispose_count); |
| } |
| |
| |
| class TestOneByteResourceWithDisposeControl : public TestOneByteResource { |
| public: |
| // Only used by non-threaded tests, so it can use static fields. |
| static int dispose_calls; |
| static int dispose_count; |
| |
| TestOneByteResourceWithDisposeControl(const char* data, bool dispose) |
| : TestOneByteResource(data, &dispose_count), dispose_(dispose) {} |
| |
| void Dispose() { |
| ++dispose_calls; |
| if (dispose_) delete this; |
| } |
| private: |
| bool dispose_; |
| }; |
| |
| |
| int TestOneByteResourceWithDisposeControl::dispose_count = 0; |
| int TestOneByteResourceWithDisposeControl::dispose_calls = 0; |
| |
| |
| TEST(ExternalStringWithDisposeHandling) { |
| const char* c_source = "1 + 2 * 3"; |
| |
| // Use a stack allocated external string resource allocated object. |
| TestOneByteResourceWithDisposeControl::dispose_count = 0; |
| TestOneByteResourceWithDisposeControl::dispose_calls = 0; |
| TestOneByteResourceWithDisposeControl res_stack(i::StrDup(c_source), false); |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<String> source = |
| String::NewExternalOneByte(env->GetIsolate(), &res_stack) |
| .ToLocalChecked(); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| CcTest::CollectAllAvailableGarbage(); |
| CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllAvailableGarbage(); |
| CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls); |
| CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| |
| // Use a heap allocated external string resource allocated object. |
| TestOneByteResourceWithDisposeControl::dispose_count = 0; |
| TestOneByteResourceWithDisposeControl::dispose_calls = 0; |
| TestOneByteResource* res_heap = |
| new TestOneByteResourceWithDisposeControl(i::StrDup(c_source), true); |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<String> source = |
| String::NewExternalOneByte(env->GetIsolate(), res_heap) |
| .ToLocalChecked(); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| CcTest::CollectAllAvailableGarbage(); |
| CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllAvailableGarbage(); |
| CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls); |
| CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_count); |
| } |
| |
| |
| THREADED_TEST(StringConcat) { |
| { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| const char* one_byte_string_1 = "function a_times_t"; |
| const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; |
| const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; |
| const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; |
| const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; |
| const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; |
| const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; |
| Local<String> left = v8_str(one_byte_string_1); |
| |
| uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); |
| Local<String> right = |
| String::NewFromTwoByte(env->GetIsolate(), two_byte_source, |
| v8::NewStringType::kNormal) |
| .ToLocalChecked(); |
| i::DeleteArray(two_byte_source); |
| |
| Local<String> source = String::Concat(left, right); |
| right = String::NewExternalOneByte( |
| env->GetIsolate(), |
| new TestOneByteResource(i::StrDup(one_byte_extern_1))) |
| .ToLocalChecked(); |
| source = String::Concat(source, right); |
| right = String::NewExternalTwoByte( |
| env->GetIsolate(), |
| new TestResource(AsciiToTwoByteString(two_byte_extern_1))) |
| .ToLocalChecked(); |
| source = String::Concat(source, right); |
| right = v8_str(one_byte_string_2); |
| source = String::Concat(source, right); |
| |
| two_byte_source = AsciiToTwoByteString(two_byte_string_2); |
| right = String::NewFromTwoByte(env->GetIsolate(), two_byte_source, |
| v8::NewStringType::kNormal) |
| .ToLocalChecked(); |
| i::DeleteArray(two_byte_source); |
| |
| source = String::Concat(source, right); |
| right = String::NewExternalTwoByte( |
| env->GetIsolate(), |
| new TestResource(AsciiToTwoByteString(two_byte_extern_2))) |
| .ToLocalChecked(); |
| source = String::Concat(source, right); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(68, value->Int32Value(env.local()).FromJust()); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::CollectAllGarbage(); |
| CcTest::CollectAllGarbage(); |
| } |
| |
| |
| THREADED_TEST(GlobalProperties) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<v8::Object> global = env->Global(); |
| CHECK(global->Set(env.local(), v8_str("pi"), v8_num(3.1415926)).FromJust()); |
| Local<Value> pi = global->Get(env.local(), v8_str("pi")).ToLocalChecked(); |
| CHECK_EQ(3.1415926, pi->NumberValue(env.local()).FromJust()); |
| } |
| |
| |
| static void handle_callback_impl(const v8::FunctionCallbackInfo<Value>& info, |
| i::Address callback) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(info, callback); |
| info.GetReturnValue().Set(v8_str("bad value")); |
| info.GetReturnValue().Set(v8_num(102)); |
| } |
| |
| |
| static void handle_callback(const v8::FunctionCallbackInfo<Value>& info) { |
| return handle_callback_impl(info, FUNCTION_ADDR(handle_callback)); |
| } |
| |
| |
| static void handle_callback_2(const v8::FunctionCallbackInfo<Value>& info) { |
| return handle_callback_impl(info, FUNCTION_ADDR(handle_callback_2)); |
| } |
| |
| static void construct_callback( |
| const v8::FunctionCallbackInfo<Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(info, FUNCTION_ADDR(construct_callback)); |
| CHECK( |
| info.This() |
| ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x"), v8_num(1)) |
| .FromJust()); |
| CHECK( |
| info.This() |
| ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("y"), v8_num(2)) |
| .FromJust()); |
| info.GetReturnValue().Set(v8_str("bad value")); |
| info.GetReturnValue().Set(info.This()); |
| } |
| |
| |
| static 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)); |
| } |
| |
| |
| template<typename Handler> |
| static void TestFunctionTemplateInitializer(Handler handler, |
| Handler handler_2) { |
| // Test constructor calls. |
| { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate, handler); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); |
| Local<Script> script = v8_compile("obj()"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(102, v8_run_int32value(script)); |
| } |
| } |
| // Use SetCallHandler to initialize a function template, should work like |
| // the previous one. |
| { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); |
| fun_templ->SetCallHandler(handler_2); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); |
| Local<Script> script = v8_compile("obj()"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(102, v8_run_int32value(script)); |
| } |
| } |
| } |
| |
| |
| template<typename Constructor, typename Accessor> |
| static void TestFunctionTemplateAccessor(Constructor constructor, |
| Accessor accessor) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(env->GetIsolate(), constructor); |
| fun_templ->SetClassName(v8_str("funky")); |
| fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), accessor); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); |
| Local<Value> result = |
| v8_compile("(new obj()).toString()")->Run(env.local()).ToLocalChecked(); |
| CHECK(v8_str("[object funky]")->Equals(env.local(), result).FromJust()); |
| CompileRun("var obj_instance = new obj();"); |
| Local<Script> script; |
| script = v8_compile("obj_instance.x"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(1, v8_run_int32value(script)); |
| } |
| script = v8_compile("obj_instance.m"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(239, v8_run_int32value(script)); |
| } |
| } |
| |
| |
| THREADED_PROFILED_TEST(FunctionTemplate) { |
| TestFunctionTemplateInitializer(handle_callback, handle_callback_2); |
| TestFunctionTemplateAccessor(construct_callback, Return239Callback); |
| } |
| |
| |
| static void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(info, FUNCTION_ADDR(SimpleCallback)); |
| info.GetReturnValue().Set(v8_num(51423 + info.Length())); |
| } |
| |
| |
| template<typename Callback> |
| static void TestSimpleCallback(Callback callback) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::ObjectTemplate> object_template = |
| v8::ObjectTemplate::New(isolate); |
| object_template->Set(isolate, "callback", |
| v8::FunctionTemplate::New(isolate, callback)); |
| v8::Local<v8::Object> object = |
| object_template->NewInstance(env.local()).ToLocalChecked(); |
| CHECK((*env) |
| ->Global() |
| ->Set(env.local(), v8_str("callback_object"), object) |
| .FromJust()); |
| v8::Local<v8::Script> script; |
| script = v8_compile("callback_object.callback(17)"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(51424, v8_run_int32value(script)); |
| } |
| script = v8_compile("callback_object.callback(17, 24)"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(51425, v8_run_int32value(script)); |
| } |
| } |
| |
| |
| THREADED_PROFILED_TEST(SimpleCallback) { |
| TestSimpleCallback(SimpleCallback); |
| } |
| |
| |
| template<typename T> |
| void FastReturnValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info); |
| |
| // constant return values |
| static int32_t fast_return_value_int32 = 471; |
| static uint32_t fast_return_value_uint32 = 571; |
| static const double kFastReturnValueDouble = 2.7; |
| // variable return values |
| static bool fast_return_value_bool = false; |
| enum ReturnValueOddball { |
| kNullReturnValue, |
| kUndefinedReturnValue, |
| kEmptyStringReturnValue |
| }; |
| static ReturnValueOddball fast_return_value_void; |
| static bool fast_return_value_object_is_empty = false; |
| |
| // Helper function to avoid compiler error: insufficient contextual information |
| // to determine type when applying FUNCTION_ADDR to a template function. |
| static i::Address address_of(v8::FunctionCallback callback) { |
| return FUNCTION_ADDR(callback); |
| } |
| |
| template<> |
| void FastReturnValueCallback<int32_t>( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, address_of(FastReturnValueCallback<int32_t>)); |
| info.GetReturnValue().Set(fast_return_value_int32); |
| } |
| |
| template<> |
| void FastReturnValueCallback<uint32_t>( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, address_of(FastReturnValueCallback<uint32_t>)); |
| info.GetReturnValue().Set(fast_return_value_uint32); |
| } |
| |
| template<> |
| void FastReturnValueCallback<double>( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, address_of(FastReturnValueCallback<double>)); |
| info.GetReturnValue().Set(kFastReturnValueDouble); |
| } |
| |
| template<> |
| void FastReturnValueCallback<bool>( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, address_of(FastReturnValueCallback<bool>)); |
| info.GetReturnValue().Set(fast_return_value_bool); |
| } |
| |
| template<> |
| void FastReturnValueCallback<void>( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, address_of(FastReturnValueCallback<void>)); |
| switch (fast_return_value_void) { |
| case kNullReturnValue: |
| info.GetReturnValue().SetNull(); |
| break; |
| case kUndefinedReturnValue: |
| info.GetReturnValue().SetUndefined(); |
| break; |
| case kEmptyStringReturnValue: |
| info.GetReturnValue().SetEmptyString(); |
| break; |
| } |
| } |
| |
| template<> |
| void FastReturnValueCallback<Object>( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| v8::Local<v8::Object> object; |
| if (!fast_return_value_object_is_empty) { |
| object = Object::New(info.GetIsolate()); |
| } |
| info.GetReturnValue().Set(object); |
| } |
| |
| template <typename T> |
| Local<Value> TestFastReturnValues() { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::EscapableHandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> object_template = |
| v8::ObjectTemplate::New(isolate); |
| v8::FunctionCallback callback = &FastReturnValueCallback<T>; |
| object_template->Set(isolate, "callback", |
| v8::FunctionTemplate::New(isolate, callback)); |
| v8::Local<v8::Object> object = |
| object_template->NewInstance(env.local()).ToLocalChecked(); |
| CHECK((*env) |
| ->Global() |
| ->Set(env.local(), v8_str("callback_object"), object) |
| .FromJust()); |
| return scope.Escape(CompileRun("callback_object.callback()")); |
| } |
| |
| |
| THREADED_PROFILED_TEST(FastReturnValues) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Value> value; |
| // check int32_t and uint32_t |
| int32_t int_values[] = { |
| 0, 234, -723, |
| i::Smi::kMinValue, i::Smi::kMaxValue |
| }; |
| for (size_t i = 0; i < arraysize(int_values); i++) { |
| for (int modifier = -1; modifier <= 1; modifier++) { |
| int int_value = int_values[i] + modifier; |
| // check int32_t |
| fast_return_value_int32 = int_value; |
| value = TestFastReturnValues<int32_t>(); |
| CHECK(value->IsInt32()); |
| CHECK_EQ(fast_return_value_int32, |
| value->Int32Value(env.local()).FromJust()); |
| // check uint32_t |
| fast_return_value_uint32 = static_cast<uint32_t>(int_value); |
| value = TestFastReturnValues<uint32_t>(); |
| CHECK(value->IsUint32()); |
| CHECK_EQ(fast_return_value_uint32, |
| value->Uint32Value(env.local()).FromJust()); |
| } |
| } |
| // check double |
| value = TestFastReturnValues<double>(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(kFastReturnValueDouble, |
| value->ToNumber(env.local()).ToLocalChecked()->Value()); |
| // check bool values |
| for (int i = 0; i < 2; i++) { |
| fast_return_value_bool = i == 0; |
| value = TestFastReturnValues<bool>(); |
| CHECK(value->IsBoolean()); |
| CHECK_EQ(fast_return_value_bool, |
| value->ToBoolean(env.local()).ToLocalChecked()->Value()); |
| } |
| // check oddballs |
| ReturnValueOddball oddballs[] = { |
| kNullReturnValue, |
| kUndefinedReturnValue, |
| kEmptyStringReturnValue |
| }; |
| for (size_t i = 0; i < arraysize(oddballs); i++) { |
| fast_return_value_void = oddballs[i]; |
| value = TestFastReturnValues<void>(); |
| switch (fast_return_value_void) { |
| case kNullReturnValue: |
| CHECK(value->IsNull()); |
| break; |
| case kUndefinedReturnValue: |
| CHECK(value->IsUndefined()); |
| break; |
| case kEmptyStringReturnValue: |
| CHECK(value->IsString()); |
| CHECK_EQ(0, v8::String::Cast(*value)->Length()); |
| break; |
| } |
| } |
| // check handles |
| fast_return_value_object_is_empty = false; |
| value = TestFastReturnValues<Object>(); |
| CHECK(value->IsObject()); |
| fast_return_value_object_is_empty = true; |
| value = TestFastReturnValues<Object>(); |
| CHECK(value->IsUndefined()); |
| } |
| |
| |
| THREADED_TEST(FunctionTemplateSetLength) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| { |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate, handle_callback, Local<v8::Value>(), |
| Local<v8::Signature>(), 23); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); |
| Local<Script> script = v8_compile("obj.length"); |
| CHECK_EQ(23, v8_run_int32value(script)); |
| } |
| { |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate, handle_callback); |
| fun_templ->SetLength(22); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); |
| Local<Script> script = v8_compile("obj.length"); |
| CHECK_EQ(22, v8_run_int32value(script)); |
| } |
| { |
| // Without setting length it defaults to 0. |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate, handle_callback); |
| Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); |
| Local<Script> script = v8_compile("obj.length"); |
| CHECK_EQ(0, v8_run_int32value(script)); |
| } |
| } |
| |
| |
| static void* expected_ptr; |
| static void callback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| void* ptr = v8::External::Cast(*args.Data())->Value(); |
| CHECK_EQ(expected_ptr, ptr); |
| args.GetReturnValue().Set(true); |
| } |
| |
| |
| static void TestExternalPointerWrapping() { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Value> data = v8::External::New(isolate, expected_ptr); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| CHECK(obj->Set(env.local(), v8_str("func"), |
| v8::FunctionTemplate::New(isolate, callback, data) |
| ->GetFunction(env.local()) |
| .ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); |
| |
| CHECK(CompileRun("function foo() {\n" |
| " for (var i = 0; i < 13; i++) obj.func();\n" |
| "}\n" |
| "foo(), true") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(ExternalWrap) { |
| // Check heap allocated object. |
| int* ptr = new int; |
| expected_ptr = ptr; |
| TestExternalPointerWrapping(); |
| delete ptr; |
| |
| // Check stack allocated object. |
| int foo; |
| expected_ptr = &foo; |
| TestExternalPointerWrapping(); |
| |
| // Check not aligned addresses. |
| const int n = 100; |
| char* s = new char[n]; |
| for (int i = 0; i < n; i++) { |
| expected_ptr = s + i; |
| TestExternalPointerWrapping(); |
| } |
| |
| delete[] s; |
| |
| // Check several invalid addresses. |
| expected_ptr = reinterpret_cast<void*>(1); |
| TestExternalPointerWrapping(); |
| |
| expected_ptr = reinterpret_cast<void*>(0xDEADBEEF); |
| TestExternalPointerWrapping(); |
| |
| expected_ptr = reinterpret_cast<void*>(0xDEADBEEF + 1); |
| TestExternalPointerWrapping(); |
| |
| #if defined(V8_HOST_ARCH_X64) |
| // Check a value with a leading 1 bit in x64 Smi encoding. |
| expected_ptr = reinterpret_cast<void*>(0x400000000); |
| TestExternalPointerWrapping(); |
| |
| expected_ptr = reinterpret_cast<void*>(0xDEADBEEFDEADBEEF); |
| TestExternalPointerWrapping(); |
| |
| expected_ptr = reinterpret_cast<void*>(0xDEADBEEFDEADBEEF + 1); |
| TestExternalPointerWrapping(); |
| #endif |
| } |
| |
| |
| THREADED_TEST(FindInstanceInPrototypeChain) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(isolate); |
| Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(isolate); |
| Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(isolate); |
| derived->Inherit(base); |
| |
| Local<v8::Function> base_function = |
| base->GetFunction(env.local()).ToLocalChecked(); |
| Local<v8::Function> derived_function = |
| derived->GetFunction(env.local()).ToLocalChecked(); |
| Local<v8::Function> other_function = |
| other->GetFunction(env.local()).ToLocalChecked(); |
| |
| Local<v8::Object> base_instance = |
| base_function->NewInstance(env.local()).ToLocalChecked(); |
| Local<v8::Object> derived_instance = |
| derived_function->NewInstance(env.local()).ToLocalChecked(); |
| Local<v8::Object> derived_instance2 = |
| derived_function->NewInstance(env.local()).ToLocalChecked(); |
| Local<v8::Object> other_instance = |
| other_function->NewInstance(env.local()).ToLocalChecked(); |
| CHECK( |
| derived_instance2->Set(env.local(), v8_str("__proto__"), derived_instance) |
| .FromJust()); |
| CHECK(other_instance->Set(env.local(), v8_str("__proto__"), derived_instance2) |
| .FromJust()); |
| |
| // base_instance is only an instance of base. |
| CHECK(base_instance->Equals(env.local(), |
| base_instance->FindInstanceInPrototypeChain(base)) |
| .FromJust()); |
| CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); |
| CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); |
| |
| // derived_instance is an instance of base and derived. |
| CHECK(derived_instance->Equals(env.local(), |
| derived_instance->FindInstanceInPrototypeChain( |
| base)) |
| .FromJust()); |
| CHECK(derived_instance->Equals(env.local(), |
| derived_instance->FindInstanceInPrototypeChain( |
| derived)) |
| .FromJust()); |
| CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); |
| |
| // other_instance is an instance of other and its immediate |
| // prototype derived_instance2 is an instance of base and derived. |
| // Note, derived_instance is an instance of base and derived too, |
| // but it comes after derived_instance2 in the prototype chain of |
| // other_instance. |
| CHECK(derived_instance2->Equals( |
| env.local(), |
| other_instance->FindInstanceInPrototypeChain(base)) |
| .FromJust()); |
| CHECK(derived_instance2->Equals(env.local(), |
| other_instance->FindInstanceInPrototypeChain( |
| derived)) |
| .FromJust()); |
| CHECK(other_instance->Equals( |
| env.local(), |
| other_instance->FindInstanceInPrototypeChain(other)) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(TinyInteger) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| int32_t value = 239; |
| Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::New(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| |
| |
| THREADED_TEST(BigSmiInteger) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Isolate* isolate = CcTest::isolate(); |
| |
| int32_t value = i::Smi::kMaxValue; |
| // We cannot add one to a Smi::kMaxValue without wrapping. |
| if (i::SmiValuesAre31Bits()) { |
| CHECK(i::Smi::IsValid(value)); |
| CHECK(!i::Smi::IsValid(value + 1)); |
| |
| Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::New(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| } |
| |
| |
| THREADED_TEST(BigInteger) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Isolate* isolate = CcTest::isolate(); |
| |
| // We cannot add one to a Smi::kMaxValue without wrapping. |
| if (i::SmiValuesAre31Bits()) { |
| // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. |
| // The code will not be run in that case, due to the "if" guard. |
| int32_t value = |
| static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); |
| CHECK_GT(value, i::Smi::kMaxValue); |
| CHECK(!i::Smi::IsValid(value)); |
| |
| Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::New(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| } |
| |
| |
| THREADED_TEST(TinyUnsignedInteger) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Isolate* isolate = CcTest::isolate(); |
| |
| uint32_t value = 239; |
| |
| Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| |
| |
| THREADED_TEST(BigUnsignedSmiInteger) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Isolate* isolate = CcTest::isolate(); |
| |
| uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); |
| CHECK(i::Smi::IsValid(value)); |
| CHECK(!i::Smi::IsValid(value + 1)); |
| |
| Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| |
| |
| THREADED_TEST(BigUnsignedInteger) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Isolate* isolate = CcTest::isolate(); |
| |
| uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; |
| CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); |
| CHECK(!i::Smi::IsValid(value)); |
| |
| Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| |
| |
| THREADED_TEST(OutOfSignedRangeUnsignedInteger) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Isolate* isolate = CcTest::isolate(); |
| |
| uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; |
| uint32_t value = INT32_MAX_AS_UINT + 1; |
| CHECK(value > INT32_MAX_AS_UINT); // No overflow. |
| |
| Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| |
| value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| } |
| |
| |
| THREADED_TEST(IsNativeError) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> syntax_error = CompileRun( |
| "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); |
| CHECK(syntax_error->IsNativeError()); |
| v8::Local<Value> not_error = CompileRun("{a:42}"); |
| CHECK(!not_error->IsNativeError()); |
| v8::Local<Value> not_object = CompileRun("42"); |
| CHECK(!not_object->IsNativeError()); |
| } |
| |
| |
| THREADED_TEST(IsGeneratorFunctionOrObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| CompileRun("function *gen() { yield 1; }\nfunction func() {}"); |
| v8::Local<Value> gen = CompileRun("gen"); |
| v8::Local<Value> genObj = CompileRun("gen()"); |
| v8::Local<Value> object = CompileRun("{a:42}"); |
| v8::Local<Value> func = CompileRun("func"); |
| |
| CHECK(gen->IsGeneratorFunction()); |
| CHECK(gen->IsFunction()); |
| CHECK(!gen->IsGeneratorObject()); |
| |
| CHECK(!genObj->IsGeneratorFunction()); |
| CHECK(!genObj->IsFunction()); |
| CHECK(genObj->IsGeneratorObject()); |
| |
| CHECK(!object->IsGeneratorFunction()); |
| CHECK(!object->IsFunction()); |
| CHECK(!object->IsGeneratorObject()); |
| |
| CHECK(!func->IsGeneratorFunction()); |
| CHECK(func->IsFunction()); |
| CHECK(!func->IsGeneratorObject()); |
| } |
| |
| THREADED_TEST(IsAsyncFunction) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| CompileRun("async function foo() {}"); |
| v8::Local<Value> foo = CompileRun("foo"); |
| |
| CHECK(foo->IsAsyncFunction()); |
| CHECK(foo->IsFunction()); |
| CHECK(!foo->IsGeneratorFunction()); |
| CHECK(!foo->IsGeneratorObject()); |
| |
| CompileRun("function bar() {}"); |
| v8::Local<Value> bar = CompileRun("bar"); |
| |
| CHECK(!bar->IsAsyncFunction()); |
| CHECK(bar->IsFunction()); |
| } |
| |
| THREADED_TEST(ArgumentsObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> arguments_object = |
| CompileRun("var out = 0; (function(){ out = arguments; })(1,2,3); out;"); |
| CHECK(arguments_object->IsArgumentsObject()); |
| v8::Local<Value> array = CompileRun("[1,2,3]"); |
| CHECK(!array->IsArgumentsObject()); |
| v8::Local<Value> object = CompileRun("{a:42}"); |
| CHECK(!object->IsArgumentsObject()); |
| } |
| |
| |
| THREADED_TEST(IsMapOrSet) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> map = CompileRun("new Map()"); |
| v8::Local<Value> set = CompileRun("new Set()"); |
| v8::Local<Value> weak_map = CompileRun("new WeakMap()"); |
| v8::Local<Value> weak_set = CompileRun("new WeakSet()"); |
| CHECK(map->IsMap()); |
| CHECK(set->IsSet()); |
| CHECK(weak_map->IsWeakMap()); |
| CHECK(weak_set->IsWeakSet()); |
| |
| CHECK(!map->IsSet()); |
| CHECK(!map->IsWeakMap()); |
| CHECK(!map->IsWeakSet()); |
| |
| CHECK(!set->IsMap()); |
| CHECK(!set->IsWeakMap()); |
| CHECK(!set->IsWeakSet()); |
| |
| CHECK(!weak_map->IsMap()); |
| CHECK(!weak_map->IsSet()); |
| CHECK(!weak_map->IsWeakSet()); |
| |
| CHECK(!weak_set->IsMap()); |
| CHECK(!weak_set->IsSet()); |
| CHECK(!weak_set->IsWeakMap()); |
| |
| v8::Local<Value> object = CompileRun("{a:42}"); |
| CHECK(!object->IsMap()); |
| CHECK(!object->IsSet()); |
| CHECK(!object->IsWeakMap()); |
| CHECK(!object->IsWeakSet()); |
| } |
| |
| |
| THREADED_TEST(StringObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> boxed_string = CompileRun("new String(\"test\")"); |
| CHECK(boxed_string->IsStringObject()); |
| v8::Local<Value> unboxed_string = CompileRun("\"test\""); |
| CHECK(!unboxed_string->IsStringObject()); |
| v8::Local<Value> boxed_not_string = CompileRun("new Number(42)"); |
| CHECK(!boxed_not_string->IsStringObject()); |
| v8::Local<Value> not_object = CompileRun("0"); |
| CHECK(!not_object->IsStringObject()); |
| v8::Local<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>(); |
| CHECK(!as_boxed.IsEmpty()); |
| Local<v8::String> the_string = as_boxed->ValueOf(); |
| CHECK(!the_string.IsEmpty()); |
| ExpectObject("\"test\"", the_string); |
| v8::Local<v8::Value> new_boxed_string = v8::StringObject::New(the_string); |
| CHECK(new_boxed_string->IsStringObject()); |
| as_boxed = new_boxed_string.As<v8::StringObject>(); |
| the_string = as_boxed->ValueOf(); |
| CHECK(!the_string.IsEmpty()); |
| ExpectObject("\"test\"", the_string); |
| } |
| |
| |
| TEST(StringObjectDelete) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Local<Value> boxed_string = CompileRun("new String(\"test\")"); |
| CHECK(boxed_string->IsStringObject()); |
| v8::Local<v8::Object> str_obj = boxed_string.As<v8::Object>(); |
| CHECK(!str_obj->Delete(context.local(), 2).FromJust()); |
| CHECK(!str_obj->Delete(context.local(), v8_num(2)).FromJust()); |
| } |
| |
| |
| THREADED_TEST(NumberObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> boxed_number = CompileRun("new Number(42)"); |
| CHECK(boxed_number->IsNumberObject()); |
| v8::Local<Value> unboxed_number = CompileRun("42"); |
| CHECK(!unboxed_number->IsNumberObject()); |
| v8::Local<Value> boxed_not_number = CompileRun("new Boolean(false)"); |
| CHECK(!boxed_not_number->IsNumberObject()); |
| v8::Local<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>(); |
| CHECK(!as_boxed.IsEmpty()); |
| double the_number = as_boxed->ValueOf(); |
| CHECK_EQ(42.0, the_number); |
| v8::Local<v8::Value> new_boxed_number = |
| v8::NumberObject::New(env->GetIsolate(), 43); |
| CHECK(new_boxed_number->IsNumberObject()); |
| as_boxed = new_boxed_number.As<v8::NumberObject>(); |
| the_number = as_boxed->ValueOf(); |
| CHECK_EQ(43.0, the_number); |
| } |
| |
| |
| THREADED_TEST(BooleanObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> boxed_boolean = CompileRun("new Boolean(true)"); |
| CHECK(boxed_boolean->IsBooleanObject()); |
| v8::Local<Value> unboxed_boolean = CompileRun("true"); |
| CHECK(!unboxed_boolean->IsBooleanObject()); |
| v8::Local<Value> boxed_not_boolean = CompileRun("new Number(42)"); |
| CHECK(!boxed_not_boolean->IsBooleanObject()); |
| v8::Local<v8::BooleanObject> as_boxed = boxed_boolean.As<v8::BooleanObject>(); |
| CHECK(!as_boxed.IsEmpty()); |
| bool the_boolean = as_boxed->ValueOf(); |
| CHECK(the_boolean); |
| v8::Local<v8::Value> boxed_true = |
| v8::BooleanObject::New(env->GetIsolate(), true); |
| v8::Local<v8::Value> boxed_false = |
| v8::BooleanObject::New(env->GetIsolate(), false); |
| CHECK(boxed_true->IsBooleanObject()); |
| CHECK(boxed_false->IsBooleanObject()); |
| as_boxed = boxed_true.As<v8::BooleanObject>(); |
| CHECK(as_boxed->ValueOf()); |
| as_boxed = boxed_false.As<v8::BooleanObject>(); |
| CHECK(!as_boxed->ValueOf()); |
| } |
| |
| |
| THREADED_TEST(PrimitiveAndWrappedBooleans) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| Local<Value> primitive_false = Boolean::New(env->GetIsolate(), false); |
| CHECK(primitive_false->IsBoolean()); |
| CHECK(!primitive_false->IsBooleanObject()); |
| CHECK(!primitive_false->BooleanValue(env.local()).FromJust()); |
| CHECK(!primitive_false->IsTrue()); |
| CHECK(primitive_false->IsFalse()); |
| |
| Local<Value> false_value = BooleanObject::New(env->GetIsolate(), false); |
| CHECK(!false_value->IsBoolean()); |
| CHECK(false_value->IsBooleanObject()); |
| CHECK(false_value->BooleanValue(env.local()).FromJust()); |
| CHECK(!false_value->IsTrue()); |
| CHECK(!false_value->IsFalse()); |
| |
| Local<BooleanObject> false_boolean_object = false_value.As<BooleanObject>(); |
| CHECK(!false_boolean_object->IsBoolean()); |
| CHECK(false_boolean_object->IsBooleanObject()); |
| CHECK(false_boolean_object->BooleanValue(env.local()).FromJust()); |
| CHECK(!false_boolean_object->ValueOf()); |
| CHECK(!false_boolean_object->IsTrue()); |
| CHECK(!false_boolean_object->IsFalse()); |
| |
| Local<Value> primitive_true = Boolean::New(env->GetIsolate(), true); |
| CHECK(primitive_true->IsBoolean()); |
| CHECK(!primitive_true->IsBooleanObject()); |
| CHECK(primitive_true->BooleanValue(env.local()).FromJust()); |
| CHECK(primitive_true->IsTrue()); |
| CHECK(!primitive_true->IsFalse()); |
| |
| Local<Value> true_value = BooleanObject::New(env->GetIsolate(), true); |
| CHECK(!true_value->IsBoolean()); |
| CHECK(true_value->IsBooleanObject()); |
| CHECK(true_value->BooleanValue(env.local()).FromJust()); |
| CHECK(!true_value->IsTrue()); |
| CHECK(!true_value->IsFalse()); |
| |
| Local<BooleanObject> true_boolean_object = true_value.As<BooleanObject>(); |
| CHECK(!true_boolean_object->IsBoolean()); |
| CHECK(true_boolean_object->IsBooleanObject()); |
| CHECK(true_boolean_object->BooleanValue(env.local()).FromJust()); |
| CHECK(true_boolean_object->ValueOf()); |
| CHECK(!true_boolean_object->IsTrue()); |
| CHECK(!true_boolean_object->IsFalse()); |
| } |
| |
| |
| THREADED_TEST(Number) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| double PI = 3.1415926; |
| Local<v8::Number> pi_obj = v8::Number::New(env->GetIsolate(), PI); |
| CHECK_EQ(PI, pi_obj->NumberValue(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(ToNumber) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<String> str = v8_str("3.1415926"); |
| CHECK_EQ(3.1415926, str->NumberValue(env.local()).FromJust()); |
| v8::Local<v8::Boolean> t = v8::True(isolate); |
| CHECK_EQ(1.0, t->NumberValue(env.local()).FromJust()); |
| v8::Local<v8::Boolean> f = v8::False(isolate); |
| CHECK_EQ(0.0, f->NumberValue(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(Date) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| double PI = 3.1415926; |
| Local<Value> date = v8::Date::New(env.local(), PI).ToLocalChecked(); |
| CHECK_EQ(3.0, date->NumberValue(env.local()).FromJust()); |
| CHECK(date.As<v8::Date>() |
| ->Set(env.local(), v8_str("property"), |
| v8::Integer::New(env->GetIsolate(), 42)) |
| .FromJust()); |
| CHECK_EQ(42, date.As<v8::Date>() |
| ->Get(env.local(), v8_str("property")) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(Boolean) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Boolean> t = v8::True(isolate); |
| CHECK(t->Value()); |
| v8::Local<v8::Boolean> f = v8::False(isolate); |
| CHECK(!f->Value()); |
| v8::Local<v8::Primitive> u = v8::Undefined(isolate); |
| CHECK(!u->BooleanValue(env.local()).FromJust()); |
| v8::Local<v8::Primitive> n = v8::Null(isolate); |
| CHECK(!n->BooleanValue(env.local()).FromJust()); |
| v8::Local<String> str1 = v8_str(""); |
| CHECK(!str1->BooleanValue(env.local()).FromJust()); |
| v8::Local<String> str2 = v8_str("x"); |
| CHECK(str2->BooleanValue(env.local()).FromJust()); |
| CHECK(!v8::Number::New(isolate, 0)->BooleanValue(env.local()).FromJust()); |
| CHECK(v8::Number::New(isolate, -1)->BooleanValue(env.local()).FromJust()); |
| CHECK(v8::Number::New(isolate, 1)->BooleanValue(env.local()).FromJust()); |
| CHECK(v8::Number::New(isolate, 42)->BooleanValue(env.local()).FromJust()); |
| CHECK(!v8_compile("NaN") |
| ->Run(env.local()) |
| .ToLocalChecked() |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| } |
| |
| |
| static void DummyCallHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(13.4)); |
| } |
| |
| |
| static void GetM(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(876)); |
| } |
| |
| |
| THREADED_TEST(GlobalPrototype) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> func_templ = |
| v8::FunctionTemplate::New(isolate); |
| func_templ->PrototypeTemplate()->Set( |
| isolate, "dummy", v8::FunctionTemplate::New(isolate, DummyCallHandler)); |
| v8::Local<ObjectTemplate> templ = func_templ->InstanceTemplate(); |
| templ->Set(isolate, "x", v8_num(200)); |
| templ->SetAccessor(v8_str("m"), GetM); |
| LocalContext env(0, templ); |
| v8::Local<Script> script(v8_compile("dummy()")); |
| v8::Local<Value> result(script->Run(env.local()).ToLocalChecked()); |
| CHECK_EQ(13.4, result->NumberValue(env.local()).FromJust()); |
| CHECK_EQ(200, v8_run_int32value(v8_compile("x"))); |
| CHECK_EQ(876, v8_run_int32value(v8_compile("m"))); |
| } |
| |
| |
| THREADED_TEST(ObjectTemplate) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> acc = |
| v8::FunctionTemplate::New(isolate, Returns42); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("acc"), |
| acc->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::String> class_name = v8_str("the_class_name"); |
| fun->SetClassName(class_name); |
| Local<ObjectTemplate> templ1 = ObjectTemplate::New(isolate, fun); |
| templ1->Set(isolate, "x", v8_num(10)); |
| templ1->Set(isolate, "y", v8_num(13)); |
| templ1->Set(v8_str("foo"), acc); |
| Local<v8::Object> instance1 = |
| templ1->NewInstance(env.local()).ToLocalChecked(); |
| CHECK(class_name->StrictEquals(instance1->GetConstructorName())); |
| CHECK(env->Global()->Set(env.local(), v8_str("p"), instance1).FromJust()); |
| CHECK(CompileRun("(p.x == 10)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(p.y == 13)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(p.foo() == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(p.foo == acc)")->BooleanValue(env.local()).FromJust()); |
| // Ensure that foo become a data field. |
| CompileRun("p.foo = function() {}"); |
| Local<v8::FunctionTemplate> fun2 = v8::FunctionTemplate::New(isolate); |
| fun2->PrototypeTemplate()->Set(isolate, "nirk", v8_num(123)); |
| Local<ObjectTemplate> templ2 = fun2->InstanceTemplate(); |
| templ2->Set(isolate, "a", v8_num(12)); |
| templ2->Set(isolate, "b", templ1); |
| templ2->Set(v8_str("bar"), acc); |
| templ2->SetAccessorProperty(v8_str("acc"), acc); |
| Local<v8::Object> instance2 = |
| templ2->NewInstance(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("q"), instance2).FromJust()); |
| CHECK(CompileRun("(q.nirk == 123)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.a == 12)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.b.x == 10)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.b.y == 13)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.b.foo() == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.b.foo === acc)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.b !== p)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.acc == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.bar() == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q.bar == acc)")->BooleanValue(env.local()).FromJust()); |
| |
| instance2 = templ2->NewInstance(env.local()).ToLocalChecked(); |
| CHECK(env->Global()->Set(env.local(), v8_str("q2"), instance2).FromJust()); |
| CHECK(CompileRun("(q2.nirk == 123)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.a == 12)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.b.x == 10)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.b.y == 13)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.b.foo() == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.b.foo === acc)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.acc == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.bar() == 42)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("(q2.bar === acc)")->BooleanValue(env.local()).FromJust()); |
| |
| CHECK(CompileRun("(q.b !== q2.b)")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("q.b.x = 17; (q2.b.x == 10)") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| CHECK(CompileRun("desc1 = Object.getOwnPropertyDescriptor(q, 'acc');" |
| "(desc1.get === acc)") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| CHECK(CompileRun("desc2 = Object.getOwnPropertyDescriptor(q2, 'acc');" |
| "(desc2.get === acc)") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| } |
| |
| THREADED_TEST(IntegerValue) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| CHECK_EQ(0, CompileRun("undefined")->IntegerValue(env.local()).FromJust()); |
| } |
| |
| static void GetNirk(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(900)); |
| } |
| |
| static void GetRino(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(560)); |
| } |
| |
| enum ObjectInstantiationMode { |
| // Create object using ObjectTemplate::NewInstance. |
| ObjectTemplate_NewInstance, |
| // Create object using FunctionTemplate::NewInstance on constructor. |
| Constructor_GetFunction_NewInstance, |
| // Create object using new operator on constructor. |
| Constructor_GetFunction_New |
| }; |
| |
| // Test object instance creation using a function template with an instance |
| // template inherited from another function template with accessors and data |
| // properties in prototype template. |
| static void TestObjectTemplateInheritedWithPrototype( |
| ObjectInstantiationMode mode) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| fun_A->SetClassName(v8_str("A")); |
| v8::Local<v8::ObjectTemplate> prototype_templ = fun_A->PrototypeTemplate(); |
| prototype_templ->Set(isolate, "a", v8_num(113)); |
| prototype_templ->SetNativeDataProperty(v8_str("nirk"), GetNirk); |
| prototype_templ->Set(isolate, "b", v8_num(153)); |
| |
| Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::String> class_name = v8_str("B"); |
| fun_B->SetClassName(class_name); |
| fun_B->Inherit(fun_A); |
| prototype_templ = fun_B->PrototypeTemplate(); |
| prototype_templ->Set(isolate, "c", v8_num(713)); |
| prototype_templ->SetNativeDataProperty(v8_str("rino"), GetRino); |
| prototype_templ->Set(isolate, "d", v8_num(753)); |
| |
| Local<ObjectTemplate> templ = fun_B->InstanceTemplate(); |
| templ->Set(isolate, "x", v8_num(10)); |
| templ->Set(isolate, "y", v8_num(13)); |
| |
| // Perform several iterations to trigger creation from cached boilerplate. |
| for (int i = 0; i < 3; i++) { |
| Local<v8::Object> instance; |
| switch (mode) { |
| case ObjectTemplate_NewInstance: |
| instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| break; |
| |
| case Constructor_GetFunction_NewInstance: { |
| Local<v8::Function> function_B = |
| fun_B->GetFunction(env.local()).ToLocalChecked(); |
| instance = function_B->NewInstance(env.local()).ToLocalChecked(); |
| break; |
| } |
| case Constructor_GetFunction_New: { |
| Local<v8::Function> function_B = |
| fun_B->GetFunction(env.local()).ToLocalChecked(); |
| if (i == 0) { |
| CHECK(env->Global() |
| ->Set(env.local(), class_name, function_B) |
| .FromJust()); |
| } |
| instance = |
| CompileRun("new B()")->ToObject(env.local()).ToLocalChecked(); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| |
| CHECK(class_name->StrictEquals(instance->GetConstructorName())); |
| CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust()); |
| |
| CHECK_EQ(10, CompileRun("o.x")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(13, CompileRun("o.y")->IntegerValue(env.local()).FromJust()); |
| |
| CHECK_EQ(113, CompileRun("o.a")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(900, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(153, CompileRun("o.b")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(713, CompileRun("o.c")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(560, CompileRun("o.rino")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(753, CompileRun("o.d")->IntegerValue(env.local()).FromJust()); |
| } |
| } |
| |
| THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype1) { |
| TestObjectTemplateInheritedWithPrototype(ObjectTemplate_NewInstance); |
| } |
| |
| THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype2) { |
| TestObjectTemplateInheritedWithPrototype(Constructor_GetFunction_NewInstance); |
| } |
| |
| THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype3) { |
| TestObjectTemplateInheritedWithPrototype(Constructor_GetFunction_New); |
| } |
| |
| // Test object instance creation using a function template without an instance |
| // template inherited from another function template. |
| static void TestObjectTemplateInheritedWithoutInstanceTemplate( |
| ObjectInstantiationMode mode) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| fun_A->SetClassName(v8_str("A")); |
| |
| Local<ObjectTemplate> templ_A = fun_A->InstanceTemplate(); |
| templ_A->SetNativeDataProperty(v8_str("nirk"), GetNirk); |
| templ_A->SetNativeDataProperty(v8_str("rino"), GetRino); |
| |
| Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::String> class_name = v8_str("B"); |
| fun_B->SetClassName(class_name); |
| fun_B->Inherit(fun_A); |
| |
| // Perform several iterations to trigger creation from cached boilerplate. |
| for (int i = 0; i < 3; i++) { |
| Local<v8::Object> instance; |
| switch (mode) { |
| case Constructor_GetFunction_NewInstance: { |
| Local<v8::Function> function_B = |
| fun_B->GetFunction(env.local()).ToLocalChecked(); |
| instance = function_B->NewInstance(env.local()).ToLocalChecked(); |
| break; |
| } |
| case Constructor_GetFunction_New: { |
| Local<v8::Function> function_B = |
| fun_B->GetFunction(env.local()).ToLocalChecked(); |
| if (i == 0) { |
| CHECK(env->Global() |
| ->Set(env.local(), class_name, function_B) |
| .FromJust()); |
| } |
| instance = |
| CompileRun("new B()")->ToObject(env.local()).ToLocalChecked(); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| |
| CHECK(class_name->StrictEquals(instance->GetConstructorName())); |
| CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust()); |
| |
| CHECK_EQ(900, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(560, CompileRun("o.rino")->IntegerValue(env.local()).FromJust()); |
| } |
| } |
| |
| THREADED_TEST(TestObjectTemplateInheritedWithPrototype1) { |
| TestObjectTemplateInheritedWithoutInstanceTemplate( |
| Constructor_GetFunction_NewInstance); |
| } |
| |
| THREADED_TEST(TestObjectTemplateInheritedWithPrototype2) { |
| TestObjectTemplateInheritedWithoutInstanceTemplate( |
| Constructor_GetFunction_New); |
| } |
| |
| THREADED_TEST(TestObjectTemplateClassInheritance) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| fun_A->SetClassName(v8_str("A")); |
| |
| Local<ObjectTemplate> templ_A = fun_A->InstanceTemplate(); |
| templ_A->SetNativeDataProperty(v8_str("nirk"), GetNirk); |
| templ_A->SetNativeDataProperty(v8_str("rino"), GetRino); |
| |
| Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::String> class_name = v8_str("B"); |
| fun_B->SetClassName(class_name); |
| fun_B->Inherit(fun_A); |
| |
| v8::Local<v8::String> subclass_name = v8_str("C"); |
| v8::Local<v8::Object> b_proto; |
| v8::Local<v8::Object> c_proto; |
| // Perform several iterations to make sure the cache doesn't break |
| // subclassing. |
| for (int i = 0; i < 3; i++) { |
| Local<v8::Function> function_B = |
| fun_B->GetFunction(env.local()).ToLocalChecked(); |
| if (i == 0) { |
| CHECK(env->Global()->Set(env.local(), class_name, function_B).FromJust()); |
| CompileRun("class C extends B {}"); |
| b_proto = |
| CompileRun("B.prototype")->ToObject(env.local()).ToLocalChecked(); |
| c_proto = |
| CompileRun("C.prototype")->ToObject(env.local()).ToLocalChecked(); |
| CHECK(b_proto->Equals(env.local(), c_proto->GetPrototype()).FromJust()); |
| } |
| Local<v8::Object> instance = |
| CompileRun("new C()")->ToObject(env.local()).ToLocalChecked(); |
| CHECK(c_proto->Equals(env.local(), instance->GetPrototype()).FromJust()); |
| |
| CHECK(subclass_name->StrictEquals(instance->GetConstructorName())); |
| CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust()); |
| |
| CHECK_EQ(900, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(560, CompileRun("o.rino")->IntegerValue(env.local()).FromJust()); |
| } |
| } |
| |
| static void NamedPropertyGetterWhichReturns42( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(v8_num(42)); |
| } |
| |
| THREADED_TEST(TestObjectTemplateReflectConstruct) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| fun_B->InstanceTemplate()->SetHandler( |
| v8::NamedPropertyHandlerConfiguration(NamedPropertyGetterWhichReturns42)); |
| v8::Local<v8::String> class_name = v8_str("B"); |
| fun_B->SetClassName(class_name); |
| |
| v8::Local<v8::String> subclass_name = v8_str("C"); |
| v8::Local<v8::Object> b_proto; |
| v8::Local<v8::Object> c_proto; |
| // Perform several iterations to make sure the cache doesn't break |
| // subclassing. |
| for (int i = 0; i < 3; i++) { |
| Local<v8::Function> function_B = |
| fun_B->GetFunction(env.local()).ToLocalChecked(); |
| if (i == 0) { |
| CHECK(env->Global()->Set(env.local(), class_name, function_B).FromJust()); |
| CompileRun("function C() {}"); |
| c_proto = |
| CompileRun("C.prototype")->ToObject(env.local()).ToLocalChecked(); |
| } |
| Local<v8::Object> instance = CompileRun("Reflect.construct(B, [], C)") |
| ->ToObject(env.local()) |
| .ToLocalChecked(); |
| CHECK(c_proto->Equals(env.local(), instance->GetPrototype()).FromJust()); |
| |
| CHECK(subclass_name->StrictEquals(instance->GetConstructorName())); |
| CHECK(env->Global()->Set(env.local(), v8_str("o"), instance).FromJust()); |
| |
| CHECK_EQ(42, CompileRun("o.nirk")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(42, CompileRun("o.rino")->IntegerValue(env.local()).FromJust()); |
| } |
| } |
| |
| static void GetFlabby(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(17.2)); |
| } |
| |
| |
| static void GetKnurd(Local<String> property, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(15.2)); |
| } |
| |
| |
| THREADED_TEST(DescriptorInheritance) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> super = v8::FunctionTemplate::New(isolate); |
| super->PrototypeTemplate()->Set(isolate, "flabby", |
| v8::FunctionTemplate::New(isolate, |
| GetFlabby)); |
| super->PrototypeTemplate()->Set(isolate, "PI", v8_num(3.14)); |
| |
| super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); |
| |
| v8::Local<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(isolate); |
| base1->Inherit(super); |
| base1->PrototypeTemplate()->Set(isolate, "v1", v8_num(20.1)); |
| |
| v8::Local<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(isolate); |
| base2->Inherit(super); |
| base2->PrototypeTemplate()->Set(isolate, "v2", v8_num(10.1)); |
| |
| LocalContext env; |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("s"), |
| super->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("base1"), |
| base1->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("base2"), |
| base2->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| // Checks right __proto__ chain. |
| CHECK(CompileRun("base1.prototype.__proto__ == s.prototype") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| CHECK(CompileRun("base2.prototype.__proto__ == s.prototype") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| |
| CHECK(v8_compile("s.prototype.PI == 3.14") |
| ->Run(env.local()) |
| .ToLocalChecked() |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| |
| // Instance accessor should not be visible on function object or its prototype |
| CHECK( |
| CompileRun("s.knurd == undefined")->BooleanValue(env.local()).FromJust()); |
| CHECK(CompileRun("s.prototype.knurd == undefined") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| CHECK(CompileRun("base1.prototype.knurd == undefined") |
| ->BooleanValue(env.local()) |
| .FromJust()); |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), base1->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked()) |
| .FromJust()); |
| CHECK_EQ(17.2, |
| CompileRun("obj.flabby()")->NumberValue(env.local()).FromJust()); |
| CHECK(CompileRun("'flabby' in obj")->BooleanValue(env.local()).FromJust()); |
| CHECK_EQ(15.2, CompileRun("obj.knurd")->NumberValue(env.local()).FromJust()); |
| CHECK(CompileRun("'knurd' in obj")->BooleanValue(env.local()).FromJust()); |
| CHECK_EQ(20.1, CompileRun("obj.v1")->NumberValue(env.local()).FromJust()); |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj2"), base2->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked()) |
| .FromJust()); |
| CHECK_EQ(17.2, |
| CompileRun("obj2.flabby()")->NumberValue(env.local()).FromJust()); |
| CHECK(CompileRun("'flabby' in obj2")->BooleanValue(env.local()).FromJust()); |
| CHECK_EQ(15.2, CompileRun("obj2.knurd")->NumberValue(env.local()).FromJust()); |
| CHECK(CompileRun("'knurd' in obj2")->BooleanValue(env.local()).FromJust()); |
| CHECK_EQ(10.1, CompileRun("obj2.v2")->NumberValue(env.local()).FromJust()); |
| |
| // base1 and base2 cannot cross reference to each's prototype |
| CHECK(CompileRun("obj.v2")->IsUndefined()); |
| CHECK(CompileRun("obj2.v1")->IsUndefined()); |
| } |
| |
| THREADED_TEST(DescriptorInheritance2) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| fun_A->SetClassName(v8_str("A")); |
| fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd1"), GetKnurd); |
| fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk1"), GetNirk); |
| fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("rino1"), GetRino); |
| |
| v8::Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| fun_B->SetClassName(v8_str("B")); |
| fun_B->Inherit(fun_A); |
| |
| v8::Local<v8::FunctionTemplate> fun_C = v8::FunctionTemplate::New(isolate); |
| fun_C->SetClassName(v8_str("C")); |
| fun_C->Inherit(fun_B); |
| fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd2"), GetKnurd); |
| fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk2"), GetNirk); |
| fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("rino2"), GetRino); |
| |
| v8::Local<v8::FunctionTemplate> fun_D = v8::FunctionTemplate::New(isolate); |
| fun_D->SetClassName(v8_str("D")); |
| fun_D->Inherit(fun_C); |
| |
| v8::Local<v8::FunctionTemplate> fun_E = v8::FunctionTemplate::New(isolate); |
| fun_E->SetClassName(v8_str("E")); |
| fun_E->Inherit(fun_D); |
| fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd3"), GetKnurd); |
| fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk3"), GetNirk); |
| fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("rino3"), GetRino); |
| |
| v8::Local<v8::FunctionTemplate> fun_F = v8::FunctionTemplate::New(isolate); |
| fun_F->SetClassName(v8_str("F")); |
| fun_F->Inherit(fun_E); |
| v8::Local<v8::ObjectTemplate> templ = fun_F->InstanceTemplate(); |
| const int kDataPropertiesNumber = 100; |
| for (int i = 0; i < kDataPropertiesNumber; i++) { |
| v8::Local<v8::Value> val = v8_num(i); |
| v8::Local<v8::String> val_str = val->ToString(env.local()).ToLocalChecked(); |
| v8::Local<v8::String> name = String::Concat(v8_str("p"), val_str); |
| |
| templ->Set(name, val); |
| templ->Set(val_str, val); |
| } |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("F"), |
| fun_F->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| v8::Local<v8::Script> script = v8_compile("o = new F()"); |
| |
| for (int i = 0; i < 100; i++) { |
| v8::HandleScope scope(isolate); |
| script->Run(env.local()).ToLocalChecked(); |
| } |
| v8::Local<v8::Object> object = script->Run(env.local()) |
| .ToLocalChecked() |
| ->ToObject(env.local()) |
| .ToLocalChecked(); |
| |
| CHECK_EQ(15.2, CompileRun("o.knurd1")->NumberValue(env.local()).FromJust()); |
| CHECK_EQ(15.2, CompileRun("o.knurd2")->NumberValue(env.local()).FromJust()); |
| CHECK_EQ(15.2, CompileRun("o.knurd3")->NumberValue(env.local()).FromJust()); |
| |
| CHECK_EQ(900, CompileRun("o.nirk1")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(900, CompileRun("o.nirk2")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(900, CompileRun("o.nirk3")->IntegerValue(env.local()).FromJust()); |
| |
| CHECK_EQ(560, CompileRun("o.rino1")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(560, CompileRun("o.rino2")->IntegerValue(env.local()).FromJust()); |
| CHECK_EQ(560, CompileRun("o.rino3")->IntegerValue(env.local()).FromJust()); |
| |
| for (int i = 0; i < kDataPropertiesNumber; i++) { |
| v8::Local<v8::Value> val = v8_num(i); |
| v8::Local<v8::String> val_str = val->ToString(env.local()).ToLocalChecked(); |
| v8::Local<v8::String> name = String::Concat(v8_str("p"), val_str); |
| |
| CHECK_EQ(i, object->Get(env.local(), name) |
| .ToLocalChecked() |
| ->IntegerValue(env.local()) |
| .FromJust()); |
| CHECK_EQ(i, object->Get(env.local(), val) |
| .ToLocalChecked() |
| ->IntegerValue(env.local()) |
| .FromJust()); |
| } |
| } |
| |
| |
| // Helper functions for Interceptor/Accessor interaction tests |
| |
| void SimpleAccessorGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| Local<Object> self = Local<Object>::Cast(info.This()); |
| info.GetReturnValue().Set(self->Get(info.GetIsolate()->GetCurrentContext(), |
| String::Concat(v8_str("accessor_"), name)) |
| .ToLocalChecked()); |
| } |
| |
| void SimpleAccessorSetter(Local<String> name, Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| Local<Object> self = Local<Object>::Cast(info.This()); |
| CHECK(self->Set(info.GetIsolate()->GetCurrentContext(), |
| String::Concat(v8_str("accessor_"), name), value) |
| .FromJust()); |
| } |
| |
| 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 SymbolAccessorGetterReturnsDefault( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(name->IsSymbol()); |
| Local<Symbol> sym = Local<Symbol>::Cast(name); |
| if (sym->Name()->IsUndefined()) return; |
| info.GetReturnValue().Set(info.Data()); |
| } |
| |
| static void ThrowingSymbolAccessorGetter( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(info.GetIsolate()->ThrowException(name)); |
| } |
| |
| |
| THREADED_TEST(AccessorIsPreservedOnAttributeChange) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| LocalContext env; |
| v8::Local<v8::Value> res = CompileRun("var a = []; a;"); |
| i::Handle<i::JSReceiver> a(v8::Utils::OpenHandle(v8::Object::Cast(*res))); |
| CHECK_EQ(1, a->map()->instance_descriptors()->number_of_descriptors()); |
| CompileRun("Object.defineProperty(a, 'length', { writable: false });"); |
| CHECK_EQ(0, a->map()->instance_descriptors()->number_of_descriptors()); |
| // But we should still have an AccessorInfo. |
| i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length"))); |
| i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR); |
| CHECK_EQ(i::LookupIterator::ACCESSOR, it.state()); |
| CHECK(it.GetAccessors()->IsAccessorInfo()); |
| } |
| |
| |
| THREADED_TEST(UndefinedIsNotEnumerable) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<Value> result = CompileRun("this.propertyIsEnumerable(undefined)"); |
| CHECK(result->IsFalse()); |
| } |
| |
| |
| v8::Local<Script> call_recursively_script; |
| static const int kTargetRecursionDepth = 100; // near maximum |
| |
| static void CallScriptRecursivelyCall( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| int depth = args.This() |
| ->Get(context, v8_str("depth")) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust(); |
| if (depth == kTargetRecursionDepth) return; |
| CHECK(args.This() |
| ->Set(context, v8_str("depth"), |
| v8::Integer::New(args.GetIsolate(), depth + 1)) |
| .FromJust()); |
| args.GetReturnValue().Set( |
| call_recursively_script->Run(context).ToLocalChecked()); |
| } |
| |
| |
| static void CallFunctionRecursivelyCall( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| int depth = args.This() |
| ->Get(context, v8_str("depth")) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust(); |
| if (depth == kTargetRecursionDepth) { |
| printf("[depth = %d]\n", depth); |
| return; |
| } |
| CHECK(args.This() |
| ->Set(context, v8_str("depth"), |
| v8::Integer::New(args.GetIsolate(), depth + 1)) |
| .FromJust()); |
| v8::Local<Value> function = |
| args.This() |
| ->Get(context, v8_str("callFunctionRecursively")) |
| .ToLocalChecked(); |
| args.GetReturnValue().Set(function.As<Function>() |
| ->Call(context, args.This(), 0, nullptr) |
| .ToLocalChecked()); |
| } |
| |
| |
| THREADED_TEST(DeepCrossLanguageRecursion) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); |
| global->Set(v8_str("callScriptRecursively"), |
| v8::FunctionTemplate::New(isolate, CallScriptRecursivelyCall)); |
| global->Set(v8_str("callFunctionRecursively"), |
| v8::FunctionTemplate::New(isolate, CallFunctionRecursivelyCall)); |
| LocalContext env(nullptr, global); |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("depth"), v8::Integer::New(isolate, 0)) |
| .FromJust()); |
| call_recursively_script = v8_compile("callScriptRecursively()"); |
| call_recursively_script->Run(env.local()).ToLocalChecked(); |
| call_recursively_script = v8::Local<Script>(); |
| |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("depth"), v8::Integer::New(isolate, 0)) |
| .FromJust()); |
| CompileRun("callFunctionRecursively()"); |
| } |
| |
| |
| static void ThrowingPropertyHandlerGet( |
| Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| // Since this interceptor is used on "with" objects, the runtime will look up |
| // @@unscopables. Punt. |
| if (key->IsSymbol()) return; |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(info.GetIsolate()->ThrowException(key)); |
| } |
| |
| |
| static void ThrowingPropertyHandlerSet( |
| Local<Name> key, Local<Value>, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetIsolate()->ThrowException(key); |
| info.GetReturnValue().SetUndefined(); // not the same as empty handle |
| } |
| |
| |
| THREADED_TEST(CallbackExceptionRegression) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| ThrowingPropertyHandlerGet, ThrowingPropertyHandlerSet)); |
| LocalContext env; |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("obj"), |
| obj->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::Local<Value> otto = |
| CompileRun("try { with (obj) { otto; } } catch (e) { e; }"); |
| CHECK(v8_str("otto")->Equals(env.local(), otto).FromJust()); |
| v8::Local<Value> netto = |
| CompileRun("try { with (obj) { netto = 4; } } catch (e) { e; }"); |
| CHECK(v8_str("netto")->Equals(env.local(), netto).FromJust()); |
| } |
| |
| |
| THREADED_TEST(FunctionPrototype) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(isolate); |
| Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); |
| LocalContext env; |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("Foo"), |
| Foo->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| Local<Script> script = v8_compile("Foo.prototype.plak"); |
| CHECK_EQ(v8_run_int32value(script), 321); |
| } |
| |
| THREADED_TEST(InternalFields) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetInternalFieldCount(1); |
| Local<v8::Object> obj = templ->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK_EQ(1, obj->InternalFieldCount()); |
| CHECK(obj->GetInternalField(0)->IsUndefined()); |
| obj->SetInternalField(0, v8_num(17)); |
| CHECK_EQ(17, obj->GetInternalField(0)->Int32Value(env.local()).FromJust()); |
| } |
| |
| THREADED_TEST(InternalFieldsOfRegularObjects) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| const char* sources[] = {"new Object()", "{ a: 'a property' }", "arguments"}; |
| for (size_t i = 0; i < arraysize(sources); ++i) { |
| i::ScopedVector<char> source(128); |
| i::SNPrintF(source, "(function() { return %s })()", sources[i]); |
| v8::Local<v8::Object> obj = CompileRun(source.start()).As<v8::Object>(); |
| CHECK_EQ(0, obj->InternalFieldCount()); |
| } |
| } |
| |
| THREADED_TEST(GlobalObjectInternalFields) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| global_template->SetInternalFieldCount(1); |
| LocalContext env(nullptr, global_template); |
| v8::Local<v8::Object> global_proxy = env->Global(); |
| v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); |
| CHECK_EQ(1, global->InternalFieldCount()); |
| CHECK(global->GetInternalField(0)->IsUndefined()); |
| global->SetInternalField(0, v8_num(17)); |
| CHECK_EQ(17, global->GetInternalField(0)->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectHasRealIndexedProperty) { |
| LocalContext env; |
| v8::HandleScope scope(CcTest::isolate()); |
| |
| v8::Local<v8::Object> global = env->Global(); |
| CHECK(global->Set(env.local(), 0, v8_str("value")).FromJust()); |
| CHECK(global->HasRealIndexedProperty(env.local(), 0).FromJust()); |
| } |
| |
| static void CheckAlignedPointerInInternalField(Local<v8::Object> obj, |
| void* value) { |
| CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); |
| obj->SetAlignedPointerInInternalField(0, value); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(value, obj->GetAlignedPointerFromInternalField(0)); |
| } |
| |
| THREADED_TEST(InternalFieldsAlignedPointers) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetInternalFieldCount(1); |
| Local<v8::Object> obj = templ->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK_EQ(1, obj->InternalFieldCount()); |
| |
| CheckAlignedPointerInInternalField(obj, nullptr); |
| |
| int* heap_allocated = new int[100]; |
| CheckAlignedPointerInInternalField(obj, heap_allocated); |
| delete[] heap_allocated; |
| |
| int stack_allocated[100]; |
| CheckAlignedPointerInInternalField(obj, stack_allocated); |
| |
| void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); |
| CheckAlignedPointerInInternalField(obj, huge); |
| |
| v8::Global<v8::Object> persistent(isolate, obj); |
| CHECK_EQ(1, Object::InternalFieldCount(persistent)); |
| CHECK_EQ(huge, Object::GetAlignedPointerFromInternalField(persistent, 0)); |
| } |
| |
| THREADED_TEST(SetAlignedPointerInInternalFields) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetInternalFieldCount(2); |
| Local<v8::Object> obj = templ->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK_EQ(2, obj->InternalFieldCount()); |
| |
| int* heap_allocated_1 = new int[100]; |
| int* heap_allocated_2 = new int[100]; |
| int indices[] = {0, 1}; |
| void* values[] = {heap_allocated_1, heap_allocated_2}; |
| |
| obj->SetAlignedPointerInInternalFields(2, indices, values); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(heap_allocated_1, obj->GetAlignedPointerFromInternalField(0)); |
| CHECK_EQ(heap_allocated_2, obj->GetAlignedPointerFromInternalField(1)); |
| |
| indices[0] = 1; |
| indices[1] = 0; |
| obj->SetAlignedPointerInInternalFields(2, indices, values); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(heap_allocated_2, obj->GetAlignedPointerFromInternalField(0)); |
| CHECK_EQ(heap_allocated_1, obj->GetAlignedPointerFromInternalField(1)); |
| |
| delete[] heap_allocated_1; |
| delete[] heap_allocated_2; |
| } |
| |
| static void CheckAlignedPointerInEmbedderData(LocalContext* env, int index, |
| void* value) { |
| CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); |
| (*env)->SetAlignedPointerInEmbedderData(index, value); |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(value, (*env)->GetAlignedPointerFromEmbedderData(index)); |
| } |
| |
| |
| static void* AlignedTestPointer(int i) { |
| return reinterpret_cast<void*>(i * 1234); |
| } |
| |
| |
| THREADED_TEST(EmbedderDataAlignedPointers) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| CheckAlignedPointerInEmbedderData(&env, 0, nullptr); |
| |
| int* heap_allocated = new int[100]; |
| CheckAlignedPointerInEmbedderData(&env, 1, heap_allocated); |
| delete[] heap_allocated; |
| |
| int stack_allocated[100]; |
| CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated); |
| |
| void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); |
| CheckAlignedPointerInEmbedderData(&env, 3, huge); |
| |
| // Test growing of the embedder data's backing store. |
| for (int i = 0; i < 100; i++) { |
| env->SetAlignedPointerInEmbedderData(i, AlignedTestPointer(i)); |
| } |
| CcTest::CollectAllGarbage(); |
| for (int i = 0; i < 100; i++) { |
| CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i)); |
| } |
| } |
| |
| static void CheckEmbedderData(LocalContext* env, int index, |
| v8::Local<Value> data) { |
| (*env)->SetEmbedderData(index, data); |
| CHECK((*env)->GetEmbedderData(index)->StrictEquals(data)); |
| } |
| |
| |
| THREADED_TEST(EmbedderData) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| CheckEmbedderData(&env, 3, v8_str("The quick brown fox jumps")); |
| CheckEmbedderData(&env, 2, v8_str("over the lazy dog.")); |
| CheckEmbedderData(&env, 1, v8::Number::New(isolate, 1.2345)); |
| CheckEmbedderData(&env, 0, v8::Boolean::New(isolate, true)); |
| } |
| |
| |
| THREADED_TEST(IdentityHash) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Ensure that the test starts with an fresh heap to test whether the hash |
| // code is based on the address. |
| CcTest::CollectAllGarbage(); |
| Local<v8::Object> obj = v8::Object::New(isolate); |
| int hash = obj->GetIdentityHash(); |
| int hash1 = obj->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| int hash2 = v8::Object::New(isolate)->GetIdentityHash(); |
| // Since the identity hash is essentially a random number two consecutive |
| // objects should not be assigned the same hash code. If the test below fails |
| // the random number generator should be evaluated. |
| CHECK_NE(hash, hash2); |
| CcTest::CollectAllGarbage(); |
| int hash3 = v8::Object::New(isolate)->GetIdentityHash(); |
| // Make sure that the identity hash is not based on the initial address of |
| // the object alone. If the test below fails the random number generator |
| // should be evaluated. |
| CHECK_NE(hash, hash3); |
| int hash4 = obj->GetIdentityHash(); |
| CHECK_EQ(hash, hash4); |
| |
| // Check identity hashes behaviour in the presence of JS accessors. |
| // Put a getter for 'v8::IdentityHash' on the Object's prototype: |
| { |
| CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); |
| Local<v8::Object> o1 = v8::Object::New(isolate); |
| Local<v8::Object> o2 = v8::Object::New(isolate); |
| CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); |
| } |
| { |
| CompileRun( |
| "function cnst() { return 42; };\n" |
| "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); |
| Local<v8::Object> o1 = v8::Object::New(isolate); |
| Local<v8::Object> o2 = v8::Object::New(isolate); |
| CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); |
| } |
| } |
| |
| |
| void GlobalProxyIdentityHash(bool set_in_js) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| v8::HandleScope scope(isolate); |
| Local<Object> global_proxy = env->Global(); |
| i::Handle<i::Object> i_global_proxy = v8::Utils::OpenHandle(*global_proxy); |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("global"), global_proxy) |
| .FromJust()); |
| int32_t hash1; |
| if (set_in_js) { |
| CompileRun("var m = new Set(); m.add(global);"); |
| i::Object* original_hash = i_global_proxy->GetHash(); |
| CHECK(original_hash->IsSmi()); |
| hash1 = i::Smi::ToInt(original_hash); |
| } else { |
| hash1 = i_global_proxy->GetOrCreateHash(i_isolate)->value(); |
| } |
| // Hash should be retained after being detached. |
| env->DetachGlobal(); |
| int hash2 = global_proxy->GetIdentityHash(); |
| CHECK_EQ(hash1, hash2); |
| { |
| // Re-attach global proxy to a new context, hash should stay the same. |
| LocalContext env2(nullptr, Local<ObjectTemplate>(), global_proxy); |
| int hash3 = global_proxy->GetIdentityHash(); |
| CHECK_EQ(hash1, hash3); |
| } |
| } |
| |
| |
| THREADED_TEST(GlobalProxyIdentityHash) { |
| GlobalProxyIdentityHash(true); |
| GlobalProxyIdentityHash(false); |
| } |
| |
| |
| TEST(SymbolIdentityHash) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| { |
| Local<v8::Symbol> symbol = v8::Symbol::New(isolate); |
| int hash = symbol->GetIdentityHash(); |
| int hash1 = symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| CcTest::CollectAllGarbage(); |
| int hash3 = symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash3); |
| } |
| |
| { |
| v8::Local<v8::Symbol> js_symbol = |
| CompileRun("Symbol('foo')").As<v8::Symbol>(); |
| int hash = js_symbol->GetIdentityHash(); |
| int hash1 = js_symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| CcTest::CollectAllGarbage(); |
| int hash3 = js_symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash3); |
| } |
| } |
| |
| |
| TEST(StringIdentityHash) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::String> str = v8_str("str1"); |
| int hash = str->GetIdentityHash(); |
| int hash1 = str->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| CcTest::CollectAllGarbage(); |
| int hash3 = str->GetIdentityHash(); |
| CHECK_EQ(hash, hash3); |
| |
| Local<v8::String> str2 = v8_str("str1"); |
| int hash4 = str2->GetIdentityHash(); |
| CHECK_EQ(hash, hash4); |
| } |
| |
| |
| THREADED_TEST(SymbolProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| v8::Local<v8::Symbol> sym1 = v8::Symbol::New(isolate); |
| v8::Local<v8::Symbol> sym2 = v8::Symbol::New(isolate, v8_str("my-symbol")); |
| v8::Local<v8::Symbol> sym3 = v8::Symbol::New(isolate, v8_str("sym3")); |
| v8::Local<v8::Symbol> sym4 = v8::Symbol::New(isolate, v8_str("native")); |
| |
| CcTest::CollectAllGarbage(); |
| |
| // Check basic symbol functionality. |
| CHECK(sym1->IsSymbol()); |
| CHECK(sym2->IsSymbol()); |
| CHECK(!obj->IsSymbol()); |
| |
| CHECK(sym1->Equals(env.local(), sym1).FromJust()); |
| CHECK(sym2->Equals(env.local(), sym2).FromJust()); |
| CHECK(!sym1->Equals(env.local(), sym2).FromJust()); |
| CHECK(!sym2->Equals(env.local(), sym1).FromJust()); |
| CHECK(sym1->StrictEquals(sym1)); |
| CHECK(sym2->StrictEquals(sym2)); |
| CHECK(!sym1->StrictEquals(sym2)); |
| CHECK(!sym2->StrictEquals(sym1)); |
| |
| CHECK(sym2->Name()->Equals(env.local(), v8_str("my-symbol")).FromJust()); |
| |
| v8::Local<v8::Value> sym_val = sym2; |
| CHECK(sym_val->IsSymbol()); |
| CHECK(sym_val->Equals(env.local(), sym2).FromJust()); |
| CHECK(sym_val->StrictEquals(sym2)); |
| CHECK(v8::Symbol::Cast(*sym_val)->Equals(env.local(), sym2).FromJust()); |
| |
| v8::Local<v8::Value> sym_obj = v8::SymbolObject::New(isolate, sym2); |
| CHECK(sym_obj->IsSymbolObject()); |
| CHECK(!sym2->IsSymbolObject()); |
| CHECK(!obj->IsSymbolObject()); |
| CHECK(sym_obj->Equals(env.local(), sym2).FromJust()); |
| CHECK(!sym_obj->StrictEquals(sym2)); |
| CHECK(v8::SymbolObject::Cast(*sym_obj) |
| ->Equals(env.local(), sym_obj) |
| .FromJust()); |
| CHECK(v8::SymbolObject::Cast(*sym_obj) |
| ->ValueOf() |
| ->Equals(env.local(), sym2) |
| .FromJust()); |
| |
| // Make sure delete of a non-existent symbol property works. |
| CHECK(obj->Delete(env.local(), sym1).FromJust()); |
| CHECK(!obj->Has(env.local(), sym1).FromJust()); |
| |
| CHECK( |
| obj->Set(env.local(), sym1, v8::Integer::New(isolate, 1503)).FromJust()); |
| CHECK(obj->Has(env.local(), sym1).FromJust()); |
| CHECK_EQ(1503, obj->Get(env.local(), sym1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK( |
| obj->Set(env.local(), sym1, v8::Integer::New(isolate, 2002)).FromJust()); |
| CHECK(obj->Has(env.local(), sym1).FromJust()); |
| CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(v8::None, obj->GetPropertyAttributes(env.local(), sym1).FromJust()); |
| |
| CHECK_EQ(0u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| unsigned num_props = |
| obj->GetPropertyNames(env.local()).ToLocalChecked()->Length(); |
| CHECK(obj->Set(env.local(), v8_str("bla"), v8::Integer::New(isolate, 20)) |
| .FromJust()); |
| CHECK_EQ(1u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| CHECK_EQ(num_props + 1, |
| obj->GetPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| CcTest::CollectAllGarbage(); |
| |
| CHECK(obj->SetAccessor(env.local(), sym3, SymbolAccessorGetter, |
| SymbolAccessorSetter) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), sym3).ToLocalChecked()->IsUndefined()); |
| CHECK(obj->Set(env.local(), sym3, v8::Integer::New(isolate, 42)).FromJust()); |
| CHECK(obj->Get(env.local(), sym3) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), v8_str("accessor_sym3")) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| |
| CHECK(obj->SetNativeDataProperty(env.local(), sym4, SymbolAccessorGetter) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), sym4).ToLocalChecked()->IsUndefined()); |
| CHECK(obj->Set(env.local(), v8_str("accessor_native"), |
| v8::Integer::New(isolate, 123)) |
| .FromJust()); |
| CHECK_EQ(123, obj->Get(env.local(), sym4) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->Set(env.local(), sym4, v8::Integer::New(isolate, 314)).FromJust()); |
| CHECK(obj->Get(env.local(), sym4) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 314)) |
| .FromJust()); |
| CHECK(obj->Delete(env.local(), v8_str("accessor_native")).FromJust()); |
| |
| // Add another property and delete it afterwards to force the object in |
| // slow case. |
| CHECK( |
| obj->Set(env.local(), sym2, v8::Integer::New(isolate, 2008)).FromJust()); |
| CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2008, obj->Get(env.local(), sym2) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| CHECK(obj->Has(env.local(), sym1).FromJust()); |
| CHECK(obj->Has(env.local(), sym2).FromJust()); |
| CHECK(obj->Has(env.local(), sym3).FromJust()); |
| CHECK(obj->Has(env.local(), v8_str("accessor_sym3")).FromJust()); |
| CHECK(obj->Delete(env.local(), sym2).FromJust()); |
| CHECK(obj->Has(env.local(), sym1).FromJust()); |
| CHECK(!obj->Has(env.local(), sym2).FromJust()); |
| CHECK(obj->Has(env.local(), sym3).FromJust()); |
| CHECK(obj->Has(env.local(), v8_str("accessor_sym3")).FromJust()); |
| CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), sym3) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), v8_str("accessor_sym3")) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| CHECK_EQ(2u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| // Symbol properties are inherited. |
| v8::Local<v8::Object> child = v8::Object::New(isolate); |
| CHECK(child->SetPrototype(env.local(), obj).FromJust()); |
| CHECK(child->Has(env.local(), sym1).FromJust()); |
| CHECK_EQ(2002, child->Get(env.local(), sym1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), sym3) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), v8_str("accessor_sym3")) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| CHECK_EQ(0u, |
| child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| } |
| |
| |
| THREADED_TEST(SymbolTemplateProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::Name> name = v8::Symbol::New(isolate); |
| CHECK(!name.IsEmpty()); |
| foo->PrototypeTemplate()->Set(name, v8::FunctionTemplate::New(isolate)); |
| v8::Local<v8::Object> new_instance = |
| foo->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked(); |
| CHECK(!new_instance.IsEmpty()); |
| CHECK(new_instance->Has(env.local(), name).FromJust()); |
| } |
| |
| |
| THREADED_TEST(PrivatePropertiesOnProxies) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Object> target = CompileRun("({})").As<v8::Object>(); |
| v8::Local<v8::Object> handler = CompileRun("({})").As<v8::Object>(); |
| |
| v8::Local<v8::Proxy> proxy = |
| v8::Proxy::New(env.local(), target, handler).ToLocalChecked(); |
| |
| v8::Local<v8::Private> priv1 = v8::Private::New(isolate); |
| v8::Local<v8::Private> priv2 = |
| v8::Private::New(isolate, v8_str("my-private")); |
| |
| CcTest::CollectAllGarbage(); |
| |
| CHECK(priv2->Name() |
| ->Equals(env.local(), |
| v8::String::NewFromUtf8(isolate, "my-private", |
| v8::NewStringType::kNormal) |
| .ToLocalChecked()) |
| .FromJust()); |
| |
| // Make sure delete of a non-existent private symbol property works. |
| proxy->DeletePrivate(env.local(), priv1).FromJust(); |
| CHECK(!proxy->HasPrivate(env.local(), priv1).FromJust()); |
| |
| CHECK(proxy->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 1503)) |
| .FromJust()); |
| CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK_EQ(1503, proxy->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(proxy->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 2002)) |
| .FromJust()); |
| CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| |
| CHECK_EQ(0u, |
| proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| unsigned num_props = |
| proxy->GetPropertyNames(env.local()).ToLocalChecked()->Length(); |
| CHECK(proxy->Set(env.local(), v8::String::NewFromUtf8( |
| isolate, "bla", v8::NewStringType::kNormal) |
| .ToLocalChecked(), |
| v8::Integer::New(isolate, 20)) |
| .FromJust()); |
| CHECK_EQ(1u, |
| proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| CHECK_EQ(num_props + 1, |
| proxy->GetPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| CcTest::CollectAllGarbage(); |
| |
| // Add another property and delete it afterwards to force the object in |
| // slow case. |
| CHECK(proxy->SetPrivate(env.local(), priv2, v8::Integer::New(isolate, 2008)) |
| .FromJust()); |
| CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2008, proxy->GetPrivate(env.local(), priv2) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(1u, |
| proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK(proxy->HasPrivate(env.local(), priv2).FromJust()); |
| CHECK(proxy->DeletePrivate(env.local(), priv2).FromJust()); |
| CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK(!proxy->HasPrivate(env.local(), priv2).FromJust()); |
| CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(1u, |
| proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| // Private properties are not inherited (for the time being). |
| v8::Local<v8::Object> child = v8::Object::New(isolate); |
| CHECK(child->SetPrototype(env.local(), proxy).FromJust()); |
| CHECK(!child->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK_EQ(0u, |
| child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| } |
| |
| |
| THREADED_TEST(PrivateProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| v8::Local<v8::Private> priv1 = v8::Private::New(isolate); |
| v8::Local<v8::Private> priv2 = |
| v8::Private::New(isolate, v8_str("my-private")); |
| |
| CcTest::CollectAllGarbage(); |
| |
| CHECK(priv2->Name() |
| ->Equals(env.local(), |
| v8::String::NewFromUtf8(isolate, "my-private", |
| v8::NewStringType::kNormal) |
| .ToLocalChecked()) |
| .FromJust()); |
| |
| // Make sure delete of a non-existent private symbol property works. |
| obj->DeletePrivate(env.local(), priv1).FromJust(); |
| CHECK(!obj->HasPrivate(env.local(), priv1).FromJust()); |
| |
| CHECK(obj->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 1503)) |
| .FromJust()); |
| CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK_EQ(1503, obj->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 2002)) |
| .FromJust()); |
| CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| |
| CHECK_EQ(0u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| unsigned num_props = |
| obj->GetPropertyNames(env.local()).ToLocalChecked()->Length(); |
| CHECK(obj->Set(env.local(), v8::String::NewFromUtf8( |
| isolate, "bla", v8::NewStringType::kNormal) |
| .ToLocalChecked(), |
| v8::Integer::New(isolate, 20)) |
| .FromJust()); |
| CHECK_EQ(1u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| CHECK_EQ(num_props + 1, |
| obj->GetPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| CcTest::CollectAllGarbage(); |
| |
| // Add another property and delete it afterwards to force the object in |
| // slow case. |
| CHECK(obj->SetPrivate(env.local(), priv2, v8::Integer::New(isolate, 2008)) |
| .FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2008, obj->GetPrivate(env.local(), priv2) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(1u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK(obj->HasPrivate(env.local(), priv2).FromJust()); |
| CHECK(obj->DeletePrivate(env.local(), priv2).FromJust()); |
| CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK(!obj->HasPrivate(env.local(), priv2).FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(1u, |
| obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| |
| // Private properties are not inherited (for the time being). |
| v8::Local<v8::Object> child = v8::Object::New(isolate); |
| CHECK(child->SetPrototype(env.local(), obj).FromJust()); |
| CHECK(!child->HasPrivate(env.local(), priv1).FromJust()); |
| CHECK_EQ(0u, |
| child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| } |
| |
| |
| THREADED_TEST(GlobalSymbols) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<String> name = v8_str("my-symbol"); |
| v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name); |
| v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name); |
| CHECK(glob2->SameValue(glob)); |
| |
| v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name); |
| v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name); |
| CHECK(glob_api2->SameValue(glob_api)); |
| CHECK(!glob_api->SameValue(glob)); |
| |
| v8::Local<v8::Symbol> sym = v8::Symbol::New(isolate, name); |
| CHECK(!sym->SameValue(glob)); |
| |
| CompileRun("var sym2 = Symbol.for('my-symbol')"); |
| v8::Local<Value> sym2 = |
| env->Global()->Get(env.local(), v8_str("sym2")).ToLocalChecked(); |
| CHECK(sym2->SameValue(glob)); |
| CHECK(!sym2->SameValue(glob_api)); |
| } |
| |
| THREADED_TEST(GlobalSymbolsNoContext) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<String> name = v8_str("my-symbol"); |
| v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name); |
| v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name); |
| CHECK(glob2->SameValue(glob)); |
| |
| v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name); |
| v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name); |
| CHECK(glob_api2->SameValue(glob_api)); |
| CHECK(!glob_api->SameValue(glob)); |
| } |
| |
| static void CheckWellKnownSymbol(v8::Local<v8::Symbol>(*getter)(v8::Isolate*), |
| const char* name) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Symbol> symbol = getter(isolate); |
| std::string script = std::string("var sym = ") + name; |
| CompileRun(script.c_str()); |
| v8::Local<Value> value = |
| env->Global()->Get(env.local(), v8_str("sym")).ToLocalChecked(); |
| |
| CHECK(!value.IsEmpty()); |
| CHECK(!symbol.IsEmpty()); |
| CHECK(value->SameValue(symbol)); |
| } |
| |
| |
| THREADED_TEST(WellKnownSymbols) { |
| CheckWellKnownSymbol(v8::Symbol::GetIterator, "Symbol.iterator"); |
| CheckWellKnownSymbol(v8::Symbol::GetUnscopables, "Symbol.unscopables"); |
| CheckWellKnownSymbol(v8::Symbol::GetHasInstance, "Symbol.hasInstance"); |
| CheckWellKnownSymbol(v8::Symbol::GetIsConcatSpreadable, |
| "Symbol.isConcatSpreadable"); |
| CheckWellKnownSymbol(v8::Symbol::GetMatch, "Symbol.match"); |
| CheckWellKnownSymbol(v8::Symbol::GetReplace, "Symbol.replace"); |
| CheckWellKnownSymbol(v8::Symbol::GetSearch, "Symbol.search"); |
| CheckWellKnownSymbol(v8::Symbol::GetSplit, "Symbol.split"); |
| CheckWellKnownSymbol(v8::Symbol::GetToPrimitive, "Symbol.toPrimitive"); |
| CheckWellKnownSymbol(v8::Symbol::GetToStringTag, "Symbol.toStringTag"); |
| } |
| |
| |
| THREADED_TEST(GlobalPrivates) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<String> name = v8_str("my-private"); |
| v8::Local<v8::Private> glob = v8::Private::ForApi(isolate, name); |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| CHECK(obj->SetPrivate(env.local(), glob, v8::Integer::New(isolate, 3)) |
| .FromJust()); |
| |
| v8::Local<v8::Private> glob2 = v8::Private::ForApi(isolate, name); |
| CHECK(obj->HasPrivate(env.local(), glob2).FromJust()); |
| |
| v8::Local<v8::Private> priv = v8::Private::New(isolate, name); |
| CHECK(!obj->HasPrivate(env.local(), priv).FromJust()); |
| |
| CompileRun("var intern = %CreatePrivateSymbol('my-private')"); |
| v8::Local<Value> intern = |
| env->Global()->Get(env.local(), v8_str("intern")).ToLocalChecked(); |
| CHECK(!obj->Has(env.local(), intern).FromJust()); |
| } |
| |
| |
| class ScopedArrayBufferContents { |
| public: |
| explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents) |
| : contents_(contents) {} |
| ~ScopedArrayBufferContents() { free(contents_.AllocationBase()); } |
| void* Data() const { return contents_.Data(); } |
| size_t ByteLength() const { return contents_.ByteLength(); } |
| |
| void* AllocationBase() const { return contents_.AllocationBase(); } |
| size_t AllocationLength() const { return contents_.AllocationLength(); } |
| v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const { |
| return contents_.AllocationMode(); |
| } |
| |
| private: |
| const v8::ArrayBuffer::Contents contents_; |
| }; |
| |
| template <typename T> |
| static void CheckInternalFieldsAreZero(v8::Local<T> value) { |
| CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount()); |
| for (int i = 0; i < value->InternalFieldCount(); i++) { |
| CHECK_EQ(0, value->GetInternalField(i) |
| ->Int32Value(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| } |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024); |
| CheckInternalFieldsAreZero(ab); |
| CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); |
| CHECK(!ab->IsExternal()); |
| CcTest::CollectAllGarbage(); |
| |
| ScopedArrayBufferContents ab_contents(ab->Externalize()); |
| CHECK(ab->IsExternal()); |
| |
| CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); |
| uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); |
| CHECK_NOT_NULL(data); |
| CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust()); |
| |
| v8::Local<v8::Value> result = CompileRun("ab.byteLength"); |
| CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| |
| result = CompileRun( |
| "var u8 = new Uint8Array(ab);" |
| "u8[0] = 0xFF;" |
| "u8[1] = 0xAA;" |
| "u8.length"); |
| CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| CHECK_EQ(0xFF, data[0]); |
| CHECK_EQ(0xAA, data[1]); |
| data[0] = 0xCC; |
| data[1] = 0x11; |
| result = CompileRun("u8[0] + u8[1]"); |
| CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_JSInternalToExternal) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| |
| v8::Local<v8::Value> result = CompileRun( |
| "var ab1 = new ArrayBuffer(2);" |
| "var u8_a = new Uint8Array(ab1);" |
| "u8_a[0] = 0xAA;" |
| "u8_a[1] = 0xFF; u8_a.buffer"); |
| Local<v8::ArrayBuffer> ab1 = Local<v8::ArrayBuffer>::Cast(result); |
| CheckInternalFieldsAreZero(ab1); |
| CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); |
| CHECK(!ab1->IsExternal()); |
| ScopedArrayBufferContents ab1_contents(ab1->Externalize()); |
| CHECK(ab1->IsExternal()); |
| |
| result = CompileRun("ab1.byteLength"); |
| CHECK_EQ(2, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun("u8_a[0]"); |
| CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun("u8_a[1]"); |
| CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab1);" |
| "u8_b[0] = 0xBB;" |
| "u8_a[0]"); |
| CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun("u8_b[1]"); |
| CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| |
| CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); |
| uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); |
| CHECK_EQ(0xBB, ab1_data[0]); |
| CHECK_EQ(0xFF, ab1_data[1]); |
| ab1_data[0] = 0xCC; |
| ab1_data[1] = 0x11; |
| result = CompileRun("u8_a[0] + u8_a[1]"); |
| CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_External) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<uint8_t> my_data(100); |
| memset(my_data.start(), 0, 100); |
| Local<v8::ArrayBuffer> ab3 = |
| v8::ArrayBuffer::New(isolate, my_data.start(), 100); |
| CheckInternalFieldsAreZero(ab3); |
| CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); |
| CHECK(ab3->IsExternal()); |
| |
| CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust()); |
| |
| v8::Local<v8::Value> result = CompileRun("ab3.byteLength"); |
| CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab3);" |
| "u8_b[0] = 0xBB;" |
| "u8_b[1] = 0xCC;" |
| "u8_b.length"); |
| CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| CHECK_EQ(0xBB, my_data[0]); |
| CHECK_EQ(0xCC, my_data[1]); |
| my_data[0] = 0xCC; |
| my_data[1] = 0x11; |
| result = CompileRun("u8_b[0] + u8_b[1]"); |
| CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_DisableNeuter) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<uint8_t> my_data(100); |
| memset(my_data.start(), 0, 100); |
| Local<v8::ArrayBuffer> ab = |
| v8::ArrayBuffer::New(isolate, my_data.start(), 100); |
| CHECK(ab->IsNeuterable()); |
| |
| i::Handle<i::JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); |
| buf->set_is_neuterable(false); |
| |
| CHECK(!ab->IsNeuterable()); |
| } |
| |
| |
| static void CheckDataViewIsNeutered(v8::Local<v8::DataView> dv) { |
| CHECK_EQ(0, static_cast<int>(dv->ByteLength())); |
| CHECK_EQ(0, static_cast<int>(dv->ByteOffset())); |
| } |
| |
| |
| static void CheckIsNeutered(v8::Local<v8::TypedArray> ta) { |
| CHECK_EQ(0, static_cast<int>(ta->ByteLength())); |
| CHECK_EQ(0, static_cast<int>(ta->Length())); |
| CHECK_EQ(0, static_cast<int>(ta->ByteOffset())); |
| } |
| |
| |
| static void CheckIsTypedArrayVarNeutered(const char* name) { |
| i::ScopedVector<char> source(1024); |
| i::SNPrintF(source, |
| "%s.byteLength == 0 && %s.byteOffset == 0 && %s.length == 0", |
| name, name, name); |
| CHECK(CompileRun(source.start())->IsTrue()); |
| v8::Local<v8::TypedArray> ta = |
| v8::Local<v8::TypedArray>::Cast(CompileRun(name)); |
| CheckIsNeutered(ta); |
| } |
| |
| |
| template <typename TypedArray, int kElementSize> |
| static Local<TypedArray> CreateAndCheck(Local<v8::ArrayBuffer> ab, |
| int byteOffset, int length) { |
| v8::Local<TypedArray> ta = TypedArray::New(ab, byteOffset, length); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta); |
| CHECK_EQ(byteOffset, static_cast<int>(ta->ByteOffset())); |
| CHECK_EQ(length, static_cast<int>(ta->Length())); |
| CHECK_EQ(length * kElementSize, static_cast<int>(ta->ByteLength())); |
| return ta; |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_NeuteringApi) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024); |
| |
| v8::Local<v8::Uint8Array> u8a = |
| CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023); |
| v8::Local<v8::Uint8ClampedArray> u8c = |
| CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023); |
| v8::Local<v8::Int8Array> i8a = |
| CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023); |
| |
| v8::Local<v8::Uint16Array> u16a = |
| CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511); |
| v8::Local<v8::Int16Array> i16a = |
| CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511); |
| |
| v8::Local<v8::Uint32Array> u32a = |
| CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255); |
| v8::Local<v8::Int32Array> i32a = |
| CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255); |
| |
| v8::Local<v8::Float32Array> f32a = |
| CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255); |
| v8::Local<v8::Float64Array> f64a = |
| CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127); |
| |
| v8::Local<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| CHECK_EQ(1, static_cast<int>(dv->ByteOffset())); |
| CHECK_EQ(1023, static_cast<int>(dv->ByteLength())); |
| |
| ScopedArrayBufferContents contents(buffer->Externalize()); |
| buffer->Neuter(); |
| CHECK_EQ(0, static_cast<int>(buffer->ByteLength())); |
| CheckIsNeutered(u8a); |
| CheckIsNeutered(u8c); |
| CheckIsNeutered(i8a); |
| CheckIsNeutered(u16a); |
| CheckIsNeutered(i16a); |
| CheckIsNeutered(u32a); |
| CheckIsNeutered(i32a); |
| CheckIsNeutered(f32a); |
| CheckIsNeutered(f64a); |
| CheckDataViewIsNeutered(dv); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_NeuteringScript) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| CompileRun( |
| "var ab = new ArrayBuffer(1024);" |
| "var u8a = new Uint8Array(ab, 1, 1023);" |
| "var u8c = new Uint8ClampedArray(ab, 1, 1023);" |
| "var i8a = new Int8Array(ab, 1, 1023);" |
| "var u16a = new Uint16Array(ab, 2, 511);" |
| "var i16a = new Int16Array(ab, 2, 511);" |
| "var u32a = new Uint32Array(ab, 4, 255);" |
| "var i32a = new Int32Array(ab, 4, 255);" |
| "var f32a = new Float32Array(ab, 4, 255);" |
| "var f64a = new Float64Array(ab, 8, 127);" |
| "var dv = new DataView(ab, 1, 1023);"); |
| |
| v8::Local<v8::ArrayBuffer> ab = |
| Local<v8::ArrayBuffer>::Cast(CompileRun("ab")); |
| |
| v8::Local<v8::DataView> dv = v8::Local<v8::DataView>::Cast(CompileRun("dv")); |
| |
| ScopedArrayBufferContents contents(ab->Externalize()); |
| ab->Neuter(); |
| CHECK_EQ(0, static_cast<int>(ab->ByteLength())); |
| CHECK_EQ(0, v8_run_int32value(v8_compile("ab.byteLength"))); |
| |
| CheckIsTypedArrayVarNeutered("u8a"); |
| CheckIsTypedArrayVarNeutered("u8c"); |
| CheckIsTypedArrayVarNeutered("i8a"); |
| CheckIsTypedArrayVarNeutered("u16a"); |
| CheckIsTypedArrayVarNeutered("i16a"); |
| CheckIsTypedArrayVarNeutered("u32a"); |
| CheckIsTypedArrayVarNeutered("i32a"); |
| CheckIsTypedArrayVarNeutered("f32a"); |
| CheckIsTypedArrayVarNeutered("f64a"); |
| |
| CHECK(CompileRun("dv.byteLength == 0 && dv.byteOffset == 0")->IsTrue()); |
| CheckDataViewIsNeutered(dv); |
| } |
| |
| THREADED_TEST(ArrayBuffer_AllocationInformation) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| const size_t ab_size = 1024; |
| Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, ab_size); |
| ScopedArrayBufferContents contents(ab->Externalize()); |
| |
| // Array buffers should have normal allocation mode. |
| CHECK_EQ(contents.AllocationMode(), |
| v8::ArrayBuffer::Allocator::AllocationMode::kNormal); |
| // The allocation must contain the buffer (normally they will be equal, but |
| // this is not required by the contract). |
| CHECK_NOT_NULL(contents.AllocationBase()); |
| const uintptr_t alloc = |
| reinterpret_cast<uintptr_t>(contents.AllocationBase()); |
| const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data()); |
| CHECK_LE(alloc, data); |
| CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength()); |
| } |
| |
| class ScopedSharedArrayBufferContents { |
| public: |
| explicit ScopedSharedArrayBufferContents( |
| const v8::SharedArrayBuffer::Contents& contents) |
| : contents_(contents) {} |
| ~ScopedSharedArrayBufferContents() { free(contents_.AllocationBase()); } |
| void* Data() const { return contents_.Data(); } |
| size_t ByteLength() const { return contents_.ByteLength(); } |
| |
| void* AllocationBase() const { return contents_.AllocationBase(); } |
| size_t AllocationLength() const { return contents_.AllocationLength(); } |
| v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const { |
| return contents_.AllocationMode(); |
| } |
| |
| private: |
| const v8::SharedArrayBuffer::Contents contents_; |
| }; |
| |
| |
| THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::SharedArrayBuffer> ab = v8::SharedArrayBuffer::New(isolate, 1024); |
| CheckInternalFieldsAreZero(ab); |
| CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); |
| CHECK(!ab->IsExternal()); |
| CcTest::CollectAllGarbage(); |
| |
| ScopedSharedArrayBufferContents ab_contents(ab->Externalize()); |
| CHECK(ab->IsExternal()); |
| |
| CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); |
| uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); |
| CHECK_NOT_NULL(data); |
| CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust()); |
| |
| v8::Local<v8::Value> result = CompileRun("ab.byteLength"); |
| CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| |
| result = CompileRun( |
| "var u8 = new Uint8Array(ab);" |
| "u8[0] = 0xFF;" |
| "u8[1] = 0xAA;" |
| "u8.length"); |
| CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| CHECK_EQ(0xFF, data[0]); |
| CHECK_EQ(0xAA, data[1]); |
| data[0] = 0xCC; |
| data[1] = 0x11; |
| result = CompileRun("u8[0] + u8[1]"); |
| CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| |
| v8::Local<v8::Value> result = CompileRun( |
| "var ab1 = new SharedArrayBuffer(2);" |
| "var u8_a = new Uint8Array(ab1);" |
| "u8_a[0] = 0xAA;" |
| "u8_a[1] = 0xFF; u8_a.buffer"); |
| Local<v8::SharedArrayBuffer> ab1 = Local<v8::SharedArrayBuffer>::Cast(result); |
| CheckInternalFieldsAreZero(ab1); |
| CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); |
| CHECK(!ab1->IsExternal()); |
| ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize()); |
| CHECK(ab1->IsExternal()); |
| |
| result = CompileRun("ab1.byteLength"); |
| CHECK_EQ(2, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun("u8_a[0]"); |
| CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun("u8_a[1]"); |
| CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab1);" |
| "u8_b[0] = 0xBB;" |
| "u8_a[0]"); |
| CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust()); |
| result = CompileRun("u8_b[1]"); |
| CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| |
| CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); |
| uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); |
| CHECK_EQ(0xBB, ab1_data[0]); |
| CHECK_EQ(0xFF, ab1_data[1]); |
| ab1_data[0] = 0xCC; |
| ab1_data[1] = 0x11; |
| result = CompileRun("u8_a[0] + u8_a[1]"); |
| CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(SharedArrayBuffer_External) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<uint8_t> my_data(100); |
| memset(my_data.start(), 0, 100); |
| Local<v8::SharedArrayBuffer> ab3 = |
| v8::SharedArrayBuffer::New(isolate, my_data.start(), 100); |
| CheckInternalFieldsAreZero(ab3); |
| CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); |
| CHECK(ab3->IsExternal()); |
| |
| CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust()); |
| |
| v8::Local<v8::Value> result = CompileRun("ab3.byteLength"); |
| CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab3);" |
| "u8_b[0] = 0xBB;" |
| "u8_b[1] = 0xCC;" |
| "u8_b.length"); |
| CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| CHECK_EQ(0xBB, my_data[0]); |
| CHECK_EQ(0xCC, my_data[1]); |
| my_data[0] = 0xCC; |
| my_data[1] = 0x11; |
| result = CompileRun("u8_b[0] + u8_b[1]"); |
| CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(HiddenProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| v8::Local<v8::Private> key = |
| v8::Private::ForApi(isolate, v8_str("api-test::hidden-key")); |
| v8::Local<v8::String> empty = v8_str(""); |
| v8::Local<v8::String> prop_name = v8_str("prop_name"); |
| |
| CcTest::CollectAllGarbage(); |
| |
| // Make sure delete of a non-existent hidden value works |
| obj->DeletePrivate(env.local(), key).FromJust(); |
| |
| CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 1503)) |
| .FromJust()); |
| CHECK_EQ(1503, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 2002)) |
| .FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| |
| CcTest::CollectAllGarbage(); |
| |
| // Make sure we do not find the hidden property. |
| CHECK(!obj->Has(env.local(), empty).FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->Get(env.local(), empty).ToLocalChecked()->IsUndefined()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK( |
| obj->Set(env.local(), empty, v8::Integer::New(isolate, 2003)).FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2003, obj->Get(env.local(), empty) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| |
| CcTest::CollectAllGarbage(); |
| |
| // Add another property and delete it afterwards to force the object in |
| // slow case. |
| CHECK(obj->Set(env.local(), prop_name, v8::Integer::New(isolate, 2008)) |
| .FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2008, obj->Get(env.local(), prop_name) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| CHECK(obj->Delete(env.local(), prop_name).FromJust()); |
| CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| |
| CcTest::CollectAllGarbage(); |
| |
| CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 2002)) |
| .FromJust()); |
| CHECK(obj->DeletePrivate(env.local(), key).FromJust()); |
| CHECK(!obj->HasPrivate(env.local(), key).FromJust()); |
| } |
| |
| |
| THREADED_TEST(Regress97784) { |
| // Regression test for crbug.com/97784 |
| // Messing with the Object.prototype should not have effect on |
| // hidden properties. |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| v8::Local<v8::Private> key = |
| v8::Private::New(env->GetIsolate(), v8_str("hidden")); |
| |
| CompileRun( |
| "set_called = false;" |
| "Object.defineProperty(" |
| " Object.prototype," |
| " 'hidden'," |
| " {get: function() { return 45; }," |
| " set: function() { set_called = true; }})"); |
| |
| CHECK(!obj->HasPrivate(env.local(), key).FromJust()); |
| // Make sure that the getter and setter from Object.prototype is not invoked. |
| // If it did we would have full access to the hidden properties in |
| // the accessor. |
| CHECK( |
| obj->SetPrivate(env.local(), key, v8::Integer::New(env->GetIsolate(), 42)) |
| .FromJust()); |
| ExpectFalse("set_called"); |
| CHECK_EQ(42, obj->GetPrivate(env.local(), key) |
| .ToLocalChecked() |
| ->Int32Value(env.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(External) { |
| v8::HandleScope scope(CcTest::isolate()); |
| int x = 3; |
| Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x); |
| LocalContext env; |
| CHECK(env->Global()->Set(env.local(), v8_str("ext"), ext).FromJust()); |
| Local<Value> reext_obj = CompileRun("this.ext"); |
| v8::Local<v8::External> reext = reext_obj.As<v8::External>(); |
| int* ptr = static_cast<int*>(reext->Value()); |
| CHECK_EQ(3, x); |
| *ptr = 10; |
| CHECK_EQ(x, 10); |
| |
| { |
| i::Handle<i::Object> obj = v8::Utils::OpenHandle(*ext); |
| CHECK_EQ(i::HeapObject::cast(*obj)->map(), CcTest::heap()->external_map()); |
| CHECK(ext->IsExternal()); |
| CHECK(!CompileRun("new Set().add(this.ext)").IsEmpty()); |
| CHECK_EQ(i::HeapObject::cast(*obj)->map(), CcTest::heap()->external_map()); |
| CHECK(ext->IsExternal()); |
| } |
| |
| // Make sure unaligned pointers are wrapped properly. |
| char* data = i::StrDup("0123456789"); |
| Local<v8::Value> zero = v8::External::New(CcTest::isolate(), &data[0]); |
| Local<v8::Value> one = v8::External::New(CcTest::isolate(), &data[1]); |
| Local<v8::Value> two = v8::External::New(CcTest::isolate(), &data[2]); |
| Local<v8::Value> three = v8::External::New(CcTest::isolate(), &data[3]); |
| |
| char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value()); |
| CHECK_EQ('0', *char_ptr); |
| char_ptr = reinterpret_cast<char*>(v8::External::Cast(*one)->Value()); |
| CHECK_EQ('1', *char_ptr); |
| char_ptr = reinterpret_cast<char*>(v8::External::Cast(*two)->Value()); |
| CHECK_EQ('2', *char_ptr); |
| char_ptr = reinterpret_cast<char*>(v8::External::Cast(*three)->Value()); |
| CHECK_EQ('3', *char_ptr); |
| i::DeleteArray(data); |
| } |
| |
| |
| THREADED_TEST(GlobalHandle) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| } |
| global.Reset(); |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| } |
| global.Reset(); |
| } |
| |
| |
| THREADED_TEST(ResettingGlobalHandle) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("longer")); |
| } |
| CHECK_EQ(global_handles->global_handles_count(), initial_handle_count); |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(6, v8::Local<String>::New(isolate, global)->Length()); |
| } |
| global.Reset(); |
| CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1); |
| } |
| |
| |
| THREADED_TEST(ResettingGlobalHandleToEmpty) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| Local<String> empty; |
| global.Reset(isolate, empty); |
| } |
| CHECK(global.IsEmpty()); |
| CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1); |
| } |
| |
| |
| template <class T> |
| static v8::Global<T> PassUnique(v8::Global<T> unique) { |
| return unique.Pass(); |
| } |
| |
| |
| template <class T> |
| static v8::Global<T> ReturnUnique(v8::Isolate* isolate, |
| const v8::Persistent<T>& global) { |
| v8::Global<String> unique(isolate, global); |
| return unique.Pass(); |
| } |
| |
| |
| THREADED_TEST(Global) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| { |
| v8::Global<String> unique(isolate, global); |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| // Test assignment via Pass |
| { |
| v8::Global<String> copy = unique.Pass(); |
| CHECK(unique.IsEmpty()); |
| CHECK(copy == global); |
| CHECK_EQ(initial_handle_count + 1, |
| global_handles->global_handles_count()); |
| unique = copy.Pass(); |
| } |
| // Test ctor via Pass |
| { |
| v8::Global<String> copy(unique.Pass()); |
| CHECK(unique.IsEmpty()); |
| CHECK(copy == global); |
| CHECK_EQ(initial_handle_count + 1, |
| global_handles->global_handles_count()); |
| unique = copy.Pass(); |
| } |
| // Test pass through function call |
| { |
| v8::Global<String> copy = PassUnique(unique.Pass()); |
| CHECK(unique.IsEmpty()); |
| CHECK(copy == global); |
| CHECK_EQ(initial_handle_count + 1, |
| global_handles->global_handles_count()); |
| unique = copy.Pass(); |
| } |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| } |
| // Test pass from function call |
| { |
| v8::Global<String> unique = ReturnUnique(isolate, global); |
| CHECK(unique == global); |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| } |
| CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); |
| global.Reset(); |
| } |
| |
| |
| namespace { |
| |
| class TwoPassCallbackData; |
| void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data); |
| void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data); |
| |
| |
| class TwoPassCallbackData { |
| public: |
| TwoPassCallbackData(v8::Isolate* isolate, int* instance_counter) |
| : first_pass_called_(false), |
| second_pass_called_(false), |
| trigger_gc_(false), |
| instance_counter_(instance_counter) { |
| HandleScope scope(isolate); |
| i::ScopedVector<char> buffer(40); |
| i::SNPrintF(buffer, "%p", static_cast<void*>(this)); |
| auto string = |
| v8::String::NewFromUtf8(isolate, buffer.start(), |
| v8::NewStringType::kNormal).ToLocalChecked(); |
| cell_.Reset(isolate, string); |
| (*instance_counter_)++; |
| } |
| |
| ~TwoPassCallbackData() { |
| CHECK(first_pass_called_); |
| CHECK(second_pass_called_); |
| CHECK(cell_.IsEmpty()); |
| (*instance_counter_)--; |
| } |
| |
| void FirstPass() { |
| CHECK(!first_pass_called_); |
| CHECK(!second_pass_called_); |
| CHECK(!cell_.IsEmpty()); |
| cell_.Reset(); |
| first_pass_called_ = true; |
| } |
| |
| void SecondPass() { |
| CHECK(first_pass_called_); |
| CHECK(!second_pass_called_); |
| CHECK(cell_.IsEmpty()); |
| second_pass_called_ = true; |
| delete this; |
| } |
| |
| void SetWeak() { |
| cell_.SetWeak(this, FirstPassCallback, v8::WeakCallbackType::kParameter); |
| } |
| |
| void MarkTriggerGc() { trigger_gc_ = true; } |
| bool trigger_gc() { return trigger_gc_; } |
| |
| int* instance_counter() { return instance_counter_; } |
| |
| private: |
| bool first_pass_called_; |
| bool second_pass_called_; |
| bool trigger_gc_; |
| v8::Global<v8::String> cell_; |
| int* instance_counter_; |
| }; |
| |
| |
| void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) { |
| ApiTestFuzzer::Fuzz(); |
| bool trigger_gc = data.GetParameter()->trigger_gc(); |
| int* instance_counter = data.GetParameter()->instance_counter(); |
| data.GetParameter()->SecondPass(); |
| if (!trigger_gc) return; |
| auto data_2 = new TwoPassCallbackData(data.GetIsolate(), instance_counter); |
| data_2->SetWeak(); |
| CcTest::CollectAllGarbage(); |
| } |
| |
| |
| void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) { |
| data.GetParameter()->FirstPass(); |
| data.SetSecondPassCallback(SecondPassCallback); |
| } |
| |
| } // namespace |
| |
| |
| TEST(TwoPassPhantomCallbacks) { |
| auto isolate = CcTest::isolate(); |
| const size_t kLength = 20; |
| int instance_counter = 0; |
| for (size_t i = 0; i < kLength; ++i) { |
| auto data = new TwoPassCallbackData(isolate, &instance_counter); |
| data->SetWeak(); |
| } |
| CHECK_EQ(static_cast<int>(kLength), instance_counter); |
| CcTest::CollectAllGarbage(); |
| EmptyMessageQueues(isolate); |
| CHECK_EQ(0, instance_counter); |
| } |
| |
| |
| TEST(TwoPassPhantomCallbacksNestedGc) { |
| auto isolate = CcTest::isolate(); |
| const size_t kLength = 20; |
| TwoPassCallbackData* array[kLength]; |
| int instance_counter = 0; |
| for (size_t i = 0; i < kLength; ++i) { |
| array[i] = new TwoPassCallbackData(isolate, &instance_counter); |
| array[i]->SetWeak(); |
| } |
| array[5]->MarkTriggerGc(); |
| array[10]->MarkTriggerGc(); |
| array[15]->MarkTriggerGc(); |
| CHECK_EQ(static_cast<int>(kLength), instance_counter); |
| CcTest::CollectAllGarbage(); |
| EmptyMessageQueues(isolate); |
| CHECK_EQ(0, instance_counter); |
| } |
| |
| |
| namespace { |
| |
| void* IntKeyToVoidPointer(int key) { return reinterpret_cast<void*>(key << 1); } |
| |
| |
| Local<v8::Object> NewObjectForIntKey( |
| v8::Isolate* isolate, const v8::Global<v8::ObjectTemplate>& templ, |
| int key) { |
| auto local = Local<v8::ObjectTemplate>::New(isolate, templ); |
| auto obj = local->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); |
| obj->SetAlignedPointerInInternalField(0, IntKeyToVoidPointer(key)); |
| return obj; |
| } |
| |
| |
| template <typename K, typename V> |
| class PhantomStdMapTraits : public v8::StdMapTraits<K, V> { |
| public: |
| typedef typename v8::GlobalValueMap<K, V, PhantomStdMapTraits<K, V>> MapType; |
| static const v8::PersistentContainerCallbackType kCallbackType = |
| v8::kWeakWithInternalFields; |
| struct WeakCallbackDataType { |
| MapType* map; |
| K key; |
| }; |
| static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key, |
| Local<V> value) { |
| WeakCallbackDataType* data = new WeakCallbackDataType; |
| data->map = map; |
| data->key = key; |
| return data; |
| } |
| static MapType* MapFromWeakCallbackInfo( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { |
| return data.GetParameter()->map; |
| } |
| static K KeyFromWeakCallbackInfo( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { |
| return data.GetParameter()->key; |
| } |
| static void DisposeCallbackData(WeakCallbackDataType* data) { delete data; } |
| static void Dispose(v8::Isolate* isolate, v8::Global<V> value, K key) { |
| CHECK_EQ(IntKeyToVoidPointer(key), |
| v8::Object::GetAlignedPointerFromInternalField(value, 0)); |
| } |
| static void OnWeakCallback( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>&) {} |
| static void DisposeWeak( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>& info) { |
| K key = KeyFromWeakCallbackInfo(info); |
| CHECK_EQ(IntKeyToVoidPointer(key), info.GetInternalField(0)); |
| DisposeCallbackData(info.GetParameter()); |
| } |
| }; |
| |
| |
| template <typename Map> |
| void TestGlobalValueMap() { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::Global<ObjectTemplate> templ; |
| { |
| HandleScope scope(isolate); |
| auto t = ObjectTemplate::New(isolate); |
| t->SetInternalFieldCount(1); |
| templ.Reset(isolate, t); |
| } |
| Map map(isolate); |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| { |
| HandleScope scope(isolate); |
| Local<v8::Object> obj = map.Get(7); |
| CHECK(obj.IsEmpty()); |
| Local<v8::Object> expected = v8::Object::New(isolate); |
| map.Set(7, expected); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| obj = map.Get(7); |
| CHECK(expected->Equals(env.local(), obj).FromJust()); |
| { |
| typename Map::PersistentValueReference ref = map.GetReference(7); |
| CHECK(expected->Equals(env.local(), ref.NewLocal(isolate)).FromJust()); |
| } |
| v8::Global<v8::Object> removed = map.Remove(7); |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| CHECK(expected == removed); |
| removed = map.Remove(7); |
| CHECK(removed.IsEmpty()); |
| map.Set(8, expected); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| map.Set(8, expected); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| { |
| typename Map::PersistentValueReference ref; |
| Local<v8::Object> expected2 = NewObjectForIntKey(isolate, templ, 8); |
| removed = map.Set(8, v8::Global<v8::Object>(isolate, expected2), &ref); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| CHECK(expected == removed); |
| CHECK(expected2->Equals(env.local(), ref.NewLocal(isolate)).FromJust()); |
| } |
| } |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| if (map.IsWeak()) { |
| CcTest::CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); |
| } else { |
| map.Clear(); |
| } |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); |
| { |
| HandleScope scope(isolate); |
| Local<v8::Object> value = NewObjectForIntKey(isolate, templ, 9); |
| map.Set(9, value); |
| map.Clear(); |
| } |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); |
| } |
| |
| } // namespace |
| |
| |
| TEST(GlobalValueMap) { |
| // Default case, w/o weak callbacks: |
| TestGlobalValueMap<v8::StdGlobalValueMap<int, v8::Object>>(); |
| |
| // Custom traits with weak callbacks: |
| typedef v8::GlobalValueMap<int, v8::Object, |
| PhantomStdMapTraits<int, v8::Object>> WeakMap; |
| TestGlobalValueMap<WeakMap>(); |
| } |
| |
| |
| TEST(PersistentValueVector) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int handle_count = global_handles->global_handles_count(); |
| HandleScope scope(isolate); |
| |
| v8::PersistentValueVector<v8::Object> vector(isolate); |
| |
| Local<v8::Object> obj1 = v8::Object::New(isolate); |
| Local<v8::Object> obj2 = v8::Object::New(isolate); |
| v8::Global<v8::Object> obj3(isolate, v8::Object::New(isolate)); |
| |
| CHECK(vector.IsEmpty()); |
| CHECK_EQ(0, static_cast<int>(vector.Size())); |
| |
| vector.ReserveCapacity(3); |
| CHECK(vector.IsEmpty()); |
| |
| vector.Append(obj1); |
| vector.Append(obj2); |
| vector.Append(obj1); |
| vector.Append(obj3.Pass()); |
| vector.Append(obj1); |
| |
| CHECK(!vector.IsEmpty()); |
| CHECK_EQ(5, static_cast<int>(vector.Size())); |
| CHECK(obj3.IsEmpty()); |
| CHECK(obj1->Equals(env.local(), vector.Get(0)).FromJust()); |
| CHECK(obj1->Equals(env.local(), vector.Get(2)).FromJust()); |
| CHECK(obj1->Equals(env.local(), vector.Get(4)).FromJust()); |
| CHECK(obj2->Equals(env.local(), vector.Get(1)).FromJust()); |
| |
| CHECK_EQ(5 + handle_count, global_handles->global_handles_count()); |
| |
| vector.Clear(); |
| CHECK(vector.IsEmpty()); |
| CHECK_EQ(0, static_cast<int>(vector.Size())); |
| CHECK_EQ(handle_count, global_handles->global_handles_count()); |
| } |
| |
| |
| THREADED_TEST(GlobalHandleUpcast) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<String> local = v8::Local<String>::New(isolate, v8_str("str")); |
| v8::Persistent<String> global_string(isolate, local); |
| v8::Persistent<Value>& global_value = |
| v8::Persistent<Value>::Cast(global_string); |
| CHECK(v8::Local<v8::Value>::New(isolate, global_value)->IsString()); |
| CHECK(global_string == v8::Persistent<String>::Cast(global_value)); |
| global_string.Reset(); |
| } |
| |
| |
| THREADED_TEST(HandleEquality) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global1; |
| v8::Persistent<String> global2; |
| { |
| v8::HandleScope scope(isolate); |
| global1.Reset(isolate, v8_str("str")); |
| global2.Reset(isolate, v8_str("str2")); |
| } |
| CHECK(global1 == global1); |
| CHECK(!(global1 != global1)); |
| { |
| v8::HandleScope scope(isolate); |
| Local<String> local1 = Local<String>::New(isolate, global1); |
| Local<String> local2 = Local<String>::New(isolate, global2); |
| |
| CHECK(global1 == local1); |
| CHECK(!(global1 != local1)); |
| CHECK(local1 == global1); |
| CHECK(!(local1 != global1)); |
| |
| CHECK(!(global1 == local2)); |
| CHECK(global1 != local2); |
| CHECK(!(local2 == global1)); |
| CHECK(local2 != global1); |
| |
| CHECK(!(local1 == local2)); |
| CHECK(local1 != local2); |
| |
| Local<String> anotherLocal1 = Local<String>::New(isolate, global1); |
| CHECK(local1 == anotherLocal1); |
| CHECK(!(local1 != anotherLocal1)); |
| } |
| global1.Reset(); |
| global2.Reset(); |
| } |
| |
| |
| THREADED_TEST(LocalHandle) { |
| v8::HandleScope scope(CcTest::isolate()); |
| v8::Local<String> local = |
| v8::Local<String>::New(CcTest::isolate(), v8_str("str")); |
| CHECK_EQ(3, local->Length()); |
| } |
| |
| |
| class WeakCallCounter { |
| public: |
| explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) {} |
| int id() { return id_; } |
| void increment() { number_of_weak_calls_++; } |
| int NumberOfWeakCalls() { return number_of_weak_calls_; } |
| |
| private: |
| int id_; |
| int number_of_weak_calls_; |
| }; |
| |
| |
| template <typename T> |
| struct WeakCallCounterAndPersistent { |
| explicit WeakCallCounterAndPersistent(WeakCallCounter* counter) |
| : counter(counter) {} |
| WeakCallCounter* counter; |
| v8::Persistent<T> handle; |
| }; |
| |
| |
| template <typename T> |
| static void WeakPointerCallback( |
| const v8::WeakCallbackInfo<WeakCallCounterAndPersistent<T>>& data) { |
| CHECK_EQ(1234, data.GetParameter()->counter->id()); |
| data.GetParameter()->counter->increment(); |
| data.GetParameter()->handle.Reset(); |
| } |
| |
| THREADED_TEST(ScriptException) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<Script> script = v8_compile("throw 'panama!';"); |
| v8::TryCatch try_catch(env->GetIsolate()); |
| v8::MaybeLocal<Value> result = script->Run(env.local()); |
| CHECK(result.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(env->GetIsolate(), try_catch.Exception()); |
| CHECK_EQ(0, strcmp(*exception_value, "panama!")); |
| } |
| |
| |
| TEST(TryCatchCustomException) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "function CustomError() { this.a = 'b'; }" |
| "(function f() { throw new CustomError(); })();"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(try_catch.Exception() |
| ->ToObject(env.local()) |
| .ToLocalChecked() |
| ->Get(env.local(), v8_str("a")) |
| .ToLocalChecked() |
| ->Equals(env.local(), v8_str("b")) |
| .FromJust()); |
| } |
| |
| |
| bool message_received; |
| |
| |
| static void check_message_0(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| CHECK_EQ(5.76, data->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| CHECK_EQ(6.75, message->GetScriptOrigin() |
| .ResourceName() |
| ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| CHECK(!message->IsSharedCrossOrigin()); |
| message_received = true; |
| } |
| |
| |
| THREADED_TEST(MessageHandler0) { |
| message_received = false; |
| v8::HandleScope scope(CcTest::isolate()); |
| CHECK(!message_received); |
| LocalContext context; |
| CcTest::isolate()->AddMessageListener(check_message_0, v8_num(5.76)); |
| v8::Local<v8::Script> script = CompileWithOrigin("throw 'error'", "6.75"); |
| CHECK(script->Run(context.local()).IsEmpty()); |
| CHECK(message_received); |
| // clear out the message listener |
| CcTest::isolate()->RemoveMessageListeners(check_message_0); |
| } |
| |
| |
| static void check_message_1(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| CHECK(data->IsNumber()); |
| CHECK_EQ(1337, |
| data->Int32Value(CcTest::isolate()->GetCurrentContext()).FromJust()); |
| CHECK(!message->IsSharedCrossOrigin()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler1) { |
| message_received = false; |
| v8::HandleScope scope(CcTest::isolate()); |
| CHECK(!message_received); |
| CcTest::isolate()->AddMessageListener(check_message_1); |
| LocalContext context; |
| CompileRun("throw 1337;"); |
| CHECK(message_received); |
| // clear out the message listener |
| CcTest::isolate()->RemoveMessageListeners(check_message_1); |
| } |
| |
| |
| static void check_message_2(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| LocalContext context; |
| CHECK(data->IsObject()); |
| v8::Local<v8::Value> hidden_property = |
| v8::Object::Cast(*data) |
| ->GetPrivate( |
| context.local(), |
| v8::Private::ForApi(CcTest::isolate(), v8_str("hidden key"))) |
| .ToLocalChecked(); |
| CHECK(v8_str("hidden value") |
| ->Equals(context.local(), hidden_property) |
| .FromJust()); |
| CHECK(!message->IsSharedCrossOrigin()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler2) { |
| message_received = false; |
| v8::HandleScope scope(CcTest::isolate()); |
| CHECK(!message_received); |
| CcTest::isolate()->AddMessageListener(check_message_2); |
| LocalContext context; |
| v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error")); |
| v8::Object::Cast(*error) |
| ->SetPrivate(context.local(), |
| v8::Private::ForApi(CcTest::isolate(), v8_str("hidden key")), |
| v8_str("hidden value")) |
| .FromJust(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("error"), error) |
| .FromJust()); |
| CompileRun("throw error;"); |
| CHECK(message_received); |
| // clear out the message listener |
| CcTest::isolate()->RemoveMessageListeners(check_message_2); |
| } |
| |
| |
| static void check_message_3(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| CHECK(message->IsSharedCrossOrigin()); |
| CHECK(message->GetScriptOrigin().Options().IsSharedCrossOrigin()); |
| CHECK(message->GetScriptOrigin().Options().IsOpaque()); |
| CHECK_EQ(6.75, message->GetScriptOrigin() |
| .ResourceName() |
| ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| CHECK_EQ(7.40, message->GetScriptOrigin() |
| .SourceMapUrl() |
| ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler3) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(!message_received); |
| isolate->AddMessageListener(check_message_3); |
| LocalContext context; |
| v8::ScriptOrigin origin = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::True(isolate), |
| Local<v8::Integer>(), v8_str("7.40"), v8::True(isolate)); |
| v8::Local<v8::Script> script = |
| Script::Compile(context.local(), v8_str("throw 'error'"), &origin) |
| .ToLocalChecked(); |
| CHECK(script->Run(context.local()).IsEmpty()); |
| CHECK(message_received); |
| // clear out the message listener |
| isolate->RemoveMessageListeners(check_message_3); |
| } |
| |
| |
| static void check_message_4(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| CHECK(!message->IsSharedCrossOrigin()); |
| CHECK_EQ(6.75, message->GetScriptOrigin() |
| .ResourceName() |
| ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler4) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(!message_received); |
| isolate->AddMessageListener(check_message_4); |
| LocalContext context; |
| v8::ScriptOrigin origin = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::False(isolate)); |
| v8::Local<v8::Script> script = |
| Script::Compile(context.local(), v8_str("throw 'error'"), &origin) |
| .ToLocalChecked(); |
| CHECK(script->Run(context.local()).IsEmpty()); |
| CHECK(message_received); |
| // clear out the message listener |
| isolate->RemoveMessageListeners(check_message_4); |
| } |
| |
| |
| static void check_message_5a(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| CHECK(message->IsSharedCrossOrigin()); |
| CHECK_EQ(6.75, message->GetScriptOrigin() |
| .ResourceName() |
| ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| message_received = true; |
| } |
| |
| |
| static void check_message_5b(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| CHECK(!message->IsSharedCrossOrigin()); |
| CHECK_EQ(6.75, message->GetScriptOrigin() |
| .ResourceName() |
| ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler5) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(!message_received); |
| isolate->AddMessageListener(check_message_5a); |
| LocalContext context; |
| v8::ScriptOrigin origin1 = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::True(isolate)); |
| v8::Local<v8::Script> script = |
| Script::Compile(context.local(), v8_str("throw 'error'"), &origin1) |
| .ToLocalChecked(); |
| CHECK(script->Run(context.local()).IsEmpty()); |
| CHECK(message_received); |
| // clear out the message listener |
| isolate->RemoveMessageListeners(check_message_5a); |
| |
| message_received = false; |
| isolate->AddMessageListener(check_message_5b); |
| v8::ScriptOrigin origin2 = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::False(isolate)); |
| script = Script::Compile(context.local(), v8_str("throw 'error'"), &origin2) |
| .ToLocalChecked(); |
| CHECK(script->Run(context.local()).IsEmpty()); |
| CHECK(message_received); |
| // clear out the message listener |
| isolate->RemoveMessageListeners(check_message_5b); |
| } |
| |
| |
| THREADED_TEST(GetSetProperty) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("foo"), v8_num(14)) |
| .FromJust()); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("12"), v8_num(92)) |
| .FromJust()); |
| CHECK(context->Global() |
| ->Set(context.local(), v8::Integer::New(isolate, 16), v8_num(32)) |
| .FromJust()); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_num(13), v8_num(56)) |
| .FromJust()); |
| Local<Value> foo = CompileRun("this.foo"); |
| CHECK_EQ(14, foo->Int32Value(context.local()).FromJust()); |
| Local<Value> twelve = CompileRun("this[12]"); |
| CHECK_EQ(92, twelve->Int32Value(context.local()).FromJust()); |
| Local<Value> sixteen = CompileRun("this[16]"); |
| CHECK_EQ(32, sixteen->Int32Value(context.local()).FromJust()); |
| Local<Value> thirteen = CompileRun("this[13]"); |
| CHECK_EQ(56, thirteen->Int32Value(context.local()).FromJust()); |
| CHECK_EQ(92, context->Global() |
| ->Get(context.local(), v8::Integer::New(isolate, 12)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(92, context->Global() |
| ->Get(context.local(), v8_str("12")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(92, context->Global() |
| ->Get(context.local(), v8_num(12)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(32, context->Global() |
| ->Get(context.local(), v8::Integer::New(isolate, 16)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(32, context->Global() |
| ->Get(context.local(), v8_str("16")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(32, context->Global() |
| ->Get(context.local(), v8_num(16)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(56, context->Global() |
| ->Get(context.local(), v8::Integer::New(isolate, 13)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(56, context->Global() |
| ->Get(context.local(), v8_str("13")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(56, context->Global() |
| ->Get(context.local(), v8_num(13)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(PropertyAttributes) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| // none |
| Local<String> prop = v8_str("none"); |
| CHECK(context->Global()->Set(context.local(), prop, v8_num(7)).FromJust()); |
| CHECK_EQ(v8::None, context->Global() |
| ->GetPropertyAttributes(context.local(), prop) |
| .FromJust()); |
| // read-only |
| prop = v8_str("read_only"); |
| context->Global() |
| ->DefineOwnProperty(context.local(), prop, v8_num(7), v8::ReadOnly) |
| .FromJust(); |
| CHECK_EQ(7, context->Global() |
| ->Get(context.local(), prop) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(v8::ReadOnly, context->Global() |
| ->GetPropertyAttributes(context.local(), prop) |
| .FromJust()); |
| CompileRun("read_only = 9"); |
| CHECK_EQ(7, context->Global() |
| ->Get(context.local(), prop) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(context->Global()->Set(context.local(), prop, v8_num(10)).FromJust()); |
| CHECK_EQ(7, context->Global() |
| ->Get(context.local(), prop) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| // dont-delete |
| prop = v8_str("dont_delete"); |
| context->Global() |
| ->DefineOwnProperty(context.local(), prop, v8_num(13), v8::DontDelete) |
| .FromJust(); |
| CHECK_EQ(13, context->Global() |
| ->Get(context.local(), prop) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CompileRun("delete dont_delete"); |
| CHECK_EQ(13, context->Global() |
| ->Get(context.local(), prop) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(v8::DontDelete, context->Global() |
| ->GetPropertyAttributes(context.local(), prop) |
| .FromJust()); |
| // dont-enum |
| prop = v8_str("dont_enum"); |
| context->Global() |
| ->DefineOwnProperty(context.local(), prop, v8_num(28), v8::DontEnum) |
| .FromJust(); |
| CHECK_EQ(v8::DontEnum, context->Global() |
| ->GetPropertyAttributes(context.local(), prop) |
| .FromJust()); |
| // absent |
| prop = v8_str("absent"); |
| CHECK_EQ(v8::None, context->Global() |
| ->GetPropertyAttributes(context.local(), prop) |
| .FromJust()); |
| Local<Value> fake_prop = v8_num(1); |
| CHECK_EQ(v8::None, context->Global() |
| ->GetPropertyAttributes(context.local(), fake_prop) |
| .FromJust()); |
| // exception |
| TryCatch try_catch(context->GetIsolate()); |
| Local<Value> exception = |
| CompileRun("({ toString: function() { throw 'exception';} })"); |
| CHECK(context->Global() |
| ->GetPropertyAttributes(context.local(), exception) |
| .IsNothing()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(context->GetIsolate(), |
| try_catch.Exception()); |
| CHECK_EQ(0, strcmp("exception", *exception_value)); |
| try_catch.Reset(); |
| } |
| |
| |
| THREADED_TEST(Array) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| Local<v8::Array> array = v8::Array::New(context->GetIsolate()); |
| CHECK_EQ(0u, array->Length()); |
| CHECK(array->Get(context.local(), 0).ToLocalChecked()->IsUndefined()); |
| CHECK(!array->Has(context.local(), 0).FromJust()); |
| CHECK(array->Get(context.local(), 100).ToLocalChecked()->IsUndefined()); |
| CHECK(!array->Has(context.local(), 100).FromJust()); |
| CHECK(array->Set(context.local(), 2, v8_num(7)).FromJust()); |
| CHECK_EQ(3u, array->Length()); |
| CHECK(!array->Has(context.local(), 0).FromJust()); |
| CHECK(!array->Has(context.local(), 1).FromJust()); |
| CHECK(array->Has(context.local(), 2).FromJust()); |
| CHECK_EQ(7, array->Get(context.local(), 2) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| Local<Value> obj = CompileRun("[1, 2, 3]"); |
| Local<v8::Array> arr = obj.As<v8::Array>(); |
| CHECK_EQ(3u, arr->Length()); |
| CHECK_EQ(1, arr->Get(context.local(), 0) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(2, arr->Get(context.local(), 1) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(3, arr->Get(context.local(), 2) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| array = v8::Array::New(context->GetIsolate(), 27); |
| CHECK_EQ(27u, array->Length()); |
| array = v8::Array::New(context->GetIsolate(), -27); |
| CHECK_EQ(0u, array->Length()); |
| } |
| |
| |
| void HandleF(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::EscapableHandleScope scope(args.GetIsolate()); |
| ApiTestFuzzer::Fuzz(); |
| Local<v8::Array> result = v8::Array::New(args.GetIsolate(), args.Length()); |
| for (int i = 0; i < args.Length(); i++) { |
| CHECK(result->Set(CcTest::isolate()->GetCurrentContext(), i, args[i]) |
| .FromJust()); |
| } |
| args.GetReturnValue().Set(scope.Escape(result)); |
| } |
| |
| |
| THREADED_TEST(Vector) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> global = ObjectTemplate::New(isolate); |
| global->Set(v8_str("f"), v8::FunctionTemplate::New(isolate, HandleF)); |
| LocalContext context(0, global); |
| |
| const char* fun = "f()"; |
| Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); |
| CHECK_EQ(0u, a0->Length()); |
| |
| const char* fun2 = "f(11)"; |
| Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); |
| CHECK_EQ(1u, a1->Length()); |
| CHECK_EQ(11, a1->Get(context.local(), 0) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| |
| const char* fun3 = "f(12, 13)"; |
| Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); |
| CHECK_EQ(2u, a2->Length()); |
| CHECK_EQ(12, a2->Get(context.local(), 0) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(13, a2->Get(context.local(), 1) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| |
| const char* fun4 = "f(14, 15, 16)"; |
| Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); |
| CHECK_EQ(3u, a3->Length()); |
| CHECK_EQ(14, a3->Get(context.local(), 0) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(15, a3->Get(context.local(), 1) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(16, a3->Get(context.local(), 2) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| |
| const char* fun5 = "f(17, 18, 19, 20)"; |
| Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); |
| CHECK_EQ(4u, a4->Length()); |
| CHECK_EQ(17, a4->Get(context.local(), 0) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(18, a4->Get(context.local(), 1) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(19, a4->Get(context.local(), 2) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(20, a4->Get(context.local(), 3) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(FunctionCall) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "function Foo() {" |
| " var result = [];" |
| " for (var i = 0; i < arguments.length; i++) {" |
| " result.push(arguments[i]);" |
| " }" |
| " return result;" |
| "}" |
| "function ReturnThisSloppy() {" |
| " return this;" |
| "}" |
| "function ReturnThisStrict() {" |
| " 'use strict';" |
| " return this;" |
| "}"); |
| Local<Function> Foo = Local<Function>::Cast( |
| context->Global()->Get(context.local(), v8_str("Foo")).ToLocalChecked()); |
| Local<Function> ReturnThisSloppy = Local<Function>::Cast( |
| context->Global() |
| ->Get(context.local(), v8_str("ReturnThisSloppy")) |
| .ToLocalChecked()); |
| Local<Function> ReturnThisStrict = Local<Function>::Cast( |
| context->Global() |
| ->Get(context.local(), v8_str("ReturnThisStrict")) |
| .ToLocalChecked()); |
| |
| v8::Local<Value>* args0 = nullptr; |
| Local<v8::Array> a0 = Local<v8::Array>::Cast( |
| Foo->Call(context.local(), Foo, 0, args0).ToLocalChecked()); |
| CHECK_EQ(0u, a0->Length()); |
| |
| v8::Local<Value> args1[] = {v8_num(1.1)}; |
| Local<v8::Array> a1 = Local<v8::Array>::Cast( |
| Foo->Call(context.local(), Foo, 1, args1).ToLocalChecked()); |
| CHECK_EQ(1u, a1->Length()); |
| CHECK_EQ(1.1, a1->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| v8::Local<Value> args2[] = {v8_num(2.2), v8_num(3.3)}; |
| Local<v8::Array> a2 = Local<v8::Array>::Cast( |
| Foo->Call(context.local(), Foo, 2, args2).ToLocalChecked()); |
| CHECK_EQ(2u, a2->Length()); |
| CHECK_EQ(2.2, a2->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(3.3, a2->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| v8::Local<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)}; |
| Local<v8::Array> a3 = Local<v8::Array>::Cast( |
| Foo->Call(context.local(), Foo, 3, args3).ToLocalChecked()); |
| CHECK_EQ(3u, a3->Length()); |
| CHECK_EQ(4.4, a3->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(5.5, a3->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(6.6, a3->Get(context.local(), v8::Integer::New(isolate, 2)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| v8::Local<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9), |
| v8_num(10.11)}; |
| Local<v8::Array> a4 = Local<v8::Array>::Cast( |
| Foo->Call(context.local(), Foo, 4, args4).ToLocalChecked()); |
| CHECK_EQ(4u, a4->Length()); |
| CHECK_EQ(7.7, a4->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(8.8, a4->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(9.9, a4->Get(context.local(), v8::Integer::New(isolate, 2)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(10.11, a4->Get(context.local(), v8::Integer::New(isolate, 3)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| Local<v8::Value> r1 = |
| ReturnThisSloppy |
| ->Call(context.local(), v8::Undefined(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r1->StrictEquals(context->Global())); |
| Local<v8::Value> r2 = |
| ReturnThisSloppy->Call(context.local(), v8::Null(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r2->StrictEquals(context->Global())); |
| Local<v8::Value> r3 = |
| ReturnThisSloppy->Call(context.local(), v8_num(42), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r3->IsNumberObject()); |
| CHECK_EQ(42.0, r3.As<v8::NumberObject>()->ValueOf()); |
| Local<v8::Value> r4 = |
| ReturnThisSloppy->Call(context.local(), v8_str("hello"), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r4->IsStringObject()); |
| CHECK(r4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> r5 = |
| ReturnThisSloppy->Call(context.local(), v8::True(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r5->IsBooleanObject()); |
| CHECK(r5.As<v8::BooleanObject>()->ValueOf()); |
| |
| Local<v8::Value> r6 = |
| ReturnThisStrict |
| ->Call(context.local(), v8::Undefined(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r6->IsUndefined()); |
| Local<v8::Value> r7 = |
| ReturnThisStrict->Call(context.local(), v8::Null(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r7->IsNull()); |
| Local<v8::Value> r8 = |
| ReturnThisStrict->Call(context.local(), v8_num(42), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r8->StrictEquals(v8_num(42))); |
| Local<v8::Value> r9 = |
| ReturnThisStrict->Call(context.local(), v8_str("hello"), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r9->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> r10 = |
| ReturnThisStrict->Call(context.local(), v8::True(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(r10->StrictEquals(v8::True(isolate))); |
| } |
| |
| |
| THREADED_TEST(ConstructCall) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "function Foo() {" |
| " var result = [];" |
| " for (var i = 0; i < arguments.length; i++) {" |
| " result.push(arguments[i]);" |
| " }" |
| " return result;" |
| "}"); |
| Local<Function> Foo = Local<Function>::Cast( |
| context->Global()->Get(context.local(), v8_str("Foo")).ToLocalChecked()); |
| |
| v8::Local<Value>* args0 = nullptr; |
| Local<v8::Array> a0 = Local<v8::Array>::Cast( |
| Foo->NewInstance(context.local(), 0, args0).ToLocalChecked()); |
| CHECK_EQ(0u, a0->Length()); |
| |
| v8::Local<Value> args1[] = {v8_num(1.1)}; |
| Local<v8::Array> a1 = Local<v8::Array>::Cast( |
| Foo->NewInstance(context.local(), 1, args1).ToLocalChecked()); |
| CHECK_EQ(1u, a1->Length()); |
| CHECK_EQ(1.1, a1->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| v8::Local<Value> args2[] = {v8_num(2.2), v8_num(3.3)}; |
| Local<v8::Array> a2 = Local<v8::Array>::Cast( |
| Foo->NewInstance(context.local(), 2, args2).ToLocalChecked()); |
| CHECK_EQ(2u, a2->Length()); |
| CHECK_EQ(2.2, a2->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(3.3, a2->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| v8::Local<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)}; |
| Local<v8::Array> a3 = Local<v8::Array>::Cast( |
| Foo->NewInstance(context.local(), 3, args3).ToLocalChecked()); |
| CHECK_EQ(3u, a3->Length()); |
| CHECK_EQ(4.4, a3->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(5.5, a3->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(6.6, a3->Get(context.local(), v8::Integer::New(isolate, 2)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| |
| v8::Local<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9), |
| v8_num(10.11)}; |
| Local<v8::Array> a4 = Local<v8::Array>::Cast( |
| Foo->NewInstance(context.local(), 4, args4).ToLocalChecked()); |
| CHECK_EQ(4u, a4->Length()); |
| CHECK_EQ(7.7, a4->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(8.8, a4->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(9.9, a4->Get(context.local(), v8::Integer::New(isolate, 2)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| CHECK_EQ(10.11, a4->Get(context.local(), v8::Integer::New(isolate, 3)) |
| .ToLocalChecked() |
| ->NumberValue(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(ConversionNumber) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| // Very large number. |
| CompileRun("var obj = Math.pow(2,32) * 1237;"); |
| Local<Value> obj = |
| env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(5312874545152.0, |
| obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(0, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(0, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| // Large number. |
| CompileRun("var obj = -1234567890123;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(-1234567890123.0, |
| obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(-1912276171, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(2382691125, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| // Small positive integer. |
| CompileRun("var obj = 42;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(42.0, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(42, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(42, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| // Negative integer. |
| CompileRun("var obj = -37;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(-37.0, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(-37, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(4294967259, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| // Positive non-int32 integer. |
| CompileRun("var obj = 0x81234567;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(2166572391.0, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(-2128394905, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(2166572391, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| // Fraction. |
| CompileRun("var obj = 42.3;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(42.3, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(42, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(42, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| // Large negative fraction. |
| CompileRun("var obj = -5726623061.75;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK_EQ(-5726623061.75, |
| obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(-1431655765, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| CHECK_EQ(2863311531, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| } |
| |
| |
| THREADED_TEST(isNumberType) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| // Very large number. |
| CompileRun("var obj = Math.pow(2,32) * 1237;"); |
| Local<Value> obj = |
| env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Large negative number. |
| CompileRun("var obj = -1234567890123;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Small positive integer. |
| CompileRun("var obj = 42;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(obj->IsInt32()); |
| CHECK(obj->IsUint32()); |
| // Negative integer. |
| CompileRun("var obj = -37;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Positive non-int32 integer. |
| CompileRun("var obj = 0x81234567;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(!obj->IsInt32()); |
| CHECK(obj->IsUint32()); |
| // Fraction. |
| CompileRun("var obj = 42.3;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Large negative fraction. |
| CompileRun("var obj = -5726623061.75;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Positive zero |
| CompileRun("var obj = 0.0;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(obj->IsInt32()); |
| CHECK(obj->IsUint32()); |
| // Positive zero |
| CompileRun("var obj = -0.0;"); |
| obj = env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| } |
| |
| static void CheckUncle(v8::Isolate* isolate, v8::TryCatch* try_catch) { |
| CHECK(try_catch->HasCaught()); |
| String::Utf8Value str_value(isolate, try_catch->Exception()); |
| CHECK_EQ(0, strcmp(*str_value, "uncle?")); |
| try_catch->Reset(); |
| } |
| |
| THREADED_TEST(ConversionException) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "function TestClass() { };" |
| "TestClass.prototype.toString = function () { throw 'uncle?'; };" |
| "var obj = new TestClass();"); |
| Local<Value> obj = |
| env->Global()->Get(env.local(), v8_str("obj")).ToLocalChecked(); |
| |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(obj->ToString(env.local()).IsEmpty()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->ToNumber(env.local()).IsEmpty()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->ToInteger(env.local()).IsEmpty()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->ToUint32(env.local()).IsEmpty()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->ToInt32(env.local()).IsEmpty()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(v8::Undefined(isolate)->ToObject(env.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| CHECK(obj->Int32Value(env.local()).IsNothing()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->Uint32Value(env.local()).IsNothing()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->NumberValue(env.local()).IsNothing()); |
| CheckUncle(isolate, &try_catch); |
| |
| CHECK(obj->IntegerValue(env.local()).IsNothing()); |
| CheckUncle(isolate, &try_catch); |
| } |
| |
| |
| void ThrowFromC(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetIsolate()->ThrowException(v8_str("konto")); |
| } |
| |
| |
| void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| if (args.Length() < 1) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| v8::HandleScope scope(args.GetIsolate()); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| Local<Value> result = |
| CompileRun(args[0] |
| ->ToString(args.GetIsolate()->GetCurrentContext()) |
| .ToLocalChecked()); |
| CHECK(!try_catch.HasCaught() || result.IsEmpty()); |
| args.GetReturnValue().Set(try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(APICatch) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| CompileRun( |
| "var thrown = false;" |
| "try {" |
| " ThrowFromC();" |
| "} catch (e) {" |
| " thrown = true;" |
| "}"); |
| Local<Value> thrown = context->Global() |
| ->Get(context.local(), v8_str("thrown")) |
| .ToLocalChecked(); |
| CHECK(thrown->BooleanValue(context.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(APIThrowTryCatch) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| v8::TryCatch try_catch(isolate); |
| CompileRun("ThrowFromC();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| // Test that a try-finally block doesn't shadow a try-catch block |
| // when setting up an external handler. |
| // |
| // BUG(271): Some of the exception propagation does not work on the |
| // ARM simulator because the simulator separates the C++ stack and the |
| // JS stack. This test therefore fails on the simulator. The test is |
| // not threaded to allow the threading tests to run on the simulator. |
| TEST(TryCatchInTryFinally) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("CCatcher"), v8::FunctionTemplate::New(isolate, CCatcher)); |
| LocalContext context(0, templ); |
| Local<Value> result = CompileRun( |
| "try {" |
| " try {" |
| " CCatcher('throw 7;');" |
| " } finally {" |
| " }" |
| "} catch (e) {" |
| "}"); |
| CHECK(result->IsTrue()); |
| } |
| |
| |
| static void check_custom_error_tostring(v8::Local<v8::Message> message, |
| v8::Local<v8::Value> data) { |
| const char* uncaught_error = "Uncaught MyError toString"; |
| CHECK(message->Get() |
| ->Equals(CcTest::isolate()->GetCurrentContext(), |
| v8_str(uncaught_error)) |
| .FromJust()); |
| } |
| |
| |
| TEST(CustomErrorToString) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| context->GetIsolate()->AddMessageListener(check_custom_error_tostring); |
| CompileRun( |
| "function MyError(name, message) { " |
| " this.name = name; " |
| " this.message = message; " |
| "} " |
| "MyError.prototype = Object.create(Error.prototype); " |
| "MyError.prototype.toString = function() { " |
| " return 'MyError toString'; " |
| "}; " |
| "throw new MyError('my name', 'my message'); "); |
| context->GetIsolate()->RemoveMessageListeners(check_custom_error_tostring); |
| } |
| |
| |
| static void check_custom_error_message(v8::Local<v8::Message> message, |
| v8::Local<v8::Value> data) { |
| const char* uncaught_error = "Uncaught MyError: my message"; |
| printf("%s\n", *v8::String::Utf8Value(CcTest::isolate(), message->Get())); |
| CHECK(message->Get() |
| ->Equals(CcTest::isolate()->GetCurrentContext(), |
| v8_str(uncaught_error)) |
| .FromJust()); |
| } |
| |
| |
| TEST(CustomErrorMessage) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| context->GetIsolate()->AddMessageListener(check_custom_error_message); |
| |
| // Handlebars. |
| CompileRun( |
| "function MyError(msg) { " |
| " this.name = 'MyError'; " |
| " this.message = msg; " |
| "} " |
| "MyError.prototype = new Error(); " |
| "throw new MyError('my message'); "); |
| |
| // Closure. |
| CompileRun( |
| "function MyError(msg) { " |
| " this.name = 'MyError'; " |
| " this.message = msg; " |
| "} " |
| "inherits = function(childCtor, parentCtor) { " |
| " function tempCtor() {}; " |
| " tempCtor.prototype = parentCtor.prototype; " |
| " childCtor.superClass_ = parentCtor.prototype; " |
| " childCtor.prototype = new tempCtor(); " |
| " childCtor.prototype.constructor = childCtor; " |
| "}; " |
| "inherits(MyError, Error); " |
| "throw new MyError('my message'); "); |
| |
| // Object.create. |
| CompileRun( |
| "function MyError(msg) { " |
| " this.name = 'MyError'; " |
| " this.message = msg; " |
| "} " |
| "MyError.prototype = Object.create(Error.prototype); " |
| "throw new MyError('my message'); "); |
| |
| context->GetIsolate()->RemoveMessageListeners(check_custom_error_message); |
| } |
| |
| |
| static void check_custom_rethrowing_message(v8::Local<v8::Message> message, |
| v8::Local<v8::Value> data) { |
| CHECK(data->IsExternal()); |
| int* callcount = static_cast<int*>(data.As<v8::External>()->Value()); |
| ++*callcount; |
| |
| const char* uncaught_error = "Uncaught exception"; |
| CHECK(message->Get() |
| ->Equals(CcTest::isolate()->GetCurrentContext(), |
| v8_str(uncaught_error)) |
| .FromJust()); |
| // Test that compiling code inside a message handler works. |
| CHECK(CompileRunChecked(CcTest::isolate(), "(function(a) { return a; })(42)") |
| ->Equals(CcTest::isolate()->GetCurrentContext(), |
| v8::Integer::NewFromUnsigned(CcTest::isolate(), 42)) |
| .FromJust()); |
| } |
| |
| |
| TEST(CustomErrorRethrowsOnToString) { |
| int callcount = 0; |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| context->GetIsolate()->AddMessageListener( |
| check_custom_rethrowing_message, v8::External::New(isolate, &callcount)); |
| |
| CompileRun( |
| "var e = { toString: function() { throw e; } };" |
| "try { throw e; } finally {}"); |
| |
| CHECK_EQ(callcount, 1); |
| context->GetIsolate()->RemoveMessageListeners( |
| check_custom_rethrowing_message); |
| } |
| |
| TEST(CustomErrorRethrowsOnToStringInsideVerboseTryCatch) { |
| int callcount = 0; |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| context->GetIsolate()->AddMessageListener( |
| check_custom_rethrowing_message, v8::External::New(isolate, &callcount)); |
| |
| CompileRun( |
| "var e = { toString: function() { throw e; } };" |
| "try { throw e; } finally {}"); |
| |
| CHECK_EQ(callcount, 1); |
| context->GetIsolate()->RemoveMessageListeners( |
| check_custom_rethrowing_message); |
| } |
| |
| |
| static void receive_message(v8::Local<v8::Message> message, |
| v8::Local<v8::Value> data) { |
| message->Get(); |
| message_received = true; |
| } |
| |
| |
| TEST(APIThrowMessage) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| isolate->AddMessageListener(receive_message); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| CompileRun("ThrowFromC();"); |
| CHECK(message_received); |
| isolate->RemoveMessageListeners(receive_message); |
| } |
| |
| |
| TEST(APIThrowMessageAndVerboseTryCatch) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| isolate->AddMessageListener(receive_message); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| Local<Value> result = CompileRun("ThrowFromC();"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(result.IsEmpty()); |
| CHECK(message_received); |
| isolate->RemoveMessageListeners(receive_message); |
| } |
| |
| |
| TEST(APIStackOverflowAndVerboseTryCatch) { |
| message_received = false; |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| context->GetIsolate()->AddMessageListener(receive_message); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| try_catch.SetVerbose(true); |
| Local<Value> result = CompileRun("function foo() { foo(); } foo();"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(result.IsEmpty()); |
| CHECK(message_received); |
| context->GetIsolate()->RemoveMessageListeners(receive_message); |
| } |
| |
| |
| THREADED_TEST(ExternalScriptException) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| |
| v8::TryCatch try_catch(isolate); |
| Local<Value> result = CompileRun("ThrowFromC(); throw 'panama';"); |
| CHECK(result.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp("konto", *exception_value)); |
| } |
| |
| |
| void CThrowCountDown(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(4, args.Length()); |
| v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| int count = args[0]->Int32Value(context).FromJust(); |
| int cInterval = args[2]->Int32Value(context).FromJust(); |
| if (count == 0) { |
| args.GetIsolate()->ThrowException(v8_str("FromC")); |
| return; |
| } else { |
| Local<v8::Object> global = context->Global(); |
| Local<Value> fun = |
| global->Get(context, v8_str("JSThrowCountDown")).ToLocalChecked(); |
| v8::Local<Value> argv[] = {v8_num(count - 1), args[1], args[2], args[3]}; |
| if (count % cInterval == 0) { |
| v8::TryCatch try_catch(args.GetIsolate()); |
| Local<Value> result = fun.As<Function>() |
| ->Call(context, global, 4, argv) |
| .FromMaybe(Local<Value>()); |
| int expected = args[3]->Int32Value(context).FromJust(); |
| if (try_catch.HasCaught()) { |
| CHECK_EQ(expected, count); |
| CHECK(result.IsEmpty()); |
| CHECK(!CcTest::i_isolate()->has_scheduled_exception()); |
| } else { |
| CHECK_NE(expected, count); |
| } |
| args.GetReturnValue().Set(result); |
| return; |
| } else { |
| args.GetReturnValue().Set(fun.As<Function>() |
| ->Call(context, global, 4, argv) |
| .FromMaybe(v8::Local<v8::Value>())); |
| return; |
| } |
| } |
| } |
| |
| |
| void JSCheck(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(3, args.Length()); |
| v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| bool equality = args[0]->BooleanValue(context).FromJust(); |
| int count = args[1]->Int32Value(context).FromJust(); |
| int expected = args[2]->Int32Value(context).FromJust(); |
| if (equality) { |
| CHECK_EQ(count, expected); |
| } else { |
| CHECK_NE(count, expected); |
| } |
| } |
| |
| |
| THREADED_TEST(EvalInTryFinally) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CompileRun( |
| "(function() {" |
| " try {" |
| " eval('asldkf (*&^&*^');" |
| " } finally {" |
| " return;" |
| " }" |
| "})()"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| // This test works by making a stack of alternating JavaScript and C |
| // activations. These activations set up exception handlers with regular |
| // intervals, one interval for C activations and another for JavaScript |
| // activations. When enough activations have been created an exception is |
| // thrown and we check that the right activation catches the exception and that |
| // no other activations do. The right activation is always the topmost one with |
| // a handler, regardless of whether it is in JavaScript or C. |
| // |
| // The notation used to describe a test case looks like this: |
| // |
| // *JS[4] *C[3] @JS[2] C[1] JS[0] |
| // |
| // Each entry is an activation, either JS or C. The index is the count at that |
| // level. Stars identify activations with exception handlers, the @ identifies |
| // the exception handler that should catch the exception. |
| // |
| // BUG(271): Some of the exception propagation does not work on the |
| // ARM simulator because the simulator separates the C++ stack and the |
| // JS stack. This test therefore fails on the simulator. The test is |
| // not threaded to allow the threading tests to run on the simulator. |
| TEST(ExceptionOrder) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("check"), v8::FunctionTemplate::New(isolate, JSCheck)); |
| templ->Set(v8_str("CThrowCountDown"), |
| v8::FunctionTemplate::New(isolate, CThrowCountDown)); |
| LocalContext context(0, templ); |
| CompileRun( |
| "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" |
| " if (count == 0) throw 'FromJS';" |
| " if (count % jsInterval == 0) {" |
| " try {" |
| " var value = CThrowCountDown(count - 1," |
| " jsInterval," |
| " cInterval," |
| " expected);" |
| " check(false, count, expected);" |
| " return value;" |
| " } catch (e) {" |
| " check(true, count, expected);" |
| " }" |
| " } else {" |
| " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" |
| " }" |
| "}"); |
| Local<Function> fun = Local<Function>::Cast( |
| context->Global() |
| ->Get(context.local(), v8_str("JSThrowCountDown")) |
| .ToLocalChecked()); |
| |
| const int argc = 4; |
| // count jsInterval cInterval expected |
| |
| // *JS[4] *C[3] @JS[2] C[1] JS[0] |
| v8::Local<Value> a0[argc] = {v8_num(4), v8_num(2), v8_num(3), v8_num(2)}; |
| fun->Call(context.local(), fun, argc, a0).ToLocalChecked(); |
| |
| // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] |
| v8::Local<Value> a1[argc] = {v8_num(5), v8_num(6), v8_num(1), v8_num(2)}; |
| fun->Call(context.local(), fun, argc, a1).ToLocalChecked(); |
| |
| // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] |
| v8::Local<Value> a2[argc] = {v8_num(6), v8_num(7), v8_num(5), v8_num(5)}; |
| fun->Call(context.local(), fun, argc, a2).ToLocalChecked(); |
| |
| // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] |
| v8::Local<Value> a3[argc] = {v8_num(6), v8_num(6), v8_num(7), v8_num(6)}; |
| fun->Call(context.local(), fun, argc, a3).ToLocalChecked(); |
| |
| // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] |
| v8::Local<Value> a4[argc] = {v8_num(6), v8_num(4), v8_num(5), v8_num(4)}; |
| fun->Call(context.local(), fun, argc, a4).ToLocalChecked(); |
| |
| // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] |
| v8::Local<Value> a5[argc] = {v8_num(6), v8_num(4), v8_num(3), v8_num(3)}; |
| fun->Call(context.local(), fun, argc, a5).ToLocalChecked(); |
| } |
| |
| |
| void ThrowValue(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(1, args.Length()); |
| args.GetIsolate()->ThrowException(args[0]); |
| } |
| |
| |
| THREADED_TEST(ThrowValues) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(isolate, ThrowValue)); |
| LocalContext context(0, templ); |
| v8::Local<v8::Array> result = v8::Local<v8::Array>::Cast( |
| CompileRun("function Run(obj) {" |
| " try {" |
| " Throw(obj);" |
| " } catch (e) {" |
| " return e;" |
| " }" |
| " return 'no exception';" |
| "}" |
| "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); |
| CHECK_EQ(5u, result->Length()); |
| CHECK(result->Get(context.local(), v8::Integer::New(isolate, 0)) |
| .ToLocalChecked() |
| ->IsString()); |
| CHECK(result->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->IsNumber()); |
| CHECK_EQ(1, result->Get(context.local(), v8::Integer::New(isolate, 1)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(result->Get(context.local(), v8::Integer::New(isolate, 2)) |
| .ToLocalChecked() |
| ->IsNumber()); |
| CHECK_EQ(0, result->Get(context.local(), v8::Integer::New(isolate, 2)) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(result->Get(context.local(), v8::Integer::New(isolate, 3)) |
| .ToLocalChecked() |
| ->IsNull()); |
| CHECK(result->Get(context.local(), v8::Integer::New(isolate, 4)) |
| .ToLocalChecked() |
| ->IsUndefined()); |
| } |
| |
| |
| THREADED_TEST(CatchZero) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("throw 10"); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(10, try_catch.Exception()->Int32Value(context.local()).FromJust()); |
| try_catch.Reset(); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("throw 0"); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(0, try_catch.Exception()->Int32Value(context.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(CatchExceptionFromWith) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("var o = {}; with (o) { throw 42; }"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(TryCatchAndFinallyHidingException) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); |
| CompileRun("f({toString: function() { throw 42; }});"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| void WithTryCatch(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::TryCatch try_catch(args.GetIsolate()); |
| } |
| |
| |
| THREADED_TEST(TryCatchAndFinally) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("native_with_try_catch"), |
| v8::FunctionTemplate::New(isolate, WithTryCatch) |
| ->GetFunction(context.local()) |
| .ToLocalChecked()) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun( |
| "try {\n" |
| " throw new Error('a');\n" |
| "} finally {\n" |
| " native_with_try_catch();\n" |
| "}\n"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| static void TryCatchNested1Helper(int depth) { |
| if (depth > 0) { |
| v8::TryCatch try_catch(CcTest::isolate()); |
| try_catch.SetVerbose(true); |
| TryCatchNested1Helper(depth - 1); |
| CHECK(try_catch.HasCaught()); |
| try_catch.ReThrow(); |
| } else { |
| CcTest::isolate()->ThrowException(v8_str("E1")); |
| } |
| } |
| |
| |
| static void TryCatchNested2Helper(int depth) { |
| if (depth > 0) { |
| v8::TryCatch try_catch(CcTest::isolate()); |
| try_catch.SetVerbose(true); |
| TryCatchNested2Helper(depth - 1); |
| CHECK(try_catch.HasCaught()); |
| try_catch.ReThrow(); |
| } else { |
| CompileRun("throw 'E2';"); |
| } |
| } |
| |
| |
| TEST(TryCatchNested) { |
| v8::V8::Initialize(); |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| { |
| // Test nested try-catch with a native throw in the end. |
| v8::TryCatch try_catch(context->GetIsolate()); |
| TryCatchNested1Helper(5); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(), |
| try_catch.Exception()), |
| "E1")); |
| } |
| |
| { |
| // Test nested try-catch with a JavaScript throw in the end. |
| v8::TryCatch try_catch(context->GetIsolate()); |
| TryCatchNested2Helper(5); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(), |
| try_catch.Exception()), |
| "E2")); |
| } |
| } |
| |
| |
| void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) { |
| CHECK(try_catch->HasCaught()); |
| Local<Message> message = try_catch->Message(); |
| Local<Value> resource = message->GetScriptOrigin().ResourceName(); |
| CHECK_EQ( |
| 0, strcmp(*v8::String::Utf8Value(CcTest::isolate(), resource), "inner")); |
| CHECK_EQ(0, strcmp(*v8::String::Utf8Value(CcTest::isolate(), message->Get()), |
| "Uncaught Error: a")); |
| CHECK_EQ(1, message->GetLineNumber(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| CHECK_EQ(0, message->GetStartColumn(CcTest::isolate()->GetCurrentContext()) |
| .FromJust()); |
| } |
| |
| |
| void TryCatchMixedNestingHelper( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| CompileRunWithOrigin("throw new Error('a');\n", "inner", 0, 0); |
| CHECK(try_catch.HasCaught()); |
| TryCatchMixedNestingCheck(&try_catch); |
| try_catch.ReThrow(); |
| } |
| |
| |
| // This test ensures that an outer TryCatch in the following situation: |
| // C++/TryCatch -> JS -> C++/TryCatch -> JS w/ SyntaxError |
| // does not clobber the Message object generated for the inner TryCatch. |
| // This exercises the ability of TryCatch.ReThrow() to restore the |
| // inner pending Message before throwing the exception again. |
| TEST(TryCatchMixedNesting) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::Initialize(); |
| v8::TryCatch try_catch(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("TryCatchMixedNestingHelper"), |
| v8::FunctionTemplate::New(isolate, TryCatchMixedNestingHelper)); |
| LocalContext context(0, templ); |
| CompileRunWithOrigin("TryCatchMixedNestingHelper();\n", "outer", 1, 1); |
| TryCatchMixedNestingCheck(&try_catch); |
| } |
| |
| |
| void TryCatchNativeHelper(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| args.GetIsolate()->ThrowException(v8_str("boom")); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| TEST(TryCatchNative) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::Initialize(); |
| v8::TryCatch try_catch(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("TryCatchNativeHelper"), |
| v8::FunctionTemplate::New(isolate, TryCatchNativeHelper)); |
| LocalContext context(0, templ); |
| CompileRun("TryCatchNativeHelper();"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| void TryCatchNativeResetHelper( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| args.GetIsolate()->ThrowException(v8_str("boom")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| TEST(TryCatchNativeReset) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::Initialize(); |
| v8::TryCatch try_catch(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("TryCatchNativeResetHelper"), |
| v8::FunctionTemplate::New(isolate, TryCatchNativeResetHelper)); |
| LocalContext context(0, templ); |
| CompileRun("TryCatchNativeResetHelper();"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(Equality) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(context->GetIsolate()); |
| // Check that equality works at all before relying on CHECK_EQ |
| CHECK(v8_str("a")->Equals(context.local(), v8_str("a")).FromJust()); |
| CHECK(!v8_str("a")->Equals(context.local(), v8_str("b")).FromJust()); |
| |
| CHECK(v8_str("a")->Equals(context.local(), v8_str("a")).FromJust()); |
| CHECK(!v8_str("a")->Equals(context.local(), v8_str("b")).FromJust()); |
| CHECK(v8_num(1)->Equals(context.local(), v8_num(1)).FromJust()); |
| CHECK(v8_num(1.00)->Equals(context.local(), v8_num(1)).FromJust()); |
| CHECK(!v8_num(1)->Equals(context.local(), v8_num(2)).FromJust()); |
| |
| // Assume String is not internalized. |
| CHECK(v8_str("a")->StrictEquals(v8_str("a"))); |
| CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); |
| CHECK(!v8_str("5")->StrictEquals(v8_num(5))); |
| CHECK(v8_num(1)->StrictEquals(v8_num(1))); |
| CHECK(!v8_num(1)->StrictEquals(v8_num(2))); |
| CHECK(v8_num(0.0)->StrictEquals(v8_num(-0.0))); |
| Local<Value> not_a_number = v8_num(std::numeric_limits<double>::quiet_NaN()); |
| CHECK(!not_a_number->StrictEquals(not_a_number)); |
| CHECK(v8::False(isolate)->StrictEquals(v8::False(isolate))); |
| CHECK(!v8::False(isolate)->StrictEquals(v8::Undefined(isolate))); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| v8::Persistent<v8::Object> alias(isolate, obj); |
| CHECK(v8::Local<v8::Object>::New(isolate, alias)->StrictEquals(obj)); |
| alias.Reset(); |
| |
| CHECK(v8_str("a")->SameValue(v8_str("a"))); |
| CHECK(!v8_str("a")->SameValue(v8_str("b"))); |
| CHECK(!v8_str("5")->SameValue(v8_num(5))); |
| CHECK(v8_num(1)->SameValue(v8_num(1))); |
| CHECK(!v8_num(1)->SameValue(v8_num(2))); |
| CHECK(!v8_num(0.0)->SameValue(v8_num(-0.0))); |
| CHECK(not_a_number->SameValue(not_a_number)); |
| CHECK(v8::False(isolate)->SameValue(v8::False(isolate))); |
| CHECK(!v8::False(isolate)->SameValue(v8::Undefined(isolate))); |
| } |
| |
| THREADED_TEST(TypeOf) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| Local<v8::Function> fun = t1->GetFunction(context.local()).ToLocalChecked(); |
| |
| CHECK(v8::Undefined(isolate) |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("undefined")) |
| .FromJust()); |
| CHECK(v8::Null(isolate) |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("object")) |
| .FromJust()); |
| CHECK(v8_str("str") |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("string")) |
| .FromJust()); |
| CHECK(v8_num(0.0) |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("number")) |
| .FromJust()); |
| CHECK(v8_num(1) |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("number")) |
| .FromJust()); |
| CHECK(v8::Object::New(isolate) |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("object")) |
| .FromJust()); |
| CHECK(v8::Boolean::New(isolate, true) |
| ->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("boolean")) |
| .FromJust()); |
| CHECK(fun->TypeOf(isolate) |
| ->Equals(context.local(), v8_str("function")) |
| .FromJust()); |
| } |
| |
| THREADED_TEST(InstanceOf) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| CompileRun( |
| "var A = {};" |
| "var B = {};" |
| "var C = {};" |
| "B.__proto__ = A;" |
| "C.__proto__ = B;" |
| "function F() {}" |
| "F.prototype = A;" |
| "var G = { [Symbol.hasInstance] : null};" |
| "var H = { [Symbol.hasInstance] : () => { throw new Error(); } };" |
| "var J = { [Symbol.hasInstance] : () => true };" |
| "class K {}" |
| "var D = new K;" |
| "class L extends K {}" |
| "var E = new L"); |
| |
| v8::Local<v8::Object> f = v8::Local<v8::Object>::Cast(CompileRun("F")); |
| v8::Local<v8::Object> g = v8::Local<v8::Object>::Cast(CompileRun("G")); |
| v8::Local<v8::Object> h = v8::Local<v8::Object>::Cast(CompileRun("H")); |
| v8::Local<v8::Object> j = v8::Local<v8::Object>::Cast(CompileRun("J")); |
| v8::Local<v8::Object> k = v8::Local<v8::Object>::Cast(CompileRun("K")); |
| v8::Local<v8::Object> l = v8::Local<v8::Object>::Cast(CompileRun("L")); |
| v8::Local<v8::Value> a = v8::Local<v8::Value>::Cast(CompileRun("A")); |
| v8::Local<v8::Value> b = v8::Local<v8::Value>::Cast(CompileRun("B")); |
| v8::Local<v8::Value> c = v8::Local<v8::Value>::Cast(CompileRun("C")); |
| v8::Local<v8::Value> d = v8::Local<v8::Value>::Cast(CompileRun("D")); |
| v8::Local<v8::Value> e = v8::Local<v8::Value>::Cast(CompileRun("E")); |
| |
| v8::TryCatch try_catch(env->GetIsolate()); |
| CHECK(!a->InstanceOf(env.local(), f).ToChecked()); |
| CHECK(b->InstanceOf(env.local(), f).ToChecked()); |
| CHECK(c->InstanceOf(env.local(), f).ToChecked()); |
| CHECK(!d->InstanceOf(env.local(), f).ToChecked()); |
| CHECK(!e->InstanceOf(env.local(), f).ToChecked()); |
| CHECK(!try_catch.HasCaught()); |
| |
| CHECK(a->InstanceOf(env.local(), g).IsNothing()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| CHECK(b->InstanceOf(env.local(), h).IsNothing()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| CHECK(v8_num(1)->InstanceOf(env.local(), j).ToChecked()); |
| CHECK(!try_catch.HasCaught()); |
| |
| CHECK(d->InstanceOf(env.local(), k).ToChecked()); |
| CHECK(e->InstanceOf(env.local(), k).ToChecked()); |
| CHECK(!d->InstanceOf(env.local(), l).ToChecked()); |
| CHECK(e->InstanceOf(env.local(), l).ToChecked()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| THREADED_TEST(MultiRun) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| Local<Script> script = v8_compile("x"); |
| for (int i = 0; i < 10; i++) { |
| script->Run(context.local()).IsEmpty(); |
| } |
| } |
| |
| |
| static void GetXValue(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK(info.Data() |
| ->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("donut")) |
| .FromJust()); |
| CHECK(name->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("x")) |
| .FromJust()); |
| info.GetReturnValue().Set(name); |
| } |
| |
| |
| THREADED_TEST(SimplePropertyRead) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, nullptr, v8_str("donut")); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| Local<Script> script = v8_compile("obj.x"); |
| for (int i = 0; i < 10; i++) { |
| Local<Value> result = script->Run(context.local()).ToLocalChecked(); |
| CHECK(result->Equals(context.local(), v8_str("x")).FromJust()); |
| } |
| } |
| |
| |
| THREADED_TEST(DefinePropertyOnAPIAccessor) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, nullptr, v8_str("donut")); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| // Uses getOwnPropertyDescriptor to check the configurable status |
| Local<Script> script_desc = v8_compile( |
| "var prop = Object.getOwnPropertyDescriptor( " |
| "obj, 'x');" |
| "prop.configurable;"); |
| Local<Value> result = script_desc->Run(context.local()).ToLocalChecked(); |
| CHECK(result->BooleanValue(context.local()).FromJust()); |
| |
| // Redefine get - but still configurable |
| Local<Script> script_define = v8_compile( |
| "var desc = { get: function(){return 42; }," |
| " configurable: true };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(context.local()).ToLocalChecked(); |
| CHECK(result->Equals(context.local(), v8_num(42)).FromJust()); |
| |
| // Check that the accessor is still configurable |
| result = script_desc->Run(context.local()).ToLocalChecked(); |
| CHECK(result->BooleanValue(context.local()).FromJust()); |
| |
| // Redefine to a non-configurable |
| script_define = v8_compile( |
| "var desc = { get: function(){return 43; }," |
| " configurable: false };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(context.local()).ToLocalChecked(); |
| CHECK(result->Equals(context.local(), v8_num(43)).FromJust()); |
| result = script_desc->Run(context.local()).ToLocalChecked(); |
| CHECK(!result->BooleanValue(context.local()).FromJust()); |
| |
| // Make sure that it is not possible to redefine again |
| v8::TryCatch try_catch(isolate); |
| CHECK(script_define->Run(context.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ(0, |
| strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| |
| |
| THREADED_TEST(DefinePropertyOnDefineGetterSetter) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, nullptr, v8_str("donut")); |
| LocalContext context; |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| Local<Script> script_desc = v8_compile( |
| "var prop =" |
| "Object.getOwnPropertyDescriptor( " |
| "obj, 'x');" |
| "prop.configurable;"); |
| Local<Value> result = script_desc->Run(context.local()).ToLocalChecked(); |
| CHECK(result->BooleanValue(context.local()).FromJust()); |
| |
| Local<Script> script_define = v8_compile( |
| "var desc = {get: function(){return 42; }," |
| " configurable: true };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(context.local()).ToLocalChecked(); |
| CHECK(result->Equals(context.local(), v8_num(42)).FromJust()); |
| |
| result = script_desc->Run(context.local()).ToLocalChecked(); |
| CHECK(result->BooleanValue(context.local()).FromJust()); |
| |
| script_define = v8_compile( |
| "var desc = {get: function(){return 43; }," |
| " configurable: false };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(context.local()).ToLocalChecked(); |
| CHECK(result->Equals(context.local(), v8_num(43)).FromJust()); |
| |
| result = script_desc->Run(context.local()).ToLocalChecked(); |
| CHECK(!result->BooleanValue(context.local()).FromJust()); |
| |
| v8::TryCatch try_catch(isolate); |
| CHECK(script_define->Run(context.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ(0, |
| strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| |
| |
| static v8::Local<v8::Object> GetGlobalProperty(LocalContext* context, |
| char const* name) { |
| return v8::Local<v8::Object>::Cast( |
| (*context) |
| ->Global() |
| ->Get(CcTest::isolate()->GetCurrentContext(), v8_str(name)) |
| .ToLocalChecked()); |
| } |
| |
| |
| THREADED_TEST(DefineAPIAccessorOnObject) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| LocalContext context; |
| |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj1"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun("var obj2 = {};"); |
| |
| CHECK(CompileRun("obj1.x")->IsUndefined()); |
| CHECK(CompileRun("obj2.x")->IsUndefined()); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| |
| ExpectString("obj1.x", "x"); |
| CHECK(CompileRun("obj2.x")->IsUndefined()); |
| |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| |
| ExpectString("obj1.x", "x"); |
| ExpectString("obj2.x", "x"); |
| |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| CompileRun( |
| "Object.defineProperty(obj1, 'x'," |
| "{ get: function() { return 'y'; }, configurable: true })"); |
| |
| ExpectString("obj1.x", "y"); |
| ExpectString("obj2.x", "x"); |
| |
| CompileRun( |
| "Object.defineProperty(obj2, 'x'," |
| "{ get: function() { return 'y'; }, configurable: true })"); |
| |
| ExpectString("obj1.x", "y"); |
| ExpectString("obj2.x", "y"); |
| |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| |
| ExpectString("obj1.x", "x"); |
| ExpectString("obj2.x", "x"); |
| |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| // Define getters/setters, but now make them not configurable. |
| CompileRun( |
| "Object.defineProperty(obj1, 'x'," |
| "{ get: function() { return 'z'; }, configurable: false })"); |
| CompileRun( |
| "Object.defineProperty(obj2, 'x'," |
| "{ get: function() { return 'z'; }, configurable: false })"); |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| ExpectString("obj1.x", "z"); |
| ExpectString("obj2.x", "z"); |
| |
| CHECK(!GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| CHECK(!GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| |
| ExpectString("obj1.x", "z"); |
| ExpectString("obj2.x", "z"); |
| } |
| |
| |
| THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| LocalContext context; |
| |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj1"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun("var obj2 = {};"); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut"), v8::DEFAULT, v8::DontDelete) |
| .FromJust()); |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut"), v8::DEFAULT, v8::DontDelete) |
| .FromJust()); |
| |
| ExpectString("obj1.x", "x"); |
| ExpectString("obj2.x", "x"); |
| |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| CHECK(!GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| CHECK(!GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(context.local(), v8_str("x"), GetXValue, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "Object.defineProperty(obj1, 'x'," |
| "{get: function() { return 'func'; }})"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ( |
| 0, strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "Object.defineProperty(obj2, 'x'," |
| "{get: function() { return 'func'; }})"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ( |
| 0, strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| } |
| |
| |
| static void Get239Value(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK(info.Data() |
| ->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("donut")) |
| .FromJust()); |
| CHECK(name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("239")) |
| .FromJust()); |
| info.GetReturnValue().Set(name); |
| } |
| |
| |
| THREADED_TEST(ElementAPIAccessor) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| LocalContext context; |
| |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj1"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun("var obj2 = {};"); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(context.local(), v8_str("239"), Get239Value, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(context.local(), v8_str("239"), Get239Value, nullptr, |
| v8_str("donut")) |
| .FromJust()); |
| |
| ExpectString("obj1[239]", "239"); |
| ExpectString("obj2[239]", "239"); |
| ExpectString("obj1['239']", "239"); |
| ExpectString("obj2['239']", "239"); |
| } |
| |
| |
| v8::Persistent<Value> xValue; |
| |
| |
| static void SetXValue(Local<Name> name, Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| Local<Context> context = info.GetIsolate()->GetCurrentContext(); |
| CHECK(value->Equals(context, v8_num(4)).FromJust()); |
| CHECK(info.Data()->Equals(context, v8_str("donut")).FromJust()); |
| CHECK(name->Equals(context, v8_str("x")).FromJust()); |
| CHECK(xValue.IsEmpty()); |
| xValue.Reset(info.GetIsolate(), value); |
| } |
| |
| |
| THREADED_TEST(SimplePropertyWrite) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); |
| LocalContext context; |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| Local<Script> script = v8_compile("obj.x = 4"); |
| for (int i = 0; i < 10; i++) { |
| CHECK(xValue.IsEmpty()); |
| script->Run(context.local()).ToLocalChecked(); |
| CHECK(v8_num(4) |
| ->Equals(context.local(), |
| Local<Value>::New(CcTest::isolate(), xValue)) |
| .FromJust()); |
| xValue.Reset(); |
| } |
| } |
| |
| |
| THREADED_TEST(SetterOnly) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), nullptr, SetXValue, v8_str("donut")); |
| LocalContext context; |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| Local<Script> script = v8_compile("obj.x = 4; obj.x"); |
| for (int i = 0; i < 10; i++) { |
| CHECK(xValue.IsEmpty()); |
| script->Run(context.local()).ToLocalChecked(); |
| CHECK(v8_num(4) |
| ->Equals(context.local(), |
| Local<Value>::New(CcTest::isolate(), xValue)) |
| .FromJust()); |
| xValue.Reset(); |
| } |
| } |
| |
| |
| THREADED_TEST(NoAccessors) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), |
| static_cast<v8::AccessorGetterCallback>(nullptr), nullptr, |
| v8_str("donut")); |
| LocalContext context; |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| Local<Script> script = v8_compile("obj.x = 4; obj.x"); |
| for (int i = 0; i < 10; i++) { |
| script->Run(context.local()).ToLocalChecked(); |
| } |
| } |
| |
| |
| THREADED_TEST(MultiContexts) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("dummy"), |
| v8::FunctionTemplate::New(isolate, DummyCallHandler)); |
| |
| Local<String> password = v8_str("Password"); |
| |
| // Create an environment |
| LocalContext context0(0, templ); |
| context0->SetSecurityToken(password); |
| v8::Local<v8::Object> global0 = context0->Global(); |
| CHECK(global0->Set(context0.local(), v8_str("custom"), v8_num(1234)) |
| .FromJust()); |
| CHECK_EQ(1234, global0->Get(context0.local(), v8_str("custom")) |
| .ToLocalChecked() |
| ->Int32Value(context0.local()) |
| .FromJust()); |
| |
| // Create an independent environment |
| LocalContext context1(0, templ); |
| context1->SetSecurityToken(password); |
| v8::Local<v8::Object> global1 = context1->Global(); |
| CHECK(global1->Set(context1.local(), v8_str("custom"), v8_num(1234)) |
| .FromJust()); |
| CHECK(!global0->Equals(context1.local(), global1).FromJust()); |
| CHECK_EQ(1234, global0->Get(context1.local(), v8_str("custom")) |
| .ToLocalChecked() |
| ->Int32Value(context0.local()) |
| .FromJust()); |
| CHECK_EQ(1234, global1->Get(context1.local(), v8_str("custom")) |
| .ToLocalChecked() |
| ->Int32Value(context1.local()) |
| .FromJust()); |
| |
| // Now create a new context with the old global |
| LocalContext context2(0, templ, global1); |
| context2->SetSecurityToken(password); |
| v8::Local<v8::Object> global2 = context2->Global(); |
| CHECK(global1->Equals(context2.local(), global2).FromJust()); |
| CHECK_EQ(0, global1->Get(context2.local(), v8_str("custom")) |
| .ToLocalChecked() |
| ->Int32Value(context1.local()) |
| .FromJust()); |
| CHECK_EQ(0, global2->Get(context2.local(), v8_str("custom")) |
| .ToLocalChecked() |
| ->Int32Value(context2.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(FunctionPrototypeAcrossContexts) { |
| // Make sure that functions created by cloning boilerplates cannot |
| // communicate through their __proto__ field. |
| |
| v8::HandleScope scope(CcTest::isolate()); |
| |
| LocalContext env0; |
| v8::Local<v8::Object> global0 = env0->Global(); |
| v8::Local<v8::Object> object0 = global0->Get(env0.local(), v8_str("Object")) |
| .ToLocalChecked() |
| .As<v8::Object>(); |
| v8::Local<v8::Object> tostring0 = |
| object0->Get(env0.local(), v8_str("toString")) |
| .ToLocalChecked() |
| .As<v8::Object>(); |
| v8::Local<v8::Object> proto0 = |
| tostring0->Get(env0.local(), v8_str("__proto__")) |
| .ToLocalChecked() |
| .As<v8::Object>(); |
| CHECK(proto0->Set(env0.local(), v8_str("custom"), v8_num(1234)).FromJust()); |
| |
| LocalContext env1; |
| v8::Local<v8::Object> global1 = env1->Global(); |
| v8::Local<v8::Object> object1 = global1->Get(env1.local(), v8_str("Object")) |
| .ToLocalChecked() |
| .As<v8::Object>(); |
| v8::Local<v8::Object> tostring1 = |
| object1->Get(env1.local(), v8_str("toString")) |
| .ToLocalChecked() |
| .As<v8::Object>(); |
| v8::Local<v8::Object> proto1 = |
| tostring1->Get(env1.local(), v8_str("__proto__")) |
| .ToLocalChecked() |
| .As<v8::Object>(); |
| CHECK(!proto1->Has(env1.local(), v8_str("custom")).FromJust()); |
| } |
| |
| |
| THREADED_TEST(Regress892105) { |
| // Make sure that object and array literals created by cloning |
| // boilerplates cannot communicate through their __proto__ |
| // field. This is rather difficult to check, but we try to add stuff |
| // to Object.prototype and Array.prototype and create a new |
| // environment. This should succeed. |
| |
| v8::HandleScope scope(CcTest::isolate()); |
| |
| Local<String> source = v8_str( |
| "Object.prototype.obj = 1234;" |
| "Array.prototype.arr = 4567;" |
| "8901"); |
| |
| LocalContext env0; |
| Local<Script> script0 = v8_compile(source); |
| CHECK_EQ(8901.0, script0->Run(env0.local()) |
| .ToLocalChecked() |
| ->NumberValue(env0.local()) |
| .FromJust()); |
| |
| LocalContext env1; |
| Local<Script> script1 = v8_compile(source); |
| CHECK_EQ(8901.0, script1->Run(env1.local()) |
| .ToLocalChecked() |
| ->NumberValue(env1.local()) |
| .FromJust()); |
| } |
| |
| static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| args.GetReturnValue().Set(args.This()); |
| } |
| |
| THREADED_TEST(UndetectableObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> desc = |
| v8::FunctionTemplate::New(env->GetIsolate()); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| |
| Local<v8::Object> obj = desc->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK( |
| env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust()); |
| |
| ExpectString("undetectable.toString()", "[object Object]"); |
| ExpectString("typeof undetectable", "undefined"); |
| ExpectString("typeof(undetectable)", "undefined"); |
| ExpectBoolean("typeof undetectable == 'undefined'", true); |
| ExpectBoolean("typeof undetectable == 'object'", false); |
| ExpectBoolean("if (undetectable) { true; } else { false; }", false); |
| ExpectBoolean("!undetectable", true); |
| |
| ExpectObject("true&&undetectable", obj); |
| ExpectBoolean("false&&undetectable", false); |
| ExpectBoolean("true||undetectable", true); |
| ExpectObject("false||undetectable", obj); |
| |
| ExpectObject("undetectable&&true", obj); |
| ExpectObject("undetectable&&false", obj); |
| ExpectBoolean("undetectable||true", true); |
| ExpectBoolean("undetectable||false", false); |
| |
| ExpectBoolean("undetectable==null", true); |
| ExpectBoolean("null==undetectable", true); |
| ExpectBoolean("undetectable==undefined", true); |
| ExpectBoolean("undefined==undetectable", true); |
| ExpectBoolean("undetectable==undetectable", true); |
| |
| |
| ExpectBoolean("undetectable===null", false); |
| ExpectBoolean("null===undetectable", false); |
| ExpectBoolean("undetectable===undefined", false); |
| ExpectBoolean("undefined===undetectable", false); |
| ExpectBoolean("undetectable===undetectable", true); |
| } |
| |
| |
| THREADED_TEST(VoidLiteral) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| |
| Local<v8::Object> obj = desc->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK( |
| env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust()); |
| |
| ExpectBoolean("undefined == void 0", true); |
| ExpectBoolean("undetectable == void 0", true); |
| ExpectBoolean("null == void 0", true); |
| ExpectBoolean("undefined === void 0", true); |
| ExpectBoolean("undetectable === void 0", false); |
| ExpectBoolean("null === void 0", false); |
| |
| ExpectBoolean("void 0 == undefined", true); |
| ExpectBoolean("void 0 == undetectable", true); |
| ExpectBoolean("void 0 == null", true); |
| ExpectBoolean("void 0 === undefined", true); |
| ExpectBoolean("void 0 === undetectable", false); |
| ExpectBoolean("void 0 === null", false); |
| |
| ExpectString( |
| "(function() {" |
| " try {" |
| " return x === void 0;" |
| " } catch(e) {" |
| " return e.toString();" |
| " }" |
| "})()", |
| "ReferenceError: x is not defined"); |
| ExpectString( |
| "(function() {" |
| " try {" |
| " return void 0 === x;" |
| " } catch(e) {" |
| " return e.toString();" |
| " }" |
| "})()", |
| "ReferenceError: x is not defined"); |
| } |
| |
| |
| THREADED_TEST(ExtensibleOnUndetectable) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| |
| Local<v8::Object> obj = desc->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK( |
| env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust()); |
| |
| Local<String> source = v8_str( |
| "undetectable.x = 42;" |
| "undetectable.x"); |
| |
| Local<Script> script = v8_compile(source); |
| |
| CHECK(v8::Integer::New(isolate, 42) |
| ->Equals(env.local(), script->Run(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| ExpectBoolean("Object.isExtensible(undetectable)", true); |
| |
| source = v8_str("Object.preventExtensions(undetectable);"); |
| script = v8_compile(source); |
| script->Run(env.local()).ToLocalChecked(); |
| ExpectBoolean("Object.isExtensible(undetectable)", false); |
| |
| source = v8_str("undetectable.y = 2000;"); |
| script = v8_compile(source); |
| script->Run(env.local()).ToLocalChecked(); |
| ExpectBoolean("undetectable.y == undefined", true); |
| } |
| |
| |
| // The point of this test is type checking. We run it only so compilers |
| // don't complain about an unused function. |
| TEST(PersistentHandles) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<String> str = v8_str("foo"); |
| v8::Persistent<String> p_str(isolate, str); |
| p_str.Reset(); |
| Local<Script> scr = v8_compile(""); |
| v8::Persistent<Script> p_scr(isolate, scr); |
| p_scr.Reset(); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| v8::Persistent<ObjectTemplate> p_templ(isolate, templ); |
| p_templ.Reset(); |
| } |
| |
| |
| static void HandleLogDelegator( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectTemplate) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); |
| global_template->Set(v8_str("JSNI_Log"), |
| v8::FunctionTemplate::New(isolate, HandleLogDelegator)); |
| v8::Local<Context> context = Context::New(isolate, 0, global_template); |
| Context::Scope context_scope(context); |
| CompileRun("JSNI_Log('LOG')"); |
| } |
| |
| |
| static const char* kSimpleExtensionSource = |
| "function Foo() {" |
| " return 4;" |
| "}"; |
| |
| |
| TEST(SimpleExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); |
| const char* extension_names[] = {"simpletest"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("Foo()"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4)) |
| .FromJust()); |
| } |
| |
| |
| static const char* kStackTraceFromExtensionSource = |
| "function foo() {" |
| " throw new Error();" |
| "}" |
| "function bar() {" |
| " foo();" |
| "}"; |
| |
| |
| TEST(StackTraceInExtension) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("stacktracetest", kStackTraceFromExtensionSource)); |
| const char* extension_names[] = {"stacktracetest"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| CompileRun( |
| "function user() { bar(); }" |
| "var error;" |
| "try{ user(); } catch (e) { error = e; }"); |
| CHECK_EQ(-1, v8_run_int32value(v8_compile("error.stack.indexOf('foo')"))); |
| CHECK_EQ(-1, v8_run_int32value(v8_compile("error.stack.indexOf('bar')"))); |
| CHECK_NE(-1, v8_run_int32value(v8_compile("error.stack.indexOf('user')"))); |
| } |
| |
| |
| TEST(NullExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("nulltest", nullptr)); |
| const char* extension_names[] = {"nulltest"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("1+3"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4)) |
| .FromJust()); |
| } |
| |
| static const char* kEmbeddedExtensionSource = |
| "function Ret54321(){return 54321;}~~@@$" |
| "$%% THIS IS A SERIES OF NON-nullptr-TERMINATED STRINGS."; |
| static const int kEmbeddedExtensionSourceValidLen = 34; |
| |
| |
| TEST(ExtensionMissingSourceLength) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("srclentest_fail", kEmbeddedExtensionSource)); |
| const char* extension_names[] = {"srclentest_fail"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK_NULL(*context); |
| } |
| |
| |
| TEST(ExtensionWithSourceLength) { |
| for (int source_len = kEmbeddedExtensionSourceValidLen - 1; |
| source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| i::ScopedVector<char> extension_name(32); |
| i::SNPrintF(extension_name, "ext #%d", source_len); |
| v8::RegisterExtension(new Extension( |
| extension_name.start(), kEmbeddedExtensionSource, 0, 0, source_len)); |
| const char* extension_names[1] = {extension_name.start()}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| if (source_len == kEmbeddedExtensionSourceValidLen) { |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("Ret54321()"); |
| CHECK(v8::Integer::New(CcTest::isolate(), 54321) |
| ->Equals(context, result) |
| .FromJust()); |
| } else { |
| // Anything but exactly the right length should fail to compile. |
| CHECK_NULL(*context); |
| } |
| } |
| } |
| |
| |
| static const char* kEvalExtensionSource1 = |
| "function UseEval1() {" |
| " var x = 42;" |
| " return eval('x');" |
| "}"; |
| |
| |
| static const char* kEvalExtensionSource2 = |
| "(function() {" |
| " var x = 42;" |
| " function e() {" |
| " return eval('x');" |
| " }" |
| " this.UseEval2 = e;" |
| "})()"; |
| |
| |
| TEST(UseEvalFromExtension) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); |
| v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); |
| const char* extension_names[] = {"evaltest1", "evaltest2"}; |
| v8::ExtensionConfiguration extensions(2, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("UseEval1()"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42)) |
| .FromJust()); |
| result = CompileRun("UseEval2()"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42)) |
| .FromJust()); |
| } |
| |
| |
| static const char* kWithExtensionSource1 = |
| "function UseWith1() {" |
| " var x = 42;" |
| " with({x:87}) { return x; }" |
| "}"; |
| |
| |
| static const char* kWithExtensionSource2 = |
| "(function() {" |
| " var x = 42;" |
| " function e() {" |
| " with ({x:87}) { return x; }" |
| " }" |
| " this.UseWith2 = e;" |
| "})()"; |
| |
| |
| TEST(UseWithFromExtension) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); |
| v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); |
| const char* extension_names[] = {"withtest1", "withtest2"}; |
| v8::ExtensionConfiguration extensions(2, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("UseWith1()"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 87)) |
| .FromJust()); |
| result = CompileRun("UseWith2()"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 87)) |
| .FromJust()); |
| } |
| |
| |
| TEST(AutoExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| Extension* extension = new Extension("autotest", kSimpleExtensionSource); |
| extension->set_auto_enable(true); |
| v8::RegisterExtension(extension); |
| v8::Local<Context> context = Context::New(CcTest::isolate()); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("Foo()"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4)) |
| .FromJust()); |
| } |
| |
| |
| static const char* kSyntaxErrorInExtensionSource = "["; |
| |
| |
| // Test that a syntax error in an extension does not cause a fatal |
| // error but results in an empty context. |
| TEST(SyntaxErrorExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("syntaxerror", kSyntaxErrorInExtensionSource)); |
| const char* extension_names[] = {"syntaxerror"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| static const char* kExceptionInExtensionSource = "throw 42"; |
| |
| |
| // Test that an exception when installing an extension does not cause |
| // a fatal error but results in an empty context. |
| TEST(ExceptionExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("exception", kExceptionInExtensionSource)); |
| const char* extension_names[] = {"exception"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| static const char* kNativeCallInExtensionSource = |
| "function call_runtime_last_index_of(x) {" |
| " return %StringLastIndexOf(x, 'bob');" |
| "}"; |
| |
| static const char* kNativeCallTest = |
| "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; |
| |
| // Test that a native runtime calls are supported in extensions. |
| TEST(NativeCallInExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("nativecall", kNativeCallInExtensionSource)); |
| const char* extension_names[] = {"nativecall"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun(kNativeCallTest); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 24)) |
| .FromJust()); |
| } |
| |
| |
| class NativeFunctionExtension : public Extension { |
| public: |
| NativeFunctionExtension(const char* name, const char* source, |
| v8::FunctionCallback fun = &Echo) |
| : Extension(name, source), function_(fun) {} |
| |
| virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| v8::Isolate* isolate, v8::Local<v8::String> name) { |
| return v8::FunctionTemplate::New(isolate, function_); |
| } |
| |
| static void Echo(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| if (args.Length() >= 1) args.GetReturnValue().Set(args[0]); |
| } |
| |
| private: |
| v8::FunctionCallback function_; |
| }; |
| |
| |
| TEST(NativeFunctionDeclaration) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| const char* name = "nativedecl"; |
| v8::RegisterExtension( |
| new NativeFunctionExtension(name, "native function foo();")); |
| const char* extension_names[] = {name}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Local<Value> result = CompileRun("foo(42);"); |
| CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42)) |
| .FromJust()); |
| } |
| |
| |
| TEST(NativeFunctionDeclarationError) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| const char* name = "nativedeclerr"; |
| // Syntax error in extension code. |
| v8::RegisterExtension( |
| new NativeFunctionExtension(name, "native\nfunction foo();")); |
| const char* extension_names[] = {name}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| TEST(NativeFunctionDeclarationErrorEscape) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| const char* name = "nativedeclerresc"; |
| // Syntax error in extension code - escape code in "native" means that |
| // it's not treated as a keyword. |
| v8::RegisterExtension( |
| new NativeFunctionExtension(name, "nativ\\u0065 function foo();")); |
| const char* extension_names[] = {name}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| static void CheckDependencies(const char* name, const char* expected) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::ExtensionConfiguration config(1, &name); |
| LocalContext context(&config); |
| CHECK( |
| v8_str(expected) |
| ->Equals(context.local(), context->Global() |
| ->Get(context.local(), v8_str("loaded")) |
| .ToLocalChecked()) |
| .FromJust()); |
| } |
| |
| |
| /* |
| * Configuration: |
| * |
| * /-- B <--\ |
| * A <- -- D <-- E |
| * \-- C <--/ |
| */ |
| THREADED_TEST(ExtensionDependency) { |
| static const char* kEDeps[] = {"D"}; |
| v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); |
| static const char* kDDeps[] = {"B", "C"}; |
| v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); |
| static const char* kBCDeps[] = {"A"}; |
| v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); |
| v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); |
| v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); |
| CheckDependencies("A", "undefinedA"); |
| CheckDependencies("B", "undefinedAB"); |
| CheckDependencies("C", "undefinedAC"); |
| CheckDependencies("D", "undefinedABCD"); |
| CheckDependencies("E", "undefinedABCDE"); |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| static const char* exts[2] = {"C", "E"}; |
| v8::ExtensionConfiguration config(2, exts); |
| LocalContext context(&config); |
| CHECK( |
| v8_str("undefinedACBDE") |
| ->Equals(context.local(), context->Global() |
| ->Get(context.local(), v8_str("loaded")) |
| .ToLocalChecked()) |
| .FromJust()); |
| } |
| |
| |
| static const char* kExtensionTestScript = |
| "native function A();" |
| "native function B();" |
| "native function C();" |
| "function Foo(i) {" |
| " if (i == 0) return A();" |
| " if (i == 1) return B();" |
| " if (i == 2) return C();" |
| "}"; |
| |
| |
| static void CallFun(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| if (args.IsConstructCall()) { |
| CHECK(args.This() |
| ->Set(args.GetIsolate()->GetCurrentContext(), v8_str("data"), |
| args.Data()) |
| .FromJust()); |
| args.GetReturnValue().SetNull(); |
| return; |
| } |
| args.GetReturnValue().Set(args.Data()); |
| } |
| |
| |
| class FunctionExtension : public Extension { |
| public: |
| FunctionExtension() : Extension("functiontest", kExtensionTestScript) {} |
| virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| v8::Isolate* isolate, v8::Local<String> name); |
| }; |
| |
| |
| static int lookup_count = 0; |
| v8::Local<v8::FunctionTemplate> FunctionExtension::GetNativeFunctionTemplate( |
| v8::Isolate* isolate, v8::Local<String> name) { |
| lookup_count++; |
| if (name->Equals(isolate->GetCurrentContext(), v8_str("A")).FromJust()) { |
| return v8::FunctionTemplate::New(isolate, CallFun, |
| v8::Integer::New(isolate, 8)); |
| } else if (name->Equals(isolate->GetCurrentContext(), v8_str("B")) |
| .FromJust()) { |
| return v8::FunctionTemplate::New(isolate, CallFun, |
| v8::Integer::New(isolate, 7)); |
| } else if (name->Equals(isolate->GetCurrentContext(), v8_str("C")) |
| .FromJust()) { |
| return v8::FunctionTemplate::New(isolate, CallFun, |
| v8::Integer::New(isolate, 6)); |
| } else { |
| return v8::Local<v8::FunctionTemplate>(); |
| } |
| } |
| |
| |
| THREADED_TEST(FunctionLookup) { |
| v8::RegisterExtension(new FunctionExtension()); |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| static const char* exts[1] = {"functiontest"}; |
| v8::ExtensionConfiguration config(1, exts); |
| LocalContext context(&config); |
| CHECK_EQ(3, lookup_count); |
| CHECK(v8::Integer::New(CcTest::isolate(), 8) |
| ->Equals(context.local(), CompileRun("Foo(0)")) |
| .FromJust()); |
| CHECK(v8::Integer::New(CcTest::isolate(), 7) |
| ->Equals(context.local(), CompileRun("Foo(1)")) |
| .FromJust()); |
| CHECK(v8::Integer::New(CcTest::isolate(), 6) |
| ->Equals(context.local(), CompileRun("Foo(2)")) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(NativeFunctionConstructCall) { |
| v8::RegisterExtension(new FunctionExtension()); |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| static const char* exts[1] = {"functiontest"}; |
| v8::ExtensionConfiguration config(1, exts); |
| LocalContext context(&config); |
| for (int i = 0; i < 10; i++) { |
| // Run a few times to ensure that allocation of objects doesn't |
| // change behavior of a constructor function. |
| CHECK(v8::Integer::New(CcTest::isolate(), 8) |
| ->Equals(context.local(), CompileRun("(new A()).data")) |
| .FromJust()); |
| CHECK(v8::Integer::New(CcTest::isolate(), 7) |
| ->Equals(context.local(), CompileRun("(new B()).data")) |
| .FromJust()); |
| CHECK(v8::Integer::New(CcTest::isolate(), 6) |
| ->Equals(context.local(), CompileRun("(new C()).data")) |
| .FromJust()); |
| } |
| } |
| |
| |
| static const char* last_location; |
| static const char* last_message; |
| void StoringErrorCallback(const char* location, const char* message) { |
| if (last_location == nullptr) { |
| last_location = location; |
| last_message = message; |
| } |
| } |
| |
| |
| // ErrorReporting creates a circular extensions configuration and |
| // tests that the fatal error handler gets called. This renders V8 |
| // unusable and therefore this test cannot be run in parallel. |
| TEST(ErrorReporting) { |
| CcTest::isolate()->SetFatalErrorHandler(StoringErrorCallback); |
| static const char* aDeps[] = {"B"}; |
| v8::RegisterExtension(new Extension("A", "", 1, aDeps)); |
| static const char* bDeps[] = {"A"}; |
| v8::RegisterExtension(new Extension("B", "", 1, bDeps)); |
| last_location = nullptr; |
| v8::ExtensionConfiguration config(1, bDeps); |
| v8::Local<Context> context = Context::New(CcTest::isolate(), &config); |
| CHECK(context.IsEmpty()); |
| CHECK(last_location); |
| } |
| |
| static size_t dcheck_count; |
| void DcheckErrorCallback(const char* file, int line, const char* message) { |
| last_message = message; |
| ++dcheck_count; |
| } |
| |
| TEST(DcheckErrorHandler) { |
| V8::SetDcheckErrorHandler(DcheckErrorCallback); |
| |
| last_message = nullptr; |
| dcheck_count = 0; |
| |
| DCHECK(false && "w00t"); |
| #ifdef DEBUG |
| CHECK_EQ(dcheck_count, 1); |
| CHECK(last_message); |
| CHECK(std::string(last_message).find("w00t") != std::string::npos); |
| #else |
| // The DCHECK should be a noop in non-DEBUG builds. |
| CHECK_EQ(dcheck_count, 0); |
| #endif |
| } |
| |
| static void MissingScriptInfoMessageListener(v8::Local<v8::Message> message, |
| v8::Local<Value> data) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| Local<Context> context = isolate->GetCurrentContext(); |
| CHECK(message->GetScriptOrigin().ResourceName()->IsUndefined()); |
| CHECK(v8::Undefined(isolate) |
| ->Equals(context, message->GetScriptOrigin().ResourceName()) |
| .FromJust()); |
| message->GetLineNumber(context).FromJust(); |
| message->GetSourceLine(context).ToLocalChecked(); |
| } |
| |
| |
| THREADED_TEST(ErrorWithMissingScriptInfo) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| context->GetIsolate()->AddMessageListener(MissingScriptInfoMessageListener); |
| CompileRun("throw Error()"); |
| context->GetIsolate()->RemoveMessageListeners( |
| MissingScriptInfoMessageListener); |
| } |
| |
| |
| struct FlagAndPersistent { |
| bool flag; |
| v8::Global<v8::Object> handle; |
| }; |
| |
| class Trivial { |
| public: |
| explicit Trivial(int x) : x_(x) {} |
| |
| int x() { return x_; } |
| void set_x(int x) { x_ = x; } |
| |
| private: |
| int x_; |
| }; |
| |
| |
| class Trivial2 { |
| public: |
| Trivial2(int x, int y) : y_(y), x_(x) {} |
| |
| int x() { return x_; } |
| void set_x(int x) { x_ = x; } |
| |
| int y() { return y_; } |
| void set_y(int y) { y_ = y; } |
| |
| private: |
| int y_; |
| int x_; |
| }; |
| |
| void CheckInternalFields( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| v8::Persistent<v8::Object>* handle = data.GetParameter(); |
| handle->Reset(); |
| Trivial* t1 = reinterpret_cast<Trivial*>(data.GetInternalField(0)); |
| Trivial2* t2 = reinterpret_cast<Trivial2*>(data.GetInternalField(1)); |
| CHECK_EQ(42, t1->x()); |
| CHECK_EQ(103, t2->x()); |
| t1->set_x(1729); |
| t2->set_x(33550336); |
| } |
| |
| void InternalFieldCallback(bool global_gc) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| Trivial* t1; |
| Trivial2* t2; |
| instance_templ->SetInternalFieldCount(2); |
| v8::Persistent<v8::Object> handle; |
| { |
| v8::HandleScope scope(isolate); |
| Local<v8::Object> obj = templ->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| handle.Reset(isolate, obj); |
| CHECK_EQ(2, obj->InternalFieldCount()); |
| CHECK(obj->GetInternalField(0)->IsUndefined()); |
| t1 = new Trivial(42); |
| t2 = new Trivial2(103, 9); |
| |
| obj->SetAlignedPointerInInternalField(0, t1); |
| t1 = reinterpret_cast<Trivial*>(obj->GetAlignedPointerFromInternalField(0)); |
| CHECK_EQ(42, t1->x()); |
| |
| obj->SetAlignedPointerInInternalField(1, t2); |
| t2 = |
| reinterpret_cast<Trivial2*>(obj->GetAlignedPointerFromInternalField(1)); |
| CHECK_EQ(103, t2->x()); |
| |
| handle.SetWeak<v8::Persistent<v8::Object>>( |
| &handle, CheckInternalFields, v8::WeakCallbackType::kInternalFields); |
| } |
| if (global_gc) { |
| CcTest::CollectAllGarbage(); |
| } else { |
| CcTest::CollectGarbage(i::NEW_SPACE); |
| } |
| |
| CHECK_EQ(1729, t1->x()); |
| CHECK_EQ(33550336, t2->x()); |
| |
| delete t1; |
| delete t2; |
| } |
| |
| THREADED_TEST(InternalFieldCallback) { |
| InternalFieldCallback(false); |
| InternalFieldCallback(true); |
| } |
| |
| v8::Local<Function> args_fun; |
| |
| |
| static void ArgumentsTestCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = args.GetIsolate(); |
| Local<Context> context = isolate->GetCurrentContext(); |
| CHECK_EQ(3, args.Length()); |
| CHECK(v8::Integer::New(isolate, 1)->Equals(context, args[0]).FromJust()); |
| CHECK(v8::Integer::New(isolate, 2)->Equals(context, args[1]).FromJust()); |
| CHECK(v8::Integer::New(isolate, 3)->Equals(context, args[2]).FromJust()); |
| CHECK(v8::Undefined(isolate)->Equals(context, args[3]).FromJust()); |
| v8::HandleScope scope(args.GetIsolate()); |
| CcTest::CollectAllGarbage(); |
| } |
| |
| |
| THREADED_TEST(Arguments) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); |
| global->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, ArgumentsTestCallback)); |
| LocalContext context(nullptr, global); |
| args_fun = context->Global() |
| ->Get(context.local(), v8_str("f")) |
| .ToLocalChecked() |
| .As<Function>(); |
| v8_compile("f(1, 2, 3)")->Run(context.local()).ToLocalChecked(); |
| } |
| |
| |
| static int p_getter_count; |
| static int p_getter_count2; |
| |
| |
| static void PGetter(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| p_getter_count++; |
| v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| v8::Local<v8::Object> global = context->Global(); |
| CHECK( |
| info.Holder() |
| ->Equals(context, global->Get(context, v8_str("o1")).ToLocalChecked()) |
| .FromJust()); |
| if (name->Equals(context, v8_str("p1")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o1")).ToLocalChecked()) |
| .FromJust()); |
| } else if (name->Equals(context, v8_str("p2")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o2")).ToLocalChecked()) |
| .FromJust()); |
| } else if (name->Equals(context, v8_str("p3")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o3")).ToLocalChecked()) |
| .FromJust()); |
| } else if (name->Equals(context, v8_str("p4")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o4")).ToLocalChecked()) |
| .FromJust()); |
| } |
| } |
| |
| |
| static void RunHolderTest(v8::Local<v8::ObjectTemplate> obj) { |
| ApiTestFuzzer::Fuzz(); |
| LocalContext context; |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o1"), |
| obj->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "o1.__proto__ = { };" |
| "var o2 = { __proto__: o1 };" |
| "var o3 = { __proto__: o2 };" |
| "var o4 = { __proto__: o3 };" |
| "for (var i = 0; i < 10; i++) o4.p4;" |
| "for (var i = 0; i < 10; i++) o3.p3;" |
| "for (var i = 0; i < 10; i++) o2.p2;" |
| "for (var i = 0; i < 10; i++) o1.p1;"); |
| } |
| |
| |
| static void PGetter2(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| p_getter_count2++; |
| v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| v8::Local<v8::Object> global = context->Global(); |
| CHECK( |
| info.Holder() |
| ->Equals(context, global->Get(context, v8_str("o1")).ToLocalChecked()) |
| .FromJust()); |
| if (name->Equals(context, v8_str("p1")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o1")).ToLocalChecked()) |
| .FromJust()); |
| } else if (name->Equals(context, v8_str("p2")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o2")).ToLocalChecked()) |
| .FromJust()); |
| } else if (name->Equals(context, v8_str("p3")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o3")).ToLocalChecked()) |
| .FromJust()); |
| } else if (name->Equals(context, v8_str("p4")).FromJust()) { |
| CHECK(info.This() |
| ->Equals(context, |
| global->Get(context, v8_str("o4")).ToLocalChecked()) |
| .FromJust()); |
| } |
| } |
| |
| |
| THREADED_TEST(GetterHolders) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("p1"), PGetter); |
| obj->SetAccessor(v8_str("p2"), PGetter); |
| obj->SetAccessor(v8_str("p3"), PGetter); |
| obj->SetAccessor(v8_str("p4"), PGetter); |
| p_getter_count = 0; |
| RunHolderTest(obj); |
| CHECK_EQ(40, p_getter_count); |
| } |
| |
| |
| THREADED_TEST(PreInterceptorHolders) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetHandler(v8::NamedPropertyHandlerConfiguration(PGetter2)); |
| p_getter_count2 = 0; |
| RunHolderTest(obj); |
| CHECK_EQ(40, p_getter_count2); |
| } |
| |
| |
| THREADED_TEST(ObjectInstantiation) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("t"), PGetter2); |
| LocalContext context; |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| templ->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| for (int i = 0; i < 100; i++) { |
| v8::HandleScope inner_scope(CcTest::isolate()); |
| v8::Local<v8::Object> obj = |
| templ->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(!obj->Equals(context.local(), context->Global() |
| ->Get(context.local(), v8_str("o")) |
| .ToLocalChecked()) |
| .FromJust()); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("o2"), obj).FromJust()); |
| v8::Local<Value> value = CompileRun("o.__proto__ === o2.__proto__"); |
| CHECK(v8::True(isolate)->Equals(context.local(), value).FromJust()); |
| CHECK(context->Global()->Set(context.local(), v8_str("o"), obj).FromJust()); |
| } |
| } |
| |
| |
| static int StrCmp16(uint16_t* a, uint16_t* b) { |
| while (true) { |
| if (*a == 0 && *b == 0) return 0; |
| if (*a != *b) return 0 + *a - *b; |
| a++; |
| b++; |
| } |
| } |
| |
| |
| static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { |
| while (true) { |
| if (n-- == 0) return 0; |
| if (*a == 0 && *b == 0) return 0; |
| if (*a != *b) return 0 + *a - *b; |
| a++; |
| b++; |
| } |
| } |
| |
| |
| int GetUtf8Length(Local<String> str) { |
| int len = str->Utf8Length(); |
| if (len < 0) { |
| i::Handle<i::String> istr(v8::Utils::OpenHandle(*str)); |
| i::String::Flatten(istr); |
| len = str->Utf8Length(); |
| } |
| return len; |
| } |
| |
| |
| THREADED_TEST(StringWrite) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Local<String> str = v8_str("abcde"); |
| // abc<Icelandic eth><Unicode snowman>. |
| v8::Local<String> str2 = v8_str("abc\xC3\xB0\xE2\x98\x83"); |
| v8::Local<String> str3 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "abc\0def", |
| v8::NewStringType::kNormal, 7) |
| .ToLocalChecked(); |
| // "ab" + lead surrogate + "wx" + trail surrogate + "yz" |
| uint16_t orphans[8] = {0x61, 0x62, 0xD800, 0x77, 0x78, 0xDC00, 0x79, 0x7A}; |
| v8::Local<String> orphans_str = |
| v8::String::NewFromTwoByte(context->GetIsolate(), orphans, |
| v8::NewStringType::kNormal, 8) |
| .ToLocalChecked(); |
| // single lead surrogate |
| uint16_t lead[1] = {0xD800}; |
| v8::Local<String> lead_str = |
| v8::String::NewFromTwoByte(context->GetIsolate(), lead, |
| v8::NewStringType::kNormal, 1) |
| .ToLocalChecked(); |
| // single trail surrogate |
| uint16_t trail[1] = {0xDC00}; |
| v8::Local<String> trail_str = |
| v8::String::NewFromTwoByte(context->GetIsolate(), trail, |
| v8::NewStringType::kNormal, 1) |
| .ToLocalChecked(); |
| // surrogate pair |
| uint16_t pair[2] = {0xD800, 0xDC00}; |
| v8::Local<String> pair_str = |
| v8::String::NewFromTwoByte(context->GetIsolate(), pair, |
| v8::NewStringType::kNormal, 2) |
| .ToLocalChecked(); |
| const int kStride = 4; // Must match stride in for loops in JS below. |
| CompileRun( |
| "var left = '';" |
| "for (var i = 0; i < 0xD800; i += 4) {" |
| " left = left + String.fromCharCode(i);" |
| "}"); |
| CompileRun( |
| "var right = '';" |
| "for (var i = 0; i < 0xD800; i += 4) {" |
| " right = String.fromCharCode(i) + right;" |
| "}"); |
| v8::Local<v8::Object> global = context->Global(); |
| Local<String> left_tree = global->Get(context.local(), v8_str("left")) |
| .ToLocalChecked() |
| .As<String>(); |
| Local<String> right_tree = global->Get(context.local(), v8_str("right")) |
| .ToLocalChecked() |
| .As<String>(); |
| |
| CHECK_EQ(5, str2->Length()); |
| CHECK_EQ(0xD800 / kStride, left_tree->Length()); |
| CHECK_EQ(0xD800 / kStride, right_tree->Length()); |
| |
| char buf[100]; |
| char utf8buf[0xD800 * 3]; |
| uint16_t wbuf[100]; |
| int len; |
| int charlen; |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); |
| CHECK_EQ(9, len); |
| CHECK_EQ(5, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83")); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 8, &charlen); |
| CHECK_EQ(8, len); |
| CHECK_EQ(5, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83\x01", 9)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 7, &charlen); |
| CHECK_EQ(5, len); |
| CHECK_EQ(4, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\x01", 5)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 6, &charlen); |
| CHECK_EQ(5, len); |
| CHECK_EQ(4, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\x01", 5)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 5, &charlen); |
| CHECK_EQ(5, len); |
| CHECK_EQ(4, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\x01", 5)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 4, &charlen); |
| CHECK_EQ(3, len); |
| CHECK_EQ(3, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\x01", 4)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 3, &charlen); |
| CHECK_EQ(3, len); |
| CHECK_EQ(3, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\x01", 4)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 2, &charlen); |
| CHECK_EQ(2, len); |
| CHECK_EQ(2, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "ab\x01", 3)); |
| |
| // allow orphan surrogates by default |
| memset(utf8buf, 0x1, 1000); |
| len = orphans_str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); |
| CHECK_EQ(13, len); |
| CHECK_EQ(8, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "ab\xED\xA0\x80wx\xED\xB0\x80yz")); |
| |
| // replace orphan surrogates with Unicode replacement character |
| memset(utf8buf, 0x1, 1000); |
| len = orphans_str->WriteUtf8(utf8buf, |
| sizeof(utf8buf), |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(13, len); |
| CHECK_EQ(8, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "ab\xEF\xBF\xBDwx\xEF\xBF\xBDyz")); |
| |
| // replace single lead surrogate with Unicode replacement character |
| memset(utf8buf, 0x1, 1000); |
| len = lead_str->WriteUtf8(utf8buf, |
| sizeof(utf8buf), |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(4, len); |
| CHECK_EQ(1, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "\xEF\xBF\xBD")); |
| |
| // replace single trail surrogate with Unicode replacement character |
| memset(utf8buf, 0x1, 1000); |
| len = trail_str->WriteUtf8(utf8buf, |
| sizeof(utf8buf), |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(4, len); |
| CHECK_EQ(1, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "\xEF\xBF\xBD")); |
| |
| // do not replace / write anything if surrogate pair does not fit the buffer |
| // space |
| memset(utf8buf, 0x1, 1000); |
| len = pair_str->WriteUtf8(utf8buf, |
| 3, |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(0, len); |
| CHECK_EQ(0, charlen); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| len = GetUtf8Length(left_tree); |
| int utf8_expected = |
| (0x80 + (0x800 - 0x80) * 2 + (0xD800 - 0x800) * 3) / kStride; |
| CHECK_EQ(utf8_expected, len); |
| len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); |
| CHECK_EQ(utf8_expected, len); |
| CHECK_EQ(0xD800 / kStride, charlen); |
| CHECK_EQ(0xED, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); |
| CHECK_EQ(0x9F, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); |
| CHECK_EQ(0xC0 - kStride, |
| static_cast<unsigned char>(utf8buf[utf8_expected - 1])); |
| CHECK_EQ(1, utf8buf[utf8_expected]); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| len = GetUtf8Length(right_tree); |
| CHECK_EQ(utf8_expected, len); |
| len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); |
| CHECK_EQ(utf8_expected, len); |
| CHECK_EQ(0xD800 / kStride, charlen); |
| CHECK_EQ(0xED, static_cast<unsigned char>(utf8buf[0])); |
| CHECK_EQ(0x9F, static_cast<unsigned char>(utf8buf[1])); |
| CHECK_EQ(0xC0 - kStride, static_cast<unsigned char>(utf8buf[2])); |
| CHECK_EQ(1, utf8buf[utf8_expected]); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf)); |
| CHECK_EQ(5, len); |
| len = str->Write(wbuf); |
| CHECK_EQ(5, len); |
| CHECK_EQ(0, strcmp("abcde", buf)); |
| uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| CHECK_EQ(0, StrCmp16(answer1, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 4); |
| CHECK_EQ(4, len); |
| len = str->Write(wbuf, 0, 4); |
| CHECK_EQ(4, len); |
| CHECK_EQ(0, strncmp("abcd\x01", buf, 5)); |
| uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 5); |
| CHECK_EQ(5, len); |
| len = str->Write(wbuf, 0, 5); |
| CHECK_EQ(5, len); |
| CHECK_EQ(0, strncmp("abcde\x01", buf, 6)); |
| uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 6); |
| CHECK_EQ(5, len); |
| len = str->Write(wbuf, 0, 6); |
| CHECK_EQ(5, len); |
| CHECK_EQ(0, strcmp("abcde", buf)); |
| uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| CHECK_EQ(0, StrCmp16(answer4, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, -1); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 4, -1); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strcmp("e", buf)); |
| uint16_t answer5[] = {'e', '\0'}; |
| CHECK_EQ(0, StrCmp16(answer5, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, 6); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 4, 6); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strcmp("e", buf)); |
| CHECK_EQ(0, StrCmp16(answer5, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, 1); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 4, 1); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strncmp("e\x01", buf, 2)); |
| uint16_t answer6[] = {'e', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 3, 1); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 3, 1); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strncmp("d\x01", buf, 2)); |
| uint16_t answer7[] = {'d', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); |
| |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| wbuf[5] = 'X'; |
| len = str->Write(wbuf, 0, 6, String::NO_NULL_TERMINATION); |
| CHECK_EQ(5, len); |
| CHECK_EQ('X', wbuf[5]); |
| uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'}; |
| uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5)); |
| CHECK_NE(0, StrCmp16(answer8b, wbuf)); |
| wbuf[5] = '\0'; |
| CHECK_EQ(0, StrCmp16(answer8b, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| buf[5] = 'X'; |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), |
| 0, |
| 6, |
| String::NO_NULL_TERMINATION); |
| CHECK_EQ(5, len); |
| CHECK_EQ('X', buf[5]); |
| CHECK_EQ(0, strncmp("abcde", buf, 5)); |
| CHECK_NE(0, strcmp("abcde", buf)); |
| buf[5] = '\0'; |
| CHECK_EQ(0, strcmp("abcde", buf)); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| utf8buf[8] = 'X'; |
| len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, |
| String::NO_NULL_TERMINATION); |
| CHECK_EQ(8, len); |
| CHECK_EQ('X', utf8buf[8]); |
| CHECK_EQ(5, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83", 8)); |
| CHECK_NE(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83")); |
| utf8buf[8] = '\0'; |
| CHECK_EQ(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83")); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| utf8buf[5] = 'X'; |
| len = str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, |
| String::NO_NULL_TERMINATION); |
| CHECK_EQ(5, len); |
| CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched. |
| CHECK_EQ(5, charlen); |
| utf8buf[5] = '\0'; |
| CHECK_EQ(0, strcmp(utf8buf, "abcde")); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| len = str3->WriteOneByte(reinterpret_cast<uint8_t*>(buf)); |
| CHECK_EQ(7, len); |
| CHECK_EQ(0, strcmp("abc", buf)); |
| CHECK_EQ(0, buf[3]); |
| CHECK_EQ(0, strcmp("def", buf + 4)); |
| |
| CHECK_EQ(0, str->WriteOneByte(nullptr, 0, 0, String::NO_NULL_TERMINATION)); |
| CHECK_EQ(0, str->WriteUtf8(nullptr, 0, 0, String::NO_NULL_TERMINATION)); |
| CHECK_EQ(0, str->Write(nullptr, 0, 0, String::NO_NULL_TERMINATION)); |
| } |
| |
| |
| static void Utf16Helper( |
| LocalContext& context, // NOLINT |
| const char* name, |
| const char* lengths_name, |
| int len) { |
| Local<v8::Array> a = Local<v8::Array>::Cast( |
| context->Global()->Get(context.local(), v8_str(name)).ToLocalChecked()); |
| Local<v8::Array> alens = |
| Local<v8::Array>::Cast(context->Global() |
| ->Get(context.local(), v8_str(lengths_name)) |
| .ToLocalChecked()); |
| for (int i = 0; i < len; i++) { |
| Local<v8::String> string = |
| Local<v8::String>::Cast(a->Get(context.local(), i).ToLocalChecked()); |
| Local<v8::Number> expected_len = Local<v8::Number>::Cast( |
| alens->Get(context.local(), i).ToLocalChecked()); |
| int length = GetUtf8Length(string); |
| CHECK_EQ(static_cast<int>(expected_len->Value()), length); |
| } |
| } |
| |
| void TestUtf8DecodingAgainstReference( |
| const char* cases[], |
| const std::vector<std::vector<uint16_t>>& unicode_expected) { |
| for (size_t test_ix = 0; test_ix < unicode_expected.size(); ++test_ix) { |
| v8::Local<String> str = v8_str(cases[test_ix]); |
| CHECK_EQ(unicode_expected[test_ix].size(), str->Length()); |
| |
| std::unique_ptr<uint16_t[]> buffer(new uint16_t[str->Length()]); |
| str->Write(buffer.get(), 0, -1, String::NO_NULL_TERMINATION); |
| |
| for (size_t i = 0; i < unicode_expected[test_ix].size(); ++i) { |
| CHECK_EQ(unicode_expected[test_ix][i], buffer[i]); |
| } |
| } |
| } |
| |
| THREADED_TEST(OverlongSequencesAndSurrogates) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| const char* cases[] = { |
| // Overlong 2-byte sequence. |
| "X\xc0\xbfY\0", |
| // Another overlong 2-byte sequence. |
| "X\xc1\xbfY\0", |
| // Overlong 3-byte sequence. |
| "X\xe0\x9f\xbfY\0", |
| // Overlong 4-byte sequence. |
| "X\xf0\x89\xbf\xbfY\0", |
| // Invalid 3-byte sequence (reserved for surrogates). |
| "X\xed\xa0\x80Y\0", |
| // Invalid 4-bytes sequence (value out of range). |
| "X\xf4\x90\x80\x80Y\0", |
| |
| // Start of an overlong 3-byte sequence but not enough continuation bytes. |
| "X\xe0\x9fY\0", |
| // Start of an overlong 4-byte sequence but not enough continuation bytes. |
| "X\xf0\x89\xbfY\0", |
| // Start of an invalid 3-byte sequence (reserved for surrogates) but not |
| // enough continuation bytes. |
| "X\xed\xa0Y\0", |
| // Start of an invalid 4-bytes sequence (value out of range) but not |
| // enough continuation bytes. |
| "X\xf4\x90\x80Y\0", |
| }; |
| const std::vector<std::vector<uint16_t>> unicode_expected = { |
| {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| }; |
| CHECK_EQ(unicode_expected.size(), arraysize(cases)); |
| TestUtf8DecodingAgainstReference(cases, unicode_expected); |
| } |
| |
| THREADED_TEST(Utf16) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| CompileRun( |
| "var pad = '01234567890123456789';" |
| "var p = [];" |
| "var plens = [20, 3, 3];" |
| "p.push('01234567890123456789');" |
| "var lead = 0xD800;" |
| "var trail = 0xDC00;" |
| "p.push(String.fromCharCode(0xD800));" |
| "p.push(String.fromCharCode(0xDC00));" |
| "var a = [];" |
| "var b = [];" |
| "var c = [];" |
| "var alens = [];" |
| "for (var i = 0; i < 3; i++) {" |
| " p[1] = String.fromCharCode(lead++);" |
| " for (var j = 0; j < 3; j++) {" |
| " p[2] = String.fromCharCode(trail++);" |
| " a.push(p[i] + p[j]);" |
| " b.push(p[i] + p[j]);" |
| " c.push(p[i] + p[j]);" |
| " alens.push(plens[i] + plens[j]);" |
| " }" |
| "}" |
| "alens[5] -= 2;" // Here the surrogate pairs match up. |
| "var a2 = [];" |
| "var b2 = [];" |
| "var c2 = [];" |
| "var a2lens = [];" |
| "for (var m = 0; m < 9; m++) {" |
| " for (var n = 0; n < 9; n++) {" |
| " a2.push(a[m] + a[n]);" |
| " b2.push(b[m] + b[n]);" |
| " var newc = 'x' + c[m] + c[n] + 'y';" |
| " c2.push(newc.substring(1, newc.length - 1));" |
| " var utf = alens[m] + alens[n];" // And here. |
| // The 'n's that start with 0xDC.. |
| // are 6-8 The 'm's that end with |
| // 0xD8.. are 1, 4 and 7 |
| " if ((m % 3) == 1 && n >= 6) utf -= 2;" |
| " a2lens.push(utf);" |
| " }" |
| "}"); |
| Utf16Helper(context, "a", "alens", 9); |
| Utf16Helper(context, "a2", "a2lens", 81); |
| } |
| |
| |
| static bool SameSymbol(Local<String> s1, Local<String> s2) { |
| i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1)); |
| i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2)); |
| return *is1 == *is2; |
| } |
| |
| |
| THREADED_TEST(Utf16Symbol) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| Local<String> symbol1 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "abc", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> symbol2 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "abc", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| CHECK(SameSymbol(symbol1, symbol2)); |
| |
| CompileRun( |
| "var sym0 = 'benedictus';" |
| "var sym0b = 'S\xC3\xB8ren';" |
| "var sym1 = '\xED\xA0\x81\xED\xB0\x87';" |
| "var sym2 = '\xF0\x90\x90\x88';" |
| "var sym3 = 'x\xED\xA0\x81\xED\xB0\x87';" |
| "var sym4 = 'x\xF0\x90\x90\x88';" |
| "if (sym1.length != 2) throw sym1;" |
| "if (sym1.charCodeAt(1) != 0xDC07) throw sym1.charCodeAt(1);" |
| "if (sym2.length != 2) throw sym2;" |
| "if (sym2.charCodeAt(1) != 0xDC08) throw sym2.charCodeAt(2);" |
| "if (sym3.length != 3) throw sym3;" |
| "if (sym3.charCodeAt(2) != 0xDC07) throw sym1.charCodeAt(2);" |
| "if (sym4.length != 3) throw sym4;" |
| "if (sym4.charCodeAt(2) != 0xDC08) throw sym2.charCodeAt(2);"); |
| Local<String> sym0 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "benedictus", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> sym0b = |
| v8::String::NewFromUtf8(context->GetIsolate(), "S\xC3\xB8ren", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> sym1 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "\xED\xA0\x81\xED\xB0\x87", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> sym2 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "\xF0\x90\x90\x88", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> sym3 = v8::String::NewFromUtf8(context->GetIsolate(), |
| "x\xED\xA0\x81\xED\xB0\x87", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> sym4 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "x\xF0\x90\x90\x88", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| v8::Local<v8::Object> global = context->Global(); |
| Local<Value> s0 = |
| global->Get(context.local(), v8_str("sym0")).ToLocalChecked(); |
| Local<Value> s0b = |
| global->Get(context.local(), v8_str("sym0b")).ToLocalChecked(); |
| Local<Value> s1 = |
| global->Get(context.local(), v8_str("sym1")).ToLocalChecked(); |
| Local<Value> s2 = |
| global->Get(context.local(), v8_str("sym2")).ToLocalChecked(); |
| Local<Value> s3 = |
| global->Get(context.local(), v8_str("sym3")).ToLocalChecked(); |
| Local<Value> s4 = |
| global->Get(context.local(), v8_str("sym4")).ToLocalChecked(); |
| CHECK(SameSymbol(sym0, Local<String>::Cast(s0))); |
| CHECK(SameSymbol(sym0b, Local<String>::Cast(s0b))); |
| CHECK(SameSymbol(sym1, Local<String>::Cast(s1))); |
| CHECK(SameSymbol(sym2, Local<String>::Cast(s2))); |
| CHECK(SameSymbol(sym3, Local<String>::Cast(s3))); |
| CHECK(SameSymbol(sym4, Local<String>::Cast(s4))); |
| } |
| |
| |
| THREADED_TEST(Utf16MissingTrailing) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| // Make sure it will go past the buffer, so it will call `WriteUtf16Slow` |
| int size = 1024 * 64; |
| uint8_t* buffer = new uint8_t[size]; |
| for (int i = 0; i < size; i += 4) { |
| buffer[i] = 0xF0; |
| buffer[i + 1] = 0x9D; |
| buffer[i + 2] = 0x80; |
| buffer[i + 3] = 0x9E; |
| } |
| |
| // Now invoke the decoder without last 3 bytes |
| v8::Local<v8::String> str = |
| v8::String::NewFromUtf8( |
| context->GetIsolate(), reinterpret_cast<char*>(buffer), |
| v8::NewStringType::kNormal, size - 3).ToLocalChecked(); |
| USE(str); |
| delete[] buffer; |
| } |
| |
| |
| THREADED_TEST(Utf16Trailing3Byte) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Make sure it will go past the buffer, so it will call `WriteUtf16Slow` |
| int size = 1024 * 63; |
| uint8_t* buffer = new uint8_t[size]; |
| for (int i = 0; i < size; i += 3) { |
| buffer[i] = 0xE2; |
| buffer[i + 1] = 0x80; |
| buffer[i + 2] = 0xA6; |
| } |
| |
| // Now invoke the decoder without last 3 bytes |
| v8::Local<v8::String> str = |
| v8::String::NewFromUtf8(isolate, reinterpret_cast<char*>(buffer), |
| v8::NewStringType::kNormal, size) |
| .ToLocalChecked(); |
| |
| v8::String::Value value(isolate, str); |
| CHECK_EQ(value.length(), size / 3); |
| CHECK_EQ((*value)[value.length() - 1], 0x2026); |
| |
| delete[] buffer; |
| } |
| |
| |
| THREADED_TEST(ToArrayIndex) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<String> str = v8_str("42"); |
| v8::MaybeLocal<v8::Uint32> index = str->ToArrayIndex(context.local()); |
| CHECK(!index.IsEmpty()); |
| CHECK_EQ(42.0, |
| index.ToLocalChecked()->Uint32Value(context.local()).FromJust()); |
| str = v8_str("42asdf"); |
| index = str->ToArrayIndex(context.local()); |
| CHECK(index.IsEmpty()); |
| str = v8_str("-42"); |
| index = str->ToArrayIndex(context.local()); |
| CHECK(index.IsEmpty()); |
| str = v8_str("4294967294"); |
| index = str->ToArrayIndex(context.local()); |
| CHECK(!index.IsEmpty()); |
| CHECK_EQ(4294967294.0, |
| index.ToLocalChecked()->Uint32Value(context.local()).FromJust()); |
| v8::Local<v8::Number> num = v8::Number::New(isolate, 1); |
| index = num->ToArrayIndex(context.local()); |
| CHECK(!index.IsEmpty()); |
| CHECK_EQ(1.0, |
| index.ToLocalChecked()->Uint32Value(context.local()).FromJust()); |
| num = v8::Number::New(isolate, -1); |
| index = num->ToArrayIndex(context.local()); |
| CHECK(index.IsEmpty()); |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| index = obj->ToArrayIndex(context.local()); |
| CHECK(index.IsEmpty()); |
| } |
| |
| |
| THREADED_TEST(ErrorConstruction) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| v8::Local<String> foo = v8_str("foo"); |
| v8::Local<String> message = v8_str("message"); |
| v8::Local<Value> range_error = v8::Exception::RangeError(foo); |
| CHECK(range_error->IsObject()); |
| CHECK(range_error.As<v8::Object>() |
| ->Get(context.local(), message) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo) |
| .FromJust()); |
| v8::Local<Value> reference_error = v8::Exception::ReferenceError(foo); |
| CHECK(reference_error->IsObject()); |
| CHECK(reference_error.As<v8::Object>() |
| ->Get(context.local(), message) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo) |
| .FromJust()); |
| v8::Local<Value> syntax_error = v8::Exception::SyntaxError(foo); |
| CHECK(syntax_error->IsObject()); |
| CHECK(syntax_error.As<v8::Object>() |
| ->Get(context.local(), message) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo) |
| .FromJust()); |
| v8::Local<Value> type_error = v8::Exception::TypeError(foo); |
| CHECK(type_error->IsObject()); |
| CHECK(type_error.As<v8::Object>() |
| ->Get(context.local(), message) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo) |
| .FromJust()); |
| v8::Local<Value> error = v8::Exception::Error(foo); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>() |
| ->Get(context.local(), message) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo) |
| .FromJust()); |
| } |
| |
| |
| static void ThrowV8Exception(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Local<String> foo = v8_str("foo"); |
| v8::Local<String> message = v8_str("message"); |
| v8::Local<Value> error = v8::Exception::Error(foo); |
| CHECK(error->IsObject()); |
| v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| CHECK(error.As<v8::Object>() |
| ->Get(context, message) |
| .ToLocalChecked() |
| ->Equals(context, foo) |
| .FromJust()); |
| info.GetIsolate()->ThrowException(error); |
| info.GetReturnValue().SetUndefined(); |
| } |
| |
| |
| THREADED_TEST(ExceptionCreateMessage) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Local<String> foo_str = v8_str("foo"); |
| v8::Local<String> message_str = v8_str("message"); |
| |
| context->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(true); |
| |
| Local<v8::FunctionTemplate> fun = |
| v8::FunctionTemplate::New(context->GetIsolate(), ThrowV8Exception); |
| v8::Local<v8::Object> global = context->Global(); |
| CHECK(global->Set(context.local(), v8_str("throwV8Exception"), |
| fun->GetFunction(context.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| TryCatch try_catch(context->GetIsolate()); |
| CompileRun( |
| "function f1() {\n" |
| " throwV8Exception();\n" |
| "};\n" |
| "f1();"); |
| CHECK(try_catch.HasCaught()); |
| |
| v8::Local<v8::Value> error = try_catch.Exception(); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>() |
| ->Get(context.local(), message_str) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo_str) |
| .FromJust()); |
| |
| v8::Local<v8::Message> message = |
| v8::Exception::CreateMessage(context->GetIsolate(), error); |
| CHECK(!message.IsEmpty()); |
| CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust()); |
| CHECK_EQ(2, message->GetStartColumn(context.local()).FromJust()); |
| |
| v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); |
| CHECK(!stackTrace.IsEmpty()); |
| CHECK_EQ(2, stackTrace->GetFrameCount()); |
| |
| stackTrace = v8::Exception::GetStackTrace(error); |
| CHECK(!stackTrace.IsEmpty()); |
| CHECK_EQ(2, stackTrace->GetFrameCount()); |
| |
| context->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(false); |
| |
| // Now check message location when SetCaptureStackTraceForUncaughtExceptions |
| // is false. |
| try_catch.Reset(); |
| |
| CompileRun( |
| "function f2() {\n" |
| " return throwV8Exception();\n" |
| "};\n" |
| "f2();"); |
| CHECK(try_catch.HasCaught()); |
| |
| error = try_catch.Exception(); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>() |
| ->Get(context.local(), message_str) |
| .ToLocalChecked() |
| ->Equals(context.local(), foo_str) |
| .FromJust()); |
| |
| message = v8::Exception::CreateMessage(context->GetIsolate(), error); |
| CHECK(!message.IsEmpty()); |
| CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust()); |
| CHECK_EQ(9, message->GetStartColumn(context.local()).FromJust()); |
| |
| // Should be empty stack trace. |
| stackTrace = message->GetStackTrace(); |
| CHECK(stackTrace.IsEmpty()); |
| CHECK(v8::Exception::GetStackTrace(error).IsEmpty()); |
| } |
| |
| |
| THREADED_TEST(ExceptionCreateMessageLength) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| // Test that the message is not truncated. |
| TryCatch try_catch(context->GetIsolate()); |
| CompileRun( |
| "var message = 'm';" |
| "while (message.length < 1000) message += message;" |
| "throw message;"); |
| CHECK(try_catch.HasCaught()); |
| |
| CHECK_LT(1000, try_catch.Message()->Get()->Length()); |
| } |
| |
| |
| static void YGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(10)); |
| } |
| |
| |
| static void YSetter(Local<String> name, |
| Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| Local<Object> this_obj = Local<Object>::Cast(info.This()); |
| v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| if (this_obj->Has(context, name).FromJust()) |
| this_obj->Delete(context, name).FromJust(); |
| CHECK(this_obj->Set(context, name, value).FromJust()); |
| } |
| |
| |
| THREADED_TEST(DeleteAccessor) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("y"), YGetter, YSetter); |
| LocalContext context; |
| v8::Local<v8::Object> holder = |
| obj->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("holder"), holder) |
| .FromJust()); |
| v8::Local<Value> result = |
| CompileRun("holder.y = 11; holder.y = 12; holder.y"); |
| CHECK_EQ(12u, result->Uint32Value(context.local()).FromJust()); |
| } |
| |
| |
| static int trouble_nesting = 0; |
| static void TroubleCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| trouble_nesting++; |
| |
| // Call a JS function that throws an uncaught exception. |
| Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| Local<v8::Object> arg_this = context->Global(); |
| Local<Value> trouble_callee = |
| (trouble_nesting == 3) |
| ? arg_this->Get(context, v8_str("trouble_callee")).ToLocalChecked() |
| : arg_this->Get(context, v8_str("trouble_caller")).ToLocalChecked(); |
| CHECK(trouble_callee->IsFunction()); |
| args.GetReturnValue().Set(Function::Cast(*trouble_callee) |
| ->Call(context, arg_this, 0, nullptr) |
| .FromMaybe(v8::Local<v8::Value>())); |
| } |
| |
| |
| static int report_count = 0; |
| static void ApiUncaughtExceptionTestListener(v8::Local<v8::Message>, |
| v8::Local<Value>) { |
| report_count++; |
| } |
| |
| |
| // Counts uncaught exceptions, but other tests running in parallel |
| // also have uncaught exceptions. |
| TEST(ApiUncaughtException) { |
| report_count = 0; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| isolate->AddMessageListener(ApiUncaughtExceptionTestListener); |
| |
| Local<v8::FunctionTemplate> fun = |
| v8::FunctionTemplate::New(isolate, TroubleCallback); |
| v8::Local<v8::Object> global = env->Global(); |
| CHECK(global->Set(env.local(), v8_str("trouble"), |
| fun->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| CompileRun( |
| "function trouble_callee() {" |
| " var x = null;" |
| " return x.foo;" |
| "};" |
| "function trouble_caller() {" |
| " trouble();" |
| "};"); |
| Local<Value> trouble = |
| global->Get(env.local(), v8_str("trouble")).ToLocalChecked(); |
| CHECK(trouble->IsFunction()); |
| Local<Value> trouble_callee = |
| global->Get(env.local(), v8_str("trouble_callee")).ToLocalChecked(); |
| CHECK(trouble_callee->IsFunction()); |
| Local<Value> trouble_caller = |
| global->Get(env.local(), v8_str("trouble_caller")).ToLocalChecked(); |
| CHECK(trouble_caller->IsFunction()); |
| Function::Cast(*trouble_caller) |
| ->Call(env.local(), global, 0, nullptr) |
| .FromMaybe(v8::Local<v8::Value>()); |
| CHECK_EQ(1, report_count); |
| isolate->RemoveMessageListeners(ApiUncaughtExceptionTestListener); |
| } |
| |
| |
| static const char* script_resource_name = "ExceptionInNativeScript.js"; |
| static void ExceptionInNativeScriptTestListener(v8::Local<v8::Message> message, |
| v8::Local<Value>) { |
| v8::Local<v8::Value> name_val = message->GetScriptOrigin().ResourceName(); |
| CHECK(!name_val.IsEmpty() && name_val->IsString()); |
| v8::String::Utf8Value name(v8::Isolate::GetCurrent(), |
| message->GetScriptOrigin().ResourceName()); |
| CHECK_EQ(0, strcmp(script_resource_name, *name)); |
| v8::Local<v8::Context> context = |
| v8::Isolate::GetCurrent()->GetCurrentContext(); |
| CHECK_EQ(3, message->GetLineNumber(context).FromJust()); |
| v8::String::Utf8Value source_line( |
| v8::Isolate::GetCurrent(), |
| message->GetSourceLine(context).ToLocalChecked()); |
| CHECK_EQ(0, strcmp(" new o.foo();", *source_line)); |
| } |
| |
| |
| TEST(ExceptionInNativeScript) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| isolate->AddMessageListener(ExceptionInNativeScriptTestListener); |
| |
| Local<v8::FunctionTemplate> fun = |
| v8::FunctionTemplate::New(isolate, TroubleCallback); |
| v8::Local<v8::Object> global = env->Global(); |
| CHECK(global->Set(env.local(), v8_str("trouble"), |
| fun->GetFunction(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| CompileRunWithOrigin( |
| "function trouble() {\n" |
| " var o = {};\n" |
| " new o.foo();\n" |
| "};", |
| script_resource_name); |
| Local<Value> trouble = |
| global->Get(env.local(), v8_str("trouble")).ToLocalChecked(); |
| CHECK(trouble->IsFunction()); |
| CHECK(Function::Cast(*trouble) |
| ->Call(env.local(), global, 0, nullptr) |
| .IsEmpty()); |
| isolate->RemoveMessageListeners(ExceptionInNativeScriptTestListener); |
| } |
| |
| |
| TEST(CompilationErrorUsingTryCatchHandler) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::TryCatch try_catch(env->GetIsolate()); |
| v8_compile("This doesn't &*&@#$&*^ compile."); |
| CHECK(*try_catch.Exception()); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| TEST(TryCatchFinallyUsingTryCatchHandler) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::TryCatch try_catch(env->GetIsolate()); |
| CompileRun("try { throw ''; } catch (e) {}"); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("try { throw ''; } finally {}"); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CompileRun( |
| "(function() {" |
| "try { throw ''; } finally { return; }" |
| "})()"); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun( |
| "(function()" |
| " { try { throw ''; } finally { throw 0; }" |
| "})()"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::HandleScope scope(args.GetIsolate()); |
| CompileRun(args[0] |
| ->ToString(args.GetIsolate()->GetCurrentContext()) |
| .ToLocalChecked()); |
| } |
| |
| |
| TEST(TryCatchFinallyStoresMessageUsingTryCatchHandler) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("CEvaluate"), |
| v8::FunctionTemplate::New(isolate, CEvaluate)); |
| LocalContext context(0, templ); |
| v8::TryCatch try_catch(isolate); |
| CompileRun("try {" |
| " CEvaluate('throw 1;');" |
| "} finally {" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(!try_catch.Message().IsEmpty()); |
| String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp(*exception_value, "1")); |
| try_catch.Reset(); |
| CompileRun("try {" |
| " CEvaluate('throw 1;');" |
| "} finally {" |
| " throw 2;" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(!try_catch.Message().IsEmpty()); |
| String::Utf8Value finally_exception_value(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp(*finally_exception_value, "2")); |
| } |
| |
| |
| // For use within the TestSecurityHandler() test. |
| static bool g_security_callback_result = false; |
| static bool SecurityTestCallback(Local<v8::Context> accessing_context, |
| Local<v8::Object> accessed_object, |
| Local<v8::Value> data) { |
| printf("a\n"); |
| CHECK(!data.IsEmpty() && data->IsInt32()); |
| CHECK_EQ(42, data->Int32Value(accessing_context).FromJust()); |
| return g_security_callback_result; |
| } |
| |
| |
| // SecurityHandler can't be run twice |
| TEST(SecurityHandler) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope0(isolate); |
| v8::Local<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| global_template->SetAccessCheckCallback(SecurityTestCallback, v8_num(42)); |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| context0->Enter(); |
| |
| v8::Local<v8::Object> global0 = context0->Global(); |
| v8::Local<Script> script0 = v8_compile("foo = 111"); |
| script0->Run(context0).ToLocalChecked(); |
| CHECK(global0->Set(context0, v8_str("0"), v8_num(999)).FromJust()); |
| v8::Local<Value> foo0 = |
| global0->Get(context0, v8_str("foo")).ToLocalChecked(); |
| CHECK_EQ(111, foo0->Int32Value(context0).FromJust()); |
| v8::Local<Value> z0 = global0->Get(context0, v8_str("0")).ToLocalChecked(); |
| CHECK_EQ(999, z0->Int32Value(context0).FromJust()); |
| |
| // Create another environment, should fail security checks. |
| v8::HandleScope scope1(isolate); |
| |
| v8::Local<Context> context1 = Context::New(isolate, nullptr, global_template); |
| context1->Enter(); |
| |
| v8::Local<v8::Object> global1 = context1->Global(); |
| global1->Set(context1, v8_str("othercontext"), global0).FromJust(); |
| // This set will fail the security check. |
| v8::Local<Script> script1 = |
| v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); |
| CHECK(script1->Run(context1).IsEmpty()); |
| g_security_callback_result = true; |
| // This read will pass the security check. |
| v8::Local<Value> foo1 = |
| global0->Get(context1, v8_str("foo")).ToLocalChecked(); |
| CHECK_EQ(111, foo1->Int32Value(context0).FromJust()); |
| // This read will pass the security check. |
| v8::Local<Value> z1 = global0->Get(context1, v8_str("0")).ToLocalChecked(); |
| CHECK_EQ(999, z1->Int32Value(context1).FromJust()); |
| |
| // Create another environment, should pass security checks. |
| { |
| v8::HandleScope scope2(isolate); |
| LocalContext context2; |
| v8::Local<v8::Object> global2 = context2->Global(); |
| CHECK(global2->Set(context2.local(), v8_str("othercontext"), global0) |
| .FromJust()); |
| v8::Local<Script> script2 = |
| v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); |
| script2->Run(context2.local()).ToLocalChecked(); |
| v8::Local<Value> foo2 = |
| global0->Get(context2.local(), v8_str("foo")).ToLocalChecked(); |
| CHECK_EQ(333, foo2->Int32Value(context2.local()).FromJust()); |
| v8::Local<Value> z2 = |
| global0->Get(context2.local(), v8_str("0")).ToLocalChecked(); |
| CHECK_EQ(888, z2->Int32Value(context2.local()).FromJust()); |
| } |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| THREADED_TEST(SecurityChecks) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| |
| // Create a function in env1. |
| CompileRun("spy=function(){return spy;}"); |
| Local<Value> spy = |
| env1->Global()->Get(env1.local(), v8_str("spy")).ToLocalChecked(); |
| CHECK(spy->IsFunction()); |
| |
| // Create another function accessing global objects. |
| CompileRun("spy2=function(){return new this.Array();}"); |
| Local<Value> spy2 = |
| env1->Global()->Get(env1.local(), v8_str("spy2")).ToLocalChecked(); |
| CHECK(spy2->IsFunction()); |
| |
| // Switch to env2 in the same domain and invoke spy on env2. |
| { |
| env2->SetSecurityToken(foo); |
| // Enter env2 |
| Context::Scope scope_env2(env2); |
| Local<Value> result = Function::Cast(*spy) |
| ->Call(env2, env2->Global(), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(result->IsFunction()); |
| } |
| |
| { |
| env2->SetSecurityToken(bar); |
| Context::Scope scope_env2(env2); |
| |
| // Call cross_domain_call, it should throw an exception |
| v8::TryCatch try_catch(env1->GetIsolate()); |
| CHECK(Function::Cast(*spy2) |
| ->Call(env2, env2->Global(), 0, nullptr) |
| .IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| |
| // Regression test case for issue 1183439. |
| THREADED_TEST(SecurityChecksForPrototypeChain) { |
| LocalContext current; |
| v8::HandleScope scope(current->GetIsolate()); |
| v8::Local<Context> other = Context::New(current->GetIsolate()); |
| |
| // Change context to be able to get to the Object function in the |
| // other context without hitting the security checks. |
| v8::Local<Value> other_object; |
| { Context::Scope scope(other); |
| other_object = |
| other->Global()->Get(other, v8_str("Object")).ToLocalChecked(); |
| CHECK(other->Global()->Set(other, v8_num(42), v8_num(87)).FromJust()); |
| } |
| |
| CHECK(current->Global() |
| ->Set(current.local(), v8_str("other"), other->Global()) |
| .FromJust()); |
| CHECK(v8_compile("other") |
| ->Run(current.local()) |
| .ToLocalChecked() |
| ->Equals(current.local(), other->Global()) |
| .FromJust()); |
| |
| // Make sure the security check fails here and we get an undefined |
| // result instead of getting the Object function. Repeat in a loop |
| // to make sure to exercise the IC code. |
| v8::Local<Script> access_other0 = v8_compile("other.Object"); |
| v8::Local<Script> access_other1 = v8_compile("other[42]"); |
| for (int i = 0; i < 5; i++) { |
| CHECK(access_other0->Run(current.local()).IsEmpty()); |
| CHECK(access_other1->Run(current.local()).IsEmpty()); |
| } |
| |
| // Create an object that has 'other' in its prototype chain and make |
| // sure we cannot access the Object function indirectly through |
| // that. Repeat in a loop to make sure to exercise the IC code. |
| v8_compile( |
| "function F() { };" |
| "F.prototype = other;" |
| "var f = new F();") |
| ->Run(current.local()) |
| .ToLocalChecked(); |
| v8::Local<Script> access_f0 = v8_compile("f.Object"); |
| v8::Local<Script> access_f1 = v8_compile("f[42]"); |
| for (int j = 0; j < 5; j++) { |
| CHECK(access_f0->Run(current.local()).IsEmpty()); |
| CHECK(access_f1->Run(current.local()).IsEmpty()); |
| } |
| |
| // Now it gets hairy: Set the prototype for the other global object |
| // to be the current global object. The prototype chain for 'f' now |
| // goes through 'other' but ends up in the current global object. |
| { Context::Scope scope(other); |
| CHECK(other->Global() |
| ->Set(other, v8_str("__proto__"), current->Global()) |
| .FromJust()); |
| } |
| // Set a named and an index property on the current global |
| // object. To force the lookup to go through the other global object, |
| // the properties must not exist in the other global object. |
| CHECK(current->Global() |
| ->Set(current.local(), v8_str("foo"), v8_num(100)) |
| .FromJust()); |
| CHECK(current->Global() |
| ->Set(current.local(), v8_num(99), v8_num(101)) |
| .FromJust()); |
| // Try to read the properties from f and make sure that the access |
| // gets stopped by the security checks on the other global object. |
| Local<Script> access_f2 = v8_compile("f.foo"); |
| Local<Script> access_f3 = v8_compile("f[99]"); |
| for (int k = 0; k < 5; k++) { |
| CHECK(access_f2->Run(current.local()).IsEmpty()); |
| CHECK(access_f3->Run(current.local()).IsEmpty()); |
| } |
| } |
| |
| |
| static bool security_check_with_gc_called; |
| |
| static bool SecurityTestCallbackWithGC(Local<v8::Context> accessing_context, |
| Local<v8::Object> accessed_object, |
| Local<v8::Value> data) { |
| CcTest::CollectAllGarbage(); |
| security_check_with_gc_called = true; |
| return true; |
| } |
| |
| |
| TEST(SecurityTestGCAllowed) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::ObjectTemplate> object_template = |
| v8::ObjectTemplate::New(isolate); |
| object_template->SetAccessCheckCallback(SecurityTestCallbackWithGC); |
| |
| v8::Local<Context> context = Context::New(isolate); |
| v8::Context::Scope context_scope(context); |
| |
| CHECK(context->Global() |
| ->Set(context, v8_str("obj"), |
| object_template->NewInstance(context).ToLocalChecked()) |
| .FromJust()); |
| |
| security_check_with_gc_called = false; |
| CompileRun("obj[0] = new String(1002);"); |
| CHECK(security_check_with_gc_called); |
| |
| security_check_with_gc_called = false; |
| CHECK(CompileRun("obj[0]") |
| ->ToString(context) |
| .ToLocalChecked() |
| ->Equals(context, v8_str("1002")) |
| .FromJust()); |
| CHECK(security_check_with_gc_called); |
| } |
| |
| |
| THREADED_TEST(CrossDomainDelete) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| CHECK( |
| env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust()); |
| CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust()); |
| |
| // Change env2 to a different domain and delete env1.prop. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = |
| CompileRun("delete env1.prop"); |
| CHECK(result.IsEmpty()); |
| } |
| |
| // Check that env1.prop still exists. |
| Local<Value> v = |
| env1->Global()->Get(env1.local(), v8_str("prop")).ToLocalChecked(); |
| CHECK(v->IsNumber()); |
| CHECK_EQ(3, v->Int32Value(env1.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(CrossDomainPropertyIsEnumerable) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| CHECK( |
| env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust()); |
| CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust()); |
| |
| // env1.prop is enumerable in env2. |
| Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun(test); |
| CHECK(result->IsTrue()); |
| } |
| |
| // Change env2 to a different domain and test again. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun(test); |
| CHECK(result.IsEmpty()); |
| } |
| } |
| |
| |
| THREADED_TEST(CrossDomainFor) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| CHECK( |
| env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust()); |
| CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust()); |
| |
| // Change env2 to a different domain and set env1's global object |
| // as the __proto__ of an object in env2 and enumerate properties |
| // in for-in. It shouldn't enumerate properties on env1's global |
| // object. It shouldn't throw either, just silently ignore them. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun( |
| "(function() {" |
| " try {" |
| " for (var p in env1) {" |
| " if (p == 'prop') return false;" |
| " }" |
| " return true;" |
| " } catch (e) {" |
| " return false;" |
| " }" |
| "})()"); |
| CHECK(result->IsTrue()); |
| } |
| } |
| |
| |
| THREADED_TEST(CrossDomainForInOnPrototype) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| CHECK( |
| env1->Global()->Set(env1.local(), v8_str("prop"), v8_num(3)).FromJust()); |
| CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust()); |
| |
| // Change env2 to a different domain and set env1's global object |
| // as the __proto__ of an object in env2 and enumerate properties |
| // in for-in. It shouldn't enumerate properties on env1's global |
| // object. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun( |
| "(function() {" |
| " var obj = { '__proto__': env1 };" |
| " try {" |
| " for (var p in obj) {" |
| " if (p == 'prop') return false;" |
| " }" |
| " return true;" |
| " } catch (e) {" |
| " return false;" |
| " }" |
| "})()"); |
| CHECK(result->IsTrue()); |
| } |
| } |
| |
| |
| TEST(ContextDetachGlobal) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| |
| Local<Value> foo = v8_str("foo"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| // Enter env2 |
| env2->Enter(); |
| |
| // Create a function in env2 and add a reference to it in env1. |
| Local<v8::Object> global2 = env2->Global(); |
| CHECK(global2->Set(env2, v8_str("prop"), |
| v8::Integer::New(env2->GetIsolate(), 1)) |
| .FromJust()); |
| CompileRun("function getProp() {return prop;}"); |
| |
| CHECK(env1->Global() |
| ->Set(env1.local(), v8_str("getProp"), |
| global2->Get(env2, v8_str("getProp")).ToLocalChecked()) |
| .FromJust()); |
| |
| // Detach env2's global, and reuse the global object of env2 |
| env2->Exit(); |
| env2->DetachGlobal(); |
| |
| v8::Local<Context> env3 = Context::New( |
| env1->GetIsolate(), 0, v8::Local<v8::ObjectTemplate>(), global2); |
| env3->SetSecurityToken(v8_str("bar")); |
| |
| env3->Enter(); |
| Local<v8::Object> global3 = env3->Global(); |
| CHECK(global2->Equals(env3, global3).FromJust()); |
| CHECK(global3->Get(env3, v8_str("prop")).ToLocalChecked()->IsUndefined()); |
| CHECK(global3->Get(env3, v8_str("getProp")).ToLocalChecked()->IsUndefined()); |
| CHECK(global3->Set(env3, v8_str("prop"), |
| v8::Integer::New(env3->GetIsolate(), -1)) |
| .FromJust()); |
| CHECK(global3->Set(env3, v8_str("prop2"), |
| v8::Integer::New(env3->GetIsolate(), 2)) |
| .FromJust()); |
| env3->Exit(); |
| |
| // Call getProp in env1, and it should return the value 1 |
| { |
| Local<v8::Object> global1 = env1->Global(); |
| Local<Value> get_prop = |
| global1->Get(env1.local(), v8_str("getProp")).ToLocalChecked(); |
| CHECK(get_prop->IsFunction()); |
| v8::TryCatch try_catch(env1->GetIsolate()); |
| Local<Value> r = Function::Cast(*get_prop) |
| ->Call(env1.local(), global1, 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(1, r->Int32Value(env1.local()).FromJust()); |
| } |
| |
| // Check that env3 is not accessible from env1 |
| { |
| v8::MaybeLocal<Value> r = global3->Get(env1.local(), v8_str("prop2")); |
| CHECK(r.IsEmpty()); |
| } |
| } |
| |
| |
| TEST(DetachGlobal) { |
| LocalContext env1; |
| v8::HandleScope scope(env1->GetIsolate()); |
| |
| // Create second environment. |
| v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| |
| // Set same security token for env1 and env2. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| // Create a property on the global object in env2. |
| { |
| v8::Context::Scope scope(env2); |
| CHECK(env2->Global() |
| ->Set(env2, v8_str("p"), v8::Integer::New(env2->GetIsolate(), 42)) |
| .FromJust()); |
| } |
| |
| // Create a reference to env2 global from env1 global. |
| CHECK(env1->Global() |
| ->Set(env1.local(), v8_str("other"), env2->Global()) |
| .FromJust()); |
| |
| // Check that we have access to other.p in env2 from env1. |
| Local<Value> result = CompileRun("other.p"); |
| CHECK(result->IsInt32()); |
| CHECK_EQ(42, result->Int32Value(env1.local()).FromJust()); |
| |
| // Hold on to global from env2 and detach global from env2. |
| Local<v8::Object> global2 = env2->Global(); |
| env2->DetachGlobal(); |
| |
| // Check that the global has been detached. No other.p property can |
| // be found. |
| result = CompileRun("other.p"); |
| CHECK(result.IsEmpty()); |
| |
| // Reuse global2 for env3. |
| v8::Local<Context> env3 = Context::New( |
| env1->GetIsolate(), 0, v8::Local<v8::ObjectTemplate>(), global2); |
| CHECK(global2->Equals(env1.local(), env3->Global()).FromJust()); |
| |
| // Start by using the same security token for env3 as for env1 and env2. |
| env3->SetSecurityToken(foo); |
| |
| // Create a property on the global object in env3. |
| { |
| v8::Context::Scope scope(env3); |
| CHECK(env3->Global() |
| ->Set(env3, v8_str("p"), v8::Integer::New(env3->GetIsolate(), 24)) |
| .FromJust()); |
| } |
| |
| // Check that other.p is now the property in env3 and that we have access. |
| result = CompileRun("other.p"); |
| CHECK(result->IsInt32()); |
| CHECK_EQ(24, result->Int32Value(env3).FromJust()); |
| } |
| |
| |
| void GetThisX(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| info.GetReturnValue().Set( |
| context->Global()->Get(context, v8_str("x")).ToLocalChecked()); |
| } |
| |
| |
| TEST(DetachedAccesses) { |
| LocalContext env1; |
| v8::HandleScope scope(env1->GetIsolate()); |
| |
| // Create second environment. |
| Local<ObjectTemplate> inner_global_template = |
| FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate(); |
| inner_global_template ->SetAccessorProperty( |
| v8_str("this_x"), FunctionTemplate::New(env1->GetIsolate(), GetThisX)); |
| v8::Local<Context> env2 = |
| Context::New(env1->GetIsolate(), nullptr, inner_global_template); |
| |
| Local<Value> foo = v8_str("foo"); |
| |
| // Set same security token for env1 and env2. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| CHECK(env1->Global() |
| ->Set(env1.local(), v8_str("x"), v8_str("env1_x")) |
| .FromJust()); |
| |
| { |
| v8::Context::Scope scope(env2); |
| CHECK(env2->Global()->Set(env2, v8_str("x"), v8_str("env2_x")).FromJust()); |
| CompileRun( |
| "function bound_x() { return x; }" |
| "function get_x() { return this.x; }" |
| "function get_x_w() { return (function() {return this.x;})(); }"); |
| CHECK(env1->Global() |
| ->Set(env1.local(), v8_str("bound_x"), CompileRun("bound_x")) |
| .FromJust()); |
| CHECK(env1->Global() |
| ->Set(env1.local(), v8_str("get_x"), CompileRun("get_x")) |
| .FromJust()); |
| CHECK(env1->Global() |
| ->Set(env1.local(), v8_str("get_x_w"), CompileRun("get_x_w")) |
| .FromJust()); |
| env1->Global() |
| ->Set(env1.local(), v8_str("this_x"), |
| CompileRun("Object.getOwnPropertyDescriptor(this, 'this_x').get")) |
| .FromJust(); |
| } |
| |
| Local<Object> env2_global = env2->Global(); |
| env2->DetachGlobal(); |
| |
| Local<Value> result; |
| result = CompileRun("bound_x()"); |
| CHECK(v8_str("env2_x")->Equals(env1.local(), result).FromJust()); |
| result = CompileRun("get_x()"); |
| CHECK(result.IsEmpty()); |
| result = CompileRun("get_x_w()"); |
| CHECK(result.IsEmpty()); |
| result = CompileRun("this_x()"); |
| CHECK(v8_str("env2_x")->Equals(env1.local(), result).FromJust()); |
| |
| // Reattach env2's proxy |
| env2 = Context::New(env1->GetIsolate(), 0, v8::Local<v8::ObjectTemplate>(), |
| env2_global); |
| env2->SetSecurityToken(foo); |
| { |
| v8::Context::Scope scope(env2); |
| CHECK(env2->Global()->Set(env2, v8_str("x"), v8_str("env3_x")).FromJust()); |
| CHECK(env2->Global()->Set(env2, v8_str("env1"), env1->Global()).FromJust()); |
| result = CompileRun( |
| "results = [];" |
| "for (var i = 0; i < 4; i++ ) {" |
| " results.push(env1.bound_x());" |
| " results.push(env1.get_x());" |
| " results.push(env1.get_x_w());" |
| " results.push(env1.this_x());" |
| "}" |
| "results"); |
| Local<v8::Array> results = Local<v8::Array>::Cast(result); |
| CHECK_EQ(16u, results->Length()); |
| for (int i = 0; i < 16; i += 4) { |
| CHECK(v8_str("env2_x") |
| ->Equals(env2, results->Get(env2, i + 0).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env1_x") |
| ->Equals(env2, results->Get(env2, i + 1).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env3_x") |
| ->Equals(env2, results->Get(env2, i + 2).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env2_x") |
| ->Equals(env2, results->Get(env2, i + 3).ToLocalChecked()) |
| .FromJust()); |
| } |
| } |
| |
| result = CompileRun( |
| "results = [];" |
| "for (var i = 0; i < 4; i++ ) {" |
| " results.push(bound_x());" |
| " results.push(get_x());" |
| " results.push(get_x_w());" |
| " results.push(this_x());" |
| "}" |
| "results"); |
| Local<v8::Array> results = Local<v8::Array>::Cast(result); |
| CHECK_EQ(16u, results->Length()); |
| for (int i = 0; i < 16; i += 4) { |
| CHECK(v8_str("env2_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 0).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env3_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 1).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env3_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 2).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env2_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 3).ToLocalChecked()) |
| .FromJust()); |
| } |
| |
| result = CompileRun( |
| "results = [];" |
| "for (var i = 0; i < 4; i++ ) {" |
| " results.push(this.bound_x());" |
| " results.push(this.get_x());" |
| " results.push(this.get_x_w());" |
| " results.push(this.this_x());" |
| "}" |
| "results"); |
| results = Local<v8::Array>::Cast(result); |
| CHECK_EQ(16u, results->Length()); |
| for (int i = 0; i < 16; i += 4) { |
| CHECK(v8_str("env2_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 0).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env1_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 1).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env3_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 2).ToLocalChecked()) |
| .FromJust()); |
| CHECK(v8_str("env2_x") |
| ->Equals(env1.local(), |
| results->Get(env1.local(), i + 3).ToLocalChecked()) |
| .FromJust()); |
| } |
| } |
| |
| |
| static bool allowed_access = false; |
| static bool AccessBlocker(Local<v8::Context> accessing_context, |
| Local<v8::Object> accessed_object, |
| Local<v8::Value> data) { |
| v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| return context->Global()->Equals(context, accessed_object).FromJust() || |
| allowed_access; |
| } |
| |
| |
| static int g_echo_value = -1; |
| |
| |
| static void EchoGetter( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(v8_num(g_echo_value)); |
| } |
| |
| |
| static void EchoSetter(Local<String> name, Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& args) { |
| if (value->IsNumber()) |
| g_echo_value = |
| value->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust(); |
| } |
| |
| |
| static void UnreachableGetter( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| UNREACHABLE(); // This function should not be called.. |
| } |
| |
| |
| static void UnreachableSetter(Local<String>, |
| Local<Value>, |
| const v8::PropertyCallbackInfo<void>&) { |
| UNREACHABLE(); // This function should not be called. |
| } |
| |
| |
| static void UnreachableFunction( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| UNREACHABLE(); // This function should not be called.. |
| } |
| |
| |
| TEST(AccessControl) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| |
| global_template->SetAccessCheckCallback(AccessBlocker); |
| |
| // Add an accessor accessible by cross-domain JS code. |
| global_template->SetAccessor( |
| v8_str("accessible_prop"), EchoGetter, EchoSetter, v8::Local<Value>(), |
| v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| |
| |
| // Add an accessor that is not accessible by cross-domain JS code. |
| global_template->SetAccessor(v8_str("blocked_prop"), UnreachableGetter, |
| UnreachableSetter, v8::Local<Value>(), |
| v8::DEFAULT); |
| |
| global_template->SetAccessorProperty( |
| v8_str("blocked_js_prop"), |
| v8::FunctionTemplate::New(isolate, UnreachableFunction), |
| v8::FunctionTemplate::New(isolate, UnreachableFunction), |
| v8::None, |
| v8::DEFAULT); |
| |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| context0->Enter(); |
| |
| v8::Local<v8::Object> global0 = context0->Global(); |
| |
| // Define a property with JS getter and setter. |
| CompileRun( |
| "function getter() { return 'getter'; };\n" |
| "function setter() { return 'setter'; }\n" |
| "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})"); |
| |
| Local<Value> getter = |
| global0->Get(context0, v8_str("getter")).ToLocalChecked(); |
| Local<Value> setter = |
| global0->Get(context0, v8_str("setter")).ToLocalChecked(); |
| |
| // And define normal element. |
| CHECK(global0->Set(context0, 239, v8_str("239")).FromJust()); |
| |
| // Define an element with JS getter and setter. |
| CompileRun( |
| "function el_getter() { return 'el_getter'; };\n" |
| "function el_setter() { return 'el_setter'; };\n" |
| "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});"); |
| |
| Local<Value> el_getter = |
| global0->Get(context0, v8_str("el_getter")).ToLocalChecked(); |
| Local<Value> el_setter = |
| global0->Get(context0, v8_str("el_setter")).ToLocalChecked(); |
| |
| v8::HandleScope scope1(isolate); |
| |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| v8::Local<v8::Object> global1 = context1->Global(); |
| CHECK(global1->Set(context1, v8_str("other"), global0).FromJust()); |
| |
| // Access blocked property. |
| CompileRun("other.blocked_prop = 1"); |
| |
| CHECK(CompileRun("other.blocked_prop").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')") |
| .IsEmpty()); |
| CHECK( |
| CompileRun("propertyIsEnumerable.call(other, 'blocked_prop')").IsEmpty()); |
| |
| // Access blocked element. |
| CHECK(CompileRun("other[239] = 1").IsEmpty()); |
| |
| CHECK(CompileRun("other[239]").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '239')").IsEmpty()); |
| CHECK(CompileRun("propertyIsEnumerable.call(other, '239')").IsEmpty()); |
| |
| allowed_access = true; |
| // Now we can enumerate the property. |
| ExpectTrue("propertyIsEnumerable.call(other, '239')"); |
| allowed_access = false; |
| |
| // Access a property with JS accessor. |
| CHECK(CompileRun("other.js_accessor_p = 2").IsEmpty()); |
| |
| CHECK(CompileRun("other.js_accessor_p").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'js_accessor_p')") |
| .IsEmpty()); |
| |
| allowed_access = true; |
| |
| ExpectString("other.js_accessor_p", "getter"); |
| ExpectObject( |
| "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); |
| ExpectObject( |
| "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); |
| ExpectUndefined( |
| "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); |
| |
| allowed_access = false; |
| |
| // Access an element with JS accessor. |
| CHECK(CompileRun("other[42] = 2").IsEmpty()); |
| |
| CHECK(CompileRun("other[42]").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '42')").IsEmpty()); |
| |
| allowed_access = true; |
| |
| ExpectString("other[42]", "el_getter"); |
| ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); |
| ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); |
| ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); |
| |
| allowed_access = false; |
| |
| v8::Local<Value> value; |
| |
| // Access accessible property |
| value = CompileRun("other.accessible_prop = 3"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(3, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(3, g_echo_value); |
| |
| value = CompileRun("other.accessible_prop"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(3, value->Int32Value(context1).FromJust()); |
| |
| value = CompileRun( |
| "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(3, value->Int32Value(context1).FromJust()); |
| |
| value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')"); |
| CHECK(value->IsTrue()); |
| |
| // Enumeration doesn't enumerate accessors from inaccessible objects in |
| // the prototype chain even if the accessors are in themselves accessible. |
| // Enumeration doesn't throw, it silently ignores what it can't access. |
| value = CompileRun( |
| "(function() {" |
| " var obj = { '__proto__': other };" |
| " try {" |
| " for (var p in obj) {" |
| " if (p == 'accessible_prop' ||" |
| " p == 'blocked_js_prop' ||" |
| " p == 'blocked_js_prop') {" |
| " return false;" |
| " }" |
| " }" |
| " return true;" |
| " } catch (e) {" |
| " return false;" |
| " }" |
| "})()"); |
| CHECK(value->IsTrue()); |
| |
| // Test that preventExtensions fails on a non-accessible object even if that |
| // object is already non-extensible. |
| CHECK(global1->Set(context1, v8_str("checked_object"), |
| global_template->NewInstance(context1).ToLocalChecked()) |
| .FromJust()); |
| allowed_access = true; |
| CompileRun("Object.preventExtensions(checked_object)"); |
| ExpectFalse("Object.isExtensible(checked_object)"); |
| allowed_access = false; |
| CHECK(CompileRun("Object.preventExtensions(checked_object)").IsEmpty()); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| TEST(AccessControlES5) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| |
| global_template->SetAccessCheckCallback(AccessBlocker); |
| |
| // Add accessible accessor. |
| global_template->SetAccessor( |
| v8_str("accessible_prop"), EchoGetter, EchoSetter, v8::Local<Value>(), |
| v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| |
| |
| // Add an accessor that is not accessible by cross-domain JS code. |
| global_template->SetAccessor(v8_str("blocked_prop"), UnreachableGetter, |
| UnreachableSetter, v8::Local<Value>(), |
| v8::DEFAULT); |
| |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| context0->Enter(); |
| |
| v8::Local<v8::Object> global0 = context0->Global(); |
| |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| v8::Local<v8::Object> global1 = context1->Global(); |
| CHECK(global1->Set(context1, v8_str("other"), global0).FromJust()); |
| |
| // Regression test for issue 1154. |
| CHECK(CompileRun("Object.keys(other).length == 1") |
| ->BooleanValue(context1) |
| .FromJust()); |
| CHECK(CompileRun("Object.keys(other)[0] == 'accessible_prop'") |
| ->BooleanValue(context1) |
| .FromJust()); |
| CHECK(CompileRun("other.blocked_prop").IsEmpty()); |
| |
| // Regression test for issue 1027. |
| CompileRun("Object.defineProperty(\n" |
| " other, 'blocked_prop', {configurable: false})"); |
| CHECK(CompileRun("other.blocked_prop").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')") |
| .IsEmpty()); |
| |
| // Regression test for issue 1171. |
| ExpectTrue("Object.isExtensible(other)"); |
| CompileRun("Object.preventExtensions(other)"); |
| ExpectTrue("Object.isExtensible(other)"); |
| |
| // Object.seal and Object.freeze. |
| CompileRun("Object.freeze(other)"); |
| ExpectTrue("Object.isExtensible(other)"); |
| |
| CompileRun("Object.seal(other)"); |
| ExpectTrue("Object.isExtensible(other)"); |
| |
| // Regression test for issue 1250. |
| // Make sure that we can set the accessible accessors value using normal |
| // assignment. |
| CompileRun("other.accessible_prop = 42"); |
| CHECK_EQ(42, g_echo_value); |
| |
| // [[DefineOwnProperty]] always throws for access-checked objects. |
| CHECK( |
| CompileRun("Object.defineProperty(other, 'accessible_prop', {value: 43})") |
| .IsEmpty()); |
| CHECK(CompileRun("other.accessible_prop == 42")->IsTrue()); |
| CHECK_EQ(42, g_echo_value); // Make sure we didn't call the setter. |
| } |
| |
| static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context, |
| Local<v8::Object> global, |
| Local<v8::Value> data) { |
| i::PrintF("Access blocked.\n"); |
| return false; |
| } |
| |
| static bool AccessAlwaysAllowed(Local<v8::Context> accessing_context, |
| Local<v8::Object> global, |
| Local<v8::Value> data) { |
| i::PrintF("Access allowed.\n"); |
| return true; |
| } |
| |
| THREADED_TEST(AccessControlGetOwnPropertyNames) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); |
| |
| obj_template->Set(v8_str("x"), v8::Integer::New(isolate, 42)); |
| obj_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| |
| // Add an accessor accessible by cross-domain JS code. |
| obj_template->SetAccessor( |
| v8_str("accessible_prop"), EchoGetter, EchoSetter, v8::Local<Value>(), |
| v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, nullptr, obj_template); |
| context0->Enter(); |
| |
| v8::Local<v8::Object> global0 = context0->Global(); |
| |
| v8::HandleScope scope1(CcTest::isolate()); |
| |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| v8::Local<v8::Object> global1 = context1->Global(); |
| CHECK(global1->Set(context1, v8_str("other"), global0).FromJust()); |
| CHECK(global1->Set(context1, v8_str("object"), |
| obj_template->NewInstance(context1).ToLocalChecked()) |
| .FromJust()); |
| |
| v8::Local<Value> value; |
| |
| // Attempt to get the property names of the other global object and |
| // of an object that requires access checks. Accessing the other |
| // global object should be blocked by access checks on the global |
| // proxy object. Accessing the object that requires access checks |
| // is blocked by the access checks on the object itself. |
| value = CompileRun( |
| "var names = Object.getOwnPropertyNames(other);" |
| "names.length == 1 && names[0] == 'accessible_prop';"); |
| CHECK(value->BooleanValue(context1).FromJust()); |
| |
| value = CompileRun( |
| "var names = Object.getOwnPropertyNames(object);" |
| "names.length == 1 && names[0] == 'accessible_prop';"); |
| CHECK(value->BooleanValue(context1).FromJust()); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| TEST(Regress470113) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); |
| obj_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| LocalContext env; |
| CHECK(env->Global() |
| ->Set(env.local(), v8_str("prohibited"), |
| obj_template->NewInstance(env.local()).ToLocalChecked()) |
| .FromJust()); |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "'use strict';\n" |
| "class C extends Object {\n" |
| " m() { super.powned = 'Powned!'; }\n" |
| "}\n" |
| "let c = new C();\n" |
| "c.m.call(prohibited)"); |
| |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| |
| static void ConstTenGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(v8_num(10)); |
| } |
| |
| |
| THREADED_TEST(CrossDomainAccessors) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::FunctionTemplate> func_template = |
| v8::FunctionTemplate::New(isolate); |
| |
| v8::Local<v8::ObjectTemplate> global_template = |
| func_template->InstanceTemplate(); |
| |
| v8::Local<v8::ObjectTemplate> proto_template = |
| func_template->PrototypeTemplate(); |
| |
| // Add an accessor to proto that's accessible by cross-domain JS code. |
| proto_template->SetAccessor(v8_str("accessible"), ConstTenGetter, 0, |
| v8::Local<Value>(), v8::ALL_CAN_READ); |
| |
| // Add an accessor that is not accessible by cross-domain JS code. |
| global_template->SetAccessor(v8_str("unreachable"), UnreachableGetter, 0, |
| v8::Local<Value>(), v8::DEFAULT); |
| |
| v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| context0->Enter(); |
| |
| Local<v8::Object> global = context0->Global(); |
| // Add a normal property that shadows 'accessible' |
| CHECK(global->Set(context0, v8_str("accessible"), v8_num(11)).FromJust()); |
| |
| // Enter a new context. |
| v8::HandleScope scope1(CcTest::isolate()); |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| v8::Local<v8::Object> global1 = context1->Global(); |
| CHECK(global1->Set(context1, v8_str("other"), global).FromJust()); |
| |
| // Should return 10, instead of 11 |
| v8::Local<Value> value = |
| v8_compile("other.accessible")->Run(context1).ToLocalChecked(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(10, value->Int32Value(context1).FromJust()); |
| |
| v8::MaybeLocal<v8::Value> maybe_value = |
| v8_compile("other.unreachable")->Run(context1); |
| CHECK(maybe_value.IsEmpty()); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| static int access_count = 0; |
| |
| static bool AccessCounter(Local<v8::Context> accessing_context, |
| Local<v8::Object> accessed_object, |
| Local<v8::Value> data) { |
| access_count++; |
| return true; |
| } |
| |
| |
| // This one is too easily disturbed by other tests. |
| TEST(AccessControlIC) { |
| access_count = 0; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| // Create an environment. |
| v8::Local<Context> context0 = Context::New(isolate); |
| context0->Enter(); |
| |
| // Create an object that requires access-check functions to be |
| // called for cross-domain access. |
| v8::Local<v8::ObjectTemplate> object_template = |
| v8::ObjectTemplate::New(isolate); |
| object_template->SetAccessCheckCallback(AccessCounter); |
| Local<v8::Object> object = |
| object_template->NewInstance(context0).ToLocalChecked(); |
| |
| v8::HandleScope scope1(isolate); |
| |
| // Create another environment. |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| // Make easy access to the object from the other environment. |
| v8::Local<v8::Object> global1 = context1->Global(); |
| CHECK(global1->Set(context1, v8_str("obj"), object).FromJust()); |
| |
| v8::Local<Value> value; |
| |
| // Check that the named access-control function is called every time. |
| CompileRun("function testProp(obj) {" |
| " for (var i = 0; i < 10; i++) obj.prop = 1;" |
| " for (var j = 0; j < 10; j++) obj.prop;" |
| " return obj.prop" |
| "}"); |
| value = CompileRun("testProp(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(21, access_count); |
| |
| // Check that the named access-control function is called every time. |
| CompileRun("var p = 'prop';" |
| "function testKeyed(obj) {" |
| " for (var i = 0; i < 10; i++) obj[p] = 1;" |
| " for (var j = 0; j < 10; j++) obj[p];" |
| " return obj[p];" |
| "}"); |
| // Use obj which requires access checks. No inline caching is used |
| // in that case. |
| value = CompileRun("testKeyed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(42, access_count); |
| // Force the inline caches into generic state and try again. |
| CompileRun("testKeyed({ a: 0 })"); |
| CompileRun("testKeyed({ b: 0 })"); |
| value = CompileRun("testKeyed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(63, access_count); |
| |
| // Check that the indexed access-control function is called every time. |
| access_count = 0; |
| |
| CompileRun("function testIndexed(obj) {" |
| " for (var i = 0; i < 10; i++) obj[0] = 1;" |
| " for (var j = 0; j < 10; j++) obj[0];" |
| " return obj[0]" |
| "}"); |
| value = CompileRun("testIndexed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(21, access_count); |
| // Force the inline caches into generic state. |
| CompileRun("testIndexed(new Array(1))"); |
| // Test that the indexed access check is called. |
| value = CompileRun("testIndexed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(42, access_count); |
| |
| access_count = 0; |
| // Check that the named access check is called when invoking |
| // functions on an object that requires access checks. |
| CompileRun("obj.f = function() {}"); |
| CompileRun("function testCallNormal(obj) {" |
| " for (var i = 0; i < 10; i++) obj.f();" |
| "}"); |
| CompileRun("testCallNormal(obj)"); |
| printf("%i\n", access_count); |
| CHECK_EQ(11, access_count); |
| |
| // Force obj into slow case. |
| value = CompileRun("delete obj.prop"); |
| CHECK(value->BooleanValue(context1).FromJust()); |
| // Force inline caches into dictionary probing mode. |
| CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); |
| // Test that the named access check is called. |
| value = CompileRun("testProp(obj);"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| CHECK_EQ(33, access_count); |
| |
| // Force the call inline cache into dictionary probing mode. |
| CompileRun("o.f = function() {}; testCallNormal(o)"); |
| // Test that the named access check is still called for each |
| // invocation of the function. |
| value = CompileRun("testCallNormal(obj)"); |
| CHECK_EQ(43, access_count); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| THREADED_TEST(Version) { v8::V8::GetVersion(); } |
| |
| |
| static void InstanceFunctionCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(12)); |
| } |
| |
| |
| THREADED_TEST(InstanceProperties) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance = t->InstanceTemplate(); |
| |
| instance->Set(v8_str("x"), v8_num(42)); |
| instance->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, InstanceFunctionCallback)); |
| |
| Local<Value> o = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| CHECK(context->Global()->Set(context.local(), v8_str("i"), o).FromJust()); |
| Local<Value> value = CompileRun("i.x"); |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| |
| value = CompileRun("i.f()"); |
| CHECK_EQ(12, value->Int32Value(context.local()).FromJust()); |
| } |
| |
| |
| static void GlobalObjectInstancePropertiesGet( |
| Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) { |
| ApiTestFuzzer::Fuzz(); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectInstanceProperties) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<Value> global_object; |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| t->InstanceTemplate()->SetHandler( |
| v8::NamedPropertyHandlerConfiguration(GlobalObjectInstancePropertiesGet)); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->Set(v8_str("x"), v8_num(42)); |
| instance_template->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, |
| InstanceFunctionCallback)); |
| |
| // The script to check how Crankshaft compiles missing global function |
| // invocations. function g is not defined and should throw on call. |
| const char* script = |
| "function wrapper(call) {" |
| " var x = 0, y = 1;" |
| " for (var i = 0; i < 1000; i++) {" |
| " x += i * 100;" |
| " y += i * 100;" |
| " }" |
| " if (call) g();" |
| "}" |
| "for (var i = 0; i < 17; i++) wrapper(false);" |
| "var thrown = 0;" |
| "try { wrapper(true); } catch (e) { thrown = 1; };" |
| "thrown"; |
| |
| { |
| LocalContext env(nullptr, instance_template); |
| // Hold on to the global object so it can be used again in another |
| // environment initialization. |
| global_object = env->Global(); |
| |
| Local<Value> value = CompileRun("x"); |
| CHECK_EQ(42, value->Int32Value(env.local()).FromJust()); |
| value = CompileRun("f()"); |
| CHECK_EQ(12, value->Int32Value(env.local()).FromJust()); |
| value = CompileRun(script); |
| CHECK_EQ(1, value->Int32Value(env.local()).FromJust()); |
| } |
| |
| { |
| // Create new environment reusing the global object. |
| LocalContext env(nullptr, instance_template, global_object); |
| Local<Value> value = CompileRun("x"); |
| CHECK_EQ(42, value->Int32Value(env.local()).FromJust()); |
| value = CompileRun("f()"); |
| CHECK_EQ(12, value->Int32Value(env.local()).FromJust()); |
| value = CompileRun(script); |
| CHECK_EQ(1, value->Int32Value(env.local()).FromJust()); |
| } |
| } |
| |
| THREADED_TEST(ObjectGetOwnPropertyNames) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Object> value = |
| v8::Local<v8::Object>::Cast(v8::StringObject::New(v8_str("test"))); |
| v8::Local<v8::Array> properties; |
| |
| CHECK(value |
| ->GetOwnPropertyNames(context.local(), |
| static_cast<v8::PropertyFilter>( |
| v8::PropertyFilter::ALL_PROPERTIES | |
| v8::PropertyFilter::SKIP_SYMBOLS)) |
| .ToLocal(&properties)); |
| CHECK_EQ(5u, properties->Length()); |
| v8::Local<v8::Value> property; |
| CHECK(properties->Get(context.local(), 4).ToLocal(&property) && |
| property->IsString()); |
| CHECK(property.As<v8::String>() |
| ->Equals(context.local(), v8_str("length")) |
| .FromMaybe(false)); |
| for (int i = 0; i < 4; ++i) { |
| v8::Local<v8::Value> property; |
| CHECK(properties->Get(context.local(), i).ToLocal(&property) && |
| property->IsInt32()); |
| CHECK_EQ(property.As<v8::Int32>()->Value(), i); |
| } |
| |
| CHECK(value->GetOwnPropertyNames(context.local(), v8::ONLY_ENUMERABLE) |
| .ToLocal(&properties)); |
| CHECK_EQ(4u, properties->Length()); |
| for (int i = 0; i < 4; ++i) { |
| v8::Local<v8::Value> property; |
| CHECK(properties->Get(context.local(), i).ToLocal(&property) && |
| property->IsInt32()); |
| CHECK_EQ(property.As<v8::Int32>()->Value(), i); |
| } |
| |
| value = value->GetPrototype().As<v8::Object>(); |
| CHECK(value |
| ->GetOwnPropertyNames(context.local(), |
| static_cast<v8::PropertyFilter>( |
| v8::PropertyFilter::ALL_PROPERTIES | |
| v8::PropertyFilter::SKIP_SYMBOLS)) |
| .ToLocal(&properties)); |
| bool concat_found = false; |
| bool starts_with_found = false; |
| for (uint32_t i = 0; i < properties->Length(); ++i) { |
| v8::Local<v8::Value> property; |
| CHECK(properties->Get(context.local(), i).ToLocal(&property)); |
| if (!property->IsString()) continue; |
| if (!concat_found) |
| concat_found = property.As<v8::String>() |
| ->Equals(context.local(), v8_str("concat")) |
| .FromMaybe(false); |
| if (!starts_with_found) |
| starts_with_found = property.As<v8::String>() |
| ->Equals(context.local(), v8_str("startsWith")) |
| .FromMaybe(false); |
| } |
| CHECK(concat_found && starts_with_found); |
| } |
| |
| THREADED_TEST(CallKnownGlobalReceiver) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<Value> global_object; |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| |
| // The script to check that we leave global object not |
| // global object proxy on stack when we deoptimize from inside |
| // arguments evaluation. |
| // To provoke error we need to both force deoptimization |
| // from arguments evaluation and to force CallIC to take |
| // CallIC_Miss code path that can't cope with global proxy. |
| const char* script = |
| "function bar(x, y) { try { } finally { } }" |
| "function baz(x) { try { } finally { } }" |
| "function bom(x) { try { } finally { } }" |
| "function foo(x) { bar([x], bom(2)); }" |
| "for (var i = 0; i < 10000; i++) foo(1);" |
| "foo"; |
| |
| Local<Value> foo; |
| { |
| LocalContext env(nullptr, instance_template); |
| // Hold on to the global object so it can be used again in another |
| // environment initialization. |
| global_object = env->Global(); |
| foo = CompileRun(script); |
| } |
| |
| { |
| // Create new environment reusing the global object. |
| LocalContext env(nullptr, instance_template, global_object); |
| CHECK(env->Global()->Set(env.local(), v8_str("foo"), foo).FromJust()); |
| CompileRun("foo()"); |
| } |
| } |
| |
| |
| static void ShadowFunctionCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(42)); |
| } |
| |
| |
| static int shadow_y; |
| static int shadow_y_setter_call_count; |
| static int shadow_y_getter_call_count; |
| |
| |
| static void ShadowYSetter(Local<String>, |
| Local<Value>, |
| const v8::PropertyCallbackInfo<void>&) { |
| shadow_y_setter_call_count++; |
| shadow_y = 42; |
| } |
| |
| |
| static void ShadowYGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| shadow_y_getter_call_count++; |
| info.GetReturnValue().Set(v8_num(shadow_y)); |
| } |
| |
| |
| static void ShadowIndexedGet(uint32_t index, |
| const v8::PropertyCallbackInfo<v8::Value>&) { |
| } |
| |
| |
| static void ShadowNamedGet(Local<Name> key, |
| const v8::PropertyCallbackInfo<v8::Value>&) {} |
| |
| |
| THREADED_TEST(ShadowObject) { |
| shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| LocalContext context(nullptr, global_template); |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| t->InstanceTemplate()->SetHandler( |
| v8::NamedPropertyHandlerConfiguration(ShadowNamedGet)); |
| t->InstanceTemplate()->SetHandler( |
| v8::IndexedPropertyHandlerConfiguration(ShadowIndexedGet)); |
| Local<ObjectTemplate> proto = t->PrototypeTemplate(); |
| Local<ObjectTemplate> instance = t->InstanceTemplate(); |
| |
| proto->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, |
| ShadowFunctionCallback, |
| Local<Value>())); |
| proto->Set(v8_str("x"), v8_num(12)); |
| |
| instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); |
| |
| Local<Value> o = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("__proto__"), o) |
| .FromJust()); |
| |
| Local<Value> value = |
| CompileRun("this.propertyIsEnumerable(0)"); |
| CHECK(value->IsBoolean()); |
| CHECK(!value->BooleanValue(context.local()).FromJust()); |
| |
| value = CompileRun("x"); |
| CHECK_EQ(12, value->Int32Value(context.local()).FromJust()); |
| |
| value = CompileRun("f()"); |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| |
| CompileRun("y = 43"); |
| CHECK_EQ(1, shadow_y_setter_call_count); |
| value = CompileRun("y"); |
| CHECK_EQ(1, shadow_y_getter_call_count); |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(HiddenPrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate); |
| t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->SetHiddenPrototype(true); |
| t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->SetHiddenPrototype(true); |
| t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); |
| Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); |
| |
| Local<v8::Object> o0 = t0->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o1 = t1->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o2 = t2->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o3 = t3->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| // Setting the prototype on an object skips hidden prototypes. |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o0->Set(context.local(), v8_str("__proto__"), o1).FromJust()); |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(1, o0->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o0->Set(context.local(), v8_str("__proto__"), o2).FromJust()); |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(1, o0->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(2, o0->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o0->Set(context.local(), v8_str("__proto__"), o3).FromJust()); |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(1, o0->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(2, o0->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(3, o0->Get(context.local(), v8_str("u")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| |
| // Getting the prototype of o0 should get the first visible one |
| // which is o3. Therefore, z should not be defined on the prototype |
| // object. |
| Local<Value> proto = |
| o0->Get(context.local(), v8_str("__proto__")).ToLocalChecked(); |
| CHECK(proto->IsObject()); |
| CHECK(proto.As<v8::Object>() |
| ->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->IsUndefined()); |
| } |
| |
| |
| THREADED_TEST(HiddenPrototypeSet) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> ot = v8::FunctionTemplate::New(isolate); |
| Local<v8::FunctionTemplate> ht = v8::FunctionTemplate::New(isolate); |
| ht->SetHiddenPrototype(true); |
| Local<v8::FunctionTemplate> pt = v8::FunctionTemplate::New(isolate); |
| ht->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); |
| |
| Local<v8::Object> o = ot->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> h = ht->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> p = pt->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| CHECK(o->Set(context.local(), v8_str("__proto__"), h).FromJust()); |
| CHECK(h->Set(context.local(), v8_str("__proto__"), p).FromJust()); |
| |
| // Setting a property that exists on the hidden prototype goes there. |
| CHECK(o->Set(context.local(), v8_str("x"), v8_num(7)).FromJust()); |
| CHECK_EQ(7, o->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(7, h->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(p->Get(context.local(), v8_str("x")).ToLocalChecked()->IsUndefined()); |
| |
| // Setting a new property should not be forwarded to the hidden prototype. |
| CHECK(o->Set(context.local(), v8_str("y"), v8_num(6)).FromJust()); |
| CHECK_EQ(6, o->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(h->Get(context.local(), v8_str("y")).ToLocalChecked()->IsUndefined()); |
| CHECK(p->Get(context.local(), v8_str("y")).ToLocalChecked()->IsUndefined()); |
| |
| // Setting a property that only exists on a prototype of the hidden prototype |
| // is treated normally again. |
| CHECK(p->Set(context.local(), v8_str("z"), v8_num(8)).FromJust()); |
| CHECK_EQ(8, o->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(8, h->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(8, p->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o->Set(context.local(), v8_str("z"), v8_num(9)).FromJust()); |
| CHECK_EQ(9, o->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(8, h->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(8, p->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| // Regression test for issue 2457. |
| THREADED_TEST(HiddenPrototypeIdentityHash) { |
| LocalContext context; |
| v8::HandleScope handle_scope(context->GetIsolate()); |
| |
| Local<FunctionTemplate> t = FunctionTemplate::New(context->GetIsolate()); |
| t->SetHiddenPrototype(true); |
| t->InstanceTemplate()->Set(v8_str("foo"), v8_num(75)); |
| Local<Object> p = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<Object> o = Object::New(context->GetIsolate()); |
| CHECK(o->SetPrototype(context.local(), p).FromJust()); |
| |
| int hash = o->GetIdentityHash(); |
| USE(hash); |
| CHECK(o->Set(context.local(), v8_str("foo"), v8_num(42)).FromJust()); |
| CHECK_EQ(hash, o->GetIdentityHash()); |
| } |
| |
| |
| THREADED_TEST(SetPrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate); |
| t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->SetHiddenPrototype(true); |
| t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->SetHiddenPrototype(true); |
| t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); |
| Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); |
| |
| Local<v8::Object> o0 = t0->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o1 = t1->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o2 = t2->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o3 = t3->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| // Setting the prototype on an object does not skip hidden prototypes. |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o0->SetPrototype(context.local(), o1).FromJust()); |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(1, o0->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o1->SetPrototype(context.local(), o2).FromJust()); |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(1, o0->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(2, o0->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK(o2->SetPrototype(context.local(), o3).FromJust()); |
| CHECK_EQ(0, o0->Get(context.local(), v8_str("x")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(1, o0->Get(context.local(), v8_str("y")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(2, o0->Get(context.local(), v8_str("z")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(3, o0->Get(context.local(), v8_str("u")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| |
| // Getting the prototype of o0 should get the first visible one |
| // which is o3. Therefore, z should not be defined on the prototype |
| // object. |
| Local<Value> proto = |
| o0->Get(context.local(), v8_str("__proto__")).ToLocalChecked(); |
| CHECK(proto->IsObject()); |
| CHECK(proto.As<v8::Object>()->Equals(context.local(), o3).FromJust()); |
| |
| // However, Object::GetPrototype ignores hidden prototype. |
| Local<Value> proto0 = o0->GetPrototype(); |
| CHECK(proto0->IsObject()); |
| CHECK(proto0.As<v8::Object>()->Equals(context.local(), o1).FromJust()); |
| |
| Local<Value> proto1 = o1->GetPrototype(); |
| CHECK(proto1->IsObject()); |
| CHECK(proto1.As<v8::Object>()->Equals(context.local(), o2).FromJust()); |
| |
| Local<Value> proto2 = o2->GetPrototype(); |
| CHECK(proto2->IsObject()); |
| CHECK(proto2.As<v8::Object>()->Equals(context.local(), o3).FromJust()); |
| } |
| |
| |
| // Getting property names of an object with a prototype chain that |
| // triggers dictionary elements in GetOwnPropertyNames() shouldn't |
| // crash the runtime. |
| THREADED_TEST(Regress91517) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->SetHiddenPrototype(true); |
| t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1)); |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->SetHiddenPrototype(true); |
| t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2)); |
| t2->InstanceTemplate()->Set(v8_str("objects"), |
| v8::ObjectTemplate::New(isolate)); |
| t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2)); |
| Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| t3->SetHiddenPrototype(true); |
| t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3)); |
| Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(isolate); |
| t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4)); |
| |
| // Force dictionary-based properties. |
| i::ScopedVector<char> name_buf(1024); |
| for (int i = 1; i <= 1000; i++) { |
| i::SNPrintF(name_buf, "sdf%d", i); |
| t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); |
| } |
| |
| Local<v8::Object> o1 = t1->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o2 = t2->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o3 = t3->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o4 = t4->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| // Create prototype chain of hidden prototypes. |
| CHECK(o4->SetPrototype(context.local(), o3).FromJust()); |
| CHECK(o3->SetPrototype(context.local(), o2).FromJust()); |
| CHECK(o2->SetPrototype(context.local(), o1).FromJust()); |
| |
| // Call the runtime version of GetOwnPropertyNames() on the natively |
| // created object through JavaScript. |
| CHECK(context->Global()->Set(context.local(), v8_str("obj"), o4).FromJust()); |
| // PROPERTY_FILTER_NONE = 0 |
| CompileRun("var names = %GetOwnPropertyKeys(obj, 0);"); |
| |
| ExpectInt32("names.length", 1006); |
| ExpectTrue("names.indexOf(\"baz\") >= 0"); |
| ExpectTrue("names.indexOf(\"boo\") >= 0"); |
| ExpectTrue("names.indexOf(\"foo\") >= 0"); |
| ExpectTrue("names.indexOf(\"fuz1\") >= 0"); |
| ExpectTrue("names.indexOf(\"objects\") >= 0"); |
| ExpectTrue("names.indexOf(\"fuz2\") >= 0"); |
| ExpectFalse("names[1005] == undefined"); |
| } |
| |
| |
| // Getting property names of an object with a hidden and inherited |
| // prototype should not duplicate the accessor properties inherited. |
| THREADED_TEST(Regress269562) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext context; |
| v8::HandleScope handle_scope(context->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> t1 = |
| v8::FunctionTemplate::New(context->GetIsolate()); |
| t1->SetHiddenPrototype(true); |
| |
| Local<v8::ObjectTemplate> i1 = t1->InstanceTemplate(); |
| i1->SetAccessor(v8_str("foo"), |
| SimpleAccessorGetter, SimpleAccessorSetter); |
| i1->SetAccessor(v8_str("bar"), |
| SimpleAccessorGetter, SimpleAccessorSetter); |
| i1->SetAccessor(v8_str("baz"), |
| SimpleAccessorGetter, SimpleAccessorSetter); |
| i1->Set(v8_str("n1"), v8_num(1)); |
| i1->Set(v8_str("n2"), v8_num(2)); |
| |
| Local<v8::Object> o1 = t1->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::FunctionTemplate> t2 = |
| v8::FunctionTemplate::New(context->GetIsolate()); |
| t2->SetHiddenPrototype(true); |
| |
| // Inherit from t1 and mark prototype as hidden. |
| t2->Inherit(t1); |
| t2->InstanceTemplate()->Set(v8_str("mine"), v8_num(4)); |
| |
| Local<v8::Object> o2 = t2->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| CHECK(o2->SetPrototype(context.local(), o1).FromJust()); |
| |
| v8::Local<v8::Symbol> sym = |
| v8::Symbol::New(context->GetIsolate(), v8_str("s1")); |
| CHECK(o1->Set(context.local(), sym, v8_num(3)).FromJust()); |
| o1->SetPrivate(context.local(), |
| v8::Private::New(context->GetIsolate(), v8_str("h1")), |
| v8::Integer::New(context->GetIsolate(), 2013)) |
| .FromJust(); |
| |
| // Call the runtime version of GetOwnPropertyNames() on |
| // the natively created object through JavaScript. |
| CHECK(context->Global()->Set(context.local(), v8_str("obj"), o2).FromJust()); |
| CHECK(context->Global()->Set(context.local(), v8_str("sym"), sym).FromJust()); |
| // PROPERTY_FILTER_NONE = 0 |
| CompileRun("var names = %GetOwnPropertyKeys(obj, 0);"); |
| |
| ExpectInt32("names.length", 7); |
| ExpectTrue("names.indexOf(\"foo\") >= 0"); |
| ExpectTrue("names.indexOf(\"bar\") >= 0"); |
| ExpectTrue("names.indexOf(\"baz\") >= 0"); |
| ExpectTrue("names.indexOf(\"n1\") >= 0"); |
| ExpectTrue("names.indexOf(\"n2\") >= 0"); |
| ExpectTrue("names.indexOf(sym) >= 0"); |
| ExpectTrue("names.indexOf(\"mine\") >= 0"); |
| } |
| |
| |
| THREADED_TEST(FunctionReadOnlyPrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42)); |
| t1->ReadOnlyPrototype(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("func1"), |
| t1->GetFunction(context.local()).ToLocalChecked()) |
| .FromJust()); |
| // Configured value of ReadOnly flag. |
| CHECK( |
| CompileRun( |
| "(function() {" |
| " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" |
| " return (descriptor['writable'] == false);" |
| "})()") |
| ->BooleanValue(context.local()) |
| .FromJust()); |
| CHECK_EQ( |
| 42, |
| CompileRun("func1.prototype.x")->Int32Value(context.local()).FromJust()); |
| CHECK_EQ(42, CompileRun("func1.prototype = {}; func1.prototype.x") |
| ->Int32Value(context.local()) |
| .FromJust()); |
| |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42)); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("func2"), |
| t2->GetFunction(context.local()).ToLocalChecked()) |
| .FromJust()); |
| // Default value of ReadOnly flag. |
| CHECK( |
| CompileRun( |
| "(function() {" |
| " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" |
| " return (descriptor['writable'] == true);" |
| "})()") |
| ->BooleanValue(context.local()) |
| .FromJust()); |
| CHECK_EQ( |
| 42, |
| CompileRun("func2.prototype.x")->Int32Value(context.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(SetPrototypeThrows) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| |
| Local<v8::Object> o0 = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<v8::Object> o1 = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| CHECK(o0->SetPrototype(context.local(), o1).FromJust()); |
| // If setting the prototype leads to the cycle, SetPrototype should |
| // return false and keep VM in sane state. |
| v8::TryCatch try_catch(isolate); |
| CHECK(o1->SetPrototype(context.local(), o0).IsNothing()); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(!CcTest::i_isolate()->has_pending_exception()); |
| |
| CHECK_EQ(42, CompileRun("function f() { return 42; }; f()") |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(FunctionRemovePrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->RemovePrototype(); |
| Local<v8::Function> fun = t1->GetFunction(context.local()).ToLocalChecked(); |
| CHECK(!fun->IsConstructor()); |
| CHECK(context->Global()->Set(context.local(), v8_str("fun"), fun).FromJust()); |
| CHECK(!CompileRun("'prototype' in fun") |
| ->BooleanValue(context.local()) |
| .FromJust()); |
| |
| v8::TryCatch try_catch(isolate); |
| CompileRun("new fun()"); |
| CHECK(try_catch.HasCaught()); |
| |
| try_catch.Reset(); |
| CHECK(fun->NewInstance(context.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(GetterSetterExceptions) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| CompileRun( |
| "function Foo() { };" |
| "function Throw() { throw 5; };" |
| "var x = { };" |
| "x.__defineSetter__('set', Throw);" |
| "x.__defineGetter__('get', Throw);"); |
| Local<v8::Object> x = Local<v8::Object>::Cast( |
| context->Global()->Get(context.local(), v8_str("x")).ToLocalChecked()); |
| v8::TryCatch try_catch(isolate); |
| CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8)) |
| .IsNothing()); |
| CHECK(x->Get(context.local(), v8_str("get")).IsEmpty()); |
| CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8)) |
| .IsNothing()); |
| CHECK(x->Get(context.local(), v8_str("get")).IsEmpty()); |
| CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8)) |
| .IsNothing()); |
| CHECK(x->Get(context.local(), v8_str("get")).IsEmpty()); |
| CHECK(x->Set(context.local(), v8_str("set"), v8::Integer::New(isolate, 8)) |
| .IsNothing()); |
| CHECK(x->Get(context.local(), v8_str("get")).IsEmpty()); |
| } |
| |
| |
| THREADED_TEST(Constructor) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("Fun")); |
| Local<Function> cons = templ->GetFunction(context.local()).ToLocalChecked(); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("Fun"), cons).FromJust()); |
| Local<v8::Object> inst = cons->NewInstance(context.local()).ToLocalChecked(); |
| i::Handle<i::JSReceiver> obj(v8::Utils::OpenHandle(*inst)); |
| CHECK(obj->IsJSObject()); |
| Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); |
| CHECK(value->BooleanValue(context.local()).FromJust()); |
| } |
| |
| |
| static void ConstructorCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| Local<Object> This; |
| |
| v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| if (args.IsConstructCall()) { |
| Local<Object> Holder = args.Holder(); |
| This = Object::New(args.GetIsolate()); |
| Local<Value> proto = Holder->GetPrototype(); |
| if (proto->IsObject()) { |
| This->SetPrototype(context, proto).FromJust(); |
| } |
| } else { |
| This = args.This(); |
| } |
| |
| This->Set(context, v8_str("a"), args[0]).FromJust(); |
| args.GetReturnValue().Set(This); |
| } |
| |
| |
| static void FakeConstructorCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(args[0]); |
| } |
| |
| |
| THREADED_TEST(ConstructorForObject) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(ConstructorCallback); |
| Local<Object> instance = |
| instance_template->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), instance) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| // Call the Object's constructor with a 32-bit signed integer. |
| value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsInt32()); |
| CHECK_EQ(28, value->Int32Value(context.local()).FromJust()); |
| |
| Local<Value> args1[] = {v8_num(28)}; |
| Local<Value> value_obj1 = |
| instance->CallAsConstructor(context.local(), 1, args1).ToLocalChecked(); |
| CHECK(value_obj1->IsObject()); |
| Local<Object> object1 = Local<Object>::Cast(value_obj1); |
| value = object1->Get(context.local(), v8_str("a")).ToLocalChecked(); |
| CHECK(value->IsInt32()); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(28, value->Int32Value(context.local()).FromJust()); |
| |
| // Call the Object's constructor with a String. |
| value = |
| CompileRun("(function() { var o = new obj('tipli'); return o.a; })()"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsString()); |
| String::Utf8Value string_value1( |
| isolate, value->ToString(context.local()).ToLocalChecked()); |
| CHECK_EQ(0, strcmp("tipli", *string_value1)); |
| |
| Local<Value> args2[] = {v8_str("tipli")}; |
| Local<Value> value_obj2 = |
| instance->CallAsConstructor(context.local(), 1, args2).ToLocalChecked(); |
| CHECK(value_obj2->IsObject()); |
| Local<Object> object2 = Local<Object>::Cast(value_obj2); |
| value = object2->Get(context.local(), v8_str("a")).ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsString()); |
| String::Utf8Value string_value2( |
| isolate, value->ToString(context.local()).ToLocalChecked()); |
| CHECK_EQ(0, strcmp("tipli", *string_value2)); |
| |
| // Call the Object's constructor with a Boolean. |
| value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsBoolean()); |
| CHECK(value->BooleanValue(context.local()).FromJust()); |
| |
| Local<Value> args3[] = {v8::True(isolate)}; |
| Local<Value> value_obj3 = |
| instance->CallAsConstructor(context.local(), 1, args3).ToLocalChecked(); |
| CHECK(value_obj3->IsObject()); |
| Local<Object> object3 = Local<Object>::Cast(value_obj3); |
| value = object3->Get(context.local(), v8_str("a")).ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsBoolean()); |
| CHECK(value->BooleanValue(context.local()).FromJust()); |
| |
| // Call the Object's constructor with undefined. |
| Local<Value> args4[] = {v8::Undefined(isolate)}; |
| Local<Value> value_obj4 = |
| instance->CallAsConstructor(context.local(), 1, args4).ToLocalChecked(); |
| CHECK(value_obj4->IsObject()); |
| Local<Object> object4 = Local<Object>::Cast(value_obj4); |
| value = object4->Get(context.local(), v8_str("a")).ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsUndefined()); |
| |
| // Call the Object's constructor with null. |
| Local<Value> args5[] = {v8::Null(isolate)}; |
| Local<Value> value_obj5 = |
| instance->CallAsConstructor(context.local(), 1, args5).ToLocalChecked(); |
| CHECK(value_obj5->IsObject()); |
| Local<Object> object5 = Local<Object>::Cast(value_obj5); |
| value = object5->Get(context.local(), v8_str("a")).ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsNull()); |
| } |
| |
| // Check exception handling when there is no constructor set for the Object. |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| Local<Object> instance = |
| instance_template->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj2"), instance) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| value = CompileRun("new obj2(28)"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(isolate, try_catch.Exception()); |
| CHECK_EQ(0, |
| strcmp("TypeError: obj2 is not a constructor", *exception_value1)); |
| try_catch.Reset(); |
| |
| Local<Value> args[] = {v8_num(29)}; |
| CHECK(instance->CallAsConstructor(context.local(), 1, args).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(isolate, try_catch.Exception()); |
| CHECK_EQ( |
| 0, strcmp("TypeError: object is not a constructor", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| // Check the case when constructor throws exception. |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(ThrowValue); |
| Local<Object> instance = |
| instance_template->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj3"), instance) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| value = CompileRun("new obj3(22)"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp("22", *exception_value1)); |
| try_catch.Reset(); |
| |
| Local<Value> args[] = {v8_num(23)}; |
| CHECK(instance->CallAsConstructor(context.local(), 1, args).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp("23", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| // Check whether constructor returns with an object or non-object. |
| { |
| Local<FunctionTemplate> function_template = |
| FunctionTemplate::New(isolate, FakeConstructorCallback); |
| Local<Function> function = |
| function_template->GetFunction(context.local()).ToLocalChecked(); |
| Local<Object> instance1 = function; |
| CHECK(instance1->IsObject()); |
| CHECK(instance1->IsFunction()); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj4"), instance1) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| CHECK(!try_catch.HasCaught()); |
| |
| { |
| Local<Value> value = CompileRun("new obj4(28)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsObject()); |
| |
| Local<Value> args[] = {v8_num(28)}; |
| value = instance1->CallAsConstructor(context.local(), 1, args) |
| .ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsObject()); |
| } |
| |
| Local<Value> proxy = CompileRun("proxy = new Proxy({},{})"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(proxy->IsProxy()); |
| |
| { |
| Local<Value> value = CompileRun("new obj4(proxy)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsProxy()); |
| CHECK(value->SameValue(proxy)); |
| |
| Local<Value> args[] = {proxy}; |
| value = instance1->CallAsConstructor(context.local(), 1, args) |
| .ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->SameValue(proxy)); |
| } |
| |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); |
| Local<Object> instance2 = |
| instance_template->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(instance2->IsObject()); |
| CHECK(instance2->IsFunction()); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj5"), instance2) |
| .FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| |
| { |
| Local<Value> value = CompileRun("new obj5(28)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(!value->IsObject()); |
| |
| Local<Value> args[] = {v8_num(28)}; |
| value = instance2->CallAsConstructor(context.local(), 1, args) |
| .ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(!value->IsObject()); |
| } |
| |
| { |
| Local<Value> value = CompileRun("new obj5(proxy)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsProxy()); |
| CHECK(value->SameValue(proxy)); |
| |
| Local<Value> args[] = {proxy}; |
| value = instance2->CallAsConstructor(context.local(), 1, args) |
| .ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->SameValue(proxy)); |
| } |
| } |
| } |
| |
| |
| THREADED_TEST(FunctionDescriptorException) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("Fun")); |
| Local<Function> cons = templ->GetFunction(context.local()).ToLocalChecked(); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("Fun"), cons).FromJust()); |
| Local<Value> value = CompileRun( |
| "function test() {" |
| " try {" |
| " (new Fun()).blah()" |
| " } catch (e) {" |
| " var str = String(e);" |
| // " if (str.indexOf('TypeError') == -1) return 1;" |
| // " if (str.indexOf('[object Fun]') != -1) return 2;" |
| // " if (str.indexOf('#<Fun>') == -1) return 3;" |
| " return 0;" |
| " }" |
| " return 4;" |
| "}" |
| "test();"); |
| CHECK_EQ(0, value->Int32Value(context.local()).FromJust()); |
| } |
| |
| |
| THREADED_TEST(EvalAliasedDynamic) { |
| LocalContext current; |
| v8::HandleScope scope(current->GetIsolate()); |
| |
| // Tests where aliased eval can only be resolved dynamically. |
| Local<Script> script = v8_compile( |
| "function f(x) { " |
| " var foo = 2;" |
| " with (x) { return eval('foo'); }" |
| "}" |
| "foo = 0;" |
| "result1 = f(new Object());" |
| "result2 = f(this);" |
| "var x = new Object();" |
| "x.eval = function(x) { return 1; };" |
| "result3 = f(x);"); |
| script->Run(current.local()).ToLocalChecked(); |
| CHECK_EQ(2, current->Global() |
| ->Get(current.local(), v8_str("result1")) |
| .ToLocalChecked() |
| ->Int32Value(current.local()) |
| .FromJust()); |
| CHECK_EQ(0, current->Global() |
| ->Get(current.local(), v8_str("result2")) |
| .ToLocalChecked() |
| ->Int32Value(current.local()) |
| .FromJust()); |
| CHECK_EQ(1, current->Global() |
| ->Get(current.local(), v8_str("result3")) |
| .ToLocalChecked() |
| ->Int32Value(current.local()) |
| .FromJust()); |
| |
| v8::TryCatch try_catch(current->GetIsolate()); |
| script = v8_compile( |
| "function f(x) { " |
| " var bar = 2;" |
| " with (x) { return eval('bar'); }" |
| "}" |
| "result4 = f(this)"); |
| script->Run(current.local()).ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(2, current->Global() |
| ->Get(current.local(), v8_str("result4")) |
| .ToLocalChecked() |
| ->Int32Value(current.local()) |
| .FromJust()); |
| |
| try_catch.Reset(); |
| } |
| |
| |
| THREADED_TEST(CrossEval) { |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext other; |
| LocalContext current; |
| |
| Local<String> token = v8_str("<security token>"); |
| other->SetSecurityToken(token); |
| current->SetSecurityToken(token); |
| |
| // Set up reference from current to other. |
| CHECK(current->Global() |
| ->Set(current.local(), v8_str("other"), other->Global()) |
| .FromJust()); |
| |
| // Check that new variables are introduced in other context. |
| Local<Script> script = v8_compile("other.eval('var foo = 1234')"); |
| script->Run(current.local()).ToLocalChecked(); |
| Local<Value> foo = |
| other->Global()->Get(current.local(), v8_str("foo")).ToLocalChecked(); |
| CHECK_EQ(1234, foo->Int32Value(other.local()).FromJust()); |
| CHECK(!current->Global()->Has(current.local(), v8_str("foo")).FromJust()); |
| |
| // Check that writing to non-existing properties introduces them in |
| // the other context. |
| script = v8_compile("other.eval('na = 1234')"); |
| script->Run(current.local()).ToLocalChecked(); |
| CHECK_EQ(1234, other->Global() |
| ->Get(current.local(), v8_str("na")) |
| .ToLocalChecked() |
| ->Int32Value(other.local()) |
| .FromJust()); |
| CHECK(!current->Global()->Has(current.local(), v8_str("na")).FromJust()); |
| |
| // Check that global variables in current context are not visible in other |
| // context. |
| v8::TryCatch try_catch(CcTest::isolate()); |
| script = v8_compile("var bar = 42; other.eval('bar');"); |
| CHECK(script->Run(current.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| // Check that local variables in current context are not visible in other |
| // context. |
| script = v8_compile( |
| "(function() { " |
| " var baz = 87;" |
| " return other.eval('baz');" |
| "})();"); |
| CHECK(script->Run(current.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| // Check that global variables in the other environment are visible |
| // when evaluting code. |
| CHECK(other->Global() |
| ->Set(other.local(), v8_str("bis"), v8_num(1234)) |
| .FromJust()); |
| script = v8_compile("other.eval('bis')"); |
| CHECK_EQ(1234, script->Run(current.local()) |
| .ToLocalChecked() |
| ->Int32Value(current.local()) |
| .FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| |
| // Check that the 'this' pointer points to the global object evaluating |
| // code. |
| CHECK(other->Global() |
| ->Set(current.local(), v8_str("t"), other->Global()) |
| .FromJust()); |
| script = v8_compile("other.eval('this == t')"); |
| Local<Value> result = script->Run(current.local()).ToLocalChecked(); |
| CHECK(result->IsTrue()); |
| CHECK(!try_catch.HasCaught()); |
| |
| // Check that variables introduced in with-statement are not visible in |
| // other context. |
| script = v8_compile("with({x:2}){other.eval('x')}"); |
| CHECK(script->Run(current.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| // Check that you cannot use 'eval.call' with another object than the |
| // current global object. |
| script = v8_compile("other.y = 1; eval.call(other, 'y')"); |
| CHECK(script->Run(current.local()).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| // Test that calling eval in a context which has been detached from |
| // its global proxy works. |
| THREADED_TEST(EvalInDetachedGlobal) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<Context> context0 = Context::New(isolate); |
| v8::Local<Context> context1 = Context::New(isolate); |
| Local<String> token = v8_str("<security token>"); |
| context0->SetSecurityToken(token); |
| context1->SetSecurityToken(token); |
| |
| // Set up function in context0 that uses eval from context0. |
| context0->Enter(); |
| v8::Local<v8::Value> fun = CompileRun( |
| "var x = 42;" |
| "(function() {" |
| " var e = eval;" |
| " return function(s) { return e(s); }" |
| "})()"); |
| context0->Exit(); |
| |
| // Put the function into context1 and call it before and after |
| // detaching the global. Before detaching, the call succeeds and |
| // after detaching undefined is returned. |
| context1->Enter(); |
| CHECK(context1->Global()->Set(context1, v8_str("fun"), fun).FromJust()); |
| v8::Local<v8::Value> x_value = CompileRun("fun('x')"); |
| CHECK_EQ(42, x_value->Int32Value(context1).FromJust()); |
| context0->DetachGlobal(); |
| x_value = CompileRun("fun('x')"); |
| CHECK(x_value->IsUndefined()); |
| context1->Exit(); |
| } |
| |
| |
| THREADED_TEST(CrossLazyLoad) { |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext other; |
| LocalContext current; |
| |
| Local<String> token = v8_str("<security token>"); |
| other->SetSecurityToken(token); |
| current->SetSecurityToken(token); |
| |
| // Set up reference from current to other. |
| CHECK(current->Global() |
| ->Set(current.local(), v8_str("other"), other->Global()) |
| .FromJust()); |
| |
| // Trigger lazy loading in other context. |
| Local<Script> script = v8_compile("other.eval('new Date(42)')"); |
| Local<Value> value = script->Run(current.local()).ToLocalChecked(); |
| CHECK_EQ(42.0, value->NumberValue(current.local()).FromJust()); |
| } |
| |
| |
| static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| if (args.IsConstructCall()) { |
| if (args[0]->IsInt32()) { |
| args.GetReturnValue().Set( |
| v8_num(-args[0] |
| ->Int32Value(args.GetIsolate()->GetCurrentContext()) |
| .FromJust())); |
| return; |
| } |
| } |
| |
| args.GetReturnValue().Set(args[0]); |
| } |
| |
| |
| // Test that a call handler can be set for objects which will allow |
| // non-function objects created through the API to be called as |
| // functions. |
| THREADED_TEST(CallAsFunction) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->SetCallAsFunctionHandler(call_as_function); |
| Local<v8::Object> instance = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj"), instance) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| value = CompileRun("obj(42)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| |
| value = CompileRun("(function(o){return o(49)})(obj)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(49, value->Int32Value(context.local()).FromJust()); |
| |
| // test special case of call as function |
| value = CompileRun("[obj]['0'](45)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(45, value->Int32Value(context.local()).FromJust()); |
| |
| value = CompileRun( |
| "obj.call = Function.prototype.call;" |
| "obj.call(null, 87)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(87, value->Int32Value(context.local()).FromJust()); |
| |
| // Regression tests for bug #1116356: Calling call through call/apply |
| // must work for non-function receivers. |
| const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; |
| value = CompileRun(apply_99); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(99, value->Int32Value(context.local()).FromJust()); |
| |
| const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; |
| value = CompileRun(call_17); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(17, value->Int32Value(context.local()).FromJust()); |
| |
| // Check that the call-as-function handler can be called through |
| // new. |
| value = CompileRun("new obj(43)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(-43, value->Int32Value(context.local()).FromJust()); |
| |
| // Check that the call-as-function handler can be called through |
| // the API. |
| v8::Local<Value> args[] = {v8_num(28)}; |
| value = instance->CallAsFunction(context.local(), instance, 1, args) |
| .ToLocalChecked(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(28, value->Int32Value(context.local()).FromJust()); |
| } |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template(t->InstanceTemplate()); |
| USE(instance_template); |
| Local<v8::Object> instance = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj2"), instance) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| // Call an object without call-as-function handler through the JS |
| value = CompileRun("obj2(28)"); |
| CHECK(value.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(isolate, try_catch.Exception()); |
| // TODO(verwaest): Better message |
| CHECK_EQ(0, strcmp("TypeError: obj2 is not a function", *exception_value1)); |
| try_catch.Reset(); |
| |
| // Call an object without call-as-function handler through the API |
| value = CompileRun("obj2(28)"); |
| v8::Local<Value> args[] = {v8_num(28)}; |
| CHECK( |
| instance->CallAsFunction(context.local(), instance, 1, args).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(isolate, try_catch.Exception()); |
| CHECK_EQ(0, |
| strcmp("TypeError: object is not a function", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->SetCallAsFunctionHandler(ThrowValue); |
| Local<v8::Object> instance = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("obj3"), instance) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| // Catch the exception which is thrown by call-as-function handler |
| value = CompileRun("obj3(22)"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp("22", *exception_value1)); |
| try_catch.Reset(); |
| |
| v8::Local<Value> args[] = {v8_num(23)}; |
| CHECK( |
| instance->CallAsFunction(context.local(), instance, 1, args).IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(isolate, try_catch.Exception()); |
| CHECK_EQ(0, strcmp("23", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->SetCallAsFunctionHandler(ReturnThis); |
| Local<v8::Object> instance = t->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| Local<v8::Value> a1 = |
| instance |
| ->CallAsFunction(context.local(), v8::Undefined(isolate), 0, |
| nullptr) |
| .ToLocalChecked(); |
| CHECK(a1->StrictEquals(instance)); |
| Local<v8::Value> a2 = |
| instance->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a2->StrictEquals(instance)); |
| Local<v8::Value> a3 = |
| instance->CallAsFunction(context.local(), v8_num(42), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a3->StrictEquals(instance)); |
| Local<v8::Value> a4 = |
| instance->CallAsFunction(context.local(), v8_str("hello"), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a4->StrictEquals(instance)); |
| Local<v8::Value> a5 = |
| instance->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a5->StrictEquals(instance)); |
| } |
| |
| { |
| CompileRun( |
| "function ReturnThisSloppy() {" |
| " return this;" |
| "}" |
| "function ReturnThisStrict() {" |
| " 'use strict';" |
| " return this;" |
| "}"); |
| Local<Function> ReturnThisSloppy = Local<Function>::Cast( |
| context->Global() |
| ->Get(context.local(), v8_str("ReturnThisSloppy")) |
| .ToLocalChecked()); |
| Local<Function> ReturnThisStrict = Local<Function>::Cast( |
| context->Global() |
| ->Get(context.local(), v8_str("ReturnThisStrict")) |
| .ToLocalChecked()); |
| |
| Local<v8::Value> a1 = |
| ReturnThisSloppy |
| ->CallAsFunction(context.local(), v8::Undefined(isolate), 0, |
| nullptr) |
| .ToLocalChecked(); |
| CHECK(a1->StrictEquals(context->Global())); |
| Local<v8::Value> a2 = |
| ReturnThisSloppy |
| ->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a2->StrictEquals(context->Global())); |
| Local<v8::Value> a3 = |
| ReturnThisSloppy |
| ->CallAsFunction(context.local(), v8_num(42), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a3->IsNumberObject()); |
| CHECK_EQ(42.0, a3.As<v8::NumberObject>()->ValueOf()); |
| Local<v8::Value> a4 = |
| ReturnThisSloppy |
| ->CallAsFunction(context.local(), v8_str("hello"), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a4->IsStringObject()); |
| CHECK(a4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> a5 = |
| ReturnThisSloppy |
| ->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a5->IsBooleanObject()); |
| CHECK(a5.As<v8::BooleanObject>()->ValueOf()); |
| |
| Local<v8::Value> a6 = |
| ReturnThisStrict |
| ->CallAsFunction(context.local(), v8::Undefined(isolate), 0, |
| nullptr) |
| .ToLocalChecked(); |
| CHECK(a6->IsUndefined()); |
| Local<v8::Value> a7 = |
| ReturnThisStrict |
| ->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a7->IsNull()); |
| Local<v8::Value> a8 = |
| ReturnThisStrict |
| ->CallAsFunction(context.local(), v8_num(42), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a8->StrictEquals(v8_num(42))); |
| Local<v8::Value> a9 = |
| ReturnThisStrict |
| ->CallAsFunction(context.local(), v8_str("hello"), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a9->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> a10 = |
| ReturnThisStrict |
| ->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK(a10->StrictEquals(v8::True(isolate))); |
| } |
| } |
| |
| |
| // Check whether a non-function object is callable. |
| THREADED_TEST(CallableObject) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(call_as_function); |
| Local<Object> instance = |
| instance_template->NewInstance(context.local()).ToLocalChecked(); |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| Local<Object> instance = |
| instance_template->NewInstance(context.local()).ToLocalChecked(); |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(!instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| Local<FunctionTemplate> function_template = |
| FunctionTemplate::New(isolate, call_as_function); |
| Local<Function> function = |
| function_template->GetFunction(context.local()).ToLocalChecked(); |
| Local<Object> instance = function; |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate); |
| Local<Function> function = |
| function_template->GetFunction(context.local()).ToLocalChecked(); |
| Local<Object> instance = function; |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| } |
| |
| |
| THREADED_TEST(Regress567998) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> desc = |
| v8::FunctionTemplate::New(env->GetIsolate()); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| |
| Local<v8::Object> obj = desc->GetFunction(env.local()) |
| .ToLocalChecked() |
| ->NewInstance(env.local()) |
| .ToLocalChecked(); |
| CHECK( |
| env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust()); |
| |
| ExpectString("undetectable.toString()", "[object Object]"); |
| ExpectString("typeof undetectable", "undefined"); |
| ExpectString("typeof(undetectable)", "undefined"); |
| ExpectBoolean("typeof undetectable == 'undefined'", true); |
| ExpectBoolean("typeof undetectable == 'object'", false); |
| ExpectBoolean("if (undetectable) { true; } else { false; }", false); |
| ExpectBoolean("!undetectable", true); |
| |
| ExpectObject("true&&undetectable", obj); |
| ExpectBoolean("false&&undetectable", false); |
| ExpectBoolean("true||undetectable", true); |
| ExpectObject("false||undetectable", obj); |
| |
| ExpectObject("undetectable&&true", obj); |
| ExpectObject("undetectable&&false", obj); |
| ExpectBoolean("undetectable||true", true); |
| ExpectBoolean("undetectable||false", false); |
| |
| ExpectBoolean("undetectable==null", true); |
| ExpectBoolean("null==undetectable", true); |
| ExpectBoolean("undetectable==undefined", true); |
| ExpectBoolean("undefined==undetectable", true); |
| ExpectBoolean("undetectable==undetectable", true); |
| |
| ExpectBoolean("undetectable===null", false); |
| ExpectBoolean("null===undetectable", false); |
| ExpectBoolean("undetectable===undefined", false); |
| ExpectBoolean("undefined===undetectable", false); |
| ExpectBoolean("undetectable===undetectable", true); |
| } |
| |
| |
| static int Recurse(v8::Isolate* isolate, int depth, int iterations) { |
| v8::HandleScope scope(isolate); |
| if (depth == 0) return v8::HandleScope::NumberOfHandles(isolate); |
| for (int i = 0; i < iterations; i++) { |
| Local<v8::Number> n(v8::Integer::New(isolate, 42)); |
| } |
| return Recurse(isolate, depth - 1, iterations); |
| } |
| |
| |
| THREADED_TEST(HandleIteration) { |
| static const int kIterations = 500; |
| static const int kNesting = 200; |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope0(isolate); |
| CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| { |
| v8::HandleScope scope1(isolate); |
| CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| for (int i = 0; i < kIterations; i++) { |
| Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42)); |
| CHECK_EQ(i + 1, v8::HandleScope::NumberOfHandles(isolate)); |
| } |
| |
| CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate)); |
| { |
| v8::HandleScope scope2(CcTest::isolate()); |
| for (int j = 0; j < kIterations; j++) { |
| Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42)); |
| CHECK_EQ(j + 1 + kIterations, |
| v8::HandleScope::NumberOfHandles(isolate)); |
| } |
| } |
| CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate)); |
| } |
| CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| CHECK_EQ(kNesting * kIterations, Recurse(isolate, kNesting, kIterations)); |
| } |
| |
| |
| static void InterceptorCallICFastApi( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi)); |
| int* call_count = |
| reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value()); |
| ++(*call_count); |
| if ((*call_count) % 20 == 0) { |
| CcTest::CollectAllGarbage(); |
| } |
| } |
| |
| static void FastApiCallback_TrivialSignature( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_TrivialSignature)); |
| v8::Isolate* isolate = CcTest::isolate(); |
| CHECK_EQ(isolate, args.GetIsolate()); |
| CHECK(args.This() |
| ->Equals(isolate->GetCurrentContext(), args.Holder()) |
| .FromJust()); |
| CHECK(args.Data() |
| ->Equals(isolate->GetCurrentContext(), v8_str("method_data")) |
| .FromJust()); |
| args.GetReturnValue().Set( |
| args[0]->Int32Value(isolate->GetCurrentContext()).FromJust() + 1); |
| } |
| |
| static void FastApiCallback_SimpleSignature( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_SimpleSignature)); |
| v8::Isolate* isolate = CcTest::isolate(); |
| CHECK_EQ(isolate, args.GetIsolate()); |
| CHECK(args.This() |
| ->GetPrototype() |
| ->Equals(isolate->GetCurrentContext(), args.Holder()) |
| .FromJust()); |
| CHECK(args.Data() |
| ->Equals(isolate->GetCurrentContext(), v8_str("method_data")) |
| .FromJust()); |
| // Note, we're using HasRealNamedProperty instead of Has to avoid |
| // invoking the interceptor again. |
| CHECK(args.Holder() |
| ->HasRealNamedProperty(isolate->GetCurrentContext(), v8_str("foo")) |
| .FromJust()); |
| args.GetReturnValue().Set( |
| args[0]->Int32Value(isolate->GetCurrentContext()).FromJust() + 1); |
| } |
| |
| |
| // Helper to maximize the odds of object moving. |
| static void GenerateSomeGarbage() { |
| CompileRun( |
| "var garbage;" |
| "for (var i = 0; i < 1000; i++) {" |
| " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" |
| "}" |
| "garbage = undefined;"); |
| } |
| |
| |
| void DirectApiCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| static int count = 0; |
| if (count++ % 3 == 0) { |
| CcTest::CollectAllGarbage(); |
| // This should move the stub |
| GenerateSomeGarbage(); // This should ensure the old stub memory is flushed |
| } |
| } |
| |
| |
| THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> nativeobject_templ = |
| v8::ObjectTemplate::New(isolate); |
| nativeobject_templ->Set(isolate, "callback", |
| v8::FunctionTemplate::New(isolate, |
| DirectApiCallback)); |
| v8::Local<v8::Object> nativeobject_obj = |
| nativeobject_templ->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("nativeobject"), nativeobject_obj) |
| .FromJust()); |
| // call the api function multiple times to ensure direct call stub creation. |
| CompileRun( |
| "function f() {" |
| " for (var i = 1; i <= 30; i++) {" |
| " nativeobject.callback();" |
| " }" |
| "}" |
| "f();"); |
| } |
| |
| |
| void ThrowingDirectApiCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| args.GetIsolate()->ThrowException(v8_str("g")); |
| } |
| |
| |
| THREADED_TEST(CallICFastApi_DirectCall_Throw) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> nativeobject_templ = |
| v8::ObjectTemplate::New(isolate); |
| nativeobject_templ->Set(isolate, "callback", |
| v8::FunctionTemplate::New(isolate, |
| ThrowingDirectApiCallback)); |
| v8::Local<v8::Object> nativeobject_obj = |
| nativeobject_templ->NewInstance(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("nativeobject"), nativeobject_obj) |
| .FromJust()); |
| // call the api function multiple times to ensure direct call stub creation. |
| v8::Local<Value> result = CompileRun( |
| "var result = '';" |
| "function f() {" |
| " for (var i = 1; i <= 5; i++) {" |
| " try { nativeobject.callback(); } catch (e) { result += e; }" |
| " }" |
| "}" |
| "f(); result;"); |
| CHECK(v8_str("ggggg")->Equals(context.local(), result).FromJust()); |
| } |
| |
| |
| static int p_getter_count_3; |
| |
| |
| static Local<Value> DoDirectGetter() { |
| if (++p_getter_count_3 % 3 == 0) { |
| CcTest::CollectAllGarbage(); |
| GenerateSomeGarbage(); |
| } |
| return v8_str("Direct Getter Result"); |
| } |
| |
| |
| static void DirectGetterCallback( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, FUNCTION_ADDR(DirectGetterCallback)); |
| info.GetReturnValue().Set(DoDirectGetter()); |
| } |
| |
| |
| template<typename Accessor> |
| static void LoadICFastApi_DirectCall_GCMoveStub(Accessor accessor) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("p1"), accessor); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o1"), |
| obj->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| p_getter_count_3 = 0; |
| v8::Local<v8::Value> result = CompileRun( |
| "function f() {" |
| " for (var i = 0; i < 30; i++) o1.p1;" |
| " return o1.p1" |
| "}" |
| "f();"); |
| CHECK(v8_str("Direct Getter Result") |
| ->Equals(context.local(), result) |
| .FromJust()); |
| CHECK_EQ(31, p_getter_count_3); |
| } |
| |
| |
| THREADED_PROFILED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { |
| LoadICFastApi_DirectCall_GCMoveStub(DirectGetterCallback); |
| } |
| |
| |
| void ThrowingDirectGetterCallback( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetIsolate()->ThrowException(v8_str("g")); |
| } |
| |
| |
| THREADED_TEST(LoadICFastApi_DirectCall_Throw) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o1"), |
| obj->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::Local<Value> result = CompileRun( |
| "var result = '';" |
| "for (var i = 0; i < 5; i++) {" |
| " try { o1.p1; } catch (e) { result += e; }" |
| "}" |
| "result;"); |
| CHECK(v8_str("ggggg")->Equals(context.local(), result).FromJust()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_TrivialSignature) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_TrivialSignature, v8_str("method_data"), |
| v8::Local<v8::Signature>()); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = o.method(41);" |
| "}"); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(100, interceptor_call_count); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| "}"); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(100, interceptor_call_count); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = {method: function(x) { return x - 1 }};" |
| " }" |
| "}"); |
| CHECK_EQ(40, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " o.method = function(x) { return x - 1 };" |
| " }" |
| "}"); |
| CHECK_EQ(40, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = 333;" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| // TODO(verwaest): Adjust message. |
| CHECK( |
| v8_str("TypeError: receiver.method is not a function") |
| ->Equals( |
| context.local(), |
| try_catch.Exception()->ToString(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = {method: receiver.method};" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK( |
| v8_str("TypeError: Illegal invocation") |
| ->Equals( |
| context.local(), |
| try_catch.Exception()->ToString(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_TrivialSignature) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_TrivialSignature, v8_str("method_data"), |
| v8::Local<v8::Signature>()); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| USE(templ); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = o.method(41);" |
| "}"); |
| |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| "}"); |
| |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss1) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = {method: function(x) { return x - 1 }};" |
| " }" |
| "}"); |
| CHECK_EQ(40, context->Global() |
| ->Get(context.local(), v8_str("result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss2) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = 333;" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| // TODO(verwaest): Adjust message. |
| CHECK( |
| v8_str("TypeError: receiver.method is not a function") |
| ->Equals( |
| context.local(), |
| try_catch.Exception()->ToString(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_TypeError) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Local<v8::Function> fun = |
| fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| GenerateSomeGarbage(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), |
| fun->NewInstance(context.local()).ToLocalChecked()) |
| .FromJust()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = Object.create(receiver);" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK( |
| v8_str("TypeError: Illegal invocation") |
| ->Equals( |
| context.local(), |
| try_catch.Exception()->ToString(context.local()).ToLocalChecked()) |
| .FromJust()); |
| CHECK_EQ(42, context->Global() |
| ->Get(context.local(), v8_str("saved_result")) |
| .ToLocalChecked() |
| ->Int32Value(context.local()) |
| .FromJust()); |
| } |
| |
| |
| static void ThrowingGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetIsolate()->ThrowException(Local<Value>()); |
| info.GetReturnValue().SetUndefined(); |
| } |
| |
| |
| THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { |
| LocalContext context; |
| HandleScope scope(context->GetIsolate()); |
| |
| Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetAccessor(v8_str("f"), ThrowingGetter); |
| |
| Local<Object> instance = templ->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| Local<Object> another = Object::New(context->GetIsolate()); |
| CHECK(another->SetPrototype(context.local(), instance).FromJust()); |
| |
| Local<Object> with_js_getter = CompileRun( |
| "o = {};\n" |
| "o.__defineGetter__('f', function() { throw undefined; });\n" |
| "o\n").As<Object>(); |
| CHECK(!with_js_getter.IsEmpty()); |
| |
| TryCatch try_catch(context->GetIsolate()); |
| |
| v8::MaybeLocal<Value> result = |
| instance->GetRealNamedProperty(context.local(), v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| Maybe<PropertyAttribute> attr = |
| instance->GetRealNamedPropertyAttributes(context.local(), v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = another->GetRealNamedProperty(context.local(), v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| attr = another->GetRealNamedPropertyAttributes(context.local(), v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = another->GetRealNamedPropertyInPrototypeChain(context.local(), |
| v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| attr = another->GetRealNamedPropertyAttributesInPrototypeChain( |
| context.local(), v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = another->Get(context.local(), v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| result = with_js_getter->GetRealNamedProperty(context.local(), v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| attr = with_js_getter->GetRealNamedPropertyAttributes(context.local(), |
| v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = with_js_getter->Get(context.local(), v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| Local<Object> target = CompileRun("({})").As<Object>(); |
| Local<Object> handler = CompileRun("({})").As<Object>(); |
| Local<v8::Proxy> proxy = |
| v8::Proxy::New(context.local(), target, handler).ToLocalChecked(); |
| |
| result = target->GetRealNamedProperty(context.local(), v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(result.IsEmpty()); |
| |
| result = proxy->GetRealNamedProperty(context.local(), v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(result.IsEmpty()); |
| } |
| |
| |
| static void ThrowingCallbackWithTryCatch( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| TryCatch try_catch(args.GetIsolate()); |
| // Verboseness is important: it triggers message delivery which can call into |
| // external code. |
| try_catch.SetVerbose(true); |
| CompileRun("throw 'from JS';"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(!CcTest::i_isolate()->has_pending_exception()); |
| CHECK(!CcTest::i_isolate()->has_scheduled_exception()); |
| } |
| |
| |
| static int call_depth; |
| |
| |
| static void WithTryCatch(Local<Message> message, Local<Value> data) { |
| TryCatch try_catch(CcTest::isolate()); |
| } |
| |
| |
| static void ThrowFromJS(Local<Message> message, Local<Value> data) { |
| if (--call_depth) CompileRun("throw 'ThrowInJS';"); |
| } |
| |
| |
| static void ThrowViaApi(Local<Message> message, Local<Value> data) { |
| if (--call_depth) CcTest::isolate()->ThrowException(v8_str("ThrowViaApi")); |
| } |
| |
| |
| static void WebKitLike(Local<Message> message, Local<Value> data) { |
| Local<String> errorMessageString = message->Get(); |
| CHECK(!errorMessageString.IsEmpty()); |
| message->GetStackTrace(); |
| message->GetScriptOrigin().ResourceName(); |
| } |
| |
| |
| THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| HandleScope scope(isolate); |
| |
| Local<Function> func = |
| FunctionTemplate::New(isolate, ThrowingCallbackWithTryCatch) |
| ->GetFunction(context.local()) |
| .ToLocalChecked(); |
| CHECK( |
| context->Global()->Set(context.local(), v8_str("func"), func).FromJust()); |
| |
| MessageCallback callbacks[] = {nullptr, WebKitLike, ThrowViaApi, ThrowFromJS, |
| WithTryCatch}; |
| for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { |
| MessageCallback callback = callbacks[i]; |
| if (callback != nullptr) { |
| isolate->AddMessageListener(callback); |
| } |
| // Some small number to control number of times message handler should |
| // throw an exception. |
| call_depth = 5; |
| ExpectFalse( |
| "var thrown = false;\n" |
| "try { func(); } catch(e) { thrown = true; }\n" |
| "thrown\n"); |
| if (callback != nullptr) { |
| isolate->RemoveMessageListeners(callback); |
| } |
| } |
| } |
| |
| |
| static void ParentGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(1)); |
| } |
| |
| |
| static void ChildGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(42)); |
| } |
| |
| |
| THREADED_TEST(Overriding) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Parent template. |
| Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> parent_instance_templ = |
| parent_templ->InstanceTemplate(); |
| parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); |
| |
| // Template that inherits from the parent template. |
| Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> child_instance_templ = |
| child_templ->InstanceTemplate(); |
| child_templ->Inherit(parent_templ); |
| // Override 'f'. The child version of 'f' should get called for child |
| // instances. |
| child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); |
| // Add 'g' twice. The 'g' added last should get called for instances. |
| child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); |
| child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); |
| |
| // Add 'h' as an accessor to the proto template with ReadOnly attributes |
| // so 'h' can be shadowed on the instance object. |
| Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); |
| child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, |
| v8::Local<Value>(), v8::DEFAULT, v8::ReadOnly); |
| |
| // Add 'i' as an accessor to the instance template with ReadOnly attributes |
| // but the attribute does not have effect because it is duplicated with |
| // nullptr setter. |
| child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, |
| v8::Local<Value>(), v8::DEFAULT, |
| v8::ReadOnly); |
| |
| |
| // Instantiate the child template. |
| Local<v8::Object> instance = child_templ->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| // Check that the child function overrides the parent one. |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("o"), instance) |
| .FromJust()); |
| Local<Value> value = v8_compile("o.f")->Run(context.local()).ToLocalChecked(); |
| // Check that the 'g' that was added last is hit. |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| value = v8_compile("o.g")->Run(context.local()).ToLocalChecked(); |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| |
| // Check that 'h' cannot be shadowed. |
| value = v8_compile("o.h = 3; o.h")->Run(context.local()).ToLocalChecked(); |
| CHECK_EQ(1, value->Int32Value(context.local()).FromJust()); |
| |
| // Check that 'i' cannot be shadowed or changed. |
| value = v8_compile("o.i = 3; o.i")->Run(context.local()).ToLocalChecked(); |
| CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| } |
| |
| |
| static void ShouldThrowOnErrorGetter( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = info.GetIsolate(); |
| Local<Boolean> should_throw_on_error = |
| Boolean::New(isolate, info.ShouldThrowOnError()); |
| info.GetReturnValue().Set(should_throw_on_error); |
| } |
| |
| |
| template <typename T> |
| static void ShouldThrowOnErrorSetter(Local<Name> name, Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<T>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = info.GetIsolate(); |
| auto context = isolate->GetCurrentContext(); |
| Local<Boolean> should_throw_on_error_value = |
| Boolean::New(isolate, info.ShouldThrowOnError()); |
| CHECK(context->Global() |
| ->Set(isolate->GetCurrentContext(), v8_str("should_throw_setter"), |
| should_throw_on_error_value) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(AccessorShouldThrowOnError) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<Object> global = context->Global(); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetAccessor(v8_str("f"), ShouldThrowOnErrorGetter, |
| ShouldThrowOnErrorSetter<void>); |
| |
| Local<v8::Object> instance = templ->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| |
| CHECK(global->Set(context.local(), v8_str("o"), instance).FromJust()); |
| |
| // SLOPPY mode |
| Local<Value> value = v8_compile("o.f")->Run(context.local()).ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| v8_compile("o.f = 153")->Run(context.local()).ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_setter")) |
| .ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| |
| // STRICT mode |
| value = v8_compile("'use strict';o.f")->Run(context.local()).ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| v8_compile("'use strict'; o.f = 153")->Run(context.local()).ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_setter")) |
| .ToLocalChecked(); |
| CHECK(value->IsTrue()); |
| } |
| |
| |
| static void ShouldThrowOnErrorQuery( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = info.GetIsolate(); |
| info.GetReturnValue().Set(v8::None); |
| |
| auto context = isolate->GetCurrentContext(); |
| Local<Boolean> should_throw_on_error_value = |
| Boolean::New(isolate, info.ShouldThrowOnError()); |
| CHECK(context->Global() |
| ->Set(isolate->GetCurrentContext(), v8_str("should_throw_query"), |
| should_throw_on_error_value) |
| .FromJust()); |
| } |
| |
| |
| static void ShouldThrowOnErrorDeleter( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = info.GetIsolate(); |
| info.GetReturnValue().Set(v8::True(isolate)); |
| |
| auto context = isolate->GetCurrentContext(); |
| Local<Boolean> should_throw_on_error_value = |
| Boolean::New(isolate, info.ShouldThrowOnError()); |
| CHECK(context->Global() |
| ->Set(isolate->GetCurrentContext(), v8_str("should_throw_deleter"), |
| should_throw_on_error_value) |
| .FromJust()); |
| } |
| |
| |
| static void ShouldThrowOnErrorPropertyEnumerator( |
| const v8::PropertyCallbackInfo<v8::Array>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = info.GetIsolate(); |
| Local<v8::Array> names = v8::Array::New(isolate, 1); |
| CHECK(names->Set(isolate->GetCurrentContext(), names, v8_num(1)).FromJust()); |
| info.GetReturnValue().Set(names); |
| |
| auto context = isolate->GetCurrentContext(); |
| Local<Boolean> should_throw_on_error_value = |
| Boolean::New(isolate, info.ShouldThrowOnError()); |
| CHECK(context->Global() |
| ->Set(isolate->GetCurrentContext(), |
| v8_str("should_throw_enumerator"), |
| should_throw_on_error_value) |
| .FromJust()); |
| } |
| |
| |
| THREADED_TEST(InterceptorShouldThrowOnError) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<Object> global = context->Global(); |
| |
| auto interceptor_templ = v8::ObjectTemplate::New(isolate); |
| v8::NamedPropertyHandlerConfiguration handler( |
| ShouldThrowOnErrorGetter, ShouldThrowOnErrorSetter<Value>, |
| ShouldThrowOnErrorQuery, ShouldThrowOnErrorDeleter, |
| ShouldThrowOnErrorPropertyEnumerator); |
| interceptor_templ->SetHandler(handler); |
| |
| Local<v8::Object> instance = |
| interceptor_templ->NewInstance(context.local()).ToLocalChecked(); |
| |
| CHECK(global->Set(context.local(), v8_str("o"), instance).FromJust()); |
| |
| // SLOPPY mode |
| Local<Value> value = v8_compile("o.f")->Run(context.local()).ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| v8_compile("o.f = 153")->Run(context.local()).ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_setter")) |
| .ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| |
| v8_compile("delete o.f")->Run(context.local()).ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_deleter")) |
| .ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| |
| v8_compile("Object.getOwnPropertyNames(o)") |
| ->Run(context.local()) |
| .ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_enumerator")) |
| .ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| |
| // STRICT mode |
| value = v8_compile("'use strict';o.f")->Run(context.local()).ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| v8_compile("'use strict'; o.f = 153")->Run(context.local()).ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_setter")) |
| .ToLocalChecked(); |
| CHECK(value->IsTrue()); |
| |
| v8_compile("'use strict'; delete o.f")->Run(context.local()).ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_deleter")) |
| .ToLocalChecked(); |
| CHECK(value->IsTrue()); |
| |
| v8_compile("'use strict'; Object.getOwnPropertyNames(o)") |
| ->Run(context.local()) |
| .ToLocalChecked(); |
| value = global->Get(context.local(), v8_str("should_throw_enumerator")) |
| .ToLocalChecked(); |
| CHECK(value->IsFalse()); |
| } |
| |
| |
| static void IsConstructHandler( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(args.IsConstructCall()); |
| } |
| |
| |
| THREADED_TEST(IsConstructCall) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Function template with call handler. |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetCallHandler(IsConstructHandler); |
| |
| LocalContext context; |
| |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("f"), |
| templ->GetFunction(context.local()).ToLocalChecked()) |
| .FromJust()); |
| Local<Value> value = v8_compile("f()")->Run(context.local()).ToLocalChecked(); |
| CHECK(!value->BooleanValue(context.local()).FromJust()); |
| value = v8_compile("new f()")->Run(context.local()).ToLocalChecked(); |
| CHECK(value->BooleanValue(context.local()).FromJust()); |
| } |
| |
| static void NewTargetHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(args.NewTarget()); |
| } |
| |
| THREADED_TEST(NewTargetHandler) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Function template with call handler. |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetCallHandler(NewTargetHandler); |
| |
| LocalContext context; |
| |
| Local<Function> function = |
| templ->GetFunction(context.local()).ToLocalChecked(); |
| CHECK(context->Global() |
| ->Set(context.local(), v8_str("f"), function) |
| .FromJust()); |
| Local<Value> value = CompileRun("f()"); |
| CHECK(value->IsUndefined()); |
| value = CompileRun("new f()"); |
| CHECK(value->IsFunction()); |
| CHECK(value == function); |
| Local<Value> subclass = CompileRun("var g = class extends f { }; g"); |
| CHECK(subclass->IsFunction()); |
| value = CompileRun("new g()"); |
| CHECK(value->IsFunction()); |
| CHECK(value == subclass); |
| value = CompileRun("Reflect.construct(f, [], Array)"); |
| CHECK(value->IsFunction()); |
| CHECK(value == |
| context->Global() |
| ->Get(context.local(), v8_str("Array")) |
| .ToLocalChecked()); |
| } |
| |
| THREADED_TEST(ObjectProtoToString) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("MyClass")); |
| |
| LocalContext context; |
| |
| Local<String> customized_tostring = v8_str("customized toString"); |
| |
| // Replace Object.prototype.toString |
| v8_compile( |
| "Object.prototype.toString = function() {" |
| " return 'customized toString';" |
| "}") |
| ->Run(context.local()) |
| .ToLocalChecked(); |
| |
| // Normal ToString call should call replaced Object.prototype.toString |
| Local<v8::Object> instance = templ->GetFunction(context.local()) |
| .ToLocalChecked() |
| ->NewInstance(context.local()) |
| .ToLocalChecked(); |
| Local<String> value = instance->ToString(context.local()).ToLocalChecked(); |
| CHECK(value->IsString() && |
| value->Equals(context.local(), customized_tostring).FromJust()); |
| |
| // ObjectProtoToString should not call replace toString function. |
| value = instance->ObjectProtoToString(context.local()).ToLocalChecked(); |
| CHECK(value->IsString() && |
| value->Equals(context.local(), v8_str("[object MyClass]")).FromJust()); |
| |
| // Check global |
| value = |
| context->Global()->ObjectProtoToString(context.local()).ToLocalChecked(); |
| CHECK(value->IsString() && |
| value->Equals(context.local(), v8_str("[object Object]")).FromJust()); |
| |
| // Check ordinary object |
| Local<Value> object = |
| v8_compile("new Object()")->Run(context.local()).ToLocalChecked(); |
| value = object.As<v8::Object>() |
| ->ObjectProtoToString(context.local()) |
| .ToLocalChecked(); |
| CHECK(value->IsString() && |
| value->Equals(context.local(), v8_str("[object Object]")).FromJust()); |
| } |
| |
| |
|