| // 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("ca
|