| // 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 <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/compilation-cache.h" |
| #include "src/debug.h" |
| #include "src/execution.h" |
| #include "src/objects.h" |
| #include "src/parser.h" |
| #include "src/smart-pointers.h" |
| #include "src/unicode-inl.h" |
| #include "src/utils.h" |
| #include "src/vm-state.h" |
| |
| static const bool kLogThreading = false; |
| |
| using ::v8::Boolean; |
| using ::v8::BooleanObject; |
| using ::v8::Context; |
| using ::v8::Extension; |
| using ::v8::Function; |
| using ::v8::FunctionTemplate; |
| using ::v8::Handle; |
| using ::v8::HandleScope; |
| using ::v8::Local; |
| using ::v8::Maybe; |
| using ::v8::Message; |
| using ::v8::MessageCallback; |
| 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::UniqueId; |
| 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::String::NewFromUtf8(env->GetIsolate(), "my_profile1"); |
| v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); |
| |
| cpu_profiler->StartProfiling(profile_name); |
| (*test)(); |
| reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->DeleteAllProfiles(); |
| } |
| |
| |
| 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.Holder())); |
| CHECK(signature_expected_receiver->Equals(args.This())); |
| v8::Handle<v8::Array> result = |
| v8::Array::New(args.GetIsolate(), args.Length()); |
| for (int i = 0; i < args.Length(); i++) |
| result->Set(v8::Integer::New(args.GetIsolate(), i), args[i]); |
| 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()); |
| } |
| |
| |
| 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::Handle<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, script->Run()->Int32Value()); |
| |
| local_env->Exit(); |
| } |
| |
| |
| THREADED_TEST(IsolateOfContext) { |
| v8::HandleScope scope(CcTest::isolate()); |
| v8::Handle<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 TestSignature(const char* loop_js, Local<Value> receiver, |
| v8::Isolate* isolate) { |
| i::ScopedVector<char> source(200); |
| i::SNPrintF(source, |
| "for (var i = 0; i < 10; i++) {" |
| " %s" |
| "}", |
| loop_js); |
| 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(try_catch.Exception()->ToString(isolate))); |
| } |
| } |
| |
| |
| THREADED_TEST(ReceiverSignature) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| // Setup templates. |
| v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::Signature> sig = v8::Signature::New(isolate, fun); |
| v8::Handle<v8::FunctionTemplate> callback_sig = |
| v8::FunctionTemplate::New( |
| isolate, IncrementingSignatureCallback, Local<Value>(), sig); |
| v8::Handle<v8::FunctionTemplate> callback = |
| v8::FunctionTemplate::New(isolate, IncrementingSignatureCallback); |
| v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(isolate); |
| sub_fun->Inherit(fun); |
| v8::Handle<v8::FunctionTemplate> unrel_fun = |
| v8::FunctionTemplate::New(isolate); |
| // Install properties. |
| v8::Handle<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(); |
| Local<Value> sub_fun_instance = sub_fun->InstanceTemplate()->NewInstance(); |
| // Setup global variables. |
| env->Global()->Set(v8_str("Fun"), fun->GetFunction()); |
| env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); |
| env->Global()->Set(v8_str("fun_instance"), fun_instance); |
| env->Global()->Set(v8_str("sub_fun_instance"), sub_fun_instance); |
| 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 obj = {};" |
| "copy_props(obj);" |
| "var unrel = new UnrelFun();" |
| "copy_props(unrel);"); |
| // Test with and without ICs |
| const char* test_objects[] = { |
| "fun_instance", "sub_fun_instance", "obj", "unrel" }; |
| unsigned bad_signature_start_offset = 2; |
| 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::Handle<v8::Primitive> undef = v8::Undefined(isolate); |
| Local<String> undef_str = undef->ToString(isolate); |
| 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(v8_str("foo")); |
| CHECK(foo_before->IsUndefined()); |
| Local<String> bar_str = v8_str("bar"); |
| obj->Set(v8_str("foo"), bar_str); |
| Local<Value> foo_after = obj->Get(v8_str("foo")); |
| CHECK(!foo_after->IsUndefined()); |
| CHECK(foo_after->IsString()); |
| CHECK(bar_str->Equals(foo_after)); |
| } |
| |
| |
| THREADED_TEST(AccessElement) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| Local<Value> before = obj->Get(1); |
| CHECK(before->IsUndefined()); |
| Local<String> bar_str = v8_str("bar"); |
| obj->Set(1, bar_str); |
| Local<Value> after = obj->Get(1); |
| CHECK(!after->IsUndefined()); |
| CHECK(after->IsString()); |
| CHECK(bar_str->Equals(after)); |
| |
| Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); |
| CHECK(v8_str("a")->Equals(value->Get(0))); |
| CHECK(v8_str("b")->Equals(value->Get(1))); |
| } |
| |
| |
| 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, script->Run()->Int32Value()); |
| } |
| |
| |
| class TestResource: public String::ExternalStringResource { |
| public: |
| explicit TestResource(uint16_t* data, int* counter = NULL, |
| 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_ != NULL) ++*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 = NULL, |
| size_t offset = 0) |
| : orig_data_(data), |
| data_(data + offset), |
| length_(strlen(data) - offset), |
| counter_(counter) {} |
| |
| ~TestOneByteResource() { |
| i::DeleteArray(orig_data_); |
| if (counter_ != NULL) ++*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::NewExternal(env->GetIsolate(), resource); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value()); |
| 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::heap()->CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->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::NewExternal(env->GetIsolate(), resource); |
| 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(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value()); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->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); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
| CHECK_EQ(source->IsExternal(), false); |
| CHECK_EQ(source->IsExternalOneByte(), false); |
| 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(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value()); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->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::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->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(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value()); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(0, dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->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::heap()->CollectGarbage(i::NEW_SPACE); |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| |
| uint16_t* two_byte_string = AsciiToTwoByteString("s1"); |
| Local<String> small_string = |
| String::NewFromTwoByte(env->GetIsolate(), two_byte_string); |
| i::DeleteArray(two_byte_string); |
| |
| // We should refuse to externalize newly created small string. |
| CHECK(!small_string->CanMakeExternal()); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
| // Old space strings should be accepted. |
| CHECK(small_string->CanMakeExternal()); |
| |
| two_byte_string = AsciiToTwoByteString("small string 2"); |
| small_string = String::NewFromTwoByte(env->GetIsolate(), two_byte_string); |
| i::DeleteArray(two_byte_string); |
| |
| // We should refuse externalizing newly created small string. |
| CHECK(!small_string->CanMakeExternal()); |
| for (int i = 0; i < 100; i++) { |
| String::Value value(small_string); |
| } |
| // Frequently used strings should be accepted. |
| CHECK(small_string->CanMakeExternal()); |
| |
| const int buf_size = 10 * 1024; |
| char* buf = i::NewArray<char>(buf_size); |
| memset(buf, 'a', buf_size); |
| buf[buf_size - 1] = '\0'; |
| |
| two_byte_string = AsciiToTwoByteString(buf); |
| Local<String> large_string = |
| String::NewFromTwoByte(env->GetIsolate(), two_byte_string); |
| i::DeleteArray(buf); |
| i::DeleteArray(two_byte_string); |
| // Large strings should be immediately accepted. |
| CHECK(large_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::heap()->CollectGarbage(i::NEW_SPACE); |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| |
| Local<String> small_string = String::NewFromUtf8(env->GetIsolate(), "s1"); |
| // We should refuse to externalize newly created small string. |
| CHECK(!small_string->CanMakeExternal()); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
| // Old space strings should be accepted. |
| CHECK(small_string->CanMakeExternal()); |
| |
| small_string = String::NewFromUtf8(env->GetIsolate(), "small string 2"); |
| // We should refuse externalizing newly created small string. |
| CHECK(!small_string->CanMakeExternal()); |
| for (int i = 0; i < 100; i++) { |
| String::Value value(small_string); |
| } |
| // Frequently used strings should be accepted. |
| CHECK(small_string->CanMakeExternal()); |
| |
| const int buf_size = 10 * 1024; |
| char* buf = i::NewArray<char>(buf_size); |
| memset(buf, 'a', buf_size); |
| buf[buf_size - 1] = '\0'; |
| Local<String> large_string = String::NewFromUtf8(env->GetIsolate(), buf); |
| i::DeleteArray(buf); |
| // Large strings should be immediately accepted. |
| CHECK(large_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. |
| SimulateFullSpace(CcTest::heap()->old_space()); |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->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), NULL, 1)); |
| CHECK(success); |
| const char* c_slice = "_bcdefghijklmnopqrstuvwxyz"; |
| success = |
| slice->MakeExternal(new TestOneByteResource(i::StrDup(c_slice), NULL, 1)); |
| CHECK(success); |
| |
| // Trigger GCs and force evacuation. |
| CcTest::heap()->CollectAllGarbage(); |
| CcTest::heap()->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::NewExternal( |
| CcTest::isolate(), new TestResource(two_byte_string)); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
| i::Handle<i::String> isymbol = |
| factory->InternalizeString(istring); |
| CHECK(isymbol->IsInternalizedString()); |
| } |
| CcTest::heap()->CollectAllGarbage(); |
| CcTest::heap()->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::NewExternal( |
| CcTest::isolate(), new TestOneByteResource(i::StrDup(one_byte_string))); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| // Trigger GCs so that the newly allocated string moves to old gen. |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
| i::Handle<i::String> isymbol = |
| factory->InternalizeString(istring); |
| CHECK(isymbol->IsInternalizedString()); |
| } |
| CcTest::heap()->CollectAllGarbage(); |
| CcTest::heap()->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::Local<v8::String> str = v8::String::NewExternal(isolate, &r); |
| CHECK(str.IsEmpty()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| v8::HandleScope scope(isolate); |
| v8::TryCatch try_catch(isolate); |
| RandomLengthResource r(1 << 30); |
| v8::Local<v8::String> str = v8::String::NewExternal(isolate, &r); |
| CHECK(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::NewExternal( |
| CcTest::isolate(), new TestResource(two_byte_string, &dispose_count)); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| CcTest::heap()->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::heap()->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::NewExternal( |
| CcTest::isolate(), |
| new TestOneByteResource(i::StrDup(one_byte_string), &dispose_count)); |
| i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| CcTest::heap()->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::heap()->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::NewExternal(env->GetIsolate(), &res_stack); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value()); |
| CcTest::heap()->CollectAllAvailableGarbage(); |
| CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->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::NewExternal(env->GetIsolate(), res_heap); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(7, value->Int32Value()); |
| CcTest::heap()->CollectAllAvailableGarbage(); |
| CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->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); |
| i::DeleteArray(two_byte_source); |
| |
| Local<String> source = String::Concat(left, right); |
| right = String::NewExternal( |
| env->GetIsolate(), |
| new TestOneByteResource(i::StrDup(one_byte_extern_1))); |
| source = String::Concat(source, right); |
| right = String::NewExternal( |
| env->GetIsolate(), |
| new TestResource(AsciiToTwoByteString(two_byte_extern_1))); |
| 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); |
| i::DeleteArray(two_byte_source); |
| |
| source = String::Concat(source, right); |
| right = String::NewExternal( |
| env->GetIsolate(), |
| new TestResource(AsciiToTwoByteString(two_byte_extern_2))); |
| source = String::Concat(source, right); |
| Local<Script> script = v8_compile(source); |
| Local<Value> value = script->Run(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(68, value->Int32Value()); |
| } |
| CcTest::i_isolate()->compilation_cache()->Clear(); |
| CcTest::heap()->CollectAllGarbage(); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| |
| THREADED_TEST(GlobalProperties) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Handle<v8::Object> global = env->Global(); |
| global->Set(v8_str("pi"), v8_num(3.1415926)); |
| Local<Value> pi = global->Get(v8_str("pi")); |
| CHECK_EQ(3.1415926, pi->NumberValue()); |
| } |
| |
| |
| 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)); |
| info.This()->Set(v8_str("x"), v8_num(1)); |
| info.This()->Set(v8_str("y"), v8_num(2)); |
| 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->Global()->Set(v8_str("obj"), fun); |
| Local<Script> script = v8_compile("obj()"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(102, script->Run()->Int32Value()); |
| } |
| } |
| // 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->Global()->Set(v8_str("obj"), fun); |
| Local<Script> script = v8_compile("obj()"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(102, script->Run()->Int32Value()); |
| } |
| } |
| } |
| |
| |
| 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->Global()->Set(v8_str("obj"), fun); |
| Local<Value> result = v8_compile("(new obj()).toString()")->Run(); |
| CHECK(v8_str("[object funky]")->Equals(result)); |
| 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, script->Run()->Int32Value()); |
| } |
| script = v8_compile("obj_instance.m"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(239, script->Run()->Int32Value()); |
| } |
| } |
| |
| |
| 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::Handle<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)->Global()->Set(v8_str("callback_object"), object); |
| v8::Handle<v8::Script> script; |
| script = v8_compile("callback_object.callback(17)"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(51424, script->Run()->Int32Value()); |
| } |
| script = v8_compile("callback_object.callback(17, 24)"); |
| for (int i = 0; i < 30; i++) { |
| CHECK_EQ(51425, script->Run()->Int32Value()); |
| } |
| } |
| |
| |
| 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::Handle<v8::Object> object; |
| if (!fast_return_value_object_is_empty) { |
| object = Object::New(info.GetIsolate()); |
| } |
| info.GetReturnValue().Set(object); |
| } |
| |
| template<typename T> |
| Handle<Value> TestFastReturnValues() { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::EscapableHandleScope scope(isolate); |
| v8::Handle<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)->Global()->Set(v8_str("callback_object"), object); |
| return scope.Escape(CompileRun("callback_object.callback()")); |
| } |
| |
| |
| THREADED_PROFILED_TEST(FastReturnValues) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<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(fast_return_value_int32 == value->Int32Value()); |
| // check uint32_t |
| fast_return_value_uint32 = static_cast<uint32_t>(int_value); |
| value = TestFastReturnValues<uint32_t>(); |
| CHECK(value->IsUint32()); |
| CHECK(fast_return_value_uint32 == value->Uint32Value()); |
| } |
| } |
| // check double |
| value = TestFastReturnValues<double>(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(kFastReturnValueDouble, value->ToNumber(isolate)->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(isolate)->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, |
| Handle<v8::Value>(), |
| Handle<v8::Signature>(), |
| 23); |
| Local<Function> fun = fun_templ->GetFunction(); |
| env->Global()->Set(v8_str("obj"), fun); |
| Local<Script> script = v8_compile("obj.length"); |
| CHECK_EQ(23, script->Run()->Int32Value()); |
| } |
| { |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate, handle_callback); |
| fun_templ->SetLength(22); |
| Local<Function> fun = fun_templ->GetFunction(); |
| env->Global()->Set(v8_str("obj"), fun); |
| Local<Script> script = v8_compile("obj.length"); |
| CHECK_EQ(22, script->Run()->Int32Value()); |
| } |
| { |
| // 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->Global()->Set(v8_str("obj"), fun); |
| Local<Script> script = v8_compile("obj.length"); |
| CHECK_EQ(0, script->Run()->Int32Value()); |
| } |
| } |
| |
| |
| 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::Handle<v8::Value> data = |
| v8::External::New(isolate, expected_ptr); |
| |
| v8::Handle<v8::Object> obj = v8::Object::New(isolate); |
| obj->Set(v8_str("func"), |
| v8::FunctionTemplate::New(isolate, callback, data)->GetFunction()); |
| env->Global()->Set(v8_str("obj"), obj); |
| |
| CHECK(CompileRun( |
| "function foo() {\n" |
| " for (var i = 0; i < 13; i++) obj.func();\n" |
| "}\n" |
| "foo(), true")->BooleanValue()); |
| } |
| |
| |
| 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(); |
| Local<v8::Function> derived_function = derived->GetFunction(); |
| Local<v8::Function> other_function = other->GetFunction(); |
| |
| Local<v8::Object> base_instance = base_function->NewInstance(); |
| Local<v8::Object> derived_instance = derived_function->NewInstance(); |
| Local<v8::Object> derived_instance2 = derived_function->NewInstance(); |
| Local<v8::Object> other_instance = other_function->NewInstance(); |
| derived_instance2->Set(v8_str("__proto__"), derived_instance); |
| other_instance->Set(v8_str("__proto__"), derived_instance2); |
| |
| // base_instance is only an instance of base. |
| CHECK( |
| base_instance->Equals(base_instance->FindInstanceInPrototypeChain(base))); |
| 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( |
| derived_instance->FindInstanceInPrototypeChain(base))); |
| CHECK(derived_instance->Equals( |
| derived_instance->FindInstanceInPrototypeChain(derived))); |
| 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( |
| other_instance->FindInstanceInPrototypeChain(base))); |
| CHECK(derived_instance2->Equals( |
| other_instance->FindInstanceInPrototypeChain(derived))); |
| CHECK(other_instance->Equals( |
| other_instance->FindInstanceInPrototypeChain(other))); |
| } |
| |
| |
| 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(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::Handle<Value> syntax_error = CompileRun( |
| "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); |
| CHECK(syntax_error->IsNativeError()); |
| v8::Handle<Value> not_error = CompileRun("{a:42}"); |
| CHECK(!not_error->IsNativeError()); |
| v8::Handle<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::Handle<Value> gen = CompileRun("gen"); |
| v8::Handle<Value> genObj = CompileRun("gen()"); |
| v8::Handle<Value> object = CompileRun("{a:42}"); |
| v8::Handle<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(ArgumentsObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Handle<Value> arguments_object = |
| CompileRun("var out = 0; (function(){ out = arguments; })(1,2,3); out;"); |
| CHECK(arguments_object->IsArgumentsObject()); |
| v8::Handle<Value> array = CompileRun("[1,2,3]"); |
| CHECK(!array->IsArgumentsObject()); |
| v8::Handle<Value> object = CompileRun("{a:42}"); |
| CHECK(!object->IsArgumentsObject()); |
| } |
| |
| |
| THREADED_TEST(IsMapOrSet) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Handle<Value> map = CompileRun("new Map()"); |
| v8::Handle<Value> set = CompileRun("new Set()"); |
| v8::Handle<Value> weak_map = CompileRun("new WeakMap()"); |
| v8::Handle<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::Handle<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::Handle<Value> boxed_string = CompileRun("new String(\"test\")"); |
| CHECK(boxed_string->IsStringObject()); |
| v8::Handle<Value> unboxed_string = CompileRun("\"test\""); |
| CHECK(!unboxed_string->IsStringObject()); |
| v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)"); |
| CHECK(!boxed_not_string->IsStringObject()); |
| v8::Handle<Value> not_object = CompileRun("0"); |
| CHECK(!not_object->IsStringObject()); |
| v8::Handle<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::Handle<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::Handle<Value> boxed_string = CompileRun("new String(\"test\")"); |
| CHECK(boxed_string->IsStringObject()); |
| v8::Handle<v8::Object> str_obj = boxed_string.As<v8::Object>(); |
| CHECK(!str_obj->Delete(2)); |
| CHECK(!str_obj->Delete(v8_num(2))); |
| } |
| |
| |
| THREADED_TEST(NumberObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Handle<Value> boxed_number = CompileRun("new Number(42)"); |
| CHECK(boxed_number->IsNumberObject()); |
| v8::Handle<Value> unboxed_number = CompileRun("42"); |
| CHECK(!unboxed_number->IsNumberObject()); |
| v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)"); |
| CHECK(!boxed_not_number->IsNumberObject()); |
| v8::Handle<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::Handle<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::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)"); |
| CHECK(boxed_boolean->IsBooleanObject()); |
| v8::Handle<Value> unboxed_boolean = CompileRun("true"); |
| CHECK(!unboxed_boolean->IsBooleanObject()); |
| v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)"); |
| CHECK(!boxed_not_boolean->IsBooleanObject()); |
| v8::Handle<v8::BooleanObject> as_boxed = |
| boxed_boolean.As<v8::BooleanObject>(); |
| CHECK(!as_boxed.IsEmpty()); |
| bool the_boolean = as_boxed->ValueOf(); |
| CHECK_EQ(true, the_boolean); |
| v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true); |
| v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false); |
| CHECK(boxed_true->IsBooleanObject()); |
| CHECK(boxed_false->IsBooleanObject()); |
| as_boxed = boxed_true.As<v8::BooleanObject>(); |
| CHECK_EQ(true, as_boxed->ValueOf()); |
| as_boxed = boxed_false.As<v8::BooleanObject>(); |
| CHECK_EQ(false, 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()); |
| CHECK(!primitive_false->IsTrue()); |
| CHECK(primitive_false->IsFalse()); |
| |
| Local<Value> false_value = BooleanObject::New(false); |
| CHECK(!false_value->IsBoolean()); |
| CHECK(false_value->IsBooleanObject()); |
| CHECK(false_value->BooleanValue()); |
| 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()); |
| // TODO(svenpanne) Uncomment when BooleanObject::BooleanValue() is deleted. |
| // CHECK(false_boolean_object->BooleanValue()); |
| 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()); |
| CHECK(primitive_true->IsTrue()); |
| CHECK(!primitive_true->IsFalse()); |
| |
| Local<Value> true_value = BooleanObject::New(true); |
| CHECK(!true_value->IsBoolean()); |
| CHECK(true_value->IsBooleanObject()); |
| CHECK(true_value->BooleanValue()); |
| 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()); |
| // TODO(svenpanne) Uncomment when BooleanObject::BooleanValue() is deleted. |
| // CHECK(true_boolean_object->BooleanValue()); |
| 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()); |
| } |
| |
| |
| 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()); |
| v8::Handle<v8::Boolean> t = v8::True(isolate); |
| CHECK_EQ(1.0, t->NumberValue()); |
| v8::Handle<v8::Boolean> f = v8::False(isolate); |
| CHECK_EQ(0.0, f->NumberValue()); |
| } |
| |
| |
| THREADED_TEST(Date) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| double PI = 3.1415926; |
| Local<Value> date = v8::Date::New(env->GetIsolate(), PI); |
| CHECK_EQ(3.0, date->NumberValue()); |
| date.As<v8::Date>()->Set(v8_str("property"), |
| v8::Integer::New(env->GetIsolate(), 42)); |
| CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(Boolean) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::Boolean> t = v8::True(isolate); |
| CHECK(t->Value()); |
| v8::Handle<v8::Boolean> f = v8::False(isolate); |
| CHECK(!f->Value()); |
| v8::Handle<v8::Primitive> u = v8::Undefined(isolate); |
| CHECK(!u->BooleanValue()); |
| v8::Handle<v8::Primitive> n = v8::Null(isolate); |
| CHECK(!n->BooleanValue()); |
| v8::Handle<String> str1 = v8_str(""); |
| CHECK(!str1->BooleanValue()); |
| v8::Handle<String> str2 = v8_str("x"); |
| CHECK(str2->BooleanValue()); |
| CHECK(!v8::Number::New(isolate, 0)->BooleanValue()); |
| CHECK(v8::Number::New(isolate, -1)->BooleanValue()); |
| CHECK(v8::Number::New(isolate, 1)->BooleanValue()); |
| CHECK(v8::Number::New(isolate, 42)->BooleanValue()); |
| CHECK(!v8_compile("NaN")->Run()->BooleanValue()); |
| } |
| |
| |
| 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::Handle<v8::FunctionTemplate> func_templ = |
| v8::FunctionTemplate::New(isolate); |
| func_templ->PrototypeTemplate()->Set( |
| isolate, "dummy", v8::FunctionTemplate::New(isolate, DummyCallHandler)); |
| v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); |
| templ->Set(isolate, "x", v8_num(200)); |
| templ->SetAccessor(v8_str("m"), GetM); |
| LocalContext env(0, templ); |
| v8::Handle<Script> script(v8_compile("dummy()")); |
| v8::Handle<Value> result(script->Run()); |
| CHECK_EQ(13.4, result->NumberValue()); |
| CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); |
| CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(ObjectTemplate) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::String> class_name = |
| v8::String::NewFromUtf8(isolate, "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)); |
| LocalContext env; |
| Local<v8::Object> instance1 = templ1->NewInstance(); |
| CHECK(class_name->StrictEquals(instance1->GetConstructorName())); |
| env->Global()->Set(v8_str("p"), instance1); |
| CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); |
| CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); |
| 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); |
| Local<v8::Object> instance2 = templ2->NewInstance(); |
| env->Global()->Set(v8_str("q"), instance2); |
| CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); |
| CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); |
| CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); |
| CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); |
| } |
| |
| |
| 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::Handle<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::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(isolate); |
| base1->Inherit(super); |
| base1->PrototypeTemplate()->Set(isolate, "v1", v8_num(20.1)); |
| |
| v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(isolate); |
| base2->Inherit(super); |
| base2->PrototypeTemplate()->Set(isolate, "v2", v8_num(10.1)); |
| |
| LocalContext env; |
| |
| env->Global()->Set(v8_str("s"), super->GetFunction()); |
| env->Global()->Set(v8_str("base1"), base1->GetFunction()); |
| env->Global()->Set(v8_str("base2"), base2->GetFunction()); |
| |
| // Checks right __proto__ chain. |
| CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); |
| CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); |
| |
| CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); |
| |
| // Instance accessor should not be visible on function object or its prototype |
| CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); |
| CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); |
| CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); |
| |
| env->Global()->Set(v8_str("obj"), |
| base1->GetFunction()->NewInstance()); |
| CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); |
| CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); |
| CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); |
| CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); |
| CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); |
| |
| env->Global()->Set(v8_str("obj2"), |
| base2->GetFunction()->NewInstance()); |
| CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); |
| CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); |
| CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); |
| CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); |
| CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); |
| |
| // base1 and base2 cannot cross reference to each's prototype |
| CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); |
| CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); |
| } |
| |
| |
| // Helper functions for Interceptor/Accessor interaction tests |
| |
| void SimpleAccessorGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| Handle<Object> self = Handle<Object>::Cast(info.This()); |
| info.GetReturnValue().Set( |
| self->Get(String::Concat(v8_str("accessor_"), name))); |
| } |
| |
| void SimpleAccessorSetter(Local<String> name, Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| Handle<Object> self = Handle<Object>::Cast(info.This()); |
| self->Set(String::Concat(v8_str("accessor_"), name), value); |
| } |
| |
| 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(ExecutableAccessorIsPreservedOnAttributeChange) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| LocalContext env; |
| v8::Local<v8::Value> res = CompileRun("var a = []; a;"); |
| i::Handle<i::JSObject> a(v8::Utils::OpenHandle(v8::Object::Cast(*res))); |
| CHECK(a->map()->instance_descriptors()->IsFixedArray()); |
| CHECK_GT(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0); |
| CompileRun("Object.defineProperty(a, 'length', { writable: false });"); |
| CHECK_EQ(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0); |
| // But we should still have an ExecutableAccessorInfo. |
| 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()->IsExecutableAccessorInfo()); |
| } |
| |
| |
| THREADED_TEST(UndefinedIsNotEnumerable) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Handle<Value> result = CompileRun("this.propertyIsEnumerable(undefined)"); |
| CHECK(result->IsFalse()); |
| } |
| |
| |
| v8::Handle<Script> call_recursively_script; |
| static const int kTargetRecursionDepth = 200; // near maximum |
| |
| |
| static void CallScriptRecursivelyCall( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| int depth = args.This()->Get(v8_str("depth"))->Int32Value(); |
| if (depth == kTargetRecursionDepth) return; |
| args.This()->Set(v8_str("depth"), |
| v8::Integer::New(args.GetIsolate(), depth + 1)); |
| args.GetReturnValue().Set(call_recursively_script->Run()); |
| } |
| |
| |
| static void CallFunctionRecursivelyCall( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| int depth = args.This()->Get(v8_str("depth"))->Int32Value(); |
| if (depth == kTargetRecursionDepth) { |
| printf("[depth = %d]\n", depth); |
| return; |
| } |
| args.This()->Set(v8_str("depth"), |
| v8::Integer::New(args.GetIsolate(), depth + 1)); |
| v8::Handle<Value> function = |
| args.This()->Get(v8_str("callFunctionRecursively")); |
| args.GetReturnValue().Set( |
| function.As<Function>()->Call(args.This(), 0, NULL)); |
| } |
| |
| |
| THREADED_TEST(DeepCrossLanguageRecursion) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<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(NULL, global); |
| |
| env->Global()->Set(v8_str("depth"), v8::Integer::New(isolate, 0)); |
| call_recursively_script = v8_compile("callScriptRecursively()"); |
| call_recursively_script->Run(); |
| call_recursively_script = v8::Handle<Script>(); |
| |
| env->Global()->Set(v8_str("depth"), v8::Integer::New(isolate, 0)); |
| CompileRun("callFunctionRecursively()"); |
| } |
| |
| |
| static void ThrowingPropertyHandlerGet( |
| Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| // Since this interceptor is used on "with" objects, the runtime will look up |
| // @@unscopables. Punt. |
| if (key->IsSymbol()) return; |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(info.GetIsolate()->ThrowException(key)); |
| } |
| |
| |
| static void ThrowingPropertyHandlerSet( |
| Local<Name> key, Local<Value>, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetIsolate()->ThrowException(key); |
| info.GetReturnValue().SetUndefined(); // not the same as empty handle |
| } |
| |
| |
| THREADED_TEST(CallbackExceptionRegression) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| ThrowingPropertyHandlerGet, ThrowingPropertyHandlerSet)); |
| LocalContext env; |
| env->Global()->Set(v8_str("obj"), obj->NewInstance()); |
| v8::Handle<Value> otto = |
| CompileRun("try { with (obj) { otto; } } catch (e) { e; }"); |
| CHECK(v8_str("otto")->Equals(otto)); |
| v8::Handle<Value> netto = |
| CompileRun("try { with (obj) { netto = 4; } } catch (e) { e; }"); |
| CHECK(v8_str("netto")->Equals(netto)); |
| } |
| |
| |
| THREADED_TEST(FunctionPrototype) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(isolate); |
| Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); |
| LocalContext env; |
| env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); |
| Local<Script> script = v8_compile("Foo.prototype.plak"); |
| CHECK_EQ(script->Run()->Int32Value(), 321); |
| } |
| |
| |
| THREADED_TEST(InternalFields) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetInternalFieldCount(1); |
| Local<v8::Object> obj = templ->GetFunction()->NewInstance(); |
| CHECK_EQ(1, obj->InternalFieldCount()); |
| CHECK(obj->GetInternalField(0)->IsUndefined()); |
| obj->SetInternalField(0, v8_num(17)); |
| CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectInternalFields) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| global_template->SetInternalFieldCount(1); |
| LocalContext env(NULL, global_template); |
| v8::Handle<v8::Object> global_proxy = env->Global(); |
| v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); |
| CHECK_EQ(1, global->InternalFieldCount()); |
| CHECK(global->GetInternalField(0)->IsUndefined()); |
| global->SetInternalField(0, v8_num(17)); |
| CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectHasRealIndexedProperty) { |
| LocalContext env; |
| v8::HandleScope scope(CcTest::isolate()); |
| |
| v8::Local<v8::Object> global = env->Global(); |
| global->Set(0, v8::String::NewFromUtf8(CcTest::isolate(), "value")); |
| CHECK(global->HasRealIndexedProperty(0)); |
| } |
| |
| |
| static void CheckAlignedPointerInInternalField(Handle<v8::Object> obj, |
| void* value) { |
| CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); |
| obj->SetAlignedPointerInInternalField(0, value); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(value, obj->GetAlignedPointerFromInternalField(0)); |
| } |
| |
| |
| THREADED_TEST(InternalFieldsAlignedPointers) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetInternalFieldCount(1); |
| Local<v8::Object> obj = templ->GetFunction()->NewInstance(); |
| CHECK_EQ(1, obj->InternalFieldCount()); |
| |
| CheckAlignedPointerInInternalField(obj, NULL); |
| |
| int* heap_allocated = new int[100]; |
| CheckAlignedPointerInInternalField(obj, heap_allocated); |
| delete[] heap_allocated; |
| |
| int stack_allocated[100]; |
| CheckAlignedPointerInInternalField(obj, stack_allocated); |
| |
| void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); |
| CheckAlignedPointerInInternalField(obj, huge); |
| |
| v8::Global<v8::Object> persistent(isolate, obj); |
| CHECK_EQ(1, Object::InternalFieldCount(persistent)); |
| CHECK_EQ(huge, Object::GetAlignedPointerFromInternalField(persistent, 0)); |
| } |
| |
| |
| static void CheckAlignedPointerInEmbedderData(LocalContext* env, int index, |
| void* value) { |
| CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); |
| (*env)->SetAlignedPointerInEmbedderData(index, value); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(value, (*env)->GetAlignedPointerFromEmbedderData(index)); |
| } |
| |
| |
| static void* AlignedTestPointer(int i) { |
| return reinterpret_cast<void*>(i * 1234); |
| } |
| |
| |
| THREADED_TEST(EmbedderDataAlignedPointers) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| CheckAlignedPointerInEmbedderData(&env, 0, NULL); |
| |
| int* heap_allocated = new int[100]; |
| CheckAlignedPointerInEmbedderData(&env, 1, heap_allocated); |
| delete[] heap_allocated; |
| |
| int stack_allocated[100]; |
| CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated); |
| |
| void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); |
| CheckAlignedPointerInEmbedderData(&env, 3, huge); |
| |
| // Test growing of the embedder data's backing store. |
| for (int i = 0; i < 100; i++) { |
| env->SetAlignedPointerInEmbedderData(i, AlignedTestPointer(i)); |
| } |
| CcTest::heap()->CollectAllGarbage(); |
| for (int i = 0; i < 100; i++) { |
| CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i)); |
| } |
| } |
| |
| |
| static void CheckEmbedderData(LocalContext* env, int index, |
| v8::Handle<Value> data) { |
| (*env)->SetEmbedderData(index, data); |
| CHECK((*env)->GetEmbedderData(index)->StrictEquals(data)); |
| } |
| |
| |
| THREADED_TEST(EmbedderData) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| CheckEmbedderData( |
| &env, 3, v8::String::NewFromUtf8(isolate, "The quick brown fox jumps")); |
| CheckEmbedderData(&env, 2, |
| v8::String::NewFromUtf8(isolate, "over the lazy dog.")); |
| CheckEmbedderData(&env, 1, v8::Number::New(isolate, 1.2345)); |
| CheckEmbedderData(&env, 0, v8::Boolean::New(isolate, true)); |
| } |
| |
| |
| THREADED_TEST(GetIsolate) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::Object> obj = v8::Object::New(isolate); |
| CHECK_EQ(isolate, obj->GetIsolate()); |
| CHECK_EQ(isolate, CcTest::global()->GetIsolate()); |
| } |
| |
| |
| THREADED_TEST(IdentityHash) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Ensure that the test starts with an fresh heap to test whether the hash |
| // code is based on the address. |
| CcTest::heap()->CollectAllGarbage(); |
| Local<v8::Object> obj = v8::Object::New(isolate); |
| int hash = obj->GetIdentityHash(); |
| int hash1 = obj->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| int hash2 = v8::Object::New(isolate)->GetIdentityHash(); |
| // Since the identity hash is essentially a random number two consecutive |
| // objects should not be assigned the same hash code. If the test below fails |
| // the random number generator should be evaluated. |
| CHECK_NE(hash, hash2); |
| CcTest::heap()->CollectAllGarbage(); |
| int hash3 = v8::Object::New(isolate)->GetIdentityHash(); |
| // Make sure that the identity hash is not based on the initial address of |
| // the object alone. If the test below fails the random number generator |
| // should be evaluated. |
| CHECK_NE(hash, hash3); |
| int hash4 = obj->GetIdentityHash(); |
| CHECK_EQ(hash, hash4); |
| |
| // Check identity hashes behaviour in the presence of JS accessors. |
| // Put a getter for 'v8::IdentityHash' on the Object's prototype: |
| { |
| CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); |
| Local<v8::Object> o1 = v8::Object::New(isolate); |
| Local<v8::Object> o2 = v8::Object::New(isolate); |
| CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); |
| } |
| { |
| CompileRun( |
| "function cnst() { return 42; };\n" |
| "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); |
| Local<v8::Object> o1 = v8::Object::New(isolate); |
| Local<v8::Object> o2 = v8::Object::New(isolate); |
| CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); |
| } |
| } |
| |
| |
| void GlobalProxyIdentityHash(bool set_in_js) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| v8::HandleScope scope(isolate); |
| Handle<Object> global_proxy = env->Global(); |
| i::Handle<i::Object> i_global_proxy = v8::Utils::OpenHandle(*global_proxy); |
| env->Global()->Set(v8_str("global"), global_proxy); |
| i::Handle<i::Object> original_hash; |
| if (set_in_js) { |
| CompileRun("var m = new Set(); m.add(global);"); |
| original_hash = i::Handle<i::Object>(i_global_proxy->GetHash(), i_isolate); |
| } else { |
| original_hash = i::Handle<i::Object>( |
| i::Object::GetOrCreateHash(i_isolate, i_global_proxy)); |
| } |
| CHECK(original_hash->IsSmi()); |
| int32_t hash1 = i::Handle<i::Smi>::cast(original_hash)->value(); |
| // Hash should be retained after being detached. |
| env->DetachGlobal(); |
| int hash2 = global_proxy->GetIdentityHash(); |
| CHECK_EQ(hash1, hash2); |
| { |
| // Re-attach global proxy to a new context, hash should stay the same. |
| LocalContext env2(NULL, Handle<ObjectTemplate>(), global_proxy); |
| int hash3 = global_proxy->GetIdentityHash(); |
| CHECK_EQ(hash1, hash3); |
| } |
| } |
| |
| |
| THREADED_TEST(GlobalProxyIdentityHash) { |
| GlobalProxyIdentityHash(true); |
| GlobalProxyIdentityHash(false); |
| } |
| |
| |
| TEST(SymbolIdentityHash) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| { |
| Local<v8::Symbol> symbol = v8::Symbol::New(isolate); |
| int hash = symbol->GetIdentityHash(); |
| int hash1 = symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| CcTest::heap()->CollectAllGarbage(); |
| int hash3 = symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash3); |
| } |
| |
| { |
| v8::Handle<v8::Symbol> js_symbol = |
| CompileRun("Symbol('foo')").As<v8::Symbol>(); |
| int hash = js_symbol->GetIdentityHash(); |
| int hash1 = js_symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| CcTest::heap()->CollectAllGarbage(); |
| int hash3 = js_symbol->GetIdentityHash(); |
| CHECK_EQ(hash, hash3); |
| } |
| } |
| |
| |
| TEST(StringIdentityHash) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::String> str = v8::String::NewFromUtf8(isolate, "str1"); |
| int hash = str->GetIdentityHash(); |
| int hash1 = str->GetIdentityHash(); |
| CHECK_EQ(hash, hash1); |
| CcTest::heap()->CollectAllGarbage(); |
| int hash3 = str->GetIdentityHash(); |
| CHECK_EQ(hash, hash3); |
| |
| Local<v8::String> str2 = v8::String::NewFromUtf8(isolate, "str1"); |
| int hash4 = str2->GetIdentityHash(); |
| CHECK_EQ(hash, hash4); |
| } |
| |
| |
| THREADED_TEST(SymbolProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| v8::Local<v8::Symbol> sym1 = v8::Symbol::New(isolate); |
| v8::Local<v8::Symbol> sym2 = v8::Symbol::New(isolate, v8_str("my-symbol")); |
| v8::Local<v8::Symbol> sym3 = v8::Symbol::New(isolate, v8_str("sym3")); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| |
| // Check basic symbol functionality. |
| CHECK(sym1->IsSymbol()); |
| CHECK(sym2->IsSymbol()); |
| CHECK(!obj->IsSymbol()); |
| |
| CHECK(sym1->Equals(sym1)); |
| CHECK(sym2->Equals(sym2)); |
| CHECK(!sym1->Equals(sym2)); |
| CHECK(!sym2->Equals(sym1)); |
| CHECK(sym1->StrictEquals(sym1)); |
| CHECK(sym2->StrictEquals(sym2)); |
| CHECK(!sym1->StrictEquals(sym2)); |
| CHECK(!sym2->StrictEquals(sym1)); |
| |
| CHECK(sym2->Name()->Equals(v8_str("my-symbol"))); |
| |
| v8::Local<v8::Value> sym_val = sym2; |
| CHECK(sym_val->IsSymbol()); |
| CHECK(sym_val->Equals(sym2)); |
| CHECK(sym_val->StrictEquals(sym2)); |
| CHECK(v8::Symbol::Cast(*sym_val)->Equals(sym2)); |
| |
| v8::Local<v8::Value> sym_obj = v8::SymbolObject::New(isolate, sym2); |
| CHECK(sym_obj->IsSymbolObject()); |
| CHECK(!sym2->IsSymbolObject()); |
| CHECK(!obj->IsSymbolObject()); |
| CHECK(!sym_obj->Equals(sym2)); |
| CHECK(!sym_obj->StrictEquals(sym2)); |
| CHECK(v8::SymbolObject::Cast(*sym_obj)->Equals(sym_obj)); |
| CHECK(v8::SymbolObject::Cast(*sym_obj)->ValueOf()->Equals(sym2)); |
| |
| // Make sure delete of a non-existent symbol property works. |
| CHECK(obj->Delete(sym1)); |
| CHECK(!obj->Has(sym1)); |
| |
| CHECK(obj->Set(sym1, v8::Integer::New(isolate, 1503))); |
| CHECK(obj->Has(sym1)); |
| CHECK_EQ(1503, obj->Get(sym1)->Int32Value()); |
| CHECK(obj->Set(sym1, v8::Integer::New(isolate, 2002))); |
| CHECK(obj->Has(sym1)); |
| CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); |
| CHECK_EQ(v8::None, obj->GetPropertyAttributes(sym1)); |
| |
| CHECK_EQ(0u, obj->GetOwnPropertyNames()->Length()); |
| unsigned num_props = obj->GetPropertyNames()->Length(); |
| CHECK(obj->Set(v8::String::NewFromUtf8(isolate, "bla"), |
| v8::Integer::New(isolate, 20))); |
| CHECK_EQ(1u, obj->GetOwnPropertyNames()->Length()); |
| CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length()); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| |
| CHECK(obj->SetAccessor(sym3, SymbolAccessorGetter, SymbolAccessorSetter)); |
| CHECK(obj->Get(sym3)->IsUndefined()); |
| CHECK(obj->Set(sym3, v8::Integer::New(isolate, 42))); |
| CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42))); |
| CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3")) |
| ->Equals(v8::Integer::New(isolate, 42))); |
| |
| // Add another property and delete it afterwards to force the object in |
| // slow case. |
| CHECK(obj->Set(sym2, v8::Integer::New(isolate, 2008))); |
| CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); |
| CHECK_EQ(2008, obj->Get(sym2)->Int32Value()); |
| CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); |
| CHECK_EQ(2u, obj->GetOwnPropertyNames()->Length()); |
| |
| CHECK(obj->Has(sym1)); |
| CHECK(obj->Has(sym2)); |
| CHECK(obj->Has(sym3)); |
| CHECK(obj->Has(v8::String::NewFromUtf8(isolate, "accessor_sym3"))); |
| CHECK(obj->Delete(sym2)); |
| CHECK(obj->Has(sym1)); |
| CHECK(!obj->Has(sym2)); |
| CHECK(obj->Has(sym3)); |
| CHECK(obj->Has(v8::String::NewFromUtf8(isolate, "accessor_sym3"))); |
| CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); |
| CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42))); |
| CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3")) |
| ->Equals(v8::Integer::New(isolate, 42))); |
| CHECK_EQ(2u, obj->GetOwnPropertyNames()->Length()); |
| |
| // Symbol properties are inherited. |
| v8::Local<v8::Object> child = v8::Object::New(isolate); |
| child->SetPrototype(obj); |
| CHECK(child->Has(sym1)); |
| CHECK_EQ(2002, child->Get(sym1)->Int32Value()); |
| CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42))); |
| CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3")) |
| ->Equals(v8::Integer::New(isolate, 42))); |
| CHECK_EQ(0u, child->GetOwnPropertyNames()->Length()); |
| } |
| |
| |
| THREADED_TEST(SymbolTemplateProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate); |
| v8::Local<v8::Name> name = v8::Symbol::New(isolate); |
| CHECK(!name.IsEmpty()); |
| foo->PrototypeTemplate()->Set(name, v8::FunctionTemplate::New(isolate)); |
| v8::Local<v8::Object> new_instance = foo->InstanceTemplate()->NewInstance(); |
| CHECK(!new_instance.IsEmpty()); |
| CHECK(new_instance->Has(name)); |
| } |
| |
| |
| THREADED_TEST(GlobalSymbols) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<String> name = v8_str("my-symbol"); |
| v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name); |
| v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name); |
| CHECK(glob2->SameValue(glob)); |
| |
| v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name); |
| v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name); |
| CHECK(glob_api2->SameValue(glob_api)); |
| CHECK(!glob_api->SameValue(glob)); |
| |
| v8::Local<v8::Symbol> sym = v8::Symbol::New(isolate, name); |
| CHECK(!sym->SameValue(glob)); |
| |
| CompileRun("var sym2 = Symbol.for('my-symbol')"); |
| v8::Local<Value> sym2 = env->Global()->Get(v8_str("sym2")); |
| CHECK(sym2->SameValue(glob)); |
| CHECK(!sym2->SameValue(glob_api)); |
| } |
| |
| |
| static void CheckWellKnownSymbol(v8::Local<v8::Symbol>(*getter)(v8::Isolate*), |
| const char* name) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Symbol> symbol = getter(isolate); |
| std::string script = std::string("var sym = ") + name; |
| CompileRun(script.c_str()); |
| v8::Local<Value> value = env->Global()->Get(v8_str("sym")); |
| |
| CHECK(!value.IsEmpty()); |
| CHECK(!symbol.IsEmpty()); |
| CHECK(value->SameValue(symbol)); |
| } |
| |
| |
| THREADED_TEST(WellKnownSymbols) { |
| CheckWellKnownSymbol(v8::Symbol::GetIterator, "Symbol.iterator"); |
| CheckWellKnownSymbol(v8::Symbol::GetUnscopables, "Symbol.unscopables"); |
| } |
| |
| |
| class ScopedArrayBufferContents { |
| public: |
| explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents) |
| : contents_(contents) {} |
| ~ScopedArrayBufferContents() { free(contents_.Data()); } |
| void* Data() const { return contents_.Data(); } |
| size_t ByteLength() const { return contents_.ByteLength(); } |
| |
| private: |
| const v8::ArrayBuffer::Contents contents_; |
| }; |
| |
| template <typename T> |
| static void CheckInternalFieldsAreZero(v8::Handle<T> value) { |
| CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount()); |
| for (int i = 0; i < value->InternalFieldCount(); i++) { |
| CHECK_EQ(0, value->GetInternalField(i)->Int32Value()); |
| } |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024); |
| CheckInternalFieldsAreZero(ab); |
| CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); |
| CHECK(!ab->IsExternal()); |
| CcTest::heap()->CollectAllGarbage(); |
| |
| ScopedArrayBufferContents ab_contents(ab->Externalize()); |
| CHECK(ab->IsExternal()); |
| |
| CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); |
| uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); |
| DCHECK(data != NULL); |
| env->Global()->Set(v8_str("ab"), ab); |
| |
| v8::Handle<v8::Value> result = CompileRun("ab.byteLength"); |
| CHECK_EQ(1024, result->Int32Value()); |
| |
| result = CompileRun( |
| "var u8 = new Uint8Array(ab);" |
| "u8[0] = 0xFF;" |
| "u8[1] = 0xAA;" |
| "u8.length"); |
| CHECK_EQ(1024, result->Int32Value()); |
| CHECK_EQ(0xFF, data[0]); |
| CHECK_EQ(0xAA, data[1]); |
| data[0] = 0xCC; |
| data[1] = 0x11; |
| result = CompileRun("u8[0] + u8[1]"); |
| CHECK_EQ(0xDD, result->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_JSInternalToExternal) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| |
| v8::Local<v8::Value> result = CompileRun( |
| "var ab1 = new ArrayBuffer(2);" |
| "var u8_a = new Uint8Array(ab1);" |
| "u8_a[0] = 0xAA;" |
| "u8_a[1] = 0xFF; u8_a.buffer"); |
| Local<v8::ArrayBuffer> ab1 = Local<v8::ArrayBuffer>::Cast(result); |
| CheckInternalFieldsAreZero(ab1); |
| CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); |
| CHECK(!ab1->IsExternal()); |
| ScopedArrayBufferContents ab1_contents(ab1->Externalize()); |
| CHECK(ab1->IsExternal()); |
| |
| result = CompileRun("ab1.byteLength"); |
| CHECK_EQ(2, result->Int32Value()); |
| result = CompileRun("u8_a[0]"); |
| CHECK_EQ(0xAA, result->Int32Value()); |
| result = CompileRun("u8_a[1]"); |
| CHECK_EQ(0xFF, result->Int32Value()); |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab1);" |
| "u8_b[0] = 0xBB;" |
| "u8_a[0]"); |
| CHECK_EQ(0xBB, result->Int32Value()); |
| result = CompileRun("u8_b[1]"); |
| CHECK_EQ(0xFF, result->Int32Value()); |
| |
| CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); |
| uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); |
| CHECK_EQ(0xBB, ab1_data[0]); |
| CHECK_EQ(0xFF, ab1_data[1]); |
| ab1_data[0] = 0xCC; |
| ab1_data[1] = 0x11; |
| result = CompileRun("u8_a[0] + u8_a[1]"); |
| CHECK_EQ(0xDD, result->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_External) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<uint8_t> my_data(100); |
| memset(my_data.start(), 0, 100); |
| Local<v8::ArrayBuffer> ab3 = |
| v8::ArrayBuffer::New(isolate, my_data.start(), 100); |
| CheckInternalFieldsAreZero(ab3); |
| CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); |
| CHECK(ab3->IsExternal()); |
| |
| env->Global()->Set(v8_str("ab3"), ab3); |
| |
| v8::Handle<v8::Value> result = CompileRun("ab3.byteLength"); |
| CHECK_EQ(100, result->Int32Value()); |
| |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab3);" |
| "u8_b[0] = 0xBB;" |
| "u8_b[1] = 0xCC;" |
| "u8_b.length"); |
| CHECK_EQ(100, result->Int32Value()); |
| CHECK_EQ(0xBB, my_data[0]); |
| CHECK_EQ(0xCC, my_data[1]); |
| my_data[0] = 0xCC; |
| my_data[1] = 0x11; |
| result = CompileRun("u8_b[0] + u8_b[1]"); |
| CHECK_EQ(0xDD, result->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_DisableNeuter) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<uint8_t> my_data(100); |
| memset(my_data.start(), 0, 100); |
| Local<v8::ArrayBuffer> ab = |
| v8::ArrayBuffer::New(isolate, my_data.start(), 100); |
| CHECK(ab->IsNeuterable()); |
| |
| i::Handle<i::JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); |
| buf->set_is_neuterable(false); |
| |
| CHECK(!ab->IsNeuterable()); |
| } |
| |
| |
| static void CheckDataViewIsNeutered(v8::Handle<v8::DataView> dv) { |
| CHECK_EQ(0, static_cast<int>(dv->ByteLength())); |
| CHECK_EQ(0, static_cast<int>(dv->ByteOffset())); |
| } |
| |
| |
| static void CheckIsNeutered(v8::Handle<v8::TypedArray> ta) { |
| CHECK_EQ(0, static_cast<int>(ta->ByteLength())); |
| CHECK_EQ(0, static_cast<int>(ta->Length())); |
| CHECK_EQ(0, static_cast<int>(ta->ByteOffset())); |
| } |
| |
| |
| static void CheckIsTypedArrayVarNeutered(const char* name) { |
| i::ScopedVector<char> source(1024); |
| i::SNPrintF(source, |
| "%s.byteLength == 0 && %s.byteOffset == 0 && %s.length == 0", |
| name, name, name); |
| CHECK(CompileRun(source.start())->IsTrue()); |
| v8::Handle<v8::TypedArray> ta = |
| v8::Handle<v8::TypedArray>::Cast(CompileRun(name)); |
| CheckIsNeutered(ta); |
| } |
| |
| |
| template <typename TypedArray, int kElementSize> |
| static Handle<TypedArray> CreateAndCheck(Handle<v8::ArrayBuffer> ab, |
| int byteOffset, int length) { |
| v8::Handle<TypedArray> ta = TypedArray::New(ab, byteOffset, length); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta); |
| CHECK_EQ(byteOffset, static_cast<int>(ta->ByteOffset())); |
| CHECK_EQ(length, static_cast<int>(ta->Length())); |
| CHECK_EQ(length * kElementSize, static_cast<int>(ta->ByteLength())); |
| return ta; |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_NeuteringApi) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Handle<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024); |
| |
| v8::Handle<v8::Uint8Array> u8a = |
| CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023); |
| v8::Handle<v8::Uint8ClampedArray> u8c = |
| CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023); |
| v8::Handle<v8::Int8Array> i8a = |
| CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023); |
| |
| v8::Handle<v8::Uint16Array> u16a = |
| CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511); |
| v8::Handle<v8::Int16Array> i16a = |
| CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511); |
| |
| v8::Handle<v8::Uint32Array> u32a = |
| CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255); |
| v8::Handle<v8::Int32Array> i32a = |
| CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255); |
| |
| v8::Handle<v8::Float32Array> f32a = |
| CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255); |
| v8::Handle<v8::Float64Array> f64a = |
| CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127); |
| |
| v8::Handle<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| CHECK_EQ(1, static_cast<int>(dv->ByteOffset())); |
| CHECK_EQ(1023, static_cast<int>(dv->ByteLength())); |
| |
| ScopedArrayBufferContents contents(buffer->Externalize()); |
| buffer->Neuter(); |
| CHECK_EQ(0, static_cast<int>(buffer->ByteLength())); |
| CheckIsNeutered(u8a); |
| CheckIsNeutered(u8c); |
| CheckIsNeutered(i8a); |
| CheckIsNeutered(u16a); |
| CheckIsNeutered(i16a); |
| CheckIsNeutered(u32a); |
| CheckIsNeutered(i32a); |
| CheckIsNeutered(f32a); |
| CheckIsNeutered(f64a); |
| CheckDataViewIsNeutered(dv); |
| } |
| |
| |
| THREADED_TEST(ArrayBuffer_NeuteringScript) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| CompileRun( |
| "var ab = new ArrayBuffer(1024);" |
| "var u8a = new Uint8Array(ab, 1, 1023);" |
| "var u8c = new Uint8ClampedArray(ab, 1, 1023);" |
| "var i8a = new Int8Array(ab, 1, 1023);" |
| "var u16a = new Uint16Array(ab, 2, 511);" |
| "var i16a = new Int16Array(ab, 2, 511);" |
| "var u32a = new Uint32Array(ab, 4, 255);" |
| "var i32a = new Int32Array(ab, 4, 255);" |
| "var f32a = new Float32Array(ab, 4, 255);" |
| "var f64a = new Float64Array(ab, 8, 127);" |
| "var dv = new DataView(ab, 1, 1023);"); |
| |
| v8::Handle<v8::ArrayBuffer> ab = |
| Local<v8::ArrayBuffer>::Cast(CompileRun("ab")); |
| |
| v8::Handle<v8::DataView> dv = |
| v8::Handle<v8::DataView>::Cast(CompileRun("dv")); |
| |
| ScopedArrayBufferContents contents(ab->Externalize()); |
| ab->Neuter(); |
| CHECK_EQ(0, static_cast<int>(ab->ByteLength())); |
| CHECK_EQ(0, CompileRun("ab.byteLength")->Int32Value()); |
| |
| CheckIsTypedArrayVarNeutered("u8a"); |
| CheckIsTypedArrayVarNeutered("u8c"); |
| CheckIsTypedArrayVarNeutered("i8a"); |
| CheckIsTypedArrayVarNeutered("u16a"); |
| CheckIsTypedArrayVarNeutered("i16a"); |
| CheckIsTypedArrayVarNeutered("u32a"); |
| CheckIsTypedArrayVarNeutered("i32a"); |
| CheckIsTypedArrayVarNeutered("f32a"); |
| CheckIsTypedArrayVarNeutered("f64a"); |
| |
| CHECK(CompileRun("dv.byteLength == 0 && dv.byteOffset == 0")->IsTrue()); |
| CheckDataViewIsNeutered(dv); |
| } |
| |
| |
| class ScopedSharedArrayBufferContents { |
| public: |
| explicit ScopedSharedArrayBufferContents( |
| const v8::SharedArrayBuffer::Contents& contents) |
| : contents_(contents) {} |
| ~ScopedSharedArrayBufferContents() { free(contents_.Data()); } |
| void* Data() const { return contents_.Data(); } |
| size_t ByteLength() const { return contents_.ByteLength(); } |
| |
| private: |
| const v8::SharedArrayBuffer::Contents contents_; |
| }; |
| |
| |
| THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::SharedArrayBuffer> ab = v8::SharedArrayBuffer::New(isolate, 1024); |
| CheckInternalFieldsAreZero(ab); |
| CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); |
| CHECK(!ab->IsExternal()); |
| CcTest::heap()->CollectAllGarbage(); |
| |
| ScopedSharedArrayBufferContents ab_contents(ab->Externalize()); |
| CHECK(ab->IsExternal()); |
| |
| CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); |
| uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); |
| DCHECK(data != NULL); |
| env->Global()->Set(v8_str("ab"), ab); |
| |
| v8::Handle<v8::Value> result = CompileRun("ab.byteLength"); |
| CHECK_EQ(1024, result->Int32Value()); |
| |
| result = CompileRun( |
| "var u8 = new Uint8Array(ab);" |
| "u8[0] = 0xFF;" |
| "u8[1] = 0xAA;" |
| "u8.length"); |
| CHECK_EQ(1024, result->Int32Value()); |
| CHECK_EQ(0xFF, data[0]); |
| CHECK_EQ(0xAA, data[1]); |
| data[0] = 0xCC; |
| data[1] = 0x11; |
| result = CompileRun("u8[0] + u8[1]"); |
| CHECK_EQ(0xDD, result->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| |
| v8::Local<v8::Value> result = CompileRun( |
| "var ab1 = new SharedArrayBuffer(2);" |
| "var u8_a = new Uint8Array(ab1);" |
| "u8_a[0] = 0xAA;" |
| "u8_a[1] = 0xFF; u8_a.buffer"); |
| Local<v8::SharedArrayBuffer> ab1 = Local<v8::SharedArrayBuffer>::Cast(result); |
| CheckInternalFieldsAreZero(ab1); |
| CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); |
| CHECK(!ab1->IsExternal()); |
| ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize()); |
| CHECK(ab1->IsExternal()); |
| |
| result = CompileRun("ab1.byteLength"); |
| CHECK_EQ(2, result->Int32Value()); |
| result = CompileRun("u8_a[0]"); |
| CHECK_EQ(0xAA, result->Int32Value()); |
| result = CompileRun("u8_a[1]"); |
| CHECK_EQ(0xFF, result->Int32Value()); |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab1);" |
| "u8_b[0] = 0xBB;" |
| "u8_a[0]"); |
| CHECK_EQ(0xBB, result->Int32Value()); |
| result = CompileRun("u8_b[1]"); |
| CHECK_EQ(0xFF, result->Int32Value()); |
| |
| CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); |
| uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); |
| CHECK_EQ(0xBB, ab1_data[0]); |
| CHECK_EQ(0xFF, ab1_data[1]); |
| ab1_data[0] = 0xCC; |
| ab1_data[1] = 0x11; |
| result = CompileRun("u8_a[0] + u8_a[1]"); |
| CHECK_EQ(0xDD, result->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(SharedArrayBuffer_External) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<uint8_t> my_data(100); |
| memset(my_data.start(), 0, 100); |
| Local<v8::SharedArrayBuffer> ab3 = |
| v8::SharedArrayBuffer::New(isolate, my_data.start(), 100); |
| CheckInternalFieldsAreZero(ab3); |
| CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); |
| CHECK(ab3->IsExternal()); |
| |
| env->Global()->Set(v8_str("ab3"), ab3); |
| |
| v8::Handle<v8::Value> result = CompileRun("ab3.byteLength"); |
| CHECK_EQ(100, result->Int32Value()); |
| |
| result = CompileRun( |
| "var u8_b = new Uint8Array(ab3);" |
| "u8_b[0] = 0xBB;" |
| "u8_b[1] = 0xCC;" |
| "u8_b.length"); |
| CHECK_EQ(100, result->Int32Value()); |
| CHECK_EQ(0xBB, my_data[0]); |
| CHECK_EQ(0xCC, my_data[1]); |
| my_data[0] = 0xCC; |
| my_data[1] = 0x11; |
| result = CompileRun("u8_b[0] + u8_b[1]"); |
| CHECK_EQ(0xDD, result->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(HiddenProperties) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| v8::Local<v8::String> key = v8_str("api-test::hidden-key"); |
| v8::Local<v8::String> empty = v8_str(""); |
| v8::Local<v8::String> prop_name = v8_str("prop_name"); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| |
| // Make sure delete of a non-existent hidden value works |
| CHECK(obj->DeleteHiddenValue(key)); |
| |
| CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 1503))); |
| CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); |
| CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2002))); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| |
| // Make sure we do not find the hidden property. |
| CHECK(!obj->Has(empty)); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| CHECK(obj->Get(empty)->IsUndefined()); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| CHECK(obj->Set(empty, v8::Integer::New(isolate, 2003))); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| CHECK_EQ(2003, obj->Get(empty)->Int32Value()); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| |
| // Add another property and delete it afterwards to force the object in |
| // slow case. |
| CHECK(obj->Set(prop_name, v8::Integer::New(isolate, 2008))); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| CHECK(obj->Delete(prop_name)); |
| CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| |
| CHECK(obj->SetHiddenValue(key, Handle<Value>())); |
| CHECK(obj->GetHiddenValue(key).IsEmpty()); |
| |
| CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2002))); |
| CHECK(obj->DeleteHiddenValue(key)); |
| CHECK(obj->GetHiddenValue(key).IsEmpty()); |
| } |
| |
| |
| THREADED_TEST(Regress97784) { |
| // Regression test for crbug.com/97784 |
| // Messing with the Object.prototype should not have effect on |
| // hidden properties. |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| v8::Local<v8::String> key = v8_str("hidden"); |
| |
| CompileRun( |
| "set_called = false;" |
| "Object.defineProperty(" |
| " Object.prototype," |
| " 'hidden'," |
| " {get: function() { return 45; }," |
| " set: function() { set_called = true; }})"); |
| |
| CHECK(obj->GetHiddenValue(key).IsEmpty()); |
| // Make sure that the getter and setter from Object.prototype is not invoked. |
| // If it did we would have full access to the hidden properties in |
| // the accessor. |
| CHECK(obj->SetHiddenValue(key, v8::Integer::New(env->GetIsolate(), 42))); |
| ExpectFalse("set_called"); |
| CHECK_EQ(42, obj->GetHiddenValue(key)->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(External) { |
| v8::HandleScope scope(CcTest::isolate()); |
| int x = 3; |
| Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x); |
| LocalContext env; |
| env->Global()->Set(v8_str("ext"), ext); |
| Local<Value> reext_obj = CompileRun("this.ext"); |
| v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); |
| int* ptr = static_cast<int*>(reext->Value()); |
| CHECK_EQ(x, 3); |
| *ptr = 10; |
| CHECK_EQ(x, 10); |
| |
| // Make sure unaligned pointers are wrapped properly. |
| char* data = i::StrDup("0123456789"); |
| Local<v8::Value> zero = v8::External::New(CcTest::isolate(), &data[0]); |
| Local<v8::Value> one = v8::External::New(CcTest::isolate(), &data[1]); |
| Local<v8::Value> two = v8::External::New(CcTest::isolate(), &data[2]); |
| Local<v8::Value> three = v8::External::New(CcTest::isolate(), &data[3]); |
| |
| char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value()); |
| CHECK_EQ('0', *char_ptr); |
| char_ptr = reinterpret_cast<char*>(v8::External::Cast(*one)->Value()); |
| CHECK_EQ('1', *char_ptr); |
| char_ptr = reinterpret_cast<char*>(v8::External::Cast(*two)->Value()); |
| CHECK_EQ('2', *char_ptr); |
| char_ptr = reinterpret_cast<char*>(v8::External::Cast(*three)->Value()); |
| CHECK_EQ('3', *char_ptr); |
| i::DeleteArray(data); |
| } |
| |
| |
| THREADED_TEST(GlobalHandle) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); |
| } |
| global.Reset(); |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); |
| } |
| global.Reset(); |
| } |
| |
| |
| THREADED_TEST(ResettingGlobalHandle) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("longer")); |
| } |
| CHECK_EQ(global_handles->global_handles_count(), initial_handle_count); |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 6); |
| } |
| global.Reset(); |
| CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1); |
| } |
| |
| |
| THREADED_TEST(ResettingGlobalHandleToEmpty) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| { |
| v8::HandleScope scope(isolate); |
| CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); |
| } |
| { |
| v8::HandleScope scope(isolate); |
| Local<String> empty; |
| global.Reset(isolate, empty); |
| } |
| CHECK(global.IsEmpty()); |
| CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1); |
| } |
| |
| |
| template <class T> |
| static v8::Global<T> PassUnique(v8::Global<T> unique) { |
| return unique.Pass(); |
| } |
| |
| |
| template <class T> |
| static v8::Global<T> ReturnUnique(v8::Isolate* isolate, |
| const v8::Persistent<T>& global) { |
| v8::Global<String> unique(isolate, global); |
| return unique.Pass(); |
| } |
| |
| |
| THREADED_TEST(Global) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global; |
| { |
| v8::HandleScope scope(isolate); |
| global.Reset(isolate, v8_str("str")); |
| } |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| { |
| v8::Global<String> unique(isolate, global); |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| // Test assignment via Pass |
| { |
| v8::Global<String> copy = unique.Pass(); |
| CHECK(unique.IsEmpty()); |
| CHECK(copy == global); |
| CHECK_EQ(initial_handle_count + 1, |
| global_handles->global_handles_count()); |
| unique = copy.Pass(); |
| } |
| // Test ctor via Pass |
| { |
| v8::Global<String> copy(unique.Pass()); |
| CHECK(unique.IsEmpty()); |
| CHECK(copy == global); |
| CHECK_EQ(initial_handle_count + 1, |
| global_handles->global_handles_count()); |
| unique = copy.Pass(); |
| } |
| // Test pass through function call |
| { |
| v8::Global<String> copy = PassUnique(unique.Pass()); |
| CHECK(unique.IsEmpty()); |
| CHECK(copy == global); |
| CHECK_EQ(initial_handle_count + 1, |
| global_handles->global_handles_count()); |
| unique = copy.Pass(); |
| } |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| } |
| // Test pass from function call |
| { |
| v8::Global<String> unique = ReturnUnique(isolate, global); |
| CHECK(unique == global); |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| } |
| CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); |
| global.Reset(); |
| } |
| |
| |
| namespace { |
| |
| class TwoPassCallbackData; |
| void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data); |
| void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data); |
| |
| |
| class TwoPassCallbackData { |
| public: |
| TwoPassCallbackData(v8::Isolate* isolate, int* instance_counter) |
| : first_pass_called_(false), |
| second_pass_called_(false), |
| trigger_gc_(false), |
| instance_counter_(instance_counter) { |
| HandleScope scope(isolate); |
| i::ScopedVector<char> buffer(40); |
| i::SNPrintF(buffer, "%p", static_cast<void*>(this)); |
| auto string = |
| v8::String::NewFromUtf8(isolate, buffer.start(), |
| v8::NewStringType::kNormal).ToLocalChecked(); |
| cell_.Reset(isolate, string); |
| (*instance_counter_)++; |
| } |
| |
| ~TwoPassCallbackData() { |
| CHECK(first_pass_called_); |
| CHECK(second_pass_called_); |
| CHECK(cell_.IsEmpty()); |
| (*instance_counter_)--; |
| } |
| |
| void FirstPass() { |
| CHECK(!first_pass_called_); |
| CHECK(!second_pass_called_); |
| CHECK(!cell_.IsEmpty()); |
| cell_.Reset(); |
| first_pass_called_ = true; |
| } |
| |
| void SecondPass() { |
| CHECK(first_pass_called_); |
| CHECK(!second_pass_called_); |
| CHECK(cell_.IsEmpty()); |
| second_pass_called_ = true; |
| delete this; |
| } |
| |
| void SetWeak() { |
| cell_.SetWeak(this, FirstPassCallback, v8::WeakCallbackType::kParameter); |
| } |
| |
| void MarkTriggerGc() { trigger_gc_ = true; } |
| bool trigger_gc() { return trigger_gc_; } |
| |
| int* instance_counter() { return instance_counter_; } |
| |
| private: |
| bool first_pass_called_; |
| bool second_pass_called_; |
| bool trigger_gc_; |
| v8::Global<v8::String> cell_; |
| int* instance_counter_; |
| }; |
| |
| |
| void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) { |
| ApiTestFuzzer::Fuzz(); |
| bool trigger_gc = data.GetParameter()->trigger_gc(); |
| int* instance_counter = data.GetParameter()->instance_counter(); |
| data.GetParameter()->SecondPass(); |
| if (!trigger_gc) return; |
| auto data_2 = new TwoPassCallbackData(data.GetIsolate(), instance_counter); |
| data_2->SetWeak(); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| |
| void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) { |
| data.GetParameter()->FirstPass(); |
| data.SetSecondPassCallback(SecondPassCallback); |
| } |
| |
| } // namespace |
| |
| |
| TEST(TwoPassPhantomCallbacks) { |
| auto isolate = CcTest::isolate(); |
| const size_t kLength = 20; |
| int instance_counter = 0; |
| for (size_t i = 0; i < kLength; ++i) { |
| auto data = new TwoPassCallbackData(isolate, &instance_counter); |
| data->SetWeak(); |
| } |
| CHECK_EQ(static_cast<int>(kLength), instance_counter); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(0, instance_counter); |
| } |
| |
| |
| TEST(TwoPassPhantomCallbacksNestedGc) { |
| auto isolate = CcTest::isolate(); |
| const size_t kLength = 20; |
| TwoPassCallbackData* array[kLength]; |
| int instance_counter = 0; |
| for (size_t i = 0; i < kLength; ++i) { |
| array[i] = new TwoPassCallbackData(isolate, &instance_counter); |
| array[i]->SetWeak(); |
| } |
| array[5]->MarkTriggerGc(); |
| array[10]->MarkTriggerGc(); |
| array[15]->MarkTriggerGc(); |
| CHECK_EQ(static_cast<int>(kLength), instance_counter); |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(0, instance_counter); |
| } |
| |
| |
| namespace { |
| |
| void* IntKeyToVoidPointer(int key) { return reinterpret_cast<void*>(key << 1); } |
| |
| |
| Local<v8::Object> NewObjectForIntKey( |
| v8::Isolate* isolate, const v8::Global<v8::ObjectTemplate>& templ, |
| int key) { |
| auto local = Local<v8::ObjectTemplate>::New(isolate, templ); |
| auto obj = local->NewInstance(); |
| obj->SetAlignedPointerInInternalField(0, IntKeyToVoidPointer(key)); |
| return obj; |
| } |
| |
| |
| template <typename K, typename V> |
| class PhantomStdMapTraits : public v8::StdMapTraits<K, V> { |
| public: |
| typedef typename v8::GlobalValueMap<K, V, PhantomStdMapTraits<K, V>> MapType; |
| static const v8::PersistentContainerCallbackType kCallbackType = |
| v8::kWeakWithInternalFields; |
| struct WeakCallbackDataType { |
| MapType* map; |
| K key; |
| }; |
| static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key, |
| Local<V> value) { |
| WeakCallbackDataType* data = new WeakCallbackDataType; |
| data->map = map; |
| data->key = key; |
| return data; |
| } |
| static MapType* MapFromWeakCallbackInfo( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { |
| return data.GetParameter()->map; |
| } |
| static K KeyFromWeakCallbackInfo( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { |
| return data.GetParameter()->key; |
| } |
| static void DisposeCallbackData(WeakCallbackDataType* data) { delete data; } |
| static void Dispose(v8::Isolate* isolate, v8::Global<V> value, K key) { |
| CHECK_EQ(IntKeyToVoidPointer(key), |
| v8::Object::GetAlignedPointerFromInternalField(value, 0)); |
| } |
| static void DisposeWeak( |
| const v8::WeakCallbackInfo<WeakCallbackDataType>& info) { |
| K key = KeyFromWeakCallbackInfo(info); |
| CHECK_EQ(IntKeyToVoidPointer(key), info.GetInternalField(0)); |
| DisposeCallbackData(info.GetParameter()); |
| } |
| }; |
| |
| |
| template <typename Map> |
| void TestGlobalValueMap() { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::Global<ObjectTemplate> templ; |
| { |
| HandleScope scope(isolate); |
| auto t = ObjectTemplate::New(isolate); |
| t->SetInternalFieldCount(1); |
| templ.Reset(isolate, t); |
| } |
| Map map(isolate); |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int initial_handle_count = global_handles->global_handles_count(); |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| { |
| HandleScope scope(isolate); |
| Local<v8::Object> obj = map.Get(7); |
| CHECK(obj.IsEmpty()); |
| Local<v8::Object> expected = v8::Object::New(isolate); |
| map.Set(7, expected); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| obj = map.Get(7); |
| CHECK(expected->Equals(obj)); |
| { |
| typename Map::PersistentValueReference ref = map.GetReference(7); |
| CHECK(expected->Equals(ref.NewLocal(isolate))); |
| } |
| v8::Global<v8::Object> removed = map.Remove(7); |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| CHECK(expected == removed); |
| removed = map.Remove(7); |
| CHECK(removed.IsEmpty()); |
| map.Set(8, expected); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| map.Set(8, expected); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| { |
| typename Map::PersistentValueReference ref; |
| Local<v8::Object> expected2 = NewObjectForIntKey(isolate, templ, 8); |
| removed = map.Set(8, v8::Global<v8::Object>(isolate, expected2), &ref); |
| CHECK_EQ(1, static_cast<int>(map.Size())); |
| CHECK(expected == removed); |
| CHECK(expected2->Equals(ref.NewLocal(isolate))); |
| } |
| } |
| CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); |
| if (map.IsWeak()) { |
| CcTest::i_isolate()->heap()->CollectAllGarbage( |
| i::Heap::kAbortIncrementalMarkingMask); |
| } else { |
| map.Clear(); |
| } |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); |
| { |
| HandleScope scope(isolate); |
| Local<v8::Object> value = NewObjectForIntKey(isolate, templ, 9); |
| map.Set(9, value); |
| map.Clear(); |
| } |
| CHECK_EQ(0, static_cast<int>(map.Size())); |
| CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); |
| } |
| |
| } // namespace |
| |
| |
| TEST(GlobalValueMap) { |
| // Default case, w/o weak callbacks: |
| TestGlobalValueMap<v8::StdGlobalValueMap<int, v8::Object>>(); |
| |
| // Custom traits with weak callbacks: |
| typedef v8::GlobalValueMap<int, v8::Object, |
| PhantomStdMapTraits<int, v8::Object>> WeakMap; |
| TestGlobalValueMap<WeakMap>(); |
| } |
| |
| |
| TEST(PersistentValueVector) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::internal::GlobalHandles* global_handles = |
| reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| int handle_count = global_handles->global_handles_count(); |
| HandleScope scope(isolate); |
| |
| v8::PersistentValueVector<v8::Object> vector(isolate); |
| |
| Local<v8::Object> obj1 = v8::Object::New(isolate); |
| Local<v8::Object> obj2 = v8::Object::New(isolate); |
| v8::Global<v8::Object> obj3(isolate, v8::Object::New(isolate)); |
| |
| CHECK(vector.IsEmpty()); |
| CHECK_EQ(0, static_cast<int>(vector.Size())); |
| |
| vector.ReserveCapacity(3); |
| CHECK(vector.IsEmpty()); |
| |
| vector.Append(obj1); |
| vector.Append(obj2); |
| vector.Append(obj1); |
| vector.Append(obj3.Pass()); |
| vector.Append(obj1); |
| |
| CHECK(!vector.IsEmpty()); |
| CHECK_EQ(5, static_cast<int>(vector.Size())); |
| CHECK(obj3.IsEmpty()); |
| CHECK(obj1->Equals(vector.Get(0))); |
| CHECK(obj1->Equals(vector.Get(2))); |
| CHECK(obj1->Equals(vector.Get(4))); |
| CHECK(obj2->Equals(vector.Get(1))); |
| |
| CHECK_EQ(5 + handle_count, global_handles->global_handles_count()); |
| |
| vector.Clear(); |
| CHECK(vector.IsEmpty()); |
| CHECK_EQ(0, static_cast<int>(vector.Size())); |
| CHECK_EQ(handle_count, global_handles->global_handles_count()); |
| } |
| |
| |
| THREADED_TEST(GlobalHandleUpcast) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<String> local = v8::Local<String>::New(isolate, v8_str("str")); |
| v8::Persistent<String> global_string(isolate, local); |
| v8::Persistent<Value>& global_value = |
| v8::Persistent<Value>::Cast(global_string); |
| CHECK(v8::Local<v8::Value>::New(isolate, global_value)->IsString()); |
| CHECK(global_string == v8::Persistent<String>::Cast(global_value)); |
| global_string.Reset(); |
| } |
| |
| |
| THREADED_TEST(HandleEquality) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Persistent<String> global1; |
| v8::Persistent<String> global2; |
| { |
| v8::HandleScope scope(isolate); |
| global1.Reset(isolate, v8_str("str")); |
| global2.Reset(isolate, v8_str("str2")); |
| } |
| CHECK_EQ(global1 == global1, true); |
| CHECK_EQ(global1 != global1, false); |
| { |
| v8::HandleScope scope(isolate); |
| Local<String> local1 = Local<String>::New(isolate, global1); |
| Local<String> local2 = Local<String>::New(isolate, global2); |
| |
| CHECK_EQ(global1 == local1, true); |
| CHECK_EQ(global1 != local1, false); |
| CHECK_EQ(local1 == global1, true); |
| CHECK_EQ(local1 != global1, false); |
| |
| CHECK_EQ(global1 == local2, false); |
| CHECK_EQ(global1 != local2, true); |
| CHECK_EQ(local2 == global1, false); |
| CHECK_EQ(local2 != global1, true); |
| |
| CHECK_EQ(local1 == local2, false); |
| CHECK_EQ(local1 != local2, true); |
| |
| Local<String> anotherLocal1 = Local<String>::New(isolate, global1); |
| CHECK_EQ(local1 == anotherLocal1, true); |
| CHECK_EQ(local1 != anotherLocal1, false); |
| } |
| global1.Reset(); |
| global2.Reset(); |
| } |
| |
| |
| THREADED_TEST(LocalHandle) { |
| v8::HandleScope scope(CcTest::isolate()); |
| v8::Local<String> local = |
| v8::Local<String>::New(CcTest::isolate(), v8_str("str")); |
| CHECK_EQ(local->Length(), 3); |
| } |
| |
| |
| class WeakCallCounter { |
| public: |
| explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) {} |
| int id() { return id_; } |
| void increment() { number_of_weak_calls_++; } |
| int NumberOfWeakCalls() { return number_of_weak_calls_; } |
| |
| private: |
| int id_; |
| int number_of_weak_calls_; |
| }; |
| |
| |
| template <typename T> |
| struct WeakCallCounterAndPersistent { |
| explicit WeakCallCounterAndPersistent(WeakCallCounter* counter) |
| : counter(counter) {} |
| WeakCallCounter* counter; |
| v8::Persistent<T> handle; |
| }; |
| |
| |
| template <typename T> |
| static void WeakPointerCallback( |
| const v8::WeakCallbackInfo<WeakCallCounterAndPersistent<T>>& data) { |
| CHECK_EQ(1234, data.GetParameter()->counter->id()); |
| data.GetParameter()->counter->increment(); |
| data.GetParameter()->handle.Reset(); |
| } |
| |
| |
| template <typename T> |
| static UniqueId MakeUniqueId(const Persistent<T>& p) { |
| return UniqueId(reinterpret_cast<uintptr_t>(*v8::Utils::OpenPersistent(p))); |
| } |
| |
| |
| THREADED_TEST(ApiObjectGroups) { |
| LocalContext env; |
| v8::Isolate* iso = env->GetIsolate(); |
| HandleScope scope(iso); |
| |
| WeakCallCounter counter(1234); |
| |
| WeakCallCounterAndPersistent<Value> g1s1(&counter); |
| WeakCallCounterAndPersistent<Value> g1s2(&counter); |
| WeakCallCounterAndPersistent<Value> g1c1(&counter); |
| WeakCallCounterAndPersistent<Value> g2s1(&counter); |
| WeakCallCounterAndPersistent<Value> g2s2(&counter); |
| WeakCallCounterAndPersistent<Value> g2c1(&counter); |
| |
| { |
| HandleScope scope(iso); |
| g1s1.handle.Reset(iso, Object::New(iso)); |
| g1s2.handle.Reset(iso, Object::New(iso)); |
| g1c1.handle.Reset(iso, Object::New(iso)); |
| g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| g2s1.handle.Reset(iso, Object::New(iso)); |
| g2s2.handle.Reset(iso, Object::New(iso)); |
| g2c1.handle.Reset(iso, Object::New(iso)); |
| g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| } |
| |
| WeakCallCounterAndPersistent<Value> root(&counter); |
| root.handle.Reset(iso, g1s1.handle); // make a root. |
| |
| // Connect group 1 and 2, make a cycle. |
| { |
| HandleScope scope(iso); |
| CHECK(Local<Object>::New(iso, g1s2.handle.As<Object>()) |
| ->Set(0, Local<Value>::New(iso, g2s2.handle))); |
| CHECK(Local<Object>::New(iso, g2s1.handle.As<Object>()) |
| ->Set(0, Local<Value>::New(iso, g1s1.handle))); |
| } |
| |
| { |
| UniqueId id1 = MakeUniqueId(g1s1.handle); |
| UniqueId id2 = MakeUniqueId(g2s2.handle); |
| iso->SetObjectGroupId(g1s1.handle, id1); |
| iso->SetObjectGroupId(g1s2.handle, id1); |
| iso->SetReferenceFromGroup(id1, g1c1.handle); |
| iso->SetObjectGroupId(g2s1.handle, id2); |
| iso->SetObjectGroupId(g2s2.handle, id2); |
| iso->SetReferenceFromGroup(id2, g2c1.handle); |
| } |
| // Do a single full GC, ensure incremental marking is stopped. |
| v8::internal::Heap* heap = |
| reinterpret_cast<v8::internal::Isolate*>(iso)->heap(); |
| heap->CollectAllGarbage(); |
| |
| // All object should be alive. |
| CHECK_EQ(0, counter.NumberOfWeakCalls()); |
| |
| // Weaken the root. |
| root.handle.SetWeak(&root, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| // But make children strong roots---all the objects (except for children) |
| // should be collectable now. |
| g1c1.handle.ClearWeak(); |
| g2c1.handle.ClearWeak(); |
| |
| // Groups are deleted, rebuild groups. |
| { |
| UniqueId id1 = MakeUniqueId(g1s1.handle); |
| UniqueId id2 = MakeUniqueId(g2s2.handle); |
| iso->SetObjectGroupId(g1s1.handle, id1); |
| iso->SetObjectGroupId(g1s2.handle, id1); |
| iso->SetReferenceFromGroup(id1, g1c1.handle); |
| iso->SetObjectGroupId(g2s1.handle, id2); |
| iso->SetObjectGroupId(g2s2.handle, id2); |
| iso->SetReferenceFromGroup(id2, g2c1.handle); |
| } |
| |
| heap->CollectAllGarbage(); |
| |
| // All objects should be gone. 5 global handles in total. |
| CHECK_EQ(5, counter.NumberOfWeakCalls()); |
| |
| // And now make children weak again and collect them. |
| g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| heap->CollectAllGarbage(); |
| CHECK_EQ(7, counter.NumberOfWeakCalls()); |
| } |
| |
| |
| THREADED_TEST(ApiObjectGroupsForSubtypes) { |
| LocalContext env; |
| v8::Isolate* iso = env->GetIsolate(); |
| HandleScope scope(iso); |
| |
| WeakCallCounter counter(1234); |
| |
| WeakCallCounterAndPersistent<Object> g1s1(&counter); |
| WeakCallCounterAndPersistent<String> g1s2(&counter); |
| WeakCallCounterAndPersistent<String> g1c1(&counter); |
| WeakCallCounterAndPersistent<Object> g2s1(&counter); |
| WeakCallCounterAndPersistent<String> g2s2(&counter); |
| WeakCallCounterAndPersistent<String> g2c1(&counter); |
| |
| { |
| HandleScope scope(iso); |
| g1s1.handle.Reset(iso, Object::New(iso)); |
| g1s2.handle.Reset(iso, String::NewFromUtf8(iso, "foo1")); |
| g1c1.handle.Reset(iso, String::NewFromUtf8(iso, "foo2")); |
| g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| g2s1.handle.Reset(iso, Object::New(iso)); |
| g2s2.handle.Reset(iso, String::NewFromUtf8(iso, "foo3")); |
| g2c1.handle.Reset(iso, String::NewFromUtf8(iso, "foo4")); |
| g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| } |
| |
| WeakCallCounterAndPersistent<Value> root(&counter); |
| root.handle.Reset(iso, g1s1.handle); // make a root. |
| |
| // Connect group 1 and 2, make a cycle. |
| { |
| HandleScope scope(iso); |
| CHECK(Local<Object>::New(iso, g1s1.handle) |
| ->Set(0, Local<Object>::New(iso, g2s1.handle))); |
| CHECK(Local<Object>::New(iso, g2s1.handle) |
| ->Set(0, Local<Object>::New(iso, g1s1.handle))); |
| } |
| |
| { |
| UniqueId id1 = MakeUniqueId(g1s1.handle); |
| UniqueId id2 = MakeUniqueId(g2s2.handle); |
| iso->SetObjectGroupId(g1s1.handle, id1); |
| iso->SetObjectGroupId(g1s2.handle, id1); |
| iso->SetReference(g1s1.handle, g1c1.handle); |
| iso->SetObjectGroupId(g2s1.handle, id2); |
| iso->SetObjectGroupId(g2s2.handle, id2); |
| iso->SetReferenceFromGroup(id2, g2c1.handle); |
| } |
| // Do a single full GC, ensure incremental marking is stopped. |
| v8::internal::Heap* heap = |
| reinterpret_cast<v8::internal::Isolate*>(iso)->heap(); |
| heap->CollectAllGarbage(); |
| |
| // All object should be alive. |
| CHECK_EQ(0, counter.NumberOfWeakCalls()); |
| |
| // Weaken the root. |
| root.handle.SetWeak(&root, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| // But make children strong roots---all the objects (except for children) |
| // should be collectable now. |
| g1c1.handle.ClearWeak(); |
| g2c1.handle.ClearWeak(); |
| |
| // Groups are deleted, rebuild groups. |
| { |
| UniqueId id1 = MakeUniqueId(g1s1.handle); |
| UniqueId id2 = MakeUniqueId(g2s2.handle); |
| iso->SetObjectGroupId(g1s1.handle, id1); |
| iso->SetObjectGroupId(g1s2.handle, id1); |
| iso->SetReference(g1s1.handle, g1c1.handle); |
| iso->SetObjectGroupId(g2s1.handle, id2); |
| iso->SetObjectGroupId(g2s2.handle, id2); |
| iso->SetReferenceFromGroup(id2, g2c1.handle); |
| } |
| |
| heap->CollectAllGarbage(); |
| |
| // All objects should be gone. 5 global handles in total. |
| CHECK_EQ(5, counter.NumberOfWeakCalls()); |
| |
| // And now make children weak again and collect them. |
| g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| heap->CollectAllGarbage(); |
| CHECK_EQ(7, counter.NumberOfWeakCalls()); |
| } |
| |
| |
| THREADED_TEST(ApiObjectGroupsCycle) { |
| LocalContext env; |
| v8::Isolate* iso = env->GetIsolate(); |
| HandleScope scope(iso); |
| |
| WeakCallCounter counter(1234); |
| |
| WeakCallCounterAndPersistent<Value> g1s1(&counter); |
| WeakCallCounterAndPersistent<Value> g1s2(&counter); |
| WeakCallCounterAndPersistent<Value> g2s1(&counter); |
| WeakCallCounterAndPersistent<Value> g2s2(&counter); |
| WeakCallCounterAndPersistent<Value> g3s1(&counter); |
| WeakCallCounterAndPersistent<Value> g3s2(&counter); |
| WeakCallCounterAndPersistent<Value> g4s1(&counter); |
| WeakCallCounterAndPersistent<Value> g4s2(&counter); |
| |
| { |
| HandleScope scope(iso); |
| g1s1.handle.Reset(iso, Object::New(iso)); |
| g1s2.handle.Reset(iso, Object::New(iso)); |
| g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| CHECK(g1s1.handle.IsWeak()); |
| CHECK(g1s2.handle.IsWeak()); |
| |
| g2s1.handle.Reset(iso, Object::New(iso)); |
| g2s2.handle.Reset(iso, Object::New(iso)); |
| g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| CHECK(g2s1.handle.IsWeak()); |
| CHECK(g2s2.handle.IsWeak()); |
| |
| g3s1.handle.Reset(iso, Object::New(iso)); |
| g3s2.handle.Reset(iso, Object::New(iso)); |
| g3s1.handle.SetWeak(&g3s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g3s2.handle.SetWeak(&g3s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| CHECK(g3s1.handle.IsWeak()); |
| CHECK(g3s2.handle.IsWeak()); |
| |
| g4s1.handle.Reset(iso, Object::New(iso)); |
| g4s2.handle.Reset(iso, Object::New(iso)); |
| g4s1.handle.SetWeak(&g4s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g4s2.handle.SetWeak(&g4s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| CHECK(g4s1.handle.IsWeak()); |
| CHECK(g4s2.handle.IsWeak()); |
| } |
| |
| WeakCallCounterAndPersistent<Value> root(&counter); |
| root.handle.Reset(iso, g1s1.handle); // make a root. |
| |
| // Connect groups. We're building the following cycle: |
| // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other |
| // groups. |
| { |
| UniqueId id1 = MakeUniqueId(g1s1.handle); |
| UniqueId id2 = MakeUniqueId(g2s1.handle); |
| UniqueId id3 = MakeUniqueId(g3s1.handle); |
| UniqueId id4 = MakeUniqueId(g4s1.handle); |
| iso->SetObjectGroupId(g1s1.handle, id1); |
| iso->SetObjectGroupId(g1s2.handle, id1); |
| iso->SetReferenceFromGroup(id1, g2s1.handle); |
| iso->SetObjectGroupId(g2s1.handle, id2); |
| iso->SetObjectGroupId(g2s2.handle, id2); |
| iso->SetReferenceFromGroup(id2, g3s1.handle); |
| iso->SetObjectGroupId(g3s1.handle, id3); |
| iso->SetObjectGroupId(g3s2.handle, id3); |
| iso->SetReferenceFromGroup(id3, g4s1.handle); |
| iso->SetObjectGroupId(g4s1.handle, id4); |
| iso->SetObjectGroupId(g4s2.handle, id4); |
| iso->SetReferenceFromGroup(id4, g1s1.handle); |
| } |
| // Do a single full GC |
| v8::internal::Heap* heap = |
| reinterpret_cast<v8::internal::Isolate*>(iso)->heap(); |
| heap->CollectAllGarbage(); |
| |
| // All object should be alive. |
| CHECK_EQ(0, counter.NumberOfWeakCalls()); |
| |
| // Weaken the root. |
| root.handle.SetWeak(&root, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| // Groups are deleted, rebuild groups. |
| { |
| UniqueId id1 = MakeUniqueId(g1s1.handle); |
| UniqueId id2 = MakeUniqueId(g2s1.handle); |
| UniqueId id3 = MakeUniqueId(g3s1.handle); |
| UniqueId id4 = MakeUniqueId(g4s1.handle); |
| iso->SetObjectGroupId(g1s1.handle, id1); |
| iso->SetObjectGroupId(g1s2.handle, id1); |
| iso->SetReferenceFromGroup(id1, g2s1.handle); |
| iso->SetObjectGroupId(g2s1.handle, id2); |
| iso->SetObjectGroupId(g2s2.handle, id2); |
| iso->SetReferenceFromGroup(id2, g3s1.handle); |
| iso->SetObjectGroupId(g3s1.handle, id3); |
| iso->SetObjectGroupId(g3s2.handle, id3); |
| iso->SetReferenceFromGroup(id3, g4s1.handle); |
| iso->SetObjectGroupId(g4s1.handle, id4); |
| iso->SetObjectGroupId(g4s2.handle, id4); |
| iso->SetReferenceFromGroup(id4, g1s1.handle); |
| } |
| |
| heap->CollectAllGarbage(); |
| |
| // All objects should be gone. 9 global handles in total. |
| CHECK_EQ(9, counter.NumberOfWeakCalls()); |
| } |
| |
| |
| // TODO(mstarzinger): This should be a THREADED_TEST but causes failures |
| // on the buildbots, so was made non-threaded for the time being. |
| TEST(ApiObjectGroupsCycleForScavenger) { |
| i::FLAG_stress_compaction = false; |
| i::FLAG_gc_global = false; |
| LocalContext env; |
| v8::Isolate* iso = env->GetIsolate(); |
| HandleScope scope(iso); |
| |
| WeakCallCounter counter(1234); |
| |
| WeakCallCounterAndPersistent<Value> g1s1(&counter); |
| WeakCallCounterAndPersistent<Value> g1s2(&counter); |
| WeakCallCounterAndPersistent<Value> g2s1(&counter); |
| WeakCallCounterAndPersistent<Value> g2s2(&counter); |
| WeakCallCounterAndPersistent<Value> g3s1(&counter); |
| WeakCallCounterAndPersistent<Value> g3s2(&counter); |
| |
| { |
| HandleScope scope(iso); |
| g1s1.handle.Reset(iso, Object::New(iso)); |
| g1s2.handle.Reset(iso, Object::New(iso)); |
| g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| g2s1.handle.Reset(iso, Object::New(iso)); |
| g2s2.handle.Reset(iso, Object::New(iso)); |
| g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| g3s1.handle.Reset(iso, Object::New(iso)); |
| g3s2.handle.Reset(iso, Object::New(iso)); |
| g3s1.handle.SetWeak(&g3s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| g3s2.handle.SetWeak(&g3s2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| } |
| |
| // Make a root. |
| WeakCallCounterAndPersistent<Value> root(&counter); |
| root.handle.Reset(iso, g1s1.handle); |
| root.handle.MarkPartiallyDependent(); |
| |
| // Connect groups. We're building the following cycle: |
| // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other |
| // groups. |
| { |
| HandleScope handle_scope(iso); |
| g1s1.handle.MarkPartiallyDependent(); |
| g1s2.handle.MarkPartiallyDependent(); |
| g2s1.handle.MarkPartiallyDependent(); |
| g2s2.handle.MarkPartiallyDependent(); |
| g3s1.handle.MarkPartiallyDependent(); |
| g3s2.handle.MarkPartiallyDependent(); |
| iso->SetObjectGroupId(g1s1.handle, UniqueId(1)); |
| iso->SetObjectGroupId(g1s2.handle, UniqueId(1)); |
| Local<Object>::New(iso, g1s1.handle.As<Object>()) |
| ->Set(v8_str("x"), Local<Value>::New(iso, g2s1.handle)); |
| iso->SetObjectGroupId(g2s1.handle, UniqueId(2)); |
| iso->SetObjectGroupId(g2s2.handle, UniqueId(2)); |
| Local<Object>::New(iso, g2s1.handle.As<Object>()) |
| ->Set(v8_str("x"), Local<Value>::New(iso, g3s1.handle)); |
| iso->SetObjectGroupId(g3s1.handle, UniqueId(3)); |
| iso->SetObjectGroupId(g3s2.handle, UniqueId(3)); |
| Local<Object>::New(iso, g3s1.handle.As<Object>()) |
| ->Set(v8_str("x"), Local<Value>::New(iso, g1s1.handle)); |
| } |
| |
| v8::internal::Heap* heap = |
| reinterpret_cast<v8::internal::Isolate*>(iso)->heap(); |
| heap->CollectAllGarbage(); |
| |
| // All objects should be alive. |
| CHECK_EQ(0, counter.NumberOfWeakCalls()); |
| |
| // Weaken the root. |
| root.handle.SetWeak(&root, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| root.handle.MarkPartiallyDependent(); |
| |
| // Groups are deleted, rebuild groups. |
| { |
| HandleScope handle_scope(iso); |
| g1s1.handle.MarkPartiallyDependent(); |
| g1s2.handle.MarkPartiallyDependent(); |
| g2s1.handle.MarkPartiallyDependent(); |
| g2s2.handle.MarkPartiallyDependent(); |
| g3s1.handle.MarkPartiallyDependent(); |
| g3s2.handle.MarkPartiallyDependent(); |
| iso->SetObjectGroupId(g1s1.handle, UniqueId(1)); |
| iso->SetObjectGroupId(g1s2.handle, UniqueId(1)); |
| Local<Object>::New(iso, g1s1.handle.As<Object>()) |
| ->Set(v8_str("x"), Local<Value>::New(iso, g2s1.handle)); |
| iso->SetObjectGroupId(g2s1.handle, UniqueId(2)); |
| iso->SetObjectGroupId(g2s2.handle, UniqueId(2)); |
| Local<Object>::New(iso, g2s1.handle.As<Object>()) |
| ->Set(v8_str("x"), Local<Value>::New(iso, g3s1.handle)); |
| iso->SetObjectGroupId(g3s1.handle, UniqueId(3)); |
| iso->SetObjectGroupId(g3s2.handle, UniqueId(3)); |
| Local<Object>::New(iso, g3s1.handle.As<Object>()) |
| ->Set(v8_str("x"), Local<Value>::New(iso, g1s1.handle)); |
| } |
| |
| heap->CollectAllGarbage(); |
| |
| // All objects should be gone. 7 global handles in total. |
| CHECK_EQ(7, counter.NumberOfWeakCalls()); |
| } |
| |
| |
| THREADED_TEST(ScriptException) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<Script> script = v8_compile("throw 'panama!';"); |
| v8::TryCatch try_catch(env->GetIsolate()); |
| Local<Value> result = script->Run(); |
| CHECK(result.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ(0, strcmp(*exception_value, "panama!")); |
| } |
| |
| |
| TEST(TryCatchCustomException) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "function CustomError() { this.a = 'b'; }" |
| "(function f() { throw new CustomError(); })();"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(try_catch.Exception() |
| ->ToObject(isolate) |
| ->Get(v8_str("a")) |
| ->Equals(v8_str("b"))); |
| } |
| |
| |
| bool message_received; |
| |
| |
| static void check_message_0(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK_EQ(5.76, data->NumberValue()); |
| CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); |
| CHECK(!message->IsSharedCrossOrigin()); |
| message_received = true; |
| } |
| |
| |
| THREADED_TEST(MessageHandler0) { |
| message_received = false; |
| v8::HandleScope scope(CcTest::isolate()); |
| CHECK(!message_received); |
| LocalContext context; |
| v8::V8::AddMessageListener(check_message_0, v8_num(5.76)); |
| v8::Handle<v8::Script> script = CompileWithOrigin("throw 'error'", "6.75"); |
| script->Run(); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_0); |
| } |
| |
| |
| static void check_message_1(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK(data->IsNumber()); |
| CHECK_EQ(1337, data->Int32Value()); |
| CHECK(!message->IsSharedCrossOrigin()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler1) { |
| message_received = false; |
| v8::HandleScope scope(CcTest::isolate()); |
| CHECK(!message_received); |
| v8::V8::AddMessageListener(check_message_1); |
| LocalContext context; |
| CompileRun("throw 1337;"); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_1); |
| } |
| |
| |
| static void check_message_2(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| LocalContext context; |
| CHECK(data->IsObject()); |
| v8::Local<v8::Value> hidden_property = |
| v8::Object::Cast(*data)->GetHiddenValue(v8_str("hidden key")); |
| CHECK(v8_str("hidden value")->Equals(hidden_property)); |
| CHECK(!message->IsSharedCrossOrigin()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler2) { |
| message_received = false; |
| v8::HandleScope scope(CcTest::isolate()); |
| CHECK(!message_received); |
| v8::V8::AddMessageListener(check_message_2); |
| LocalContext context; |
| v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error")); |
| v8::Object::Cast(*error) |
| ->SetHiddenValue(v8_str("hidden key"), v8_str("hidden value")); |
| context->Global()->Set(v8_str("error"), error); |
| CompileRun("throw error;"); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_2); |
| } |
| |
| |
| static void check_message_3(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK(message->IsSharedCrossOrigin()); |
| CHECK(message->GetScriptOrigin().Options().IsSharedCrossOrigin()); |
| CHECK(message->GetScriptOrigin().Options().IsEmbedderDebugScript()); |
| CHECK(message->GetScriptOrigin().Options().IsOpaque()); |
| CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); |
| CHECK_EQ(7.40, message->GetScriptOrigin().SourceMapUrl()->NumberValue()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler3) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(!message_received); |
| v8::V8::AddMessageListener(check_message_3); |
| LocalContext context; |
| v8::ScriptOrigin origin = v8::ScriptOrigin( |
| v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::True(isolate), Handle<v8::Integer>(), |
| v8::True(isolate), v8_str("7.40"), v8::True(isolate)); |
| v8::Handle<v8::Script> script = |
| Script::Compile(v8_str("throw 'error'"), &origin); |
| script->Run(); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_3); |
| } |
| |
| |
| static void check_message_4(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK(!message->IsSharedCrossOrigin()); |
| CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler4) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(!message_received); |
| v8::V8::AddMessageListener(check_message_4); |
| LocalContext context; |
| v8::ScriptOrigin origin = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::False(isolate)); |
| v8::Handle<v8::Script> script = |
| Script::Compile(v8_str("throw 'error'"), &origin); |
| script->Run(); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_4); |
| } |
| |
| |
| static void check_message_5a(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK(message->IsSharedCrossOrigin()); |
| CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); |
| message_received = true; |
| } |
| |
| |
| static void check_message_5b(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK(!message->IsSharedCrossOrigin()); |
| CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); |
| message_received = true; |
| } |
| |
| |
| TEST(MessageHandler5) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| CHECK(!message_received); |
| v8::V8::AddMessageListener(check_message_5a); |
| LocalContext context; |
| v8::ScriptOrigin origin1 = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::True(isolate)); |
| v8::Handle<v8::Script> script = |
| Script::Compile(v8_str("throw 'error'"), &origin1); |
| script->Run(); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_5a); |
| |
| message_received = false; |
| v8::V8::AddMessageListener(check_message_5b); |
| v8::ScriptOrigin origin2 = |
| v8::ScriptOrigin(v8_str("6.75"), v8::Integer::New(isolate, 1), |
| v8::Integer::New(isolate, 2), v8::False(isolate)); |
| script = Script::Compile(v8_str("throw 'error'"), &origin2); |
| script->Run(); |
| CHECK(message_received); |
| // clear out the message listener |
| v8::V8::RemoveMessageListeners(check_message_5b); |
| } |
| |
| |
| TEST(NativeWeakMap) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| Local<v8::NativeWeakMap> weak_map(v8::NativeWeakMap::New(isolate)); |
| CHECK(!weak_map.IsEmpty()); |
| |
| LocalContext env; |
| Local<Object> value = v8::Object::New(isolate); |
| |
| Local<Object> local1 = v8::Object::New(isolate); |
| CHECK(!weak_map->Has(local1)); |
| CHECK(weak_map->Get(local1)->IsUndefined()); |
| weak_map->Set(local1, value); |
| CHECK(weak_map->Has(local1)); |
| CHECK(value->Equals(weak_map->Get(local1))); |
| |
| WeakCallCounter counter(1234); |
| WeakCallCounterAndPersistent<Value> o1(&counter); |
| WeakCallCounterAndPersistent<Value> o2(&counter); |
| WeakCallCounterAndPersistent<Value> s1(&counter); |
| { |
| HandleScope scope(isolate); |
| Local<v8::Object> obj1 = v8::Object::New(isolate); |
| Local<v8::Object> obj2 = v8::Object::New(isolate); |
| Local<v8::Symbol> sym1 = v8::Symbol::New(isolate); |
| |
| weak_map->Set(obj1, value); |
| weak_map->Set(obj2, value); |
| weak_map->Set(sym1, value); |
| |
| o1.handle.Reset(isolate, obj1); |
| o2.handle.Reset(isolate, obj2); |
| s1.handle.Reset(isolate, sym1); |
| |
| CHECK(weak_map->Has(local1)); |
| CHECK(weak_map->Has(obj1)); |
| CHECK(weak_map->Has(obj2)); |
| CHECK(weak_map->Has(sym1)); |
| |
| CHECK(value->Equals(weak_map->Get(local1))); |
| CHECK(value->Equals(weak_map->Get(obj1))); |
| CHECK(value->Equals(weak_map->Get(obj2))); |
| CHECK(value->Equals(weak_map->Get(sym1))); |
| } |
| CcTest::heap()->CollectAllGarbage(); |
| { |
| HandleScope scope(isolate); |
| CHECK(value->Equals(weak_map->Get(local1))); |
| CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, o1.handle)))); |
| CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, o2.handle)))); |
| CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, s1.handle)))); |
| } |
| |
| o1.handle.SetWeak(&o1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| o2.handle.SetWeak(&o2, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| s1.handle.SetWeak(&s1, &WeakPointerCallback, |
| v8::WeakCallbackType::kParameter); |
| |
| CcTest::heap()->CollectAllGarbage(); |
| CHECK_EQ(3, counter.NumberOfWeakCalls()); |
| |
| CHECK(o1.handle.IsEmpty()); |
| CHECK(o2.handle.IsEmpty()); |
| CHECK(s1.handle.IsEmpty()); |
| |
| CHECK(value->Equals(weak_map->Get(local1))); |
| CHECK(weak_map->Delete(local1)); |
| CHECK(!weak_map->Has(local1)); |
| CHECK(weak_map->Get(local1)->IsUndefined()); |
| } |
| |
| |
| THREADED_TEST(GetSetProperty) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| context->Global()->Set(v8_str("foo"), v8_num(14)); |
| context->Global()->Set(v8_str("12"), v8_num(92)); |
| context->Global()->Set(v8::Integer::New(isolate, 16), v8_num(32)); |
| context->Global()->Set(v8_num(13), v8_num(56)); |
| Local<Value> foo = CompileRun("this.foo"); |
| CHECK_EQ(14, foo->Int32Value()); |
| Local<Value> twelve = CompileRun("this[12]"); |
| CHECK_EQ(92, twelve->Int32Value()); |
| Local<Value> sixteen = CompileRun("this[16]"); |
| CHECK_EQ(32, sixteen->Int32Value()); |
| Local<Value> thirteen = CompileRun("this[13]"); |
| CHECK_EQ(56, thirteen->Int32Value()); |
| CHECK_EQ(92, |
| context->Global()->Get(v8::Integer::New(isolate, 12))->Int32Value()); |
| CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); |
| CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); |
| CHECK_EQ(32, |
| context->Global()->Get(v8::Integer::New(isolate, 16))->Int32Value()); |
| CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); |
| CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); |
| CHECK_EQ(56, |
| context->Global()->Get(v8::Integer::New(isolate, 13))->Int32Value()); |
| CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); |
| CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(PropertyAttributes) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| // none |
| Local<String> prop = v8_str("none"); |
| context->Global()->Set(prop, v8_num(7)); |
| CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); |
| // read-only |
| prop = v8_str("read_only"); |
| context->Global()->ForceSet(prop, v8_num(7), v8::ReadOnly); |
| CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); |
| CHECK_EQ(v8::ReadOnly, context->Global()->GetPropertyAttributes(prop)); |
| CompileRun("read_only = 9"); |
| CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); |
| context->Global()->Set(prop, v8_num(10)); |
| CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); |
| // dont-delete |
| prop = v8_str("dont_delete"); |
| context->Global()->ForceSet(prop, v8_num(13), v8::DontDelete); |
| CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); |
| CompileRun("delete dont_delete"); |
| CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); |
| CHECK_EQ(v8::DontDelete, context->Global()->GetPropertyAttributes(prop)); |
| // dont-enum |
| prop = v8_str("dont_enum"); |
| context->Global()->ForceSet(prop, v8_num(28), v8::DontEnum); |
| CHECK_EQ(v8::DontEnum, context->Global()->GetPropertyAttributes(prop)); |
| // absent |
| prop = v8_str("absent"); |
| CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); |
| Local<Value> fake_prop = v8_num(1); |
| CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(fake_prop)); |
| // exception |
| TryCatch try_catch(context->GetIsolate()); |
| Local<Value> exception = |
| CompileRun("({ toString: function() { throw 'exception';} })"); |
| CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(exception)); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("exception", *exception_value)); |
| try_catch.Reset(); |
| } |
| |
| |
| THREADED_TEST(Array) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| Local<v8::Array> array = v8::Array::New(context->GetIsolate()); |
| CHECK_EQ(0u, array->Length()); |
| CHECK(array->Get(0)->IsUndefined()); |
| CHECK(!array->Has(0)); |
| CHECK(array->Get(100)->IsUndefined()); |
| CHECK(!array->Has(100)); |
| array->Set(2, v8_num(7)); |
| CHECK_EQ(3u, array->Length()); |
| CHECK(!array->Has(0)); |
| CHECK(!array->Has(1)); |
| CHECK(array->Has(2)); |
| CHECK_EQ(7, array->Get(2)->Int32Value()); |
| Local<Value> obj = CompileRun("[1, 2, 3]"); |
| Local<v8::Array> arr = obj.As<v8::Array>(); |
| CHECK_EQ(3u, arr->Length()); |
| CHECK_EQ(1, arr->Get(0)->Int32Value()); |
| CHECK_EQ(2, arr->Get(1)->Int32Value()); |
| CHECK_EQ(3, arr->Get(2)->Int32Value()); |
| array = v8::Array::New(context->GetIsolate(), 27); |
| CHECK_EQ(27u, array->Length()); |
| array = v8::Array::New(context->GetIsolate(), -27); |
| CHECK_EQ(0u, array->Length()); |
| } |
| |
| |
| void HandleF(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::EscapableHandleScope scope(args.GetIsolate()); |
| ApiTestFuzzer::Fuzz(); |
| Local<v8::Array> result = v8::Array::New(args.GetIsolate(), args.Length()); |
| for (int i = 0; i < args.Length(); i++) result->Set(i, args[i]); |
| args.GetReturnValue().Set(scope.Escape(result)); |
| } |
| |
| |
| THREADED_TEST(Vector) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> global = ObjectTemplate::New(isolate); |
| global->Set(v8_str("f"), v8::FunctionTemplate::New(isolate, HandleF)); |
| LocalContext context(0, global); |
| |
| const char* fun = "f()"; |
| Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); |
| CHECK_EQ(0u, a0->Length()); |
| |
| const char* fun2 = "f(11)"; |
| Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); |
| CHECK_EQ(1u, a1->Length()); |
| CHECK_EQ(11, a1->Get(0)->Int32Value()); |
| |
| const char* fun3 = "f(12, 13)"; |
| Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); |
| CHECK_EQ(2u, a2->Length()); |
| CHECK_EQ(12, a2->Get(0)->Int32Value()); |
| CHECK_EQ(13, a2->Get(1)->Int32Value()); |
| |
| const char* fun4 = "f(14, 15, 16)"; |
| Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); |
| CHECK_EQ(3u, a3->Length()); |
| CHECK_EQ(14, a3->Get(0)->Int32Value()); |
| CHECK_EQ(15, a3->Get(1)->Int32Value()); |
| CHECK_EQ(16, a3->Get(2)->Int32Value()); |
| |
| const char* fun5 = "f(17, 18, 19, 20)"; |
| Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); |
| CHECK_EQ(4u, a4->Length()); |
| CHECK_EQ(17, a4->Get(0)->Int32Value()); |
| CHECK_EQ(18, a4->Get(1)->Int32Value()); |
| CHECK_EQ(19, a4->Get(2)->Int32Value()); |
| CHECK_EQ(20, a4->Get(3)->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(FunctionCall) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "function Foo() {" |
| " var result = [];" |
| " for (var i = 0; i < arguments.length; i++) {" |
| " result.push(arguments[i]);" |
| " }" |
| " return result;" |
| "}" |
| "function ReturnThisSloppy() {" |
| " return this;" |
| "}" |
| "function ReturnThisStrict() {" |
| " 'use strict';" |
| " return this;" |
| "}"); |
| Local<Function> Foo = |
| Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); |
| Local<Function> ReturnThisSloppy = |
| Local<Function>::Cast(context->Global()->Get(v8_str("ReturnThisSloppy"))); |
| Local<Function> ReturnThisStrict = |
| Local<Function>::Cast(context->Global()->Get(v8_str("ReturnThisStrict"))); |
| |
| v8::Handle<Value>* args0 = NULL; |
| Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); |
| CHECK_EQ(0u, a0->Length()); |
| |
| v8::Handle<Value> args1[] = {v8_num(1.1)}; |
| Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); |
| CHECK_EQ(1u, a1->Length()); |
| CHECK_EQ(1.1, a1->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| |
| v8::Handle<Value> args2[] = {v8_num(2.2), v8_num(3.3)}; |
| Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); |
| CHECK_EQ(2u, a2->Length()); |
| CHECK_EQ(2.2, a2->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| CHECK_EQ(3.3, a2->Get(v8::Integer::New(isolate, 1))->NumberValue()); |
| |
| v8::Handle<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)}; |
| Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); |
| CHECK_EQ(3u, a3->Length()); |
| CHECK_EQ(4.4, a3->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| CHECK_EQ(5.5, a3->Get(v8::Integer::New(isolate, 1))->NumberValue()); |
| CHECK_EQ(6.6, a3->Get(v8::Integer::New(isolate, 2))->NumberValue()); |
| |
| v8::Handle<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9), |
| v8_num(10.11)}; |
| Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); |
| CHECK_EQ(4u, a4->Length()); |
| CHECK_EQ(7.7, a4->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| CHECK_EQ(8.8, a4->Get(v8::Integer::New(isolate, 1))->NumberValue()); |
| CHECK_EQ(9.9, a4->Get(v8::Integer::New(isolate, 2))->NumberValue()); |
| CHECK_EQ(10.11, a4->Get(v8::Integer::New(isolate, 3))->NumberValue()); |
| |
| Local<v8::Value> r1 = ReturnThisSloppy->Call(v8::Undefined(isolate), 0, NULL); |
| CHECK(r1->StrictEquals(context->Global())); |
| Local<v8::Value> r2 = ReturnThisSloppy->Call(v8::Null(isolate), 0, NULL); |
| CHECK(r2->StrictEquals(context->Global())); |
| Local<v8::Value> r3 = ReturnThisSloppy->Call(v8_num(42), 0, NULL); |
| CHECK(r3->IsNumberObject()); |
| CHECK_EQ(42.0, r3.As<v8::NumberObject>()->ValueOf()); |
| Local<v8::Value> r4 = ReturnThisSloppy->Call(v8_str("hello"), 0, NULL); |
| CHECK(r4->IsStringObject()); |
| CHECK(r4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> r5 = ReturnThisSloppy->Call(v8::True(isolate), 0, NULL); |
| CHECK(r5->IsBooleanObject()); |
| CHECK(r5.As<v8::BooleanObject>()->ValueOf()); |
| |
| Local<v8::Value> r6 = ReturnThisStrict->Call(v8::Undefined(isolate), 0, NULL); |
| CHECK(r6->IsUndefined()); |
| Local<v8::Value> r7 = ReturnThisStrict->Call(v8::Null(isolate), 0, NULL); |
| CHECK(r7->IsNull()); |
| Local<v8::Value> r8 = ReturnThisStrict->Call(v8_num(42), 0, NULL); |
| CHECK(r8->StrictEquals(v8_num(42))); |
| Local<v8::Value> r9 = ReturnThisStrict->Call(v8_str("hello"), 0, NULL); |
| CHECK(r9->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> r10 = ReturnThisStrict->Call(v8::True(isolate), 0, NULL); |
| CHECK(r10->StrictEquals(v8::True(isolate))); |
| } |
| |
| |
| THREADED_TEST(ConstructCall) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "function Foo() {" |
| " var result = [];" |
| " for (var i = 0; i < arguments.length; i++) {" |
| " result.push(arguments[i]);" |
| " }" |
| " return result;" |
| "}"); |
| Local<Function> Foo = |
| Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); |
| |
| v8::Handle<Value>* args0 = NULL; |
| Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); |
| CHECK_EQ(0u, a0->Length()); |
| |
| v8::Handle<Value> args1[] = {v8_num(1.1)}; |
| Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); |
| CHECK_EQ(1u, a1->Length()); |
| CHECK_EQ(1.1, a1->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| |
| v8::Handle<Value> args2[] = {v8_num(2.2), v8_num(3.3)}; |
| Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); |
| CHECK_EQ(2u, a2->Length()); |
| CHECK_EQ(2.2, a2->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| CHECK_EQ(3.3, a2->Get(v8::Integer::New(isolate, 1))->NumberValue()); |
| |
| v8::Handle<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)}; |
| Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); |
| CHECK_EQ(3u, a3->Length()); |
| CHECK_EQ(4.4, a3->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| CHECK_EQ(5.5, a3->Get(v8::Integer::New(isolate, 1))->NumberValue()); |
| CHECK_EQ(6.6, a3->Get(v8::Integer::New(isolate, 2))->NumberValue()); |
| |
| v8::Handle<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9), |
| v8_num(10.11)}; |
| Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); |
| CHECK_EQ(4u, a4->Length()); |
| CHECK_EQ(7.7, a4->Get(v8::Integer::New(isolate, 0))->NumberValue()); |
| CHECK_EQ(8.8, a4->Get(v8::Integer::New(isolate, 1))->NumberValue()); |
| CHECK_EQ(9.9, a4->Get(v8::Integer::New(isolate, 2))->NumberValue()); |
| CHECK_EQ(10.11, a4->Get(v8::Integer::New(isolate, 3))->NumberValue()); |
| } |
| |
| |
| static void CheckUncle(v8::TryCatch* try_catch) { |
| CHECK(try_catch->HasCaught()); |
| String::Utf8Value str_value(try_catch->Exception()); |
| CHECK_EQ(0, strcmp(*str_value, "uncle?")); |
| try_catch->Reset(); |
| } |
| |
| |
| THREADED_TEST(ConversionNumber) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| // Very large number. |
| CompileRun("var obj = Math.pow(2,32) * 1237;"); |
| Local<Value> obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(5312874545152.0, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(0, obj->ToInt32(isolate)->Value()); |
| CHECK(0u == |
| obj->ToUint32(isolate)->Value()); // NOLINT - no CHECK_EQ for unsigned. |
| // Large number. |
| CompileRun("var obj = -1234567890123;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(-1234567890123.0, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(-1912276171, obj->ToInt32(isolate)->Value()); |
| CHECK(2382691125u == obj->ToUint32(isolate)->Value()); // NOLINT |
| // Small positive integer. |
| CompileRun("var obj = 42;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(42.0, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(42, obj->ToInt32(isolate)->Value()); |
| CHECK(42u == obj->ToUint32(isolate)->Value()); // NOLINT |
| // Negative integer. |
| CompileRun("var obj = -37;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(-37.0, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(-37, obj->ToInt32(isolate)->Value()); |
| CHECK(4294967259u == obj->ToUint32(isolate)->Value()); // NOLINT |
| // Positive non-int32 integer. |
| CompileRun("var obj = 0x81234567;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(2166572391.0, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(-2128394905, obj->ToInt32(isolate)->Value()); |
| CHECK(2166572391u == obj->ToUint32(isolate)->Value()); // NOLINT |
| // Fraction. |
| CompileRun("var obj = 42.3;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(42.3, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(42, obj->ToInt32(isolate)->Value()); |
| CHECK(42u == obj->ToUint32(isolate)->Value()); // NOLINT |
| // Large negative fraction. |
| CompileRun("var obj = -5726623061.75;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK_EQ(-5726623061.75, obj->ToNumber(isolate)->Value()); |
| CHECK_EQ(-1431655765, obj->ToInt32(isolate)->Value()); |
| CHECK(2863311531u == obj->ToUint32(isolate)->Value()); // NOLINT |
| } |
| |
| |
| THREADED_TEST(isNumberType) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| // Very large number. |
| CompileRun("var obj = Math.pow(2,32) * 1237;"); |
| Local<Value> obj = env->Global()->Get(v8_str("obj")); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Large negative number. |
| CompileRun("var obj = -1234567890123;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Small positive integer. |
| CompileRun("var obj = 42;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(obj->IsInt32()); |
| CHECK(obj->IsUint32()); |
| // Negative integer. |
| CompileRun("var obj = -37;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Positive non-int32 integer. |
| CompileRun("var obj = 0x81234567;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(!obj->IsInt32()); |
| CHECK(obj->IsUint32()); |
| // Fraction. |
| CompileRun("var obj = 42.3;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Large negative fraction. |
| CompileRun("var obj = -5726623061.75;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| // Positive zero |
| CompileRun("var obj = 0.0;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(obj->IsInt32()); |
| CHECK(obj->IsUint32()); |
| // Positive zero |
| CompileRun("var obj = -0.0;"); |
| obj = env->Global()->Get(v8_str("obj")); |
| CHECK(!obj->IsInt32()); |
| CHECK(!obj->IsUint32()); |
| } |
| |
| |
| THREADED_TEST(ConversionException) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| CompileRun( |
| "function TestClass() { };" |
| "TestClass.prototype.toString = function () { throw 'uncle?'; };" |
| "var obj = new TestClass();"); |
| Local<Value> obj = env->Global()->Get(v8_str("obj")); |
| |
| v8::TryCatch try_catch(isolate); |
| |
| Local<Value> to_string_result = obj->ToString(isolate); |
| CHECK(to_string_result.IsEmpty()); |
| CheckUncle(&try_catch); |
| |
| Local<Value> to_number_result = obj->ToNumber(isolate); |
| CHECK(to_number_result.IsEmpty()); |
| CheckUncle(&try_catch); |
| |
| Local<Value> to_integer_result = obj->ToInteger(isolate); |
| CHECK(to_integer_result.IsEmpty()); |
| CheckUncle(&try_catch); |
| |
| Local<Value> to_uint32_result = obj->ToUint32(isolate); |
| CHECK(to_uint32_result.IsEmpty()); |
| CheckUncle(&try_catch); |
| |
| Local<Value> to_int32_result = obj->ToInt32(isolate); |
| CHECK(to_int32_result.IsEmpty()); |
| CheckUncle(&try_catch); |
| |
| Local<Value> to_object_result = v8::Undefined(isolate)->ToObject(isolate); |
| CHECK(to_object_result.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| int32_t int32_value = obj->Int32Value(); |
| CHECK_EQ(0, int32_value); |
| CheckUncle(&try_catch); |
| |
| uint32_t uint32_value = obj->Uint32Value(); |
| CHECK_EQ(0u, uint32_value); |
| CheckUncle(&try_catch); |
| |
| double number_value = obj->NumberValue(); |
| CHECK(std::isnan(number_value)); |
| CheckUncle(&try_catch); |
| |
| int64_t integer_value = obj->IntegerValue(); |
| CHECK_EQ(0, integer_value); |
| CheckUncle(&try_catch); |
| } |
| |
| |
| void ThrowFromC(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetIsolate()->ThrowException(v8_str("konto")); |
| } |
| |
| |
| void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| if (args.Length() < 1) { |
| args.GetReturnValue().Set(false); |
| return; |
| } |
| v8::HandleScope scope(args.GetIsolate()); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| Local<Value> result = CompileRun(args[0]->ToString(args.GetIsolate())); |
| CHECK(!try_catch.HasCaught() || result.IsEmpty()); |
| args.GetReturnValue().Set(try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(APICatch) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| CompileRun( |
| "var thrown = false;" |
| "try {" |
| " ThrowFromC();" |
| "} catch (e) {" |
| " thrown = true;" |
| "}"); |
| Local<Value> thrown = context->Global()->Get(v8_str("thrown")); |
| CHECK(thrown->BooleanValue()); |
| } |
| |
| |
| THREADED_TEST(APIThrowTryCatch) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| v8::TryCatch try_catch(isolate); |
| CompileRun("ThrowFromC();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| // Test that a try-finally block doesn't shadow a try-catch block |
| // when setting up an external handler. |
| // |
| // BUG(271): Some of the exception propagation does not work on the |
| // ARM simulator because the simulator separates the C++ stack and the |
| // JS stack. This test therefore fails on the simulator. The test is |
| // not threaded to allow the threading tests to run on the simulator. |
| TEST(TryCatchInTryFinally) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("CCatcher"), v8::FunctionTemplate::New(isolate, CCatcher)); |
| LocalContext context(0, templ); |
| Local<Value> result = CompileRun( |
| "try {" |
| " try {" |
| " CCatcher('throw 7;');" |
| " } finally {" |
| " }" |
| "} catch (e) {" |
| "}"); |
| CHECK(result->IsTrue()); |
| } |
| |
| |
| static void check_reference_error_message(v8::Handle<v8::Message> message, |
| v8::Handle<v8::Value> data) { |
| const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; |
| CHECK(message->Get()->Equals(v8_str(reference_error))); |
| } |
| |
| |
| static void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK(false); |
| } |
| |
| |
| // Test that overwritten methods are not invoked on uncaught exception |
| // formatting. However, they are invoked when performing normal error |
| // string conversions. |
| TEST(APIThrowMessageOverwrittenToString) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::AddMessageListener(check_reference_error_message); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail)); |
| LocalContext context(NULL, templ); |
| CompileRun("asdf;"); |
| CompileRun( |
| "var limit = {};" |
| "limit.valueOf = fail;" |
| "Error.stackTraceLimit = limit;"); |
| CompileRun("asdf"); |
| CompileRun("Array.prototype.pop = fail;"); |
| CompileRun("Object.prototype.hasOwnProperty = fail;"); |
| CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }"); |
| CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }"); |
| CompileRun("String.prototype.toString = function f() { return 'Yikes'; }"); |
| CompileRun( |
| "ReferenceError.prototype.toString =" |
| " function() { return 'Whoops' }"); |
| CompileRun("asdf;"); |
| CompileRun("ReferenceError.prototype.constructor.name = void 0;"); |
| CompileRun("asdf;"); |
| CompileRun("ReferenceError.prototype.constructor = void 0;"); |
| CompileRun("asdf;"); |
| CompileRun("ReferenceError.prototype.__proto__ = new Object();"); |
| CompileRun("asdf;"); |
| CompileRun("ReferenceError.prototype = new Object();"); |
| CompileRun("asdf;"); |
| v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); |
| CHECK(string->Equals(v8_str("Whoops"))); |
| CompileRun( |
| "ReferenceError.prototype.constructor = new Object();" |
| "ReferenceError.prototype.constructor.name = 1;" |
| "Number.prototype.toString = function() { return 'Whoops'; };" |
| "ReferenceError.prototype.toString = Object.prototype.toString;"); |
| CompileRun("asdf;"); |
| v8::V8::RemoveMessageListeners(check_reference_error_message); |
| } |
| |
| |
| static void check_custom_error_tostring(v8::Handle<v8::Message> message, |
| v8::Handle<v8::Value> data) { |
| const char* uncaught_error = "Uncaught MyError toString"; |
| CHECK(message->Get()->Equals(v8_str(uncaught_error))); |
| } |
| |
| |
| TEST(CustomErrorToString) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::V8::AddMessageListener(check_custom_error_tostring); |
| CompileRun( |
| "function MyError(name, message) { " |
| " this.name = name; " |
| " this.message = message; " |
| "} " |
| "MyError.prototype = Object.create(Error.prototype); " |
| "MyError.prototype.toString = function() { " |
| " return 'MyError toString'; " |
| "}; " |
| "throw new MyError('my name', 'my message'); "); |
| v8::V8::RemoveMessageListeners(check_custom_error_tostring); |
| } |
| |
| |
| static void check_custom_error_message(v8::Handle<v8::Message> message, |
| v8::Handle<v8::Value> data) { |
| const char* uncaught_error = "Uncaught MyError: my message"; |
| printf("%s\n", *v8::String::Utf8Value(message->Get())); |
| CHECK(message->Get()->Equals(v8_str(uncaught_error))); |
| } |
| |
| |
| TEST(CustomErrorMessage) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::V8::AddMessageListener(check_custom_error_message); |
| |
| // Handlebars. |
| CompileRun( |
| "function MyError(msg) { " |
| " this.name = 'MyError'; " |
| " this.message = msg; " |
| "} " |
| "MyError.prototype = new Error(); " |
| "throw new MyError('my message'); "); |
| |
| // Closure. |
| CompileRun( |
| "function MyError(msg) { " |
| " this.name = 'MyError'; " |
| " this.message = msg; " |
| "} " |
| "inherits = function(childCtor, parentCtor) { " |
| " function tempCtor() {}; " |
| " tempCtor.prototype = parentCtor.prototype; " |
| " childCtor.superClass_ = parentCtor.prototype; " |
| " childCtor.prototype = new tempCtor(); " |
| " childCtor.prototype.constructor = childCtor; " |
| "}; " |
| "inherits(MyError, Error); " |
| "throw new MyError('my message'); "); |
| |
| // Object.create. |
| CompileRun( |
| "function MyError(msg) { " |
| " this.name = 'MyError'; " |
| " this.message = msg; " |
| "} " |
| "MyError.prototype = Object.create(Error.prototype); " |
| "throw new MyError('my message'); "); |
| |
| v8::V8::RemoveMessageListeners(check_custom_error_message); |
| } |
| |
| |
| static void check_custom_rethrowing_message(v8::Handle<v8::Message> message, |
| v8::Handle<v8::Value> data) { |
| const char* uncaught_error = "Uncaught exception"; |
| CHECK(message->Get()->Equals(v8_str(uncaught_error))); |
| } |
| |
| |
| TEST(CustomErrorRethrowsOnToString) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::V8::AddMessageListener(check_custom_rethrowing_message); |
| |
| CompileRun( |
| "var e = { toString: function() { throw e; } };" |
| "try { throw e; } finally {}"); |
| |
| v8::V8::RemoveMessageListeners(check_custom_rethrowing_message); |
| } |
| |
| |
| static void receive_message(v8::Handle<v8::Message> message, |
| v8::Handle<v8::Value> data) { |
| message->Get(); |
| message_received = true; |
| } |
| |
| |
| TEST(APIThrowMessage) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::AddMessageListener(receive_message); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| CompileRun("ThrowFromC();"); |
| CHECK(message_received); |
| v8::V8::RemoveMessageListeners(receive_message); |
| } |
| |
| |
| TEST(APIThrowMessageAndVerboseTryCatch) { |
| message_received = false; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::AddMessageListener(receive_message); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| Local<Value> result = CompileRun("ThrowFromC();"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(result.IsEmpty()); |
| CHECK(message_received); |
| v8::V8::RemoveMessageListeners(receive_message); |
| } |
| |
| |
| TEST(APIStackOverflowAndVerboseTryCatch) { |
| message_received = false; |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::V8::AddMessageListener(receive_message); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| try_catch.SetVerbose(true); |
| Local<Value> result = CompileRun("function foo() { foo(); } foo();"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(result.IsEmpty()); |
| CHECK(message_received); |
| v8::V8::RemoveMessageListeners(receive_message); |
| } |
| |
| |
| THREADED_TEST(ExternalScriptException) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("ThrowFromC"), |
| v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| LocalContext context(0, templ); |
| |
| v8::TryCatch try_catch(isolate); |
| Local<Value> result = CompileRun("ThrowFromC(); throw 'panama';"); |
| CHECK(result.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("konto", *exception_value)); |
| } |
| |
| |
| void CThrowCountDown(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(4, args.Length()); |
| int count = args[0]->Int32Value(); |
| int cInterval = args[2]->Int32Value(); |
| if (count == 0) { |
| args.GetIsolate()->ThrowException(v8_str("FromC")); |
| return; |
| } else { |
| Local<v8::Object> global = args.GetIsolate()->GetCurrentContext()->Global(); |
| Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); |
| v8::Handle<Value> argv[] = {v8_num(count - 1), args[1], args[2], args[3]}; |
| if (count % cInterval == 0) { |
| v8::TryCatch try_catch(args.GetIsolate()); |
| Local<Value> result = fun.As<Function>()->Call(global, 4, argv); |
| int expected = args[3]->Int32Value(); |
| if (try_catch.HasCaught()) { |
| CHECK_EQ(expected, count); |
| CHECK(result.IsEmpty()); |
| CHECK(!CcTest::i_isolate()->has_scheduled_exception()); |
| } else { |
| CHECK_NE(expected, count); |
| } |
| args.GetReturnValue().Set(result); |
| return; |
| } else { |
| args.GetReturnValue().Set(fun.As<Function>()->Call(global, 4, argv)); |
| return; |
| } |
| } |
| } |
| |
| |
| void JSCheck(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(3, args.Length()); |
| bool equality = args[0]->BooleanValue(); |
| int count = args[1]->Int32Value(); |
| int expected = args[2]->Int32Value(); |
| if (equality) { |
| CHECK_EQ(count, expected); |
| } else { |
| CHECK_NE(count, expected); |
| } |
| } |
| |
| |
| THREADED_TEST(EvalInTryFinally) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CompileRun( |
| "(function() {" |
| " try {" |
| " eval('asldkf (*&^&*^');" |
| " } finally {" |
| " return;" |
| " }" |
| "})()"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| // This test works by making a stack of alternating JavaScript and C |
| // activations. These activations set up exception handlers with regular |
| // intervals, one interval for C activations and another for JavaScript |
| // activations. When enough activations have been created an exception is |
| // thrown and we check that the right activation catches the exception and that |
| // no other activations do. The right activation is always the topmost one with |
| // a handler, regardless of whether it is in JavaScript or C. |
| // |
| // The notation used to describe a test case looks like this: |
| // |
| // *JS[4] *C[3] @JS[2] C[1] JS[0] |
| // |
| // Each entry is an activation, either JS or C. The index is the count at that |
| // level. Stars identify activations with exception handlers, the @ identifies |
| // the exception handler that should catch the exception. |
| // |
| // BUG(271): Some of the exception propagation does not work on the |
| // ARM simulator because the simulator separates the C++ stack and the |
| // JS stack. This test therefore fails on the simulator. The test is |
| // not threaded to allow the threading tests to run on the simulator. |
| TEST(ExceptionOrder) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("check"), v8::FunctionTemplate::New(isolate, JSCheck)); |
| templ->Set(v8_str("CThrowCountDown"), |
| v8::FunctionTemplate::New(isolate, CThrowCountDown)); |
| LocalContext context(0, templ); |
| CompileRun( |
| "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" |
| " if (count == 0) throw 'FromJS';" |
| " if (count % jsInterval == 0) {" |
| " try {" |
| " var value = CThrowCountDown(count - 1," |
| " jsInterval," |
| " cInterval," |
| " expected);" |
| " check(false, count, expected);" |
| " return value;" |
| " } catch (e) {" |
| " check(true, count, expected);" |
| " }" |
| " } else {" |
| " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" |
| " }" |
| "}"); |
| Local<Function> fun = |
| Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); |
| |
| const int argc = 4; |
| // count jsInterval cInterval expected |
| |
| // *JS[4] *C[3] @JS[2] C[1] JS[0] |
| v8::Handle<Value> a0[argc] = {v8_num(4), v8_num(2), v8_num(3), v8_num(2)}; |
| fun->Call(fun, argc, a0); |
| |
| // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] |
| v8::Handle<Value> a1[argc] = {v8_num(5), v8_num(6), v8_num(1), v8_num(2)}; |
| fun->Call(fun, argc, a1); |
| |
| // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] |
| v8::Handle<Value> a2[argc] = {v8_num(6), v8_num(7), v8_num(5), v8_num(5)}; |
| fun->Call(fun, argc, a2); |
| |
| // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] |
| v8::Handle<Value> a3[argc] = {v8_num(6), v8_num(6), v8_num(7), v8_num(6)}; |
| fun->Call(fun, argc, a3); |
| |
| // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] |
| v8::Handle<Value> a4[argc] = {v8_num(6), v8_num(4), v8_num(5), v8_num(4)}; |
| fun->Call(fun, argc, a4); |
| |
| // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] |
| v8::Handle<Value> a5[argc] = {v8_num(6), v8_num(4), v8_num(3), v8_num(3)}; |
| fun->Call(fun, argc, a5); |
| } |
| |
| |
| void ThrowValue(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK_EQ(1, args.Length()); |
| args.GetIsolate()->ThrowException(args[0]); |
| } |
| |
| |
| THREADED_TEST(ThrowValues) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(isolate, ThrowValue)); |
| LocalContext context(0, templ); |
| v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( |
| "function Run(obj) {" |
| " try {" |
| " Throw(obj);" |
| " } catch (e) {" |
| " return e;" |
| " }" |
| " return 'no exception';" |
| "}" |
| "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); |
| CHECK_EQ(5u, result->Length()); |
| CHECK(result->Get(v8::Integer::New(isolate, 0))->IsString()); |
| CHECK(result->Get(v8::Integer::New(isolate, 1))->IsNumber()); |
| CHECK_EQ(1, result->Get(v8::Integer::New(isolate, 1))->Int32Value()); |
| CHECK(result->Get(v8::Integer::New(isolate, 2))->IsNumber()); |
| CHECK_EQ(0, result->Get(v8::Integer::New(isolate, 2))->Int32Value()); |
| CHECK(result->Get(v8::Integer::New(isolate, 3))->IsNull()); |
| CHECK(result->Get(v8::Integer::New(isolate, 4))->IsUndefined()); |
| } |
| |
| |
| THREADED_TEST(CatchZero) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("throw 10"); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(10, try_catch.Exception()->Int32Value()); |
| try_catch.Reset(); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("throw 0"); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(0, try_catch.Exception()->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(CatchExceptionFromWith) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("var o = {}; with (o) { throw 42; }"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(TryCatchAndFinallyHidingException) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); |
| CompileRun("f({toString: function() { throw 42; }});"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| void WithTryCatch(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::TryCatch try_catch(args.GetIsolate()); |
| } |
| |
| |
| THREADED_TEST(TryCatchAndFinally) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| context->Global()->Set( |
| v8_str("native_with_try_catch"), |
| v8::FunctionTemplate::New(isolate, WithTryCatch)->GetFunction()); |
| v8::TryCatch try_catch(isolate); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun( |
| "try {\n" |
| " throw new Error('a');\n" |
| "} finally {\n" |
| " native_with_try_catch();\n" |
| "}\n"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| static void TryCatchNested1Helper(int depth) { |
| if (depth > 0) { |
| v8::TryCatch try_catch(CcTest::isolate()); |
| try_catch.SetVerbose(true); |
| TryCatchNested1Helper(depth - 1); |
| CHECK(try_catch.HasCaught()); |
| try_catch.ReThrow(); |
| } else { |
| CcTest::isolate()->ThrowException(v8_str("E1")); |
| } |
| } |
| |
| |
| static void TryCatchNested2Helper(int depth) { |
| if (depth > 0) { |
| v8::TryCatch try_catch(CcTest::isolate()); |
| try_catch.SetVerbose(true); |
| TryCatchNested2Helper(depth - 1); |
| CHECK(try_catch.HasCaught()); |
| try_catch.ReThrow(); |
| } else { |
| CompileRun("throw 'E2';"); |
| } |
| } |
| |
| |
| TEST(TryCatchNested) { |
| v8::V8::Initialize(); |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| { |
| // Test nested try-catch with a native throw in the end. |
| v8::TryCatch try_catch(context->GetIsolate()); |
| TryCatchNested1Helper(5); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "E1")); |
| } |
| |
| { |
| // Test nested try-catch with a JavaScript throw in the end. |
| v8::TryCatch try_catch(context->GetIsolate()); |
| TryCatchNested2Helper(5); |
| CHECK(try_catch.HasCaught()); |
| CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "E2")); |
| } |
| } |
| |
| |
| void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) { |
| CHECK(try_catch->HasCaught()); |
| Handle<Message> message = try_catch->Message(); |
| Handle<Value> resource = message->GetScriptOrigin().ResourceName(); |
| CHECK_EQ(0, strcmp(*v8::String::Utf8Value(resource), "inner")); |
| CHECK_EQ(0, |
| strcmp(*v8::String::Utf8Value(message->Get()), "Uncaught Error: a")); |
| CHECK_EQ(1, message->GetLineNumber()); |
| CHECK_EQ(0, message->GetStartColumn()); |
| } |
| |
| |
| void TryCatchMixedNestingHelper( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| CompileRunWithOrigin("throw new Error('a');\n", "inner", 0, 0); |
| CHECK(try_catch.HasCaught()); |
| TryCatchMixedNestingCheck(&try_catch); |
| try_catch.ReThrow(); |
| } |
| |
| |
| // This test ensures that an outer TryCatch in the following situation: |
| // C++/TryCatch -> JS -> C++/TryCatch -> JS w/ SyntaxError |
| // does not clobber the Message object generated for the inner TryCatch. |
| // This exercises the ability of TryCatch.ReThrow() to restore the |
| // inner pending Message before throwing the exception again. |
| TEST(TryCatchMixedNesting) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::Initialize(); |
| v8::TryCatch try_catch(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("TryCatchMixedNestingHelper"), |
| v8::FunctionTemplate::New(isolate, TryCatchMixedNestingHelper)); |
| LocalContext context(0, templ); |
| CompileRunWithOrigin("TryCatchMixedNestingHelper();\n", "outer", 1, 1); |
| TryCatchMixedNestingCheck(&try_catch); |
| } |
| |
| |
| void TryCatchNativeHelper(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| args.GetIsolate()->ThrowException(v8_str("boom")); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| TEST(TryCatchNative) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::Initialize(); |
| v8::TryCatch try_catch(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("TryCatchNativeHelper"), |
| v8::FunctionTemplate::New(isolate, TryCatchNativeHelper)); |
| LocalContext context(0, templ); |
| CompileRun("TryCatchNativeHelper();"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| void TryCatchNativeResetHelper( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::TryCatch try_catch(args.GetIsolate()); |
| args.GetIsolate()->ThrowException(v8_str("boom")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| TEST(TryCatchNativeReset) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::Initialize(); |
| v8::TryCatch try_catch(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("TryCatchNativeResetHelper"), |
| v8::FunctionTemplate::New(isolate, TryCatchNativeResetHelper)); |
| LocalContext context(0, templ); |
| CompileRun("TryCatchNativeResetHelper();"); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(Equality) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(context->GetIsolate()); |
| // Check that equality works at all before relying on CHECK_EQ |
| CHECK(v8_str("a")->Equals(v8_str("a"))); |
| CHECK(!v8_str("a")->Equals(v8_str("b"))); |
| |
| CHECK(v8_str("a")->Equals(v8_str("a"))); |
| CHECK(!v8_str("a")->Equals(v8_str("b"))); |
| CHECK(v8_num(1)->Equals(v8_num(1))); |
| CHECK(v8_num(1.00)->Equals(v8_num(1))); |
| CHECK(!v8_num(1)->Equals(v8_num(2))); |
| |
| // Assume String is not internalized. |
| CHECK(v8_str("a")->StrictEquals(v8_str("a"))); |
| CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); |
| CHECK(!v8_str("5")->StrictEquals(v8_num(5))); |
| CHECK(v8_num(1)->StrictEquals(v8_num(1))); |
| CHECK(!v8_num(1)->StrictEquals(v8_num(2))); |
| CHECK(v8_num(0.0)->StrictEquals(v8_num(-0.0))); |
| Local<Value> not_a_number = v8_num(std::numeric_limits<double>::quiet_NaN()); |
| CHECK(!not_a_number->StrictEquals(not_a_number)); |
| CHECK(v8::False(isolate)->StrictEquals(v8::False(isolate))); |
| CHECK(!v8::False(isolate)->StrictEquals(v8::Undefined(isolate))); |
| |
| v8::Handle<v8::Object> obj = v8::Object::New(isolate); |
| v8::Persistent<v8::Object> alias(isolate, obj); |
| CHECK(v8::Local<v8::Object>::New(isolate, alias)->StrictEquals(obj)); |
| alias.Reset(); |
| |
| CHECK(v8_str("a")->SameValue(v8_str("a"))); |
| CHECK(!v8_str("a")->SameValue(v8_str("b"))); |
| CHECK(!v8_str("5")->SameValue(v8_num(5))); |
| CHECK(v8_num(1)->SameValue(v8_num(1))); |
| CHECK(!v8_num(1)->SameValue(v8_num(2))); |
| CHECK(!v8_num(0.0)->SameValue(v8_num(-0.0))); |
| CHECK(not_a_number->SameValue(not_a_number)); |
| CHECK(v8::False(isolate)->SameValue(v8::False(isolate))); |
| CHECK(!v8::False(isolate)->SameValue(v8::Undefined(isolate))); |
| } |
| |
| |
| THREADED_TEST(MultiRun) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| Local<Script> script = v8_compile("x"); |
| for (int i = 0; i < 10; i++) script->Run(); |
| } |
| |
| |
| static void GetXValue(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK(info.Data()->Equals(v8_str("donut"))); |
| CHECK(name->Equals(v8_str("x"))); |
| info.GetReturnValue().Set(name); |
| } |
| |
| |
| THREADED_TEST(SimplePropertyRead) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); |
| context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| Local<Script> script = v8_compile("obj.x"); |
| for (int i = 0; i < 10; i++) { |
| Local<Value> result = script->Run(); |
| CHECK(result->Equals(v8_str("x"))); |
| } |
| } |
| |
| |
| THREADED_TEST(DefinePropertyOnAPIAccessor) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); |
| context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| |
| // Uses getOwnPropertyDescriptor to check the configurable status |
| Local<Script> script_desc = v8_compile( |
| "var prop = Object.getOwnPropertyDescriptor( " |
| "obj, 'x');" |
| "prop.configurable;"); |
| Local<Value> result = script_desc->Run(); |
| CHECK_EQ(result->BooleanValue(), true); |
| |
| // Redefine get - but still configurable |
| Local<Script> script_define = v8_compile( |
| "var desc = { get: function(){return 42; }," |
| " configurable: true };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(); |
| CHECK(result->Equals(v8_num(42))); |
| |
| // Check that the accessor is still configurable |
| result = script_desc->Run(); |
| CHECK_EQ(result->BooleanValue(), true); |
| |
| // Redefine to a non-configurable |
| script_define = v8_compile( |
| "var desc = { get: function(){return 43; }," |
| " configurable: false };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(); |
| CHECK(result->Equals(v8_num(43))); |
| result = script_desc->Run(); |
| CHECK_EQ(result->BooleanValue(), false); |
| |
| // Make sure that it is not possible to redefine again |
| v8::TryCatch try_catch(isolate); |
| result = script_define->Run(); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ(0, |
| strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| |
| |
| THREADED_TEST(DefinePropertyOnDefineGetterSetter) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); |
| LocalContext context; |
| context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| |
| Local<Script> script_desc = v8_compile( |
| "var prop =" |
| "Object.getOwnPropertyDescriptor( " |
| "obj, 'x');" |
| "prop.configurable;"); |
| Local<Value> result = script_desc->Run(); |
| CHECK_EQ(result->BooleanValue(), true); |
| |
| Local<Script> script_define = v8_compile( |
| "var desc = {get: function(){return 42; }," |
| " configurable: true };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(); |
| CHECK(result->Equals(v8_num(42))); |
| |
| |
| result = script_desc->Run(); |
| CHECK_EQ(result->BooleanValue(), true); |
| |
| |
| script_define = v8_compile( |
| "var desc = {get: function(){return 43; }," |
| " configurable: false };" |
| "Object.defineProperty(obj, 'x', desc);" |
| "obj.x"); |
| result = script_define->Run(); |
| CHECK(result->Equals(v8_num(43))); |
| result = script_desc->Run(); |
| |
| CHECK_EQ(result->BooleanValue(), false); |
| |
| v8::TryCatch try_catch(isolate); |
| result = script_define->Run(); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ(0, |
| strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| |
| |
| static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, |
| char const* name) { |
| return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); |
| } |
| |
| |
| THREADED_TEST(DefineAPIAccessorOnObject) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| LocalContext context; |
| |
| context->Global()->Set(v8_str("obj1"), templ->NewInstance()); |
| CompileRun("var obj2 = {};"); |
| |
| CHECK(CompileRun("obj1.x")->IsUndefined()); |
| CHECK(CompileRun("obj2.x")->IsUndefined()); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| |
| ExpectString("obj1.x", "x"); |
| CHECK(CompileRun("obj2.x")->IsUndefined()); |
| |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| |
| ExpectString("obj1.x", "x"); |
| ExpectString("obj2.x", "x"); |
| |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| CompileRun( |
| "Object.defineProperty(obj1, 'x'," |
| "{ get: function() { return 'y'; }, configurable: true })"); |
| |
| ExpectString("obj1.x", "y"); |
| ExpectString("obj2.x", "x"); |
| |
| CompileRun( |
| "Object.defineProperty(obj2, 'x'," |
| "{ get: function() { return 'y'; }, configurable: true })"); |
| |
| ExpectString("obj1.x", "y"); |
| ExpectString("obj2.x", "y"); |
| |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| |
| ExpectString("obj1.x", "x"); |
| ExpectString("obj2.x", "x"); |
| |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| // Define getters/setters, but now make them not configurable. |
| CompileRun( |
| "Object.defineProperty(obj1, 'x'," |
| "{ get: function() { return 'z'; }, configurable: false })"); |
| CompileRun( |
| "Object.defineProperty(obj2, 'x'," |
| "{ get: function() { return 'z'; }, configurable: false })"); |
| |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| ExpectString("obj1.x", "z"); |
| ExpectString("obj2.x", "z"); |
| |
| CHECK(!GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| CHECK(!GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| |
| ExpectString("obj1.x", "z"); |
| ExpectString("obj2.x", "z"); |
| } |
| |
| |
| THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| LocalContext context; |
| |
| context->Global()->Set(v8_str("obj1"), templ->NewInstance()); |
| CompileRun("var obj2 = {};"); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"), |
| v8::DEFAULT, v8::DontDelete)); |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"), |
| v8::DEFAULT, v8::DontDelete)); |
| |
| ExpectString("obj1.x", "x"); |
| ExpectString("obj2.x", "x"); |
| |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); |
| ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); |
| |
| CHECK(!GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| CHECK(!GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "Object.defineProperty(obj1, 'x'," |
| "{get: function() { return 'func'; }})"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ( |
| 0, strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "Object.defineProperty(obj2, 'x'," |
| "{get: function() { return 'func'; }})"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ( |
| 0, strcmp(*exception_value, "TypeError: Cannot redefine property: x")); |
| } |
| } |
| |
| |
| static void Get239Value(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CHECK(info.Data()->Equals(v8_str("donut"))); |
| CHECK(name->Equals(v8_str("239"))); |
| info.GetReturnValue().Set(name); |
| } |
| |
| |
| THREADED_TEST(ElementAPIAccessor) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| LocalContext context; |
| |
| context->Global()->Set(v8_str("obj1"), templ->NewInstance()); |
| CompileRun("var obj2 = {};"); |
| |
| CHECK(GetGlobalProperty(&context, "obj1") |
| ->SetAccessor(v8_str("239"), Get239Value, NULL, v8_str("donut"))); |
| CHECK(GetGlobalProperty(&context, "obj2") |
| ->SetAccessor(v8_str("239"), Get239Value, NULL, v8_str("donut"))); |
| |
| ExpectString("obj1[239]", "239"); |
| ExpectString("obj2[239]", "239"); |
| ExpectString("obj1['239']", "239"); |
| ExpectString("obj2['239']", "239"); |
| } |
| |
| |
| v8::Persistent<Value> xValue; |
| |
| |
| static void SetXValue(Local<String> name, Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| CHECK(value->Equals(v8_num(4))); |
| CHECK(info.Data()->Equals(v8_str("donut"))); |
| CHECK(name->Equals(v8_str("x"))); |
| CHECK(xValue.IsEmpty()); |
| xValue.Reset(info.GetIsolate(), value); |
| } |
| |
| |
| THREADED_TEST(SimplePropertyWrite) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); |
| LocalContext context; |
| context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| Local<Script> script = v8_compile("obj.x = 4"); |
| for (int i = 0; i < 10; i++) { |
| CHECK(xValue.IsEmpty()); |
| script->Run(); |
| CHECK(v8_num(4)->Equals(Local<Value>::New(CcTest::isolate(), xValue))); |
| xValue.Reset(); |
| } |
| } |
| |
| |
| THREADED_TEST(SetterOnly) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), NULL, SetXValue, v8_str("donut")); |
| LocalContext context; |
| context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| Local<Script> script = v8_compile("obj.x = 4; obj.x"); |
| for (int i = 0; i < 10; i++) { |
| CHECK(xValue.IsEmpty()); |
| script->Run(); |
| CHECK(v8_num(4)->Equals(Local<Value>::New(CcTest::isolate(), xValue))); |
| xValue.Reset(); |
| } |
| } |
| |
| |
| THREADED_TEST(NoAccessors) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("x"), static_cast<v8::AccessorGetterCallback>(NULL), |
| NULL, v8_str("donut")); |
| LocalContext context; |
| context->Global()->Set(v8_str("obj"), templ->NewInstance()); |
| Local<Script> script = v8_compile("obj.x = 4; obj.x"); |
| for (int i = 0; i < 10; i++) { |
| script->Run(); |
| } |
| } |
| |
| |
| THREADED_TEST(MultiContexts) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("dummy"), |
| v8::FunctionTemplate::New(isolate, DummyCallHandler)); |
| |
| Local<String> password = v8_str("Password"); |
| |
| // Create an environment |
| LocalContext context0(0, templ); |
| context0->SetSecurityToken(password); |
| v8::Handle<v8::Object> global0 = context0->Global(); |
| global0->Set(v8_str("custom"), v8_num(1234)); |
| CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); |
| |
| // Create an independent environment |
| LocalContext context1(0, templ); |
| context1->SetSecurityToken(password); |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("custom"), v8_num(1234)); |
| CHECK(!global0->Equals(global1)); |
| CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); |
| CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); |
| |
| // Now create a new context with the old global |
| LocalContext context2(0, templ, global1); |
| context2->SetSecurityToken(password); |
| v8::Handle<v8::Object> global2 = context2->Global(); |
| CHECK(global1->Equals(global2)); |
| CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); |
| CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(FunctionPrototypeAcrossContexts) { |
| // Make sure that functions created by cloning boilerplates cannot |
| // communicate through their __proto__ field. |
| |
| v8::HandleScope scope(CcTest::isolate()); |
| |
| LocalContext env0; |
| v8::Handle<v8::Object> global0 = env0->Global(); |
| v8::Handle<v8::Object> object0 = |
| global0->Get(v8_str("Object")).As<v8::Object>(); |
| v8::Handle<v8::Object> tostring0 = |
| object0->Get(v8_str("toString")).As<v8::Object>(); |
| v8::Handle<v8::Object> proto0 = |
| tostring0->Get(v8_str("__proto__")).As<v8::Object>(); |
| proto0->Set(v8_str("custom"), v8_num(1234)); |
| |
| LocalContext env1; |
| v8::Handle<v8::Object> global1 = env1->Global(); |
| v8::Handle<v8::Object> object1 = |
| global1->Get(v8_str("Object")).As<v8::Object>(); |
| v8::Handle<v8::Object> tostring1 = |
| object1->Get(v8_str("toString")).As<v8::Object>(); |
| v8::Handle<v8::Object> proto1 = |
| tostring1->Get(v8_str("__proto__")).As<v8::Object>(); |
| CHECK(!proto1->Has(v8_str("custom"))); |
| } |
| |
| |
| THREADED_TEST(Regress892105) { |
| // Make sure that object and array literals created by cloning |
| // boilerplates cannot communicate through their __proto__ |
| // field. This is rather difficult to check, but we try to add stuff |
| // to Object.prototype and Array.prototype and create a new |
| // environment. This should succeed. |
| |
| v8::HandleScope scope(CcTest::isolate()); |
| |
| Local<String> source = v8_str( |
| "Object.prototype.obj = 1234;" |
| "Array.prototype.arr = 4567;" |
| "8901"); |
| |
| LocalContext env0; |
| Local<Script> script0 = v8_compile(source); |
| CHECK_EQ(8901.0, script0->Run()->NumberValue()); |
| |
| LocalContext env1; |
| Local<Script> script1 = v8_compile(source); |
| CHECK_EQ(8901.0, script1->Run()->NumberValue()); |
| } |
| |
| |
| THREADED_TEST(UndetectableObject) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> desc = |
| v8::FunctionTemplate::New(env->GetIsolate()); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| |
| Local<v8::Object> obj = desc->GetFunction()->NewInstance(); |
| env->Global()->Set(v8_str("undetectable"), obj); |
| |
| ExpectString("undetectable.toString()", "[object Object]"); |
| ExpectString("typeof undetectable", "undefined"); |
| ExpectString("typeof(undetectable)", "undefined"); |
| ExpectBoolean("typeof undetectable == 'undefined'", true); |
| ExpectBoolean("typeof undetectable == 'object'", false); |
| ExpectBoolean("if (undetectable) { true; } else { false; }", false); |
| ExpectBoolean("!undetectable", true); |
| |
| ExpectObject("true&&undetectable", obj); |
| ExpectBoolean("false&&undetectable", false); |
| ExpectBoolean("true||undetectable", true); |
| ExpectObject("false||undetectable", obj); |
| |
| ExpectObject("undetectable&&true", obj); |
| ExpectObject("undetectable&&false", obj); |
| ExpectBoolean("undetectable||true", true); |
| ExpectBoolean("undetectable||false", false); |
| |
| ExpectBoolean("undetectable==null", true); |
| ExpectBoolean("null==undetectable", true); |
| ExpectBoolean("undetectable==undefined", true); |
| ExpectBoolean("undefined==undetectable", true); |
| ExpectBoolean("undetectable==undetectable", true); |
| |
| |
| ExpectBoolean("undetectable===null", false); |
| ExpectBoolean("null===undetectable", false); |
| ExpectBoolean("undetectable===undefined", false); |
| ExpectBoolean("undefined===undetectable", false); |
| ExpectBoolean("undetectable===undetectable", true); |
| } |
| |
| |
| THREADED_TEST(VoidLiteral) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| |
| Local<v8::Object> obj = desc->GetFunction()->NewInstance(); |
| env->Global()->Set(v8_str("undetectable"), obj); |
| |
| ExpectBoolean("undefined == void 0", true); |
| ExpectBoolean("undetectable == void 0", true); |
| ExpectBoolean("null == void 0", true); |
| ExpectBoolean("undefined === void 0", true); |
| ExpectBoolean("undetectable === void 0", false); |
| ExpectBoolean("null === void 0", false); |
| |
| ExpectBoolean("void 0 == undefined", true); |
| ExpectBoolean("void 0 == undetectable", true); |
| ExpectBoolean("void 0 == null", true); |
| ExpectBoolean("void 0 === undefined", true); |
| ExpectBoolean("void 0 === undetectable", false); |
| ExpectBoolean("void 0 === null", false); |
| |
| ExpectString( |
| "(function() {" |
| " try {" |
| " return x === void 0;" |
| " } catch(e) {" |
| " return e.toString();" |
| " }" |
| "})()", |
| "ReferenceError: x is not defined"); |
| ExpectString( |
| "(function() {" |
| " try {" |
| " return void 0 === x;" |
| " } catch(e) {" |
| " return e.toString();" |
| " }" |
| "})()", |
| "ReferenceError: x is not defined"); |
| } |
| |
| |
| THREADED_TEST(ExtensibleOnUndetectable) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| |
| Local<v8::Object> obj = desc->GetFunction()->NewInstance(); |
| env->Global()->Set(v8_str("undetectable"), obj); |
| |
| Local<String> source = v8_str( |
| "undetectable.x = 42;" |
| "undetectable.x"); |
| |
| Local<Script> script = v8_compile(source); |
| |
| CHECK(v8::Integer::New(isolate, 42)->Equals(script->Run())); |
| |
| ExpectBoolean("Object.isExtensible(undetectable)", true); |
| |
| source = v8_str("Object.preventExtensions(undetectable);"); |
| script = v8_compile(source); |
| script->Run(); |
| ExpectBoolean("Object.isExtensible(undetectable)", false); |
| |
| source = v8_str("undetectable.y = 2000;"); |
| script = v8_compile(source); |
| script->Run(); |
| ExpectBoolean("undetectable.y == undefined", true); |
| } |
| |
| |
| // The point of this test is type checking. We run it only so compilers |
| // don't complain about an unused function. |
| TEST(PersistentHandles) { |
| LocalContext env; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<String> str = v8_str("foo"); |
| v8::Persistent<String> p_str(isolate, str); |
| p_str.Reset(); |
| Local<Script> scr = v8_compile(""); |
| v8::Persistent<Script> p_scr(isolate, scr); |
| p_scr.Reset(); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| v8::Persistent<ObjectTemplate> p_templ(isolate, templ); |
| p_templ.Reset(); |
| } |
| |
| |
| static void HandleLogDelegator( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectTemplate) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); |
| global_template->Set(v8_str("JSNI_Log"), |
| v8::FunctionTemplate::New(isolate, HandleLogDelegator)); |
| v8::Local<Context> context = Context::New(isolate, 0, global_template); |
| Context::Scope context_scope(context); |
| CompileRun("JSNI_Log('LOG')"); |
| } |
| |
| |
| static const char* kSimpleExtensionSource = |
| "function Foo() {" |
| " return 4;" |
| "}"; |
| |
| |
| TEST(SimpleExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); |
| const char* extension_names[] = {"simpletest"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("Foo()"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 4))); |
| } |
| |
| |
| static const char* kStackTraceFromExtensionSource = |
| "function foo() {" |
| " throw new Error();" |
| "}" |
| "function bar() {" |
| " foo();" |
| "}"; |
| |
| |
| TEST(StackTraceInExtension) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("stacktracetest", kStackTraceFromExtensionSource)); |
| const char* extension_names[] = {"stacktracetest"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| CompileRun( |
| "function user() { bar(); }" |
| "var error;" |
| "try{ user(); } catch (e) { error = e; }"); |
| CHECK_EQ(-1, CompileRun("error.stack.indexOf('foo')")->Int32Value()); |
| CHECK_EQ(-1, CompileRun("error.stack.indexOf('bar')")->Int32Value()); |
| CHECK_NE(-1, CompileRun("error.stack.indexOf('user')")->Int32Value()); |
| } |
| |
| |
| TEST(NullExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("nulltest", NULL)); |
| const char* extension_names[] = {"nulltest"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("1+3"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 4))); |
| } |
| |
| |
| static const char* kEmbeddedExtensionSource = |
| "function Ret54321(){return 54321;}~~@@$" |
| "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; |
| static const int kEmbeddedExtensionSourceValidLen = 34; |
| |
| |
| TEST(ExtensionMissingSourceLength) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("srclentest_fail", kEmbeddedExtensionSource)); |
| const char* extension_names[] = {"srclentest_fail"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(0 == *context); |
| } |
| |
| |
| TEST(ExtensionWithSourceLength) { |
| for (int source_len = kEmbeddedExtensionSourceValidLen - 1; |
| source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| i::ScopedVector<char> extension_name(32); |
| i::SNPrintF(extension_name, "ext #%d", source_len); |
| v8::RegisterExtension(new Extension( |
| extension_name.start(), kEmbeddedExtensionSource, 0, 0, source_len)); |
| const char* extension_names[1] = {extension_name.start()}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| if (source_len == kEmbeddedExtensionSourceValidLen) { |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("Ret54321()"); |
| CHECK(v8::Integer::New(CcTest::isolate(), 54321)->Equals(result)); |
| } else { |
| // Anything but exactly the right length should fail to compile. |
| CHECK(0 == *context); |
| } |
| } |
| } |
| |
| |
| static const char* kEvalExtensionSource1 = |
| "function UseEval1() {" |
| " var x = 42;" |
| " return eval('x');" |
| "}"; |
| |
| |
| static const char* kEvalExtensionSource2 = |
| "(function() {" |
| " var x = 42;" |
| " function e() {" |
| " return eval('x');" |
| " }" |
| " this.UseEval2 = e;" |
| "})()"; |
| |
| |
| TEST(UseEvalFromExtension) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); |
| v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); |
| const char* extension_names[] = {"evaltest1", "evaltest2"}; |
| v8::ExtensionConfiguration extensions(2, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("UseEval1()"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 42))); |
| result = CompileRun("UseEval2()"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 42))); |
| } |
| |
| |
| static const char* kWithExtensionSource1 = |
| "function UseWith1() {" |
| " var x = 42;" |
| " with({x:87}) { return x; }" |
| "}"; |
| |
| |
| static const char* kWithExtensionSource2 = |
| "(function() {" |
| " var x = 42;" |
| " function e() {" |
| " with ({x:87}) { return x; }" |
| " }" |
| " this.UseWith2 = e;" |
| "})()"; |
| |
| |
| TEST(UseWithFromExtension) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); |
| v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); |
| const char* extension_names[] = {"withtest1", "withtest2"}; |
| v8::ExtensionConfiguration extensions(2, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("UseWith1()"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 87))); |
| result = CompileRun("UseWith2()"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 87))); |
| } |
| |
| |
| TEST(AutoExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| Extension* extension = new Extension("autotest", kSimpleExtensionSource); |
| extension->set_auto_enable(true); |
| v8::RegisterExtension(extension); |
| v8::Handle<Context> context = Context::New(CcTest::isolate()); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("Foo()"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 4))); |
| } |
| |
| |
| static const char* kSyntaxErrorInExtensionSource = "["; |
| |
| |
| // Test that a syntax error in an extension does not cause a fatal |
| // error but results in an empty context. |
| TEST(SyntaxErrorExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("syntaxerror", kSyntaxErrorInExtensionSource)); |
| const char* extension_names[] = {"syntaxerror"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| static const char* kExceptionInExtensionSource = "throw 42"; |
| |
| |
| // Test that an exception when installing an extension does not cause |
| // a fatal error but results in an empty context. |
| TEST(ExceptionExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("exception", kExceptionInExtensionSource)); |
| const char* extension_names[] = {"exception"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| static const char* kNativeCallInExtensionSource = |
| "function call_runtime_last_index_of(x) {" |
| " return %StringLastIndexOf(x, 'bob', 10);" |
| "}"; |
| |
| |
| static const char* kNativeCallTest = |
| "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; |
| |
| // Test that a native runtime calls are supported in extensions. |
| TEST(NativeCallInExtensions) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::RegisterExtension( |
| new Extension("nativecall", kNativeCallInExtensionSource)); |
| const char* extension_names[] = {"nativecall"}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun(kNativeCallTest); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 3))); |
| } |
| |
| |
| class NativeFunctionExtension : public Extension { |
| public: |
| NativeFunctionExtension(const char* name, const char* source, |
| v8::FunctionCallback fun = &Echo) |
| : Extension(name, source), function_(fun) {} |
| |
| virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| v8::Isolate* isolate, v8::Handle<v8::String> name) { |
| return v8::FunctionTemplate::New(isolate, function_); |
| } |
| |
| static void Echo(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| if (args.Length() >= 1) args.GetReturnValue().Set(args[0]); |
| } |
| |
| private: |
| v8::FunctionCallback function_; |
| }; |
| |
| |
| TEST(NativeFunctionDeclaration) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| const char* name = "nativedecl"; |
| v8::RegisterExtension( |
| new NativeFunctionExtension(name, "native function foo();")); |
| const char* extension_names[] = {name}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| Context::Scope lock(context); |
| v8::Handle<Value> result = CompileRun("foo(42);"); |
| CHECK(result->Equals(v8::Integer::New(CcTest::isolate(), 42))); |
| } |
| |
| |
| TEST(NativeFunctionDeclarationError) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| const char* name = "nativedeclerr"; |
| // Syntax error in extension code. |
| v8::RegisterExtension( |
| new NativeFunctionExtension(name, "native\nfunction foo();")); |
| const char* extension_names[] = {name}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| TEST(NativeFunctionDeclarationErrorEscape) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| const char* name = "nativedeclerresc"; |
| // Syntax error in extension code - escape code in "native" means that |
| // it's not treated as a keyword. |
| v8::RegisterExtension( |
| new NativeFunctionExtension(name, "nativ\\u0065 function foo();")); |
| const char* extension_names[] = {name}; |
| v8::ExtensionConfiguration extensions(1, extension_names); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &extensions); |
| CHECK(context.IsEmpty()); |
| } |
| |
| |
| static void CheckDependencies(const char* name, const char* expected) { |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| v8::ExtensionConfiguration config(1, &name); |
| LocalContext context(&config); |
| CHECK(String::NewFromUtf8(CcTest::isolate(), expected) |
| ->Equals(context->Global()->Get(v8_str("loaded")))); |
| } |
| |
| |
| /* |
| * Configuration: |
| * |
| * /-- B <--\ |
| * A <- -- D <-- E |
| * \-- C <--/ |
| */ |
| THREADED_TEST(ExtensionDependency) { |
| static const char* kEDeps[] = {"D"}; |
| v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); |
| static const char* kDDeps[] = {"B", "C"}; |
| v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); |
| static const char* kBCDeps[] = {"A"}; |
| v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); |
| v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); |
| v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); |
| CheckDependencies("A", "undefinedA"); |
| CheckDependencies("B", "undefinedAB"); |
| CheckDependencies("C", "undefinedAC"); |
| CheckDependencies("D", "undefinedABCD"); |
| CheckDependencies("E", "undefinedABCDE"); |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| static const char* exts[2] = {"C", "E"}; |
| v8::ExtensionConfiguration config(2, exts); |
| LocalContext context(&config); |
| CHECK(v8_str("undefinedACBDE") |
| ->Equals(context->Global()->Get(v8_str("loaded")))); |
| } |
| |
| |
| static const char* kExtensionTestScript = |
| "native function A();" |
| "native function B();" |
| "native function C();" |
| "function Foo(i) {" |
| " if (i == 0) return A();" |
| " if (i == 1) return B();" |
| " if (i == 2) return C();" |
| "}"; |
| |
| |
| static void CallFun(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| if (args.IsConstructCall()) { |
| args.This()->Set(v8_str("data"), args.Data()); |
| args.GetReturnValue().SetNull(); |
| return; |
| } |
| args.GetReturnValue().Set(args.Data()); |
| } |
| |
| |
| class FunctionExtension : public Extension { |
| public: |
| FunctionExtension() : Extension("functiontest", kExtensionTestScript) {} |
| virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| v8::Isolate* isolate, v8::Handle<String> name); |
| }; |
| |
| |
| static int lookup_count = 0; |
| v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunctionTemplate( |
| v8::Isolate* isolate, v8::Handle<String> name) { |
| lookup_count++; |
| if (name->Equals(v8_str("A"))) { |
| return v8::FunctionTemplate::New(isolate, CallFun, |
| v8::Integer::New(isolate, 8)); |
| } else if (name->Equals(v8_str("B"))) { |
| return v8::FunctionTemplate::New(isolate, CallFun, |
| v8::Integer::New(isolate, 7)); |
| } else if (name->Equals(v8_str("C"))) { |
| return v8::FunctionTemplate::New(isolate, CallFun, |
| v8::Integer::New(isolate, 6)); |
| } else { |
| return v8::Handle<v8::FunctionTemplate>(); |
| } |
| } |
| |
| |
| THREADED_TEST(FunctionLookup) { |
| v8::RegisterExtension(new FunctionExtension()); |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| static const char* exts[1] = {"functiontest"}; |
| v8::ExtensionConfiguration config(1, exts); |
| LocalContext context(&config); |
| CHECK_EQ(3, lookup_count); |
| CHECK(v8::Integer::New(CcTest::isolate(), 8)->Equals(CompileRun("Foo(0)"))); |
| CHECK(v8::Integer::New(CcTest::isolate(), 7)->Equals(CompileRun("Foo(1)"))); |
| CHECK(v8::Integer::New(CcTest::isolate(), 6)->Equals(CompileRun("Foo(2)"))); |
| } |
| |
| |
| THREADED_TEST(NativeFunctionConstructCall) { |
| v8::RegisterExtension(new FunctionExtension()); |
| v8::HandleScope handle_scope(CcTest::isolate()); |
| static const char* exts[1] = {"functiontest"}; |
| v8::ExtensionConfiguration config(1, exts); |
| LocalContext context(&config); |
| for (int i = 0; i < 10; i++) { |
| // Run a few times to ensure that allocation of objects doesn't |
| // change behavior of a constructor function. |
| CHECK(v8::Integer::New(CcTest::isolate(), 8) |
| ->Equals(CompileRun("(new A()).data"))); |
| CHECK(v8::Integer::New(CcTest::isolate(), 7) |
| ->Equals(CompileRun("(new B()).data"))); |
| CHECK(v8::Integer::New(CcTest::isolate(), 6) |
| ->Equals(CompileRun("(new C()).data"))); |
| } |
| } |
| |
| |
| static const char* last_location; |
| static const char* last_message; |
| void StoringErrorCallback(const char* location, const char* message) { |
| if (last_location == NULL) { |
| last_location = location; |
| last_message = message; |
| } |
| } |
| |
| |
| // ErrorReporting creates a circular extensions configuration and |
| // tests that the fatal error handler gets called. This renders V8 |
| // unusable and therefore this test cannot be run in parallel. |
| TEST(ErrorReporting) { |
| v8::V8::SetFatalErrorHandler(StoringErrorCallback); |
| static const char* aDeps[] = {"B"}; |
| v8::RegisterExtension(new Extension("A", "", 1, aDeps)); |
| static const char* bDeps[] = {"A"}; |
| v8::RegisterExtension(new Extension("B", "", 1, bDeps)); |
| last_location = NULL; |
| v8::ExtensionConfiguration config(1, bDeps); |
| v8::Handle<Context> context = Context::New(CcTest::isolate(), &config); |
| CHECK(context.IsEmpty()); |
| CHECK(last_location); |
| } |
| |
| |
| static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, |
| v8::Handle<Value> data) { |
| CHECK(message->GetScriptOrigin().ResourceName()->IsUndefined()); |
| CHECK(v8::Undefined(CcTest::isolate()) |
| ->Equals(message->GetScriptOrigin().ResourceName())); |
| message->GetLineNumber(); |
| message->GetSourceLine(); |
| } |
| |
| |
| THREADED_TEST(ErrorWithMissingScriptInfo) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::V8::AddMessageListener(MissingScriptInfoMessageListener); |
| CompileRun("throw Error()"); |
| v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); |
| } |
| |
| |
| struct FlagAndPersistent { |
| bool flag; |
| v8::Global<v8::Object> handle; |
| }; |
| |
| |
| static void SetFlag(const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| data.GetParameter()->flag = true; |
| data.GetParameter()->handle.Reset(); |
| } |
| |
| |
| static void IndependentWeakHandle(bool global_gc, bool interlinked) { |
| v8::Isolate* iso = CcTest::isolate(); |
| v8::HandleScope scope(iso); |
| v8::Handle<Context> context = Context::New(iso); |
| Context::Scope context_scope(context); |
| |
| FlagAndPersistent object_a, object_b; |
| |
| intptr_t big_heap_size; |
| |
| { |
| v8::HandleScope handle_scope(iso); |
| Local<Object> a(v8::Object::New(iso)); |
| Local<Object> b(v8::Object::New(iso)); |
| object_a.handle.Reset(iso, a); |
| object_b.handle.Reset(iso, b); |
| if (interlinked) { |
| a->Set(v8_str("x"), b); |
| b->Set(v8_str("x"), a); |
| } |
| if (global_gc) { |
| CcTest::heap()->CollectAllGarbage(); |
| } else { |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| } |
| // We are relying on this creating a big flag array and reserving the space |
| // up front. |
| v8::Handle<Value> big_array = CompileRun("new Array(50000)"); |
| a->Set(v8_str("y"), big_array); |
| big_heap_size = CcTest::heap()->SizeOfObjects(); |
| } |
| |
| object_a.flag = false; |
| object_b.flag = false; |
| object_a.handle.SetWeak(&object_a, &SetFlag, |
| v8::WeakCallbackType::kParameter); |
| object_b.handle.SetWeak(&object_b, &SetFlag, |
| v8::WeakCallbackType::kParameter); |
| CHECK(!object_b.handle.IsIndependent()); |
| object_a.handle.MarkIndependent(); |
| object_b.handle.MarkIndependent(); |
| CHECK(object_b.handle.IsIndependent()); |
| if (global_gc) { |
| CcTest::heap()->CollectAllGarbage(); |
| } else { |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| } |
| // A single GC should be enough to reclaim the memory, since we are using |
| // phantom handles. |
| CHECK_LT(CcTest::heap()->SizeOfObjects(), big_heap_size - 200000); |
| CHECK(object_a.flag); |
| CHECK(object_b.flag); |
| } |
| |
| |
| TEST(IndependentWeakHandle) { |
| IndependentWeakHandle(false, false); |
| IndependentWeakHandle(false, true); |
| IndependentWeakHandle(true, false); |
| IndependentWeakHandle(true, true); |
| } |
| |
| |
| class Trivial { |
| public: |
| explicit Trivial(int x) : x_(x) {} |
| |
| int x() { return x_; } |
| void set_x(int x) { x_ = x; } |
| |
| private: |
| int x_; |
| }; |
| |
| |
| class Trivial2 { |
| public: |
| Trivial2(int x, int y) : y_(y), x_(x) {} |
| |
| int x() { return x_; } |
| void set_x(int x) { x_ = x; } |
| |
| int y() { return y_; } |
| void set_y(int y) { y_ = y; } |
| |
| private: |
| int y_; |
| int x_; |
| }; |
| |
| |
| void CheckInternalFields( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| v8::Persistent<v8::Object>* handle = data.GetParameter(); |
| handle->Reset(); |
| Trivial* t1 = reinterpret_cast<Trivial*>(data.GetInternalField1()); |
| Trivial2* t2 = reinterpret_cast<Trivial2*>(data.GetInternalField2()); |
| CHECK_EQ(42, t1->x()); |
| CHECK_EQ(103, t2->x()); |
| t1->set_x(1729); |
| t2->set_x(33550336); |
| } |
| |
| |
| void InternalFieldCallback(bool global_gc) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| Trivial* t1; |
| Trivial2* t2; |
| instance_templ->SetInternalFieldCount(2); |
| { |
| v8::HandleScope scope(isolate); |
| Local<v8::Object> obj = templ->GetFunction()->NewInstance(); |
| v8::Persistent<v8::Object> handle(isolate, obj); |
| CHECK_EQ(2, obj->InternalFieldCount()); |
| CHECK(obj->GetInternalField(0)->IsUndefined()); |
| t1 = new Trivial(42); |
| t2 = new Trivial2(103, 9); |
| |
| obj->SetAlignedPointerInInternalField(0, t1); |
| t1 = reinterpret_cast<Trivial*>(obj->GetAlignedPointerFromInternalField(0)); |
| CHECK_EQ(42, t1->x()); |
| |
| obj->SetAlignedPointerInInternalField(1, t2); |
| t2 = |
| reinterpret_cast<Trivial2*>(obj->GetAlignedPointerFromInternalField(1)); |
| CHECK_EQ(103, t2->x()); |
| |
| handle.SetWeak<v8::Persistent<v8::Object>>( |
| &handle, CheckInternalFields, v8::WeakCallbackType::kInternalFields); |
| if (!global_gc) { |
| handle.MarkIndependent(); |
| } |
| } |
| if (global_gc) { |
| CcTest::heap()->CollectAllGarbage(); |
| } else { |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| } |
| |
| CHECK_EQ(1729, t1->x()); |
| CHECK_EQ(33550336, t2->x()); |
| |
| delete t1; |
| delete t2; |
| } |
| |
| |
| THREADED_TEST(InternalFieldCallback) { |
| InternalFieldCallback(false); |
| InternalFieldCallback(true); |
| } |
| |
| |
| static void ResetUseValueAndSetFlag( |
| const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| // Blink will reset the handle, and then use the other handle, so they |
| // can't use the same backing slot. |
| data.GetParameter()->handle.Reset(); |
| data.GetParameter()->flag = true; |
| } |
| |
| |
| static void ResetWeakHandle(bool global_gc) { |
| v8::Isolate* iso = CcTest::isolate(); |
| v8::HandleScope scope(iso); |
| v8::Handle<Context> context = Context::New(iso); |
| Context::Scope context_scope(context); |
| |
| FlagAndPersistent object_a, object_b; |
| |
| { |
| v8::HandleScope handle_scope(iso); |
| Local<Object> a(v8::Object::New(iso)); |
| Local<Object> b(v8::Object::New(iso)); |
| object_a.handle.Reset(iso, a); |
| object_b.handle.Reset(iso, b); |
| if (global_gc) { |
| CcTest::heap()->CollectAllGarbage( |
| TestHeap::Heap::kAbortIncrementalMarkingMask); |
| } else { |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| } |
| } |
| |
| object_a.flag = false; |
| object_b.flag = false; |
| object_a.handle.SetWeak(&object_a, &ResetUseValueAndSetFlag, |
| v8::WeakCallbackType::kParameter); |
| object_b.handle.SetWeak(&object_b, &ResetUseValueAndSetFlag, |
| v8::WeakCallbackType::kParameter); |
| if (!global_gc) { |
| object_a.handle.MarkIndependent(); |
| object_b.handle.MarkIndependent(); |
| CHECK(object_b.handle.IsIndependent()); |
| } |
| if (global_gc) { |
| CcTest::heap()->CollectAllGarbage( |
| TestHeap::Heap::kAbortIncrementalMarkingMask); |
| } else { |
| CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
| } |
| CHECK(object_a.flag); |
| CHECK(object_b.flag); |
| } |
| |
| |
| THREADED_TEST(ResetWeakHandle) { |
| ResetWeakHandle(false); |
| ResetWeakHandle(true); |
| } |
| |
| |
| static void InvokeScavenge() { CcTest::heap()->CollectGarbage(i::NEW_SPACE); } |
| |
| |
| static void InvokeMarkSweep() { CcTest::heap()->CollectAllGarbage(); } |
| |
| |
| static void ForceScavenge2( |
| const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| data.GetParameter()->flag = true; |
| InvokeScavenge(); |
| } |
| |
| static void ForceScavenge1( |
| const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| data.GetParameter()->handle.Reset(); |
| data.SetSecondPassCallback(ForceScavenge2); |
| } |
| |
| |
| static void ForceMarkSweep2( |
| const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| data.GetParameter()->flag = true; |
| InvokeMarkSweep(); |
| } |
| |
| static void ForceMarkSweep1( |
| const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| data.GetParameter()->handle.Reset(); |
| data.SetSecondPassCallback(ForceMarkSweep2); |
| } |
| |
| |
| THREADED_TEST(GCFromWeakCallbacks) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<Context> context = Context::New(isolate); |
| Context::Scope context_scope(context); |
| |
| static const int kNumberOfGCTypes = 2; |
| typedef v8::WeakCallbackInfo<FlagAndPersistent>::Callback Callback; |
| Callback gc_forcing_callback[kNumberOfGCTypes] = {&ForceScavenge1, |
| &ForceMarkSweep1}; |
| |
| typedef void (*GCInvoker)(); |
| GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; |
| |
| for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { |
| for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { |
| FlagAndPersistent object; |
| { |
| v8::HandleScope handle_scope(isolate); |
| object.handle.Reset(isolate, v8::Object::New(isolate)); |
| } |
| object.flag = false; |
| object.handle.SetWeak(&object, gc_forcing_callback[inner_gc], |
| v8::WeakCallbackType::kParameter); |
| object.handle.MarkIndependent(); |
| invoke_gc[outer_gc](); |
| CHECK(object.flag); |
| } |
| } |
| } |
| |
| |
| v8::Handle<Function> args_fun; |
| |
| |
| static void ArgumentsTestCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Isolate* isolate = args.GetIsolate(); |
| CHECK(args_fun->Equals(args.Callee())); |
| CHECK_EQ(3, args.Length()); |
| CHECK(v8::Integer::New(isolate, 1)->Equals(args[0])); |
| CHECK(v8::Integer::New(isolate, 2)->Equals(args[1])); |
| CHECK(v8::Integer::New(isolate, 3)->Equals(args[2])); |
| CHECK(v8::Undefined(isolate)->Equals(args[3])); |
| v8::HandleScope scope(args.GetIsolate()); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| |
| THREADED_TEST(Arguments) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); |
| global->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, ArgumentsTestCallback)); |
| LocalContext context(NULL, global); |
| args_fun = context->Global()->Get(v8_str("f")).As<Function>(); |
| v8_compile("f(1, 2, 3)")->Run(); |
| } |
| |
| |
| static int p_getter_count; |
| static int p_getter_count2; |
| |
| |
| static void PGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| p_getter_count++; |
| v8::Handle<v8::Object> global = |
| info.GetIsolate()->GetCurrentContext()->Global(); |
| CHECK(info.Holder()->Equals(global->Get(v8_str("o1")))); |
| if (name->Equals(v8_str("p1"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o1")))); |
| } else if (name->Equals(v8_str("p2"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o2")))); |
| } else if (name->Equals(v8_str("p3"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o3")))); |
| } else if (name->Equals(v8_str("p4"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o4")))); |
| } |
| } |
| |
| |
| static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { |
| ApiTestFuzzer::Fuzz(); |
| LocalContext context; |
| context->Global()->Set(v8_str("o1"), obj->NewInstance()); |
| CompileRun( |
| "o1.__proto__ = { };" |
| "var o2 = { __proto__: o1 };" |
| "var o3 = { __proto__: o2 };" |
| "var o4 = { __proto__: o3 };" |
| "for (var i = 0; i < 10; i++) o4.p4;" |
| "for (var i = 0; i < 10; i++) o3.p3;" |
| "for (var i = 0; i < 10; i++) o2.p2;" |
| "for (var i = 0; i < 10; i++) o1.p1;"); |
| } |
| |
| |
| static void PGetter2(Local<Name> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| p_getter_count2++; |
| v8::Handle<v8::Object> global = |
| info.GetIsolate()->GetCurrentContext()->Global(); |
| CHECK(info.Holder()->Equals(global->Get(v8_str("o1")))); |
| if (name->Equals(v8_str("p1"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o1")))); |
| } else if (name->Equals(v8_str("p2"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o2")))); |
| } else if (name->Equals(v8_str("p3"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o3")))); |
| } else if (name->Equals(v8_str("p4"))) { |
| CHECK(info.This()->Equals(global->Get(v8_str("o4")))); |
| } |
| } |
| |
| |
| THREADED_TEST(GetterHolders) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("p1"), PGetter); |
| obj->SetAccessor(v8_str("p2"), PGetter); |
| obj->SetAccessor(v8_str("p3"), PGetter); |
| obj->SetAccessor(v8_str("p4"), PGetter); |
| p_getter_count = 0; |
| RunHolderTest(obj); |
| CHECK_EQ(40, p_getter_count); |
| } |
| |
| |
| THREADED_TEST(PreInterceptorHolders) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetHandler(v8::NamedPropertyHandlerConfiguration(PGetter2)); |
| p_getter_count2 = 0; |
| RunHolderTest(obj); |
| CHECK_EQ(40, p_getter_count2); |
| } |
| |
| |
| THREADED_TEST(ObjectInstantiation) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessor(v8_str("t"), PGetter2); |
| LocalContext context; |
| context->Global()->Set(v8_str("o"), templ->NewInstance()); |
| for (int i = 0; i < 100; i++) { |
| v8::HandleScope inner_scope(CcTest::isolate()); |
| v8::Handle<v8::Object> obj = templ->NewInstance(); |
| CHECK(!obj->Equals(context->Global()->Get(v8_str("o")))); |
| context->Global()->Set(v8_str("o2"), obj); |
| v8::Handle<Value> value = |
| CompileRun("o.__proto__ === o2.__proto__"); |
| CHECK(v8::True(isolate)->Equals(value)); |
| context->Global()->Set(v8_str("o"), obj); |
| } |
| } |
| |
| |
| static int StrCmp16(uint16_t* a, uint16_t* b) { |
| while (true) { |
| if (*a == 0 && *b == 0) return 0; |
| if (*a != *b) return 0 + *a - *b; |
| a++; |
| b++; |
| } |
| } |
| |
| |
| static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { |
| while (true) { |
| if (n-- == 0) return 0; |
| if (*a == 0 && *b == 0) return 0; |
| if (*a != *b) return 0 + *a - *b; |
| a++; |
| b++; |
| } |
| } |
| |
| |
| int GetUtf8Length(Handle<String> str) { |
| int len = str->Utf8Length(); |
| if (len < 0) { |
| i::Handle<i::String> istr(v8::Utils::OpenHandle(*str)); |
| i::String::Flatten(istr); |
| len = str->Utf8Length(); |
| } |
| return len; |
| } |
| |
| |
| THREADED_TEST(StringWrite) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Handle<String> str = v8_str("abcde"); |
| // abc<Icelandic eth><Unicode snowman>. |
| v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); |
| v8::Handle<String> str3 = v8::String::NewFromUtf8( |
| context->GetIsolate(), "abc\0def", v8::String::kNormalString, 7); |
| // "ab" + lead surrogate + "cd" + trail surrogate + "ef" |
| uint16_t orphans[8] = { 0x61, 0x62, 0xd800, 0x63, 0x64, 0xdc00, 0x65, 0x66 }; |
| v8::Handle<String> orphans_str = v8::String::NewFromTwoByte( |
| context->GetIsolate(), orphans, v8::String::kNormalString, 8); |
| // single lead surrogate |
| uint16_t lead[1] = { 0xd800 }; |
| v8::Handle<String> lead_str = v8::String::NewFromTwoByte( |
| context->GetIsolate(), lead, v8::String::kNormalString, 1); |
| // single trail surrogate |
| uint16_t trail[1] = { 0xdc00 }; |
| v8::Handle<String> trail_str = v8::String::NewFromTwoByte( |
| context->GetIsolate(), trail, v8::String::kNormalString, 1); |
| // surrogate pair |
| uint16_t pair[2] = { 0xd800, 0xdc00 }; |
| v8::Handle<String> pair_str = v8::String::NewFromTwoByte( |
| context->GetIsolate(), pair, v8::String::kNormalString, 2); |
| const int kStride = 4; // Must match stride in for loops in JS below. |
| CompileRun( |
| "var left = '';" |
| "for (var i = 0; i < 0xd800; i += 4) {" |
| " left = left + String.fromCharCode(i);" |
| "}"); |
| CompileRun( |
| "var right = '';" |
| "for (var i = 0; i < 0xd800; i += 4) {" |
| " right = String.fromCharCode(i) + right;" |
| "}"); |
| v8::Handle<v8::Object> global = context->Global(); |
| Handle<String> left_tree = global->Get(v8_str("left")).As<String>(); |
| Handle<String> right_tree = global->Get(v8_str("right")).As<String>(); |
| |
| CHECK_EQ(5, str2->Length()); |
| CHECK_EQ(0xd800 / kStride, left_tree->Length()); |
| CHECK_EQ(0xd800 / kStride, right_tree->Length()); |
| |
| char buf[100]; |
| char utf8buf[0xd800 * 3]; |
| uint16_t wbuf[100]; |
| int len; |
| int charlen; |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); |
| CHECK_EQ(9, len); |
| CHECK_EQ(5, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 8, &charlen); |
| CHECK_EQ(8, len); |
| CHECK_EQ(5, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 7, &charlen); |
| CHECK_EQ(5, len); |
| CHECK_EQ(4, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 6, &charlen); |
| CHECK_EQ(5, len); |
| CHECK_EQ(4, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 5, &charlen); |
| CHECK_EQ(5, len); |
| CHECK_EQ(4, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 4, &charlen); |
| CHECK_EQ(3, len); |
| CHECK_EQ(3, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 3, &charlen); |
| CHECK_EQ(3, len); |
| CHECK_EQ(3, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); |
| |
| memset(utf8buf, 0x1, 1000); |
| len = str2->WriteUtf8(utf8buf, 2, &charlen); |
| CHECK_EQ(2, len); |
| CHECK_EQ(2, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); |
| |
| // allow orphan surrogates by default |
| memset(utf8buf, 0x1, 1000); |
| len = orphans_str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); |
| CHECK_EQ(13, len); |
| CHECK_EQ(8, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "ab\355\240\200cd\355\260\200ef")); |
| |
| // replace orphan surrogates with unicode replacement character |
| memset(utf8buf, 0x1, 1000); |
| len = orphans_str->WriteUtf8(utf8buf, |
| sizeof(utf8buf), |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(13, len); |
| CHECK_EQ(8, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "ab\357\277\275cd\357\277\275ef")); |
| |
| // replace single lead surrogate with unicode replacement character |
| memset(utf8buf, 0x1, 1000); |
| len = lead_str->WriteUtf8(utf8buf, |
| sizeof(utf8buf), |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(4, len); |
| CHECK_EQ(1, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "\357\277\275")); |
| |
| // replace single trail surrogate with unicode replacement character |
| memset(utf8buf, 0x1, 1000); |
| len = trail_str->WriteUtf8(utf8buf, |
| sizeof(utf8buf), |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(4, len); |
| CHECK_EQ(1, charlen); |
| CHECK_EQ(0, strcmp(utf8buf, "\357\277\275")); |
| |
| // do not replace / write anything if surrogate pair does not fit the buffer |
| // space |
| memset(utf8buf, 0x1, 1000); |
| len = pair_str->WriteUtf8(utf8buf, |
| 3, |
| &charlen, |
| String::REPLACE_INVALID_UTF8); |
| CHECK_EQ(0, len); |
| CHECK_EQ(0, charlen); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| len = GetUtf8Length(left_tree); |
| int utf8_expected = |
| (0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride; |
| CHECK_EQ(utf8_expected, len); |
| len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); |
| CHECK_EQ(utf8_expected, len); |
| CHECK_EQ(0xd800 / kStride, charlen); |
| CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); |
| CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); |
| CHECK_EQ(0xc0 - kStride, |
| static_cast<unsigned char>(utf8buf[utf8_expected - 1])); |
| CHECK_EQ(1, utf8buf[utf8_expected]); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| len = GetUtf8Length(right_tree); |
| CHECK_EQ(utf8_expected, len); |
| len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); |
| CHECK_EQ(utf8_expected, len); |
| CHECK_EQ(0xd800 / kStride, charlen); |
| CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[0])); |
| CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[1])); |
| CHECK_EQ(0xc0 - kStride, static_cast<unsigned char>(utf8buf[2])); |
| CHECK_EQ(1, utf8buf[utf8_expected]); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf)); |
| CHECK_EQ(5, len); |
| len = str->Write(wbuf); |
| CHECK_EQ(5, len); |
| CHECK_EQ(0, strcmp("abcde", buf)); |
| uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| CHECK_EQ(0, StrCmp16(answer1, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 4); |
| CHECK_EQ(4, len); |
| len = str->Write(wbuf, 0, 4); |
| CHECK_EQ(4, len); |
| CHECK_EQ(0, strncmp("abcd\1", buf, 5)); |
| uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 5); |
| CHECK_EQ(5, len); |
| len = str->Write(wbuf, 0, 5); |
| CHECK_EQ(5, len); |
| CHECK_EQ(0, strncmp("abcde\1", buf, 6)); |
| uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 0, 6); |
| CHECK_EQ(5, len); |
| len = str->Write(wbuf, 0, 6); |
| CHECK_EQ(5, len); |
| CHECK_EQ(0, strcmp("abcde", buf)); |
| uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| CHECK_EQ(0, StrCmp16(answer4, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, -1); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 4, -1); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strcmp("e", buf)); |
| uint16_t answer5[] = {'e', '\0'}; |
| CHECK_EQ(0, StrCmp16(answer5, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, 6); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 4, 6); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strcmp("e", buf)); |
| CHECK_EQ(0, StrCmp16(answer5, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 4, 1); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 4, 1); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strncmp("e\1", buf, 2)); |
| uint16_t answer6[] = {'e', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), 3, 1); |
| CHECK_EQ(1, len); |
| len = str->Write(wbuf, 3, 1); |
| CHECK_EQ(1, len); |
| CHECK_EQ(0, strncmp("d\1", buf, 2)); |
| uint16_t answer7[] = {'d', 0x101}; |
| CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); |
| |
| memset(wbuf, 0x1, sizeof(wbuf)); |
| wbuf[5] = 'X'; |
| len = str->Write(wbuf, 0, 6, String::NO_NULL_TERMINATION); |
| CHECK_EQ(5, len); |
| CHECK_EQ('X', wbuf[5]); |
| uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'}; |
| uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5)); |
| CHECK_NE(0, StrCmp16(answer8b, wbuf)); |
| wbuf[5] = '\0'; |
| CHECK_EQ(0, StrCmp16(answer8b, wbuf)); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| buf[5] = 'X'; |
| len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf), |
| 0, |
| 6, |
| String::NO_NULL_TERMINATION); |
| CHECK_EQ(5, len); |
| CHECK_EQ('X', buf[5]); |
| CHECK_EQ(0, strncmp("abcde", buf, 5)); |
| CHECK_NE(0, strcmp("abcde", buf)); |
| buf[5] = '\0'; |
| CHECK_EQ(0, strcmp("abcde", buf)); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| utf8buf[8] = 'X'; |
| len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, |
| String::NO_NULL_TERMINATION); |
| CHECK_EQ(8, len); |
| CHECK_EQ('X', utf8buf[8]); |
| CHECK_EQ(5, charlen); |
| CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203", 8)); |
| CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203")); |
| utf8buf[8] = '\0'; |
| CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); |
| |
| memset(utf8buf, 0x1, sizeof(utf8buf)); |
| utf8buf[5] = 'X'; |
| len = str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, |
| String::NO_NULL_TERMINATION); |
| CHECK_EQ(5, len); |
| CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched. |
| CHECK_EQ(5, charlen); |
| utf8buf[5] = '\0'; |
| CHECK_EQ(0, strcmp(utf8buf, "abcde")); |
| |
| memset(buf, 0x1, sizeof(buf)); |
| len = str3->WriteOneByte(reinterpret_cast<uint8_t*>(buf)); |
| CHECK_EQ(7, len); |
| CHECK_EQ(0, strcmp("abc", buf)); |
| CHECK_EQ(0, buf[3]); |
| CHECK_EQ(0, strcmp("def", buf + 4)); |
| |
| CHECK_EQ(0, str->WriteOneByte(NULL, 0, 0, String::NO_NULL_TERMINATION)); |
| CHECK_EQ(0, str->WriteUtf8(NULL, 0, 0, String::NO_NULL_TERMINATION)); |
| CHECK_EQ(0, str->Write(NULL, 0, 0, String::NO_NULL_TERMINATION)); |
| } |
| |
| |
| static void Utf16Helper( |
| LocalContext& context, // NOLINT |
| const char* name, |
| const char* lengths_name, |
| int len) { |
| Local<v8::Array> a = |
| Local<v8::Array>::Cast(context->Global()->Get(v8_str(name))); |
| Local<v8::Array> alens = |
| Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name))); |
| for (int i = 0; i < len; i++) { |
| Local<v8::String> string = |
| Local<v8::String>::Cast(a->Get(i)); |
| Local<v8::Number> expected_len = |
| Local<v8::Number>::Cast(alens->Get(i)); |
| int length = GetUtf8Length(string); |
| CHECK_EQ(static_cast<int>(expected_len->Value()), length); |
| } |
| } |
| |
| |
| THREADED_TEST(Utf16) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| CompileRun( |
| "var pad = '01234567890123456789';" |
| "var p = [];" |
| "var plens = [20, 3, 3];" |
| "p.push('01234567890123456789');" |
| "var lead = 0xd800;" |
| "var trail = 0xdc00;" |
| "p.push(String.fromCharCode(0xd800));" |
| "p.push(String.fromCharCode(0xdc00));" |
| "var a = [];" |
| "var b = [];" |
| "var c = [];" |
| "var alens = [];" |
| "for (var i = 0; i < 3; i++) {" |
| " p[1] = String.fromCharCode(lead++);" |
| " for (var j = 0; j < 3; j++) {" |
| " p[2] = String.fromCharCode(trail++);" |
| " a.push(p[i] + p[j]);" |
| " b.push(p[i] + p[j]);" |
| " c.push(p[i] + p[j]);" |
| " alens.push(plens[i] + plens[j]);" |
| " }" |
| "}" |
| "alens[5] -= 2;" // Here the surrogate pairs match up. |
| "var a2 = [];" |
| "var b2 = [];" |
| "var c2 = [];" |
| "var a2lens = [];" |
| "for (var m = 0; m < 9; m++) {" |
| " for (var n = 0; n < 9; n++) {" |
| " a2.push(a[m] + a[n]);" |
| " b2.push(b[m] + b[n]);" |
| " var newc = 'x' + c[m] + c[n] + 'y';" |
| " c2.push(newc.substring(1, newc.length - 1));" |
| " var utf = alens[m] + alens[n];" // And here. |
| // The 'n's that start with 0xdc.. are 6-8 |
| // The 'm's that end with 0xd8.. are 1, 4 and 7 |
| " if ((m % 3) == 1 && n >= 6) utf -= 2;" |
| " a2lens.push(utf);" |
| " }" |
| "}"); |
| Utf16Helper(context, "a", "alens", 9); |
| Utf16Helper(context, "a2", "a2lens", 81); |
| } |
| |
| |
| static bool SameSymbol(Handle<String> s1, Handle<String> s2) { |
| i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1)); |
| i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2)); |
| return *is1 == *is2; |
| } |
| |
| |
| THREADED_TEST(Utf16Symbol) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| Handle<String> symbol1 = v8::String::NewFromUtf8( |
| context->GetIsolate(), "abc", v8::String::kInternalizedString); |
| Handle<String> symbol2 = v8::String::NewFromUtf8( |
| context->GetIsolate(), "abc", v8::String::kInternalizedString); |
| CHECK(SameSymbol(symbol1, symbol2)); |
| |
| CompileRun( |
| "var sym0 = 'benedictus';" |
| "var sym0b = 'S\303\270ren';" |
| "var sym1 = '\355\240\201\355\260\207';" |
| "var sym2 = '\360\220\220\210';" |
| "var sym3 = 'x\355\240\201\355\260\207';" |
| "var sym4 = 'x\360\220\220\210';" |
| "if (sym1.length != 2) throw sym1;" |
| "if (sym1.charCodeAt(1) != 0xdc07) throw sym1.charCodeAt(1);" |
| "if (sym2.length != 2) throw sym2;" |
| "if (sym2.charCodeAt(1) != 0xdc08) throw sym2.charCodeAt(2);" |
| "if (sym3.length != 3) throw sym3;" |
| "if (sym3.charCodeAt(2) != 0xdc07) throw sym1.charCodeAt(2);" |
| "if (sym4.length != 3) throw sym4;" |
| "if (sym4.charCodeAt(2) != 0xdc08) throw sym2.charCodeAt(2);"); |
| Handle<String> sym0 = v8::String::NewFromUtf8( |
| context->GetIsolate(), "benedictus", v8::String::kInternalizedString); |
| Handle<String> sym0b = v8::String::NewFromUtf8( |
| context->GetIsolate(), "S\303\270ren", v8::String::kInternalizedString); |
| Handle<String> sym1 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "\355\240\201\355\260\207", |
| v8::String::kInternalizedString); |
| Handle<String> sym2 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "\360\220\220\210", |
| v8::String::kInternalizedString); |
| Handle<String> sym3 = v8::String::NewFromUtf8( |
| context->GetIsolate(), "x\355\240\201\355\260\207", |
| v8::String::kInternalizedString); |
| Handle<String> sym4 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "x\360\220\220\210", |
| v8::String::kInternalizedString); |
| v8::Local<v8::Object> global = context->Global(); |
| Local<Value> s0 = global->Get(v8_str("sym0")); |
| Local<Value> s0b = global->Get(v8_str("sym0b")); |
| Local<Value> s1 = global->Get(v8_str("sym1")); |
| Local<Value> s2 = global->Get(v8_str("sym2")); |
| Local<Value> s3 = global->Get(v8_str("sym3")); |
| Local<Value> s4 = global->Get(v8_str("sym4")); |
| CHECK(SameSymbol(sym0, Handle<String>::Cast(s0))); |
| CHECK(SameSymbol(sym0b, Handle<String>::Cast(s0b))); |
| CHECK(SameSymbol(sym1, Handle<String>::Cast(s1))); |
| CHECK(SameSymbol(sym2, Handle<String>::Cast(s2))); |
| CHECK(SameSymbol(sym3, Handle<String>::Cast(s3))); |
| CHECK(SameSymbol(sym4, Handle<String>::Cast(s4))); |
| } |
| |
| |
| THREADED_TEST(ToArrayIndex) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Handle<String> str = v8_str("42"); |
| v8::Handle<v8::Uint32> index = str->ToArrayIndex(); |
| CHECK(!index.IsEmpty()); |
| CHECK_EQ(42.0, index->Uint32Value()); |
| str = v8_str("42asdf"); |
| index = str->ToArrayIndex(); |
| CHECK(index.IsEmpty()); |
| str = v8_str("-42"); |
| index = str->ToArrayIndex(); |
| CHECK(index.IsEmpty()); |
| str = v8_str("4294967294"); |
| index = str->ToArrayIndex(); |
| CHECK(!index.IsEmpty()); |
| CHECK_EQ(4294967294.0, index->Uint32Value()); |
| v8::Handle<v8::Number> num = v8::Number::New(isolate, 1); |
| index = num->ToArrayIndex(); |
| CHECK(!index.IsEmpty()); |
| CHECK_EQ(1.0, index->Uint32Value()); |
| num = v8::Number::New(isolate, -1); |
| index = num->ToArrayIndex(); |
| CHECK(index.IsEmpty()); |
| v8::Handle<v8::Object> obj = v8::Object::New(isolate); |
| index = obj->ToArrayIndex(); |
| CHECK(index.IsEmpty()); |
| } |
| |
| |
| THREADED_TEST(ErrorConstruction) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| v8::Handle<String> foo = v8_str("foo"); |
| v8::Handle<String> message = v8_str("message"); |
| v8::Handle<Value> range_error = v8::Exception::RangeError(foo); |
| CHECK(range_error->IsObject()); |
| CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); |
| v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); |
| CHECK(reference_error->IsObject()); |
| CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); |
| v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); |
| CHECK(syntax_error->IsObject()); |
| CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); |
| v8::Handle<Value> type_error = v8::Exception::TypeError(foo); |
| CHECK(type_error->IsObject()); |
| CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); |
| v8::Handle<Value> error = v8::Exception::Error(foo); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); |
| } |
| |
| |
| static void ThrowV8Exception(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Handle<String> foo = v8_str("foo"); |
| v8::Handle<String> message = v8_str("message"); |
| v8::Handle<Value> error = v8::Exception::Error(foo); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); |
| info.GetIsolate()->ThrowException(error); |
| info.GetReturnValue().SetUndefined(); |
| } |
| |
| |
| THREADED_TEST(ExceptionCreateMessage) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Handle<String> foo_str = v8_str("foo"); |
| v8::Handle<String> message_str = v8_str("message"); |
| |
| v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); |
| |
| Local<v8::FunctionTemplate> fun = |
| v8::FunctionTemplate::New(context->GetIsolate(), ThrowV8Exception); |
| v8::Local<v8::Object> global = context->Global(); |
| global->Set(v8_str("throwV8Exception"), fun->GetFunction()); |
| |
| TryCatch try_catch(context->GetIsolate()); |
| CompileRun( |
| "function f1() {\n" |
| " throwV8Exception();\n" |
| "};\n" |
| "f1();"); |
| CHECK(try_catch.HasCaught()); |
| |
| v8::Handle<v8::Value> error = try_catch.Exception(); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str)); |
| |
| v8::Handle<v8::Message> message = v8::Exception::CreateMessage(error); |
| CHECK(!message.IsEmpty()); |
| CHECK_EQ(2, message->GetLineNumber()); |
| CHECK_EQ(2, message->GetStartColumn()); |
| |
| v8::Handle<v8::StackTrace> stackTrace = message->GetStackTrace(); |
| CHECK(!stackTrace.IsEmpty()); |
| CHECK_EQ(2, stackTrace->GetFrameCount()); |
| |
| stackTrace = v8::Exception::GetStackTrace(error); |
| CHECK(!stackTrace.IsEmpty()); |
| CHECK_EQ(2, stackTrace->GetFrameCount()); |
| |
| v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); |
| |
| // Now check message location when SetCaptureStackTraceForUncaughtExceptions |
| // is false. |
| try_catch.Reset(); |
| |
| CompileRun( |
| "function f2() {\n" |
| " return throwV8Exception();\n" |
| "};\n" |
| "f2();"); |
| CHECK(try_catch.HasCaught()); |
| |
| error = try_catch.Exception(); |
| CHECK(error->IsObject()); |
| CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str)); |
| |
| message = v8::Exception::CreateMessage(error); |
| CHECK(!message.IsEmpty()); |
| CHECK_EQ(2, message->GetLineNumber()); |
| CHECK_EQ(9, message->GetStartColumn()); |
| |
| // Should be empty stack trace. |
| stackTrace = message->GetStackTrace(); |
| CHECK(stackTrace.IsEmpty()); |
| CHECK(v8::Exception::GetStackTrace(error).IsEmpty()); |
| } |
| |
| |
| static void YGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(10)); |
| } |
| |
| |
| static void YSetter(Local<String> name, |
| Local<Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| Local<Object> this_obj = Local<Object>::Cast(info.This()); |
| if (this_obj->Has(name)) this_obj->Delete(name); |
| this_obj->Set(name, value); |
| } |
| |
| |
| THREADED_TEST(DeleteAccessor) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("y"), YGetter, YSetter); |
| LocalContext context; |
| v8::Handle<v8::Object> holder = obj->NewInstance(); |
| context->Global()->Set(v8_str("holder"), holder); |
| v8::Handle<Value> result = CompileRun( |
| "holder.y = 11; holder.y = 12; holder.y"); |
| CHECK_EQ(12u, result->Uint32Value()); |
| } |
| |
| |
| THREADED_TEST(TypeSwitch) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; |
| v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); |
| LocalContext context; |
| v8::Handle<v8::Object> obj0 = v8::Object::New(isolate); |
| v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); |
| v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); |
| v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); |
| for (int i = 0; i < 10; i++) { |
| CHECK_EQ(0, type_switch->match(obj0)); |
| CHECK_EQ(1, type_switch->match(obj1)); |
| CHECK_EQ(2, type_switch->match(obj2)); |
| CHECK_EQ(3, type_switch->match(obj3)); |
| CHECK_EQ(3, type_switch->match(obj3)); |
| CHECK_EQ(2, type_switch->match(obj2)); |
| CHECK_EQ(1, type_switch->match(obj1)); |
| CHECK_EQ(0, type_switch->match(obj0)); |
| } |
| } |
| |
| |
| static int trouble_nesting = 0; |
| static void TroubleCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| trouble_nesting++; |
| |
| // Call a JS function that throws an uncaught exception. |
| Local<v8::Object> arg_this = |
| args.GetIsolate()->GetCurrentContext()->Global(); |
| Local<Value> trouble_callee = (trouble_nesting == 3) ? |
| arg_this->Get(v8_str("trouble_callee")) : |
| arg_this->Get(v8_str("trouble_caller")); |
| CHECK(trouble_callee->IsFunction()); |
| args.GetReturnValue().Set( |
| Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL)); |
| } |
| |
| |
| static int report_count = 0; |
| static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, |
| v8::Handle<Value>) { |
| report_count++; |
| } |
| |
| |
| // Counts uncaught exceptions, but other tests running in parallel |
| // also have uncaught exceptions. |
| TEST(ApiUncaughtException) { |
| report_count = 0; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); |
| |
| Local<v8::FunctionTemplate> fun = |
| v8::FunctionTemplate::New(isolate, TroubleCallback); |
| v8::Local<v8::Object> global = env->Global(); |
| global->Set(v8_str("trouble"), fun->GetFunction()); |
| |
| CompileRun( |
| "function trouble_callee() {" |
| " var x = null;" |
| " return x.foo;" |
| "};" |
| "function trouble_caller() {" |
| " trouble();" |
| "};"); |
| Local<Value> trouble = global->Get(v8_str("trouble")); |
| CHECK(trouble->IsFunction()); |
| Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); |
| CHECK(trouble_callee->IsFunction()); |
| Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); |
| CHECK(trouble_caller->IsFunction()); |
| Function::Cast(*trouble_caller)->Call(global, 0, NULL); |
| CHECK_EQ(1, report_count); |
| v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); |
| } |
| |
| |
| TEST(ApiUncaughtExceptionInObjectObserve) { |
| v8::internal::FLAG_stack_size = 150; |
| report_count = 0; |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); |
| CompileRun( |
| "var obj = {};" |
| "var observe_count = 0;" |
| "function observer1() { ++observe_count; };" |
| "function observer2() { ++observe_count; };" |
| "function observer_throws() { throw new Error(); };" |
| "function stack_overflow() { return (function f(x) { f(x+1); })(0); };" |
| "Object.observe(obj, observer_throws.bind());" |
| "Object.observe(obj, observer1);" |
| "Object.observe(obj, stack_overflow);" |
| "Object.observe(obj, observer2);" |
| "Object.observe(obj, observer_throws.bind());" |
| "obj.foo = 'bar';"); |
| CHECK_EQ(3, report_count); |
| ExpectInt32("observe_count", 2); |
| v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); |
| } |
| |
| |
| static const char* script_resource_name = "ExceptionInNativeScript.js"; |
| static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, |
| v8::Handle<Value>) { |
| v8::Handle<v8::Value> name_val = message->GetScriptOrigin().ResourceName(); |
| CHECK(!name_val.IsEmpty() && name_val->IsString()); |
| v8::String::Utf8Value name(message->GetScriptOrigin().ResourceName()); |
| CHECK_EQ(0, strcmp(script_resource_name, *name)); |
| CHECK_EQ(3, message->GetLineNumber()); |
| v8::String::Utf8Value source_line(message->GetSourceLine()); |
| CHECK_EQ(0, strcmp(" new o.foo();", *source_line)); |
| } |
| |
| |
| TEST(ExceptionInNativeScript) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); |
| |
| Local<v8::FunctionTemplate> fun = |
| v8::FunctionTemplate::New(isolate, TroubleCallback); |
| v8::Local<v8::Object> global = env->Global(); |
| global->Set(v8_str("trouble"), fun->GetFunction()); |
| |
| CompileRunWithOrigin( |
| "function trouble() {\n" |
| " var o = {};\n" |
| " new o.foo();\n" |
| "};", |
| script_resource_name); |
| Local<Value> trouble = global->Get(v8_str("trouble")); |
| CHECK(trouble->IsFunction()); |
| Function::Cast(*trouble)->Call(global, 0, NULL); |
| v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); |
| } |
| |
| |
| TEST(CompilationErrorUsingTryCatchHandler) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::TryCatch try_catch(env->GetIsolate()); |
| v8_compile("This doesn't &*&@#$&*^ compile."); |
| CHECK(*try_catch.Exception()); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| TEST(TryCatchFinallyUsingTryCatchHandler) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::TryCatch try_catch(env->GetIsolate()); |
| CompileRun("try { throw ''; } catch (e) {}"); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun("try { throw ''; } finally {}"); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CompileRun( |
| "(function() {" |
| "try { throw ''; } finally { return; }" |
| "})()"); |
| CHECK(!try_catch.HasCaught()); |
| CompileRun( |
| "(function()" |
| " { try { throw ''; } finally { throw 0; }" |
| "})()"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::HandleScope scope(args.GetIsolate()); |
| CompileRun(args[0]->ToString(args.GetIsolate())); |
| } |
| |
| |
| TEST(TryCatchFinallyStoresMessageUsingTryCatchHandler) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->Set(v8_str("CEvaluate"), |
| v8::FunctionTemplate::New(isolate, CEvaluate)); |
| LocalContext context(0, templ); |
| v8::TryCatch try_catch(isolate); |
| CompileRun("try {" |
| " CEvaluate('throw 1;');" |
| "} finally {" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(!try_catch.Message().IsEmpty()); |
| String::Utf8Value exception_value(try_catch.Exception()); |
| CHECK_EQ(0, strcmp(*exception_value, "1")); |
| try_catch.Reset(); |
| CompileRun("try {" |
| " CEvaluate('throw 1;');" |
| "} finally {" |
| " throw 2;" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(!try_catch.Message().IsEmpty()); |
| String::Utf8Value finally_exception_value(try_catch.Exception()); |
| CHECK_EQ(0, strcmp(*finally_exception_value, "2")); |
| } |
| |
| |
| // For use within the TestSecurityHandler() test. |
| static bool g_security_callback_result = false; |
| static bool SecurityTestCallback(Local<v8::Object> global, Local<Value> name, |
| v8::AccessType type, Local<Value> data) { |
| printf("a\n"); |
| return g_security_callback_result; |
| } |
| |
| |
| // SecurityHandler can't be run twice |
| TEST(SecurityHandler) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope0(isolate); |
| v8::Handle<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| global_template->SetAccessCheckCallbacks(SecurityTestCallback, NULL); |
| // Create an environment |
| v8::Handle<Context> context0 = Context::New(isolate, NULL, global_template); |
| context0->Enter(); |
| |
| v8::Handle<v8::Object> global0 = context0->Global(); |
| v8::Handle<Script> script0 = v8_compile("foo = 111"); |
| script0->Run(); |
| global0->Set(v8_str("0"), v8_num(999)); |
| v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); |
| CHECK_EQ(111, foo0->Int32Value()); |
| v8::Handle<Value> z0 = global0->Get(v8_str("0")); |
| CHECK_EQ(999, z0->Int32Value()); |
| |
| // Create another environment, should fail security checks. |
| v8::HandleScope scope1(isolate); |
| |
| v8::Handle<Context> context1 = |
| Context::New(isolate, NULL, global_template); |
| context1->Enter(); |
| |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("othercontext"), global0); |
| // This set will fail the security check. |
| v8::Handle<Script> script1 = |
| v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); |
| script1->Run(); |
| g_security_callback_result = true; |
| // This read will pass the security check. |
| v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); |
| CHECK_EQ(111, foo1->Int32Value()); |
| // This read will pass the security check. |
| v8::Handle<Value> z1 = global0->Get(v8_str("0")); |
| CHECK_EQ(999, z1->Int32Value()); |
| |
| // Create another environment, should pass security checks. |
| { |
| v8::HandleScope scope2(isolate); |
| LocalContext context2; |
| v8::Handle<v8::Object> global2 = context2->Global(); |
| global2->Set(v8_str("othercontext"), global0); |
| v8::Handle<Script> script2 = |
| v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); |
| script2->Run(); |
| v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); |
| CHECK_EQ(333, foo2->Int32Value()); |
| v8::Handle<Value> z2 = global0->Get(v8_str("0")); |
| CHECK_EQ(888, z2->Int32Value()); |
| } |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| THREADED_TEST(SecurityChecks) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Handle<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| |
| // Create a function in env1. |
| CompileRun("spy=function(){return spy;}"); |
| Local<Value> spy = env1->Global()->Get(v8_str("spy")); |
| CHECK(spy->IsFunction()); |
| |
| // Create another function accessing global objects. |
| CompileRun("spy2=function(){return new this.Array();}"); |
| Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); |
| CHECK(spy2->IsFunction()); |
| |
| // Switch to env2 in the same domain and invoke spy on env2. |
| { |
| env2->SetSecurityToken(foo); |
| // Enter env2 |
| Context::Scope scope_env2(env2); |
| Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); |
| CHECK(result->IsFunction()); |
| } |
| |
| { |
| env2->SetSecurityToken(bar); |
| Context::Scope scope_env2(env2); |
| |
| // Call cross_domain_call, it should throw an exception |
| v8::TryCatch try_catch(env1->GetIsolate()); |
| Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| |
| // Regression test case for issue 1183439. |
| THREADED_TEST(SecurityChecksForPrototypeChain) { |
| LocalContext current; |
| v8::HandleScope scope(current->GetIsolate()); |
| v8::Handle<Context> other = Context::New(current->GetIsolate()); |
| |
| // Change context to be able to get to the Object function in the |
| // other context without hitting the security checks. |
| v8::Local<Value> other_object; |
| { Context::Scope scope(other); |
| other_object = other->Global()->Get(v8_str("Object")); |
| other->Global()->Set(v8_num(42), v8_num(87)); |
| } |
| |
| current->Global()->Set(v8_str("other"), other->Global()); |
| CHECK(v8_compile("other")->Run()->Equals(other->Global())); |
| |
| // Make sure the security check fails here and we get an undefined |
| // result instead of getting the Object function. Repeat in a loop |
| // to make sure to exercise the IC code. |
| v8::Local<Script> access_other0 = v8_compile("other.Object"); |
| v8::Local<Script> access_other1 = v8_compile("other[42]"); |
| for (int i = 0; i < 5; i++) { |
| CHECK(access_other0->Run().IsEmpty()); |
| CHECK(access_other1->Run().IsEmpty()); |
| } |
| |
| // Create an object that has 'other' in its prototype chain and make |
| // sure we cannot access the Object function indirectly through |
| // that. Repeat in a loop to make sure to exercise the IC code. |
| v8_compile("function F() { };" |
| "F.prototype = other;" |
| "var f = new F();")->Run(); |
| v8::Local<Script> access_f0 = v8_compile("f.Object"); |
| v8::Local<Script> access_f1 = v8_compile("f[42]"); |
| for (int j = 0; j < 5; j++) { |
| CHECK(access_f0->Run().IsEmpty()); |
| CHECK(access_f1->Run().IsEmpty()); |
| } |
| |
| // Now it gets hairy: Set the prototype for the other global object |
| // to be the current global object. The prototype chain for 'f' now |
| // goes through 'other' but ends up in the current global object. |
| { Context::Scope scope(other); |
| other->Global()->Set(v8_str("__proto__"), current->Global()); |
| } |
| // Set a named and an index property on the current global |
| // object. To force the lookup to go through the other global object, |
| // the properties must not exist in the other global object. |
| current->Global()->Set(v8_str("foo"), v8_num(100)); |
| current->Global()->Set(v8_num(99), v8_num(101)); |
| // Try to read the properties from f and make sure that the access |
| // gets stopped by the security checks on the other global object. |
| Local<Script> access_f2 = v8_compile("f.foo"); |
| Local<Script> access_f3 = v8_compile("f[99]"); |
| for (int k = 0; k < 5; k++) { |
| CHECK(access_f2->Run().IsEmpty()); |
| CHECK(access_f3->Run().IsEmpty()); |
| } |
| } |
| |
| |
| static bool security_check_with_gc_called; |
| |
| static bool SecurityTestCallbackWithGC(Local<v8::Object> global, |
| Local<v8::Value> name, |
| v8::AccessType type, Local<Value> data) { |
| CcTest::heap()->CollectAllGarbage(); |
| security_check_with_gc_called = true; |
| return true; |
| } |
| |
| |
| TEST(SecurityTestGCAllowed) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Handle<v8::ObjectTemplate> object_template = |
| v8::ObjectTemplate::New(isolate); |
| object_template->SetAccessCheckCallbacks(SecurityTestCallbackWithGC, NULL); |
| |
| v8::Handle<Context> context = Context::New(isolate); |
| v8::Context::Scope context_scope(context); |
| |
| context->Global()->Set(v8_str("obj"), object_template->NewInstance()); |
| |
| security_check_with_gc_called = false; |
| CompileRun("obj[0] = new String(1002);"); |
| CHECK(security_check_with_gc_called); |
| |
| security_check_with_gc_called = false; |
| CHECK(CompileRun("obj[0]")->ToString(isolate)->Equals(v8_str("1002"))); |
| CHECK(security_check_with_gc_called); |
| } |
| |
| |
| THREADED_TEST(CrossDomainDelete) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Handle<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| env1->Global()->Set(v8_str("prop"), v8_num(3)); |
| env2->Global()->Set(v8_str("env1"), env1->Global()); |
| |
| // Change env2 to a different domain and delete env1.prop. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = |
| CompileRun("delete env1.prop"); |
| CHECK(result.IsEmpty()); |
| } |
| |
| // Check that env1.prop still exists. |
| Local<Value> v = env1->Global()->Get(v8_str("prop")); |
| CHECK(v->IsNumber()); |
| CHECK_EQ(3, v->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(CrossDomainIsPropertyEnumerable) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Handle<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| env1->Global()->Set(v8_str("prop"), v8_num(3)); |
| env2->Global()->Set(v8_str("env1"), env1->Global()); |
| |
| // env1.prop is enumerable in env2. |
| Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun(test); |
| CHECK(result->IsTrue()); |
| } |
| |
| // Change env2 to a different domain and test again. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun(test); |
| CHECK(result.IsEmpty()); |
| } |
| } |
| |
| |
| THREADED_TEST(CrossDomainForIn) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Handle<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| Local<Value> bar = v8_str("bar"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| env1->Global()->Set(v8_str("prop"), v8_num(3)); |
| env2->Global()->Set(v8_str("env1"), env1->Global()); |
| |
| // Change env2 to a different domain and set env1's global object |
| // as the __proto__ of an object in env2 and enumerate properties |
| // in for-in. It shouldn't enumerate properties on env1's global |
| // object. |
| env2->SetSecurityToken(bar); |
| { |
| Context::Scope scope_env2(env2); |
| Local<Value> result = CompileRun( |
| "(function() {" |
| " var obj = { '__proto__': env1 };" |
| " try {" |
| " for (var p in obj) {" |
| " if (p == 'prop') return false;" |
| " }" |
| " return false;" |
| " } catch (e) {" |
| " return true;" |
| " }" |
| "})()"); |
| CHECK(result->IsTrue()); |
| } |
| } |
| |
| |
| TEST(ContextDetachGlobal) { |
| LocalContext env1; |
| v8::HandleScope handle_scope(env1->GetIsolate()); |
| v8::Handle<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<v8::Object> global1 = env1->Global(); |
| |
| Local<Value> foo = v8_str("foo"); |
| |
| // Set to the same domain. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| // Enter env2 |
| env2->Enter(); |
| |
| // Create a function in env2 and add a reference to it in env1. |
| Local<v8::Object> global2 = env2->Global(); |
| global2->Set(v8_str("prop"), v8::Integer::New(env2->GetIsolate(), 1)); |
| CompileRun("function getProp() {return prop;}"); |
| |
| env1->Global()->Set(v8_str("getProp"), |
| global2->Get(v8_str("getProp"))); |
| |
| // Detach env2's global, and reuse the global object of env2 |
| env2->Exit(); |
| env2->DetachGlobal(); |
| |
| v8::Handle<Context> env3 = Context::New(env1->GetIsolate(), |
| 0, |
| v8::Handle<v8::ObjectTemplate>(), |
| global2); |
| env3->SetSecurityToken(v8_str("bar")); |
| env3->Enter(); |
| |
| Local<v8::Object> global3 = env3->Global(); |
| CHECK(global2->Equals(global3)); |
| CHECK(global3->Get(v8_str("prop"))->IsUndefined()); |
| CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); |
| global3->Set(v8_str("prop"), v8::Integer::New(env3->GetIsolate(), -1)); |
| global3->Set(v8_str("prop2"), v8::Integer::New(env3->GetIsolate(), 2)); |
| env3->Exit(); |
| |
| // Call getProp in env1, and it should return the value 1 |
| { |
| Local<Value> get_prop = global1->Get(v8_str("getProp")); |
| CHECK(get_prop->IsFunction()); |
| v8::TryCatch try_catch(env1->GetIsolate()); |
| Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(1, r->Int32Value()); |
| } |
| |
| // Check that env3 is not accessible from env1 |
| { |
| Local<Value> r = global3->Get(v8_str("prop2")); |
| CHECK(r.IsEmpty()); |
| } |
| } |
| |
| |
| TEST(DetachGlobal) { |
| LocalContext env1; |
| v8::HandleScope scope(env1->GetIsolate()); |
| |
| // Create second environment. |
| v8::Handle<Context> env2 = Context::New(env1->GetIsolate()); |
| |
| Local<Value> foo = v8_str("foo"); |
| |
| // Set same security token for env1 and env2. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| // Create a property on the global object in env2. |
| { |
| v8::Context::Scope scope(env2); |
| env2->Global()->Set(v8_str("p"), v8::Integer::New(env2->GetIsolate(), 42)); |
| } |
| |
| // Create a reference to env2 global from env1 global. |
| env1->Global()->Set(v8_str("other"), env2->Global()); |
| |
| // Check that we have access to other.p in env2 from env1. |
| Local<Value> result = CompileRun("other.p"); |
| CHECK(result->IsInt32()); |
| CHECK_EQ(42, result->Int32Value()); |
| |
| // Hold on to global from env2 and detach global from env2. |
| Local<v8::Object> global2 = env2->Global(); |
| env2->DetachGlobal(); |
| |
| // Check that the global has been detached. No other.p property can |
| // be found. |
| result = CompileRun("other.p"); |
| CHECK(result.IsEmpty()); |
| |
| // Reuse global2 for env3. |
| v8::Handle<Context> env3 = Context::New(env1->GetIsolate(), |
| 0, |
| v8::Handle<v8::ObjectTemplate>(), |
| global2); |
| CHECK(global2->Equals(env3->Global())); |
| |
| // Start by using the same security token for env3 as for env1 and env2. |
| env3->SetSecurityToken(foo); |
| |
| // Create a property on the global object in env3. |
| { |
| v8::Context::Scope scope(env3); |
| env3->Global()->Set(v8_str("p"), v8::Integer::New(env3->GetIsolate(), 24)); |
| } |
| |
| // Check that other.p is now the property in env3 and that we have access. |
| result = CompileRun("other.p"); |
| CHECK(result->IsInt32()); |
| CHECK_EQ(24, result->Int32Value()); |
| |
| // Change security token for env3 to something different from env1 and env2. |
| env3->SetSecurityToken(v8_str("bar")); |
| |
| // Check that we do not have access to other.p in env1. |other| is now |
| // the global object for env3 which has a different security token, |
| // so access should be blocked. |
| result = CompileRun("other.p"); |
| CHECK(result.IsEmpty()); |
| } |
| |
| |
| void GetThisX(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set( |
| info.GetIsolate()->GetCurrentContext()->Global()->Get(v8_str("x"))); |
| } |
| |
| |
| TEST(DetachedAccesses) { |
| LocalContext env1; |
| v8::HandleScope scope(env1->GetIsolate()); |
| |
| // Create second environment. |
| Local<ObjectTemplate> inner_global_template = |
| FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate(); |
| inner_global_template ->SetAccessorProperty( |
| v8_str("this_x"), FunctionTemplate::New(env1->GetIsolate(), GetThisX)); |
| v8::Local<Context> env2 = |
| Context::New(env1->GetIsolate(), NULL, inner_global_template); |
| |
| Local<Value> foo = v8_str("foo"); |
| |
| // Set same security token for env1 and env2. |
| env1->SetSecurityToken(foo); |
| env2->SetSecurityToken(foo); |
| |
| env1->Global()->Set(v8_str("x"), v8_str("env1_x")); |
| |
| { |
| v8::Context::Scope scope(env2); |
| env2->Global()->Set(v8_str("x"), v8_str("env2_x")); |
| CompileRun( |
| "function bound_x() { return x; }" |
| "function get_x() { return this.x; }" |
| "function get_x_w() { return (function() {return this.x;})(); }"); |
| env1->Global()->Set(v8_str("bound_x"), CompileRun("bound_x")); |
| env1->Global()->Set(v8_str("get_x"), CompileRun("get_x")); |
| env1->Global()->Set(v8_str("get_x_w"), CompileRun("get_x_w")); |
| env1->Global()->Set( |
| v8_str("this_x"), |
| CompileRun("Object.getOwnPropertyDescriptor(this, 'this_x').get")); |
| } |
| |
| Local<Object> env2_global = env2->Global(); |
| env2_global->TurnOnAccessCheck(); |
| env2->DetachGlobal(); |
| |
| Local<Value> result; |
| result = CompileRun("bound_x()"); |
| CHECK(v8_str("env2_x")->Equals(result)); |
| result = CompileRun("get_x()"); |
| CHECK(result.IsEmpty()); |
| result = CompileRun("get_x_w()"); |
| CHECK(result.IsEmpty()); |
| result = CompileRun("this_x()"); |
| CHECK(v8_str("env2_x")->Equals(result)); |
| |
| // Reattach env2's proxy |
| env2 = Context::New(env1->GetIsolate(), |
| 0, |
| v8::Handle<v8::ObjectTemplate>(), |
| env2_global); |
| env2->SetSecurityToken(foo); |
| { |
| v8::Context::Scope scope(env2); |
| env2->Global()->Set(v8_str("x"), v8_str("env3_x")); |
| env2->Global()->Set(v8_str("env1"), env1->Global()); |
| result = CompileRun( |
| "results = [];" |
| "for (var i = 0; i < 4; i++ ) {" |
| " results.push(env1.bound_x());" |
| " results.push(env1.get_x());" |
| " results.push(env1.get_x_w());" |
| " results.push(env1.this_x());" |
| "}" |
| "results"); |
| Local<v8::Array> results = Local<v8::Array>::Cast(result); |
| CHECK_EQ(16u, results->Length()); |
| for (int i = 0; i < 16; i += 4) { |
| CHECK(v8_str("env2_x")->Equals(results->Get(i + 0))); |
| CHECK(v8_str("env1_x")->Equals(results->Get(i + 1))); |
| CHECK(v8_str("env3_x")->Equals(results->Get(i + 2))); |
| CHECK(v8_str("env2_x")->Equals(results->Get(i + 3))); |
| } |
| } |
| |
| result = CompileRun( |
| "results = [];" |
| "for (var i = 0; i < 4; i++ ) {" |
| " results.push(bound_x());" |
| " results.push(get_x());" |
| " results.push(get_x_w());" |
| " results.push(this_x());" |
| "}" |
| "results"); |
| Local<v8::Array> results = Local<v8::Array>::Cast(result); |
| CHECK_EQ(16u, results->Length()); |
| for (int i = 0; i < 16; i += 4) { |
| CHECK(v8_str("env2_x")->Equals(results->Get(i + 0))); |
| CHECK(v8_str("env3_x")->Equals(results->Get(i + 1))); |
| CHECK(v8_str("env3_x")->Equals(results->Get(i + 2))); |
| CHECK(v8_str("env2_x")->Equals(results->Get(i + 3))); |
| } |
| |
| result = CompileRun( |
| "results = [];" |
| "for (var i = 0; i < 4; i++ ) {" |
| " results.push(this.bound_x());" |
| " results.push(this.get_x());" |
| " results.push(this.get_x_w());" |
| " results.push(this.this_x());" |
| "}" |
| "results"); |
| results = Local<v8::Array>::Cast(result); |
| CHECK_EQ(16u, results->Length()); |
| for (int i = 0; i < 16; i += 4) { |
| CHECK(v8_str("env2_x")->Equals(results->Get(i + 0))); |
| CHECK(v8_str("env1_x")->Equals(results->Get(i + 1))); |
| CHECK(v8_str("env3_x")->Equals(results->Get(i + 2))); |
| CHECK(v8_str("env2_x")->Equals(results->Get(i + 3))); |
| } |
| } |
| |
| |
| static bool allowed_access = false; |
| static bool AccessBlocker(Local<v8::Object> global, Local<Value> name, |
| v8::AccessType type, Local<Value> data) { |
| return CcTest::isolate()->GetCurrentContext()->Global()->Equals(global) || |
| allowed_access; |
| } |
| |
| |
| static int g_echo_value = -1; |
| |
| |
| static void EchoGetter( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(v8_num(g_echo_value)); |
| } |
| |
| |
| static void EchoSetter(Local<String> name, |
| Local<Value> value, |
| const v8::PropertyCallbackInfo<void>&) { |
| if (value->IsNumber()) |
| g_echo_value = value->Int32Value(); |
| } |
| |
| |
| static void UnreachableGetter( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(false); // This function should not be called.. |
| } |
| |
| |
| static void UnreachableSetter(Local<String>, |
| Local<Value>, |
| const v8::PropertyCallbackInfo<void>&) { |
| CHECK(false); // This function should nto be called. |
| } |
| |
| |
| static void UnreachableFunction( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| CHECK(false); // This function should not be called.. |
| } |
| |
| |
| TEST(AccessControl) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Handle<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| |
| global_template->SetAccessCheckCallbacks(AccessBlocker, NULL); |
| |
| // Add an accessor accessible by cross-domain JS code. |
| global_template->SetAccessor( |
| v8_str("accessible_prop"), |
| EchoGetter, EchoSetter, |
| v8::Handle<Value>(), |
| v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| |
| |
| // Add an accessor that is not accessible by cross-domain JS code. |
| global_template->SetAccessor(v8_str("blocked_prop"), |
| UnreachableGetter, UnreachableSetter, |
| v8::Handle<Value>(), |
| v8::DEFAULT); |
| |
| global_template->SetAccessorProperty( |
| v8_str("blocked_js_prop"), |
| v8::FunctionTemplate::New(isolate, UnreachableFunction), |
| v8::FunctionTemplate::New(isolate, UnreachableFunction), |
| v8::None, |
| v8::DEFAULT); |
| |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, NULL, global_template); |
| context0->Enter(); |
| |
| v8::Handle<v8::Object> global0 = context0->Global(); |
| |
| // Define a property with JS getter and setter. |
| CompileRun( |
| "function getter() { return 'getter'; };\n" |
| "function setter() { return 'setter'; }\n" |
| "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})"); |
| |
| Local<Value> getter = global0->Get(v8_str("getter")); |
| Local<Value> setter = global0->Get(v8_str("setter")); |
| |
| // And define normal element. |
| global0->Set(239, v8_str("239")); |
| |
| // Define an element with JS getter and setter. |
| CompileRun( |
| "function el_getter() { return 'el_getter'; };\n" |
| "function el_setter() { return 'el_setter'; };\n" |
| "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});"); |
| |
| Local<Value> el_getter = global0->Get(v8_str("el_getter")); |
| Local<Value> el_setter = global0->Get(v8_str("el_setter")); |
| |
| v8::HandleScope scope1(isolate); |
| |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("other"), global0); |
| |
| // Access blocked property. |
| CompileRun("other.blocked_prop = 1"); |
| |
| CHECK(CompileRun("other.blocked_prop").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')") |
| .IsEmpty()); |
| CHECK( |
| CompileRun("propertyIsEnumerable.call(other, 'blocked_prop')").IsEmpty()); |
| |
| // Access blocked element. |
| CHECK(CompileRun("other[239] = 1").IsEmpty()); |
| |
| CHECK(CompileRun("other[239]").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '239')").IsEmpty()); |
| CHECK(CompileRun("propertyIsEnumerable.call(other, '239')").IsEmpty()); |
| |
| allowed_access = true; |
| // Now we can enumerate the property. |
| ExpectTrue("propertyIsEnumerable.call(other, '239')"); |
| allowed_access = false; |
| |
| // Access a property with JS accessor. |
| CHECK(CompileRun("other.js_accessor_p = 2").IsEmpty()); |
| |
| CHECK(CompileRun("other.js_accessor_p").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'js_accessor_p')") |
| .IsEmpty()); |
| |
| allowed_access = true; |
| |
| ExpectString("other.js_accessor_p", "getter"); |
| ExpectObject( |
| "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); |
| ExpectObject( |
| "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); |
| ExpectUndefined( |
| "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); |
| |
| allowed_access = false; |
| |
| // Access an element with JS accessor. |
| CHECK(CompileRun("other[42] = 2").IsEmpty()); |
| |
| CHECK(CompileRun("other[42]").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '42')").IsEmpty()); |
| |
| allowed_access = true; |
| |
| ExpectString("other[42]", "el_getter"); |
| ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); |
| ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); |
| ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); |
| |
| allowed_access = false; |
| |
| v8::Handle<Value> value; |
| |
| // Access accessible property |
| value = CompileRun("other.accessible_prop = 3"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(3, value->Int32Value()); |
| CHECK_EQ(3, g_echo_value); |
| |
| value = CompileRun("other.accessible_prop"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(3, value->Int32Value()); |
| |
| value = CompileRun( |
| "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(3, value->Int32Value()); |
| |
| value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')"); |
| CHECK(value->IsTrue()); |
| |
| // Enumeration doesn't enumerate accessors from inaccessible objects in |
| // the prototype chain even if the accessors are in themselves accessible. |
| value = CompileRun( |
| "(function() {" |
| " var obj = { '__proto__': other };" |
| " try {" |
| " for (var p in obj) {" |
| " if (p == 'accessible_prop' ||" |
| " p == 'blocked_js_prop' ||" |
| " p == 'blocked_js_prop') {" |
| " return false;" |
| " }" |
| " }" |
| " return false;" |
| " } catch (e) {" |
| " return true;" |
| " }" |
| "})()"); |
| CHECK(value->IsTrue()); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| TEST(AccessControlES5) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Handle<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| |
| global_template->SetAccessCheckCallbacks(AccessBlocker, NULL); |
| |
| // Add accessible accessor. |
| global_template->SetAccessor( |
| v8_str("accessible_prop"), |
| EchoGetter, EchoSetter, |
| v8::Handle<Value>(), |
| v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| |
| |
| // Add an accessor that is not accessible by cross-domain JS code. |
| global_template->SetAccessor(v8_str("blocked_prop"), |
| UnreachableGetter, UnreachableSetter, |
| v8::Handle<Value>(), |
| v8::DEFAULT); |
| |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, NULL, global_template); |
| context0->Enter(); |
| |
| v8::Handle<v8::Object> global0 = context0->Global(); |
| |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("other"), global0); |
| |
| // Regression test for issue 1154. |
| CHECK(CompileRun("Object.keys(other)").IsEmpty()); |
| CHECK(CompileRun("other.blocked_prop").IsEmpty()); |
| |
| // Regression test for issue 1027. |
| CompileRun("Object.defineProperty(\n" |
| " other, 'blocked_prop', {configurable: false})"); |
| CHECK(CompileRun("other.blocked_prop").IsEmpty()); |
| CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')") |
| .IsEmpty()); |
| |
| // Regression test for issue 1171. |
| ExpectTrue("Object.isExtensible(other)"); |
| CompileRun("Object.preventExtensions(other)"); |
| ExpectTrue("Object.isExtensible(other)"); |
| |
| // Object.seal and Object.freeze. |
| CompileRun("Object.freeze(other)"); |
| ExpectTrue("Object.isExtensible(other)"); |
| |
| CompileRun("Object.seal(other)"); |
| ExpectTrue("Object.isExtensible(other)"); |
| |
| // Regression test for issue 1250. |
| // Make sure that we can set the accessible accessors value using normal |
| // assignment. |
| CompileRun("other.accessible_prop = 42"); |
| CHECK_EQ(42, g_echo_value); |
| |
| v8::Handle<Value> value; |
| CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})"); |
| value = CompileRun("other.accessible_prop == 42"); |
| CHECK(value->IsTrue()); |
| } |
| |
| |
| static bool AccessAlwaysBlocked(Local<v8::Object> global, Local<Value> name, |
| v8::AccessType type, Local<Value> data) { |
| i::PrintF("Access blocked.\n"); |
| return false; |
| } |
| |
| |
| THREADED_TEST(AccessControlGetOwnPropertyNames) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj_template = |
| v8::ObjectTemplate::New(isolate); |
| |
| obj_template->Set(v8_str("x"), v8::Integer::New(isolate, 42)); |
| obj_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL); |
| |
| // Create an environment |
| v8::Local<Context> context0 = Context::New(isolate, NULL, obj_template); |
| context0->Enter(); |
| |
| v8::Handle<v8::Object> global0 = context0->Global(); |
| |
| v8::HandleScope scope1(CcTest::isolate()); |
| |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("other"), global0); |
| global1->Set(v8_str("object"), obj_template->NewInstance()); |
| |
| v8::Handle<Value> value; |
| |
| // Attempt to get the property names of the other global object and |
| // of an object that requires access checks. Accessing the other |
| // global object should be blocked by access checks on the global |
| // proxy object. Accessing the object that requires access checks |
| // is blocked by the access checks on the object itself. |
| value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); |
| CHECK(value.IsEmpty()); |
| |
| value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); |
| CHECK(value.IsEmpty()); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| TEST(SuperAccessControl) { |
| i::FLAG_allow_natives_syntax = true; |
| i::FLAG_harmony_classes = true; |
| i::FLAG_harmony_object_literals = true; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj_template = |
| v8::ObjectTemplate::New(isolate); |
| obj_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL); |
| LocalContext env; |
| env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance()); |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "var f = { m() { return super.hasOwnProperty; } }.m;" |
| "var m = %ToMethod(f, prohibited);" |
| "m();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "var f = {m() { return super[42]; } }.m;" |
| "var m = %ToMethod(f, prohibited);" |
| "m();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "var f = {m() { super.hasOwnProperty = function () {}; } }.m;" |
| "var m = %ToMethod(f, prohibited);" |
| "m();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "Object.defineProperty(Object.prototype, 'x', { set : function(){}});" |
| "var f = {" |
| " m() { " |
| " 'use strict';" |
| " super.x = function () {};" |
| " }" |
| "}.m;" |
| "var m = %ToMethod(f, prohibited);" |
| "m();"); |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| |
| TEST(Regress470113) { |
| i::FLAG_harmony_classes = true; |
| i::FLAG_harmony_object_literals = true; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj_template = |
| v8::ObjectTemplate::New(isolate); |
| obj_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL); |
| LocalContext env; |
| env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance()); |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "'use strict';\n" |
| "class C extends Object {\n" |
| " m() { super.powned = 'Powned!'; }\n" |
| "}\n" |
| "let c = new C();\n" |
| "c.m.call(prohibited)"); |
| |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| |
| static void ConstTenGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetReturnValue().Set(v8_num(10)); |
| } |
| |
| |
| THREADED_TEST(CrossDomainAccessors) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Handle<v8::FunctionTemplate> func_template = |
| v8::FunctionTemplate::New(isolate); |
| |
| v8::Handle<v8::ObjectTemplate> global_template = |
| func_template->InstanceTemplate(); |
| |
| v8::Handle<v8::ObjectTemplate> proto_template = |
| func_template->PrototypeTemplate(); |
| |
| // Add an accessor to proto that's accessible by cross-domain JS code. |
| proto_template->SetAccessor(v8_str("accessible"), |
| ConstTenGetter, 0, |
| v8::Handle<Value>(), |
| v8::ALL_CAN_READ); |
| |
| // Add an accessor that is not accessible by cross-domain JS code. |
| global_template->SetAccessor(v8_str("unreachable"), |
| UnreachableGetter, 0, |
| v8::Handle<Value>(), |
| v8::DEFAULT); |
| |
| v8::Local<Context> context0 = Context::New(isolate, NULL, global_template); |
| context0->Enter(); |
| |
| Local<v8::Object> global = context0->Global(); |
| // Add a normal property that shadows 'accessible' |
| global->Set(v8_str("accessible"), v8_num(11)); |
| |
| // Enter a new context. |
| v8::HandleScope scope1(CcTest::isolate()); |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("other"), global); |
| |
| // Should return 10, instead of 11 |
| v8::Handle<Value> value = v8_compile("other.accessible")->Run(); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(10, value->Int32Value()); |
| |
| value = v8_compile("other.unreachable")->Run(); |
| CHECK(value.IsEmpty()); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| static int access_count = 0; |
| |
| static bool AccessCounter(Local<v8::Object> global, Local<Value> name, |
| v8::AccessType type, Local<Value> data) { |
| access_count++; |
| return true; |
| } |
| |
| |
| // This one is too easily disturbed by other tests. |
| TEST(AccessControlIC) { |
| access_count = 0; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| // Create an environment. |
| v8::Local<Context> context0 = Context::New(isolate); |
| context0->Enter(); |
| |
| // Create an object that requires access-check functions to be |
| // called for cross-domain access. |
| v8::Handle<v8::ObjectTemplate> object_template = |
| v8::ObjectTemplate::New(isolate); |
| object_template->SetAccessCheckCallbacks(AccessCounter, NULL); |
| Local<v8::Object> object = object_template->NewInstance(); |
| |
| v8::HandleScope scope1(isolate); |
| |
| // Create another environment. |
| v8::Local<Context> context1 = Context::New(isolate); |
| context1->Enter(); |
| |
| // Make easy access to the object from the other environment. |
| v8::Handle<v8::Object> global1 = context1->Global(); |
| global1->Set(v8_str("obj"), object); |
| |
| v8::Handle<Value> value; |
| |
| // Check that the named access-control function is called every time. |
| CompileRun("function testProp(obj) {" |
| " for (var i = 0; i < 10; i++) obj.prop = 1;" |
| " for (var j = 0; j < 10; j++) obj.prop;" |
| " return obj.prop" |
| "}"); |
| value = CompileRun("testProp(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value()); |
| CHECK_EQ(21, access_count); |
| |
| // Check that the named access-control function is called every time. |
| CompileRun("var p = 'prop';" |
| "function testKeyed(obj) {" |
| " for (var i = 0; i < 10; i++) obj[p] = 1;" |
| " for (var j = 0; j < 10; j++) obj[p];" |
| " return obj[p];" |
| "}"); |
| // Use obj which requires access checks. No inline caching is used |
| // in that case. |
| value = CompileRun("testKeyed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value()); |
| CHECK_EQ(42, access_count); |
| // Force the inline caches into generic state and try again. |
| CompileRun("testKeyed({ a: 0 })"); |
| CompileRun("testKeyed({ b: 0 })"); |
| value = CompileRun("testKeyed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value()); |
| CHECK_EQ(63, access_count); |
| |
| // Check that the indexed access-control function is called every time. |
| access_count = 0; |
| |
| CompileRun("function testIndexed(obj) {" |
| " for (var i = 0; i < 10; i++) obj[0] = 1;" |
| " for (var j = 0; j < 10; j++) obj[0];" |
| " return obj[0]" |
| "}"); |
| value = CompileRun("testIndexed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value()); |
| CHECK_EQ(21, access_count); |
| // Force the inline caches into generic state. |
| CompileRun("testIndexed(new Array(1))"); |
| // Test that the indexed access check is called. |
| value = CompileRun("testIndexed(obj)"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value()); |
| CHECK_EQ(42, access_count); |
| |
| access_count = 0; |
| // Check that the named access check is called when invoking |
| // functions on an object that requires access checks. |
| CompileRun("obj.f = function() {}"); |
| CompileRun("function testCallNormal(obj) {" |
| " for (var i = 0; i < 10; i++) obj.f();" |
| "}"); |
| CompileRun("testCallNormal(obj)"); |
| printf("%i\n", access_count); |
| CHECK_EQ(11, access_count); |
| |
| // Force obj into slow case. |
| value = CompileRun("delete obj.prop"); |
| CHECK(value->BooleanValue()); |
| // Force inline caches into dictionary probing mode. |
| CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); |
| // Test that the named access check is called. |
| value = CompileRun("testProp(obj);"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(1, value->Int32Value()); |
| CHECK_EQ(33, access_count); |
| |
| // Force the call inline cache into dictionary probing mode. |
| CompileRun("o.f = function() {}; testCallNormal(o)"); |
| // Test that the named access check is still called for each |
| // invocation of the function. |
| value = CompileRun("testCallNormal(obj)"); |
| CHECK_EQ(43, access_count); |
| |
| context1->Exit(); |
| context0->Exit(); |
| } |
| |
| |
| THREADED_TEST(Version) { v8::V8::GetVersion(); } |
| |
| |
| static void InstanceFunctionCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(12)); |
| } |
| |
| |
| THREADED_TEST(InstanceProperties) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance = t->InstanceTemplate(); |
| |
| instance->Set(v8_str("x"), v8_num(42)); |
| instance->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, InstanceFunctionCallback)); |
| |
| Local<Value> o = t->GetFunction()->NewInstance(); |
| |
| context->Global()->Set(v8_str("i"), o); |
| Local<Value> value = CompileRun("i.x"); |
| CHECK_EQ(42, value->Int32Value()); |
| |
| value = CompileRun("i.f()"); |
| CHECK_EQ(12, value->Int32Value()); |
| } |
| |
| |
| static void GlobalObjectInstancePropertiesGet( |
| Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) { |
| ApiTestFuzzer::Fuzz(); |
| } |
| |
| |
| THREADED_TEST(GlobalObjectInstanceProperties) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<Value> global_object; |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| t->InstanceTemplate()->SetHandler( |
| v8::NamedPropertyHandlerConfiguration(GlobalObjectInstancePropertiesGet)); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->Set(v8_str("x"), v8_num(42)); |
| instance_template->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, |
| InstanceFunctionCallback)); |
| |
| // The script to check how Crankshaft compiles missing global function |
| // invocations. function g is not defined and should throw on call. |
| const char* script = |
| "function wrapper(call) {" |
| " var x = 0, y = 1;" |
| " for (var i = 0; i < 1000; i++) {" |
| " x += i * 100;" |
| " y += i * 100;" |
| " }" |
| " if (call) g();" |
| "}" |
| "for (var i = 0; i < 17; i++) wrapper(false);" |
| "var thrown = 0;" |
| "try { wrapper(true); } catch (e) { thrown = 1; };" |
| "thrown"; |
| |
| { |
| LocalContext env(NULL, instance_template); |
| // Hold on to the global object so it can be used again in another |
| // environment initialization. |
| global_object = env->Global(); |
| |
| Local<Value> value = CompileRun("x"); |
| CHECK_EQ(42, value->Int32Value()); |
| value = CompileRun("f()"); |
| CHECK_EQ(12, value->Int32Value()); |
| value = CompileRun(script); |
| CHECK_EQ(1, value->Int32Value()); |
| } |
| |
| { |
| // Create new environment reusing the global object. |
| LocalContext env(NULL, instance_template, global_object); |
| Local<Value> value = CompileRun("x"); |
| CHECK_EQ(42, value->Int32Value()); |
| value = CompileRun("f()"); |
| CHECK_EQ(12, value->Int32Value()); |
| value = CompileRun(script); |
| CHECK_EQ(1, value->Int32Value()); |
| } |
| } |
| |
| |
| THREADED_TEST(CallKnownGlobalReceiver) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<Value> global_object; |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| |
| // The script to check that we leave global object not |
| // global object proxy on stack when we deoptimize from inside |
| // arguments evaluation. |
| // To provoke error we need to both force deoptimization |
| // from arguments evaluation and to force CallIC to take |
| // CallIC_Miss code path that can't cope with global proxy. |
| const char* script = |
| "function bar(x, y) { try { } finally { } }" |
| "function baz(x) { try { } finally { } }" |
| "function bom(x) { try { } finally { } }" |
| "function foo(x) { bar([x], bom(2)); }" |
| "for (var i = 0; i < 10000; i++) foo(1);" |
| "foo"; |
| |
| Local<Value> foo; |
| { |
| LocalContext env(NULL, instance_template); |
| // Hold on to the global object so it can be used again in another |
| // environment initialization. |
| global_object = env->Global(); |
| foo = CompileRun(script); |
| } |
| |
| { |
| // Create new environment reusing the global object. |
| LocalContext env(NULL, instance_template, global_object); |
| env->Global()->Set(v8_str("foo"), foo); |
| CompileRun("foo()"); |
| } |
| } |
| |
| |
| static void ShadowFunctionCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(42)); |
| } |
| |
| |
| static int shadow_y; |
| static int shadow_y_setter_call_count; |
| static int shadow_y_getter_call_count; |
| |
| |
| static void ShadowYSetter(Local<String>, |
| Local<Value>, |
| const v8::PropertyCallbackInfo<void>&) { |
| shadow_y_setter_call_count++; |
| shadow_y = 42; |
| } |
| |
| |
| static void ShadowYGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| shadow_y_getter_call_count++; |
| info.GetReturnValue().Set(v8_num(shadow_y)); |
| } |
| |
| |
| static void ShadowIndexedGet(uint32_t index, |
| const v8::PropertyCallbackInfo<v8::Value>&) { |
| } |
| |
| |
| static void ShadowNamedGet(Local<Name> key, |
| const v8::PropertyCallbackInfo<v8::Value>&) {} |
| |
| |
| THREADED_TEST(ShadowObject) { |
| shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| LocalContext context(NULL, global_template); |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| t->InstanceTemplate()->SetHandler( |
| v8::NamedPropertyHandlerConfiguration(ShadowNamedGet)); |
| t->InstanceTemplate()->SetHandler( |
| v8::IndexedPropertyHandlerConfiguration(ShadowIndexedGet)); |
| Local<ObjectTemplate> proto = t->PrototypeTemplate(); |
| Local<ObjectTemplate> instance = t->InstanceTemplate(); |
| |
| proto->Set(v8_str("f"), |
| v8::FunctionTemplate::New(isolate, |
| ShadowFunctionCallback, |
| Local<Value>())); |
| proto->Set(v8_str("x"), v8_num(12)); |
| |
| instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); |
| |
| Local<Value> o = t->GetFunction()->NewInstance(); |
| context->Global()->Set(v8_str("__proto__"), o); |
| |
| Local<Value> value = |
| CompileRun("this.propertyIsEnumerable(0)"); |
| CHECK(value->IsBoolean()); |
| CHECK(!value->BooleanValue()); |
| |
| value = CompileRun("x"); |
| CHECK_EQ(12, value->Int32Value()); |
| |
| value = CompileRun("f()"); |
| CHECK_EQ(42, value->Int32Value()); |
| |
| CompileRun("y = 43"); |
| CHECK_EQ(1, shadow_y_setter_call_count); |
| value = CompileRun("y"); |
| CHECK_EQ(1, shadow_y_getter_call_count); |
| CHECK_EQ(42, value->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(HiddenPrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate); |
| t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->SetHiddenPrototype(true); |
| t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->SetHiddenPrototype(true); |
| t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); |
| Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); |
| |
| Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); |
| Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); |
| Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); |
| Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); |
| |
| // Setting the prototype on an object skips hidden prototypes. |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| o0->Set(v8_str("__proto__"), o1); |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); |
| o0->Set(v8_str("__proto__"), o2); |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); |
| CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); |
| o0->Set(v8_str("__proto__"), o3); |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); |
| CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); |
| CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); |
| |
| // Getting the prototype of o0 should get the first visible one |
| // which is o3. Therefore, z should not be defined on the prototype |
| // object. |
| Local<Value> proto = o0->Get(v8_str("__proto__")); |
| CHECK(proto->IsObject()); |
| CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); |
| } |
| |
| |
| THREADED_TEST(HiddenPrototypeSet) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> ot = v8::FunctionTemplate::New(isolate); |
| Local<v8::FunctionTemplate> ht = v8::FunctionTemplate::New(isolate); |
| ht->SetHiddenPrototype(true); |
| Local<v8::FunctionTemplate> pt = v8::FunctionTemplate::New(isolate); |
| ht->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); |
| |
| Local<v8::Object> o = ot->GetFunction()->NewInstance(); |
| Local<v8::Object> h = ht->GetFunction()->NewInstance(); |
| Local<v8::Object> p = pt->GetFunction()->NewInstance(); |
| o->Set(v8_str("__proto__"), h); |
| h->Set(v8_str("__proto__"), p); |
| |
| // Setting a property that exists on the hidden prototype goes there. |
| o->Set(v8_str("x"), v8_num(7)); |
| CHECK_EQ(7, o->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(7, h->Get(v8_str("x"))->Int32Value()); |
| CHECK(p->Get(v8_str("x"))->IsUndefined()); |
| |
| // Setting a new property should not be forwarded to the hidden prototype. |
| o->Set(v8_str("y"), v8_num(6)); |
| CHECK_EQ(6, o->Get(v8_str("y"))->Int32Value()); |
| CHECK(h->Get(v8_str("y"))->IsUndefined()); |
| CHECK(p->Get(v8_str("y"))->IsUndefined()); |
| |
| // Setting a property that only exists on a prototype of the hidden prototype |
| // is treated normally again. |
| p->Set(v8_str("z"), v8_num(8)); |
| CHECK_EQ(8, o->Get(v8_str("z"))->Int32Value()); |
| CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value()); |
| CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value()); |
| o->Set(v8_str("z"), v8_num(9)); |
| CHECK_EQ(9, o->Get(v8_str("z"))->Int32Value()); |
| CHECK_EQ(8, h->Get(v8_str("z"))->Int32Value()); |
| CHECK_EQ(8, p->Get(v8_str("z"))->Int32Value()); |
| } |
| |
| |
| // Regression test for issue 2457. |
| THREADED_TEST(HiddenPrototypeIdentityHash) { |
| LocalContext context; |
| v8::HandleScope handle_scope(context->GetIsolate()); |
| |
| Handle<FunctionTemplate> t = FunctionTemplate::New(context->GetIsolate()); |
| t->SetHiddenPrototype(true); |
| t->InstanceTemplate()->Set(v8_str("foo"), v8_num(75)); |
| Handle<Object> p = t->GetFunction()->NewInstance(); |
| Handle<Object> o = Object::New(context->GetIsolate()); |
| o->SetPrototype(p); |
| |
| int hash = o->GetIdentityHash(); |
| USE(hash); |
| o->Set(v8_str("foo"), v8_num(42)); |
| DCHECK_EQ(hash, o->GetIdentityHash()); |
| } |
| |
| |
| THREADED_TEST(SetPrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate); |
| t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->SetHiddenPrototype(true); |
| t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->SetHiddenPrototype(true); |
| t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); |
| Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); |
| |
| Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); |
| Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); |
| Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); |
| Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); |
| |
| // Setting the prototype on an object does not skip hidden prototypes. |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK(o0->SetPrototype(o1)); |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); |
| CHECK(o1->SetPrototype(o2)); |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); |
| CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); |
| CHECK(o2->SetPrototype(o3)); |
| CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); |
| CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); |
| CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); |
| CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); |
| |
| // Getting the prototype of o0 should get the first visible one |
| // which is o3. Therefore, z should not be defined on the prototype |
| // object. |
| Local<Value> proto = o0->Get(v8_str("__proto__")); |
| CHECK(proto->IsObject()); |
| CHECK(proto.As<v8::Object>()->Equals(o3)); |
| |
| // However, Object::GetPrototype ignores hidden prototype. |
| Local<Value> proto0 = o0->GetPrototype(); |
| CHECK(proto0->IsObject()); |
| CHECK(proto0.As<v8::Object>()->Equals(o1)); |
| |
| Local<Value> proto1 = o1->GetPrototype(); |
| CHECK(proto1->IsObject()); |
| CHECK(proto1.As<v8::Object>()->Equals(o2)); |
| |
| Local<Value> proto2 = o2->GetPrototype(); |
| CHECK(proto2->IsObject()); |
| CHECK(proto2.As<v8::Object>()->Equals(o3)); |
| } |
| |
| |
| // Getting property names of an object with a prototype chain that |
| // triggers dictionary elements in GetOwnPropertyNames() shouldn't |
| // crash the runtime. |
| THREADED_TEST(Regress91517) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->SetHiddenPrototype(true); |
| t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1)); |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->SetHiddenPrototype(true); |
| t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2)); |
| t2->InstanceTemplate()->Set(v8_str("objects"), v8::Object::New(isolate)); |
| t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2)); |
| Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| t3->SetHiddenPrototype(true); |
| t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3)); |
| Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(isolate); |
| t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4)); |
| |
| // Force dictionary-based properties. |
| i::ScopedVector<char> name_buf(1024); |
| for (int i = 1; i <= 1000; i++) { |
| i::SNPrintF(name_buf, "sdf%d", i); |
| t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); |
| } |
| |
| Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); |
| Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); |
| Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); |
| Local<v8::Object> o4 = t4->GetFunction()->NewInstance(); |
| |
| // Create prototype chain of hidden prototypes. |
| CHECK(o4->SetPrototype(o3)); |
| CHECK(o3->SetPrototype(o2)); |
| CHECK(o2->SetPrototype(o1)); |
| |
| // Call the runtime version of GetOwnPropertyNames() on the natively |
| // created object through JavaScript. |
| context->Global()->Set(v8_str("obj"), o4); |
| // PROPERTY_ATTRIBUTES_NONE = 0 |
| CompileRun("var names = %GetOwnPropertyNames(obj, 0);"); |
| |
| ExpectInt32("names.length", 1006); |
| ExpectTrue("names.indexOf(\"baz\") >= 0"); |
| ExpectTrue("names.indexOf(\"boo\") >= 0"); |
| ExpectTrue("names.indexOf(\"foo\") >= 0"); |
| ExpectTrue("names.indexOf(\"fuz1\") >= 0"); |
| ExpectTrue("names.indexOf(\"fuz2\") >= 0"); |
| ExpectFalse("names[1005] == undefined"); |
| } |
| |
| |
| // Getting property names of an object with a hidden and inherited |
| // prototype should not duplicate the accessor properties inherited. |
| THREADED_TEST(Regress269562) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext context; |
| v8::HandleScope handle_scope(context->GetIsolate()); |
| |
| Local<v8::FunctionTemplate> t1 = |
| v8::FunctionTemplate::New(context->GetIsolate()); |
| t1->SetHiddenPrototype(true); |
| |
| Local<v8::ObjectTemplate> i1 = t1->InstanceTemplate(); |
| i1->SetAccessor(v8_str("foo"), |
| SimpleAccessorGetter, SimpleAccessorSetter); |
| i1->SetAccessor(v8_str("bar"), |
| SimpleAccessorGetter, SimpleAccessorSetter); |
| i1->SetAccessor(v8_str("baz"), |
| SimpleAccessorGetter, SimpleAccessorSetter); |
| i1->Set(v8_str("n1"), v8_num(1)); |
| i1->Set(v8_str("n2"), v8_num(2)); |
| |
| Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); |
| Local<v8::FunctionTemplate> t2 = |
| v8::FunctionTemplate::New(context->GetIsolate()); |
| t2->SetHiddenPrototype(true); |
| |
| // Inherit from t1 and mark prototype as hidden. |
| t2->Inherit(t1); |
| t2->InstanceTemplate()->Set(v8_str("mine"), v8_num(4)); |
| |
| Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); |
| CHECK(o2->SetPrototype(o1)); |
| |
| v8::Local<v8::Symbol> sym = |
| v8::Symbol::New(context->GetIsolate(), v8_str("s1")); |
| o1->Set(sym, v8_num(3)); |
| o1->SetHiddenValue( |
| v8_str("h1"), v8::Integer::New(context->GetIsolate(), 2013)); |
| |
| // Call the runtime version of GetOwnPropertyNames() on |
| // the natively created object through JavaScript. |
| context->Global()->Set(v8_str("obj"), o2); |
| context->Global()->Set(v8_str("sym"), sym); |
| // PROPERTY_ATTRIBUTES_NONE = 0 |
| CompileRun("var names = %GetOwnPropertyNames(obj, 0);"); |
| |
| ExpectInt32("names.length", 7); |
| ExpectTrue("names.indexOf(\"foo\") >= 0"); |
| ExpectTrue("names.indexOf(\"bar\") >= 0"); |
| ExpectTrue("names.indexOf(\"baz\") >= 0"); |
| ExpectTrue("names.indexOf(\"n1\") >= 0"); |
| ExpectTrue("names.indexOf(\"n2\") >= 0"); |
| ExpectTrue("names.indexOf(sym) >= 0"); |
| ExpectTrue("names.indexOf(\"mine\") >= 0"); |
| } |
| |
| |
| THREADED_TEST(FunctionReadOnlyPrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42)); |
| t1->ReadOnlyPrototype(); |
| context->Global()->Set(v8_str("func1"), t1->GetFunction()); |
| // Configured value of ReadOnly flag. |
| CHECK(CompileRun( |
| "(function() {" |
| " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" |
| " return (descriptor['writable'] == false);" |
| "})()")->BooleanValue()); |
| CHECK_EQ(42, CompileRun("func1.prototype.x")->Int32Value()); |
| CHECK_EQ(42, |
| CompileRun("func1.prototype = {}; func1.prototype.x")->Int32Value()); |
| |
| Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(isolate, 42)); |
| context->Global()->Set(v8_str("func2"), t2->GetFunction()); |
| // Default value of ReadOnly flag. |
| CHECK(CompileRun( |
| "(function() {" |
| " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" |
| " return (descriptor['writable'] == true);" |
| "})()")->BooleanValue()); |
| CHECK_EQ(42, CompileRun("func2.prototype.x")->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(SetPrototypeThrows) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| |
| Local<v8::Object> o0 = t->GetFunction()->NewInstance(); |
| Local<v8::Object> o1 = t->GetFunction()->NewInstance(); |
| |
| CHECK(o0->SetPrototype(o1)); |
| // If setting the prototype leads to the cycle, SetPrototype should |
| // return false and keep VM in sane state. |
| v8::TryCatch try_catch(isolate); |
| CHECK(!o1->SetPrototype(o0)); |
| CHECK(!try_catch.HasCaught()); |
| DCHECK(!CcTest::i_isolate()->has_pending_exception()); |
| |
| CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(FunctionRemovePrototype) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| t1->RemovePrototype(); |
| Local<v8::Function> fun = t1->GetFunction(); |
| context->Global()->Set(v8_str("fun"), fun); |
| CHECK(!CompileRun("'prototype' in fun")->BooleanValue()); |
| |
| v8::TryCatch try_catch(isolate); |
| CompileRun("new fun()"); |
| CHECK(try_catch.HasCaught()); |
| |
| try_catch.Reset(); |
| fun->NewInstance(); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| THREADED_TEST(GetterSetterExceptions) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| CompileRun( |
| "function Foo() { };" |
| "function Throw() { throw 5; };" |
| "var x = { };" |
| "x.__defineSetter__('set', Throw);" |
| "x.__defineGetter__('get', Throw);"); |
| Local<v8::Object> x = |
| Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); |
| v8::TryCatch try_catch(isolate); |
| x->Set(v8_str("set"), v8::Integer::New(isolate, 8)); |
| x->Get(v8_str("get")); |
| x->Set(v8_str("set"), v8::Integer::New(isolate, 8)); |
| x->Get(v8_str("get")); |
| x->Set(v8_str("set"), v8::Integer::New(isolate, 8)); |
| x->Get(v8_str("get")); |
| x->Set(v8_str("set"), v8::Integer::New(isolate, 8)); |
| x->Get(v8_str("get")); |
| } |
| |
| |
| THREADED_TEST(Constructor) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("Fun")); |
| Local<Function> cons = templ->GetFunction(); |
| context->Global()->Set(v8_str("Fun"), cons); |
| Local<v8::Object> inst = cons->NewInstance(); |
| i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst)); |
| CHECK(obj->IsJSObject()); |
| Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); |
| CHECK(value->BooleanValue()); |
| } |
| |
| |
| static void ConstructorCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| Local<Object> This; |
| |
| if (args.IsConstructCall()) { |
| Local<Object> Holder = args.Holder(); |
| This = Object::New(args.GetIsolate()); |
| Local<Value> proto = Holder->GetPrototype(); |
| if (proto->IsObject()) { |
| This->SetPrototype(proto); |
| } |
| } else { |
| This = args.This(); |
| } |
| |
| This->Set(v8_str("a"), args[0]); |
| args.GetReturnValue().Set(This); |
| } |
| |
| |
| static void FakeConstructorCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(args[0]); |
| } |
| |
| |
| THREADED_TEST(ConstructorForObject) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(ConstructorCallback); |
| Local<Object> instance = instance_template->NewInstance(); |
| context->Global()->Set(v8_str("obj"), instance); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| // Call the Object's constructor with a 32-bit signed integer. |
| value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsInt32()); |
| CHECK_EQ(28, value->Int32Value()); |
| |
| Local<Value> args1[] = {v8_num(28)}; |
| Local<Value> value_obj1 = instance->CallAsConstructor(1, args1); |
| CHECK(value_obj1->IsObject()); |
| Local<Object> object1 = Local<Object>::Cast(value_obj1); |
| value = object1->Get(v8_str("a")); |
| CHECK(value->IsInt32()); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(28, value->Int32Value()); |
| |
| // Call the Object's constructor with a String. |
| value = |
| CompileRun("(function() { var o = new obj('tipli'); return o.a; })()"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsString()); |
| String::Utf8Value string_value1(value->ToString(isolate)); |
| CHECK_EQ(0, strcmp("tipli", *string_value1)); |
| |
| Local<Value> args2[] = {v8_str("tipli")}; |
| Local<Value> value_obj2 = instance->CallAsConstructor(1, args2); |
| CHECK(value_obj2->IsObject()); |
| Local<Object> object2 = Local<Object>::Cast(value_obj2); |
| value = object2->Get(v8_str("a")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsString()); |
| String::Utf8Value string_value2(value->ToString(isolate)); |
| CHECK_EQ(0, strcmp("tipli", *string_value2)); |
| |
| // Call the Object's constructor with a Boolean. |
| value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsBoolean()); |
| CHECK_EQ(true, value->BooleanValue()); |
| |
| Handle<Value> args3[] = {v8::True(isolate)}; |
| Local<Value> value_obj3 = instance->CallAsConstructor(1, args3); |
| CHECK(value_obj3->IsObject()); |
| Local<Object> object3 = Local<Object>::Cast(value_obj3); |
| value = object3->Get(v8_str("a")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsBoolean()); |
| CHECK_EQ(true, value->BooleanValue()); |
| |
| // Call the Object's constructor with undefined. |
| Handle<Value> args4[] = {v8::Undefined(isolate)}; |
| Local<Value> value_obj4 = instance->CallAsConstructor(1, args4); |
| CHECK(value_obj4->IsObject()); |
| Local<Object> object4 = Local<Object>::Cast(value_obj4); |
| value = object4->Get(v8_str("a")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsUndefined()); |
| |
| // Call the Object's constructor with null. |
| Handle<Value> args5[] = {v8::Null(isolate)}; |
| Local<Value> value_obj5 = instance->CallAsConstructor(1, args5); |
| CHECK(value_obj5->IsObject()); |
| Local<Object> object5 = Local<Object>::Cast(value_obj5); |
| value = object5->Get(v8_str("a")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsNull()); |
| } |
| |
| // Check exception handling when there is no constructor set for the Object. |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| Local<Object> instance = instance_template->NewInstance(); |
| context->Global()->Set(v8_str("obj2"), instance); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| value = CompileRun("new obj2(28)"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("TypeError: obj2 is not a function", *exception_value1)); |
| try_catch.Reset(); |
| |
| Local<Value> args[] = {v8_num(29)}; |
| value = instance->CallAsConstructor(1, args); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(try_catch.Exception()); |
| CHECK_EQ( |
| 0, strcmp("TypeError: #<Object> is not a function", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| // Check the case when constructor throws exception. |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(ThrowValue); |
| Local<Object> instance = instance_template->NewInstance(); |
| context->Global()->Set(v8_str("obj3"), instance); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| value = CompileRun("new obj3(22)"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("22", *exception_value1)); |
| try_catch.Reset(); |
| |
| Local<Value> args[] = {v8_num(23)}; |
| value = instance->CallAsConstructor(1, args); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("23", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| // Check whether constructor returns with an object or non-object. |
| { |
| Local<FunctionTemplate> function_template = |
| FunctionTemplate::New(isolate, FakeConstructorCallback); |
| Local<Function> function = function_template->GetFunction(); |
| Local<Object> instance1 = function; |
| context->Global()->Set(v8_str("obj4"), instance1); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| CHECK(instance1->IsObject()); |
| CHECK(instance1->IsFunction()); |
| |
| value = CompileRun("new obj4(28)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsObject()); |
| |
| Local<Value> args1[] = {v8_num(28)}; |
| value = instance1->CallAsConstructor(1, args1); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(value->IsObject()); |
| |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); |
| Local<Object> instance2 = instance_template->NewInstance(); |
| context->Global()->Set(v8_str("obj5"), instance2); |
| CHECK(!try_catch.HasCaught()); |
| |
| CHECK(instance2->IsObject()); |
| CHECK(!instance2->IsFunction()); |
| |
| value = CompileRun("new obj5(28)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(!value->IsObject()); |
| |
| Local<Value> args2[] = {v8_num(28)}; |
| value = instance2->CallAsConstructor(1, args2); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(!value->IsObject()); |
| } |
| } |
| |
| |
| THREADED_TEST(FunctionDescriptorException) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("Fun")); |
| Local<Function> cons = templ->GetFunction(); |
| context->Global()->Set(v8_str("Fun"), cons); |
| Local<Value> value = CompileRun( |
| "function test() {" |
| " try {" |
| " (new Fun()).blah()" |
| " } catch (e) {" |
| " var str = String(e);" |
| // " if (str.indexOf('TypeError') == -1) return 1;" |
| // " if (str.indexOf('[object Fun]') != -1) return 2;" |
| // " if (str.indexOf('#<Fun>') == -1) return 3;" |
| " return 0;" |
| " }" |
| " return 4;" |
| "}" |
| "test();"); |
| CHECK_EQ(0, value->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(EvalAliasedDynamic) { |
| LocalContext current; |
| v8::HandleScope scope(current->GetIsolate()); |
| |
| // Tests where aliased eval can only be resolved dynamically. |
| Local<Script> script = v8_compile( |
| "function f(x) { " |
| " var foo = 2;" |
| " with (x) { return eval('foo'); }" |
| "}" |
| "foo = 0;" |
| "result1 = f(new Object());" |
| "result2 = f(this);" |
| "var x = new Object();" |
| "x.eval = function(x) { return 1; };" |
| "result3 = f(x);"); |
| script->Run(); |
| CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); |
| CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); |
| CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); |
| |
| v8::TryCatch try_catch(current->GetIsolate()); |
| script = v8_compile( |
| "function f(x) { " |
| " var bar = 2;" |
| " with (x) { return eval('bar'); }" |
| "}" |
| "result4 = f(this)"); |
| script->Run(); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value()); |
| |
| try_catch.Reset(); |
| } |
| |
| |
| THREADED_TEST(CrossEval) { |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext other; |
| LocalContext current; |
| |
| Local<String> token = v8_str("<security token>"); |
| other->SetSecurityToken(token); |
| current->SetSecurityToken(token); |
| |
| // Set up reference from current to other. |
| current->Global()->Set(v8_str("other"), other->Global()); |
| |
| // Check that new variables are introduced in other context. |
| Local<Script> script = v8_compile("other.eval('var foo = 1234')"); |
| script->Run(); |
| Local<Value> foo = other->Global()->Get(v8_str("foo")); |
| CHECK_EQ(1234, foo->Int32Value()); |
| CHECK(!current->Global()->Has(v8_str("foo"))); |
| |
| // Check that writing to non-existing properties introduces them in |
| // the other context. |
| script = v8_compile("other.eval('na = 1234')"); |
| script->Run(); |
| CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); |
| CHECK(!current->Global()->Has(v8_str("na"))); |
| |
| // Check that global variables in current context are not visible in other |
| // context. |
| v8::TryCatch try_catch(CcTest::isolate()); |
| script = v8_compile("var bar = 42; other.eval('bar');"); |
| Local<Value> result = script->Run(); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| // Check that local variables in current context are not visible in other |
| // context. |
| script = v8_compile( |
| "(function() { " |
| " var baz = 87;" |
| " return other.eval('baz');" |
| "})();"); |
| result = script->Run(); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| // Check that global variables in the other environment are visible |
| // when evaluting code. |
| other->Global()->Set(v8_str("bis"), v8_num(1234)); |
| script = v8_compile("other.eval('bis')"); |
| CHECK_EQ(1234, script->Run()->Int32Value()); |
| CHECK(!try_catch.HasCaught()); |
| |
| // Check that the 'this' pointer points to the global object evaluating |
| // code. |
| other->Global()->Set(v8_str("t"), other->Global()); |
| script = v8_compile("other.eval('this == t')"); |
| result = script->Run(); |
| CHECK(result->IsTrue()); |
| CHECK(!try_catch.HasCaught()); |
| |
| // Check that variables introduced in with-statement are not visible in |
| // other context. |
| script = v8_compile("with({x:2}){other.eval('x')}"); |
| result = script->Run(); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| |
| // Check that you cannot use 'eval.call' with another object than the |
| // current global object. |
| script = v8_compile("other.y = 1; eval.call(other, 'y')"); |
| result = script->Run(); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| |
| // Test that calling eval in a context which has been detached from |
| // its global proxy works. |
| THREADED_TEST(EvalInDetachedGlobal) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<Context> context0 = Context::New(isolate); |
| v8::Local<Context> context1 = Context::New(isolate); |
| |
| // Set up function in context0 that uses eval from context0. |
| context0->Enter(); |
| v8::Handle<v8::Value> fun = CompileRun( |
| "var x = 42;" |
| "(function() {" |
| " var e = eval;" |
| " return function(s) { return e(s); }" |
| "})()"); |
| context0->Exit(); |
| |
| // Put the function into context1 and call it before and after |
| // detaching the global. Before detaching, the call succeeds and |
| // after detaching and exception is thrown. |
| context1->Enter(); |
| context1->Global()->Set(v8_str("fun"), fun); |
| v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); |
| CHECK_EQ(42, x_value->Int32Value()); |
| context0->DetachGlobal(); |
| v8::TryCatch catcher(isolate); |
| x_value = CompileRun("fun('x')"); |
| CHECK_EQ(42, x_value->Int32Value()); |
| context1->Exit(); |
| } |
| |
| |
| THREADED_TEST(CrossLazyLoad) { |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext other; |
| LocalContext current; |
| |
| Local<String> token = v8_str("<security token>"); |
| other->SetSecurityToken(token); |
| current->SetSecurityToken(token); |
| |
| // Set up reference from current to other. |
| current->Global()->Set(v8_str("other"), other->Global()); |
| |
| // Trigger lazy loading in other context. |
| Local<Script> script = v8_compile("other.eval('new Date(42)')"); |
| Local<Value> value = script->Run(); |
| CHECK_EQ(42.0, value->NumberValue()); |
| } |
| |
| |
| static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| if (args.IsConstructCall()) { |
| if (args[0]->IsInt32()) { |
| args.GetReturnValue().Set(v8_num(-args[0]->Int32Value())); |
| return; |
| } |
| } |
| |
| args.GetReturnValue().Set(args[0]); |
| } |
| |
| |
| static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| args.GetReturnValue().Set(args.This()); |
| } |
| |
| |
| // Test that a call handler can be set for objects which will allow |
| // non-function objects created through the API to be called as |
| // functions. |
| THREADED_TEST(CallAsFunction) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->SetCallAsFunctionHandler(call_as_function); |
| Local<v8::Object> instance = t->GetFunction()->NewInstance(); |
| context->Global()->Set(v8_str("obj"), instance); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| value = CompileRun("obj(42)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(42, value->Int32Value()); |
| |
| value = CompileRun("(function(o){return o(49)})(obj)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(49, value->Int32Value()); |
| |
| // test special case of call as function |
| value = CompileRun("[obj]['0'](45)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(45, value->Int32Value()); |
| |
| value = CompileRun( |
| "obj.call = Function.prototype.call;" |
| "obj.call(null, 87)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(87, value->Int32Value()); |
| |
| // Regression tests for bug #1116356: Calling call through call/apply |
| // must work for non-function receivers. |
| const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; |
| value = CompileRun(apply_99); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(99, value->Int32Value()); |
| |
| const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; |
| value = CompileRun(call_17); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(17, value->Int32Value()); |
| |
| // Check that the call-as-function handler can be called through |
| // new. |
| value = CompileRun("new obj(43)"); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(-43, value->Int32Value()); |
| |
| // Check that the call-as-function handler can be called through |
| // the API. |
| v8::Handle<Value> args[] = {v8_num(28)}; |
| value = instance->CallAsFunction(instance, 1, args); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(28, value->Int32Value()); |
| } |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template(t->InstanceTemplate()); |
| USE(instance_template); |
| Local<v8::Object> instance = t->GetFunction()->NewInstance(); |
| context->Global()->Set(v8_str("obj2"), instance); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| // Call an object without call-as-function handler through the JS |
| value = CompileRun("obj2(28)"); |
| CHECK(value.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(try_catch.Exception()); |
| // TODO(verwaest): Better message |
| CHECK_EQ(0, strcmp("TypeError: obj2 is not a function", *exception_value1)); |
| try_catch.Reset(); |
| |
| // Call an object without call-as-function handler through the API |
| value = CompileRun("obj2(28)"); |
| v8::Handle<Value> args[] = {v8_num(28)}; |
| value = instance->CallAsFunction(instance, 1, args); |
| CHECK(value.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("TypeError: [object Object] is not a function", |
| *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->SetCallAsFunctionHandler(ThrowValue); |
| Local<v8::Object> instance = t->GetFunction()->NewInstance(); |
| context->Global()->Set(v8_str("obj3"), instance); |
| v8::TryCatch try_catch(isolate); |
| Local<Value> value; |
| CHECK(!try_catch.HasCaught()); |
| |
| // Catch the exception which is thrown by call-as-function handler |
| value = CompileRun("obj3(22)"); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value1(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("22", *exception_value1)); |
| try_catch.Reset(); |
| |
| v8::Handle<Value> args[] = {v8_num(23)}; |
| value = instance->CallAsFunction(instance, 1, args); |
| CHECK(try_catch.HasCaught()); |
| String::Utf8Value exception_value2(try_catch.Exception()); |
| CHECK_EQ(0, strcmp("23", *exception_value2)); |
| try_catch.Reset(); |
| } |
| |
| { |
| Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| instance_template->SetCallAsFunctionHandler(ReturnThis); |
| Local<v8::Object> instance = t->GetFunction()->NewInstance(); |
| |
| Local<v8::Value> a1 = |
| instance->CallAsFunction(v8::Undefined(isolate), 0, NULL); |
| CHECK(a1->StrictEquals(instance)); |
| Local<v8::Value> a2 = instance->CallAsFunction(v8::Null(isolate), 0, NULL); |
| CHECK(a2->StrictEquals(instance)); |
| Local<v8::Value> a3 = instance->CallAsFunction(v8_num(42), 0, NULL); |
| CHECK(a3->StrictEquals(instance)); |
| Local<v8::Value> a4 = instance->CallAsFunction(v8_str("hello"), 0, NULL); |
| CHECK(a4->StrictEquals(instance)); |
| Local<v8::Value> a5 = instance->CallAsFunction(v8::True(isolate), 0, NULL); |
| CHECK(a5->StrictEquals(instance)); |
| } |
| |
| { |
| CompileRun( |
| "function ReturnThisSloppy() {" |
| " return this;" |
| "}" |
| "function ReturnThisStrict() {" |
| " 'use strict';" |
| " return this;" |
| "}"); |
| Local<Function> ReturnThisSloppy = Local<Function>::Cast( |
| context->Global()->Get(v8_str("ReturnThisSloppy"))); |
| Local<Function> ReturnThisStrict = Local<Function>::Cast( |
| context->Global()->Get(v8_str("ReturnThisStrict"))); |
| |
| Local<v8::Value> a1 = |
| ReturnThisSloppy->CallAsFunction(v8::Undefined(isolate), 0, NULL); |
| CHECK(a1->StrictEquals(context->Global())); |
| Local<v8::Value> a2 = |
| ReturnThisSloppy->CallAsFunction(v8::Null(isolate), 0, NULL); |
| CHECK(a2->StrictEquals(context->Global())); |
| Local<v8::Value> a3 = ReturnThisSloppy->CallAsFunction(v8_num(42), 0, NULL); |
| CHECK(a3->IsNumberObject()); |
| CHECK_EQ(42.0, a3.As<v8::NumberObject>()->ValueOf()); |
| Local<v8::Value> a4 = |
| ReturnThisSloppy->CallAsFunction(v8_str("hello"), 0, NULL); |
| CHECK(a4->IsStringObject()); |
| CHECK(a4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> a5 = |
| ReturnThisSloppy->CallAsFunction(v8::True(isolate), 0, NULL); |
| CHECK(a5->IsBooleanObject()); |
| CHECK(a5.As<v8::BooleanObject>()->ValueOf()); |
| |
| Local<v8::Value> a6 = |
| ReturnThisStrict->CallAsFunction(v8::Undefined(isolate), 0, NULL); |
| CHECK(a6->IsUndefined()); |
| Local<v8::Value> a7 = |
| ReturnThisStrict->CallAsFunction(v8::Null(isolate), 0, NULL); |
| CHECK(a7->IsNull()); |
| Local<v8::Value> a8 = ReturnThisStrict->CallAsFunction(v8_num(42), 0, NULL); |
| CHECK(a8->StrictEquals(v8_num(42))); |
| Local<v8::Value> a9 = |
| ReturnThisStrict->CallAsFunction(v8_str("hello"), 0, NULL); |
| CHECK(a9->StrictEquals(v8_str("hello"))); |
| Local<v8::Value> a10 = |
| ReturnThisStrict->CallAsFunction(v8::True(isolate), 0, NULL); |
| CHECK(a10->StrictEquals(v8::True(isolate))); |
| } |
| } |
| |
| |
| // Check whether a non-function object is callable. |
| THREADED_TEST(CallableObject) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| instance_template->SetCallAsFunctionHandler(call_as_function); |
| Local<Object> instance = instance_template->NewInstance(); |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| Local<Object> instance = instance_template->NewInstance(); |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(!instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| Local<FunctionTemplate> function_template = |
| FunctionTemplate::New(isolate, call_as_function); |
| Local<Function> function = function_template->GetFunction(); |
| Local<Object> instance = function; |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| { |
| Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate); |
| Local<Function> function = function_template->GetFunction(); |
| Local<Object> instance = function; |
| v8::TryCatch try_catch(isolate); |
| |
| CHECK(instance->IsCallable()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| } |
| |
| |
| static int Recurse(v8::Isolate* isolate, int depth, int iterations) { |
| v8::HandleScope scope(isolate); |
| if (depth == 0) return v8::HandleScope::NumberOfHandles(isolate); |
| for (int i = 0; i < iterations; i++) { |
| Local<v8::Number> n(v8::Integer::New(isolate, 42)); |
| } |
| return Recurse(isolate, depth - 1, iterations); |
| } |
| |
| |
| THREADED_TEST(HandleIteration) { |
| static const int kIterations = 500; |
| static const int kNesting = 200; |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope0(isolate); |
| CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| { |
| v8::HandleScope scope1(isolate); |
| CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| for (int i = 0; i < kIterations; i++) { |
| Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42)); |
| CHECK_EQ(i + 1, v8::HandleScope::NumberOfHandles(isolate)); |
| } |
| |
| CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate)); |
| { |
| v8::HandleScope scope2(CcTest::isolate()); |
| for (int j = 0; j < kIterations; j++) { |
| Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42)); |
| CHECK_EQ(j + 1 + kIterations, |
| v8::HandleScope::NumberOfHandles(isolate)); |
| } |
| } |
| CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate)); |
| } |
| CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| CHECK_EQ(kNesting * kIterations, Recurse(isolate, kNesting, kIterations)); |
| } |
| |
| |
| static void InterceptorCallICFastApi( |
| Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi)); |
| int* call_count = |
| reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value()); |
| ++(*call_count); |
| if ((*call_count) % 20 == 0) { |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| } |
| |
| static void FastApiCallback_TrivialSignature( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_TrivialSignature)); |
| v8::Isolate* isolate = CcTest::isolate(); |
| CHECK_EQ(isolate, args.GetIsolate()); |
| CHECK(args.This()->Equals(args.Holder())); |
| CHECK(args.Data()->Equals(v8_str("method_data"))); |
| args.GetReturnValue().Set(args[0]->Int32Value() + 1); |
| } |
| |
| static void FastApiCallback_SimpleSignature( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_SimpleSignature)); |
| v8::Isolate* isolate = CcTest::isolate(); |
| CHECK_EQ(isolate, args.GetIsolate()); |
| CHECK(args.This()->GetPrototype()->Equals(args.Holder())); |
| CHECK(args.Data()->Equals(v8_str("method_data"))); |
| // Note, we're using HasRealNamedProperty instead of Has to avoid |
| // invoking the interceptor again. |
| CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); |
| args.GetReturnValue().Set(args[0]->Int32Value() + 1); |
| } |
| |
| |
| // Helper to maximize the odds of object moving. |
| static void GenerateSomeGarbage() { |
| CompileRun( |
| "var garbage;" |
| "for (var i = 0; i < 1000; i++) {" |
| " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" |
| "}" |
| "garbage = undefined;"); |
| } |
| |
| |
| void DirectApiCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| static int count = 0; |
| if (count++ % 3 == 0) { |
| CcTest::heap()->CollectAllGarbage(); |
| // This should move the stub |
| GenerateSomeGarbage(); // This should ensure the old stub memory is flushed |
| } |
| } |
| |
| |
| THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> nativeobject_templ = |
| v8::ObjectTemplate::New(isolate); |
| nativeobject_templ->Set(isolate, "callback", |
| v8::FunctionTemplate::New(isolate, |
| DirectApiCallback)); |
| v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); |
| context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); |
| // call the api function multiple times to ensure direct call stub creation. |
| CompileRun( |
| "function f() {" |
| " for (var i = 1; i <= 30; i++) {" |
| " nativeobject.callback();" |
| " }" |
| "}" |
| "f();"); |
| } |
| |
| |
| void ThrowingDirectApiCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| args.GetIsolate()->ThrowException(v8_str("g")); |
| } |
| |
| |
| THREADED_TEST(CallICFastApi_DirectCall_Throw) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> nativeobject_templ = |
| v8::ObjectTemplate::New(isolate); |
| nativeobject_templ->Set(isolate, "callback", |
| v8::FunctionTemplate::New(isolate, |
| ThrowingDirectApiCallback)); |
| v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); |
| context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); |
| // call the api function multiple times to ensure direct call stub creation. |
| v8::Handle<Value> result = CompileRun( |
| "var result = '';" |
| "function f() {" |
| " for (var i = 1; i <= 5; i++) {" |
| " try { nativeobject.callback(); } catch (e) { result += e; }" |
| " }" |
| "}" |
| "f(); result;"); |
| CHECK(v8_str("ggggg")->Equals(result)); |
| } |
| |
| |
| static int p_getter_count_3; |
| |
| |
| static Handle<Value> DoDirectGetter() { |
| if (++p_getter_count_3 % 3 == 0) { |
| CcTest::heap()->CollectAllGarbage(); |
| GenerateSomeGarbage(); |
| } |
| return v8_str("Direct Getter Result"); |
| } |
| |
| |
| static void DirectGetterCallback( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CheckReturnValue(info, FUNCTION_ADDR(DirectGetterCallback)); |
| info.GetReturnValue().Set(DoDirectGetter()); |
| } |
| |
| |
| template<typename Accessor> |
| static void LoadICFastApi_DirectCall_GCMoveStub(Accessor accessor) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("p1"), accessor); |
| context->Global()->Set(v8_str("o1"), obj->NewInstance()); |
| p_getter_count_3 = 0; |
| v8::Handle<v8::Value> result = CompileRun( |
| "function f() {" |
| " for (var i = 0; i < 30; i++) o1.p1;" |
| " return o1.p1" |
| "}" |
| "f();"); |
| CHECK(v8_str("Direct Getter Result")->Equals(result)); |
| CHECK_EQ(31, p_getter_count_3); |
| } |
| |
| |
| THREADED_PROFILED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { |
| LoadICFastApi_DirectCall_GCMoveStub(DirectGetterCallback); |
| } |
| |
| |
| void ThrowingDirectGetterCallback( |
| Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| info.GetIsolate()->ThrowException(v8_str("g")); |
| } |
| |
| |
| THREADED_TEST(LoadICFastApi_DirectCall_Throw) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate); |
| obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback); |
| context->Global()->Set(v8_str("o1"), obj->NewInstance()); |
| v8::Handle<Value> result = CompileRun( |
| "var result = '';" |
| "for (var i = 0; i < 5; i++) {" |
| " try { o1.p1; } catch (e) { result += e; }" |
| "}" |
| "result;"); |
| CHECK(v8_str("ggggg")->Equals(result)); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_TrivialSignature) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = |
| v8::FunctionTemplate::New(isolate, |
| FastApiCallback_TrivialSignature, |
| v8_str("method_data"), |
| v8::Handle<v8::Signature>()); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, NULL, NULL, NULL, NULL, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = o.method(41);" |
| "}"); |
| CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); |
| CHECK_EQ(100, interceptor_call_count); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, NULL, NULL, NULL, NULL, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| "}"); |
| CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); |
| CHECK_EQ(100, interceptor_call_count); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, NULL, NULL, NULL, NULL, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = {method: function(x) { return x - 1 }};" |
| " }" |
| "}"); |
| CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, NULL, NULL, NULL, NULL, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " o.method = function(x) { return x - 1 };" |
| " }" |
| "}"); |
| CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, NULL, NULL, NULL, NULL, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = 333;" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| // TODO(verwaest): Adjust message. |
| CHECK(v8_str("TypeError: receiver.method is not a function") |
| ->Equals(try_catch.Exception()->ToString(isolate))); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { |
| int interceptor_call_count = 0; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorCallICFastApi, NULL, NULL, NULL, NULL, |
| v8::External::New(isolate, &interceptor_call_count))); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = {method: receiver.method};" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(v8_str("TypeError: Illegal invocation") |
| ->Equals(try_catch.Exception()->ToString(isolate))); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| CHECK_GE(interceptor_call_count, 50); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_TrivialSignature) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = |
| v8::FunctionTemplate::New(isolate, |
| FastApiCallback_TrivialSignature, |
| v8_str("method_data"), |
| v8::Handle<v8::Signature>()); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| USE(templ); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = o.method(41);" |
| "}"); |
| |
| CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| "}"); |
| |
| CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss1) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = {method: function(x) { return x - 1 }};" |
| " }" |
| "}"); |
| CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss2) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = 333;" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| // TODO(verwaest): Adjust message. |
| CHECK(v8_str("TypeError: receiver.method is not a function") |
| ->Equals(try_catch.Exception()->ToString(isolate))); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| } |
| |
| |
| THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_TypeError) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate); |
| v8::Handle<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| isolate, FastApiCallback_SimpleSignature, v8_str("method_data"), |
| v8::Signature::New(isolate, fun_templ)); |
| v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| proto_templ->Set(v8_str("method"), method_templ); |
| fun_templ->SetHiddenPrototype(true); |
| v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| CHECK(!templ.IsEmpty()); |
| LocalContext context; |
| v8::Handle<v8::Function> fun = fun_templ->GetFunction(); |
| GenerateSomeGarbage(); |
| context->Global()->Set(v8_str("o"), fun->NewInstance()); |
| v8::TryCatch try_catch(isolate); |
| CompileRun( |
| "o.foo = 17;" |
| "var receiver = {};" |
| "receiver.__proto__ = o;" |
| "var result = 0;" |
| "var saved_result = 0;" |
| "for (var i = 0; i < 100; i++) {" |
| " result = receiver.method(41);" |
| " if (i == 50) {" |
| " saved_result = result;" |
| " receiver = Object.create(receiver);" |
| " }" |
| "}"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(v8_str("TypeError: Illegal invocation") |
| ->Equals(try_catch.Exception()->ToString(isolate))); |
| CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); |
| } |
| |
| |
| static void ThrowingGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetIsolate()->ThrowException(Handle<Value>()); |
| info.GetReturnValue().SetUndefined(); |
| } |
| |
| |
| THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { |
| LocalContext context; |
| HandleScope scope(context->GetIsolate()); |
| |
| Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| instance_templ->SetAccessor(v8_str("f"), ThrowingGetter); |
| |
| Local<Object> instance = templ->GetFunction()->NewInstance(); |
| |
| Local<Object> another = Object::New(context->GetIsolate()); |
| another->SetPrototype(instance); |
| |
| Local<Object> with_js_getter = CompileRun( |
| "o = {};\n" |
| "o.__defineGetter__('f', function() { throw undefined; });\n" |
| "o\n").As<Object>(); |
| CHECK(!with_js_getter.IsEmpty()); |
| |
| TryCatch try_catch(context->GetIsolate()); |
| |
| Local<Value> result = instance->GetRealNamedProperty(v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| Maybe<PropertyAttribute> attr = |
| instance->GetRealNamedPropertyAttributes(v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = another->GetRealNamedProperty(v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| attr = another->GetRealNamedPropertyAttributes(v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| attr = another->GetRealNamedPropertyAttributesInPrototypeChain(v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = another->Get(v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| result = with_js_getter->GetRealNamedProperty(v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| |
| attr = with_js_getter->GetRealNamedPropertyAttributes(v8_str("f")); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(Just(None) == attr); |
| |
| result = with_js_getter->Get(v8_str("f")); |
| CHECK(try_catch.HasCaught()); |
| try_catch.Reset(); |
| CHECK(result.IsEmpty()); |
| } |
| |
| |
| static void ThrowingCallbackWithTryCatch( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| TryCatch try_catch(args.GetIsolate()); |
| // Verboseness is important: it triggers message delivery which can call into |
| // external code. |
| try_catch.SetVerbose(true); |
| CompileRun("throw 'from JS';"); |
| CHECK(try_catch.HasCaught()); |
| CHECK(!CcTest::i_isolate()->has_pending_exception()); |
| CHECK(!CcTest::i_isolate()->has_scheduled_exception()); |
| } |
| |
| |
| static int call_depth; |
| |
| |
| static void WithTryCatch(Handle<Message> message, Handle<Value> data) { |
| TryCatch try_catch(CcTest::isolate()); |
| } |
| |
| |
| static void ThrowFromJS(Handle<Message> message, Handle<Value> data) { |
| if (--call_depth) CompileRun("throw 'ThrowInJS';"); |
| } |
| |
| |
| static void ThrowViaApi(Handle<Message> message, Handle<Value> data) { |
| if (--call_depth) CcTest::isolate()->ThrowException(v8_str("ThrowViaApi")); |
| } |
| |
| |
| static void WebKitLike(Handle<Message> message, Handle<Value> data) { |
| Handle<String> errorMessageString = message->Get(); |
| CHECK(!errorMessageString.IsEmpty()); |
| message->GetStackTrace(); |
| message->GetScriptOrigin().ResourceName(); |
| } |
| |
| |
| THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| HandleScope scope(isolate); |
| |
| Local<Function> func = |
| FunctionTemplate::New(isolate, |
| ThrowingCallbackWithTryCatch)->GetFunction(); |
| context->Global()->Set(v8_str("func"), func); |
| |
| MessageCallback callbacks[] = |
| { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch }; |
| for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { |
| MessageCallback callback = callbacks[i]; |
| if (callback != NULL) { |
| V8::AddMessageListener(callback); |
| } |
| // Some small number to control number of times message handler should |
| // throw an exception. |
| call_depth = 5; |
| ExpectFalse( |
| "var thrown = false;\n" |
| "try { func(); } catch(e) { thrown = true; }\n" |
| "thrown\n"); |
| if (callback != NULL) { |
| V8::RemoveMessageListeners(callback); |
| } |
| } |
| } |
| |
| |
| static void ParentGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(1)); |
| } |
| |
| |
| static void ChildGetter(Local<String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| ApiTestFuzzer::Fuzz(); |
| info.GetReturnValue().Set(v8_num(42)); |
| } |
| |
| |
| THREADED_TEST(Overriding) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Parent template. |
| Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> parent_instance_templ = |
| parent_templ->InstanceTemplate(); |
| parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); |
| |
| // Template that inherits from the parent template. |
| Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(isolate); |
| Local<ObjectTemplate> child_instance_templ = |
| child_templ->InstanceTemplate(); |
| child_templ->Inherit(parent_templ); |
| // Override 'f'. The child version of 'f' should get called for child |
| // instances. |
| child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); |
| // Add 'g' twice. The 'g' added last should get called for instances. |
| child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); |
| child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); |
| |
| // Add 'h' as an accessor to the proto template with ReadOnly attributes |
| // so 'h' can be shadowed on the instance object. |
| Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); |
| child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, |
| v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); |
| |
| // Add 'i' as an accessor to the instance template with ReadOnly attributes |
| // but the attribute does not have effect because it is duplicated with |
| // NULL setter. |
| child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, |
| v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); |
| |
| |
| |
| // Instantiate the child template. |
| Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); |
| |
| // Check that the child function overrides the parent one. |
| context->Global()->Set(v8_str("o"), instance); |
| Local<Value> value = v8_compile("o.f")->Run(); |
| // Check that the 'g' that was added last is hit. |
| CHECK_EQ(42, value->Int32Value()); |
| value = v8_compile("o.g")->Run(); |
| CHECK_EQ(42, value->Int32Value()); |
| |
| // Check that 'h' cannot be shadowed. |
| value = v8_compile("o.h = 3; o.h")->Run(); |
| CHECK_EQ(1, value->Int32Value()); |
| |
| // Check that 'i' cannot be shadowed or changed. |
| value = v8_compile("o.i = 3; o.i")->Run(); |
| CHECK_EQ(42, value->Int32Value()); |
| } |
| |
| |
| static void IsConstructHandler( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(args.IsConstructCall()); |
| } |
| |
| |
| THREADED_TEST(IsConstructCall) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Function template with call handler. |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetCallHandler(IsConstructHandler); |
| |
| LocalContext context; |
| |
| context->Global()->Set(v8_str("f"), templ->GetFunction()); |
| Local<Value> value = v8_compile("f()")->Run(); |
| CHECK(!value->BooleanValue()); |
| value = v8_compile("new f()")->Run(); |
| CHECK(value->BooleanValue()); |
| } |
| |
| |
| THREADED_TEST(ObjectProtoToString) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("MyClass")); |
| |
| LocalContext context; |
| |
| Local<String> customized_tostring = v8_str("customized toString"); |
| |
| // Replace Object.prototype.toString |
| v8_compile("Object.prototype.toString = function() {" |
| " return 'customized toString';" |
| "}")->Run(); |
| |
| // Normal ToString call should call replaced Object.prototype.toString |
| Local<v8::Object> instance = templ->GetFunction()->NewInstance(); |
| Local<String> value = instance->ToString(isolate); |
| CHECK(value->IsString() && value->Equals(customized_tostring)); |
| |
| // ObjectProtoToString should not call replace toString function. |
| value = instance->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); |
| |
| // Check global |
| value = context->Global()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); |
| |
| // Check ordinary object |
| Local<Value> object = v8_compile("new Object()")->Run(); |
| value = object.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); |
| } |
| |
| |
| TEST(ObjectProtoToStringES6) { |
| // TODO(dslomov, caitp): merge into ObjectProtoToString test once shipped. |
| i::FLAG_harmony_tostring = true; |
| LocalContext context; |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName(v8_str("MyClass")); |
| |
| Local<String> customized_tostring = v8_str("customized toString"); |
| |
| // Replace Object.prototype.toString |
| CompileRun( |
| "Object.prototype.toString = function() {" |
| " return 'customized toString';" |
| "}"); |
| |
| // Normal ToString call should call replaced Object.prototype.toString |
| Local<v8::Object> instance = templ->GetFunction()->NewInstance(); |
| Local<String> value = instance->ToString(isolate); |
| CHECK(value->IsString() && value->Equals(customized_tostring)); |
| |
| // ObjectProtoToString should not call replace toString function. |
| value = instance->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); |
| |
| // Check global |
| value = context->Global()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); |
| |
| // Check ordinary object |
| Local<Value> object = CompileRun("new Object()"); |
| value = object.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); |
| |
| // Check that ES6 semantics using @@toStringTag work |
| Local<v8::Symbol> toStringTag = v8::Symbol::GetToStringTag(isolate); |
| |
| #define TEST_TOSTRINGTAG(type, tag, expected) \ |
| do { \ |
| object = CompileRun("new " #type "()"); \ |
| object.As<v8::Object>()->Set(toStringTag, v8_str(#tag)); \ |
| value = object.As<v8::Object>()->ObjectProtoToString(); \ |
| CHECK(value->IsString() && \ |
| value->Equals(v8_str("[object " #expected "]"))); \ |
| } while (0) |
| |
| TEST_TOSTRINGTAG(Array, Object, Object); |
| TEST_TOSTRINGTAG(Object, Arguments, Arguments); |
| TEST_TOSTRINGTAG(Object, Array, Array); |
| TEST_TOSTRINGTAG(Object, Boolean, Boolean); |
| TEST_TOSTRINGTAG(Object, Date, Date); |
| TEST_TOSTRINGTAG(Object, Error, Error); |
| TEST_TOSTRINGTAG(Object, Function, Function); |
| TEST_TOSTRINGTAG(Object, Number, Number); |
| TEST_TOSTRINGTAG(Object, RegExp, RegExp); |
| TEST_TOSTRINGTAG(Object, String, String); |
| TEST_TOSTRINGTAG(Object, Foo, Foo); |
| |
| #undef TEST_TOSTRINGTAG |
| |
| Local<v8::RegExp> valueRegExp = v8::RegExp::New(v8_str("^$"), |
| v8::RegExp::kNone); |
| Local<Value> valueNumber = v8_num(123); |
| Local<v8::Symbol> valueSymbol = v8_symbol("TestSymbol"); |
| Local<v8::Function> valueFunction = |
| CompileRun("(function fn() {})").As<v8::Function>(); |
| Local<v8::Object> valueObject = v8::Object::New(v8::Isolate::GetCurrent()); |
| Local<v8::Primitive> valueNull = v8::Null(v8::Isolate::GetCurrent()); |
| Local<v8::Primitive> valueUndef = v8::Undefined(v8::Isolate::GetCurrent()); |
| |
| #define TEST_TOSTRINGTAG(type, tagValue, expected) \ |
| do { \ |
| object = CompileRun("new " #type "()"); \ |
| object.As<v8::Object>()->Set(toStringTag, tagValue); \ |
| value = object.As<v8::Object>()->ObjectProtoToString(); \ |
| CHECK(value->IsString() && \ |
| value->Equals(v8_str("[object " #expected "]"))); \ |
| } while (0) |
| |
| #define TEST_TOSTRINGTAG_TYPES(tagValue) \ |
| TEST_TOSTRINGTAG(Array, tagValue, Array); \ |
| TEST_TOSTRINGTAG(Object, tagValue, Object); \ |
| TEST_TOSTRINGTAG(Function, tagValue, Function); \ |
| TEST_TOSTRINGTAG(Date, tagValue, Date); \ |
| TEST_TOSTRINGTAG(RegExp, tagValue, RegExp); \ |
| TEST_TOSTRINGTAG(Error, tagValue, Error); \ |
| |
| // Test non-String-valued @@toStringTag |
| TEST_TOSTRINGTAG_TYPES(valueRegExp); |
| TEST_TOSTRINGTAG_TYPES(valueNumber); |
| TEST_TOSTRINGTAG_TYPES(valueSymbol); |
| TEST_TOSTRINGTAG_TYPES(valueFunction); |
| TEST_TOSTRINGTAG_TYPES(valueObject); |
| TEST_TOSTRINGTAG_TYPES(valueNull); |
| TEST_TOSTRINGTAG_TYPES(valueUndef); |
| |
| #undef TEST_TOSTRINGTAG |
| #undef TEST_TOSTRINGTAG_TYPES |
| |
| // @@toStringTag getter throws |
| Local<Value> obj = v8::Object::New(isolate); |
| obj.As<v8::Object>()->SetAccessor(toStringTag, ThrowingSymbolAccessorGetter); |
| { |
| TryCatch try_catch(isolate); |
| value = obj.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| // @@toStringTag getter does not throw |
| obj = v8::Object::New(isolate); |
| obj.As<v8::Object>()->SetAccessor( |
| toStringTag, SymbolAccessorGetterReturnsDefault, 0, v8_str("Test")); |
| { |
| TryCatch try_catch(isolate); |
| value = obj.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object Test]"))); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| // JS @@toStringTag value |
| obj = CompileRun("obj = {}; obj[Symbol.toStringTag] = 'Test'; obj"); |
| { |
| TryCatch try_catch(isolate); |
| value = obj.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object Test]"))); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| // JS @@toStringTag getter throws |
| obj = CompileRun( |
| "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {" |
| " get: function() { throw 'Test'; }" |
| "}); obj"); |
| { |
| TryCatch try_catch(isolate); |
| value = obj.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| } |
| |
| // JS @@toStringTag getter does not throw |
| obj = CompileRun( |
| "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {" |
| " get: function() { return 'Test'; }" |
| "}); obj"); |
| { |
| TryCatch try_catch(isolate); |
| value = obj.As<v8::Object>()->ObjectProtoToString(); |
| CHECK(value->IsString() && value->Equals(v8_str("[object Test]"))); |
| CHECK(!try_catch.HasCaught()); |
| } |
| } |
| |
| |
| THREADED_TEST(ObjectGetConstructorName) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| LocalContext context; |
| v8::HandleScope scope(isolate); |
| v8_compile( |
| "function Parent() {};" |
| "function Child() {};" |
| "Child.prototype = new Parent();" |
| "var outer = { inner: function() { } };" |
| "var p = new Parent();" |
| "var c = new Child();" |
| "var x = new outer.inner();" |
| "var proto = Child.prototype;")->Run(); |
| |
| Local<v8::Value> p = context->Global()->Get(v8_str("p")); |
| CHECK(p->IsObject() && |
| p->ToObject(isolate)->GetConstructorName()->Equals(v8_str("Parent"))); |
| |
| Local<v8::Value> c = context->Global()->Get(v8_str("c")); |
| CHECK(c->IsObject() && |
| c->ToObject(isolate)->GetConstructorName()->Equals(v8_str("Child"))); |
| |
| Local<v8::Value> x = context->Global()->Get(v8_str("x")); |
| CHECK(x->IsObject() && |
| x->ToObject(isolate)->GetConstructorName()->Equals( |
| v8_str("outer.inner"))); |
| |
| Local<v8::Value> child_prototype = context->Global()->Get(v8_str("proto")); |
| CHECK(child_prototype->IsObject() && |
| child_prototype->ToObject(isolate)->GetConstructorName()->Equals( |
| v8_str("Parent"))); |
| } |
| |
| |
| bool ApiTestFuzzer::fuzzing_ = false; |
| v8::base::Semaphore ApiTestFuzzer::all_tests_done_(0); |
| int ApiTestFuzzer::active_tests_; |
| int ApiTestFuzzer::tests_being_run_; |
| int ApiTestFuzzer::current_; |
| |
| |
| // We are in a callback and want to switch to another thread (if we |
| // are currently running the thread fuzzing test). |
| void ApiTestFuzzer::Fuzz() { |
| if (!fuzzing_) return; |
| ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; |
| test->ContextSwitch(); |
| } |
| |
| |
| // Let the next thread go. Since it is also waiting on the V8 lock it may |
| // not start immediately. |
| bool ApiTestFuzzer::NextThread() { |
| int test_position = GetNextTestNumber(); |
| const char* test_name = RegisterThreadedTest::nth(current_)->name(); |
| if (test_position == current_) { |
| if (kLogThreading) |
| printf("Stay with %s\n", test_name); |
| return false; |
| } |
| if (kLogThreading) { |
| printf("Switch from %s to %s\n", |
| test_name, |
| RegisterThreadedTest::nth(test_position)->name()); |
| } |
| current_ = test_position; |
| RegisterThreadedTest::nth(current_)->fuzzer_->gate_.Signal(); |
| return true; |
| } |
| |
| |
| void ApiTestFuzzer::Run() { |
| // When it is our turn... |
| gate_.Wait(); |
| { |
| // ... get the V8 lock and start running the test. |
| v8::Locker locker(CcTest::isolate()); |
| CallTest(); |
| } |
| // This test finished. |
| active_ = false; |
| active_tests_--; |
| // If it was the last then signal that fact. |
| if (active_tests_ == 0) { |
| all_tests_done_.Signal(); |
| } else { |
| // Otherwise select a new test and start that. |
| NextThread(); |
| } |
| } |
| |
| |
| static unsigned linear_congruential_generator; |
| |
| |
| void ApiTestFuzzer::SetUp(PartOfTest part) { |
| linear_congruential_generator = i::FLAG_testing_prng_seed; |
| fuzzing_ = true; |
| int count = RegisterThreadedTest::count(); |
| int start = count * part / (LAST_PART + 1); |
| int end = (count * (part + 1) / (LAST_PART + 1)) - 1; |
| active_tests_ = tests_being_run_ = end - start + 1; |
| for (int i = 0; i < tests_being_run_; i++) { |
| RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); |
| } |
| for (int i = 0; i < active_tests_; i++) { |
| RegisterThreadedTest::nth(i)->fuzzer_->Start(); |
| } |
| } |
| |
| |
| static void CallTestNumber(int test_number) { |
| (RegisterThreadedTest::nth(test_number)->callback())(); |
| } |
| |
| |
| void ApiTestFuzzer::RunAllTests() { |
| // Set off the first test. |
| current_ = -1; |
| NextThread(); |
| // Wait till they are all done. |
| all_tests_done_.Wait(); |
| } |
| |
| |
| int ApiTestFuzzer::GetNextTestNumber() { |
| int next_test; |
| do { |
| next_test = (linear_congruential_generator >> 16) % tests_being_run_; |
| linear_congruential_generator *= 1664525u; |
| linear_congruential_generator += 1013904223u; |
| } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); |
| return next_test; |
| } |
| |
| |
| void ApiTestFuzzer::ContextSwitch() { |
| // If the new thread is the same as the current thread there is nothing to do. |
| if (NextThread()) { |
| // Now it can start. |
| v8::Unlocker unlocker(CcTest::isolate()); |
| // Wait till someone starts us again. |
| gate_.Wait(); |
| // And we're off. |
| } |
| } |
| |
| |
| void ApiTestFuzzer::TearDown() { |
| fuzzing_ = false; |
| for (int i = 0; i < RegisterThreadedTest::count(); i++) { |
| ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; |
| if (fuzzer != NULL) fuzzer->Join(); |
| } |
| } |
| |
| |
| // Lets not be needlessly self-referential. |
| TEST(Threading1) { |
| ApiTestFuzzer::SetUp(ApiTestFuzzer::FIRST_PART); |
| ApiTestFuzzer::RunAllTests(); |
| ApiTestFuzzer::TearDown(); |
| } |
| |
| |
| TEST(Threading2) { |
| ApiTestFuzzer::SetUp(ApiTestFuzzer::SECOND_PART); |
| ApiTestFuzzer::RunAllTests(); |
| ApiTestFuzzer::TearDown(); |
| } |
| |
| |
| TEST(Threading3) { |
| ApiTestFuzzer::SetUp(ApiTestFuzzer::THIRD_PART); |
| ApiTestFuzzer::RunAllTests(); |
| ApiTestFuzzer::TearDown(); |
| } |
| |
| |
| TEST(Threading4) { |
| ApiTestFuzzer::SetUp(ApiTestFuzzer::FOURTH_PART); |
| ApiTestFuzzer::RunAllTests(); |
| ApiTestFuzzer::TearDown(); |
| } |
| |
| |
| void ApiTestFuzzer::CallTest() { |
| v8::Isolate::Scope scope(CcTest::isolate()); |
| if (kLogThreading) |
| printf("Start test %d\n", test_number_); |
| CallTestNumber(test_number_); |
| if (kLogThreading) |
| printf("End test %d\n", test_number_); |
| } |
| |
| |
| static void ThrowInJS(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| v8::Isolate* isolate = args.GetIsolate(); |
| CHECK(v8::Locker::IsLocked(isolate)); |
| ApiTestFuzzer::Fuzz(); |
| v8::Unlocker unlocker(isolate); |
| const char* code = "throw 7;"; |
| { |
| v8::Locker nested_locker(isolate); |
| v8::HandleScope scope(isolate); |
| v8::Handle<Value> exception; |
| { |
| v8::TryCatch try_catch(isolate); |
| v8::Handle<Value> value = CompileRun(code); |
| CHECK(value.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| // Make sure to wrap the exception in a new handle because |
| // the handle returned from the TryCatch is destroyed |
| // when the TryCatch is destroyed. |
| exception = Local<Value>::New(isolate, try_catch.Exception()); |
| } |
| args.GetIsolate()->ThrowException(exception); |
| } |
| } |
| |
| |
| static void ThrowInJSNoCatch(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| CHECK(v8::Locker::IsLocked(CcTest::isolate())); |
| ApiTestFuzzer::Fuzz(); |
| v8::Unlocker unlocker(CcTest::isolate()); |
| const char* code = "throw 7;"; |
| { |
| v8::Locker nested_locker(CcTest::isolate()); |
| v8::HandleScope scope(args.GetIsolate()); |
| v8::Handle<Value> value = CompileRun(code); |
| CHECK(value.IsEmpty()); |
| args.GetReturnValue().Set(v8_str("foo")); |
| } |
| } |
| |
| |
| // These are locking tests that don't need to be run again |
| // as part of the locking aggregation tests. |
| TEST(NestedLockers) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::Locker locker(isolate); |
| CHECK(v8::Locker::IsLocked(isolate)); |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(isolate, ThrowInJS); |
| Local<Function> fun = fun_templ->GetFunction(); |
| env->Global()->Set(v8_str("throw_in_js"), fun); |
| Local<Script> script = v8_compile("(function () {" |
| " try {" |
| " throw_in_js();" |
| " return 42;" |
| " } catch (e) {" |
| " return e * 13;" |
| " }" |
| "})();"); |
| CHECK_EQ(91, script->Run()->Int32Value()); |
| } |
| |
| |
| // These are locking tests that don't need to be run again |
| // as part of the locking aggregation tests. |
| TEST(NestedLockersNoTryCatch) { |
| v8::Locker locker(CcTest::isolate()); |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(env->GetIsolate(), ThrowInJSNoCatch); |
| Local<Function> fun = fun_templ->GetFunction(); |
| env->Global()->Set(v8_str("throw_in_js"), fun); |
| Local<Script> script = v8_compile("(function () {" |
| " try {" |
| " throw_in_js();" |
| " return 42;" |
| " } catch (e) {" |
| " return e * 13;" |
| " }" |
| "})();"); |
| CHECK_EQ(91, script->Run()->Int32Value()); |
| } |
| |
| |
| THREADED_TEST(RecursiveLocking) { |
| v8::Locker locker(CcTest::isolate()); |
| { |
| v8::Locker locker2(CcTest::isolate()); |
| CHECK(v8::Locker::IsLocked(CcTest::isolate())); |
| } |
| } |
| |
| |
| static void UnlockForAMoment(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| v8::Unlocker unlocker(CcTest::isolate()); |
| } |
| |
| |
| THREADED_TEST(LockUnlockLock) { |
| { |
| v8::Locker locker(CcTest::isolate()); |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext env; |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(CcTest::isolate(), UnlockForAMoment); |
| Local<Function> fun = fun_templ->GetFunction(); |
| env->Global()->Set(v8_str("unlock_for_a_moment"), fun); |
| Local<Script> script = v8_compile("(function () {" |
| " unlock_for_a_moment();" |
| " return 42;" |
| "})();"); |
| CHECK_EQ(42, script->Run()->Int32Value()); |
| } |
| { |
| v8::Locker locker(CcTest::isolate()); |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext env; |
| Local<v8::FunctionTemplate> fun_templ = |
| v8::FunctionTemplate::New(CcTest::isolate(), UnlockForAMoment); |
| Local<Function> fun = fun_templ->GetFunction(); |
| env->Global()->Set(v8_str("unlock_for_a_moment"), fun); |
| Local<Script> script = v8_compile("(function () {" |
| " unlock_for_a_moment();" |
| " return 42;" |
| "})();"); |
| CHECK_EQ(42, script->Run()->Int32Value()); |
| } |
| } |
| |
| |
| static int GetGlobalObjectsCount() { |
| int count = 0; |
| i::HeapIterator it(CcTest::heap()); |
| for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) |
| if (object->IsJSGlobalObject()) count++; |
| return count; |
| } |
| |
| |
| static void CheckSurvivingGlobalObjectsCount(int expected) { |
| // We need to collect all garbage twice to be sure that everything |
| // has been collected. This is because inline caches are cleared in |
| // the first garbage collection but some of the maps have already |
| // been marked at that point. Therefore some of the maps are not |
| // collected until the second garbage collection. |
| CcTest::heap()->CollectAllGarbage(); |
| CcTest::heap()->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); |
| int count = GetGlobalObjectsCount(); |
| #ifdef DEBUG |
| if (count != expected) CcTest::heap()->TracePathToGlobal(); |
| #endif |
| CHECK_EQ(expected, count); |
| } |
| |
| |
| TEST(DontLeakGlobalObjects) { |
| // Regression test for issues 1139850 and 1174891. |
| |
| i::FLAG_expose_gc = true; |
| v8::V8::Initialize(); |
| |
| for (int i = 0; i < 5; i++) { |
| { v8::HandleScope scope(CcTest::isolate()); |
| LocalContext context; |
| } |
| CcTest::isolate()->ContextDisposedNotification(); |
| CheckSurvivingGlobalObjectsCount(0); |
| |
| { v8::HandleScope scope(CcTest::isolate()); |
| LocalContext context; |
| v8_compile("Date")->Run(); |
| } |
| CcTest::isolate()->ContextDisposedNotification(); |
| CheckSurvivingGlobalObjectsCount(0); |
| |
| { v8::HandleScope scope(CcTest::isolate()); |
| LocalContext context; |
| v8_compile("/aaa/")->Run(); |
| } |
| CcTest::isolate()->ContextDisposedNotification(); |
| CheckSurvivingGlobalObjectsCount(0); |
| |
| { v8::HandleScope scope(CcTest::isolate()); |
| const char* extension_list[] = { "v8/gc" }; |
| v8::ExtensionConfiguration extensions(1, extension_list); |
| LocalContext context(&extensions); |
| v8_compile("gc();")->Run(); |
| } |
| CcTest::isolate()->ContextDisposedNotification(); |
| CheckSurvivingGlobalObjectsCount(0); |
| } |
| } |
| |
| |
| TEST(CopyablePersistent) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| i::GlobalHandles* globals = |
| reinterpret_cast<i::Isolate*>(isolate)->global_handles(); |
| int initial_handles = globals->global_handles_count(); |
| typedef v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > |
| CopyableObject; |
| { |
| CopyableObject handle1; |
| { |
| v8::HandleScope scope(isolate); |
| handle1.Reset(isolate, v8::Object::New(isolate)); |
| } |
| CHECK_EQ(initial_handles + 1, globals->global_handles_count()); |
| CopyableObject handle2; |
| handle2 = handle1; |
| CHECK(handle1 == handle2); |
| CHECK_EQ(initial_handles + 2, globals->global_handles_count()); |
| CopyableObject handle3(handle2); |
| CHECK(handle1 == handle3); |
| CHECK_EQ(initial_handles + 3, globals->global_handles_count()); |
| } |
| // Verify autodispose |
| CHECK_EQ(initial_handles, globals->global_handles_count()); |
| } |
| |
| |
| static void WeakApiCallback( |
| const v8::WeakCallbackInfo<Persistent<v8::Object>>& data) { |
| data.GetParameter()->Reset(); |
| delete data.GetParameter(); |
| } |
| |
| |
| TEST(WeakCallbackApi) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| i::GlobalHandles* globals = |
| reinterpret_cast<i::Isolate*>(isolate)->global_handles(); |
| int initial_handles = globals->global_handles_count(); |
| { |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| obj->Set(v8_str("key"), v8::Integer::New(isolate, 231)); |
| v8::Persistent<v8::Object>* handle = |
| new v8::Persistent<v8::Object>(isolate, obj); |
| handle->SetWeak<v8::Persistent<v8::Object>>( |
| handle, WeakApiCallback, v8::WeakCallbackType::kParameter); |
| } |
| reinterpret_cast<i::Isolate*>(isolate)->heap()->CollectAllGarbage( |
| i::Heap::kAbortIncrementalMarkingMask); |
| // Verify disposed. |
| CHECK_EQ(initial_handles, globals->global_handles_count()); |
| } |
| |
| |
| v8::Persistent<v8::Object> some_object; |
| v8::Persistent<v8::Object> bad_handle; |
| |
| |
| void NewPersistentHandleCallback2( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| v8::HandleScope scope(data.GetIsolate()); |
| bad_handle.Reset(data.GetIsolate(), some_object); |
| } |
| |
| |
| void NewPersistentHandleCallback1( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| data.GetParameter()->Reset(); |
| data.SetSecondPassCallback(NewPersistentHandleCallback2); |
| } |
| |
| |
| THREADED_TEST(NewPersistentHandleFromWeakCallback) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| |
| v8::Persistent<v8::Object> handle1, handle2; |
| { |
| v8::HandleScope scope(isolate); |
| some_object.Reset(isolate, v8::Object::New(isolate)); |
| handle1.Reset(isolate, v8::Object::New(isolate)); |
| handle2.Reset(isolate, v8::Object::New(isolate)); |
| } |
| // Note: order is implementation dependent alas: currently |
| // global handle nodes are processed by PostGarbageCollectionProcessing |
| // in reverse allocation order, so if second allocated handle is deleted, |
| // weak callback of the first handle would be able to 'reallocate' it. |
| handle1.SetWeak(&handle1, NewPersistentHandleCallback1, |
| v8::WeakCallbackType::kParameter); |
| handle2.Reset(); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| |
| v8::Persistent<v8::Object> to_be_disposed; |
| |
| |
| void DisposeAndForceGcCallback2( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| to_be_disposed.Reset(); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| |
| void DisposeAndForceGcCallback1( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| data.GetParameter()->Reset(); |
| data.SetSecondPassCallback(DisposeAndForceGcCallback2); |
| } |
| |
| |
| THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| |
| v8::Persistent<v8::Object> handle1, handle2; |
| { |
| v8::HandleScope scope(isolate); |
| handle1.Reset(isolate, v8::Object::New(isolate)); |
| handle2.Reset(isolate, v8::Object::New(isolate)); |
| } |
| handle1.SetWeak(&handle1, DisposeAndForceGcCallback1, |
| v8::WeakCallbackType::kParameter); |
| to_be_disposed.Reset(isolate, handle2); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| void DisposingCallback( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| data.GetParameter()->Reset(); |
| } |
| |
| void HandleCreatingCallback2( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| v8::HandleScope scope(data.GetIsolate()); |
| v8::Global<v8::Object>(data.GetIsolate(), v8::Object::New(data.GetIsolate())); |
| } |
| |
| |
| void HandleCreatingCallback1( |
| const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| data.GetParameter()->Reset(); |
| data.SetSecondPassCallback(HandleCreatingCallback2); |
| } |
| |
| |
| THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| |
| v8::Persistent<v8::Object> handle1, handle2, handle3; |
| { |
| v8::HandleScope scope(isolate); |
| handle3.Reset(isolate, v8::Object::New(isolate)); |
| handle2.Reset(isolate, v8::Object::New(isolate)); |
| handle1.Reset(isolate, v8::Object::New(isolate)); |
| } |
| handle2.SetWeak(&handle2, DisposingCallback, |
| v8::WeakCallbackType::kParameter); |
| handle3.SetWeak(&handle3, HandleCreatingCallback1, |
| v8::WeakCallbackType::kParameter); |
| CcTest::heap()->CollectAllGarbage(); |
| } |
| |
| |
| THREADED_TEST(CheckForCrossContextObjectLiterals) { |
| v8::V8::Initialize(); |
| |
| const int nof = 2; |
| const char* sources[nof] = { |
| "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", |
| "Object()" |
| }; |
| |
| for (int i = 0; i < nof; i++) { |
| const char* source = sources[i]; |
| { v8::HandleScope scope(CcTest::isolate()); |
| LocalContext context; |
| CompileRun(source); |
| } |
| { v8::HandleScope scope(CcTest::isolate()); |
| LocalContext context; |
| CompileRun(source); |
| } |
| } |
| } |
| |
| |
| static v8::Handle<Value> NestedScope(v8::Local<Context> env) { |
| v8::EscapableHandleScope inner(env->GetIsolate()); |
| env->Enter(); |
| v8::Local<Value> three = v8_num(3); |
| v8::Local<Value> value = inner.Escape(three); |
| env->Exit(); |
| return value; |
| } |
| |
| |
| THREADED_TEST(NestedHandleScopeAndContexts) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope outer(isolate); |
| v8::Local<Context> env = Context::New(isolate); |
| env->Enter(); |
| v8::Handle<Value> value = NestedScope(env); |
| v8::Handle<String> str(value->ToString(isolate)); |
| CHECK(!str.IsEmpty()); |
| env->Exit(); |
| } |
| |
| |
| static bool MatchPointers(void* key1, void* key2) { |
| return key1 == key2; |
| } |
| |
| |
| struct SymbolInfo { |
| size_t id; |
| size_t size; |
| std::string name; |
| }; |
| |
| |
| class SetFunctionEntryHookTest { |
| public: |
| SetFunctionEntryHookTest() { |
| CHECK(instance_ == NULL); |
| instance_ = this; |
| } |
| ~SetFunctionEntryHookTest() { |
| CHECK(instance_ == this); |
| instance_ = NULL; |
| } |
| void Reset() { |
| symbols_.clear(); |
| symbol_locations_.clear(); |
| invocations_.clear(); |
| } |
| void RunTest(); |
| void OnJitEvent(const v8::JitCodeEvent* event); |
| static void JitEvent(const v8::JitCodeEvent* event) { |
| CHECK(instance_ != NULL); |
| instance_->OnJitEvent(event); |
| } |
| |
| void OnEntryHook(uintptr_t function, |
| uintptr_t return_addr_location); |
| static void EntryHook(uintptr_t function, |
| uintptr_t return_addr_location) { |
| CHECK(instance_ != NULL); |
| instance_->OnEntryHook(function, return_addr_location); |
| } |
| |
| static void RuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| CHECK(instance_ != NULL); |
| args.GetReturnValue().Set(v8_num(42)); |
| } |
| void RunLoopInNewEnv(v8::Isolate* isolate); |
| |
| // Records addr as location of symbol. |
| void InsertSymbolAt(i::Address addr, SymbolInfo* symbol); |
| |
| // Finds the symbol containing addr |
| SymbolInfo* FindSymbolForAddr(i::Address addr); |
| // Returns the number of invocations where the caller name contains |
| // \p caller_name and the function name contains \p function_name. |
| int CountInvocations(const char* caller_name, |
| const char* function_name); |
| |
| i::Handle<i::JSFunction> foo_func_; |
| i::Handle<i::JSFunction> bar_func_; |
| |
| typedef std::map<size_t, SymbolInfo> SymbolMap; |
| typedef std::map<i::Address, SymbolInfo*> SymbolLocationMap; |
| typedef std::map<std::pair<SymbolInfo*, SymbolInfo*>, int> InvocationMap; |
| SymbolMap symbols_; |
| SymbolLocationMap symbol_locations_; |
| InvocationMap invocations_; |
| |
| static SetFunctionEntryHookTest* instance_; |
| }; |
| SetFunctionEntryHookTest* SetFunctionEntryHookTest::instance_ = NULL; |
| |
| |
| // Returns true if addr is in the range [start, start+len). |
| static bool Overlaps(i::Address start, size_t len, i::Address addr) { |
| if (start <= addr && start + len > addr) |
| return true; |
| |
| return false; |
| } |
| |
| void SetFunctionEntryHookTest::InsertSymbolAt(i::Address addr, |
| SymbolInfo* symbol) { |
| // Insert the symbol at the new location. |
| SymbolLocationMap::iterator it = |
| symbol_locations_.insert(std::make_pair(addr, symbol)).first; |
| // Now erase symbols to the left and right that overlap this one. |
| while (it != symbol_locations_.begin()) { |
| SymbolLocationMap::iterator left = it; |
| --left; |
| if (!Overlaps(left->first, left->second->size, addr)) |
| break; |
| symbol_locations_.erase(left); |
| } |
| |
| // Now erase symbols to the left and right that overlap this one. |
| while (true) { |
| SymbolLocationMap::iterator right = it; |
| ++right; |
| if (right == symbol_locations_.end()) |
| break; |
| if (!Overlaps(addr, symbol->size, right->first)) |
| break; |
| symbol_locations_.erase(right); |
| } |
| } |
| |
| |
| void SetFunctionEntryHookTest::OnJitEvent(const v8::JitCodeEvent* event) { |
| switch (event->type) { |
| case v8::JitCodeEvent::CODE_ADDED: { |
| CHECK(event->code_start != NULL); |
| CHECK_NE(0, static_cast<int>(event->code_len)); |
| CHECK(event->name.str != NULL); |
| size_t symbol_id = symbols_.size(); |
| |
| // Record the new symbol. |
| SymbolInfo& info = symbols_[symbol_id]; |
| info.id = symbol_id; |
| info.size = event->code_len; |
| info.name.assign(event->name.str, event->name.str + event->name.len); |
| |
| // And record it's location. |
| InsertSymbolAt(reinterpret_cast<i::Address>(event->code_start), &info); |
| } |
| break; |
| |
| case v8::JitCodeEvent::CODE_MOVED: { |
| // We would like to never see code move that we haven't seen before, |
| // but the code creation event does not happen until the line endings |
| // have been calculated (this is so that we can report the line in the |
| // script at which the function source is found, see |
| // Compiler::RecordFunctionCompilation) and the line endings |
| // calculations can cause a GC, which can move the newly created code |
| // before its existence can be logged. |
| SymbolLocationMap::iterator it( |
| symbol_locations_.find( |
| reinterpret_cast<i::Address>(event->code_start))); |
| if (it != symbol_locations_.end()) { |
| // Found a symbol at this location, move it. |
| SymbolInfo* info = it->second; |
| symbol_locations_.erase(it); |
| InsertSymbolAt(reinterpret_cast<i::Address>(event->new_code_start), |
| info); |
| } |
| } |
| default: |
| break; |
| } |
| } |
| |
| void SetFunctionEntryHookTest::OnEntryHook( |
| uintptr_t function, uintptr_t return_addr_location) { |
| // Get the function's code object. |
| i::Code* function_code = i::Code::GetCodeFromTargetAddress( |
| reinterpret_cast<i::Address>(function)); |
| CHECK(function_code != NULL); |
| |
| // Then try and look up the caller's code object. |
| i::Address caller = *reinterpret_cast<i::Address*>(return_addr_location); |
| |
| // Count the invocation. |
| SymbolInfo* caller_symbol = FindSymbolForAddr(caller); |
| SymbolInfo* function_symbol = |
| FindSymbolForAddr(reinterpret_cast<i::Address>(function)); |
| ++invocations_[std::make_pair(caller_symbol, function_symbol)]; |
| |
| if (!bar_func_.is_null() && function_code == bar_func_->code()) { |
| // Check that we have a symbol for the "bar" function at the right location. |
| SymbolLocationMap::iterator it( |
| symbol_locations_.find(function_code->instruction_start())); |
| CHECK(it != symbol_locations_.end()); |
| } |
| |
| if (!foo_func_.is_null() && function_code == foo_func_->code()) { |
| // Check that we have a symbol for "foo" at the right location. |
| SymbolLocationMap::iterator it( |
| symbol_locations_.find(function_code->instruction_start())); |
| CHECK(it != symbol_locations_.end()); |
| } |
| } |
| |
| |
| SymbolInfo* SetFunctionEntryHookTest::FindSymbolForAddr(i::Address addr) { |
| SymbolLocationMap::iterator it(symbol_locations_.lower_bound(addr)); |
| // Do we have a direct hit on a symbol? |
| if (it != symbol_locations_.end()) { |
| if (it->first == addr) |
| return it->second; |
| } |
| |
| // If not a direct hit, it'll have to be the previous symbol. |
| if (it == symbol_locations_.begin()) |
| return NULL; |
| |
| --it; |
| size_t offs = addr - it->first; |
| if (offs < it->second->size) |
| return it->second; |
| |
| return NULL; |
| } |
| |
| |
| int SetFunctionEntryHookTest::CountInvocations( |
| const char* caller_name, const char* function_name) { |
| InvocationMap::iterator it(invocations_.begin()); |
| int invocations = 0; |
| for (; it != invocations_.end(); ++it) { |
| SymbolInfo* caller = it->first.first; |
| SymbolInfo* function = it->first.second; |
| |
| // Filter out non-matching functions. |
| if (function_name != NULL) { |
| if (function->name.find(function_name) == std::string::npos) |
| continue; |
| } |
| |
| // Filter out non-matching callers. |
| if (caller_name != NULL) { |
| if (caller == NULL) |
| continue; |
| if (caller->name.find(caller_name) == std::string::npos) |
| continue; |
| } |
| |
| // It matches add the invocation count to the tally. |
| invocations += it->second; |
| } |
| |
| return invocations; |
| } |
| |
| |
| void SetFunctionEntryHookTest::RunLoopInNewEnv(v8::Isolate* isolate) { |
| v8::HandleScope outer(isolate); |
| v8::Local<Context> env = Context::New(isolate); |
| env->Enter(); |
| |
| Local<ObjectTemplate> t = ObjectTemplate::New(isolate); |
| t->Set(v8_str("asdf"), v8::FunctionTemplate::New(isolate, RuntimeCallback)); |
| env->Global()->Set(v8_str("obj"), t->NewInstance()); |
| |
| const char* script = |
| "function bar() {\n" |
| " var sum = 0;\n" |
| " for (i = 0; i < 100; ++i)\n" |
| " sum = foo(i);\n" |
| " return sum;\n" |
| "}\n" |
| "function foo(i) { return i * i; }\n" |
| "// Invoke on the runtime function.\n" |
| "obj.asdf()"; |
| CompileRun(script); |
| bar_func_ = i::Handle<i::JSFunction>::cast( |
| v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar")))); |
| DCHECK(!bar_func_.is_null()); |
| |
| foo_func_ = |
| i::Handle<i::JSFunction>::cast( |
| v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo")))); |
| DCHECK(!foo_func_.is_null()); |
| |
| v8::Handle<v8::Value> value = CompileRun("bar();"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); |
| |
| // Test the optimized codegen path. |
| value = CompileRun("%OptimizeFunctionOnNextCall(foo);" |
| "bar();"); |
| CHECK(value->IsNumber()); |
| CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); |
| |
| env->Exit(); |
| } |
| |
| |
| void SetFunctionEntryHookTest::RunTest() { |
| // Work in a new isolate throughout. |
| v8::Isolate::CreateParams create_params; |
| create_params.entry_hook = EntryHook; |
| create_params.code_event_handler = JitEvent; |
| create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| v8::Isolate* isolate = v8::Isolate::New(create_params); |
| |
| { |
| v8::Isolate::Scope scope(isolate); |
| |
| RunLoopInNewEnv(isolate); |
| |
| // Check the exepected invocation counts. |
| CHECK_EQ(2, CountInvocations(NULL, "bar")); |
| CHECK_EQ(200, CountInvocations("bar", "foo")); |
| CHECK_EQ(200, CountInvocations(NULL, "foo")); |
| |
| // Verify that we have an entry hook on some specific stubs. |
| CHECK_NE(0, CountInvocations(NULL, "CEntryStub")); |
| CHECK_NE(0, CountInvocations(NULL, "JSEntryStub")); |
| CHECK_NE(0, CountInvocations(NULL, "JSEntryTrampoline")); |
| } |
| isolate->Dispose(); |
| |
| Reset(); |
| |
| // Make sure a second isolate is unaffected by the previous entry hook. |
| create_params = v8::Isolate::CreateParams(); |
| create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| isolate = v8::Isolate::New(create_params); |
| { |
| v8::Isolate::Scope scope(isolate); |
| |
| // Reset the entry count to zero and set the entry hook. |
| RunLoopInNewEnv(isolate); |
| |
| // We should record no invocations in this isolate. |
| CHECK_EQ(0, static_cast<int>(invocations_.size())); |
| } |
| |
| isolate->Dispose(); |
| } |
| |
| |
| TEST(SetFunctionEntryHook) { |
| // FunctionEntryHook does not work well with experimental natives. |
| // Experimental natives are compiled during snapshot deserialization. |
| // This test breaks because InstallGetter (function from snapshot that |
| // only gets called from experimental natives) is compiled with entry hooks. |
| i::FLAG_allow_natives_syntax = true; |
| i::FLAG_use_inlining = false; |
| |
| SetFunctionEntryHookTest test; |
| test.RunTest(); |
| } |
| |
| |
| static i::HashMap* code_map = NULL; |
| static i::HashMap* jitcode_line_info = NULL; |
| static int saw_bar = 0; |
| static int move_events = 0; |
| |
| |
| static bool FunctionNameIs(const char* expected, |
| const v8::JitCodeEvent* event) { |
| // Log lines for functions are of the general form: |
| // "LazyCompile:<type><function_name>", where the type is one of |
| // "*", "~" or "". |
| static const char kPreamble[] = "LazyCompile:"; |
| static size_t kPreambleLen = sizeof(kPreamble) - 1; |
| |
| if (event->name.len < sizeof(kPreamble) - 1 || |
| strncmp(kPreamble, event->name.str, kPreambleLen) != 0) { |
| return false; |
| } |
| |
| const char* tail = event->name.str + kPreambleLen; |
| size_t tail_len = event->name.len - kPreambleLen; |
| size_t expected_len = strlen(expected); |
| if (tail_len > 1 && (*tail == '*' || *tail == '~')) { |
| --tail_len; |
| ++tail; |
| } |
| |
| // Check for tails like 'bar :1'. |
| if (tail_len > expected_len + 2 && |
| tail[expected_len] == ' ' && |
| tail[expected_len + 1] == ':' && |
| tail[expected_len + 2] && |
| !strncmp(tail, expected, expected_len)) { |
| return true; |
| } |
| |
| if (tail_len != expected_len) |
| return false; |
| |
| return strncmp(tail, expected, expected_len) == 0; |
| } |
| |
| |
| static void event_handler(const v8::JitCodeEvent* event) { |
| CHECK(event != NULL); |
| CHECK(code_map != NULL); |
| CHECK(jitcode_line_info != NULL); |
| |
| class DummyJitCodeLineInfo { |
| }; |
| |
| switch (event->type) { |
| case v8::JitCodeEvent::CODE_ADDED: { |
| CHECK(event->code_start != NULL); |
| CHECK_NE(0, static_cast<int>(event->code_len)); |
| CHECK(event->name.str != NULL); |
| i::HashMap::Entry* entry = code_map->LookupOrInsert( |
| event->code_start, i::ComputePointerHash(event->code_start)); |
| entry->value = reinterpret_cast<void*>(event->code_len); |
| |
| if (FunctionNameIs("bar", event)) { |
| ++saw_bar; |
| } |
| } |
| break; |
| |
| case v8::JitCodeEvent::CODE_MOVED: { |
| uint32_t hash = i::ComputePointerHash(event->code_start); |
| // We would like to never see code move that we haven't seen before, |
| // but the code creation event does not happen until the line endings |
| // have been calculated (this is so that we can report the line in the |
| // script at which the function source is found, see |
| // Compiler::RecordFunctionCompilation) and the line endings |
| // calculations can cause a GC, which can move the newly created code |
| // before its existence can be logged. |
| i::HashMap::Entry* entry = code_map->Lookup(event->code_start, hash); |
| if (entry != NULL) { |
| ++move_events; |
| |
| CHECK_EQ(reinterpret_cast<void*>(event->code_len), entry->value); |
| code_map->Remove(event->code_start, hash); |
| |
| entry = code_map->LookupOrInsert( |
| event->new_code_start, |
| i::ComputePointerHash(event->new_code_start)); |
| entry->value = reinterpret_cast<void*>(event->code_len); |
| } |
| } |
| break; |
| |
| case v8::JitCodeEvent::CODE_REMOVED: |
| // Object/code removal events are currently not dispatched from the GC. |
| CHECK(false); |
| break; |
| |
| // For CODE_START_LINE_INFO_RECORDING event, we will create one |
| // DummyJitCodeLineInfo data structure pointed by event->user_dat. We |
| // record it in jitcode_line_info. |
| case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: { |
| DummyJitCodeLineInfo* line_info = new DummyJitCodeLineInfo(); |
| v8::JitCodeEvent* temp_event = const_cast<v8::JitCodeEvent*>(event); |
| temp_event->user_data = line_info; |
| i::HashMap::Entry* entry = jitcode_line_info->LookupOrInsert( |
| line_info, i::ComputePointerHash(line_info)); |
| entry->value = reinterpret_cast<void*>(line_info); |
| } |
| break; |
| // For these two events, we will check whether the event->user_data |
| // data structure is created before during CODE_START_LINE_INFO_RECORDING |
| // event. And delete it in CODE_END_LINE_INFO_RECORDING event handling. |
| case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: { |
| CHECK(event->user_data != NULL); |
| uint32_t hash = i::ComputePointerHash(event->user_data); |
| i::HashMap::Entry* entry = |
| jitcode_line_info->Lookup(event->user_data, hash); |
| CHECK(entry != NULL); |
| delete reinterpret_cast<DummyJitCodeLineInfo*>(event->user_data); |
| } |
| break; |
| |
| case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: { |
| CHECK(event->user_data != NULL); |
| uint32_t hash = i::ComputePointerHash(event->user_data); |
| i::HashMap::Entry* entry = |
| jitcode_line_info->Lookup(event->user_data, hash); |
| CHECK(entry != NULL); |
| } |
| break; |
| |
| default: |
| // Impossible event. |
| CHECK(false); |
| break; |
| } |
| } |
| |
| |
| UNINITIALIZED_TEST(SetJitCodeEventHandler) { |
| i::FLAG_stress_compaction = true; |
| i::FLAG_incremental_marking = false; |
| if (i::FLAG_never_compact) return; |
| const char* script = |
| "function bar() {" |
| " var sum = 0;" |
| " for (i = 0; i < 10; ++i)" |
| " sum = foo(i);" |
| " return sum;" |
| "}" |
| "function foo(i) { return i; };" |
| "bar();"; |
| |
| // Run this test in a new isolate to make sure we don't |
| // have remnants of state from other code. |
| v8::Isolate::CreateParams create_params; |
| create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| v8::Isolate* isolate = v8::Isolate::New(create_params); |
| isolate->Enter(); |
| i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| i::Heap* heap = i_isolate->heap(); |
| |
| // Start with a clean slate. |
| heap->CollectAllAvailableGarbage("TestSetJitCodeEventHandler_Prepare"); |
| |
| { |
| v8::HandleScope scope(isolate); |
| i::HashMap code(MatchPointers); |
| code_map = &code; |
| |
| i::HashMap lineinfo(MatchPointers); |
| jitcode_line_info = &lineinfo; |
| |
| saw_bar = 0; |
| move_events = 0; |
| |
| isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, event_handler); |
| |
| // Generate new code objects sparsely distributed across several |
| // different fragmented code-space pages. |
| const int kIterations = 10; |
| for (int i = 0; i < kIterations; ++i) { |
| LocalContext env(isolate); |
| i::AlwaysAllocateScope always_allocate(i_isolate); |
| SimulateFullSpace(heap->code_space()); |
| CompileRun(script); |
| |
| // Keep a strong reference to the code object in the handle scope. |
| i::Handle<i::Code> bar_code(i::Handle<i::JSFunction>::cast( |
| v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar"))))->code()); |
| i::Handle<i::Code> foo_code(i::Handle<i::JSFunction>::cast( |
| v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo"))))->code()); |
| |
| // Clear the compilation cache to get more wastage. |
| reinterpret_cast<i::Isolate*>(isolate)->compilation_cache()->Clear(); |
| } |
| |
| // Force code movement. |
| heap->CollectAllAvailableGarbage("TestSetJitCodeEventHandler_Move"); |
| |
| isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); |
| |
| CHECK_LE(kIterations, saw_bar); |
| CHECK_LT(0, move_events); |
| |
| code_map = NULL; |
| jitcode_line_info = NULL; |
| } |
| |
| isolate->Exit(); |
| isolate->Dispose(); |
| |
| // Do this in a new isolate. |
| isolate = v8::Isolate::New(create_params); |
| isolate->Enter(); |
| |
| // Verify that we get callbacks for existing code objects when we |
| // request enumeration of existing code. |
| { |
| v8::HandleScope scope(isolate); |
| LocalContext env(isolate); |
| CompileRun(script); |
| |
| // Now get code through initial iteration. |
| i::HashMap code(MatchPointers); |
| code_map = &code; |
| |
| i::HashMap lineinfo(MatchPointers); |
| jitcode_line_info = &lineinfo; |
| |
| isolate->SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting, |
| event_handler); |
| isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); |
| |
| jitcode_line_info = NULL; |
| // We expect that we got some events. Note that if we could get code removal |
| // notifications, we could compare two collections, one created by listening |
| // from the time of creation of an isolate, and the other by subscribing |
| // with EnumExisting. |
| CHECK_LT(0u, code.occupancy()); |
| |
| code_map = NULL; |
| } |
| |
| isolate->Exit(); |
| isolate->Dispose(); |
| } |
| |
| |
| THREADED_TEST(ExternalAllocatedMemory) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope outer(isolate); |
| v8::Local<Context> env(Context::New(isolate)); |
| CHECK(!env.IsEmpty()); |
| const int64_t kSize = 1024*1024; |
| int64_t baseline = isolate->AdjustAmountOfExternalAllocatedMemory(0); |
| CHECK_EQ(baseline + kSize, |
| isolate->AdjustAmountOfExternalAllocatedMemory(kSize)); |
| CHECK_EQ(baseline, |
| isolate->AdjustAmountOfExternalAllocatedMemory(-kSize)); |
| const int64_t kTriggerGCSize = |
| v8::internal::Internals::kExternalAllocationLimit + 1; |
| CHECK_EQ(baseline + kTriggerGCSize, |
| isolate->AdjustAmountOfExternalAllocatedMemory(kTriggerGCSize)); |
| CHECK_EQ(baseline, |
| isolate->AdjustAmountOfExternalAllocatedMemory(-kTriggerGCSize)); |
| } |
| |
| |
| // Regression test for issue 54, object templates with internal fields |
| // but no accessors or interceptors did not get their internal field |
| // count set on instances. |
| THREADED_TEST(Regress54) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope outer(isolate); |
| static v8::Persistent<v8::ObjectTemplate> templ; |
| if (templ.IsEmpty()) { |
| v8::EscapableHandleScope inner(isolate); |
| v8::Local<v8::ObjectTemplate> local = v8::ObjectTemplate::New(isolate); |
| local->SetInternalFieldCount(1); |
| templ.Reset(isolate, inner.Escape(local)); |
| } |
| v8::Handle<v8::Object> result = |
| v8::Local<v8::ObjectTemplate>::New(isolate, templ)->NewInstance(); |
| CHECK_EQ(1, result->InternalFieldCount()); |
| } |
| |
| |
| // If part of the threaded tests, this test makes ThreadingTest fail |
| // on mac. |
| TEST(CatchStackOverflow) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| v8::Handle<v8::Value> result = CompileRun( |
| "function f() {" |
| " return f();" |
| "}" |
| "" |
| "f();"); |
| CHECK(result.IsEmpty()); |
| } |
| |
| |
| static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, |
| const char* resource_name, |
| int line_offset) { |
| v8::HandleScope scope(CcTest::isolate()); |
| v8::TryCatch try_catch(CcTest::isolate()); |
| v8::Handle<v8::Value> result = script->Run(); |
| CHECK(result.IsEmpty()); |
| CHECK(try_catch.HasCaught()); |
| v8::Handle<v8::Message> message = try_catch.Message(); |
| CHECK(!message.IsEmpty()); |
| CHECK_EQ(10 + line_offset, message->GetLineNumber()); |
| CHECK_EQ(91, message->GetStartPosition()); |
| CHECK_EQ(92, message->GetEndPosition()); |
| CHECK_EQ(2, message->GetStartColumn()); |
| CHECK_EQ(3, message->GetEndColumn()); |
| v8::String::Utf8Value line(message->GetSourceLine()); |
| CHECK_EQ(0, strcmp(" throw 'nirk';", *line)); |
| v8::String::Utf8Value name(message->GetScriptOrigin().ResourceName()); |
| CHECK_EQ(0, strcmp(resource_name, *name)); |
| } |
| |
| |
| THREADED_TEST(TryCatchSourceInfo) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Local<v8::String> source = v8_str( |
| "function Foo() {\n" |
| " return Bar();\n" |
| "}\n" |
| "\n" |
| "function Bar() {\n" |
| " return Baz();\n" |
| "}\n" |
| "\n" |
| "function Baz() {\n" |
| " throw 'nirk';\n" |
| "}\n" |
| "\n" |
| "Foo();\n"); |
| |
| const char* resource_name; |
| v8::Handle<v8::Script> script; |
| resource_name = "test.js"; |
| script = CompileWithOrigin(source, resource_name); |
| CheckTryCatchSourceInfo(script, resource_name, 0); |
| |
| resource_name = "test1.js"; |
| v8::ScriptOrigin origin1( |
| v8::String::NewFromUtf8(context->GetIsolate(), resource_name)); |
| script = v8::Script::Compile(source, &origin1); |
| CheckTryCatchSourceInfo(script, resource_name, 0); |
| |
| resource_name = "test2.js"; |
| v8::ScriptOrigin origin2( |
| v8::String::NewFromUtf8(context->GetIsolate(), resource_name), |
| v8::Integer::New(context->GetIsolate(), 7)); |
| script = v8::Script::Compile(source, &origin2); |
| CheckTryCatchSourceInfo(script, resource_name, 7); |
| } |
| |
| |
| THREADED_TEST(TryCatchSourceInfoForEOSError) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::TryCatch try_catch(context->GetIsolate()); |
| v8::Script::Compile(v8_str("!\n")); |
| CHECK(try_catch.HasCaught()); |
| v8::Handle<v8::Message> message = try_catch.Message(); |
| CHECK_EQ(1, message->GetLineNumber()); |
| CHECK_EQ(0, message->GetStartColumn()); |
| } |
| |
| |
| THREADED_TEST(CompilationCache) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Handle<v8::String> source0 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "1234"); |
| v8::Handle<v8::String> source1 = |
| v8::String::NewFromUtf8(context->GetIsolate(), "1234"); |
| v8::Handle<v8::Script> script0 = CompileWithOrigin(source0, "test.js"); |
| v8::Handle<v8::Script> script1 = CompileWithOrigin(source1, "test.js"); |
| v8::Handle<v8::Script> script2 = |
| v8::Script::Compile(source0); // different origin |
| CHECK_EQ(1234, script0->Run()->Int32Value()); |
| CHECK_EQ(1234, script1->Run()->Int32Value()); |
| CHECK_EQ(1234, script2->Run()->Int32Value()); |
| } |
| |
| |
| static void FunctionNameCallback( |
| const v8::FunctionCallbackInfo<v8::Value>& args) { |
| ApiTestFuzzer::Fuzz(); |
| args.GetReturnValue().Set(v8_num(42)); |
| } |
| |
| |
| THREADED_TEST(CallbackFunctionName) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> t = ObjectTemplate::New(isolate); |
| t->Set(v8_str("asdf"), |
| v8::FunctionTemplate::New(isolate, FunctionNameCallback)); |
| context->Global()->Set(v8_str("obj"), t->NewInstance()); |
| v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); |
| CHECK(value->IsString()); |
| v8::String::Utf8Value name(value); |
| CHECK_EQ(0, strcmp("asdf", *name)); |
| } |
| |
| |
| THREADED_TEST(DateAccess) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| v8::Handle<v8::Value> date = |
| v8::Date::New(context->GetIsolate(), 1224744689038.0); |
| CHECK(date->IsDate()); |
| CHECK_EQ(1224744689038.0, date.As<v8::Date>()->ValueOf()); |
| } |
| |
| |
| void CheckProperties(v8::Isolate* isolate, v8::Handle<v8::Value> val, |
| unsigned elmc, const char* elmv[]) { |
| v8::Handle<v8::Object> obj = val.As<v8::Object>(); |
| v8::Handle<v8::Array> props = obj->GetPropertyNames(); |
| CHECK_EQ(elmc, props->Length()); |
| for (unsigned i = 0; i < elmc; i++) { |
| v8::String::Utf8Value elm(props->Get(v8::Integer::New(isolate, i))); |
| CHECK_EQ(0, strcmp(elmv[i], *elm)); |
| } |
| } |
| |
| |
| void CheckOwnProperties(v8::Isolate* isolate, v8::Handle<v8::Value> val, |
| unsigned elmc, const char* elmv[]) { |
| v8::Handle<v8::Object> obj = val.As<v8::Object>(); |
| v8::Handle<v8::Array> props = obj->GetOwnPropertyNames(); |
| CHECK_EQ(elmc, props->Length()); |
| for (unsigned i = 0; i < elmc; i++) { |
| v8::String::Utf8Value elm(props->Get(v8::Integer::New(isolate, i))); |
| CHECK_EQ(0, strcmp(elmv[i], *elm)); |
| } |
| } |
| |
| |
| THREADED_TEST(PropertyEnumeration) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::Value> obj = CompileRun( |
| "var result = [];" |
| "result[0] = {};" |
| "result[1] = {a: 1, b: 2};" |
| "result[2] = [1, 2, 3];" |
| "var proto = {x: 1, y: 2, z: 3};" |
| "var x = { __proto__: proto, w: 0, z: 1 };" |
| "result[3] = x;" |
| "result;"); |
| v8::Handle<v8::Array> elms = obj.As<v8::Array>(); |
| CHECK_EQ(4u, elms->Length()); |
| int elmc0 = 0; |
| const char** elmv0 = NULL; |
| CheckProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 0)), elmc0, elmv0); |
| CheckOwnProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 0)), elmc0, elmv0); |
| int elmc1 = 2; |
| const char* elmv1[] = {"a", "b"}; |
| CheckProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 1)), elmc1, elmv1); |
| CheckOwnProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 1)), elmc1, elmv1); |
| int elmc2 = 3; |
| const char* elmv2[] = {"0", "1", "2"}; |
| CheckProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 2)), elmc2, elmv2); |
| CheckOwnProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 2)), elmc2, elmv2); |
| int elmc3 = 4; |
| const char* elmv3[] = {"w", "z", "x", "y"}; |
| CheckProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 3)), elmc3, elmv3); |
| int elmc4 = 2; |
| const char* elmv4[] = {"w", "z"}; |
| CheckOwnProperties( |
| isolate, elms->Get(v8::Integer::New(isolate, 3)), elmc4, elmv4); |
| } |
| |
| |
| THREADED_TEST(PropertyEnumeration2) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::Value> obj = CompileRun( |
| "var result = [];" |
| "result[0] = {};" |
| "result[1] = {a: 1, b: 2};" |
| "result[2] = [1, 2, 3];" |
| "var proto = {x: 1, y: 2, z: 3};" |
| "var x = { __proto__: proto, w: 0, z: 1 };" |
| "result[3] = x;" |
| "result;"); |
| v8::Handle<v8::Array> elms = obj.As<v8::Array>(); |
| CHECK_EQ(4u, elms->Length()); |
| int elmc0 = 0; |
| const char** elmv0 = NULL; |
| CheckProperties(isolate, |
| elms->Get(v8::Integer::New(isolate, 0)), elmc0, elmv0); |
| |
| v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(isolate, 0)); |
| v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames(); |
| CHECK_EQ(0u, props->Length()); |
| for (uint32_t i = 0; i < props->Length(); i++) { |
| printf("p[%u]\n", i); |
| } |
| } |
| |
| |
| THREADED_TEST(AccessChecksReenabledCorrectly) { |
| LocalContext context; |
| v8::Isolate* isolate = context->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| templ->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL); |
| templ->Set(v8_str("a"), v8_str("a")); |
| // Add more than 8 (see kMaxFastProperties) properties |
| // so that the constructor will force copying map. |
| // Cannot sprintf, gcc complains unsafety. |
| char buf[4]; |
| for (char i = '0'; i <= '9' ; i++) { |
| buf[0] = i; |
| for (char j = '0'; j <= '9'; j++) { |
| buf[1] = j; |
| for (char k = '0'; k <= '9'; k++) { |
| buf[2] = k; |
| buf[3] = 0; |
| templ->Set(v8_str(buf), v8::Number::New(isolate, k)); |
| } |
| } |
| } |
| |
| Local<v8::Object> instance_1 = templ->NewInstance(); |
| context->Global()->Set(v8_str("obj_1"), instance_1); |
| |
| Local<Value> value_1 = CompileRun("obj_1.a"); |
| CHECK(value_1.IsEmpty()); |
| |
| Local<v8::Object> instance_2 = templ->NewInstance(); |
| context->Global()->Set(v8_str("obj_2"), instance_2); |
| |
| Local<Value> value_2 = CompileRun("obj_2.a"); |
| CHECK(value_2.IsEmpty()); |
| } |
| |
| |
| THREADED_TEST(TurnOnAccessCheck) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| // Create an environment with access check to the global object disabled by |
| // default. |
| v8::Handle<v8::ObjectTemplate> global_template = |
| v8::ObjectTemplate::New(isolate); |
| global_template->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL, |
| v8::Handle<v8::Value>(), false); |
| v8::Local<Context> context = Context::New(isolate, NULL, global_template); |
| Context::Scope context_scope(context); |
| |
| // Set up a property and a number of functions. |
| context->Global()->Set(v8_str("a"), v8_num(1)); |
| CompileRun("function f1() {return a;}" |
| "function f2() {return a;}" |
| "function g1() {return h();}" |
| "function g2() {return h();}" |
| "function h() {return 1;}"); |
| Local<Function> f1 = |
| Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); |
| Local<Function> f2 = |
| Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); |
| Local<Function> g1 = |
| Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); |
| Local<Function> g2 = |
| Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); |
| Local<Function> h = |
| Local<Function>::Cast(context->Global()->Get(v8_str("h"))); |
| |
| // Get the global object. |
| v8::Handle<v8::Object> global = context->Global(); |
| |
| // Call f1 one time and f2 a number of times. This will ensure that f1 still |
| // uses the runtime system to retreive property a whereas f2 uses global load |
| // inline cache. |
| CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); |
| for (int i = 0; i < 4; i++) { |
| CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); |
| } |
| |
| // Same for g1 and g2. |
| CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); |
| for (int i = 0; i < 4; i++) { |
| CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); |
| } |
| |
| // Detach the global and turn on access check. |
| Local<Object> hidden_global = Local<Object>::Cast( |
| context->Global()->GetPrototype()); |
| context->DetachGlobal(); |
| hidden_global->TurnOnAccessCheck(); |
| |
| // Failing access check results in exception. |
| CHECK(f1->Call(global, 0, NULL).IsEmpty()); |
| CHECK(f2->Call(global, 0, NULL).IsEmpty()); |
| CHECK(g1->Call(global, 0, NULL).IsEmpty()); |
| CHECK(g2->Call(global, 0, NULL).IsEmpty()); |
| |
| // No failing access check when just returning a constant. |
| CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); |
| } |
| |
| |
| // Tests that ScriptData can be serialized and deserialized. |
| TEST(PreCompileSerialization) { |
| v8::V8::Initialize(); |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| HandleScope handle_scope(isolate); |
| |
| i::FLAG_min_preparse_length = 0; |
| const char* script = "function foo(a) { return a+1; }"; |
| v8::ScriptCompiler::Source source(v8_str(script)); |
| v8::ScriptCompiler::Compile(isolate, &source, |
| v8::ScriptCompiler::kProduceParserCache); |
| // Serialize. |
| const v8::ScriptCompiler::CachedData* cd = source.GetCachedData(); |
| i::byte* serialized_data = i::NewArray<i::byte>(cd->length); |
| i::MemCopy(serialized_data, cd->data, cd->length); |
| |
| // Deserialize. |
| i::ScriptData* deserialized = new i::ScriptData(serialized_data, cd->length); |
| |
| // Verify that the original is the same as the deserialized. |
| CHECK_EQ(cd->length, deserialized->length()); |
| CHECK_EQ(0, memcmp(cd->data, deserialized->data(), cd->length)); |
| |
| delete deserialized; |
| i::DeleteArray(serialized_data); |
| } |
| |
| |
| // This tests that we do not allow dictionary load/call inline caches |
| // to use functions that have not yet been compiled. The potential |
| // problem of loading a function that has not yet been compiled can |
| // arise because we share code between contexts via the compilation |
| // cache. |
| THREADED_TEST(DictionaryICLoadedFunction) { |
| v8::HandleScope scope(CcTest::isolate()); |
| // Test LoadIC. |
| for (int i = 0; i < 2; i++) { |
| LocalContext context; |
| context->Global()->Set(v8_str("tmp"), v8::True(CcTest::isolate())); |
| context->Global()->Delete(v8_str("tmp")); |
| CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); |
| } |
| // Test CallIC. |
| for (int i = 0; i < 2; i++) { |
| LocalContext context; |
| context->Global()->Set(v8_str("tmp"), v8::True(CcTest::isolate())); |
| context->Global()->Delete(v8_str("tmp")); |
| CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); |
| } |
| } |
| |
| |
| // Test that cross-context new calls use the context of the callee to |
| // create the new JavaScript object. |
| THREADED_TEST(CrossContextNew) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<Context> context0 = Context::New(isolate); |
| v8::Local<Context> context1 = Context::New(isolate); |
| |
| // Allow cross-domain access. |
| Local<String> token = v8_str("<security token>"); |
| context0->SetSecurityToken(token); |
| context1->SetSecurityToken(token); |
| |
| // Set an 'x' property on the Object prototype and define a |
| // constructor function in context0. |
| context0->Enter(); |
| CompileRun("Object.prototype.x = 42; function C() {};"); |
| context0->Exit(); |
| |
| // Call the constructor function from context0 and check that the |
| // result has the 'x' property. |
| context1->Enter(); |
| context1->Global()->Set(v8_str("other"), context0->Global()); |
| Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); |
| CHECK(value->IsInt32()); |
| CHECK_EQ(42, value->Int32Value()); |
| context1->Exit(); |
| } |
| |
| |
| // Verify that we can clone an object |
| TEST(ObjectClone) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| const char* sample = |
| "var rv = {};" \ |
| "rv.alpha = 'hello';" \ |
| "rv.beta = 123;" \ |
| "rv;"; |
| |
| // Create an object, verify basics. |
| Local<Value> val = CompileRun(sample); |
| CHECK(val->IsObject()); |
| Local<v8::Object> obj = val.As<v8::Object>(); |
| obj->Set(v8_str("gamma"), v8_str("cloneme")); |
| |
| CHECK(v8_str("hello")->Equals(obj->Get(v8_str("alpha")))); |
| CHECK(v8::Integer::New(isolate, 123)->Equals(obj->Get(v8_str("beta")))); |
| CHECK(v8_str("cloneme")->Equals(obj->Get(v8_str("gamma")))); |
| |
| // Clone it. |
| Local<v8::Object> clone = obj->Clone(); |
| CHECK(v8_str("hello")->Equals(clone->Get(v8_str("alpha")))); |
| CHECK(v8::Integer::New(isolate, 123)->Equals(clone->Get(v8_str("beta")))); |
| CHECK(v8_str("cloneme")->Equals(clone->Get(v8_str("gamma")))); |
| |
| // Set a property on the clone, verify each object. |
| clone->Set(v8_str("beta"), v8::Integer::New(isolate, 456)); |
| CHECK(v8::Integer::New(isolate, 123)->Equals(obj->Get(v8_str("beta")))); |
| CHECK(v8::Integer::New(isolate, 456)->Equals(clone->Get(v8_str("beta")))); |
| } |
| |
| |
| class OneByteVectorResource : public v8::String::ExternalOneByteStringResource { |
| public: |
| explicit OneByteVectorResource(i::Vector<const char> vector) |
| : data_(vector) {} |
| virtual ~OneByteVectorResource() {} |
| virtual size_t length() const { return data_.length(); } |
| virtual const char* data() const { return data_.start(); } |
| private: |
| i::Vector<const char> data_; |
| }; |
| |
| |
| class UC16VectorResource : public v8::String::ExternalStringResource { |
| public: |
| explicit UC16VectorResource(i::Vector<const i::uc16> vector) |
| : data_(vector) {} |
| virtual ~UC16VectorResource() {} |
| virtual size_t length() const { return data_.length(); } |
| virtual const i::uc16* data() const { return data_.start(); } |
| private: |
| i::Vector<const i::uc16> data_; |
| }; |
| |
| |
| static void MorphAString(i::String* string, |
| OneByteVectorResource* one_byte_resource, |
| UC16VectorResource* uc16_resource) { |
| CHECK(i::StringShape(string).IsExternal()); |
| if (string->IsOneByteRepresentation()) { |
| // Check old map is not internalized or long. |
| CHECK(string->map() == CcTest::heap()->external_one_byte_string_map()); |
| // Morph external string to be TwoByte string. |
| string->set_map(CcTest::heap()->external_string_map()); |
| i::ExternalTwoByteString* morphed = |
| i::ExternalTwoByteString::cast(string); |
| morphed->set_resource(uc16_resource); |
| } else { |
| // Check old map is not internalized or long. |
| CHECK(string->map() == CcTest::heap()->external_string_map()); |
| // Morph external string to be one-byte string. |
| string->set_map(CcTest::heap()->external_one_byte_string_map()); |
| i::ExternalOneByteString* morphed = i::ExternalOneByteString::cast(string); |
| morphed->set_resource(one_byte_resource); |
| } |
| } |
| |
| |
| // Test that we can still flatten a string if the components it is built up |
| // from have been turned into 16 bit strings in the mean time. |
| THREADED_TEST(MorphCompositeStringTest) { |
| char utf_buffer[129]; |
| const char* c_string = "Now is the time for all good men" |
| " to come to the aid of the party"; |
| uint16_t* two_byte_string = AsciiToTwoByteString(c_string); |
| { |
| LocalContext env; |
| i::Factory* factory = CcTest::i_isolate()->factory(); |
| v8::HandleScope scope(env->GetIsolate()); |
| OneByteVectorResource one_byte_resource( |
| i::Vector<const char>(c_string, i::StrLength(c_string))); |
| UC16VectorResource uc16_resource( |
| i::Vector<const uint16_t>(two_byte_string, |
| i::StrLength(c_string))); |
| |
| Local<String> lhs( |
| v8::Utils::ToLocal(factory->NewExternalStringFromOneByte( |
| &one_byte_resource).ToHandleChecked())); |
| Local<String> rhs( |
| v8::Utils::ToLocal(factory->NewExternalStringFromOneByte( |
| &one_byte_resource).ToHandleChecked())); |
| |
| env->Global()->Set(v8_str("lhs"), lhs); |
| env->Global()->Set(v8_str("rhs"), rhs); |
| |
| CompileRun( |
| "var cons = lhs + rhs;" |
| "var slice = lhs.substring(1, lhs.length - 1);" |
| "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); |
| |
| CHECK(lhs->IsOneByte()); |
| CHECK(rhs->IsOneByte()); |
| |
| MorphAString(*v8::Utils::OpenHandle(*lhs), &one_byte_resource, |
| &uc16_resource); |
| MorphAString(*v8::Utils::OpenHandle(*rhs), &one_byte_resource, |
| &uc16_resource); |
| |
| // This should UTF-8 without flattening, since everything is ASCII. |
| Handle<String> cons = v8_compile("cons")->Run().As<String>(); |
| CHECK_EQ(128, cons->Utf8Length()); |
| int nchars = -1; |
| CHECK_EQ(129, cons->WriteUtf8(utf_buffer, -1, &nchars)); |
| CHECK_EQ(128, nchars); |
| CHECK_EQ(0, strcmp( |
| utf_buffer, |
| "Now is the time for all good men to come to the aid of the party" |
| "Now is the time for all good men to come to the aid of the party")); |
| |
| // Now do some stuff to make sure the strings are flattened, etc. |
| CompileRun( |
| "/[^a-z]/.test(cons);" |
| "/[^a-z]/.test(slice);" |
| "/[^a-z]/.test(slice_on_cons);"); |
| const char* expected_cons = |
| "Now is the time for all good men to come to the aid of the party" |
| "Now is the time for all good men to come to the aid of the party"; |
| const char* expected_slice = |
| "ow is the time for all good men to come to the aid of the part"; |
| const char* expected_slice_on_cons = |
| "ow is the time for all good men to come to the aid of the party" |
| "Now is the time for all good men to come to the aid of the part"; |
| CHECK(String::NewFromUtf8(env->GetIsolate(), expected_cons) |
| ->Equals(env->Global()->Get(v8_str("cons")))); |
| CHECK(String::NewFromUtf8(env->GetIsolate(), expected_slice) |
| ->Equals(env->Global()->Get(v8_str("slice")))); |
| CHECK(String::NewFromUtf8(env->GetIsolate(), expected_slice_on_cons) |
| ->Equals(env->Global()->Get(v8_str("slice_on_cons")))); |
| } |
| i::DeleteArray(two_byte_string); |
| } |
| |
| |
| TEST(CompileExternalTwoByteSource) { |
| LocalContext context; |
| v8::HandleScope scope(context->GetIsolate()); |
| |
| // This is a very short list of sources, which currently is to check for a |
| // regression caused by r2703. |
| const char* one_byte_sources[] = { |
| "0.5", |
| "-0.5", // This mainly testes PushBack in the Scanner. |
| "--0.5", // This mainly testes PushBack in the Scanner. |
| NULL}; |
| |
| // Compile the sources as external two byte strings. |
| for (int i = 0; one_byte_sources[i] != NULL; i++) { |
| uint16_t* two_byte_string = AsciiToTwoByteString(one_byte_sources[i]); |
| TestResource* uc16_resource = new TestResource(two_byte_string); |
| v8::Local<v8::String> source = |
| v8::String::NewExternal(context->GetIsolate(), uc16_resource); |
| v8::Script::Compile(source); |
| } |
| } |
| |
| |
| #ifndef V8_INTERPRETED_REGEXP |
| |
| struct RegExpInterruptionData { |
| v8::base::Atomic32 loop_count; |
| UC16VectorResource* string_resource; |
| v8::Persistent<v8::String> string; |
| } regexp_interruption_data; |
| |
| |
| class RegExpInterruptionThread : public v8::base::Thread { |
| public: |
| explicit RegExpInterruptionThread(v8::Isolate* isolate) |
| : Thread(Options("TimeoutThread")), isolate_(isolate) {} |
| |
| virtual void Run() { |
| for (v8::base::NoBarrier_Store(®exp_interruption_data.loop_count, 0); |
| v8::base::NoBarrier_Load(®exp_interruption_data.loop_count) < 7; |
| v8::base::NoBarrier_AtomicIncrement( |
| ®exp_interruption_data.loop_count, 1)) { |
| // Wait a bit before requesting GC. |
| v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(50)); |
| reinterpret_cast<i::Isolate*>(isolate_)->stack_guard()->RequestGC(); |
| } |
| // Wait a bit before terminating. |
| v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(50)); |
| v8::V8::TerminateExecution(isolate_); |
| } |
| |
| private: |
| v8::Isolate* isolate_; |
| }; |
| |
| |
| void RunBeforeGC(v8::GCType type, v8::GCCallbackFlags flags) { |
| if (v8::base::NoBarrier_Load(®exp_interruption_data.loop_count) != 2) { |
| return; |
| } |
| v8::HandleScope scope(CcTest::isolate()); |
| v8::Local<v8::String> string = v8::Local<v8::String>::New( |
| CcTest::isolate(), regexp_interruption_data.string); |
| string->MakeExternal(regexp_interruption_data.string_resource); |
| } |
| |
| |
| // Test that RegExp execution can be interrupted. Specifically, we test |
| // * interrupting with GC |
| // * turn the subject string from one-byte internal to two-byte external string |
| // * force termination |
| TEST(RegExpInterruption) { |
| v8::HandleScope scope(CcTest::isolate()); |
| LocalContext env; |
| |
| RegExpInterruptionThread timeout_thread(CcTest::isolate()); |
| |
| v8::V8::AddGCPrologueCallback(RunBeforeGC); |
| static const char* one_byte_content = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| i::uc16* uc16_content = AsciiToTwoByteString(one_byte_content); |
| v8::Local<v8::String> string = v8_str(one_byte_content); |
| |
| CcTest::global()->Set(v8_str("a"), string); |
| regexp_interruption_data.string.Reset(CcTest::isolate(), string); |
| regexp_interruption_data.string_resource = new UC16VectorResource( |
| i::Vector<const i::uc16>(uc16_content, i::StrLength(one_byte_content))); |
| |
| v8::TryCatch try_catch(CcTest::isolate()); |
| timeout_thread.Start(); |
| |
| CompileRun("/((a*)*)*b/.exec(a)"); |
| CHECK(try_catch.HasTerminated()); |
| |
| timeout_thread.Join(); |
| |
| regexp_interruption_data.string.Reset(); |
| i::DeleteArray(uc16_content); |
| } |
| |
| #endif // V8_INTERPRETED_REGEXP |
| |
| |
| // Test that we cannot set a property on the global object if there |
| // is a read-only property in the prototype chain. |
| TEST(ReadOnlyPropertyInGlobalProto) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| LocalContext context(0, templ); |
| v8::Handle<v8::Object> global = context->Global(); |
| v8::Handle<v8::Object> global_proto = |
| v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); |
| global_proto->ForceSet(v8_str("x"), v8::Integer::New(isolate, 0), |
| v8::ReadOnly); |
| global_proto->ForceSet(v8_str("y"), v8::Integer::New(isolate, 0), |
| v8::ReadOnly); |
| // Check without 'eval' or 'with'. |
| v8::Handle<v8::Value> res = |
| CompileRun("function f() { x = 42; return x; }; f()"); |
| CHECK(v8::Integer::New(isolate, 0)->Equals(res)); |
| // Check with 'eval'. |
| res = CompileRun("function f() { eval('1'); y = 43; return y; }; f()"); |
| CHECK(v8::Integer::New(isolate, 0)->Equals(res)); |
| // Check with 'with'. |
| res = CompileRun("function f() { with (this) { y = 44 }; return y; }; f()"); |
| CHECK(v8::Integer::New(isolate, 0)->Equals(res)); |
| } |
| |
| static int force_set_set_count = 0; |
| static int force_set_get_count = 0; |
| bool pass_on_get = false; |
| |
| static void ForceSetGetter(v8::Local<v8::String> name, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| force_set_get_count++; |
| if (pass_on_get) { |
| return; |
| } |
| info.GetReturnValue().Set(3); |
| } |
| |
| static void ForceSetSetter(v8::Local<v8::String> name, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| force_set_set_count++; |
| } |
| |
| static void ForceSetInterceptGetter( |
| v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| CHECK(name->IsString()); |
| ForceSetGetter(Local<String>::Cast(name), info); |
| } |
| |
| static void ForceSetInterceptSetter( |
| v8::Local<v8::Name> name, v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| force_set_set_count++; |
| info.GetReturnValue().SetUndefined(); |
| } |
| |
| |
| TEST(ForceSet) { |
| force_set_get_count = 0; |
| force_set_set_count = 0; |
| pass_on_get = false; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| v8::Handle<v8::String> access_property = |
| v8::String::NewFromUtf8(isolate, "a"); |
| templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); |
| LocalContext context(NULL, templ); |
| v8::Handle<v8::Object> global = context->Global(); |
| |
| // Ordinary properties |
| v8::Handle<v8::String> simple_property = |
| v8::String::NewFromUtf8(isolate, "p"); |
| global->ForceSet(simple_property, v8::Int32::New(isolate, 4), v8::ReadOnly); |
| CHECK_EQ(4, global->Get(simple_property)->Int32Value()); |
| // This should fail because the property is read-only |
| global->Set(simple_property, v8::Int32::New(isolate, 5)); |
| CHECK_EQ(4, global->Get(simple_property)->Int32Value()); |
| // This should succeed even though the property is read-only |
| global->ForceSet(simple_property, v8::Int32::New(isolate, 6)); |
| CHECK_EQ(6, global->Get(simple_property)->Int32Value()); |
| |
| // Accessors |
| CHECK_EQ(0, force_set_set_count); |
| CHECK_EQ(0, force_set_get_count); |
| CHECK_EQ(3, global->Get(access_property)->Int32Value()); |
| // CHECK_EQ the property shouldn't override it, just call the setter |
| // which in this case does nothing. |
| global->Set(access_property, v8::Int32::New(isolate, 7)); |
| CHECK_EQ(3, global->Get(access_property)->Int32Value()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(2, force_set_get_count); |
| // ForceSet doesn't call the accessors for now. |
| // TODO(verwaest): Update once blink doesn't rely on ForceSet to delete api |
| // accessors. |
| global->ForceSet(access_property, v8::Int32::New(isolate, 8)); |
| CHECK_EQ(8, global->Get(access_property)->Int32Value()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(2, force_set_get_count); |
| } |
| |
| |
| TEST(ForceSetWithInterceptor) { |
| force_set_get_count = 0; |
| force_set_set_count = 0; |
| pass_on_get = false; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::HandleScope scope(isolate); |
| v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| ForceSetInterceptGetter, ForceSetInterceptSetter)); |
| LocalContext context(NULL, templ); |
| v8::Handle<v8::Object> global = context->Global(); |
| |
| v8::Handle<v8::String> some_property = |
| v8::String::NewFromUtf8(isolate, "a"); |
| CHECK_EQ(0, force_set_set_count); |
| CHECK_EQ(0, force_set_get_count); |
| CHECK_EQ(3, global->Get(some_property)->Int32Value()); |
| // Setting the property shouldn't override it, just call the setter |
| // which in this case does nothing. |
| global->Set(some_property, v8::Int32::New(isolate, 7)); |
| CHECK_EQ(3, global->Get(some_property)->Int32Value()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(2, force_set_get_count); |
| // Getting the property when the interceptor returns an empty handle |
| // should yield undefined, since the property isn't present on the |
| // object itself yet. |
| pass_on_get = true; |
| CHECK(global->Get(some_property)->IsUndefined()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(3, force_set_get_count); |
| // Forcing the property to be set should cause the value to be |
| // set locally without calling the interceptor. |
| global->ForceSet(some_property, v8::Int32::New(isolate, 8)); |
| CHECK_EQ(8, global->Get(some_property)->Int32Value()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(4, force_set_get_count); |
| // Reenabling the interceptor should cause it to take precedence over |
| // the property |
| pass_on_get = false; |
| CHECK_EQ(3, global->Get(some_property)->Int32Value()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(5, force_set_get_count); |
| // The interceptor should also work for other properties |
| CHECK_EQ(3, global->Get(v8::String::NewFromUtf8(isolate, "b")) |
| ->Int32Value()); |
| CHECK_EQ(1, force_set_set_count); |
| CHECK_EQ(6, force_set_get_count); |
| } |
| |
| |
| TEST(CreateDataProperty) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| CompileRun( |
| "var a = {};" |
| "var b = [];" |
| "Object.defineProperty(a, 'foo', {value: 23});" |
| "Object.defineProperty(a, 'bar', {value: 23, configurable: true});"); |
| |
| v8::Local<v8::Object> obj = |
| v8::Local<v8::Object>::Cast(env->Global()->Get(v8_str("a"))); |
| v8::Local<v8::Array> arr = |
| v8::Local<v8::Array>::Cast(env->Global()->Get(v8_str("b"))); |
| { |
| // Can't change a non-configurable properties. |
| v8::TryCatch try_catch(isolate); |
| CHECK(!obj->CreateDataProperty(env.local(), v8_str("foo"), |
| v8::Integer::New(isolate, 42)).FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| CHECK(obj->CreateDataProperty(env.local(), v8_str("bar"), |
| v8::Integer::New(isolate, 42)).FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| v8::Local<v8::Value> val = |
| obj->Get(env.local(), v8_str("bar")).ToLocalChecked(); |
| CHECK(val->IsNumber()); |
| CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| } |
| |
| { |
| // Set a regular property. |
| v8::TryCatch try_catch(isolate); |
| CHECK(obj->CreateDataProperty(env.local(), v8_str("blub"), |
| v8::Integer::New(isolate, 42)).FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| v8::Local<v8::Value> val = |
| obj->Get(env.local(), v8_str("blub")).ToLocalChecked(); |
| CHECK(val->IsNumber()); |
| CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| } |
| |
| { |
| // Set an indexed property. |
| v8::TryCatch try_catch(isolate); |
| CHECK(obj->CreateDataProperty(env.local(), v8_str("1"), |
| v8::Integer::New(isolate, 42)).FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked(); |
| CHECK(val->IsNumber()); |
| CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| } |
| |
| { |
| // Special cases for arrays. |
| v8::TryCatch try_catch(isolate); |
| CHECK(!arr->CreateDataProperty(env.local(), v8_str("length"), |
| v8::Integer::New(isolate, 1)).FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| { |
| // Special cases for arrays: index exceeds the array's length |
| v8::TryCatch try_catch(isolate); |
| CHECK(arr->CreateDataProperty(env.local(), 1, v8::Integer::New(isolate, 23)) |
| .FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| CHECK_EQ(2U, arr->Length()); |
| v8::Local<v8::Value> val = arr->Get(env.local(), 1).ToLocalChecked(); |
| CHECK(val->IsNumber()); |
| CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust()); |
| |
| // Set an existing entry. |
| CHECK(arr->CreateDataProperty(env.local(), 0, v8::Integer::New(isolate, 42)) |
| .FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| val = arr->Get(env.local(), 0).ToLocalChecked(); |
| CHECK(val->IsNumber()); |
| CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| } |
| |
| CompileRun("Object.freeze(a);"); |
| { |
| // Can't change non-extensible objects. |
| v8::TryCatch try_catch(isolate); |
| CHECK(!obj->CreateDataProperty(env.local(), v8_str("baz"), |
| v8::Integer::New(isolate, 42)).FromJust()); |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| templ->SetAccessCheckCallbacks(AccessAlwaysBlocked, NULL); |
| v8::Local<v8::Object> access_checked = |
| templ->NewInstance(env.local()).ToLocalChecked(); |
| { |
| v8::TryCatch try_catch(isolate); |
| CHECK(access_checked->CreateDataProperty(env.local(), v8_str("foo"), |
| v8::Integer::New(isolate, 42)) |
| .IsNothing()); |
| CHECK(try_catch.HasCaught()); |
| } |
| } |
| |
| |
| TEST(DefineOwnProperty) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| CompileRun( |
|