| 1 | // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 | // Redistribution and use in source and binary forms, with or without |
| 3 | // modification, are permitted provided that the following conditions are |
| 4 | // met: |
| 5 | // |
| 6 | // * Redistributions of source code must retain the above copyright |
| 7 | // notice, this list of conditions and the following disclaimer. |
| 8 | // * Redistributions in binary form must reproduce the above |
| 9 | // copyright notice, this list of conditions and the following |
| 10 | // disclaimer in the documentation and/or other materials provided |
| 11 | // with the distribution. |
| 12 | // * Neither the name of Google Inc. nor the names of its |
| 13 | // contributors may be used to endorse or promote products derived |
| 14 | // from this software without specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | |
| 28 | #include <climits> |
| 29 | #include <csignal> |
| 30 | #include <map> |
| 31 | #include <memory> |
| 32 | #include <string> |
| 33 | |
| 34 | #include "test/cctest/test-api.h" |
| 35 | |
| 36 | #if V8_OS_POSIX |
| 37 | #include <unistd.h> // NOLINT |
| 38 | #endif |
| 39 | |
| 40 | #include "include/v8-util.h" |
| 41 | #include "src/api-inl.h" |
| 42 | #include "src/arguments.h" |
| 43 | #include "src/base/overflowing-math.h" |
| 44 | #include "src/base/platform/platform.h" |
| 45 | #include "src/compilation-cache.h" |
| 46 | #include "src/debug/debug.h" |
| 47 | #include "src/execution.h" |
| 48 | #include "src/feedback-vector-inl.h" |
| 49 | #include "src/feedback-vector.h" |
| 50 | #include "src/futex-emulation.h" |
| 51 | #include "src/global-handles.h" |
| 52 | #include "src/heap/heap-inl.h" |
| 53 | #include "src/heap/incremental-marking.h" |
| 54 | #include "src/heap/local-allocator.h" |
| 55 | #include "src/lookup.h" |
| 56 | #include "src/objects-inl.h" |
| 57 | #include "src/objects/hash-table-inl.h" |
| 58 | #include "src/objects/js-array-buffer-inl.h" |
| 59 | #include "src/objects/js-array-inl.h" |
| 60 | #include "src/objects/js-promise-inl.h" |
| 61 | #include "src/profiler/cpu-profiler.h" |
| 62 | #include "src/unicode-inl.h" |
| 63 | #include "src/utils.h" |
| 64 | #include "src/vm-state.h" |
| 65 | #include "src/wasm/wasm-js.h" |
| 66 | #include "test/cctest/heap/heap-tester.h" |
| 67 | #include "test/cctest/heap/heap-utils.h" |
| 68 | #include "test/cctest/wasm/wasm-run-utils.h" |
| 69 | #include "test/common/wasm/wasm-macro-gen.h" |
| 70 | |
| 71 | static const bool kLogThreading = false; |
| 72 | |
| 73 | using ::v8::Array; |
| 74 | using ::v8::Boolean; |
| 75 | using ::v8::BooleanObject; |
| 76 | using ::v8::Context; |
| 77 | using ::v8::Extension; |
| 78 | using ::v8::Function; |
| 79 | using ::v8::FunctionTemplate; |
| 80 | using ::v8::HandleScope; |
| 81 | using ::v8::Local; |
| 82 | using ::v8::Maybe; |
| 83 | using ::v8::Message; |
| 84 | using ::v8::MessageCallback; |
| 85 | using ::v8::Module; |
| 86 | using ::v8::Name; |
| 87 | using ::v8::None; |
| 88 | using ::v8::Object; |
| 89 | using ::v8::ObjectTemplate; |
| 90 | using ::v8::Persistent; |
| 91 | using ::v8::PropertyAttribute; |
| 92 | using ::v8::Script; |
| 93 | using ::v8::StackTrace; |
| 94 | using ::v8::String; |
| 95 | using ::v8::Symbol; |
| 96 | using ::v8::TryCatch; |
| 97 | using ::v8::Undefined; |
| 98 | using ::v8::V8; |
| 99 | using ::v8::Value; |
| 100 | |
| 101 | |
| 102 | #define THREADED_PROFILED_TEST(Name) \ |
| 103 | static void Test##Name(); \ |
| 104 | TEST(Name##WithProfiler) { \ |
| 105 | RunWithProfiler(&Test##Name); \ |
| 106 | } \ |
| 107 | THREADED_TEST(Name) |
| 108 | |
| 109 | void RunWithProfiler(void (*test)()) { |
| 110 | LocalContext env; |
| 111 | v8::HandleScope scope(env->GetIsolate()); |
| 112 | v8::Local<v8::String> profile_name = v8_str("my_profile1" ); |
| 113 | v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate()); |
| 114 | cpu_profiler->StartProfiling(profile_name); |
| 115 | (*test)(); |
| 116 | reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->DeleteAllProfiles(); |
| 117 | cpu_profiler->Dispose(); |
| 118 | } |
| 119 | |
| 120 | |
| 121 | static int signature_callback_count; |
| 122 | static Local<Value> signature_expected_receiver; |
| 123 | static void IncrementingSignatureCallback( |
| 124 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 125 | ApiTestFuzzer::Fuzz(); |
| 126 | signature_callback_count++; |
| 127 | CHECK(signature_expected_receiver->Equals( |
| 128 | args.GetIsolate()->GetCurrentContext(), |
| 129 | args.Holder()) |
| 130 | .FromJust()); |
| 131 | CHECK(signature_expected_receiver->Equals( |
| 132 | args.GetIsolate()->GetCurrentContext(), |
| 133 | args.This()) |
| 134 | .FromJust()); |
| 135 | v8::Local<v8::Array> result = |
| 136 | v8::Array::New(args.GetIsolate(), args.Length()); |
| 137 | for (int i = 0; i < args.Length(); i++) { |
| 138 | CHECK(result->Set(args.GetIsolate()->GetCurrentContext(), |
| 139 | v8::Integer::New(args.GetIsolate(), i), args[i]) |
| 140 | .FromJust()); |
| 141 | } |
| 142 | args.GetReturnValue().Set(result); |
| 143 | } |
| 144 | |
| 145 | |
| 146 | static void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 147 | info.GetReturnValue().Set(42); |
| 148 | } |
| 149 | |
| 150 | |
| 151 | // Tests that call v8::V8::Dispose() cannot be threaded. |
| 152 | UNINITIALIZED_TEST(InitializeAndDisposeOnce) { |
| 153 | CHECK(v8::V8::Initialize()); |
| 154 | CHECK(v8::V8::Dispose()); |
| 155 | } |
| 156 | |
| 157 | |
| 158 | // Tests that call v8::V8::Dispose() cannot be threaded. |
| 159 | UNINITIALIZED_TEST(InitializeAndDisposeMultiple) { |
| 160 | for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); |
| 161 | for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize()); |
| 162 | for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); |
| 163 | for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize()); |
| 164 | for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); |
| 165 | } |
| 166 | |
| 167 | // Tests that Smi::kZero is set up properly. |
| 168 | UNINITIALIZED_TEST(SmiZero) { CHECK_EQ(i::Smi::kZero, i::Smi::kZero); } |
| 169 | |
| 170 | THREADED_TEST(Handles) { |
| 171 | v8::HandleScope scope(CcTest::isolate()); |
| 172 | Local<Context> local_env; |
| 173 | { |
| 174 | LocalContext env; |
| 175 | local_env = env.local(); |
| 176 | } |
| 177 | |
| 178 | // Local context should still be live. |
| 179 | CHECK(!local_env.IsEmpty()); |
| 180 | local_env->Enter(); |
| 181 | |
| 182 | v8::Local<v8::Primitive> undef = v8::Undefined(CcTest::isolate()); |
| 183 | CHECK(!undef.IsEmpty()); |
| 184 | CHECK(undef->IsUndefined()); |
| 185 | |
| 186 | const char* source = "1 + 2 + 3" ; |
| 187 | Local<Script> script = v8_compile(source); |
| 188 | CHECK_EQ(6, v8_run_int32value(script)); |
| 189 | |
| 190 | local_env->Exit(); |
| 191 | } |
| 192 | |
| 193 | |
| 194 | THREADED_TEST(IsolateOfContext) { |
| 195 | v8::HandleScope scope(CcTest::isolate()); |
| 196 | v8::Local<Context> env = Context::New(CcTest::isolate()); |
| 197 | |
| 198 | CHECK(!env->GetIsolate()->InContext()); |
| 199 | CHECK(env->GetIsolate() == CcTest::isolate()); |
| 200 | env->Enter(); |
| 201 | CHECK(env->GetIsolate()->InContext()); |
| 202 | CHECK(env->GetIsolate() == CcTest::isolate()); |
| 203 | env->Exit(); |
| 204 | CHECK(!env->GetIsolate()->InContext()); |
| 205 | CHECK(env->GetIsolate() == CcTest::isolate()); |
| 206 | } |
| 207 | |
| 208 | static void TestSignatureLooped(const char* operation, Local<Value> receiver, |
| 209 | v8::Isolate* isolate) { |
| 210 | i::ScopedVector<char> source(200); |
| 211 | i::SNPrintF(source, |
| 212 | "for (var i = 0; i < 10; i++) {" |
| 213 | " %s" |
| 214 | "}" , |
| 215 | operation); |
| 216 | signature_callback_count = 0; |
| 217 | signature_expected_receiver = receiver; |
| 218 | bool expected_to_throw = receiver.IsEmpty(); |
| 219 | v8::TryCatch try_catch(isolate); |
| 220 | CompileRun(source.start()); |
| 221 | CHECK_EQ(expected_to_throw, try_catch.HasCaught()); |
| 222 | if (!expected_to_throw) { |
| 223 | CHECK_EQ(10, signature_callback_count); |
| 224 | } else { |
| 225 | CHECK(v8_str("TypeError: Illegal invocation" ) |
| 226 | ->Equals(isolate->GetCurrentContext(), |
| 227 | try_catch.Exception() |
| 228 | ->ToString(isolate->GetCurrentContext()) |
| 229 | .ToLocalChecked()) |
| 230 | .FromJust()); |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | static void TestSignatureOptimized(const char* operation, Local<Value> receiver, |
| 235 | v8::Isolate* isolate) { |
| 236 | i::ScopedVector<char> source(200); |
| 237 | i::SNPrintF(source, |
| 238 | "function test() {" |
| 239 | " %s" |
| 240 | "}" |
| 241 | "try { test() } catch(e) {}" |
| 242 | "try { test() } catch(e) {}" |
| 243 | "%%OptimizeFunctionOnNextCall(test);" |
| 244 | "test()" , |
| 245 | operation); |
| 246 | signature_callback_count = 0; |
| 247 | signature_expected_receiver = receiver; |
| 248 | bool expected_to_throw = receiver.IsEmpty(); |
| 249 | v8::TryCatch try_catch(isolate); |
| 250 | CompileRun(source.start()); |
| 251 | CHECK_EQ(expected_to_throw, try_catch.HasCaught()); |
| 252 | if (!expected_to_throw) { |
| 253 | CHECK_EQ(3, signature_callback_count); |
| 254 | } else { |
| 255 | CHECK(v8_str("TypeError: Illegal invocation" ) |
| 256 | ->Equals(isolate->GetCurrentContext(), |
| 257 | try_catch.Exception() |
| 258 | ->ToString(isolate->GetCurrentContext()) |
| 259 | .ToLocalChecked()) |
| 260 | .FromJust()); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | static void TestSignature(const char* operation, Local<Value> receiver, |
| 265 | v8::Isolate* isolate) { |
| 266 | TestSignatureLooped(operation, receiver, isolate); |
| 267 | TestSignatureOptimized(operation, receiver, isolate); |
| 268 | } |
| 269 | |
| 270 | THREADED_TEST(ReceiverSignature) { |
| 271 | i::FLAG_allow_natives_syntax = true; |
| 272 | LocalContext env; |
| 273 | v8::Isolate* isolate = env->GetIsolate(); |
| 274 | v8::HandleScope scope(isolate); |
| 275 | // Setup templates. |
| 276 | v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); |
| 277 | v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun); |
| 278 | v8::Local<v8::FunctionTemplate> callback_sig = v8::FunctionTemplate::New( |
| 279 | isolate, IncrementingSignatureCallback, Local<Value>(), sig); |
| 280 | v8::Local<v8::FunctionTemplate> callback = |
| 281 | v8::FunctionTemplate::New(isolate, IncrementingSignatureCallback); |
| 282 | v8::Local<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(isolate); |
| 283 | sub_fun->Inherit(fun); |
| 284 | v8::Local<v8::FunctionTemplate> direct_sub_fun = |
| 285 | v8::FunctionTemplate::New(isolate); |
| 286 | direct_sub_fun->Inherit(fun); |
| 287 | v8::Local<v8::FunctionTemplate> unrel_fun = |
| 288 | v8::FunctionTemplate::New(isolate); |
| 289 | // Install properties. |
| 290 | v8::Local<v8::ObjectTemplate> fun_proto = fun->PrototypeTemplate(); |
| 291 | fun_proto->Set(v8_str("prop_sig" ), callback_sig); |
| 292 | fun_proto->Set(v8_str("prop" ), callback); |
| 293 | fun_proto->SetAccessorProperty( |
| 294 | v8_str("accessor_sig" ), callback_sig, callback_sig); |
| 295 | fun_proto->SetAccessorProperty(v8_str("accessor" ), callback, callback); |
| 296 | // Instantiate templates. |
| 297 | Local<Value> fun_instance = |
| 298 | fun->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked(); |
| 299 | Local<Value> sub_fun_instance = |
| 300 | sub_fun->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked(); |
| 301 | // Instance template with properties. |
| 302 | v8::Local<v8::ObjectTemplate> direct_instance_templ = |
| 303 | direct_sub_fun->InstanceTemplate(); |
| 304 | direct_instance_templ->Set(v8_str("prop_sig" ), callback_sig); |
| 305 | direct_instance_templ->Set(v8_str("prop" ), callback); |
| 306 | direct_instance_templ->SetAccessorProperty(v8_str("accessor_sig" ), |
| 307 | callback_sig, callback_sig); |
| 308 | direct_instance_templ->SetAccessorProperty(v8_str("accessor" ), callback, |
| 309 | callback); |
| 310 | Local<Value> direct_instance = |
| 311 | direct_instance_templ->NewInstance(env.local()).ToLocalChecked(); |
| 312 | // Setup global variables. |
| 313 | CHECK(env->Global() |
| 314 | ->Set(env.local(), v8_str("Fun" ), |
| 315 | fun->GetFunction(env.local()).ToLocalChecked()) |
| 316 | .FromJust()); |
| 317 | CHECK(env->Global() |
| 318 | ->Set(env.local(), v8_str("UnrelFun" ), |
| 319 | unrel_fun->GetFunction(env.local()).ToLocalChecked()) |
| 320 | .FromJust()); |
| 321 | CHECK(env->Global() |
| 322 | ->Set(env.local(), v8_str("fun_instance" ), fun_instance) |
| 323 | .FromJust()); |
| 324 | CHECK(env->Global() |
| 325 | ->Set(env.local(), v8_str("sub_fun_instance" ), sub_fun_instance) |
| 326 | .FromJust()); |
| 327 | CHECK(env->Global() |
| 328 | ->Set(env.local(), v8_str("direct_instance" ), direct_instance) |
| 329 | .FromJust()); |
| 330 | CompileRun( |
| 331 | "var accessor_sig_key = 'accessor_sig';" |
| 332 | "var accessor_key = 'accessor';" |
| 333 | "var prop_sig_key = 'prop_sig';" |
| 334 | "var prop_key = 'prop';" |
| 335 | "" |
| 336 | "function copy_props(obj) {" |
| 337 | " var keys = [accessor_sig_key, accessor_key, prop_sig_key, prop_key];" |
| 338 | " var source = Fun.prototype;" |
| 339 | " for (var i in keys) {" |
| 340 | " var key = keys[i];" |
| 341 | " var desc = Object.getOwnPropertyDescriptor(source, key);" |
| 342 | " Object.defineProperty(obj, key, desc);" |
| 343 | " }" |
| 344 | "}" |
| 345 | "" |
| 346 | "var plain = {};" |
| 347 | "copy_props(plain);" |
| 348 | "var unrelated = new UnrelFun();" |
| 349 | "copy_props(unrelated);" |
| 350 | "var inherited = { __proto__: fun_instance };" |
| 351 | "var inherited_direct = { __proto__: direct_instance };" ); |
| 352 | // Test with and without ICs |
| 353 | const char* test_objects[] = { |
| 354 | "fun_instance" , "sub_fun_instance" , "direct_instance" , "plain" , |
| 355 | "unrelated" , "inherited" , "inherited_direct" }; |
| 356 | unsigned bad_signature_start_offset = 3; |
| 357 | for (unsigned i = 0; i < arraysize(test_objects); i++) { |
| 358 | i::ScopedVector<char> source(200); |
| 359 | i::SNPrintF( |
| 360 | source, "var test_object = %s; test_object" , test_objects[i]); |
| 361 | Local<Value> test_object = CompileRun(source.start()); |
| 362 | TestSignature("test_object.prop();" , test_object, isolate); |
| 363 | TestSignature("test_object.accessor;" , test_object, isolate); |
| 364 | TestSignature("test_object[accessor_key];" , test_object, isolate); |
| 365 | TestSignature("test_object.accessor = 1;" , test_object, isolate); |
| 366 | TestSignature("test_object[accessor_key] = 1;" , test_object, isolate); |
| 367 | if (i >= bad_signature_start_offset) test_object = Local<Value>(); |
| 368 | TestSignature("test_object.prop_sig();" , test_object, isolate); |
| 369 | TestSignature("test_object.accessor_sig;" , test_object, isolate); |
| 370 | TestSignature("test_object[accessor_sig_key];" , test_object, isolate); |
| 371 | TestSignature("test_object.accessor_sig = 1;" , test_object, isolate); |
| 372 | TestSignature("test_object[accessor_sig_key] = 1;" , test_object, isolate); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | |
| 377 | THREADED_TEST(HulIgennem) { |
| 378 | LocalContext env; |
| 379 | v8::Isolate* isolate = env->GetIsolate(); |
| 380 | v8::HandleScope scope(isolate); |
| 381 | v8::Local<v8::Primitive> undef = v8::Undefined(isolate); |
| 382 | Local<String> undef_str = undef->ToString(env.local()).ToLocalChecked(); |
| 383 | char* value = i::NewArray<char>(undef_str->Utf8Length(isolate) + 1); |
| 384 | undef_str->WriteUtf8(isolate, value); |
| 385 | CHECK_EQ(0, strcmp(value, "undefined" )); |
| 386 | i::DeleteArray(value); |
| 387 | } |
| 388 | |
| 389 | |
| 390 | THREADED_TEST(Access) { |
| 391 | LocalContext env; |
| 392 | v8::Isolate* isolate = env->GetIsolate(); |
| 393 | v8::HandleScope scope(isolate); |
| 394 | Local<v8::Object> obj = v8::Object::New(isolate); |
| 395 | Local<Value> foo_before = |
| 396 | obj->Get(env.local(), v8_str("foo" )).ToLocalChecked(); |
| 397 | CHECK(foo_before->IsUndefined()); |
| 398 | Local<String> bar_str = v8_str("bar" ); |
| 399 | CHECK(obj->Set(env.local(), v8_str("foo" ), bar_str).FromJust()); |
| 400 | Local<Value> foo_after = |
| 401 | obj->Get(env.local(), v8_str("foo" )).ToLocalChecked(); |
| 402 | CHECK(!foo_after->IsUndefined()); |
| 403 | CHECK(foo_after->IsString()); |
| 404 | CHECK(bar_str->Equals(env.local(), foo_after).FromJust()); |
| 405 | |
| 406 | CHECK(obj->Set(env.local(), v8_str("foo" ), bar_str).ToChecked()); |
| 407 | bool result; |
| 408 | CHECK(obj->Set(env.local(), v8_str("foo" ), bar_str).To(&result)); |
| 409 | CHECK(result); |
| 410 | } |
| 411 | |
| 412 | |
| 413 | THREADED_TEST(AccessElement) { |
| 414 | LocalContext env; |
| 415 | v8::HandleScope scope(env->GetIsolate()); |
| 416 | Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| 417 | Local<Value> before = obj->Get(env.local(), 1).ToLocalChecked(); |
| 418 | CHECK(before->IsUndefined()); |
| 419 | Local<String> bar_str = v8_str("bar" ); |
| 420 | CHECK(obj->Set(env.local(), 1, bar_str).FromJust()); |
| 421 | Local<Value> after = obj->Get(env.local(), 1).ToLocalChecked(); |
| 422 | CHECK(!after->IsUndefined()); |
| 423 | CHECK(after->IsString()); |
| 424 | CHECK(bar_str->Equals(env.local(), after).FromJust()); |
| 425 | |
| 426 | Local<v8::Array> value = CompileRun("[\"a\", \"b\"]" ).As<v8::Array>(); |
| 427 | CHECK(v8_str("a" ) |
| 428 | ->Equals(env.local(), value->Get(env.local(), 0).ToLocalChecked()) |
| 429 | .FromJust()); |
| 430 | CHECK(v8_str("b" ) |
| 431 | ->Equals(env.local(), value->Get(env.local(), 1).ToLocalChecked()) |
| 432 | .FromJust()); |
| 433 | } |
| 434 | |
| 435 | |
| 436 | THREADED_TEST(Script) { |
| 437 | LocalContext env; |
| 438 | v8::HandleScope scope(env->GetIsolate()); |
| 439 | const char* source = "1 + 2 + 3" ; |
| 440 | Local<Script> script = v8_compile(source); |
| 441 | CHECK_EQ(6, v8_run_int32value(script)); |
| 442 | } |
| 443 | |
| 444 | |
| 445 | class TestResource: public String::ExternalStringResource { |
| 446 | public: |
| 447 | explicit TestResource(uint16_t* data, int* counter = nullptr, |
| 448 | bool owning_data = true) |
| 449 | : data_(data), length_(0), counter_(counter), owning_data_(owning_data) { |
| 450 | while (data[length_]) ++length_; |
| 451 | } |
| 452 | |
| 453 | ~TestResource() override { |
| 454 | if (owning_data_) i::DeleteArray(data_); |
| 455 | if (counter_ != nullptr) ++*counter_; |
| 456 | } |
| 457 | |
| 458 | const uint16_t* data() const override { return data_; } |
| 459 | |
| 460 | size_t length() const override { return length_; } |
| 461 | |
| 462 | private: |
| 463 | uint16_t* data_; |
| 464 | size_t length_; |
| 465 | int* counter_; |
| 466 | bool owning_data_; |
| 467 | }; |
| 468 | |
| 469 | |
| 470 | class TestOneByteResource : public String::ExternalOneByteStringResource { |
| 471 | public: |
| 472 | explicit TestOneByteResource(const char* data, int* counter = nullptr, |
| 473 | size_t offset = 0) |
| 474 | : orig_data_(data), |
| 475 | data_(data + offset), |
| 476 | length_(strlen(data) - offset), |
| 477 | counter_(counter) {} |
| 478 | |
| 479 | ~TestOneByteResource() override { |
| 480 | i::DeleteArray(orig_data_); |
| 481 | if (counter_ != nullptr) ++*counter_; |
| 482 | } |
| 483 | |
| 484 | const char* data() const override { return data_; } |
| 485 | |
| 486 | size_t length() const override { return length_; } |
| 487 | |
| 488 | private: |
| 489 | const char* orig_data_; |
| 490 | const char* data_; |
| 491 | size_t length_; |
| 492 | int* counter_; |
| 493 | }; |
| 494 | |
| 495 | |
| 496 | THREADED_TEST(ScriptUsingStringResource) { |
| 497 | int dispose_count = 0; |
| 498 | const char* c_source = "1 + 2 * 3" ; |
| 499 | uint16_t* two_byte_source = AsciiToTwoByteString(c_source); |
| 500 | { |
| 501 | LocalContext env; |
| 502 | v8::HandleScope scope(env->GetIsolate()); |
| 503 | TestResource* resource = new TestResource(two_byte_source, &dispose_count); |
| 504 | Local<String> source = |
| 505 | String::NewExternalTwoByte(env->GetIsolate(), resource) |
| 506 | .ToLocalChecked(); |
| 507 | Local<Script> script = v8_compile(source); |
| 508 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 509 | CHECK(value->IsNumber()); |
| 510 | CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| 511 | CHECK(source->IsExternal()); |
| 512 | CHECK_EQ(resource, |
| 513 | static_cast<TestResource*>(source->GetExternalStringResource())); |
| 514 | String::Encoding encoding = String::UNKNOWN_ENCODING; |
| 515 | CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), |
| 516 | source->GetExternalStringResourceBase(&encoding)); |
| 517 | CHECK_EQ(String::TWO_BYTE_ENCODING, encoding); |
| 518 | CcTest::CollectAllGarbage(); |
| 519 | CHECK_EQ(0, dispose_count); |
| 520 | } |
| 521 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 522 | CcTest::CollectAllAvailableGarbage(); |
| 523 | CHECK_EQ(1, dispose_count); |
| 524 | } |
| 525 | |
| 526 | |
| 527 | THREADED_TEST(ScriptUsingOneByteStringResource) { |
| 528 | int dispose_count = 0; |
| 529 | const char* c_source = "1 + 2 * 3" ; |
| 530 | { |
| 531 | LocalContext env; |
| 532 | v8::HandleScope scope(env->GetIsolate()); |
| 533 | TestOneByteResource* resource = |
| 534 | new TestOneByteResource(i::StrDup(c_source), &dispose_count); |
| 535 | Local<String> source = |
| 536 | String::NewExternalOneByte(env->GetIsolate(), resource) |
| 537 | .ToLocalChecked(); |
| 538 | CHECK(source->IsExternalOneByte()); |
| 539 | CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), |
| 540 | source->GetExternalOneByteStringResource()); |
| 541 | String::Encoding encoding = String::UNKNOWN_ENCODING; |
| 542 | CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), |
| 543 | source->GetExternalStringResourceBase(&encoding)); |
| 544 | CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); |
| 545 | Local<Script> script = v8_compile(source); |
| 546 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 547 | CHECK(value->IsNumber()); |
| 548 | CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| 549 | CcTest::CollectAllGarbage(); |
| 550 | CHECK_EQ(0, dispose_count); |
| 551 | } |
| 552 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 553 | CcTest::CollectAllAvailableGarbage(); |
| 554 | CHECK_EQ(1, dispose_count); |
| 555 | } |
| 556 | |
| 557 | |
| 558 | THREADED_TEST(ScriptMakingExternalString) { |
| 559 | int dispose_count = 0; |
| 560 | uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3" ); |
| 561 | { |
| 562 | LocalContext env; |
| 563 | v8::HandleScope scope(env->GetIsolate()); |
| 564 | Local<String> source = |
| 565 | String::NewFromTwoByte(env->GetIsolate(), two_byte_source, |
| 566 | v8::NewStringType::kNormal) |
| 567 | .ToLocalChecked(); |
| 568 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 569 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 570 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 571 | CHECK(!source->IsExternal()); |
| 572 | CHECK(!source->IsExternalOneByte()); |
| 573 | String::Encoding encoding = String::UNKNOWN_ENCODING; |
| 574 | CHECK(!source->GetExternalStringResourceBase(&encoding)); |
| 575 | CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); |
| 576 | bool success = source->MakeExternal(new TestResource(two_byte_source, |
| 577 | &dispose_count)); |
| 578 | CHECK(success); |
| 579 | Local<Script> script = v8_compile(source); |
| 580 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 581 | CHECK(value->IsNumber()); |
| 582 | CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| 583 | CcTest::CollectAllGarbage(); |
| 584 | CHECK_EQ(0, dispose_count); |
| 585 | } |
| 586 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 587 | CcTest::CollectAllGarbage(); |
| 588 | CHECK_EQ(1, dispose_count); |
| 589 | } |
| 590 | |
| 591 | |
| 592 | THREADED_TEST(ScriptMakingExternalOneByteString) { |
| 593 | int dispose_count = 0; |
| 594 | const char* c_source = "1 + 2 * 3" ; |
| 595 | { |
| 596 | LocalContext env; |
| 597 | v8::HandleScope scope(env->GetIsolate()); |
| 598 | Local<String> source = v8_str(c_source); |
| 599 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 600 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 601 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 602 | bool success = source->MakeExternal( |
| 603 | new TestOneByteResource(i::StrDup(c_source), &dispose_count)); |
| 604 | CHECK(success); |
| 605 | Local<Script> script = v8_compile(source); |
| 606 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 607 | CHECK(value->IsNumber()); |
| 608 | CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| 609 | CcTest::CollectAllGarbage(); |
| 610 | CHECK_EQ(0, dispose_count); |
| 611 | } |
| 612 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 613 | CcTest::CollectAllGarbage(); |
| 614 | CHECK_EQ(1, dispose_count); |
| 615 | } |
| 616 | |
| 617 | |
| 618 | TEST(MakingExternalStringConditions) { |
| 619 | LocalContext env; |
| 620 | v8::HandleScope scope(env->GetIsolate()); |
| 621 | |
| 622 | // Free some space in the new space so that we can check freshness. |
| 623 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 624 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 625 | |
| 626 | uint16_t* two_byte_string = AsciiToTwoByteString("s1" ); |
| 627 | Local<String> tiny_local_string = |
| 628 | String::NewFromTwoByte(env->GetIsolate(), two_byte_string, |
| 629 | v8::NewStringType::kNormal) |
| 630 | .ToLocalChecked(); |
| 631 | i::DeleteArray(two_byte_string); |
| 632 | |
| 633 | two_byte_string = AsciiToTwoByteString("s1234" ); |
| 634 | Local<String> local_string = |
| 635 | String::NewFromTwoByte(env->GetIsolate(), two_byte_string, |
| 636 | v8::NewStringType::kNormal) |
| 637 | .ToLocalChecked(); |
| 638 | i::DeleteArray(two_byte_string); |
| 639 | |
| 640 | // We should refuse to externalize new space strings. |
| 641 | CHECK(!local_string->CanMakeExternal()); |
| 642 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 643 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 644 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 645 | // Old space strings should be accepted. |
| 646 | CHECK(local_string->CanMakeExternal()); |
| 647 | |
| 648 | // Tiny strings are not in-place externalizable when pointer compression is |
| 649 | // enabled. |
| 650 | CHECK_EQ(i::kTaggedSize == i::kSystemPointerSize, |
| 651 | tiny_local_string->CanMakeExternal()); |
| 652 | } |
| 653 | |
| 654 | |
| 655 | TEST(MakingExternalOneByteStringConditions) { |
| 656 | LocalContext env; |
| 657 | v8::HandleScope scope(env->GetIsolate()); |
| 658 | |
| 659 | // Free some space in the new space so that we can check freshness. |
| 660 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 661 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 662 | |
| 663 | Local<String> tiny_local_string = v8_str("s" ); |
| 664 | Local<String> local_string = v8_str("s1234" ); |
| 665 | // We should refuse to externalize new space strings. |
| 666 | CHECK(!local_string->CanMakeExternal()); |
| 667 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 668 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 669 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 670 | // Old space strings should be accepted. |
| 671 | CHECK(local_string->CanMakeExternal()); |
| 672 | |
| 673 | // Tiny strings are not in-place externalizable when pointer compression is |
| 674 | // enabled. |
| 675 | CHECK_EQ(i::kTaggedSize == i::kSystemPointerSize, |
| 676 | tiny_local_string->CanMakeExternal()); |
| 677 | } |
| 678 | |
| 679 | |
| 680 | TEST(MakingExternalUnalignedOneByteString) { |
| 681 | LocalContext env; |
| 682 | v8::HandleScope scope(env->GetIsolate()); |
| 683 | |
| 684 | CompileRun("function cons(a, b) { return a + b; }" |
| 685 | "function slice(a) { return a.substring(1); }" ); |
| 686 | // Create a cons string that will land in old pointer space. |
| 687 | Local<String> cons = Local<String>::Cast(CompileRun( |
| 688 | "cons('abcdefghijklm', 'nopqrstuvwxyz');" )); |
| 689 | // Create a sliced string that will land in old pointer space. |
| 690 | Local<String> slice = Local<String>::Cast(CompileRun( |
| 691 | "slice('abcdefghijklmnopqrstuvwxyz');" )); |
| 692 | |
| 693 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 694 | i::heap::SimulateFullSpace(CcTest::heap()->old_space()); |
| 695 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 696 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 697 | |
| 698 | // Turn into external string with unaligned resource data. |
| 699 | const char* c_cons = "_abcdefghijklmnopqrstuvwxyz" ; |
| 700 | bool success = cons->MakeExternal( |
| 701 | new TestOneByteResource(i::StrDup(c_cons), nullptr, 1)); |
| 702 | CHECK(success); |
| 703 | const char* c_slice = "_bcdefghijklmnopqrstuvwxyz" ; |
| 704 | success = slice->MakeExternal( |
| 705 | new TestOneByteResource(i::StrDup(c_slice), nullptr, 1)); |
| 706 | CHECK(success); |
| 707 | |
| 708 | // Trigger GCs and force evacuation. |
| 709 | CcTest::CollectAllGarbage(); |
| 710 | CcTest::heap()->CollectAllGarbage(i::Heap::kReduceMemoryFootprintMask, |
| 711 | i::GarbageCollectionReason::kTesting); |
| 712 | } |
| 713 | |
| 714 | THREADED_TEST(UsingExternalString) { |
| 715 | i::Factory* factory = CcTest::i_isolate()->factory(); |
| 716 | { |
| 717 | v8::HandleScope scope(CcTest::isolate()); |
| 718 | uint16_t* two_byte_string = AsciiToTwoByteString("test string" ); |
| 719 | Local<String> string = |
| 720 | String::NewExternalTwoByte(CcTest::isolate(), |
| 721 | new TestResource(two_byte_string)) |
| 722 | .ToLocalChecked(); |
| 723 | i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| 724 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 725 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 726 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 727 | i::Handle<i::String> isymbol = |
| 728 | factory->InternalizeString(istring); |
| 729 | CHECK(isymbol->IsInternalizedString()); |
| 730 | } |
| 731 | CcTest::CollectAllGarbage(); |
| 732 | CcTest::CollectAllGarbage(); |
| 733 | } |
| 734 | |
| 735 | |
| 736 | THREADED_TEST(UsingExternalOneByteString) { |
| 737 | i::Factory* factory = CcTest::i_isolate()->factory(); |
| 738 | { |
| 739 | v8::HandleScope scope(CcTest::isolate()); |
| 740 | const char* one_byte_string = "test string" ; |
| 741 | Local<String> string = |
| 742 | String::NewExternalOneByte( |
| 743 | CcTest::isolate(), |
| 744 | new TestOneByteResource(i::StrDup(one_byte_string))) |
| 745 | .ToLocalChecked(); |
| 746 | i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| 747 | // Trigger GCs so that the newly allocated string moves to old gen. |
| 748 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 749 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 750 | i::Handle<i::String> isymbol = |
| 751 | factory->InternalizeString(istring); |
| 752 | CHECK(isymbol->IsInternalizedString()); |
| 753 | } |
| 754 | CcTest::CollectAllGarbage(); |
| 755 | CcTest::CollectAllGarbage(); |
| 756 | } |
| 757 | |
| 758 | |
| 759 | class RandomLengthResource : public v8::String::ExternalStringResource { |
| 760 | public: |
| 761 | explicit RandomLengthResource(int length) : length_(length) {} |
| 762 | const uint16_t* data() const override { return string_; } |
| 763 | size_t length() const override { return length_; } |
| 764 | |
| 765 | private: |
| 766 | uint16_t string_[10]; |
| 767 | int length_; |
| 768 | }; |
| 769 | |
| 770 | |
| 771 | class RandomLengthOneByteResource |
| 772 | : public v8::String::ExternalOneByteStringResource { |
| 773 | public: |
| 774 | explicit RandomLengthOneByteResource(int length) : length_(length) {} |
| 775 | const char* data() const override { return string_; } |
| 776 | size_t length() const override { return length_; } |
| 777 | |
| 778 | private: |
| 779 | char string_[10]; |
| 780 | int length_; |
| 781 | }; |
| 782 | |
| 783 | |
| 784 | THREADED_TEST(NewExternalForVeryLongString) { |
| 785 | auto isolate = CcTest::isolate(); |
| 786 | { |
| 787 | v8::HandleScope scope(isolate); |
| 788 | v8::TryCatch try_catch(isolate); |
| 789 | RandomLengthOneByteResource r(1 << 30); |
| 790 | v8::MaybeLocal<v8::String> maybe_str = |
| 791 | v8::String::NewExternalOneByte(isolate, &r); |
| 792 | CHECK(maybe_str.IsEmpty()); |
| 793 | CHECK(!try_catch.HasCaught()); |
| 794 | } |
| 795 | |
| 796 | { |
| 797 | v8::HandleScope scope(isolate); |
| 798 | v8::TryCatch try_catch(isolate); |
| 799 | RandomLengthResource r(1 << 30); |
| 800 | v8::MaybeLocal<v8::String> maybe_str = |
| 801 | v8::String::NewExternalTwoByte(isolate, &r); |
| 802 | CHECK(maybe_str.IsEmpty()); |
| 803 | CHECK(!try_catch.HasCaught()); |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | TEST(ScavengeExternalString) { |
| 808 | ManualGCScope manual_gc_scope; |
| 809 | i::FLAG_stress_compaction = false; |
| 810 | i::FLAG_gc_global = false; |
| 811 | int dispose_count = 0; |
| 812 | bool in_young_generation = false; |
| 813 | { |
| 814 | v8::HandleScope scope(CcTest::isolate()); |
| 815 | uint16_t* two_byte_string = AsciiToTwoByteString("test string" ); |
| 816 | Local<String> string = |
| 817 | String::NewExternalTwoByte( |
| 818 | CcTest::isolate(), |
| 819 | new TestResource(two_byte_string, &dispose_count)) |
| 820 | .ToLocalChecked(); |
| 821 | i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| 822 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 823 | in_young_generation = i::Heap::InYoungGeneration(*istring); |
| 824 | CHECK_IMPLIES(!in_young_generation, |
| 825 | CcTest::heap()->old_space()->Contains(*istring)); |
| 826 | CHECK_EQ(0, dispose_count); |
| 827 | } |
| 828 | CcTest::CollectGarbage(in_young_generation ? i::NEW_SPACE : i::OLD_SPACE); |
| 829 | CHECK_EQ(1, dispose_count); |
| 830 | } |
| 831 | |
| 832 | TEST(ScavengeExternalOneByteString) { |
| 833 | ManualGCScope manual_gc_scope; |
| 834 | i::FLAG_stress_compaction = false; |
| 835 | i::FLAG_gc_global = false; |
| 836 | int dispose_count = 0; |
| 837 | bool in_young_generation = false; |
| 838 | { |
| 839 | v8::HandleScope scope(CcTest::isolate()); |
| 840 | const char* one_byte_string = "test string" ; |
| 841 | Local<String> string = |
| 842 | String::NewExternalOneByte( |
| 843 | CcTest::isolate(), |
| 844 | new TestOneByteResource(i::StrDup(one_byte_string), &dispose_count)) |
| 845 | .ToLocalChecked(); |
| 846 | i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); |
| 847 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 848 | in_young_generation = i::Heap::InYoungGeneration(*istring); |
| 849 | CHECK_IMPLIES(!in_young_generation, |
| 850 | CcTest::heap()->old_space()->Contains(*istring)); |
| 851 | CHECK_EQ(0, dispose_count); |
| 852 | } |
| 853 | CcTest::CollectGarbage(in_young_generation ? i::NEW_SPACE : i::OLD_SPACE); |
| 854 | CHECK_EQ(1, dispose_count); |
| 855 | } |
| 856 | |
| 857 | |
| 858 | class TestOneByteResourceWithDisposeControl : public TestOneByteResource { |
| 859 | public: |
| 860 | // Only used by non-threaded tests, so it can use static fields. |
| 861 | static int dispose_calls; |
| 862 | static int dispose_count; |
| 863 | |
| 864 | TestOneByteResourceWithDisposeControl(const char* data, bool dispose) |
| 865 | : TestOneByteResource(data, &dispose_count), dispose_(dispose) {} |
| 866 | |
| 867 | void Dispose() override { |
| 868 | ++dispose_calls; |
| 869 | if (dispose_) delete this; |
| 870 | } |
| 871 | private: |
| 872 | bool dispose_; |
| 873 | }; |
| 874 | |
| 875 | |
| 876 | int TestOneByteResourceWithDisposeControl::dispose_count = 0; |
| 877 | int TestOneByteResourceWithDisposeControl::dispose_calls = 0; |
| 878 | |
| 879 | |
| 880 | TEST(ExternalStringWithDisposeHandling) { |
| 881 | const char* c_source = "1 + 2 * 3" ; |
| 882 | |
| 883 | // Use a stack allocated external string resource allocated object. |
| 884 | TestOneByteResourceWithDisposeControl::dispose_count = 0; |
| 885 | TestOneByteResourceWithDisposeControl::dispose_calls = 0; |
| 886 | TestOneByteResourceWithDisposeControl res_stack(i::StrDup(c_source), false); |
| 887 | { |
| 888 | LocalContext env; |
| 889 | v8::HandleScope scope(env->GetIsolate()); |
| 890 | Local<String> source = |
| 891 | String::NewExternalOneByte(env->GetIsolate(), &res_stack) |
| 892 | .ToLocalChecked(); |
| 893 | Local<Script> script = v8_compile(source); |
| 894 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 895 | CHECK(value->IsNumber()); |
| 896 | CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| 897 | CcTest::CollectAllAvailableGarbage(); |
| 898 | CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| 899 | } |
| 900 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 901 | CcTest::CollectAllAvailableGarbage(); |
| 902 | CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls); |
| 903 | CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| 904 | |
| 905 | // Use a heap allocated external string resource allocated object. |
| 906 | TestOneByteResourceWithDisposeControl::dispose_count = 0; |
| 907 | TestOneByteResourceWithDisposeControl::dispose_calls = 0; |
| 908 | TestOneByteResource* res_heap = |
| 909 | new TestOneByteResourceWithDisposeControl(i::StrDup(c_source), true); |
| 910 | { |
| 911 | LocalContext env; |
| 912 | v8::HandleScope scope(env->GetIsolate()); |
| 913 | Local<String> source = |
| 914 | String::NewExternalOneByte(env->GetIsolate(), res_heap) |
| 915 | .ToLocalChecked(); |
| 916 | Local<Script> script = v8_compile(source); |
| 917 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 918 | CHECK(value->IsNumber()); |
| 919 | CHECK_EQ(7, value->Int32Value(env.local()).FromJust()); |
| 920 | CcTest::CollectAllAvailableGarbage(); |
| 921 | CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); |
| 922 | } |
| 923 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 924 | CcTest::CollectAllAvailableGarbage(); |
| 925 | CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls); |
| 926 | CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_count); |
| 927 | } |
| 928 | |
| 929 | |
| 930 | THREADED_TEST(StringConcat) { |
| 931 | { |
| 932 | LocalContext env; |
| 933 | v8::Isolate* isolate = env->GetIsolate(); |
| 934 | v8::HandleScope scope(isolate); |
| 935 | const char* one_byte_string_1 = "function a_times_t" ; |
| 936 | const char* two_byte_string_1 = "wo_plus_b(a, b) {return " ; |
| 937 | const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + " ; |
| 938 | const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + " ; |
| 939 | const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + " ; |
| 940 | const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + " ; |
| 941 | const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);" ; |
| 942 | Local<String> left = v8_str(one_byte_string_1); |
| 943 | |
| 944 | uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); |
| 945 | Local<String> right = |
| 946 | String::NewFromTwoByte(env->GetIsolate(), two_byte_source, |
| 947 | v8::NewStringType::kNormal) |
| 948 | .ToLocalChecked(); |
| 949 | i::DeleteArray(two_byte_source); |
| 950 | |
| 951 | Local<String> source = String::Concat(isolate, left, right); |
| 952 | right = String::NewExternalOneByte( |
| 953 | env->GetIsolate(), |
| 954 | new TestOneByteResource(i::StrDup(one_byte_extern_1))) |
| 955 | .ToLocalChecked(); |
| 956 | source = String::Concat(isolate, source, right); |
| 957 | right = String::NewExternalTwoByte( |
| 958 | env->GetIsolate(), |
| 959 | new TestResource(AsciiToTwoByteString(two_byte_extern_1))) |
| 960 | .ToLocalChecked(); |
| 961 | source = String::Concat(isolate, source, right); |
| 962 | right = v8_str(one_byte_string_2); |
| 963 | source = String::Concat(isolate, source, right); |
| 964 | |
| 965 | two_byte_source = AsciiToTwoByteString(two_byte_string_2); |
| 966 | right = String::NewFromTwoByte(env->GetIsolate(), two_byte_source, |
| 967 | v8::NewStringType::kNormal) |
| 968 | .ToLocalChecked(); |
| 969 | i::DeleteArray(two_byte_source); |
| 970 | |
| 971 | source = String::Concat(isolate, source, right); |
| 972 | right = String::NewExternalTwoByte( |
| 973 | env->GetIsolate(), |
| 974 | new TestResource(AsciiToTwoByteString(two_byte_extern_2))) |
| 975 | .ToLocalChecked(); |
| 976 | source = String::Concat(isolate, source, right); |
| 977 | Local<Script> script = v8_compile(source); |
| 978 | Local<Value> value = script->Run(env.local()).ToLocalChecked(); |
| 979 | CHECK(value->IsNumber()); |
| 980 | CHECK_EQ(68, value->Int32Value(env.local()).FromJust()); |
| 981 | } |
| 982 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 983 | CcTest::CollectAllGarbage(); |
| 984 | CcTest::CollectAllGarbage(); |
| 985 | } |
| 986 | |
| 987 | |
| 988 | THREADED_TEST(GlobalProperties) { |
| 989 | LocalContext env; |
| 990 | v8::HandleScope scope(env->GetIsolate()); |
| 991 | v8::Local<v8::Object> global = env->Global(); |
| 992 | CHECK(global->Set(env.local(), v8_str("pi" ), v8_num(3.1415926)).FromJust()); |
| 993 | Local<Value> pi = global->Get(env.local(), v8_str("pi" )).ToLocalChecked(); |
| 994 | CHECK_EQ(3.1415926, pi->NumberValue(env.local()).FromJust()); |
| 995 | } |
| 996 | |
| 997 | |
| 998 | static void handle_callback_impl(const v8::FunctionCallbackInfo<Value>& info, |
| 999 | i::Address callback) { |
| 1000 | ApiTestFuzzer::Fuzz(); |
| 1001 | CheckReturnValue(info, callback); |
| 1002 | info.GetReturnValue().Set(v8_str("bad value" )); |
| 1003 | info.GetReturnValue().Set(v8_num(102)); |
| 1004 | } |
| 1005 | |
| 1006 | |
| 1007 | static void handle_callback(const v8::FunctionCallbackInfo<Value>& info) { |
| 1008 | return handle_callback_impl(info, FUNCTION_ADDR(handle_callback)); |
| 1009 | } |
| 1010 | |
| 1011 | |
| 1012 | static void handle_callback_2(const v8::FunctionCallbackInfo<Value>& info) { |
| 1013 | return handle_callback_impl(info, FUNCTION_ADDR(handle_callback_2)); |
| 1014 | } |
| 1015 | |
| 1016 | static void construct_callback( |
| 1017 | const v8::FunctionCallbackInfo<Value>& info) { |
| 1018 | ApiTestFuzzer::Fuzz(); |
| 1019 | CheckReturnValue(info, FUNCTION_ADDR(construct_callback)); |
| 1020 | CHECK( |
| 1021 | info.This() |
| 1022 | ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x" ), v8_num(1)) |
| 1023 | .FromJust()); |
| 1024 | CHECK( |
| 1025 | info.This() |
| 1026 | ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("y" ), v8_num(2)) |
| 1027 | .FromJust()); |
| 1028 | info.GetReturnValue().Set(v8_str("bad value" )); |
| 1029 | info.GetReturnValue().Set(info.This()); |
| 1030 | } |
| 1031 | |
| 1032 | |
| 1033 | static void Return239Callback( |
| 1034 | Local<String> name, const v8::PropertyCallbackInfo<Value>& info) { |
| 1035 | ApiTestFuzzer::Fuzz(); |
| 1036 | CheckReturnValue(info, FUNCTION_ADDR(Return239Callback)); |
| 1037 | info.GetReturnValue().Set(v8_str("bad value" )); |
| 1038 | info.GetReturnValue().Set(v8_num(239)); |
| 1039 | } |
| 1040 | |
| 1041 | |
| 1042 | template<typename Handler> |
| 1043 | static void TestFunctionTemplateInitializer(Handler handler, |
| 1044 | Handler handler_2) { |
| 1045 | // Test constructor calls. |
| 1046 | { |
| 1047 | LocalContext env; |
| 1048 | v8::Isolate* isolate = env->GetIsolate(); |
| 1049 | v8::HandleScope scope(isolate); |
| 1050 | |
| 1051 | Local<v8::FunctionTemplate> fun_templ = |
| 1052 | v8::FunctionTemplate::New(isolate, handler); |
| 1053 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 1054 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), fun).FromJust()); |
| 1055 | Local<Script> script = v8_compile("obj()" ); |
| 1056 | for (int i = 0; i < 30; i++) { |
| 1057 | CHECK_EQ(102, v8_run_int32value(script)); |
| 1058 | } |
| 1059 | } |
| 1060 | // Use SetCallHandler to initialize a function template, should work like |
| 1061 | // the previous one. |
| 1062 | { |
| 1063 | LocalContext env; |
| 1064 | v8::Isolate* isolate = env->GetIsolate(); |
| 1065 | v8::HandleScope scope(isolate); |
| 1066 | |
| 1067 | Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); |
| 1068 | fun_templ->SetCallHandler(handler_2); |
| 1069 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 1070 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), fun).FromJust()); |
| 1071 | Local<Script> script = v8_compile("obj()" ); |
| 1072 | for (int i = 0; i < 30; i++) { |
| 1073 | CHECK_EQ(102, v8_run_int32value(script)); |
| 1074 | } |
| 1075 | } |
| 1076 | } |
| 1077 | |
| 1078 | |
| 1079 | template<typename Constructor, typename Accessor> |
| 1080 | static void TestFunctionTemplateAccessor(Constructor constructor, |
| 1081 | Accessor accessor) { |
| 1082 | LocalContext env; |
| 1083 | v8::HandleScope scope(env->GetIsolate()); |
| 1084 | |
| 1085 | Local<v8::FunctionTemplate> fun_templ = |
| 1086 | v8::FunctionTemplate::New(env->GetIsolate(), constructor); |
| 1087 | fun_templ->SetClassName(v8_str("funky" )); |
| 1088 | fun_templ->InstanceTemplate()->SetAccessor(v8_str("m" ), accessor); |
| 1089 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 1090 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), fun).FromJust()); |
| 1091 | Local<Value> result = |
| 1092 | v8_compile("(new obj()).toString()" )->Run(env.local()).ToLocalChecked(); |
| 1093 | CHECK(v8_str("[object funky]" )->Equals(env.local(), result).FromJust()); |
| 1094 | CompileRun("var obj_instance = new obj();" ); |
| 1095 | Local<Script> script; |
| 1096 | script = v8_compile("obj_instance.x" ); |
| 1097 | for (int i = 0; i < 30; i++) { |
| 1098 | CHECK_EQ(1, v8_run_int32value(script)); |
| 1099 | } |
| 1100 | script = v8_compile("obj_instance.m" ); |
| 1101 | for (int i = 0; i < 30; i++) { |
| 1102 | CHECK_EQ(239, v8_run_int32value(script)); |
| 1103 | } |
| 1104 | } |
| 1105 | |
| 1106 | |
| 1107 | THREADED_PROFILED_TEST(FunctionTemplate) { |
| 1108 | TestFunctionTemplateInitializer(handle_callback, handle_callback_2); |
| 1109 | TestFunctionTemplateAccessor(construct_callback, Return239Callback); |
| 1110 | } |
| 1111 | |
| 1112 | static void FunctionCallbackForProxyTest( |
| 1113 | const v8::FunctionCallbackInfo<Value>& info) { |
| 1114 | info.GetReturnValue().Set(info.This()); |
| 1115 | } |
| 1116 | |
| 1117 | THREADED_TEST(FunctionTemplateWithProxy) { |
| 1118 | LocalContext env; |
| 1119 | v8::Isolate* isolate = env->GetIsolate(); |
| 1120 | v8::HandleScope scope(isolate); |
| 1121 | |
| 1122 | v8::Local<v8::FunctionTemplate> function_template = |
| 1123 | v8::FunctionTemplate::New(isolate, FunctionCallbackForProxyTest); |
| 1124 | v8::Local<v8::Function> function = |
| 1125 | function_template->GetFunction(env.local()).ToLocalChecked(); |
| 1126 | CHECK((*env)->Global()->Set(env.local(), v8_str("f" ), function).FromJust()); |
| 1127 | v8::Local<v8::Value> proxy = |
| 1128 | CompileRun("var proxy = new Proxy({}, {}); proxy" ); |
| 1129 | CHECK(proxy->IsProxy()); |
| 1130 | |
| 1131 | v8::Local<v8::Value> result = CompileRun("f(proxy)" ); |
| 1132 | CHECK(result->Equals(env.local(), (*env)->Global()).FromJust()); |
| 1133 | |
| 1134 | result = CompileRun("f.call(proxy)" ); |
| 1135 | CHECK(result->Equals(env.local(), proxy).FromJust()); |
| 1136 | |
| 1137 | result = CompileRun("Reflect.apply(f, proxy, [1])" ); |
| 1138 | CHECK(result->Equals(env.local(), proxy).FromJust()); |
| 1139 | } |
| 1140 | |
| 1141 | static void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1142 | ApiTestFuzzer::Fuzz(); |
| 1143 | CheckReturnValue(info, FUNCTION_ADDR(SimpleCallback)); |
| 1144 | info.GetReturnValue().Set(v8_num(51423 + info.Length())); |
| 1145 | } |
| 1146 | |
| 1147 | |
| 1148 | template<typename Callback> |
| 1149 | static void TestSimpleCallback(Callback callback) { |
| 1150 | LocalContext env; |
| 1151 | v8::Isolate* isolate = env->GetIsolate(); |
| 1152 | v8::HandleScope scope(isolate); |
| 1153 | |
| 1154 | v8::Local<v8::ObjectTemplate> object_template = |
| 1155 | v8::ObjectTemplate::New(isolate); |
| 1156 | object_template->Set(isolate, "callback" , |
| 1157 | v8::FunctionTemplate::New(isolate, callback)); |
| 1158 | v8::Local<v8::Object> object = |
| 1159 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 1160 | CHECK((*env) |
| 1161 | ->Global() |
| 1162 | ->Set(env.local(), v8_str("callback_object" ), object) |
| 1163 | .FromJust()); |
| 1164 | v8::Local<v8::Script> script; |
| 1165 | script = v8_compile("callback_object.callback(17)" ); |
| 1166 | for (int i = 0; i < 30; i++) { |
| 1167 | CHECK_EQ(51424, v8_run_int32value(script)); |
| 1168 | } |
| 1169 | script = v8_compile("callback_object.callback(17, 24)" ); |
| 1170 | for (int i = 0; i < 30; i++) { |
| 1171 | CHECK_EQ(51425, v8_run_int32value(script)); |
| 1172 | } |
| 1173 | } |
| 1174 | |
| 1175 | |
| 1176 | THREADED_PROFILED_TEST(SimpleCallback) { |
| 1177 | TestSimpleCallback(SimpleCallback); |
| 1178 | } |
| 1179 | |
| 1180 | |
| 1181 | template<typename T> |
| 1182 | void FastReturnValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info); |
| 1183 | |
| 1184 | // constant return values |
| 1185 | static int32_t fast_return_value_int32 = 471; |
| 1186 | static uint32_t fast_return_value_uint32 = 571; |
| 1187 | static const double kFastReturnValueDouble = 2.7; |
| 1188 | // variable return values |
| 1189 | static bool fast_return_value_bool = false; |
| 1190 | enum ReturnValueOddball { |
| 1191 | kNullReturnValue, |
| 1192 | kUndefinedReturnValue, |
| 1193 | kEmptyStringReturnValue |
| 1194 | }; |
| 1195 | static ReturnValueOddball fast_return_value_void; |
| 1196 | static bool fast_return_value_object_is_empty = false; |
| 1197 | |
| 1198 | // Helper function to avoid compiler error: insufficient contextual information |
| 1199 | // to determine type when applying FUNCTION_ADDR to a template function. |
| 1200 | static i::Address address_of(v8::FunctionCallback callback) { |
| 1201 | return FUNCTION_ADDR(callback); |
| 1202 | } |
| 1203 | |
| 1204 | template<> |
| 1205 | void FastReturnValueCallback<int32_t>( |
| 1206 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1207 | CheckReturnValue(info, address_of(FastReturnValueCallback<int32_t>)); |
| 1208 | info.GetReturnValue().Set(fast_return_value_int32); |
| 1209 | } |
| 1210 | |
| 1211 | template<> |
| 1212 | void FastReturnValueCallback<uint32_t>( |
| 1213 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1214 | CheckReturnValue(info, address_of(FastReturnValueCallback<uint32_t>)); |
| 1215 | info.GetReturnValue().Set(fast_return_value_uint32); |
| 1216 | } |
| 1217 | |
| 1218 | template<> |
| 1219 | void FastReturnValueCallback<double>( |
| 1220 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1221 | CheckReturnValue(info, address_of(FastReturnValueCallback<double>)); |
| 1222 | info.GetReturnValue().Set(kFastReturnValueDouble); |
| 1223 | } |
| 1224 | |
| 1225 | template<> |
| 1226 | void FastReturnValueCallback<bool>( |
| 1227 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1228 | CheckReturnValue(info, address_of(FastReturnValueCallback<bool>)); |
| 1229 | info.GetReturnValue().Set(fast_return_value_bool); |
| 1230 | } |
| 1231 | |
| 1232 | template<> |
| 1233 | void FastReturnValueCallback<void>( |
| 1234 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1235 | CheckReturnValue(info, address_of(FastReturnValueCallback<void>)); |
| 1236 | switch (fast_return_value_void) { |
| 1237 | case kNullReturnValue: |
| 1238 | info.GetReturnValue().SetNull(); |
| 1239 | break; |
| 1240 | case kUndefinedReturnValue: |
| 1241 | info.GetReturnValue().SetUndefined(); |
| 1242 | break; |
| 1243 | case kEmptyStringReturnValue: |
| 1244 | info.GetReturnValue().SetEmptyString(); |
| 1245 | break; |
| 1246 | } |
| 1247 | } |
| 1248 | |
| 1249 | template<> |
| 1250 | void FastReturnValueCallback<Object>( |
| 1251 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 1252 | v8::Local<v8::Object> object; |
| 1253 | if (!fast_return_value_object_is_empty) { |
| 1254 | object = Object::New(info.GetIsolate()); |
| 1255 | } |
| 1256 | info.GetReturnValue().Set(object); |
| 1257 | } |
| 1258 | |
| 1259 | template <typename T> |
| 1260 | Local<Value> TestFastReturnValues() { |
| 1261 | LocalContext env; |
| 1262 | v8::Isolate* isolate = env->GetIsolate(); |
| 1263 | v8::EscapableHandleScope scope(isolate); |
| 1264 | v8::Local<v8::ObjectTemplate> object_template = |
| 1265 | v8::ObjectTemplate::New(isolate); |
| 1266 | v8::FunctionCallback callback = &FastReturnValueCallback<T>; |
| 1267 | object_template->Set(isolate, "callback" , |
| 1268 | v8::FunctionTemplate::New(isolate, callback)); |
| 1269 | v8::Local<v8::Object> object = |
| 1270 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 1271 | CHECK((*env) |
| 1272 | ->Global() |
| 1273 | ->Set(env.local(), v8_str("callback_object" ), object) |
| 1274 | .FromJust()); |
| 1275 | return scope.Escape(CompileRun("callback_object.callback()" )); |
| 1276 | } |
| 1277 | |
| 1278 | |
| 1279 | THREADED_PROFILED_TEST(FastReturnValues) { |
| 1280 | LocalContext env; |
| 1281 | v8::Isolate* isolate = env->GetIsolate(); |
| 1282 | v8::HandleScope scope(isolate); |
| 1283 | v8::Local<v8::Value> value; |
| 1284 | // check int32_t and uint32_t |
| 1285 | int32_t int_values[] = { |
| 1286 | 0, 234, -723, |
| 1287 | i::Smi::kMinValue, i::Smi::kMaxValue |
| 1288 | }; |
| 1289 | for (size_t i = 0; i < arraysize(int_values); i++) { |
| 1290 | for (int modifier = -1; modifier <= 1; modifier++) { |
| 1291 | int int_value = v8::base::AddWithWraparound(int_values[i], modifier); |
| 1292 | // check int32_t |
| 1293 | fast_return_value_int32 = int_value; |
| 1294 | value = TestFastReturnValues<int32_t>(); |
| 1295 | CHECK(value->IsInt32()); |
| 1296 | CHECK_EQ(fast_return_value_int32, |
| 1297 | value->Int32Value(env.local()).FromJust()); |
| 1298 | // check uint32_t |
| 1299 | fast_return_value_uint32 = static_cast<uint32_t>(int_value); |
| 1300 | value = TestFastReturnValues<uint32_t>(); |
| 1301 | CHECK(value->IsUint32()); |
| 1302 | CHECK_EQ(fast_return_value_uint32, |
| 1303 | value->Uint32Value(env.local()).FromJust()); |
| 1304 | } |
| 1305 | } |
| 1306 | // check double |
| 1307 | value = TestFastReturnValues<double>(); |
| 1308 | CHECK(value->IsNumber()); |
| 1309 | CHECK_EQ(kFastReturnValueDouble, |
| 1310 | value->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 1311 | // check bool values |
| 1312 | for (int i = 0; i < 2; i++) { |
| 1313 | fast_return_value_bool = i == 0; |
| 1314 | value = TestFastReturnValues<bool>(); |
| 1315 | CHECK(value->IsBoolean()); |
| 1316 | CHECK_EQ(fast_return_value_bool, value->BooleanValue(isolate)); |
| 1317 | } |
| 1318 | // check oddballs |
| 1319 | ReturnValueOddball oddballs[] = { |
| 1320 | kNullReturnValue, |
| 1321 | kUndefinedReturnValue, |
| 1322 | kEmptyStringReturnValue |
| 1323 | }; |
| 1324 | for (size_t i = 0; i < arraysize(oddballs); i++) { |
| 1325 | fast_return_value_void = oddballs[i]; |
| 1326 | value = TestFastReturnValues<void>(); |
| 1327 | switch (fast_return_value_void) { |
| 1328 | case kNullReturnValue: |
| 1329 | CHECK(value->IsNull()); |
| 1330 | break; |
| 1331 | case kUndefinedReturnValue: |
| 1332 | CHECK(value->IsUndefined()); |
| 1333 | break; |
| 1334 | case kEmptyStringReturnValue: |
| 1335 | CHECK(value->IsString()); |
| 1336 | CHECK_EQ(0, v8::String::Cast(*value)->Length()); |
| 1337 | break; |
| 1338 | } |
| 1339 | } |
| 1340 | // check handles |
| 1341 | fast_return_value_object_is_empty = false; |
| 1342 | value = TestFastReturnValues<Object>(); |
| 1343 | CHECK(value->IsObject()); |
| 1344 | fast_return_value_object_is_empty = true; |
| 1345 | value = TestFastReturnValues<Object>(); |
| 1346 | CHECK(value->IsUndefined()); |
| 1347 | } |
| 1348 | |
| 1349 | |
| 1350 | THREADED_TEST(FunctionTemplateSetLength) { |
| 1351 | LocalContext env; |
| 1352 | v8::Isolate* isolate = env->GetIsolate(); |
| 1353 | v8::HandleScope scope(isolate); |
| 1354 | { |
| 1355 | Local<v8::FunctionTemplate> fun_templ = |
| 1356 | v8::FunctionTemplate::New(isolate, handle_callback, Local<v8::Value>(), |
| 1357 | Local<v8::Signature>(), 23); |
| 1358 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 1359 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), fun).FromJust()); |
| 1360 | Local<Script> script = v8_compile("obj.length" ); |
| 1361 | CHECK_EQ(23, v8_run_int32value(script)); |
| 1362 | } |
| 1363 | { |
| 1364 | Local<v8::FunctionTemplate> fun_templ = |
| 1365 | v8::FunctionTemplate::New(isolate, handle_callback); |
| 1366 | fun_templ->SetLength(22); |
| 1367 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 1368 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), fun).FromJust()); |
| 1369 | Local<Script> script = v8_compile("obj.length" ); |
| 1370 | CHECK_EQ(22, v8_run_int32value(script)); |
| 1371 | } |
| 1372 | { |
| 1373 | // Without setting length it defaults to 0. |
| 1374 | Local<v8::FunctionTemplate> fun_templ = |
| 1375 | v8::FunctionTemplate::New(isolate, handle_callback); |
| 1376 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 1377 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), fun).FromJust()); |
| 1378 | Local<Script> script = v8_compile("obj.length" ); |
| 1379 | CHECK_EQ(0, v8_run_int32value(script)); |
| 1380 | } |
| 1381 | } |
| 1382 | |
| 1383 | |
| 1384 | static void* expected_ptr; |
| 1385 | static void callback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 1386 | void* ptr = v8::External::Cast(*args.Data())->Value(); |
| 1387 | CHECK_EQ(expected_ptr, ptr); |
| 1388 | args.GetReturnValue().Set(true); |
| 1389 | } |
| 1390 | |
| 1391 | |
| 1392 | static void TestExternalPointerWrapping() { |
| 1393 | LocalContext env; |
| 1394 | v8::Isolate* isolate = env->GetIsolate(); |
| 1395 | v8::HandleScope scope(isolate); |
| 1396 | |
| 1397 | v8::Local<v8::Value> data = v8::External::New(isolate, expected_ptr); |
| 1398 | |
| 1399 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 1400 | CHECK(obj->Set(env.local(), v8_str("func" ), |
| 1401 | v8::FunctionTemplate::New(isolate, callback, data) |
| 1402 | ->GetFunction(env.local()) |
| 1403 | .ToLocalChecked()) |
| 1404 | .FromJust()); |
| 1405 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), obj).FromJust()); |
| 1406 | |
| 1407 | CHECK(CompileRun("function foo() {\n" |
| 1408 | " for (var i = 0; i < 13; i++) obj.func();\n" |
| 1409 | "}\n" |
| 1410 | "foo(), true" ) |
| 1411 | ->BooleanValue(isolate)); |
| 1412 | } |
| 1413 | |
| 1414 | |
| 1415 | THREADED_TEST(ExternalWrap) { |
| 1416 | // Check heap allocated object. |
| 1417 | int* ptr = new int; |
| 1418 | expected_ptr = ptr; |
| 1419 | TestExternalPointerWrapping(); |
| 1420 | delete ptr; |
| 1421 | |
| 1422 | // Check stack allocated object. |
| 1423 | int foo; |
| 1424 | expected_ptr = &foo; |
| 1425 | TestExternalPointerWrapping(); |
| 1426 | |
| 1427 | // Check not aligned addresses. |
| 1428 | const int n = 100; |
| 1429 | char* s = new char[n]; |
| 1430 | for (int i = 0; i < n; i++) { |
| 1431 | expected_ptr = s + i; |
| 1432 | TestExternalPointerWrapping(); |
| 1433 | } |
| 1434 | |
| 1435 | delete[] s; |
| 1436 | |
| 1437 | // Check several invalid addresses. |
| 1438 | expected_ptr = reinterpret_cast<void*>(1); |
| 1439 | TestExternalPointerWrapping(); |
| 1440 | |
| 1441 | expected_ptr = reinterpret_cast<void*>(0xDEADBEEF); |
| 1442 | TestExternalPointerWrapping(); |
| 1443 | |
| 1444 | expected_ptr = reinterpret_cast<void*>(0xDEADBEEF + 1); |
| 1445 | TestExternalPointerWrapping(); |
| 1446 | |
| 1447 | #if defined(V8_HOST_ARCH_X64) |
| 1448 | // Check a value with a leading 1 bit in x64 Smi encoding. |
| 1449 | expected_ptr = reinterpret_cast<void*>(0x400000000); |
| 1450 | TestExternalPointerWrapping(); |
| 1451 | |
| 1452 | expected_ptr = reinterpret_cast<void*>(0xDEADBEEFDEADBEEF); |
| 1453 | TestExternalPointerWrapping(); |
| 1454 | |
| 1455 | expected_ptr = reinterpret_cast<void*>(0xDEADBEEFDEADBEEF + 1); |
| 1456 | TestExternalPointerWrapping(); |
| 1457 | #endif |
| 1458 | } |
| 1459 | |
| 1460 | |
| 1461 | THREADED_TEST(FindInstanceInPrototypeChain) { |
| 1462 | LocalContext env; |
| 1463 | v8::Isolate* isolate = env->GetIsolate(); |
| 1464 | v8::HandleScope scope(isolate); |
| 1465 | |
| 1466 | Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(isolate); |
| 1467 | Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(isolate); |
| 1468 | Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(isolate); |
| 1469 | derived->Inherit(base); |
| 1470 | |
| 1471 | Local<v8::Function> base_function = |
| 1472 | base->GetFunction(env.local()).ToLocalChecked(); |
| 1473 | Local<v8::Function> derived_function = |
| 1474 | derived->GetFunction(env.local()).ToLocalChecked(); |
| 1475 | Local<v8::Function> other_function = |
| 1476 | other->GetFunction(env.local()).ToLocalChecked(); |
| 1477 | |
| 1478 | Local<v8::Object> base_instance = |
| 1479 | base_function->NewInstance(env.local()).ToLocalChecked(); |
| 1480 | Local<v8::Object> derived_instance = |
| 1481 | derived_function->NewInstance(env.local()).ToLocalChecked(); |
| 1482 | Local<v8::Object> derived_instance2 = |
| 1483 | derived_function->NewInstance(env.local()).ToLocalChecked(); |
| 1484 | Local<v8::Object> other_instance = |
| 1485 | other_function->NewInstance(env.local()).ToLocalChecked(); |
| 1486 | CHECK( |
| 1487 | derived_instance2->Set(env.local(), v8_str("__proto__" ), derived_instance) |
| 1488 | .FromJust()); |
| 1489 | CHECK(other_instance->Set(env.local(), v8_str("__proto__" ), derived_instance2) |
| 1490 | .FromJust()); |
| 1491 | |
| 1492 | // base_instance is only an instance of base. |
| 1493 | CHECK(base_instance->Equals(env.local(), |
| 1494 | base_instance->FindInstanceInPrototypeChain(base)) |
| 1495 | .FromJust()); |
| 1496 | CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); |
| 1497 | CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); |
| 1498 | |
| 1499 | // derived_instance is an instance of base and derived. |
| 1500 | CHECK(derived_instance->Equals(env.local(), |
| 1501 | derived_instance->FindInstanceInPrototypeChain( |
| 1502 | base)) |
| 1503 | .FromJust()); |
| 1504 | CHECK(derived_instance->Equals(env.local(), |
| 1505 | derived_instance->FindInstanceInPrototypeChain( |
| 1506 | derived)) |
| 1507 | .FromJust()); |
| 1508 | CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); |
| 1509 | |
| 1510 | // other_instance is an instance of other and its immediate |
| 1511 | // prototype derived_instance2 is an instance of base and derived. |
| 1512 | // Note, derived_instance is an instance of base and derived too, |
| 1513 | // but it comes after derived_instance2 in the prototype chain of |
| 1514 | // other_instance. |
| 1515 | CHECK(derived_instance2->Equals( |
| 1516 | env.local(), |
| 1517 | other_instance->FindInstanceInPrototypeChain(base)) |
| 1518 | .FromJust()); |
| 1519 | CHECK(derived_instance2->Equals(env.local(), |
| 1520 | other_instance->FindInstanceInPrototypeChain( |
| 1521 | derived)) |
| 1522 | .FromJust()); |
| 1523 | CHECK(other_instance->Equals( |
| 1524 | env.local(), |
| 1525 | other_instance->FindInstanceInPrototypeChain(other)) |
| 1526 | .FromJust()); |
| 1527 | } |
| 1528 | |
| 1529 | |
| 1530 | THREADED_TEST(TinyInteger) { |
| 1531 | LocalContext env; |
| 1532 | v8::Isolate* isolate = env->GetIsolate(); |
| 1533 | v8::HandleScope scope(isolate); |
| 1534 | |
| 1535 | int32_t value = 239; |
| 1536 | Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); |
| 1537 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1538 | } |
| 1539 | |
| 1540 | |
| 1541 | THREADED_TEST(BigSmiInteger) { |
| 1542 | LocalContext env; |
| 1543 | v8::HandleScope scope(env->GetIsolate()); |
| 1544 | v8::Isolate* isolate = CcTest::isolate(); |
| 1545 | |
| 1546 | int32_t value = i::Smi::kMaxValue; |
| 1547 | // We cannot add one to a Smi::kMaxValue without wrapping. |
| 1548 | if (i::SmiValuesAre31Bits()) { |
| 1549 | CHECK(i::Smi::IsValid(value)); |
| 1550 | CHECK(!i::Smi::IsValid(value + 1)); |
| 1551 | |
| 1552 | Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); |
| 1553 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1554 | } |
| 1555 | } |
| 1556 | |
| 1557 | |
| 1558 | THREADED_TEST(BigInteger) { |
| 1559 | LocalContext env; |
| 1560 | v8::HandleScope scope(env->GetIsolate()); |
| 1561 | v8::Isolate* isolate = CcTest::isolate(); |
| 1562 | |
| 1563 | // We cannot add one to a Smi::kMaxValue without wrapping. |
| 1564 | if (i::SmiValuesAre31Bits()) { |
| 1565 | // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. |
| 1566 | // The code will not be run in that case, due to the "if" guard. |
| 1567 | int32_t value = |
| 1568 | static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); |
| 1569 | CHECK_GT(value, i::Smi::kMaxValue); |
| 1570 | CHECK(!i::Smi::IsValid(value)); |
| 1571 | |
| 1572 | Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); |
| 1573 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1574 | } |
| 1575 | } |
| 1576 | |
| 1577 | |
| 1578 | THREADED_TEST(TinyUnsignedInteger) { |
| 1579 | LocalContext env; |
| 1580 | v8::HandleScope scope(env->GetIsolate()); |
| 1581 | v8::Isolate* isolate = CcTest::isolate(); |
| 1582 | |
| 1583 | uint32_t value = 239; |
| 1584 | |
| 1585 | Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| 1586 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1587 | } |
| 1588 | |
| 1589 | |
| 1590 | THREADED_TEST(BigUnsignedSmiInteger) { |
| 1591 | LocalContext env; |
| 1592 | v8::HandleScope scope(env->GetIsolate()); |
| 1593 | v8::Isolate* isolate = CcTest::isolate(); |
| 1594 | |
| 1595 | uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); |
| 1596 | CHECK(i::Smi::IsValid(value)); |
| 1597 | CHECK(!i::Smi::IsValid(value + 1)); |
| 1598 | |
| 1599 | Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| 1600 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1601 | } |
| 1602 | |
| 1603 | |
| 1604 | THREADED_TEST(BigUnsignedInteger) { |
| 1605 | LocalContext env; |
| 1606 | v8::HandleScope scope(env->GetIsolate()); |
| 1607 | v8::Isolate* isolate = CcTest::isolate(); |
| 1608 | |
| 1609 | uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; |
| 1610 | CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); |
| 1611 | CHECK(!i::Smi::IsValid(value)); |
| 1612 | |
| 1613 | Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| 1614 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1615 | } |
| 1616 | |
| 1617 | |
| 1618 | THREADED_TEST(OutOfSignedRangeUnsignedInteger) { |
| 1619 | LocalContext env; |
| 1620 | v8::HandleScope scope(env->GetIsolate()); |
| 1621 | v8::Isolate* isolate = CcTest::isolate(); |
| 1622 | |
| 1623 | uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; |
| 1624 | uint32_t value = INT32_MAX_AS_UINT + 1; |
| 1625 | CHECK(value > INT32_MAX_AS_UINT); // No overflow. |
| 1626 | |
| 1627 | Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); |
| 1628 | CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); |
| 1629 | } |
| 1630 | |
| 1631 | |
| 1632 | THREADED_TEST(IsNativeError) { |
| 1633 | LocalContext env; |
| 1634 | v8::HandleScope scope(env->GetIsolate()); |
| 1635 | v8::Local<Value> syntax_error = CompileRun( |
| 1636 | "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; " ); |
| 1637 | CHECK(syntax_error->IsNativeError()); |
| 1638 | v8::Local<Value> not_error = CompileRun("{a:42}" ); |
| 1639 | CHECK(!not_error->IsNativeError()); |
| 1640 | v8::Local<Value> not_object = CompileRun("42" ); |
| 1641 | CHECK(!not_object->IsNativeError()); |
| 1642 | } |
| 1643 | |
| 1644 | |
| 1645 | THREADED_TEST(IsGeneratorFunctionOrObject) { |
| 1646 | LocalContext env; |
| 1647 | v8::HandleScope scope(env->GetIsolate()); |
| 1648 | |
| 1649 | CompileRun("function *gen() { yield 1; }\nfunction func() {}" ); |
| 1650 | v8::Local<Value> gen = CompileRun("gen" ); |
| 1651 | v8::Local<Value> genObj = CompileRun("gen()" ); |
| 1652 | v8::Local<Value> object = CompileRun("{a:42}" ); |
| 1653 | v8::Local<Value> func = CompileRun("func" ); |
| 1654 | |
| 1655 | CHECK(gen->IsGeneratorFunction()); |
| 1656 | CHECK(gen->IsFunction()); |
| 1657 | CHECK(!gen->IsGeneratorObject()); |
| 1658 | |
| 1659 | CHECK(!genObj->IsGeneratorFunction()); |
| 1660 | CHECK(!genObj->IsFunction()); |
| 1661 | CHECK(genObj->IsGeneratorObject()); |
| 1662 | |
| 1663 | CHECK(!object->IsGeneratorFunction()); |
| 1664 | CHECK(!object->IsFunction()); |
| 1665 | CHECK(!object->IsGeneratorObject()); |
| 1666 | |
| 1667 | CHECK(!func->IsGeneratorFunction()); |
| 1668 | CHECK(func->IsFunction()); |
| 1669 | CHECK(!func->IsGeneratorObject()); |
| 1670 | } |
| 1671 | |
| 1672 | THREADED_TEST(IsAsyncFunction) { |
| 1673 | LocalContext env; |
| 1674 | v8::Isolate* isolate = env->GetIsolate(); |
| 1675 | v8::HandleScope scope(isolate); |
| 1676 | |
| 1677 | CompileRun("async function foo() {}" ); |
| 1678 | v8::Local<Value> foo = CompileRun("foo" ); |
| 1679 | |
| 1680 | CHECK(foo->IsAsyncFunction()); |
| 1681 | CHECK(foo->IsFunction()); |
| 1682 | CHECK(!foo->IsGeneratorFunction()); |
| 1683 | CHECK(!foo->IsGeneratorObject()); |
| 1684 | |
| 1685 | CompileRun("function bar() {}" ); |
| 1686 | v8::Local<Value> bar = CompileRun("bar" ); |
| 1687 | |
| 1688 | CHECK(!bar->IsAsyncFunction()); |
| 1689 | CHECK(bar->IsFunction()); |
| 1690 | } |
| 1691 | |
| 1692 | THREADED_TEST(ArgumentsObject) { |
| 1693 | LocalContext env; |
| 1694 | v8::HandleScope scope(env->GetIsolate()); |
| 1695 | v8::Local<Value> arguments_object = |
| 1696 | CompileRun("var out = 0; (function(){ out = arguments; })(1,2,3); out;" ); |
| 1697 | CHECK(arguments_object->IsArgumentsObject()); |
| 1698 | v8::Local<Value> array = CompileRun("[1,2,3]" ); |
| 1699 | CHECK(!array->IsArgumentsObject()); |
| 1700 | v8::Local<Value> object = CompileRun("{a:42}" ); |
| 1701 | CHECK(!object->IsArgumentsObject()); |
| 1702 | } |
| 1703 | |
| 1704 | |
| 1705 | THREADED_TEST(IsMapOrSet) { |
| 1706 | LocalContext env; |
| 1707 | v8::HandleScope scope(env->GetIsolate()); |
| 1708 | v8::Local<Value> map = CompileRun("new Map()" ); |
| 1709 | v8::Local<Value> set = CompileRun("new Set()" ); |
| 1710 | v8::Local<Value> weak_map = CompileRun("new WeakMap()" ); |
| 1711 | v8::Local<Value> weak_set = CompileRun("new WeakSet()" ); |
| 1712 | CHECK(map->IsMap()); |
| 1713 | CHECK(set->IsSet()); |
| 1714 | CHECK(weak_map->IsWeakMap()); |
| 1715 | CHECK(weak_set->IsWeakSet()); |
| 1716 | |
| 1717 | CHECK(!map->IsSet()); |
| 1718 | CHECK(!map->IsWeakMap()); |
| 1719 | CHECK(!map->IsWeakSet()); |
| 1720 | |
| 1721 | CHECK(!set->IsMap()); |
| 1722 | CHECK(!set->IsWeakMap()); |
| 1723 | CHECK(!set->IsWeakSet()); |
| 1724 | |
| 1725 | CHECK(!weak_map->IsMap()); |
| 1726 | CHECK(!weak_map->IsSet()); |
| 1727 | CHECK(!weak_map->IsWeakSet()); |
| 1728 | |
| 1729 | CHECK(!weak_set->IsMap()); |
| 1730 | CHECK(!weak_set->IsSet()); |
| 1731 | CHECK(!weak_set->IsWeakMap()); |
| 1732 | |
| 1733 | v8::Local<Value> object = CompileRun("{a:42}" ); |
| 1734 | CHECK(!object->IsMap()); |
| 1735 | CHECK(!object->IsSet()); |
| 1736 | CHECK(!object->IsWeakMap()); |
| 1737 | CHECK(!object->IsWeakSet()); |
| 1738 | } |
| 1739 | |
| 1740 | |
| 1741 | THREADED_TEST(StringObject) { |
| 1742 | LocalContext env; |
| 1743 | v8::HandleScope scope(env->GetIsolate()); |
| 1744 | v8::Local<Value> boxed_string = CompileRun("new String(\"test\")" ); |
| 1745 | CHECK(boxed_string->IsStringObject()); |
| 1746 | v8::Local<Value> unboxed_string = CompileRun("\"test\"" ); |
| 1747 | CHECK(!unboxed_string->IsStringObject()); |
| 1748 | v8::Local<Value> boxed_not_string = CompileRun("new Number(42)" ); |
| 1749 | CHECK(!boxed_not_string->IsStringObject()); |
| 1750 | v8::Local<Value> not_object = CompileRun("0" ); |
| 1751 | CHECK(!not_object->IsStringObject()); |
| 1752 | v8::Local<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>(); |
| 1753 | CHECK(!as_boxed.IsEmpty()); |
| 1754 | Local<v8::String> the_string = as_boxed->ValueOf(); |
| 1755 | CHECK(!the_string.IsEmpty()); |
| 1756 | ExpectObject("\"test\"" , the_string); |
| 1757 | v8::Local<v8::Value> new_boxed_string = |
| 1758 | v8::StringObject::New(CcTest::isolate(), the_string); |
| 1759 | CHECK(new_boxed_string->IsStringObject()); |
| 1760 | as_boxed = new_boxed_string.As<v8::StringObject>(); |
| 1761 | the_string = as_boxed->ValueOf(); |
| 1762 | CHECK(!the_string.IsEmpty()); |
| 1763 | ExpectObject("\"test\"" , the_string); |
| 1764 | } |
| 1765 | |
| 1766 | |
| 1767 | TEST(StringObjectDelete) { |
| 1768 | LocalContext context; |
| 1769 | v8::HandleScope scope(context->GetIsolate()); |
| 1770 | v8::Local<Value> boxed_string = CompileRun("new String(\"test\")" ); |
| 1771 | CHECK(boxed_string->IsStringObject()); |
| 1772 | v8::Local<v8::Object> str_obj = boxed_string.As<v8::Object>(); |
| 1773 | CHECK(!str_obj->Delete(context.local(), 2).FromJust()); |
| 1774 | CHECK(!str_obj->Delete(context.local(), v8_num(2)).FromJust()); |
| 1775 | } |
| 1776 | |
| 1777 | |
| 1778 | THREADED_TEST(NumberObject) { |
| 1779 | LocalContext env; |
| 1780 | v8::HandleScope scope(env->GetIsolate()); |
| 1781 | v8::Local<Value> boxed_number = CompileRun("new Number(42)" ); |
| 1782 | CHECK(boxed_number->IsNumberObject()); |
| 1783 | v8::Local<Value> unboxed_number = CompileRun("42" ); |
| 1784 | CHECK(!unboxed_number->IsNumberObject()); |
| 1785 | v8::Local<Value> boxed_not_number = CompileRun("new Boolean(false)" ); |
| 1786 | CHECK(!boxed_not_number->IsNumberObject()); |
| 1787 | v8::Local<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>(); |
| 1788 | CHECK(!as_boxed.IsEmpty()); |
| 1789 | double the_number = as_boxed->ValueOf(); |
| 1790 | CHECK_EQ(42.0, the_number); |
| 1791 | v8::Local<v8::Value> new_boxed_number = |
| 1792 | v8::NumberObject::New(env->GetIsolate(), 43); |
| 1793 | CHECK(new_boxed_number->IsNumberObject()); |
| 1794 | as_boxed = new_boxed_number.As<v8::NumberObject>(); |
| 1795 | the_number = as_boxed->ValueOf(); |
| 1796 | CHECK_EQ(43.0, the_number); |
| 1797 | } |
| 1798 | |
| 1799 | THREADED_TEST(BigIntObject) { |
| 1800 | LocalContext env; |
| 1801 | v8::Isolate* isolate = env->GetIsolate(); |
| 1802 | v8::HandleScope scope(isolate); |
| 1803 | v8::Local<v8::Context> context(env.local()); |
| 1804 | v8::Local<Value> boxed_bigint = CompileRun("new Object(42n)" ); |
| 1805 | CHECK(!boxed_bigint->IsBigInt()); |
| 1806 | CHECK(boxed_bigint->IsBigIntObject()); |
| 1807 | v8::Local<Value> unboxed_bigint = CompileRun("42n" ); |
| 1808 | CHECK(unboxed_bigint->IsBigInt()); |
| 1809 | CHECK(!unboxed_bigint->IsBigIntObject()); |
| 1810 | v8::Local<v8::BigIntObject> as_boxed = boxed_bigint.As<v8::BigIntObject>(); |
| 1811 | CHECK(!as_boxed.IsEmpty()); |
| 1812 | v8::Local<v8::BigInt> unpacked = as_boxed->ValueOf(); |
| 1813 | CHECK(!unpacked.IsEmpty()); |
| 1814 | v8::Local<v8::Value> new_boxed_bigint = v8::BigIntObject::New(isolate, 43); |
| 1815 | CHECK(new_boxed_bigint->IsBigIntObject()); |
| 1816 | v8::Local<v8::Value> new_unboxed_bigint = v8::BigInt::New(isolate, 44); |
| 1817 | CHECK(new_unboxed_bigint->IsBigInt()); |
| 1818 | |
| 1819 | // Test functionality inherited from v8::Value. |
| 1820 | CHECK(unboxed_bigint->BooleanValue(isolate)); |
| 1821 | v8::Local<v8::String> string = |
| 1822 | unboxed_bigint->ToString(context).ToLocalChecked(); |
| 1823 | CHECK_EQ(0, strcmp("42" , *v8::String::Utf8Value(isolate, string))); |
| 1824 | |
| 1825 | // IntegerValue throws. |
| 1826 | CHECK(unboxed_bigint->IntegerValue(context).IsNothing()); |
| 1827 | } |
| 1828 | |
| 1829 | THREADED_TEST(BooleanObject) { |
| 1830 | LocalContext env; |
| 1831 | v8::HandleScope scope(env->GetIsolate()); |
| 1832 | v8::Local<Value> boxed_boolean = CompileRun("new Boolean(true)" ); |
| 1833 | CHECK(boxed_boolean->IsBooleanObject()); |
| 1834 | v8::Local<Value> unboxed_boolean = CompileRun("true" ); |
| 1835 | CHECK(!unboxed_boolean->IsBooleanObject()); |
| 1836 | v8::Local<Value> boxed_not_boolean = CompileRun("new Number(42)" ); |
| 1837 | CHECK(!boxed_not_boolean->IsBooleanObject()); |
| 1838 | v8::Local<v8::BooleanObject> as_boxed = boxed_boolean.As<v8::BooleanObject>(); |
| 1839 | CHECK(!as_boxed.IsEmpty()); |
| 1840 | bool the_boolean = as_boxed->ValueOf(); |
| 1841 | CHECK(the_boolean); |
| 1842 | v8::Local<v8::Value> boxed_true = |
| 1843 | v8::BooleanObject::New(env->GetIsolate(), true); |
| 1844 | v8::Local<v8::Value> boxed_false = |
| 1845 | v8::BooleanObject::New(env->GetIsolate(), false); |
| 1846 | CHECK(boxed_true->IsBooleanObject()); |
| 1847 | CHECK(boxed_false->IsBooleanObject()); |
| 1848 | as_boxed = boxed_true.As<v8::BooleanObject>(); |
| 1849 | CHECK(as_boxed->ValueOf()); |
| 1850 | as_boxed = boxed_false.As<v8::BooleanObject>(); |
| 1851 | CHECK(!as_boxed->ValueOf()); |
| 1852 | } |
| 1853 | |
| 1854 | |
| 1855 | THREADED_TEST(PrimitiveAndWrappedBooleans) { |
| 1856 | LocalContext env; |
| 1857 | v8::Isolate* isolate = env->GetIsolate(); |
| 1858 | v8::HandleScope scope(isolate); |
| 1859 | |
| 1860 | Local<Value> primitive_false = Boolean::New(isolate, false); |
| 1861 | CHECK(primitive_false->IsBoolean()); |
| 1862 | CHECK(!primitive_false->IsBooleanObject()); |
| 1863 | CHECK(!primitive_false->BooleanValue(isolate)); |
| 1864 | CHECK(!primitive_false->IsTrue()); |
| 1865 | CHECK(primitive_false->IsFalse()); |
| 1866 | |
| 1867 | Local<Value> false_value = BooleanObject::New(isolate, false); |
| 1868 | CHECK(!false_value->IsBoolean()); |
| 1869 | CHECK(false_value->IsBooleanObject()); |
| 1870 | CHECK(false_value->BooleanValue(isolate)); |
| 1871 | CHECK(!false_value->IsTrue()); |
| 1872 | CHECK(!false_value->IsFalse()); |
| 1873 | |
| 1874 | Local<BooleanObject> false_boolean_object = false_value.As<BooleanObject>(); |
| 1875 | CHECK(!false_boolean_object->IsBoolean()); |
| 1876 | CHECK(false_boolean_object->IsBooleanObject()); |
| 1877 | CHECK(false_boolean_object->BooleanValue(isolate)); |
| 1878 | CHECK(!false_boolean_object->ValueOf()); |
| 1879 | CHECK(!false_boolean_object->IsTrue()); |
| 1880 | CHECK(!false_boolean_object->IsFalse()); |
| 1881 | |
| 1882 | Local<Value> primitive_true = Boolean::New(isolate, true); |
| 1883 | CHECK(primitive_true->IsBoolean()); |
| 1884 | CHECK(!primitive_true->IsBooleanObject()); |
| 1885 | CHECK(primitive_true->BooleanValue(isolate)); |
| 1886 | CHECK(primitive_true->IsTrue()); |
| 1887 | CHECK(!primitive_true->IsFalse()); |
| 1888 | |
| 1889 | Local<Value> true_value = BooleanObject::New(isolate, true); |
| 1890 | CHECK(!true_value->IsBoolean()); |
| 1891 | CHECK(true_value->IsBooleanObject()); |
| 1892 | CHECK(true_value->BooleanValue(isolate)); |
| 1893 | CHECK(!true_value->IsTrue()); |
| 1894 | CHECK(!true_value->IsFalse()); |
| 1895 | |
| 1896 | Local<BooleanObject> true_boolean_object = true_value.As<BooleanObject>(); |
| 1897 | CHECK(!true_boolean_object->IsBoolean()); |
| 1898 | CHECK(true_boolean_object->IsBooleanObject()); |
| 1899 | CHECK(true_boolean_object->BooleanValue(isolate)); |
| 1900 | CHECK(true_boolean_object->ValueOf()); |
| 1901 | CHECK(!true_boolean_object->IsTrue()); |
| 1902 | CHECK(!true_boolean_object->IsFalse()); |
| 1903 | } |
| 1904 | |
| 1905 | |
| 1906 | THREADED_TEST(Number) { |
| 1907 | LocalContext env; |
| 1908 | v8::HandleScope scope(env->GetIsolate()); |
| 1909 | double PI = 3.1415926; |
| 1910 | Local<v8::Number> pi_obj = v8::Number::New(env->GetIsolate(), PI); |
| 1911 | CHECK_EQ(PI, pi_obj->NumberValue(env.local()).FromJust()); |
| 1912 | } |
| 1913 | |
| 1914 | |
| 1915 | THREADED_TEST(ToNumber) { |
| 1916 | LocalContext env; |
| 1917 | v8::Isolate* isolate = CcTest::isolate(); |
| 1918 | v8::HandleScope scope(isolate); |
| 1919 | Local<String> str = v8_str("3.1415926" ); |
| 1920 | CHECK_EQ(3.1415926, str->NumberValue(env.local()).FromJust()); |
| 1921 | v8::Local<v8::Boolean> t = v8::True(isolate); |
| 1922 | CHECK_EQ(1.0, t->NumberValue(env.local()).FromJust()); |
| 1923 | v8::Local<v8::Boolean> f = v8::False(isolate); |
| 1924 | CHECK_EQ(0.0, f->NumberValue(env.local()).FromJust()); |
| 1925 | } |
| 1926 | |
| 1927 | |
| 1928 | THREADED_TEST(Date) { |
| 1929 | LocalContext env; |
| 1930 | v8::HandleScope scope(env->GetIsolate()); |
| 1931 | double PI = 3.1415926; |
| 1932 | Local<Value> date = v8::Date::New(env.local(), PI).ToLocalChecked(); |
| 1933 | CHECK_EQ(3.0, date->NumberValue(env.local()).FromJust()); |
| 1934 | CHECK(date.As<v8::Date>() |
| 1935 | ->Set(env.local(), v8_str("property" ), |
| 1936 | v8::Integer::New(env->GetIsolate(), 42)) |
| 1937 | .FromJust()); |
| 1938 | CHECK_EQ(42, date.As<v8::Date>() |
| 1939 | ->Get(env.local(), v8_str("property" )) |
| 1940 | .ToLocalChecked() |
| 1941 | ->Int32Value(env.local()) |
| 1942 | .FromJust()); |
| 1943 | } |
| 1944 | |
| 1945 | |
| 1946 | THREADED_TEST(Boolean) { |
| 1947 | LocalContext env; |
| 1948 | v8::Isolate* isolate = env->GetIsolate(); |
| 1949 | v8::HandleScope scope(isolate); |
| 1950 | v8::Local<v8::Boolean> t = v8::True(isolate); |
| 1951 | CHECK(t->Value()); |
| 1952 | v8::Local<v8::Boolean> f = v8::False(isolate); |
| 1953 | CHECK(!f->Value()); |
| 1954 | v8::Local<v8::Primitive> u = v8::Undefined(isolate); |
| 1955 | CHECK(!u->BooleanValue(isolate)); |
| 1956 | v8::Local<v8::Primitive> n = v8::Null(isolate); |
| 1957 | CHECK(!n->BooleanValue(isolate)); |
| 1958 | v8::Local<String> str1 = v8_str("" ); |
| 1959 | CHECK(!str1->BooleanValue(isolate)); |
| 1960 | v8::Local<String> str2 = v8_str("x" ); |
| 1961 | CHECK(str2->BooleanValue(isolate)); |
| 1962 | CHECK(!v8::Number::New(isolate, 0)->BooleanValue(isolate)); |
| 1963 | CHECK(v8::Number::New(isolate, -1)->BooleanValue(isolate)); |
| 1964 | CHECK(v8::Number::New(isolate, 1)->BooleanValue(isolate)); |
| 1965 | CHECK(v8::Number::New(isolate, 42)->BooleanValue(isolate)); |
| 1966 | CHECK(!v8_compile("NaN" ) |
| 1967 | ->Run(env.local()) |
| 1968 | .ToLocalChecked() |
| 1969 | ->BooleanValue(isolate)); |
| 1970 | } |
| 1971 | |
| 1972 | |
| 1973 | static void DummyCallHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 1974 | ApiTestFuzzer::Fuzz(); |
| 1975 | args.GetReturnValue().Set(v8_num(13.4)); |
| 1976 | } |
| 1977 | |
| 1978 | |
| 1979 | static void GetM(Local<String> name, |
| 1980 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 1981 | ApiTestFuzzer::Fuzz(); |
| 1982 | info.GetReturnValue().Set(v8_num(876)); |
| 1983 | } |
| 1984 | |
| 1985 | |
| 1986 | THREADED_TEST(GlobalPrototype) { |
| 1987 | v8::Isolate* isolate = CcTest::isolate(); |
| 1988 | v8::HandleScope scope(isolate); |
| 1989 | v8::Local<v8::FunctionTemplate> func_templ = |
| 1990 | v8::FunctionTemplate::New(isolate); |
| 1991 | func_templ->PrototypeTemplate()->Set( |
| 1992 | isolate, "dummy" , v8::FunctionTemplate::New(isolate, DummyCallHandler)); |
| 1993 | v8::Local<ObjectTemplate> templ = func_templ->InstanceTemplate(); |
| 1994 | templ->Set(isolate, "x" , v8_num(200)); |
| 1995 | templ->SetAccessor(v8_str("m" ), GetM); |
| 1996 | LocalContext env(nullptr, templ); |
| 1997 | v8::Local<Script> script(v8_compile("dummy()" )); |
| 1998 | v8::Local<Value> result(script->Run(env.local()).ToLocalChecked()); |
| 1999 | CHECK_EQ(13.4, result->NumberValue(env.local()).FromJust()); |
| 2000 | CHECK_EQ(200, v8_run_int32value(v8_compile("x" ))); |
| 2001 | CHECK_EQ(876, v8_run_int32value(v8_compile("m" ))); |
| 2002 | } |
| 2003 | |
| 2004 | |
| 2005 | THREADED_TEST(ObjectTemplate) { |
| 2006 | LocalContext env; |
| 2007 | v8::Isolate* isolate = CcTest::isolate(); |
| 2008 | v8::HandleScope scope(isolate); |
| 2009 | Local<v8::FunctionTemplate> acc = |
| 2010 | v8::FunctionTemplate::New(isolate, Returns42); |
| 2011 | CHECK(env->Global() |
| 2012 | ->Set(env.local(), v8_str("acc" ), |
| 2013 | acc->GetFunction(env.local()).ToLocalChecked()) |
| 2014 | .FromJust()); |
| 2015 | |
| 2016 | Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); |
| 2017 | v8::Local<v8::String> class_name = v8_str("the_class_name" ); |
| 2018 | fun->SetClassName(class_name); |
| 2019 | Local<ObjectTemplate> templ1 = ObjectTemplate::New(isolate, fun); |
| 2020 | templ1->Set(isolate, "x" , v8_num(10)); |
| 2021 | templ1->Set(isolate, "y" , v8_num(13)); |
| 2022 | templ1->Set(v8_str("foo" ), acc); |
| 2023 | Local<v8::Object> instance1 = |
| 2024 | templ1->NewInstance(env.local()).ToLocalChecked(); |
| 2025 | CHECK(class_name->StrictEquals(instance1->GetConstructorName())); |
| 2026 | CHECK(env->Global()->Set(env.local(), v8_str("p" ), instance1).FromJust()); |
| 2027 | CHECK(CompileRun("(p.x == 10)" )->BooleanValue(isolate)); |
| 2028 | CHECK(CompileRun("(p.y == 13)" )->BooleanValue(isolate)); |
| 2029 | CHECK(CompileRun("(p.foo() == 42)" )->BooleanValue(isolate)); |
| 2030 | CHECK(CompileRun("(p.foo == acc)" )->BooleanValue(isolate)); |
| 2031 | // Ensure that foo become a data field. |
| 2032 | CompileRun("p.foo = function() {}" ); |
| 2033 | Local<v8::FunctionTemplate> fun2 = v8::FunctionTemplate::New(isolate); |
| 2034 | fun2->PrototypeTemplate()->Set(isolate, "nirk" , v8_num(123)); |
| 2035 | Local<ObjectTemplate> templ2 = fun2->InstanceTemplate(); |
| 2036 | templ2->Set(isolate, "a" , v8_num(12)); |
| 2037 | templ2->Set(isolate, "b" , templ1); |
| 2038 | templ2->Set(v8_str("bar" ), acc); |
| 2039 | templ2->SetAccessorProperty(v8_str("acc" ), acc); |
| 2040 | Local<v8::Object> instance2 = |
| 2041 | templ2->NewInstance(env.local()).ToLocalChecked(); |
| 2042 | CHECK(env->Global()->Set(env.local(), v8_str("q" ), instance2).FromJust()); |
| 2043 | CHECK(CompileRun("(q.nirk == 123)" )->BooleanValue(isolate)); |
| 2044 | CHECK(CompileRun("(q.a == 12)" )->BooleanValue(isolate)); |
| 2045 | CHECK(CompileRun("(q.b.x == 10)" )->BooleanValue(isolate)); |
| 2046 | CHECK(CompileRun("(q.b.y == 13)" )->BooleanValue(isolate)); |
| 2047 | CHECK(CompileRun("(q.b.foo() == 42)" )->BooleanValue(isolate)); |
| 2048 | CHECK(CompileRun("(q.b.foo === acc)" )->BooleanValue(isolate)); |
| 2049 | CHECK(CompileRun("(q.b !== p)" )->BooleanValue(isolate)); |
| 2050 | CHECK(CompileRun("(q.acc == 42)" )->BooleanValue(isolate)); |
| 2051 | CHECK(CompileRun("(q.bar() == 42)" )->BooleanValue(isolate)); |
| 2052 | CHECK(CompileRun("(q.bar == acc)" )->BooleanValue(isolate)); |
| 2053 | |
| 2054 | instance2 = templ2->NewInstance(env.local()).ToLocalChecked(); |
| 2055 | CHECK(env->Global()->Set(env.local(), v8_str("q2" ), instance2).FromJust()); |
| 2056 | CHECK(CompileRun("(q2.nirk == 123)" )->BooleanValue(isolate)); |
| 2057 | CHECK(CompileRun("(q2.a == 12)" )->BooleanValue(isolate)); |
| 2058 | CHECK(CompileRun("(q2.b.x == 10)" )->BooleanValue(isolate)); |
| 2059 | CHECK(CompileRun("(q2.b.y == 13)" )->BooleanValue(isolate)); |
| 2060 | CHECK(CompileRun("(q2.b.foo() == 42)" )->BooleanValue(isolate)); |
| 2061 | CHECK(CompileRun("(q2.b.foo === acc)" )->BooleanValue(isolate)); |
| 2062 | CHECK(CompileRun("(q2.acc == 42)" )->BooleanValue(isolate)); |
| 2063 | CHECK(CompileRun("(q2.bar() == 42)" )->BooleanValue(isolate)); |
| 2064 | CHECK(CompileRun("(q2.bar === acc)" )->BooleanValue(isolate)); |
| 2065 | |
| 2066 | CHECK(CompileRun("(q.b !== q2.b)" )->BooleanValue(isolate)); |
| 2067 | CHECK(CompileRun("q.b.x = 17; (q2.b.x == 10)" )->BooleanValue(isolate)); |
| 2068 | CHECK(CompileRun("desc1 = Object.getOwnPropertyDescriptor(q, 'acc');" |
| 2069 | "(desc1.get === acc)" ) |
| 2070 | ->BooleanValue(isolate)); |
| 2071 | CHECK(CompileRun("desc2 = Object.getOwnPropertyDescriptor(q2, 'acc');" |
| 2072 | "(desc2.get === acc)" ) |
| 2073 | ->BooleanValue(isolate)); |
| 2074 | } |
| 2075 | |
| 2076 | THREADED_TEST(IntegerValue) { |
| 2077 | LocalContext env; |
| 2078 | v8::Isolate* isolate = CcTest::isolate(); |
| 2079 | v8::HandleScope scope(isolate); |
| 2080 | |
| 2081 | CHECK_EQ(0, CompileRun("undefined" )->IntegerValue(env.local()).FromJust()); |
| 2082 | } |
| 2083 | |
| 2084 | static void GetNirk(Local<String> name, |
| 2085 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2086 | ApiTestFuzzer::Fuzz(); |
| 2087 | info.GetReturnValue().Set(v8_num(900)); |
| 2088 | } |
| 2089 | |
| 2090 | static void GetRino(Local<String> name, |
| 2091 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2092 | ApiTestFuzzer::Fuzz(); |
| 2093 | info.GetReturnValue().Set(v8_num(560)); |
| 2094 | } |
| 2095 | |
| 2096 | enum ObjectInstantiationMode { |
| 2097 | // Create object using ObjectTemplate::NewInstance. |
| 2098 | ObjectTemplate_NewInstance, |
| 2099 | // Create object using FunctionTemplate::NewInstance on constructor. |
| 2100 | Constructor_GetFunction_NewInstance, |
| 2101 | // Create object using new operator on constructor. |
| 2102 | Constructor_GetFunction_New |
| 2103 | }; |
| 2104 | |
| 2105 | // Test object instance creation using a function template with an instance |
| 2106 | // template inherited from another function template with accessors and data |
| 2107 | // properties in prototype template. |
| 2108 | static void TestObjectTemplateInheritedWithPrototype( |
| 2109 | ObjectInstantiationMode mode) { |
| 2110 | LocalContext env; |
| 2111 | v8::Isolate* isolate = CcTest::isolate(); |
| 2112 | v8::HandleScope scope(isolate); |
| 2113 | |
| 2114 | Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| 2115 | fun_A->SetClassName(v8_str("A" )); |
| 2116 | v8::Local<v8::ObjectTemplate> prototype_templ = fun_A->PrototypeTemplate(); |
| 2117 | prototype_templ->Set(isolate, "a" , v8_num(113)); |
| 2118 | prototype_templ->SetNativeDataProperty(v8_str("nirk" ), GetNirk); |
| 2119 | prototype_templ->Set(isolate, "b" , v8_num(153)); |
| 2120 | |
| 2121 | Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| 2122 | v8::Local<v8::String> class_name = v8_str("B" ); |
| 2123 | fun_B->SetClassName(class_name); |
| 2124 | fun_B->Inherit(fun_A); |
| 2125 | prototype_templ = fun_B->PrototypeTemplate(); |
| 2126 | prototype_templ->Set(isolate, "c" , v8_num(713)); |
| 2127 | prototype_templ->SetNativeDataProperty(v8_str("rino" ), GetRino); |
| 2128 | prototype_templ->Set(isolate, "d" , v8_num(753)); |
| 2129 | |
| 2130 | Local<ObjectTemplate> templ = fun_B->InstanceTemplate(); |
| 2131 | templ->Set(isolate, "x" , v8_num(10)); |
| 2132 | templ->Set(isolate, "y" , v8_num(13)); |
| 2133 | |
| 2134 | // Perform several iterations to trigger creation from cached boilerplate. |
| 2135 | for (int i = 0; i < 3; i++) { |
| 2136 | Local<v8::Object> instance; |
| 2137 | switch (mode) { |
| 2138 | case ObjectTemplate_NewInstance: |
| 2139 | instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 2140 | break; |
| 2141 | |
| 2142 | case Constructor_GetFunction_NewInstance: { |
| 2143 | Local<v8::Function> function_B = |
| 2144 | fun_B->GetFunction(env.local()).ToLocalChecked(); |
| 2145 | instance = function_B->NewInstance(env.local()).ToLocalChecked(); |
| 2146 | break; |
| 2147 | } |
| 2148 | case Constructor_GetFunction_New: { |
| 2149 | Local<v8::Function> function_B = |
| 2150 | fun_B->GetFunction(env.local()).ToLocalChecked(); |
| 2151 | if (i == 0) { |
| 2152 | CHECK(env->Global() |
| 2153 | ->Set(env.local(), class_name, function_B) |
| 2154 | .FromJust()); |
| 2155 | } |
| 2156 | instance = |
| 2157 | CompileRun("new B()" )->ToObject(env.local()).ToLocalChecked(); |
| 2158 | break; |
| 2159 | } |
| 2160 | default: |
| 2161 | UNREACHABLE(); |
| 2162 | } |
| 2163 | |
| 2164 | CHECK(class_name->StrictEquals(instance->GetConstructorName())); |
| 2165 | CHECK(env->Global()->Set(env.local(), v8_str("o" ), instance).FromJust()); |
| 2166 | |
| 2167 | CHECK_EQ(10, CompileRun("o.x" )->IntegerValue(env.local()).FromJust()); |
| 2168 | CHECK_EQ(13, CompileRun("o.y" )->IntegerValue(env.local()).FromJust()); |
| 2169 | |
| 2170 | CHECK_EQ(113, CompileRun("o.a" )->IntegerValue(env.local()).FromJust()); |
| 2171 | CHECK_EQ(900, CompileRun("o.nirk" )->IntegerValue(env.local()).FromJust()); |
| 2172 | CHECK_EQ(153, CompileRun("o.b" )->IntegerValue(env.local()).FromJust()); |
| 2173 | CHECK_EQ(713, CompileRun("o.c" )->IntegerValue(env.local()).FromJust()); |
| 2174 | CHECK_EQ(560, CompileRun("o.rino" )->IntegerValue(env.local()).FromJust()); |
| 2175 | CHECK_EQ(753, CompileRun("o.d" )->IntegerValue(env.local()).FromJust()); |
| 2176 | } |
| 2177 | } |
| 2178 | |
| 2179 | THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype1) { |
| 2180 | TestObjectTemplateInheritedWithPrototype(ObjectTemplate_NewInstance); |
| 2181 | } |
| 2182 | |
| 2183 | THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype2) { |
| 2184 | TestObjectTemplateInheritedWithPrototype(Constructor_GetFunction_NewInstance); |
| 2185 | } |
| 2186 | |
| 2187 | THREADED_TEST(TestObjectTemplateInheritedWithAccessorsInPrototype3) { |
| 2188 | TestObjectTemplateInheritedWithPrototype(Constructor_GetFunction_New); |
| 2189 | } |
| 2190 | |
| 2191 | // Test object instance creation using a function template without an instance |
| 2192 | // template inherited from another function template. |
| 2193 | static void TestObjectTemplateInheritedWithoutInstanceTemplate( |
| 2194 | ObjectInstantiationMode mode) { |
| 2195 | LocalContext env; |
| 2196 | v8::Isolate* isolate = CcTest::isolate(); |
| 2197 | v8::HandleScope scope(isolate); |
| 2198 | |
| 2199 | Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| 2200 | fun_A->SetClassName(v8_str("A" )); |
| 2201 | |
| 2202 | Local<ObjectTemplate> templ_A = fun_A->InstanceTemplate(); |
| 2203 | templ_A->SetNativeDataProperty(v8_str("nirk" ), GetNirk); |
| 2204 | templ_A->SetNativeDataProperty(v8_str("rino" ), GetRino); |
| 2205 | |
| 2206 | Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| 2207 | v8::Local<v8::String> class_name = v8_str("B" ); |
| 2208 | fun_B->SetClassName(class_name); |
| 2209 | fun_B->Inherit(fun_A); |
| 2210 | |
| 2211 | // Perform several iterations to trigger creation from cached boilerplate. |
| 2212 | for (int i = 0; i < 3; i++) { |
| 2213 | Local<v8::Object> instance; |
| 2214 | switch (mode) { |
| 2215 | case Constructor_GetFunction_NewInstance: { |
| 2216 | Local<v8::Function> function_B = |
| 2217 | fun_B->GetFunction(env.local()).ToLocalChecked(); |
| 2218 | instance = function_B->NewInstance(env.local()).ToLocalChecked(); |
| 2219 | break; |
| 2220 | } |
| 2221 | case Constructor_GetFunction_New: { |
| 2222 | Local<v8::Function> function_B = |
| 2223 | fun_B->GetFunction(env.local()).ToLocalChecked(); |
| 2224 | if (i == 0) { |
| 2225 | CHECK(env->Global() |
| 2226 | ->Set(env.local(), class_name, function_B) |
| 2227 | .FromJust()); |
| 2228 | } |
| 2229 | instance = |
| 2230 | CompileRun("new B()" )->ToObject(env.local()).ToLocalChecked(); |
| 2231 | break; |
| 2232 | } |
| 2233 | default: |
| 2234 | UNREACHABLE(); |
| 2235 | } |
| 2236 | |
| 2237 | CHECK(class_name->StrictEquals(instance->GetConstructorName())); |
| 2238 | CHECK(env->Global()->Set(env.local(), v8_str("o" ), instance).FromJust()); |
| 2239 | |
| 2240 | CHECK_EQ(900, CompileRun("o.nirk" )->IntegerValue(env.local()).FromJust()); |
| 2241 | CHECK_EQ(560, CompileRun("o.rino" )->IntegerValue(env.local()).FromJust()); |
| 2242 | } |
| 2243 | } |
| 2244 | |
| 2245 | THREADED_TEST(TestObjectTemplateInheritedWithPrototype1) { |
| 2246 | TestObjectTemplateInheritedWithoutInstanceTemplate( |
| 2247 | Constructor_GetFunction_NewInstance); |
| 2248 | } |
| 2249 | |
| 2250 | THREADED_TEST(TestObjectTemplateInheritedWithPrototype2) { |
| 2251 | TestObjectTemplateInheritedWithoutInstanceTemplate( |
| 2252 | Constructor_GetFunction_New); |
| 2253 | } |
| 2254 | |
| 2255 | THREADED_TEST(TestObjectTemplateClassInheritance) { |
| 2256 | LocalContext env; |
| 2257 | v8::Isolate* isolate = CcTest::isolate(); |
| 2258 | v8::HandleScope scope(isolate); |
| 2259 | |
| 2260 | Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| 2261 | fun_A->SetClassName(v8_str("A" )); |
| 2262 | |
| 2263 | Local<ObjectTemplate> templ_A = fun_A->InstanceTemplate(); |
| 2264 | templ_A->SetNativeDataProperty(v8_str("nirk" ), GetNirk); |
| 2265 | templ_A->SetNativeDataProperty(v8_str("rino" ), GetRino); |
| 2266 | |
| 2267 | Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| 2268 | v8::Local<v8::String> class_name = v8_str("B" ); |
| 2269 | fun_B->SetClassName(class_name); |
| 2270 | fun_B->Inherit(fun_A); |
| 2271 | |
| 2272 | v8::Local<v8::String> subclass_name = v8_str("C" ); |
| 2273 | v8::Local<v8::Object> b_proto; |
| 2274 | v8::Local<v8::Object> c_proto; |
| 2275 | // Perform several iterations to make sure the cache doesn't break |
| 2276 | // subclassing. |
| 2277 | for (int i = 0; i < 3; i++) { |
| 2278 | Local<v8::Function> function_B = |
| 2279 | fun_B->GetFunction(env.local()).ToLocalChecked(); |
| 2280 | if (i == 0) { |
| 2281 | CHECK(env->Global()->Set(env.local(), class_name, function_B).FromJust()); |
| 2282 | CompileRun("class C extends B {}" ); |
| 2283 | b_proto = |
| 2284 | CompileRun("B.prototype" )->ToObject(env.local()).ToLocalChecked(); |
| 2285 | c_proto = |
| 2286 | CompileRun("C.prototype" )->ToObject(env.local()).ToLocalChecked(); |
| 2287 | CHECK(b_proto->Equals(env.local(), c_proto->GetPrototype()).FromJust()); |
| 2288 | } |
| 2289 | Local<v8::Object> instance = |
| 2290 | CompileRun("new C()" )->ToObject(env.local()).ToLocalChecked(); |
| 2291 | CHECK(c_proto->Equals(env.local(), instance->GetPrototype()).FromJust()); |
| 2292 | |
| 2293 | CHECK(subclass_name->StrictEquals(instance->GetConstructorName())); |
| 2294 | CHECK(env->Global()->Set(env.local(), v8_str("o" ), instance).FromJust()); |
| 2295 | |
| 2296 | CHECK_EQ(900, CompileRun("o.nirk" )->IntegerValue(env.local()).FromJust()); |
| 2297 | CHECK_EQ(560, CompileRun("o.rino" )->IntegerValue(env.local()).FromJust()); |
| 2298 | } |
| 2299 | } |
| 2300 | |
| 2301 | static void NamedPropertyGetterWhichReturns42( |
| 2302 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2303 | info.GetReturnValue().Set(v8_num(42)); |
| 2304 | } |
| 2305 | |
| 2306 | THREADED_TEST(TestObjectTemplateReflectConstruct) { |
| 2307 | LocalContext env; |
| 2308 | v8::Isolate* isolate = CcTest::isolate(); |
| 2309 | v8::HandleScope scope(isolate); |
| 2310 | |
| 2311 | Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| 2312 | fun_B->InstanceTemplate()->SetHandler( |
| 2313 | v8::NamedPropertyHandlerConfiguration(NamedPropertyGetterWhichReturns42)); |
| 2314 | v8::Local<v8::String> class_name = v8_str("B" ); |
| 2315 | fun_B->SetClassName(class_name); |
| 2316 | |
| 2317 | v8::Local<v8::String> subclass_name = v8_str("C" ); |
| 2318 | v8::Local<v8::Object> b_proto; |
| 2319 | v8::Local<v8::Object> c_proto; |
| 2320 | // Perform several iterations to make sure the cache doesn't break |
| 2321 | // subclassing. |
| 2322 | for (int i = 0; i < 3; i++) { |
| 2323 | Local<v8::Function> function_B = |
| 2324 | fun_B->GetFunction(env.local()).ToLocalChecked(); |
| 2325 | if (i == 0) { |
| 2326 | CHECK(env->Global()->Set(env.local(), class_name, function_B).FromJust()); |
| 2327 | CompileRun("function C() {}" ); |
| 2328 | c_proto = |
| 2329 | CompileRun("C.prototype" )->ToObject(env.local()).ToLocalChecked(); |
| 2330 | } |
| 2331 | Local<v8::Object> instance = CompileRun("Reflect.construct(B, [], C)" ) |
| 2332 | ->ToObject(env.local()) |
| 2333 | .ToLocalChecked(); |
| 2334 | CHECK(c_proto->Equals(env.local(), instance->GetPrototype()).FromJust()); |
| 2335 | |
| 2336 | CHECK(subclass_name->StrictEquals(instance->GetConstructorName())); |
| 2337 | CHECK(env->Global()->Set(env.local(), v8_str("o" ), instance).FromJust()); |
| 2338 | |
| 2339 | CHECK_EQ(42, CompileRun("o.nirk" )->IntegerValue(env.local()).FromJust()); |
| 2340 | CHECK_EQ(42, CompileRun("o.rino" )->IntegerValue(env.local()).FromJust()); |
| 2341 | } |
| 2342 | } |
| 2343 | |
| 2344 | static void GetFlabby(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 2345 | ApiTestFuzzer::Fuzz(); |
| 2346 | args.GetReturnValue().Set(v8_num(17.2)); |
| 2347 | } |
| 2348 | |
| 2349 | |
| 2350 | static void GetKnurd(Local<String> property, |
| 2351 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2352 | ApiTestFuzzer::Fuzz(); |
| 2353 | info.GetReturnValue().Set(v8_num(15.2)); |
| 2354 | } |
| 2355 | |
| 2356 | |
| 2357 | THREADED_TEST(DescriptorInheritance) { |
| 2358 | v8::Isolate* isolate = CcTest::isolate(); |
| 2359 | v8::HandleScope scope(isolate); |
| 2360 | v8::Local<v8::FunctionTemplate> super = v8::FunctionTemplate::New(isolate); |
| 2361 | super->PrototypeTemplate()->Set(isolate, "flabby" , |
| 2362 | v8::FunctionTemplate::New(isolate, |
| 2363 | GetFlabby)); |
| 2364 | super->PrototypeTemplate()->Set(isolate, "PI" , v8_num(3.14)); |
| 2365 | |
| 2366 | super->InstanceTemplate()->SetAccessor(v8_str("knurd" ), GetKnurd); |
| 2367 | |
| 2368 | v8::Local<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(isolate); |
| 2369 | base1->Inherit(super); |
| 2370 | base1->PrototypeTemplate()->Set(isolate, "v1" , v8_num(20.1)); |
| 2371 | |
| 2372 | v8::Local<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(isolate); |
| 2373 | base2->Inherit(super); |
| 2374 | base2->PrototypeTemplate()->Set(isolate, "v2" , v8_num(10.1)); |
| 2375 | |
| 2376 | LocalContext env; |
| 2377 | |
| 2378 | CHECK(env->Global() |
| 2379 | ->Set(env.local(), v8_str("s" ), |
| 2380 | super->GetFunction(env.local()).ToLocalChecked()) |
| 2381 | .FromJust()); |
| 2382 | CHECK(env->Global() |
| 2383 | ->Set(env.local(), v8_str("base1" ), |
| 2384 | base1->GetFunction(env.local()).ToLocalChecked()) |
| 2385 | .FromJust()); |
| 2386 | CHECK(env->Global() |
| 2387 | ->Set(env.local(), v8_str("base2" ), |
| 2388 | base2->GetFunction(env.local()).ToLocalChecked()) |
| 2389 | .FromJust()); |
| 2390 | |
| 2391 | // Checks right __proto__ chain. |
| 2392 | CHECK(CompileRun("base1.prototype.__proto__ == s.prototype" ) |
| 2393 | ->BooleanValue(isolate)); |
| 2394 | CHECK(CompileRun("base2.prototype.__proto__ == s.prototype" ) |
| 2395 | ->BooleanValue(isolate)); |
| 2396 | |
| 2397 | CHECK(v8_compile("s.prototype.PI == 3.14" ) |
| 2398 | ->Run(env.local()) |
| 2399 | .ToLocalChecked() |
| 2400 | ->BooleanValue(isolate)); |
| 2401 | |
| 2402 | // Instance accessor should not be visible on function object or its prototype |
| 2403 | CHECK(CompileRun("s.knurd == undefined" )->BooleanValue(isolate)); |
| 2404 | CHECK(CompileRun("s.prototype.knurd == undefined" )->BooleanValue(isolate)); |
| 2405 | CHECK( |
| 2406 | CompileRun("base1.prototype.knurd == undefined" )->BooleanValue(isolate)); |
| 2407 | |
| 2408 | CHECK(env->Global() |
| 2409 | ->Set(env.local(), v8_str("obj" ), base1->GetFunction(env.local()) |
| 2410 | .ToLocalChecked() |
| 2411 | ->NewInstance(env.local()) |
| 2412 | .ToLocalChecked()) |
| 2413 | .FromJust()); |
| 2414 | CHECK_EQ(17.2, |
| 2415 | CompileRun("obj.flabby()" )->NumberValue(env.local()).FromJust()); |
| 2416 | CHECK(CompileRun("'flabby' in obj" )->BooleanValue(isolate)); |
| 2417 | CHECK_EQ(15.2, CompileRun("obj.knurd" )->NumberValue(env.local()).FromJust()); |
| 2418 | CHECK(CompileRun("'knurd' in obj" )->BooleanValue(isolate)); |
| 2419 | CHECK_EQ(20.1, CompileRun("obj.v1" )->NumberValue(env.local()).FromJust()); |
| 2420 | |
| 2421 | CHECK(env->Global() |
| 2422 | ->Set(env.local(), v8_str("obj2" ), base2->GetFunction(env.local()) |
| 2423 | .ToLocalChecked() |
| 2424 | ->NewInstance(env.local()) |
| 2425 | .ToLocalChecked()) |
| 2426 | .FromJust()); |
| 2427 | CHECK_EQ(17.2, |
| 2428 | CompileRun("obj2.flabby()" )->NumberValue(env.local()).FromJust()); |
| 2429 | CHECK(CompileRun("'flabby' in obj2" )->BooleanValue(isolate)); |
| 2430 | CHECK_EQ(15.2, CompileRun("obj2.knurd" )->NumberValue(env.local()).FromJust()); |
| 2431 | CHECK(CompileRun("'knurd' in obj2" )->BooleanValue(isolate)); |
| 2432 | CHECK_EQ(10.1, CompileRun("obj2.v2" )->NumberValue(env.local()).FromJust()); |
| 2433 | |
| 2434 | // base1 and base2 cannot cross reference to each's prototype |
| 2435 | CHECK(CompileRun("obj.v2" )->IsUndefined()); |
| 2436 | CHECK(CompileRun("obj2.v1" )->IsUndefined()); |
| 2437 | } |
| 2438 | |
| 2439 | THREADED_TEST(DescriptorInheritance2) { |
| 2440 | LocalContext env; |
| 2441 | v8::Isolate* isolate = CcTest::isolate(); |
| 2442 | v8::HandleScope scope(isolate); |
| 2443 | v8::Local<v8::FunctionTemplate> fun_A = v8::FunctionTemplate::New(isolate); |
| 2444 | fun_A->SetClassName(v8_str("A" )); |
| 2445 | fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd1" ), GetKnurd); |
| 2446 | fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk1" ), GetNirk); |
| 2447 | fun_A->InstanceTemplate()->SetNativeDataProperty(v8_str("rino1" ), GetRino); |
| 2448 | |
| 2449 | v8::Local<v8::FunctionTemplate> fun_B = v8::FunctionTemplate::New(isolate); |
| 2450 | fun_B->SetClassName(v8_str("B" )); |
| 2451 | fun_B->Inherit(fun_A); |
| 2452 | |
| 2453 | v8::Local<v8::FunctionTemplate> fun_C = v8::FunctionTemplate::New(isolate); |
| 2454 | fun_C->SetClassName(v8_str("C" )); |
| 2455 | fun_C->Inherit(fun_B); |
| 2456 | fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd2" ), GetKnurd); |
| 2457 | fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk2" ), GetNirk); |
| 2458 | fun_C->InstanceTemplate()->SetNativeDataProperty(v8_str("rino2" ), GetRino); |
| 2459 | |
| 2460 | v8::Local<v8::FunctionTemplate> fun_D = v8::FunctionTemplate::New(isolate); |
| 2461 | fun_D->SetClassName(v8_str("D" )); |
| 2462 | fun_D->Inherit(fun_C); |
| 2463 | |
| 2464 | v8::Local<v8::FunctionTemplate> fun_E = v8::FunctionTemplate::New(isolate); |
| 2465 | fun_E->SetClassName(v8_str("E" )); |
| 2466 | fun_E->Inherit(fun_D); |
| 2467 | fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("knurd3" ), GetKnurd); |
| 2468 | fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("nirk3" ), GetNirk); |
| 2469 | fun_E->InstanceTemplate()->SetNativeDataProperty(v8_str("rino3" ), GetRino); |
| 2470 | |
| 2471 | v8::Local<v8::FunctionTemplate> fun_F = v8::FunctionTemplate::New(isolate); |
| 2472 | fun_F->SetClassName(v8_str("F" )); |
| 2473 | fun_F->Inherit(fun_E); |
| 2474 | v8::Local<v8::ObjectTemplate> templ = fun_F->InstanceTemplate(); |
| 2475 | const int kDataPropertiesNumber = 100; |
| 2476 | for (int i = 0; i < kDataPropertiesNumber; i++) { |
| 2477 | v8::Local<v8::Value> val = v8_num(i); |
| 2478 | v8::Local<v8::String> val_str = val->ToString(env.local()).ToLocalChecked(); |
| 2479 | v8::Local<v8::String> name = String::Concat(isolate, v8_str("p" ), val_str); |
| 2480 | |
| 2481 | templ->Set(name, val); |
| 2482 | templ->Set(val_str, val); |
| 2483 | } |
| 2484 | |
| 2485 | CHECK(env->Global() |
| 2486 | ->Set(env.local(), v8_str("F" ), |
| 2487 | fun_F->GetFunction(env.local()).ToLocalChecked()) |
| 2488 | .FromJust()); |
| 2489 | |
| 2490 | v8::Local<v8::Script> script = v8_compile("o = new F()" ); |
| 2491 | |
| 2492 | for (int i = 0; i < 100; i++) { |
| 2493 | v8::HandleScope scope(isolate); |
| 2494 | script->Run(env.local()).ToLocalChecked(); |
| 2495 | } |
| 2496 | v8::Local<v8::Object> object = script->Run(env.local()) |
| 2497 | .ToLocalChecked() |
| 2498 | ->ToObject(env.local()) |
| 2499 | .ToLocalChecked(); |
| 2500 | |
| 2501 | CHECK_EQ(15.2, CompileRun("o.knurd1" )->NumberValue(env.local()).FromJust()); |
| 2502 | CHECK_EQ(15.2, CompileRun("o.knurd2" )->NumberValue(env.local()).FromJust()); |
| 2503 | CHECK_EQ(15.2, CompileRun("o.knurd3" )->NumberValue(env.local()).FromJust()); |
| 2504 | |
| 2505 | CHECK_EQ(900, CompileRun("o.nirk1" )->IntegerValue(env.local()).FromJust()); |
| 2506 | CHECK_EQ(900, CompileRun("o.nirk2" )->IntegerValue(env.local()).FromJust()); |
| 2507 | CHECK_EQ(900, CompileRun("o.nirk3" )->IntegerValue(env.local()).FromJust()); |
| 2508 | |
| 2509 | CHECK_EQ(560, CompileRun("o.rino1" )->IntegerValue(env.local()).FromJust()); |
| 2510 | CHECK_EQ(560, CompileRun("o.rino2" )->IntegerValue(env.local()).FromJust()); |
| 2511 | CHECK_EQ(560, CompileRun("o.rino3" )->IntegerValue(env.local()).FromJust()); |
| 2512 | |
| 2513 | for (int i = 0; i < kDataPropertiesNumber; i++) { |
| 2514 | v8::Local<v8::Value> val = v8_num(i); |
| 2515 | v8::Local<v8::String> val_str = val->ToString(env.local()).ToLocalChecked(); |
| 2516 | v8::Local<v8::String> name = String::Concat(isolate, v8_str("p" ), val_str); |
| 2517 | |
| 2518 | CHECK_EQ(i, object->Get(env.local(), name) |
| 2519 | .ToLocalChecked() |
| 2520 | ->IntegerValue(env.local()) |
| 2521 | .FromJust()); |
| 2522 | CHECK_EQ(i, object->Get(env.local(), val) |
| 2523 | .ToLocalChecked() |
| 2524 | ->IntegerValue(env.local()) |
| 2525 | .FromJust()); |
| 2526 | } |
| 2527 | } |
| 2528 | |
| 2529 | |
| 2530 | // Helper functions for Interceptor/Accessor interaction tests |
| 2531 | |
| 2532 | void SimpleAccessorGetter(Local<String> name, |
| 2533 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2534 | Local<Object> self = Local<Object>::Cast(info.This()); |
| 2535 | info.GetReturnValue().Set( |
| 2536 | self->Get(info.GetIsolate()->GetCurrentContext(), |
| 2537 | String::Concat(info.GetIsolate(), v8_str("accessor_" ), name)) |
| 2538 | .ToLocalChecked()); |
| 2539 | } |
| 2540 | |
| 2541 | void SimpleAccessorSetter(Local<String> name, Local<Value> value, |
| 2542 | const v8::PropertyCallbackInfo<void>& info) { |
| 2543 | Local<Object> self = Local<Object>::Cast(info.This()); |
| 2544 | CHECK(self->Set(info.GetIsolate()->GetCurrentContext(), |
| 2545 | String::Concat(info.GetIsolate(), v8_str("accessor_" ), name), |
| 2546 | value) |
| 2547 | .FromJust()); |
| 2548 | } |
| 2549 | |
| 2550 | void SymbolAccessorGetter(Local<Name> name, |
| 2551 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2552 | CHECK(name->IsSymbol()); |
| 2553 | Local<Symbol> sym = Local<Symbol>::Cast(name); |
| 2554 | if (sym->Name()->IsUndefined()) |
| 2555 | return; |
| 2556 | SimpleAccessorGetter(Local<String>::Cast(sym->Name()), info); |
| 2557 | } |
| 2558 | |
| 2559 | void SymbolAccessorSetter(Local<Name> name, Local<Value> value, |
| 2560 | const v8::PropertyCallbackInfo<void>& info) { |
| 2561 | CHECK(name->IsSymbol()); |
| 2562 | Local<Symbol> sym = Local<Symbol>::Cast(name); |
| 2563 | if (sym->Name()->IsUndefined()) |
| 2564 | return; |
| 2565 | SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info); |
| 2566 | } |
| 2567 | |
| 2568 | void SymbolAccessorGetterReturnsDefault( |
| 2569 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2570 | CHECK(name->IsSymbol()); |
| 2571 | Local<Symbol> sym = Local<Symbol>::Cast(name); |
| 2572 | if (sym->Name()->IsUndefined()) return; |
| 2573 | info.GetReturnValue().Set(info.Data()); |
| 2574 | } |
| 2575 | |
| 2576 | static void ThrowingSymbolAccessorGetter( |
| 2577 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2578 | info.GetReturnValue().Set(info.GetIsolate()->ThrowException(name)); |
| 2579 | } |
| 2580 | |
| 2581 | |
| 2582 | THREADED_TEST(AccessorIsPreservedOnAttributeChange) { |
| 2583 | v8::Isolate* isolate = CcTest::isolate(); |
| 2584 | v8::HandleScope scope(isolate); |
| 2585 | LocalContext env; |
| 2586 | v8::Local<v8::Value> res = CompileRun("var a = []; a;" ); |
| 2587 | i::Handle<i::JSReceiver> a(v8::Utils::OpenHandle(v8::Object::Cast(*res))); |
| 2588 | CHECK_EQ(1, a->map()->instance_descriptors()->number_of_descriptors()); |
| 2589 | CompileRun("Object.defineProperty(a, 'length', { writable: false });" ); |
| 2590 | CHECK_EQ(0, a->map()->instance_descriptors()->number_of_descriptors()); |
| 2591 | // But we should still have an AccessorInfo. |
| 2592 | i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length" ))); |
| 2593 | i::LookupIterator it(CcTest::i_isolate(), a, name, |
| 2594 | i::LookupIterator::OWN_SKIP_INTERCEPTOR); |
| 2595 | CHECK_EQ(i::LookupIterator::ACCESSOR, it.state()); |
| 2596 | CHECK(it.GetAccessors()->IsAccessorInfo()); |
| 2597 | } |
| 2598 | |
| 2599 | |
| 2600 | THREADED_TEST(UndefinedIsNotEnumerable) { |
| 2601 | LocalContext env; |
| 2602 | v8::HandleScope scope(env->GetIsolate()); |
| 2603 | v8::Local<Value> result = CompileRun("this.propertyIsEnumerable(undefined)" ); |
| 2604 | CHECK(result->IsFalse()); |
| 2605 | } |
| 2606 | |
| 2607 | |
| 2608 | v8::Local<Script> call_recursively_script; |
| 2609 | static const int kTargetRecursionDepth = 100; // near maximum |
| 2610 | |
| 2611 | static void CallScriptRecursivelyCall( |
| 2612 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 2613 | ApiTestFuzzer::Fuzz(); |
| 2614 | v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| 2615 | int depth = args.This() |
| 2616 | ->Get(context, v8_str("depth" )) |
| 2617 | .ToLocalChecked() |
| 2618 | ->Int32Value(context) |
| 2619 | .FromJust(); |
| 2620 | if (depth == kTargetRecursionDepth) return; |
| 2621 | CHECK(args.This() |
| 2622 | ->Set(context, v8_str("depth" ), |
| 2623 | v8::Integer::New(args.GetIsolate(), depth + 1)) |
| 2624 | .FromJust()); |
| 2625 | args.GetReturnValue().Set( |
| 2626 | call_recursively_script->Run(context).ToLocalChecked()); |
| 2627 | } |
| 2628 | |
| 2629 | |
| 2630 | static void CallFunctionRecursivelyCall( |
| 2631 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 2632 | ApiTestFuzzer::Fuzz(); |
| 2633 | v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| 2634 | int depth = args.This() |
| 2635 | ->Get(context, v8_str("depth" )) |
| 2636 | .ToLocalChecked() |
| 2637 | ->Int32Value(context) |
| 2638 | .FromJust(); |
| 2639 | if (depth == kTargetRecursionDepth) { |
| 2640 | printf("[depth = %d]\n" , depth); |
| 2641 | return; |
| 2642 | } |
| 2643 | CHECK(args.This() |
| 2644 | ->Set(context, v8_str("depth" ), |
| 2645 | v8::Integer::New(args.GetIsolate(), depth + 1)) |
| 2646 | .FromJust()); |
| 2647 | v8::Local<Value> function = |
| 2648 | args.This() |
| 2649 | ->Get(context, v8_str("callFunctionRecursively" )) |
| 2650 | .ToLocalChecked(); |
| 2651 | args.GetReturnValue().Set(function.As<Function>() |
| 2652 | ->Call(context, args.This(), 0, nullptr) |
| 2653 | .ToLocalChecked()); |
| 2654 | } |
| 2655 | |
| 2656 | |
| 2657 | THREADED_TEST(DeepCrossLanguageRecursion) { |
| 2658 | v8::Isolate* isolate = CcTest::isolate(); |
| 2659 | v8::HandleScope scope(isolate); |
| 2660 | v8::Local<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); |
| 2661 | global->Set(v8_str("callScriptRecursively" ), |
| 2662 | v8::FunctionTemplate::New(isolate, CallScriptRecursivelyCall)); |
| 2663 | global->Set(v8_str("callFunctionRecursively" ), |
| 2664 | v8::FunctionTemplate::New(isolate, CallFunctionRecursivelyCall)); |
| 2665 | LocalContext env(nullptr, global); |
| 2666 | |
| 2667 | CHECK(env->Global() |
| 2668 | ->Set(env.local(), v8_str("depth" ), v8::Integer::New(isolate, 0)) |
| 2669 | .FromJust()); |
| 2670 | call_recursively_script = v8_compile("callScriptRecursively()" ); |
| 2671 | call_recursively_script->Run(env.local()).ToLocalChecked(); |
| 2672 | call_recursively_script = v8::Local<Script>(); |
| 2673 | |
| 2674 | CHECK(env->Global() |
| 2675 | ->Set(env.local(), v8_str("depth" ), v8::Integer::New(isolate, 0)) |
| 2676 | .FromJust()); |
| 2677 | CompileRun("callFunctionRecursively()" ); |
| 2678 | } |
| 2679 | |
| 2680 | |
| 2681 | static void ThrowingPropertyHandlerGet( |
| 2682 | Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2683 | // Since this interceptor is used on "with" objects, the runtime will look up |
| 2684 | // @@unscopables. Punt. |
| 2685 | if (key->IsSymbol()) return; |
| 2686 | ApiTestFuzzer::Fuzz(); |
| 2687 | info.GetReturnValue().Set(info.GetIsolate()->ThrowException(key)); |
| 2688 | } |
| 2689 | |
| 2690 | |
| 2691 | static void ThrowingPropertyHandlerSet( |
| 2692 | Local<Name> key, Local<Value>, |
| 2693 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 2694 | info.GetIsolate()->ThrowException(key); |
| 2695 | info.GetReturnValue().SetUndefined(); // not the same as empty handle |
| 2696 | } |
| 2697 | |
| 2698 | |
| 2699 | THREADED_TEST(CallbackExceptionRegression) { |
| 2700 | v8::Isolate* isolate = CcTest::isolate(); |
| 2701 | v8::HandleScope scope(isolate); |
| 2702 | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| 2703 | obj->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 2704 | ThrowingPropertyHandlerGet, ThrowingPropertyHandlerSet)); |
| 2705 | LocalContext env; |
| 2706 | CHECK(env->Global() |
| 2707 | ->Set(env.local(), v8_str("obj" ), |
| 2708 | obj->NewInstance(env.local()).ToLocalChecked()) |
| 2709 | .FromJust()); |
| 2710 | v8::Local<Value> otto = |
| 2711 | CompileRun("try { with (obj) { otto; } } catch (e) { e; }" ); |
| 2712 | CHECK(v8_str("otto" )->Equals(env.local(), otto).FromJust()); |
| 2713 | v8::Local<Value> netto = |
| 2714 | CompileRun("try { with (obj) { netto = 4; } } catch (e) { e; }" ); |
| 2715 | CHECK(v8_str("netto" )->Equals(env.local(), netto).FromJust()); |
| 2716 | } |
| 2717 | |
| 2718 | |
| 2719 | THREADED_TEST(FunctionPrototype) { |
| 2720 | v8::Isolate* isolate = CcTest::isolate(); |
| 2721 | v8::HandleScope scope(isolate); |
| 2722 | Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(isolate); |
| 2723 | Foo->PrototypeTemplate()->Set(v8_str("plak" ), v8_num(321)); |
| 2724 | LocalContext env; |
| 2725 | CHECK(env->Global() |
| 2726 | ->Set(env.local(), v8_str("Foo" ), |
| 2727 | Foo->GetFunction(env.local()).ToLocalChecked()) |
| 2728 | .FromJust()); |
| 2729 | Local<Script> script = v8_compile("Foo.prototype.plak" ); |
| 2730 | CHECK_EQ(v8_run_int32value(script), 321); |
| 2731 | } |
| 2732 | |
| 2733 | THREADED_TEST(InternalFields) { |
| 2734 | LocalContext env; |
| 2735 | v8::Isolate* isolate = env->GetIsolate(); |
| 2736 | v8::HandleScope scope(isolate); |
| 2737 | |
| 2738 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 2739 | Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 2740 | instance_templ->SetInternalFieldCount(1); |
| 2741 | Local<v8::Object> obj = templ->GetFunction(env.local()) |
| 2742 | .ToLocalChecked() |
| 2743 | ->NewInstance(env.local()) |
| 2744 | .ToLocalChecked(); |
| 2745 | CHECK_EQ(1, obj->InternalFieldCount()); |
| 2746 | CHECK(obj->GetInternalField(0)->IsUndefined()); |
| 2747 | obj->SetInternalField(0, v8_num(17)); |
| 2748 | CHECK_EQ(17, obj->GetInternalField(0)->Int32Value(env.local()).FromJust()); |
| 2749 | } |
| 2750 | |
| 2751 | TEST(InternalFieldsSubclassing) { |
| 2752 | LocalContext env; |
| 2753 | v8::Isolate* isolate = env->GetIsolate(); |
| 2754 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 2755 | v8::HandleScope scope(isolate); |
| 2756 | for (int nof_embedder_fields = 0; |
| 2757 | nof_embedder_fields < i::JSObject::kMaxEmbedderFields; |
| 2758 | nof_embedder_fields++) { |
| 2759 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 2760 | Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 2761 | instance_templ->SetInternalFieldCount(nof_embedder_fields); |
| 2762 | Local<Function> constructor = |
| 2763 | templ->GetFunction(env.local()).ToLocalChecked(); |
| 2764 | // Check that instances have the correct NOF properties. |
| 2765 | Local<v8::Object> obj = |
| 2766 | constructor->NewInstance(env.local()).ToLocalChecked(); |
| 2767 | |
| 2768 | i::Handle<i::JSObject> i_obj = |
| 2769 | i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); |
| 2770 | CHECK_EQ(nof_embedder_fields, obj->InternalFieldCount()); |
| 2771 | CHECK_EQ(0, i_obj->map()->GetInObjectProperties()); |
| 2772 | // Check writing and reading internal fields. |
| 2773 | for (int j = 0; j < nof_embedder_fields; j++) { |
| 2774 | CHECK(obj->GetInternalField(j)->IsUndefined()); |
| 2775 | int value = 17 + j; |
| 2776 | obj->SetInternalField(j, v8_num(value)); |
| 2777 | } |
| 2778 | for (int j = 0; j < nof_embedder_fields; j++) { |
| 2779 | int value = 17 + j; |
| 2780 | CHECK_EQ(value, |
| 2781 | obj->GetInternalField(j)->Int32Value(env.local()).FromJust()); |
| 2782 | } |
| 2783 | CHECK(env->Global() |
| 2784 | ->Set(env.local(), v8_str("BaseClass" ), constructor) |
| 2785 | .FromJust()); |
| 2786 | // Create various levels of subclasses to stress instance size calculation. |
| 2787 | const int kMaxNofProperties = |
| 2788 | i::JSObject::kMaxInObjectProperties - |
| 2789 | nof_embedder_fields * i::kEmbedderDataSlotSizeInTaggedSlots; |
| 2790 | // Select only a few values to speed up the test. |
| 2791 | int sizes[] = {0, |
| 2792 | 1, |
| 2793 | 2, |
| 2794 | 3, |
| 2795 | 4, |
| 2796 | 5, |
| 2797 | 6, |
| 2798 | kMaxNofProperties / 4, |
| 2799 | kMaxNofProperties / 2, |
| 2800 | kMaxNofProperties - 2, |
| 2801 | kMaxNofProperties - 1, |
| 2802 | kMaxNofProperties + 1, |
| 2803 | kMaxNofProperties + 2, |
| 2804 | kMaxNofProperties * 2, |
| 2805 | kMaxNofProperties * 2}; |
| 2806 | for (size_t i = 0; i < arraysize(sizes); i++) { |
| 2807 | int nof_properties = sizes[i]; |
| 2808 | bool in_object_only = nof_properties <= kMaxNofProperties; |
| 2809 | std::ostringstream src; |
| 2810 | // Assembler source string for a subclass with {nof_properties} |
| 2811 | // in-object properties. |
| 2812 | src << "(function() {\n" |
| 2813 | << " class SubClass extends BaseClass {\n" |
| 2814 | << " constructor() {\n" |
| 2815 | << " super();\n" ; |
| 2816 | // Set {nof_properties} instance properties in the constructor. |
| 2817 | for (int j = 0; j < nof_properties; j++) { |
| 2818 | src << " this.property" << j << " = " << j << ";\n" ; |
| 2819 | } |
| 2820 | src << " }\n" |
| 2821 | << " };\n" |
| 2822 | << " let instance;\n" |
| 2823 | << " for (let i = 0; i < 3; i++) {\n" |
| 2824 | << " instance = new SubClass();\n" |
| 2825 | << " }" |
| 2826 | << " return instance;\n" |
| 2827 | << "})();" ; |
| 2828 | Local<v8::Object> value = CompileRun(src.str().c_str()).As<v8::Object>(); |
| 2829 | |
| 2830 | i::Handle<i::JSObject> i_value = |
| 2831 | i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*value)); |
| 2832 | #ifdef VERIFY_HEAP |
| 2833 | i_value->HeapObjectVerify(i_isolate); |
| 2834 | i_value->map()->HeapObjectVerify(i_isolate); |
| 2835 | i_value->map()->FindRootMap(i_isolate)->HeapObjectVerify(i_isolate); |
| 2836 | #endif |
| 2837 | CHECK_EQ(nof_embedder_fields, value->InternalFieldCount()); |
| 2838 | if (in_object_only) { |
| 2839 | CHECK_LE(nof_properties, i_value->map()->GetInObjectProperties()); |
| 2840 | } else { |
| 2841 | CHECK_LE(i_value->map()->GetInObjectProperties(), kMaxNofProperties); |
| 2842 | } |
| 2843 | |
| 2844 | // Make Sure we get the precise property count. |
| 2845 | i_value->map()->FindRootMap(i_isolate)->CompleteInobjectSlackTracking( |
| 2846 | i_isolate); |
| 2847 | // TODO(cbruni): fix accounting to make this condition true. |
| 2848 | // CHECK_EQ(0, i_value->map()->UnusedPropertyFields()); |
| 2849 | if (in_object_only) { |
| 2850 | CHECK_EQ(nof_properties, i_value->map()->GetInObjectProperties()); |
| 2851 | } else { |
| 2852 | CHECK_LE(i_value->map()->GetInObjectProperties(), kMaxNofProperties); |
| 2853 | } |
| 2854 | } |
| 2855 | } |
| 2856 | } |
| 2857 | |
| 2858 | THREADED_TEST(InternalFieldsOfRegularObjects) { |
| 2859 | LocalContext env; |
| 2860 | v8::Isolate* isolate = env->GetIsolate(); |
| 2861 | v8::HandleScope scope(isolate); |
| 2862 | |
| 2863 | const char* sources[] = {"new Object()" , "{ a: 'a property' }" , "arguments" }; |
| 2864 | for (size_t i = 0; i < arraysize(sources); ++i) { |
| 2865 | i::ScopedVector<char> source(128); |
| 2866 | i::SNPrintF(source, "(function() { return %s })()" , sources[i]); |
| 2867 | v8::Local<v8::Object> obj = CompileRun(source.start()).As<v8::Object>(); |
| 2868 | CHECK_EQ(0, obj->InternalFieldCount()); |
| 2869 | } |
| 2870 | } |
| 2871 | |
| 2872 | THREADED_TEST(GlobalObjectInternalFields) { |
| 2873 | v8::Isolate* isolate = CcTest::isolate(); |
| 2874 | v8::HandleScope scope(isolate); |
| 2875 | Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| 2876 | global_template->SetInternalFieldCount(1); |
| 2877 | LocalContext env(nullptr, global_template); |
| 2878 | v8::Local<v8::Object> global_proxy = env->Global(); |
| 2879 | v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); |
| 2880 | CHECK_EQ(1, global->InternalFieldCount()); |
| 2881 | CHECK(global->GetInternalField(0)->IsUndefined()); |
| 2882 | global->SetInternalField(0, v8_num(17)); |
| 2883 | CHECK_EQ(17, global->GetInternalField(0)->Int32Value(env.local()).FromJust()); |
| 2884 | } |
| 2885 | |
| 2886 | |
| 2887 | THREADED_TEST(GlobalObjectHasRealIndexedProperty) { |
| 2888 | LocalContext env; |
| 2889 | v8::HandleScope scope(CcTest::isolate()); |
| 2890 | |
| 2891 | v8::Local<v8::Object> global = env->Global(); |
| 2892 | CHECK(global->Set(env.local(), 0, v8_str("value" )).FromJust()); |
| 2893 | CHECK(global->HasRealIndexedProperty(env.local(), 0).FromJust()); |
| 2894 | } |
| 2895 | |
| 2896 | static void CheckAlignedPointerInInternalField(Local<v8::Object> obj, |
| 2897 | void* value) { |
| 2898 | CHECK(HAS_SMI_TAG(reinterpret_cast<i::Address>(value))); |
| 2899 | obj->SetAlignedPointerInInternalField(0, value); |
| 2900 | CcTest::CollectAllGarbage(); |
| 2901 | CHECK_EQ(value, obj->GetAlignedPointerFromInternalField(0)); |
| 2902 | } |
| 2903 | |
| 2904 | THREADED_TEST(InternalFieldsAlignedPointers) { |
| 2905 | LocalContext env; |
| 2906 | v8::Isolate* isolate = env->GetIsolate(); |
| 2907 | v8::HandleScope scope(isolate); |
| 2908 | |
| 2909 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 2910 | Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 2911 | instance_templ->SetInternalFieldCount(1); |
| 2912 | Local<v8::Object> obj = templ->GetFunction(env.local()) |
| 2913 | .ToLocalChecked() |
| 2914 | ->NewInstance(env.local()) |
| 2915 | .ToLocalChecked(); |
| 2916 | CHECK_EQ(1, obj->InternalFieldCount()); |
| 2917 | |
| 2918 | CheckAlignedPointerInInternalField(obj, nullptr); |
| 2919 | |
| 2920 | int* heap_allocated = new int[100]; |
| 2921 | CheckAlignedPointerInInternalField(obj, heap_allocated); |
| 2922 | delete[] heap_allocated; |
| 2923 | |
| 2924 | int stack_allocated[100]; |
| 2925 | CheckAlignedPointerInInternalField(obj, stack_allocated); |
| 2926 | |
| 2927 | void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); |
| 2928 | CheckAlignedPointerInInternalField(obj, huge); |
| 2929 | |
| 2930 | v8::Global<v8::Object> persistent(isolate, obj); |
| 2931 | CHECK_EQ(1, Object::InternalFieldCount(persistent)); |
| 2932 | CHECK_EQ(huge, Object::GetAlignedPointerFromInternalField(persistent, 0)); |
| 2933 | } |
| 2934 | |
| 2935 | THREADED_TEST(SetAlignedPointerInInternalFields) { |
| 2936 | LocalContext env; |
| 2937 | v8::Isolate* isolate = env->GetIsolate(); |
| 2938 | v8::HandleScope scope(isolate); |
| 2939 | |
| 2940 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 2941 | Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 2942 | instance_templ->SetInternalFieldCount(2); |
| 2943 | Local<v8::Object> obj = templ->GetFunction(env.local()) |
| 2944 | .ToLocalChecked() |
| 2945 | ->NewInstance(env.local()) |
| 2946 | .ToLocalChecked(); |
| 2947 | CHECK_EQ(2, obj->InternalFieldCount()); |
| 2948 | |
| 2949 | int* heap_allocated_1 = new int[100]; |
| 2950 | int* heap_allocated_2 = new int[100]; |
| 2951 | int indices[] = {0, 1}; |
| 2952 | void* values[] = {heap_allocated_1, heap_allocated_2}; |
| 2953 | |
| 2954 | obj->SetAlignedPointerInInternalFields(2, indices, values); |
| 2955 | CcTest::CollectAllGarbage(); |
| 2956 | CHECK_EQ(heap_allocated_1, obj->GetAlignedPointerFromInternalField(0)); |
| 2957 | CHECK_EQ(heap_allocated_2, obj->GetAlignedPointerFromInternalField(1)); |
| 2958 | |
| 2959 | indices[0] = 1; |
| 2960 | indices[1] = 0; |
| 2961 | obj->SetAlignedPointerInInternalFields(2, indices, values); |
| 2962 | CcTest::CollectAllGarbage(); |
| 2963 | CHECK_EQ(heap_allocated_2, obj->GetAlignedPointerFromInternalField(0)); |
| 2964 | CHECK_EQ(heap_allocated_1, obj->GetAlignedPointerFromInternalField(1)); |
| 2965 | |
| 2966 | delete[] heap_allocated_1; |
| 2967 | delete[] heap_allocated_2; |
| 2968 | } |
| 2969 | |
| 2970 | static void CheckAlignedPointerInEmbedderData(LocalContext* env, int index, |
| 2971 | void* value) { |
| 2972 | CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); |
| 2973 | (*env)->SetAlignedPointerInEmbedderData(index, value); |
| 2974 | CcTest::CollectAllGarbage(); |
| 2975 | CHECK_EQ(value, (*env)->GetAlignedPointerFromEmbedderData(index)); |
| 2976 | } |
| 2977 | |
| 2978 | |
| 2979 | static void* AlignedTestPointer(int i) { |
| 2980 | return reinterpret_cast<void*>(i * 1234); |
| 2981 | } |
| 2982 | |
| 2983 | |
| 2984 | THREADED_TEST(EmbedderDataAlignedPointers) { |
| 2985 | LocalContext env; |
| 2986 | v8::HandleScope scope(env->GetIsolate()); |
| 2987 | |
| 2988 | CheckAlignedPointerInEmbedderData(&env, 0, nullptr); |
| 2989 | CHECK_EQ(1, (*env)->GetNumberOfEmbedderDataFields()); |
| 2990 | |
| 2991 | int* heap_allocated = new int[100]; |
| 2992 | CheckAlignedPointerInEmbedderData(&env, 1, heap_allocated); |
| 2993 | CHECK_EQ(2, (*env)->GetNumberOfEmbedderDataFields()); |
| 2994 | delete[] heap_allocated; |
| 2995 | |
| 2996 | int stack_allocated[100]; |
| 2997 | CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated); |
| 2998 | CHECK_EQ(3, (*env)->GetNumberOfEmbedderDataFields()); |
| 2999 | |
| 3000 | void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); |
| 3001 | CheckAlignedPointerInEmbedderData(&env, 3, huge); |
| 3002 | CHECK_EQ(4, (*env)->GetNumberOfEmbedderDataFields()); |
| 3003 | |
| 3004 | // Test growing of the embedder data's backing store. |
| 3005 | for (int i = 0; i < 100; i++) { |
| 3006 | env->SetAlignedPointerInEmbedderData(i, AlignedTestPointer(i)); |
| 3007 | } |
| 3008 | CcTest::CollectAllGarbage(); |
| 3009 | for (int i = 0; i < 100; i++) { |
| 3010 | CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i)); |
| 3011 | } |
| 3012 | } |
| 3013 | |
| 3014 | static void CheckEmbedderData(LocalContext* env, int index, |
| 3015 | v8::Local<Value> data) { |
| 3016 | (*env)->SetEmbedderData(index, data); |
| 3017 | CHECK((*env)->GetEmbedderData(index)->StrictEquals(data)); |
| 3018 | } |
| 3019 | |
| 3020 | |
| 3021 | THREADED_TEST(EmbedderData) { |
| 3022 | LocalContext env; |
| 3023 | v8::Isolate* isolate = env->GetIsolate(); |
| 3024 | v8::HandleScope scope(isolate); |
| 3025 | |
| 3026 | CHECK_EQ(0, (*env)->GetNumberOfEmbedderDataFields()); |
| 3027 | CheckEmbedderData(&env, 3, v8_str("The quick brown fox jumps" )); |
| 3028 | CHECK_EQ(4, (*env)->GetNumberOfEmbedderDataFields()); |
| 3029 | CheckEmbedderData(&env, 2, v8_str("over the lazy dog." )); |
| 3030 | CHECK_EQ(4, (*env)->GetNumberOfEmbedderDataFields()); |
| 3031 | CheckEmbedderData(&env, 1, v8::Number::New(isolate, 1.2345)); |
| 3032 | CHECK_EQ(4, (*env)->GetNumberOfEmbedderDataFields()); |
| 3033 | CheckEmbedderData(&env, 0, v8::Boolean::New(isolate, true)); |
| 3034 | CHECK_EQ(4, (*env)->GetNumberOfEmbedderDataFields()); |
| 3035 | CheckEmbedderData(&env, 211, v8::Boolean::New(isolate, true)); |
| 3036 | CHECK_EQ(212, (*env)->GetNumberOfEmbedderDataFields()); |
| 3037 | } |
| 3038 | |
| 3039 | |
| 3040 | THREADED_TEST(IdentityHash) { |
| 3041 | LocalContext env; |
| 3042 | v8::Isolate* isolate = env->GetIsolate(); |
| 3043 | v8::HandleScope scope(isolate); |
| 3044 | |
| 3045 | // Ensure that the test starts with an fresh heap to test whether the hash |
| 3046 | // code is based on the address. |
| 3047 | CcTest::CollectAllGarbage(); |
| 3048 | Local<v8::Object> obj = v8::Object::New(isolate); |
| 3049 | int hash = obj->GetIdentityHash(); |
| 3050 | int hash1 = obj->GetIdentityHash(); |
| 3051 | CHECK_EQ(hash, hash1); |
| 3052 | int hash2 = v8::Object::New(isolate)->GetIdentityHash(); |
| 3053 | // Since the identity hash is essentially a random number two consecutive |
| 3054 | // objects should not be assigned the same hash code. If the test below fails |
| 3055 | // the random number generator should be evaluated. |
| 3056 | CHECK_NE(hash, hash2); |
| 3057 | CcTest::CollectAllGarbage(); |
| 3058 | int hash3 = v8::Object::New(isolate)->GetIdentityHash(); |
| 3059 | // Make sure that the identity hash is not based on the initial address of |
| 3060 | // the object alone. If the test below fails the random number generator |
| 3061 | // should be evaluated. |
| 3062 | CHECK_NE(hash, hash3); |
| 3063 | int hash4 = obj->GetIdentityHash(); |
| 3064 | CHECK_EQ(hash, hash4); |
| 3065 | |
| 3066 | // Check identity hashes behaviour in the presence of JS accessors. |
| 3067 | // Put a getter for 'v8::IdentityHash' on the Object's prototype: |
| 3068 | { |
| 3069 | CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n" ); |
| 3070 | Local<v8::Object> o1 = v8::Object::New(isolate); |
| 3071 | Local<v8::Object> o2 = v8::Object::New(isolate); |
| 3072 | CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); |
| 3073 | } |
| 3074 | { |
| 3075 | CompileRun( |
| 3076 | "function cnst() { return 42; };\n" |
| 3077 | "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n" ); |
| 3078 | Local<v8::Object> o1 = v8::Object::New(isolate); |
| 3079 | Local<v8::Object> o2 = v8::Object::New(isolate); |
| 3080 | CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); |
| 3081 | } |
| 3082 | } |
| 3083 | |
| 3084 | |
| 3085 | void GlobalProxyIdentityHash(bool set_in_js) { |
| 3086 | LocalContext env; |
| 3087 | v8::Isolate* isolate = env->GetIsolate(); |
| 3088 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 3089 | v8::HandleScope scope(isolate); |
| 3090 | Local<Object> global_proxy = env->Global(); |
| 3091 | i::Handle<i::Object> i_global_proxy = v8::Utils::OpenHandle(*global_proxy); |
| 3092 | CHECK(env->Global() |
| 3093 | ->Set(env.local(), v8_str("global" ), global_proxy) |
| 3094 | .FromJust()); |
| 3095 | int32_t hash1; |
| 3096 | if (set_in_js) { |
| 3097 | CompileRun("var m = new Set(); m.add(global);" ); |
| 3098 | i::Object original_hash = i_global_proxy->GetHash(); |
| 3099 | CHECK(original_hash->IsSmi()); |
| 3100 | hash1 = i::Smi::ToInt(original_hash); |
| 3101 | } else { |
| 3102 | hash1 = i_global_proxy->GetOrCreateHash(i_isolate)->value(); |
| 3103 | } |
| 3104 | // Hash should be retained after being detached. |
| 3105 | env->DetachGlobal(); |
| 3106 | int hash2 = global_proxy->GetIdentityHash(); |
| 3107 | CHECK_EQ(hash1, hash2); |
| 3108 | { |
| 3109 | // Re-attach global proxy to a new context, hash should stay the same. |
| 3110 | LocalContext env2(nullptr, Local<ObjectTemplate>(), global_proxy); |
| 3111 | int hash3 = global_proxy->GetIdentityHash(); |
| 3112 | CHECK_EQ(hash1, hash3); |
| 3113 | } |
| 3114 | } |
| 3115 | |
| 3116 | |
| 3117 | THREADED_TEST(GlobalProxyIdentityHash) { |
| 3118 | GlobalProxyIdentityHash(true); |
| 3119 | GlobalProxyIdentityHash(false); |
| 3120 | } |
| 3121 | |
| 3122 | |
| 3123 | TEST(SymbolIdentityHash) { |
| 3124 | LocalContext env; |
| 3125 | v8::Isolate* isolate = env->GetIsolate(); |
| 3126 | v8::HandleScope scope(isolate); |
| 3127 | |
| 3128 | { |
| 3129 | Local<v8::Symbol> symbol = v8::Symbol::New(isolate); |
| 3130 | int hash = symbol->GetIdentityHash(); |
| 3131 | int hash1 = symbol->GetIdentityHash(); |
| 3132 | CHECK_EQ(hash, hash1); |
| 3133 | CcTest::CollectAllGarbage(); |
| 3134 | int hash3 = symbol->GetIdentityHash(); |
| 3135 | CHECK_EQ(hash, hash3); |
| 3136 | } |
| 3137 | |
| 3138 | { |
| 3139 | v8::Local<v8::Symbol> js_symbol = |
| 3140 | CompileRun("Symbol('foo')" ).As<v8::Symbol>(); |
| 3141 | int hash = js_symbol->GetIdentityHash(); |
| 3142 | int hash1 = js_symbol->GetIdentityHash(); |
| 3143 | CHECK_EQ(hash, hash1); |
| 3144 | CcTest::CollectAllGarbage(); |
| 3145 | int hash3 = js_symbol->GetIdentityHash(); |
| 3146 | CHECK_EQ(hash, hash3); |
| 3147 | } |
| 3148 | } |
| 3149 | |
| 3150 | |
| 3151 | TEST(StringIdentityHash) { |
| 3152 | LocalContext env; |
| 3153 | v8::Isolate* isolate = env->GetIsolate(); |
| 3154 | v8::HandleScope scope(isolate); |
| 3155 | |
| 3156 | Local<v8::String> str = v8_str("str1" ); |
| 3157 | int hash = str->GetIdentityHash(); |
| 3158 | int hash1 = str->GetIdentityHash(); |
| 3159 | CHECK_EQ(hash, hash1); |
| 3160 | CcTest::CollectAllGarbage(); |
| 3161 | int hash3 = str->GetIdentityHash(); |
| 3162 | CHECK_EQ(hash, hash3); |
| 3163 | |
| 3164 | Local<v8::String> str2 = v8_str("str1" ); |
| 3165 | int hash4 = str2->GetIdentityHash(); |
| 3166 | CHECK_EQ(hash, hash4); |
| 3167 | } |
| 3168 | |
| 3169 | |
| 3170 | THREADED_TEST(SymbolProperties) { |
| 3171 | LocalContext env; |
| 3172 | v8::Isolate* isolate = env->GetIsolate(); |
| 3173 | v8::HandleScope scope(isolate); |
| 3174 | |
| 3175 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 3176 | v8::Local<v8::Symbol> sym1 = v8::Symbol::New(isolate); |
| 3177 | v8::Local<v8::Symbol> sym2 = v8::Symbol::New(isolate, v8_str("my-symbol" )); |
| 3178 | v8::Local<v8::Symbol> sym3 = v8::Symbol::New(isolate, v8_str("sym3" )); |
| 3179 | v8::Local<v8::Symbol> sym4 = v8::Symbol::New(isolate, v8_str("native" )); |
| 3180 | |
| 3181 | CcTest::CollectAllGarbage(); |
| 3182 | |
| 3183 | // Check basic symbol functionality. |
| 3184 | CHECK(sym1->IsSymbol()); |
| 3185 | CHECK(sym2->IsSymbol()); |
| 3186 | CHECK(!obj->IsSymbol()); |
| 3187 | |
| 3188 | CHECK(sym1->Equals(env.local(), sym1).FromJust()); |
| 3189 | CHECK(sym2->Equals(env.local(), sym2).FromJust()); |
| 3190 | CHECK(!sym1->Equals(env.local(), sym2).FromJust()); |
| 3191 | CHECK(!sym2->Equals(env.local(), sym1).FromJust()); |
| 3192 | CHECK(sym1->StrictEquals(sym1)); |
| 3193 | CHECK(sym2->StrictEquals(sym2)); |
| 3194 | CHECK(!sym1->StrictEquals(sym2)); |
| 3195 | CHECK(!sym2->StrictEquals(sym1)); |
| 3196 | |
| 3197 | CHECK(sym2->Name()->Equals(env.local(), v8_str("my-symbol" )).FromJust()); |
| 3198 | |
| 3199 | v8::Local<v8::Value> sym_val = sym2; |
| 3200 | CHECK(sym_val->IsSymbol()); |
| 3201 | CHECK(sym_val->Equals(env.local(), sym2).FromJust()); |
| 3202 | CHECK(sym_val->StrictEquals(sym2)); |
| 3203 | CHECK(v8::Symbol::Cast(*sym_val)->Equals(env.local(), sym2).FromJust()); |
| 3204 | |
| 3205 | v8::Local<v8::Value> sym_obj = v8::SymbolObject::New(isolate, sym2); |
| 3206 | CHECK(sym_obj->IsSymbolObject()); |
| 3207 | CHECK(!sym2->IsSymbolObject()); |
| 3208 | CHECK(!obj->IsSymbolObject()); |
| 3209 | CHECK(sym_obj->Equals(env.local(), sym2).FromJust()); |
| 3210 | CHECK(!sym_obj->StrictEquals(sym2)); |
| 3211 | CHECK(v8::SymbolObject::Cast(*sym_obj) |
| 3212 | ->Equals(env.local(), sym_obj) |
| 3213 | .FromJust()); |
| 3214 | CHECK(v8::SymbolObject::Cast(*sym_obj) |
| 3215 | ->ValueOf() |
| 3216 | ->Equals(env.local(), sym2) |
| 3217 | .FromJust()); |
| 3218 | |
| 3219 | // Make sure delete of a non-existent symbol property works. |
| 3220 | CHECK(obj->Delete(env.local(), sym1).FromJust()); |
| 3221 | CHECK(!obj->Has(env.local(), sym1).FromJust()); |
| 3222 | |
| 3223 | CHECK( |
| 3224 | obj->Set(env.local(), sym1, v8::Integer::New(isolate, 1503)).FromJust()); |
| 3225 | CHECK(obj->Has(env.local(), sym1).FromJust()); |
| 3226 | CHECK_EQ(1503, obj->Get(env.local(), sym1) |
| 3227 | .ToLocalChecked() |
| 3228 | ->Int32Value(env.local()) |
| 3229 | .FromJust()); |
| 3230 | CHECK( |
| 3231 | obj->Set(env.local(), sym1, v8::Integer::New(isolate, 2002)).FromJust()); |
| 3232 | CHECK(obj->Has(env.local(), sym1).FromJust()); |
| 3233 | CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| 3234 | .ToLocalChecked() |
| 3235 | ->Int32Value(env.local()) |
| 3236 | .FromJust()); |
| 3237 | CHECK_EQ(v8::None, obj->GetPropertyAttributes(env.local(), sym1).FromJust()); |
| 3238 | |
| 3239 | CHECK_EQ(0u, |
| 3240 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3241 | unsigned num_props = |
| 3242 | obj->GetPropertyNames(env.local()).ToLocalChecked()->Length(); |
| 3243 | CHECK(obj->Set(env.local(), v8_str("bla" ), v8::Integer::New(isolate, 20)) |
| 3244 | .FromJust()); |
| 3245 | CHECK_EQ(1u, |
| 3246 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3247 | CHECK_EQ(num_props + 1, |
| 3248 | obj->GetPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3249 | |
| 3250 | CcTest::CollectAllGarbage(); |
| 3251 | |
| 3252 | CHECK(obj->SetAccessor(env.local(), sym3, SymbolAccessorGetter, |
| 3253 | SymbolAccessorSetter) |
| 3254 | .FromJust()); |
| 3255 | CHECK(obj->Get(env.local(), sym3).ToLocalChecked()->IsUndefined()); |
| 3256 | CHECK(obj->Set(env.local(), sym3, v8::Integer::New(isolate, 42)).FromJust()); |
| 3257 | CHECK(obj->Get(env.local(), sym3) |
| 3258 | .ToLocalChecked() |
| 3259 | ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| 3260 | .FromJust()); |
| 3261 | CHECK(obj->Get(env.local(), v8_str("accessor_sym3" )) |
| 3262 | .ToLocalChecked() |
| 3263 | ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| 3264 | .FromJust()); |
| 3265 | |
| 3266 | CHECK(obj->SetNativeDataProperty(env.local(), sym4, SymbolAccessorGetter) |
| 3267 | .FromJust()); |
| 3268 | CHECK(obj->Get(env.local(), sym4).ToLocalChecked()->IsUndefined()); |
| 3269 | CHECK(obj->Set(env.local(), v8_str("accessor_native" ), |
| 3270 | v8::Integer::New(isolate, 123)) |
| 3271 | .FromJust()); |
| 3272 | CHECK_EQ(123, obj->Get(env.local(), sym4) |
| 3273 | .ToLocalChecked() |
| 3274 | ->Int32Value(env.local()) |
| 3275 | .FromJust()); |
| 3276 | CHECK(obj->Set(env.local(), sym4, v8::Integer::New(isolate, 314)).FromJust()); |
| 3277 | CHECK(obj->Get(env.local(), sym4) |
| 3278 | .ToLocalChecked() |
| 3279 | ->Equals(env.local(), v8::Integer::New(isolate, 314)) |
| 3280 | .FromJust()); |
| 3281 | CHECK(obj->Delete(env.local(), v8_str("accessor_native" )).FromJust()); |
| 3282 | |
| 3283 | // Add another property and delete it afterwards to force the object in |
| 3284 | // slow case. |
| 3285 | CHECK( |
| 3286 | obj->Set(env.local(), sym2, v8::Integer::New(isolate, 2008)).FromJust()); |
| 3287 | CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| 3288 | .ToLocalChecked() |
| 3289 | ->Int32Value(env.local()) |
| 3290 | .FromJust()); |
| 3291 | CHECK_EQ(2008, obj->Get(env.local(), sym2) |
| 3292 | .ToLocalChecked() |
| 3293 | ->Int32Value(env.local()) |
| 3294 | .FromJust()); |
| 3295 | CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| 3296 | .ToLocalChecked() |
| 3297 | ->Int32Value(env.local()) |
| 3298 | .FromJust()); |
| 3299 | CHECK_EQ(2u, |
| 3300 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3301 | |
| 3302 | CHECK(obj->Has(env.local(), sym1).FromJust()); |
| 3303 | CHECK(obj->Has(env.local(), sym2).FromJust()); |
| 3304 | CHECK(obj->Has(env.local(), sym3).FromJust()); |
| 3305 | CHECK(obj->Has(env.local(), v8_str("accessor_sym3" )).FromJust()); |
| 3306 | CHECK(obj->Delete(env.local(), sym2).FromJust()); |
| 3307 | CHECK(obj->Has(env.local(), sym1).FromJust()); |
| 3308 | CHECK(!obj->Has(env.local(), sym2).FromJust()); |
| 3309 | CHECK(obj->Has(env.local(), sym3).FromJust()); |
| 3310 | CHECK(obj->Has(env.local(), v8_str("accessor_sym3" )).FromJust()); |
| 3311 | CHECK_EQ(2002, obj->Get(env.local(), sym1) |
| 3312 | .ToLocalChecked() |
| 3313 | ->Int32Value(env.local()) |
| 3314 | .FromJust()); |
| 3315 | CHECK(obj->Get(env.local(), sym3) |
| 3316 | .ToLocalChecked() |
| 3317 | ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| 3318 | .FromJust()); |
| 3319 | CHECK(obj->Get(env.local(), v8_str("accessor_sym3" )) |
| 3320 | .ToLocalChecked() |
| 3321 | ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| 3322 | .FromJust()); |
| 3323 | CHECK_EQ(2u, |
| 3324 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3325 | |
| 3326 | // Symbol properties are inherited. |
| 3327 | v8::Local<v8::Object> child = v8::Object::New(isolate); |
| 3328 | CHECK(child->SetPrototype(env.local(), obj).FromJust()); |
| 3329 | CHECK(child->Has(env.local(), sym1).FromJust()); |
| 3330 | CHECK_EQ(2002, child->Get(env.local(), sym1) |
| 3331 | .ToLocalChecked() |
| 3332 | ->Int32Value(env.local()) |
| 3333 | .FromJust()); |
| 3334 | CHECK(obj->Get(env.local(), sym3) |
| 3335 | .ToLocalChecked() |
| 3336 | ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| 3337 | .FromJust()); |
| 3338 | CHECK(obj->Get(env.local(), v8_str("accessor_sym3" )) |
| 3339 | .ToLocalChecked() |
| 3340 | ->Equals(env.local(), v8::Integer::New(isolate, 42)) |
| 3341 | .FromJust()); |
| 3342 | CHECK_EQ(0u, |
| 3343 | child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3344 | } |
| 3345 | |
| 3346 | |
| 3347 | THREADED_TEST(SymbolTemplateProperties) { |
| 3348 | LocalContext env; |
| 3349 | v8::Isolate* isolate = env->GetIsolate(); |
| 3350 | v8::HandleScope scope(isolate); |
| 3351 | v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate); |
| 3352 | v8::Local<v8::Name> name = v8::Symbol::New(isolate); |
| 3353 | CHECK(!name.IsEmpty()); |
| 3354 | foo->PrototypeTemplate()->Set(name, v8::FunctionTemplate::New(isolate)); |
| 3355 | v8::Local<v8::Object> new_instance = |
| 3356 | foo->InstanceTemplate()->NewInstance(env.local()).ToLocalChecked(); |
| 3357 | CHECK(!new_instance.IsEmpty()); |
| 3358 | CHECK(new_instance->Has(env.local(), name).FromJust()); |
| 3359 | } |
| 3360 | |
| 3361 | |
| 3362 | THREADED_TEST(PrivatePropertiesOnProxies) { |
| 3363 | LocalContext env; |
| 3364 | v8::Isolate* isolate = env->GetIsolate(); |
| 3365 | v8::HandleScope scope(isolate); |
| 3366 | |
| 3367 | v8::Local<v8::Object> target = CompileRun("({})" ).As<v8::Object>(); |
| 3368 | v8::Local<v8::Object> handler = CompileRun("({})" ).As<v8::Object>(); |
| 3369 | |
| 3370 | v8::Local<v8::Proxy> proxy = |
| 3371 | v8::Proxy::New(env.local(), target, handler).ToLocalChecked(); |
| 3372 | |
| 3373 | v8::Local<v8::Private> priv1 = v8::Private::New(isolate); |
| 3374 | v8::Local<v8::Private> priv2 = |
| 3375 | v8::Private::New(isolate, v8_str("my-private" )); |
| 3376 | |
| 3377 | CcTest::CollectAllGarbage(); |
| 3378 | |
| 3379 | CHECK(priv2->Name() |
| 3380 | ->Equals(env.local(), |
| 3381 | v8::String::NewFromUtf8(isolate, "my-private" , |
| 3382 | v8::NewStringType::kNormal) |
| 3383 | .ToLocalChecked()) |
| 3384 | .FromJust()); |
| 3385 | |
| 3386 | // Make sure delete of a non-existent private symbol property works. |
| 3387 | proxy->DeletePrivate(env.local(), priv1).FromJust(); |
| 3388 | CHECK(!proxy->HasPrivate(env.local(), priv1).FromJust()); |
| 3389 | |
| 3390 | CHECK(proxy->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 1503)) |
| 3391 | .FromJust()); |
| 3392 | CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| 3393 | CHECK_EQ(1503, proxy->GetPrivate(env.local(), priv1) |
| 3394 | .ToLocalChecked() |
| 3395 | ->Int32Value(env.local()) |
| 3396 | .FromJust()); |
| 3397 | CHECK(proxy->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 2002)) |
| 3398 | .FromJust()); |
| 3399 | CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| 3400 | CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| 3401 | .ToLocalChecked() |
| 3402 | ->Int32Value(env.local()) |
| 3403 | .FromJust()); |
| 3404 | |
| 3405 | CHECK_EQ(0u, |
| 3406 | proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3407 | unsigned num_props = |
| 3408 | proxy->GetPropertyNames(env.local()).ToLocalChecked()->Length(); |
| 3409 | CHECK(proxy->Set(env.local(), v8::String::NewFromUtf8( |
| 3410 | isolate, "bla" , v8::NewStringType::kNormal) |
| 3411 | .ToLocalChecked(), |
| 3412 | v8::Integer::New(isolate, 20)) |
| 3413 | .FromJust()); |
| 3414 | CHECK_EQ(1u, |
| 3415 | proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3416 | CHECK_EQ(num_props + 1, |
| 3417 | proxy->GetPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3418 | |
| 3419 | CcTest::CollectAllGarbage(); |
| 3420 | |
| 3421 | // Add another property and delete it afterwards to force the object in |
| 3422 | // slow case. |
| 3423 | CHECK(proxy->SetPrivate(env.local(), priv2, v8::Integer::New(isolate, 2008)) |
| 3424 | .FromJust()); |
| 3425 | CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| 3426 | .ToLocalChecked() |
| 3427 | ->Int32Value(env.local()) |
| 3428 | .FromJust()); |
| 3429 | CHECK_EQ(2008, proxy->GetPrivate(env.local(), priv2) |
| 3430 | .ToLocalChecked() |
| 3431 | ->Int32Value(env.local()) |
| 3432 | .FromJust()); |
| 3433 | CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| 3434 | .ToLocalChecked() |
| 3435 | ->Int32Value(env.local()) |
| 3436 | .FromJust()); |
| 3437 | CHECK_EQ(1u, |
| 3438 | proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3439 | |
| 3440 | CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| 3441 | CHECK(proxy->HasPrivate(env.local(), priv2).FromJust()); |
| 3442 | CHECK(proxy->DeletePrivate(env.local(), priv2).FromJust()); |
| 3443 | CHECK(proxy->HasPrivate(env.local(), priv1).FromJust()); |
| 3444 | CHECK(!proxy->HasPrivate(env.local(), priv2).FromJust()); |
| 3445 | CHECK_EQ(2002, proxy->GetPrivate(env.local(), priv1) |
| 3446 | .ToLocalChecked() |
| 3447 | ->Int32Value(env.local()) |
| 3448 | .FromJust()); |
| 3449 | CHECK_EQ(1u, |
| 3450 | proxy->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3451 | |
| 3452 | // Private properties are not inherited (for the time being). |
| 3453 | v8::Local<v8::Object> child = v8::Object::New(isolate); |
| 3454 | CHECK(child->SetPrototype(env.local(), proxy).FromJust()); |
| 3455 | CHECK(!child->HasPrivate(env.local(), priv1).FromJust()); |
| 3456 | CHECK_EQ(0u, |
| 3457 | child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3458 | } |
| 3459 | |
| 3460 | |
| 3461 | THREADED_TEST(PrivateProperties) { |
| 3462 | LocalContext env; |
| 3463 | v8::Isolate* isolate = env->GetIsolate(); |
| 3464 | v8::HandleScope scope(isolate); |
| 3465 | |
| 3466 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 3467 | v8::Local<v8::Private> priv1 = v8::Private::New(isolate); |
| 3468 | v8::Local<v8::Private> priv2 = |
| 3469 | v8::Private::New(isolate, v8_str("my-private" )); |
| 3470 | |
| 3471 | CcTest::CollectAllGarbage(); |
| 3472 | |
| 3473 | CHECK(priv2->Name() |
| 3474 | ->Equals(env.local(), |
| 3475 | v8::String::NewFromUtf8(isolate, "my-private" , |
| 3476 | v8::NewStringType::kNormal) |
| 3477 | .ToLocalChecked()) |
| 3478 | .FromJust()); |
| 3479 | |
| 3480 | // Make sure delete of a non-existent private symbol property works. |
| 3481 | obj->DeletePrivate(env.local(), priv1).FromJust(); |
| 3482 | CHECK(!obj->HasPrivate(env.local(), priv1).FromJust()); |
| 3483 | |
| 3484 | CHECK(obj->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 1503)) |
| 3485 | .FromJust()); |
| 3486 | CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| 3487 | CHECK_EQ(1503, obj->GetPrivate(env.local(), priv1) |
| 3488 | .ToLocalChecked() |
| 3489 | ->Int32Value(env.local()) |
| 3490 | .FromJust()); |
| 3491 | CHECK(obj->SetPrivate(env.local(), priv1, v8::Integer::New(isolate, 2002)) |
| 3492 | .FromJust()); |
| 3493 | CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| 3494 | CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| 3495 | .ToLocalChecked() |
| 3496 | ->Int32Value(env.local()) |
| 3497 | .FromJust()); |
| 3498 | |
| 3499 | CHECK_EQ(0u, |
| 3500 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3501 | unsigned num_props = |
| 3502 | obj->GetPropertyNames(env.local()).ToLocalChecked()->Length(); |
| 3503 | CHECK(obj->Set(env.local(), v8::String::NewFromUtf8( |
| 3504 | isolate, "bla" , v8::NewStringType::kNormal) |
| 3505 | .ToLocalChecked(), |
| 3506 | v8::Integer::New(isolate, 20)) |
| 3507 | .FromJust()); |
| 3508 | CHECK_EQ(1u, |
| 3509 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3510 | CHECK_EQ(num_props + 1, |
| 3511 | obj->GetPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3512 | |
| 3513 | CcTest::CollectAllGarbage(); |
| 3514 | |
| 3515 | // Add another property and delete it afterwards to force the object in |
| 3516 | // slow case. |
| 3517 | CHECK(obj->SetPrivate(env.local(), priv2, v8::Integer::New(isolate, 2008)) |
| 3518 | .FromJust()); |
| 3519 | CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| 3520 | .ToLocalChecked() |
| 3521 | ->Int32Value(env.local()) |
| 3522 | .FromJust()); |
| 3523 | CHECK_EQ(2008, obj->GetPrivate(env.local(), priv2) |
| 3524 | .ToLocalChecked() |
| 3525 | ->Int32Value(env.local()) |
| 3526 | .FromJust()); |
| 3527 | CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| 3528 | .ToLocalChecked() |
| 3529 | ->Int32Value(env.local()) |
| 3530 | .FromJust()); |
| 3531 | CHECK_EQ(1u, |
| 3532 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3533 | |
| 3534 | CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| 3535 | CHECK(obj->HasPrivate(env.local(), priv2).FromJust()); |
| 3536 | CHECK(obj->DeletePrivate(env.local(), priv2).FromJust()); |
| 3537 | CHECK(obj->HasPrivate(env.local(), priv1).FromJust()); |
| 3538 | CHECK(!obj->HasPrivate(env.local(), priv2).FromJust()); |
| 3539 | CHECK_EQ(2002, obj->GetPrivate(env.local(), priv1) |
| 3540 | .ToLocalChecked() |
| 3541 | ->Int32Value(env.local()) |
| 3542 | .FromJust()); |
| 3543 | CHECK_EQ(1u, |
| 3544 | obj->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3545 | |
| 3546 | // Private properties are not inherited (for the time being). |
| 3547 | v8::Local<v8::Object> child = v8::Object::New(isolate); |
| 3548 | CHECK(child->SetPrototype(env.local(), obj).FromJust()); |
| 3549 | CHECK(!child->HasPrivate(env.local(), priv1).FromJust()); |
| 3550 | CHECK_EQ(0u, |
| 3551 | child->GetOwnPropertyNames(env.local()).ToLocalChecked()->Length()); |
| 3552 | } |
| 3553 | |
| 3554 | |
| 3555 | THREADED_TEST(GlobalSymbols) { |
| 3556 | LocalContext env; |
| 3557 | v8::Isolate* isolate = env->GetIsolate(); |
| 3558 | v8::HandleScope scope(isolate); |
| 3559 | |
| 3560 | v8::Local<String> name = v8_str("my-symbol" ); |
| 3561 | v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name); |
| 3562 | v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name); |
| 3563 | CHECK(glob2->SameValue(glob)); |
| 3564 | |
| 3565 | v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name); |
| 3566 | v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name); |
| 3567 | CHECK(glob_api2->SameValue(glob_api)); |
| 3568 | CHECK(!glob_api->SameValue(glob)); |
| 3569 | |
| 3570 | v8::Local<v8::Symbol> sym = v8::Symbol::New(isolate, name); |
| 3571 | CHECK(!sym->SameValue(glob)); |
| 3572 | |
| 3573 | CompileRun("var sym2 = Symbol.for('my-symbol')" ); |
| 3574 | v8::Local<Value> sym2 = |
| 3575 | env->Global()->Get(env.local(), v8_str("sym2" )).ToLocalChecked(); |
| 3576 | CHECK(sym2->SameValue(glob)); |
| 3577 | CHECK(!sym2->SameValue(glob_api)); |
| 3578 | } |
| 3579 | |
| 3580 | THREADED_TEST(GlobalSymbolsNoContext) { |
| 3581 | v8::Isolate* isolate = CcTest::isolate(); |
| 3582 | v8::HandleScope scope(isolate); |
| 3583 | |
| 3584 | v8::Local<String> name = v8_str("my-symbol" ); |
| 3585 | v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name); |
| 3586 | v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name); |
| 3587 | CHECK(glob2->SameValue(glob)); |
| 3588 | |
| 3589 | v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name); |
| 3590 | v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name); |
| 3591 | CHECK(glob_api2->SameValue(glob_api)); |
| 3592 | CHECK(!glob_api->SameValue(glob)); |
| 3593 | } |
| 3594 | |
| 3595 | static void CheckWellKnownSymbol(v8::Local<v8::Symbol>(*getter)(v8::Isolate*), |
| 3596 | const char* name) { |
| 3597 | LocalContext env; |
| 3598 | v8::Isolate* isolate = env->GetIsolate(); |
| 3599 | v8::HandleScope scope(isolate); |
| 3600 | |
| 3601 | v8::Local<v8::Symbol> symbol = getter(isolate); |
| 3602 | std::string script = std::string("var sym = " ) + name; |
| 3603 | CompileRun(script.c_str()); |
| 3604 | v8::Local<Value> value = |
| 3605 | env->Global()->Get(env.local(), v8_str("sym" )).ToLocalChecked(); |
| 3606 | |
| 3607 | CHECK(!value.IsEmpty()); |
| 3608 | CHECK(!symbol.IsEmpty()); |
| 3609 | CHECK(value->SameValue(symbol)); |
| 3610 | } |
| 3611 | |
| 3612 | |
| 3613 | THREADED_TEST(WellKnownSymbols) { |
| 3614 | CheckWellKnownSymbol(v8::Symbol::GetIterator, "Symbol.iterator" ); |
| 3615 | CheckWellKnownSymbol(v8::Symbol::GetUnscopables, "Symbol.unscopables" ); |
| 3616 | CheckWellKnownSymbol(v8::Symbol::GetHasInstance, "Symbol.hasInstance" ); |
| 3617 | CheckWellKnownSymbol(v8::Symbol::GetIsConcatSpreadable, |
| 3618 | "Symbol.isConcatSpreadable" ); |
| 3619 | CheckWellKnownSymbol(v8::Symbol::GetMatch, "Symbol.match" ); |
| 3620 | CheckWellKnownSymbol(v8::Symbol::GetReplace, "Symbol.replace" ); |
| 3621 | CheckWellKnownSymbol(v8::Symbol::GetSearch, "Symbol.search" ); |
| 3622 | CheckWellKnownSymbol(v8::Symbol::GetSplit, "Symbol.split" ); |
| 3623 | CheckWellKnownSymbol(v8::Symbol::GetToPrimitive, "Symbol.toPrimitive" ); |
| 3624 | CheckWellKnownSymbol(v8::Symbol::GetToStringTag, "Symbol.toStringTag" ); |
| 3625 | } |
| 3626 | |
| 3627 | |
| 3628 | THREADED_TEST(GlobalPrivates) { |
| 3629 | i::FLAG_allow_natives_syntax = true; |
| 3630 | LocalContext env; |
| 3631 | v8::Isolate* isolate = env->GetIsolate(); |
| 3632 | v8::HandleScope scope(isolate); |
| 3633 | |
| 3634 | v8::Local<String> name = v8_str("my-private" ); |
| 3635 | v8::Local<v8::Private> glob = v8::Private::ForApi(isolate, name); |
| 3636 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 3637 | CHECK(obj->SetPrivate(env.local(), glob, v8::Integer::New(isolate, 3)) |
| 3638 | .FromJust()); |
| 3639 | |
| 3640 | v8::Local<v8::Private> glob2 = v8::Private::ForApi(isolate, name); |
| 3641 | CHECK(obj->HasPrivate(env.local(), glob2).FromJust()); |
| 3642 | |
| 3643 | v8::Local<v8::Private> priv = v8::Private::New(isolate, name); |
| 3644 | CHECK(!obj->HasPrivate(env.local(), priv).FromJust()); |
| 3645 | |
| 3646 | CompileRun("var intern = %CreatePrivateSymbol('my-private')" ); |
| 3647 | v8::Local<Value> intern = |
| 3648 | env->Global()->Get(env.local(), v8_str("intern" )).ToLocalChecked(); |
| 3649 | CHECK(!obj->Has(env.local(), intern).FromJust()); |
| 3650 | } |
| 3651 | |
| 3652 | |
| 3653 | class ScopedArrayBufferContents { |
| 3654 | public: |
| 3655 | explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents) |
| 3656 | : contents_(contents) {} |
| 3657 | ~ScopedArrayBufferContents() { free(contents_.AllocationBase()); } |
| 3658 | void* Data() const { return contents_.Data(); } |
| 3659 | size_t ByteLength() const { return contents_.ByteLength(); } |
| 3660 | |
| 3661 | void* AllocationBase() const { return contents_.AllocationBase(); } |
| 3662 | size_t AllocationLength() const { return contents_.AllocationLength(); } |
| 3663 | v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const { |
| 3664 | return contents_.AllocationMode(); |
| 3665 | } |
| 3666 | |
| 3667 | private: |
| 3668 | const v8::ArrayBuffer::Contents contents_; |
| 3669 | }; |
| 3670 | |
| 3671 | template <typename T> |
| 3672 | static void CheckInternalFieldsAreZero(v8::Local<T> value) { |
| 3673 | CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount()); |
| 3674 | for (int i = 0; i < value->InternalFieldCount(); i++) { |
| 3675 | CHECK_EQ(0, value->GetInternalField(i) |
| 3676 | ->Int32Value(CcTest::isolate()->GetCurrentContext()) |
| 3677 | .FromJust()); |
| 3678 | } |
| 3679 | } |
| 3680 | |
| 3681 | |
| 3682 | THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { |
| 3683 | LocalContext env; |
| 3684 | v8::Isolate* isolate = env->GetIsolate(); |
| 3685 | v8::HandleScope handle_scope(isolate); |
| 3686 | |
| 3687 | Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024); |
| 3688 | CheckInternalFieldsAreZero(ab); |
| 3689 | CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); |
| 3690 | CHECK(!ab->IsExternal()); |
| 3691 | CcTest::CollectAllGarbage(); |
| 3692 | |
| 3693 | ScopedArrayBufferContents ab_contents(ab->Externalize()); |
| 3694 | CHECK(ab->IsExternal()); |
| 3695 | |
| 3696 | CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); |
| 3697 | uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); |
| 3698 | CHECK_NOT_NULL(data); |
| 3699 | CHECK(env->Global()->Set(env.local(), v8_str("ab" ), ab).FromJust()); |
| 3700 | |
| 3701 | v8::Local<v8::Value> result = CompileRun("ab.byteLength" ); |
| 3702 | CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| 3703 | |
| 3704 | result = CompileRun( |
| 3705 | "var u8 = new Uint8Array(ab);" |
| 3706 | "u8[0] = 0xFF;" |
| 3707 | "u8[1] = 0xAA;" |
| 3708 | "u8.length" ); |
| 3709 | CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| 3710 | CHECK_EQ(0xFF, data[0]); |
| 3711 | CHECK_EQ(0xAA, data[1]); |
| 3712 | data[0] = 0xCC; |
| 3713 | data[1] = 0x11; |
| 3714 | result = CompileRun("u8[0] + u8[1]" ); |
| 3715 | CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| 3716 | } |
| 3717 | |
| 3718 | |
| 3719 | THREADED_TEST(ArrayBuffer_JSInternalToExternal) { |
| 3720 | LocalContext env; |
| 3721 | v8::Isolate* isolate = env->GetIsolate(); |
| 3722 | v8::HandleScope handle_scope(isolate); |
| 3723 | |
| 3724 | |
| 3725 | v8::Local<v8::Value> result = CompileRun( |
| 3726 | "var ab1 = new ArrayBuffer(2);" |
| 3727 | "var u8_a = new Uint8Array(ab1);" |
| 3728 | "u8_a[0] = 0xAA;" |
| 3729 | "u8_a[1] = 0xFF; u8_a.buffer" ); |
| 3730 | Local<v8::ArrayBuffer> ab1 = Local<v8::ArrayBuffer>::Cast(result); |
| 3731 | CheckInternalFieldsAreZero(ab1); |
| 3732 | CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); |
| 3733 | CHECK(!ab1->IsExternal()); |
| 3734 | ScopedArrayBufferContents ab1_contents(ab1->Externalize()); |
| 3735 | CHECK(ab1->IsExternal()); |
| 3736 | |
| 3737 | result = CompileRun("ab1.byteLength" ); |
| 3738 | CHECK_EQ(2, result->Int32Value(env.local()).FromJust()); |
| 3739 | result = CompileRun("u8_a[0]" ); |
| 3740 | CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust()); |
| 3741 | result = CompileRun("u8_a[1]" ); |
| 3742 | CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| 3743 | result = CompileRun( |
| 3744 | "var u8_b = new Uint8Array(ab1);" |
| 3745 | "u8_b[0] = 0xBB;" |
| 3746 | "u8_a[0]" ); |
| 3747 | CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust()); |
| 3748 | result = CompileRun("u8_b[1]" ); |
| 3749 | CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| 3750 | |
| 3751 | CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); |
| 3752 | uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); |
| 3753 | CHECK_EQ(0xBB, ab1_data[0]); |
| 3754 | CHECK_EQ(0xFF, ab1_data[1]); |
| 3755 | ab1_data[0] = 0xCC; |
| 3756 | ab1_data[1] = 0x11; |
| 3757 | result = CompileRun("u8_a[0] + u8_a[1]" ); |
| 3758 | CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| 3759 | } |
| 3760 | |
| 3761 | |
| 3762 | THREADED_TEST(ArrayBuffer_External) { |
| 3763 | LocalContext env; |
| 3764 | v8::Isolate* isolate = env->GetIsolate(); |
| 3765 | v8::HandleScope handle_scope(isolate); |
| 3766 | |
| 3767 | i::ScopedVector<uint8_t> my_data(100); |
| 3768 | memset(my_data.start(), 0, 100); |
| 3769 | Local<v8::ArrayBuffer> ab3 = |
| 3770 | v8::ArrayBuffer::New(isolate, my_data.start(), 100); |
| 3771 | CheckInternalFieldsAreZero(ab3); |
| 3772 | CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); |
| 3773 | CHECK(ab3->IsExternal()); |
| 3774 | |
| 3775 | CHECK(env->Global()->Set(env.local(), v8_str("ab3" ), ab3).FromJust()); |
| 3776 | |
| 3777 | v8::Local<v8::Value> result = CompileRun("ab3.byteLength" ); |
| 3778 | CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| 3779 | |
| 3780 | result = CompileRun( |
| 3781 | "var u8_b = new Uint8Array(ab3);" |
| 3782 | "u8_b[0] = 0xBB;" |
| 3783 | "u8_b[1] = 0xCC;" |
| 3784 | "u8_b.length" ); |
| 3785 | CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| 3786 | CHECK_EQ(0xBB, my_data[0]); |
| 3787 | CHECK_EQ(0xCC, my_data[1]); |
| 3788 | my_data[0] = 0xCC; |
| 3789 | my_data[1] = 0x11; |
| 3790 | result = CompileRun("u8_b[0] + u8_b[1]" ); |
| 3791 | CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| 3792 | } |
| 3793 | |
| 3794 | THREADED_TEST(ArrayBuffer_DisableDetach) { |
| 3795 | LocalContext env; |
| 3796 | v8::Isolate* isolate = env->GetIsolate(); |
| 3797 | v8::HandleScope handle_scope(isolate); |
| 3798 | |
| 3799 | i::ScopedVector<uint8_t> my_data(100); |
| 3800 | memset(my_data.start(), 0, 100); |
| 3801 | Local<v8::ArrayBuffer> ab = |
| 3802 | v8::ArrayBuffer::New(isolate, my_data.start(), 100); |
| 3803 | CHECK(ab->IsDetachable()); |
| 3804 | |
| 3805 | i::Handle<i::JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); |
| 3806 | buf->set_is_detachable(false); |
| 3807 | |
| 3808 | CHECK(!ab->IsDetachable()); |
| 3809 | } |
| 3810 | |
| 3811 | static void CheckDataViewIsDetached(v8::Local<v8::DataView> dv) { |
| 3812 | CHECK_EQ(0, static_cast<int>(dv->ByteLength())); |
| 3813 | CHECK_EQ(0, static_cast<int>(dv->ByteOffset())); |
| 3814 | } |
| 3815 | |
| 3816 | static void CheckIsDetached(v8::Local<v8::TypedArray> ta) { |
| 3817 | CHECK_EQ(0, static_cast<int>(ta->ByteLength())); |
| 3818 | CHECK_EQ(0, static_cast<int>(ta->Length())); |
| 3819 | CHECK_EQ(0, static_cast<int>(ta->ByteOffset())); |
| 3820 | } |
| 3821 | |
| 3822 | static void CheckIsTypedArrayVarDetached(const char* name) { |
| 3823 | i::ScopedVector<char> source(1024); |
| 3824 | i::SNPrintF(source, |
| 3825 | "%s.byteLength == 0 && %s.byteOffset == 0 && %s.length == 0" , |
| 3826 | name, name, name); |
| 3827 | CHECK(CompileRun(source.start())->IsTrue()); |
| 3828 | v8::Local<v8::TypedArray> ta = |
| 3829 | v8::Local<v8::TypedArray>::Cast(CompileRun(name)); |
| 3830 | CheckIsDetached(ta); |
| 3831 | } |
| 3832 | |
| 3833 | template <typename TypedArray, int kElementSize> |
| 3834 | static Local<TypedArray> CreateAndCheck(Local<v8::ArrayBuffer> ab, |
| 3835 | int byteOffset, int length) { |
| 3836 | v8::Local<TypedArray> ta = TypedArray::New(ab, byteOffset, length); |
| 3837 | CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta); |
| 3838 | CHECK_EQ(byteOffset, static_cast<int>(ta->ByteOffset())); |
| 3839 | CHECK_EQ(length, static_cast<int>(ta->Length())); |
| 3840 | CHECK_EQ(length * kElementSize, static_cast<int>(ta->ByteLength())); |
| 3841 | return ta; |
| 3842 | } |
| 3843 | |
| 3844 | THREADED_TEST(ArrayBuffer_DetachingApi) { |
| 3845 | LocalContext env; |
| 3846 | v8::Isolate* isolate = env->GetIsolate(); |
| 3847 | v8::HandleScope handle_scope(isolate); |
| 3848 | |
| 3849 | v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024); |
| 3850 | |
| 3851 | v8::Local<v8::Uint8Array> u8a = |
| 3852 | CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023); |
| 3853 | v8::Local<v8::Uint8ClampedArray> u8c = |
| 3854 | CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023); |
| 3855 | v8::Local<v8::Int8Array> i8a = |
| 3856 | CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023); |
| 3857 | |
| 3858 | v8::Local<v8::Uint16Array> u16a = |
| 3859 | CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511); |
| 3860 | v8::Local<v8::Int16Array> i16a = |
| 3861 | CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511); |
| 3862 | |
| 3863 | v8::Local<v8::Uint32Array> u32a = |
| 3864 | CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255); |
| 3865 | v8::Local<v8::Int32Array> i32a = |
| 3866 | CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255); |
| 3867 | |
| 3868 | v8::Local<v8::Float32Array> f32a = |
| 3869 | CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255); |
| 3870 | v8::Local<v8::Float64Array> f64a = |
| 3871 | CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127); |
| 3872 | |
| 3873 | v8::Local<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023); |
| 3874 | CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| 3875 | CHECK_EQ(1, static_cast<int>(dv->ByteOffset())); |
| 3876 | CHECK_EQ(1023, static_cast<int>(dv->ByteLength())); |
| 3877 | |
| 3878 | ScopedArrayBufferContents contents(buffer->Externalize()); |
| 3879 | buffer->Detach(); |
| 3880 | CHECK_EQ(0, static_cast<int>(buffer->ByteLength())); |
| 3881 | CheckIsDetached(u8a); |
| 3882 | CheckIsDetached(u8c); |
| 3883 | CheckIsDetached(i8a); |
| 3884 | CheckIsDetached(u16a); |
| 3885 | CheckIsDetached(i16a); |
| 3886 | CheckIsDetached(u32a); |
| 3887 | CheckIsDetached(i32a); |
| 3888 | CheckIsDetached(f32a); |
| 3889 | CheckIsDetached(f64a); |
| 3890 | CheckDataViewIsDetached(dv); |
| 3891 | } |
| 3892 | |
| 3893 | THREADED_TEST(ArrayBuffer_DetachingScript) { |
| 3894 | LocalContext env; |
| 3895 | v8::Isolate* isolate = env->GetIsolate(); |
| 3896 | v8::HandleScope handle_scope(isolate); |
| 3897 | |
| 3898 | CompileRun( |
| 3899 | "var ab = new ArrayBuffer(1024);" |
| 3900 | "var u8a = new Uint8Array(ab, 1, 1023);" |
| 3901 | "var u8c = new Uint8ClampedArray(ab, 1, 1023);" |
| 3902 | "var i8a = new Int8Array(ab, 1, 1023);" |
| 3903 | "var u16a = new Uint16Array(ab, 2, 511);" |
| 3904 | "var i16a = new Int16Array(ab, 2, 511);" |
| 3905 | "var u32a = new Uint32Array(ab, 4, 255);" |
| 3906 | "var i32a = new Int32Array(ab, 4, 255);" |
| 3907 | "var f32a = new Float32Array(ab, 4, 255);" |
| 3908 | "var f64a = new Float64Array(ab, 8, 127);" |
| 3909 | "var dv = new DataView(ab, 1, 1023);" ); |
| 3910 | |
| 3911 | v8::Local<v8::ArrayBuffer> ab = |
| 3912 | Local<v8::ArrayBuffer>::Cast(CompileRun("ab" )); |
| 3913 | |
| 3914 | v8::Local<v8::DataView> dv = v8::Local<v8::DataView>::Cast(CompileRun("dv" )); |
| 3915 | |
| 3916 | ScopedArrayBufferContents contents(ab->Externalize()); |
| 3917 | ab->Detach(); |
| 3918 | CHECK_EQ(0, static_cast<int>(ab->ByteLength())); |
| 3919 | CHECK_EQ(0, v8_run_int32value(v8_compile("ab.byteLength" ))); |
| 3920 | |
| 3921 | CheckIsTypedArrayVarDetached("u8a" ); |
| 3922 | CheckIsTypedArrayVarDetached("u8c" ); |
| 3923 | CheckIsTypedArrayVarDetached("i8a" ); |
| 3924 | CheckIsTypedArrayVarDetached("u16a" ); |
| 3925 | CheckIsTypedArrayVarDetached("i16a" ); |
| 3926 | CheckIsTypedArrayVarDetached("u32a" ); |
| 3927 | CheckIsTypedArrayVarDetached("i32a" ); |
| 3928 | CheckIsTypedArrayVarDetached("f32a" ); |
| 3929 | CheckIsTypedArrayVarDetached("f64a" ); |
| 3930 | |
| 3931 | CHECK(CompileRun("dv.byteLength == 0 && dv.byteOffset == 0" )->IsTrue()); |
| 3932 | CheckDataViewIsDetached(dv); |
| 3933 | } |
| 3934 | |
| 3935 | THREADED_TEST(ArrayBuffer_AllocationInformation) { |
| 3936 | LocalContext env; |
| 3937 | v8::Isolate* isolate = env->GetIsolate(); |
| 3938 | v8::HandleScope handle_scope(isolate); |
| 3939 | |
| 3940 | const size_t ab_size = 1024; |
| 3941 | Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, ab_size); |
| 3942 | ScopedArrayBufferContents contents(ab->Externalize()); |
| 3943 | |
| 3944 | // Array buffers should have normal allocation mode. |
| 3945 | CHECK_EQ(contents.AllocationMode(), |
| 3946 | v8::ArrayBuffer::Allocator::AllocationMode::kNormal); |
| 3947 | // The allocation must contain the buffer (normally they will be equal, but |
| 3948 | // this is not required by the contract). |
| 3949 | CHECK_NOT_NULL(contents.AllocationBase()); |
| 3950 | const uintptr_t alloc = |
| 3951 | reinterpret_cast<uintptr_t>(contents.AllocationBase()); |
| 3952 | const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data()); |
| 3953 | CHECK_LE(alloc, data); |
| 3954 | CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength()); |
| 3955 | } |
| 3956 | |
| 3957 | THREADED_TEST(ArrayBuffer_ExternalizeEmpty) { |
| 3958 | LocalContext env; |
| 3959 | v8::Isolate* isolate = env->GetIsolate(); |
| 3960 | v8::HandleScope handle_scope(isolate); |
| 3961 | |
| 3962 | Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 0); |
| 3963 | CheckInternalFieldsAreZero(ab); |
| 3964 | CHECK_EQ(0, static_cast<int>(ab->ByteLength())); |
| 3965 | CHECK(!ab->IsExternal()); |
| 3966 | |
| 3967 | // Externalize the buffer (taking ownership of the backing store memory). |
| 3968 | ScopedArrayBufferContents ab_contents(ab->Externalize()); |
| 3969 | |
| 3970 | Local<v8::Uint8Array> u8a = v8::Uint8Array::New(ab, 0, 0); |
| 3971 | // Calling Buffer() will materialize the ArrayBuffer (transitioning it from |
| 3972 | // on-heap to off-heap if need be). This should not affect whether it is |
| 3973 | // marked as is_external or not. |
| 3974 | USE(u8a->Buffer()); |
| 3975 | |
| 3976 | CHECK(ab->IsExternal()); |
| 3977 | } |
| 3978 | |
| 3979 | class ScopedSharedArrayBufferContents { |
| 3980 | public: |
| 3981 | explicit ScopedSharedArrayBufferContents( |
| 3982 | const v8::SharedArrayBuffer::Contents& contents) |
| 3983 | : contents_(contents) {} |
| 3984 | ~ScopedSharedArrayBufferContents() { free(contents_.AllocationBase()); } |
| 3985 | void* Data() const { return contents_.Data(); } |
| 3986 | size_t ByteLength() const { return contents_.ByteLength(); } |
| 3987 | |
| 3988 | void* AllocationBase() const { return contents_.AllocationBase(); } |
| 3989 | size_t AllocationLength() const { return contents_.AllocationLength(); } |
| 3990 | v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const { |
| 3991 | return contents_.AllocationMode(); |
| 3992 | } |
| 3993 | |
| 3994 | private: |
| 3995 | const v8::SharedArrayBuffer::Contents contents_; |
| 3996 | }; |
| 3997 | |
| 3998 | |
| 3999 | THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) { |
| 4000 | i::FLAG_harmony_sharedarraybuffer = true; |
| 4001 | LocalContext env; |
| 4002 | v8::Isolate* isolate = env->GetIsolate(); |
| 4003 | v8::HandleScope handle_scope(isolate); |
| 4004 | |
| 4005 | Local<v8::SharedArrayBuffer> ab = v8::SharedArrayBuffer::New(isolate, 1024); |
| 4006 | CheckInternalFieldsAreZero(ab); |
| 4007 | CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); |
| 4008 | CHECK(!ab->IsExternal()); |
| 4009 | CcTest::CollectAllGarbage(); |
| 4010 | |
| 4011 | ScopedSharedArrayBufferContents ab_contents(ab->Externalize()); |
| 4012 | CHECK(ab->IsExternal()); |
| 4013 | |
| 4014 | CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); |
| 4015 | uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); |
| 4016 | CHECK_NOT_NULL(data); |
| 4017 | CHECK(env->Global()->Set(env.local(), v8_str("ab" ), ab).FromJust()); |
| 4018 | |
| 4019 | v8::Local<v8::Value> result = CompileRun("ab.byteLength" ); |
| 4020 | CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| 4021 | |
| 4022 | result = CompileRun( |
| 4023 | "var u8 = new Uint8Array(ab);" |
| 4024 | "u8[0] = 0xFF;" |
| 4025 | "u8[1] = 0xAA;" |
| 4026 | "u8.length" ); |
| 4027 | CHECK_EQ(1024, result->Int32Value(env.local()).FromJust()); |
| 4028 | CHECK_EQ(0xFF, data[0]); |
| 4029 | CHECK_EQ(0xAA, data[1]); |
| 4030 | data[0] = 0xCC; |
| 4031 | data[1] = 0x11; |
| 4032 | result = CompileRun("u8[0] + u8[1]" ); |
| 4033 | CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| 4034 | } |
| 4035 | |
| 4036 | |
| 4037 | THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) { |
| 4038 | i::FLAG_harmony_sharedarraybuffer = true; |
| 4039 | LocalContext env; |
| 4040 | v8::Isolate* isolate = env->GetIsolate(); |
| 4041 | v8::HandleScope handle_scope(isolate); |
| 4042 | |
| 4043 | |
| 4044 | v8::Local<v8::Value> result = CompileRun( |
| 4045 | "var ab1 = new SharedArrayBuffer(2);" |
| 4046 | "var u8_a = new Uint8Array(ab1);" |
| 4047 | "u8_a[0] = 0xAA;" |
| 4048 | "u8_a[1] = 0xFF; u8_a.buffer" ); |
| 4049 | Local<v8::SharedArrayBuffer> ab1 = Local<v8::SharedArrayBuffer>::Cast(result); |
| 4050 | CheckInternalFieldsAreZero(ab1); |
| 4051 | CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); |
| 4052 | CHECK(!ab1->IsExternal()); |
| 4053 | ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize()); |
| 4054 | CHECK(ab1->IsExternal()); |
| 4055 | |
| 4056 | result = CompileRun("ab1.byteLength" ); |
| 4057 | CHECK_EQ(2, result->Int32Value(env.local()).FromJust()); |
| 4058 | result = CompileRun("u8_a[0]" ); |
| 4059 | CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust()); |
| 4060 | result = CompileRun("u8_a[1]" ); |
| 4061 | CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| 4062 | result = CompileRun( |
| 4063 | "var u8_b = new Uint8Array(ab1);" |
| 4064 | "u8_b[0] = 0xBB;" |
| 4065 | "u8_a[0]" ); |
| 4066 | CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust()); |
| 4067 | result = CompileRun("u8_b[1]" ); |
| 4068 | CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust()); |
| 4069 | |
| 4070 | CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); |
| 4071 | uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); |
| 4072 | CHECK_EQ(0xBB, ab1_data[0]); |
| 4073 | CHECK_EQ(0xFF, ab1_data[1]); |
| 4074 | ab1_data[0] = 0xCC; |
| 4075 | ab1_data[1] = 0x11; |
| 4076 | result = CompileRun("u8_a[0] + u8_a[1]" ); |
| 4077 | CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| 4078 | } |
| 4079 | |
| 4080 | |
| 4081 | THREADED_TEST(SharedArrayBuffer_External) { |
| 4082 | i::FLAG_harmony_sharedarraybuffer = true; |
| 4083 | LocalContext env; |
| 4084 | v8::Isolate* isolate = env->GetIsolate(); |
| 4085 | v8::HandleScope handle_scope(isolate); |
| 4086 | |
| 4087 | i::ScopedVector<uint8_t> my_data(100); |
| 4088 | memset(my_data.start(), 0, 100); |
| 4089 | Local<v8::SharedArrayBuffer> ab3 = |
| 4090 | v8::SharedArrayBuffer::New(isolate, my_data.start(), 100); |
| 4091 | CheckInternalFieldsAreZero(ab3); |
| 4092 | CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); |
| 4093 | CHECK(ab3->IsExternal()); |
| 4094 | |
| 4095 | CHECK(env->Global()->Set(env.local(), v8_str("ab3" ), ab3).FromJust()); |
| 4096 | |
| 4097 | v8::Local<v8::Value> result = CompileRun("ab3.byteLength" ); |
| 4098 | CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| 4099 | |
| 4100 | result = CompileRun( |
| 4101 | "var u8_b = new Uint8Array(ab3);" |
| 4102 | "u8_b[0] = 0xBB;" |
| 4103 | "u8_b[1] = 0xCC;" |
| 4104 | "u8_b.length" ); |
| 4105 | CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); |
| 4106 | CHECK_EQ(0xBB, my_data[0]); |
| 4107 | CHECK_EQ(0xCC, my_data[1]); |
| 4108 | my_data[0] = 0xCC; |
| 4109 | my_data[1] = 0x11; |
| 4110 | result = CompileRun("u8_b[0] + u8_b[1]" ); |
| 4111 | CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); |
| 4112 | } |
| 4113 | |
| 4114 | |
| 4115 | THREADED_TEST(HiddenProperties) { |
| 4116 | LocalContext env; |
| 4117 | v8::Isolate* isolate = env->GetIsolate(); |
| 4118 | v8::HandleScope scope(isolate); |
| 4119 | |
| 4120 | v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| 4121 | v8::Local<v8::Private> key = |
| 4122 | v8::Private::ForApi(isolate, v8_str("api-test::hidden-key" )); |
| 4123 | v8::Local<v8::String> empty = v8_str("" ); |
| 4124 | v8::Local<v8::String> prop_name = v8_str("prop_name" ); |
| 4125 | |
| 4126 | CcTest::CollectAllGarbage(); |
| 4127 | |
| 4128 | // Make sure delete of a non-existent hidden value works |
| 4129 | obj->DeletePrivate(env.local(), key).FromJust(); |
| 4130 | |
| 4131 | CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 1503)) |
| 4132 | .FromJust()); |
| 4133 | CHECK_EQ(1503, obj->GetPrivate(env.local(), key) |
| 4134 | .ToLocalChecked() |
| 4135 | ->Int32Value(env.local()) |
| 4136 | .FromJust()); |
| 4137 | CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 2002)) |
| 4138 | .FromJust()); |
| 4139 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4140 | .ToLocalChecked() |
| 4141 | ->Int32Value(env.local()) |
| 4142 | .FromJust()); |
| 4143 | |
| 4144 | CcTest::CollectAllGarbage(); |
| 4145 | |
| 4146 | // Make sure we do not find the hidden property. |
| 4147 | CHECK(!obj->Has(env.local(), empty).FromJust()); |
| 4148 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4149 | .ToLocalChecked() |
| 4150 | ->Int32Value(env.local()) |
| 4151 | .FromJust()); |
| 4152 | CHECK(obj->Get(env.local(), empty).ToLocalChecked()->IsUndefined()); |
| 4153 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4154 | .ToLocalChecked() |
| 4155 | ->Int32Value(env.local()) |
| 4156 | .FromJust()); |
| 4157 | CHECK( |
| 4158 | obj->Set(env.local(), empty, v8::Integer::New(isolate, 2003)).FromJust()); |
| 4159 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4160 | .ToLocalChecked() |
| 4161 | ->Int32Value(env.local()) |
| 4162 | .FromJust()); |
| 4163 | CHECK_EQ(2003, obj->Get(env.local(), empty) |
| 4164 | .ToLocalChecked() |
| 4165 | ->Int32Value(env.local()) |
| 4166 | .FromJust()); |
| 4167 | |
| 4168 | CcTest::CollectAllGarbage(); |
| 4169 | |
| 4170 | // Add another property and delete it afterwards to force the object in |
| 4171 | // slow case. |
| 4172 | CHECK(obj->Set(env.local(), prop_name, v8::Integer::New(isolate, 2008)) |
| 4173 | .FromJust()); |
| 4174 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4175 | .ToLocalChecked() |
| 4176 | ->Int32Value(env.local()) |
| 4177 | .FromJust()); |
| 4178 | CHECK_EQ(2008, obj->Get(env.local(), prop_name) |
| 4179 | .ToLocalChecked() |
| 4180 | ->Int32Value(env.local()) |
| 4181 | .FromJust()); |
| 4182 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4183 | .ToLocalChecked() |
| 4184 | ->Int32Value(env.local()) |
| 4185 | .FromJust()); |
| 4186 | CHECK(obj->Delete(env.local(), prop_name).FromJust()); |
| 4187 | CHECK_EQ(2002, obj->GetPrivate(env.local(), key) |
| 4188 | .ToLocalChecked() |
| 4189 | ->Int32Value(env.local()) |
| 4190 | .FromJust()); |
| 4191 | |
| 4192 | CcTest::CollectAllGarbage(); |
| 4193 | |
| 4194 | CHECK(obj->SetPrivate(env.local(), key, v8::Integer::New(isolate, 2002)) |
| 4195 | .FromJust()); |
| 4196 | CHECK(obj->DeletePrivate(env.local(), key).FromJust()); |
| 4197 | CHECK(!obj->HasPrivate(env.local(), key).FromJust()); |
| 4198 | } |
| 4199 | |
| 4200 | |
| 4201 | THREADED_TEST(Regress97784) { |
| 4202 | // Regression test for crbug.com/97784 |
| 4203 | // Messing with the Object.prototype should not have effect on |
| 4204 | // hidden properties. |
| 4205 | LocalContext env; |
| 4206 | v8::HandleScope scope(env->GetIsolate()); |
| 4207 | |
| 4208 | v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); |
| 4209 | v8::Local<v8::Private> key = |
| 4210 | v8::Private::New(env->GetIsolate(), v8_str("hidden" )); |
| 4211 | |
| 4212 | CompileRun( |
| 4213 | "set_called = false;" |
| 4214 | "Object.defineProperty(" |
| 4215 | " Object.prototype," |
| 4216 | " 'hidden'," |
| 4217 | " {get: function() { return 45; }," |
| 4218 | " set: function() { set_called = true; }})" ); |
| 4219 | |
| 4220 | CHECK(!obj->HasPrivate(env.local(), key).FromJust()); |
| 4221 | // Make sure that the getter and setter from Object.prototype is not invoked. |
| 4222 | // If it did we would have full access to the hidden properties in |
| 4223 | // the accessor. |
| 4224 | CHECK( |
| 4225 | obj->SetPrivate(env.local(), key, v8::Integer::New(env->GetIsolate(), 42)) |
| 4226 | .FromJust()); |
| 4227 | ExpectFalse("set_called" ); |
| 4228 | CHECK_EQ(42, obj->GetPrivate(env.local(), key) |
| 4229 | .ToLocalChecked() |
| 4230 | ->Int32Value(env.local()) |
| 4231 | .FromJust()); |
| 4232 | } |
| 4233 | |
| 4234 | |
| 4235 | THREADED_TEST(External) { |
| 4236 | v8::HandleScope scope(CcTest::isolate()); |
| 4237 | int x = 3; |
| 4238 | Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x); |
| 4239 | LocalContext env; |
| 4240 | CHECK(env->Global()->Set(env.local(), v8_str("ext" ), ext).FromJust()); |
| 4241 | Local<Value> reext_obj = CompileRun("this.ext" ); |
| 4242 | v8::Local<v8::External> reext = reext_obj.As<v8::External>(); |
| 4243 | int* ptr = static_cast<int*>(reext->Value()); |
| 4244 | CHECK_EQ(3, x); |
| 4245 | *ptr = 10; |
| 4246 | CHECK_EQ(x, 10); |
| 4247 | |
| 4248 | { |
| 4249 | i::Handle<i::Object> obj = v8::Utils::OpenHandle(*ext); |
| 4250 | CHECK_EQ(i::HeapObject::cast(*obj)->map(), CcTest::heap()->external_map()); |
| 4251 | CHECK(ext->IsExternal()); |
| 4252 | CHECK(!CompileRun("new Set().add(this.ext)" ).IsEmpty()); |
| 4253 | CHECK_EQ(i::HeapObject::cast(*obj)->map(), CcTest::heap()->external_map()); |
| 4254 | CHECK(ext->IsExternal()); |
| 4255 | } |
| 4256 | |
| 4257 | // Make sure unaligned pointers are wrapped properly. |
| 4258 | char* data = i::StrDup("0123456789" ); |
| 4259 | Local<v8::Value> zero = v8::External::New(CcTest::isolate(), &data[0]); |
| 4260 | Local<v8::Value> one = v8::External::New(CcTest::isolate(), &data[1]); |
| 4261 | Local<v8::Value> two = v8::External::New(CcTest::isolate(), &data[2]); |
| 4262 | Local<v8::Value> three = v8::External::New(CcTest::isolate(), &data[3]); |
| 4263 | |
| 4264 | char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value()); |
| 4265 | CHECK_EQ('0', *char_ptr); |
| 4266 | char_ptr = reinterpret_cast<char*>(v8::External::Cast(*one)->Value()); |
| 4267 | CHECK_EQ('1', *char_ptr); |
| 4268 | char_ptr = reinterpret_cast<char*>(v8::External::Cast(*two)->Value()); |
| 4269 | CHECK_EQ('2', *char_ptr); |
| 4270 | char_ptr = reinterpret_cast<char*>(v8::External::Cast(*three)->Value()); |
| 4271 | CHECK_EQ('3', *char_ptr); |
| 4272 | i::DeleteArray(data); |
| 4273 | } |
| 4274 | |
| 4275 | |
| 4276 | THREADED_TEST(GlobalHandle) { |
| 4277 | v8::Isolate* isolate = CcTest::isolate(); |
| 4278 | v8::Persistent<String> global; |
| 4279 | { |
| 4280 | v8::HandleScope scope(isolate); |
| 4281 | global.Reset(isolate, v8_str("str" )); |
| 4282 | } |
| 4283 | { |
| 4284 | v8::HandleScope scope(isolate); |
| 4285 | CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| 4286 | } |
| 4287 | global.Reset(); |
| 4288 | { |
| 4289 | v8::HandleScope scope(isolate); |
| 4290 | global.Reset(isolate, v8_str("str" )); |
| 4291 | } |
| 4292 | { |
| 4293 | v8::HandleScope scope(isolate); |
| 4294 | CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| 4295 | } |
| 4296 | global.Reset(); |
| 4297 | } |
| 4298 | |
| 4299 | |
| 4300 | THREADED_TEST(ResettingGlobalHandle) { |
| 4301 | v8::Isolate* isolate = CcTest::isolate(); |
| 4302 | v8::Persistent<String> global; |
| 4303 | { |
| 4304 | v8::HandleScope scope(isolate); |
| 4305 | global.Reset(isolate, v8_str("str" )); |
| 4306 | } |
| 4307 | v8::internal::GlobalHandles* global_handles = |
| 4308 | reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| 4309 | size_t initial_handle_count = global_handles->handles_count(); |
| 4310 | { |
| 4311 | v8::HandleScope scope(isolate); |
| 4312 | CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| 4313 | } |
| 4314 | { |
| 4315 | v8::HandleScope scope(isolate); |
| 4316 | global.Reset(isolate, v8_str("longer" )); |
| 4317 | } |
| 4318 | CHECK_EQ(global_handles->handles_count(), initial_handle_count); |
| 4319 | { |
| 4320 | v8::HandleScope scope(isolate); |
| 4321 | CHECK_EQ(6, v8::Local<String>::New(isolate, global)->Length()); |
| 4322 | } |
| 4323 | global.Reset(); |
| 4324 | CHECK_EQ(global_handles->handles_count(), initial_handle_count - 1); |
| 4325 | } |
| 4326 | |
| 4327 | |
| 4328 | THREADED_TEST(ResettingGlobalHandleToEmpty) { |
| 4329 | v8::Isolate* isolate = CcTest::isolate(); |
| 4330 | v8::Persistent<String> global; |
| 4331 | { |
| 4332 | v8::HandleScope scope(isolate); |
| 4333 | global.Reset(isolate, v8_str("str" )); |
| 4334 | } |
| 4335 | v8::internal::GlobalHandles* global_handles = |
| 4336 | reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| 4337 | size_t initial_handle_count = global_handles->handles_count(); |
| 4338 | { |
| 4339 | v8::HandleScope scope(isolate); |
| 4340 | CHECK_EQ(3, v8::Local<String>::New(isolate, global)->Length()); |
| 4341 | } |
| 4342 | { |
| 4343 | v8::HandleScope scope(isolate); |
| 4344 | Local<String> empty; |
| 4345 | global.Reset(isolate, empty); |
| 4346 | } |
| 4347 | CHECK(global.IsEmpty()); |
| 4348 | CHECK_EQ(global_handles->handles_count(), initial_handle_count - 1); |
| 4349 | } |
| 4350 | |
| 4351 | |
| 4352 | template <class T> |
| 4353 | static v8::Global<T> PassUnique(v8::Global<T> unique) { |
| 4354 | return unique.Pass(); |
| 4355 | } |
| 4356 | |
| 4357 | |
| 4358 | template <class T> |
| 4359 | static v8::Global<T> ReturnUnique(v8::Isolate* isolate, |
| 4360 | const v8::Persistent<T>& global) { |
| 4361 | v8::Global<String> unique(isolate, global); |
| 4362 | return unique.Pass(); |
| 4363 | } |
| 4364 | |
| 4365 | |
| 4366 | THREADED_TEST(Global) { |
| 4367 | v8::Isolate* isolate = CcTest::isolate(); |
| 4368 | v8::Persistent<String> global; |
| 4369 | { |
| 4370 | v8::HandleScope scope(isolate); |
| 4371 | global.Reset(isolate, v8_str("str" )); |
| 4372 | } |
| 4373 | v8::internal::GlobalHandles* global_handles = |
| 4374 | reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| 4375 | size_t initial_handle_count = global_handles->handles_count(); |
| 4376 | { |
| 4377 | v8::Global<String> unique(isolate, global); |
| 4378 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4379 | // Test assignment via Pass |
| 4380 | { |
| 4381 | v8::Global<String> copy = unique.Pass(); |
| 4382 | CHECK(unique.IsEmpty()); |
| 4383 | CHECK(copy == global); |
| 4384 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4385 | unique = copy.Pass(); |
| 4386 | } |
| 4387 | // Test ctor via Pass |
| 4388 | { |
| 4389 | v8::Global<String> copy(unique.Pass()); |
| 4390 | CHECK(unique.IsEmpty()); |
| 4391 | CHECK(copy == global); |
| 4392 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4393 | unique = copy.Pass(); |
| 4394 | } |
| 4395 | // Test pass through function call |
| 4396 | { |
| 4397 | v8::Global<String> copy = PassUnique(unique.Pass()); |
| 4398 | CHECK(unique.IsEmpty()); |
| 4399 | CHECK(copy == global); |
| 4400 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4401 | unique = copy.Pass(); |
| 4402 | } |
| 4403 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4404 | } |
| 4405 | // Test pass from function call |
| 4406 | { |
| 4407 | v8::Global<String> unique = ReturnUnique(isolate, global); |
| 4408 | CHECK(unique == global); |
| 4409 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4410 | } |
| 4411 | CHECK_EQ(initial_handle_count, global_handles->handles_count()); |
| 4412 | global.Reset(); |
| 4413 | } |
| 4414 | |
| 4415 | |
| 4416 | namespace { |
| 4417 | |
| 4418 | class TwoPassCallbackData; |
| 4419 | void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data); |
| 4420 | void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data); |
| 4421 | |
| 4422 | |
| 4423 | class TwoPassCallbackData { |
| 4424 | public: |
| 4425 | TwoPassCallbackData(v8::Isolate* isolate, int* instance_counter) |
| 4426 | : first_pass_called_(false), |
| 4427 | second_pass_called_(false), |
| 4428 | trigger_gc_(false), |
| 4429 | instance_counter_(instance_counter) { |
| 4430 | HandleScope scope(isolate); |
| 4431 | i::ScopedVector<char> buffer(40); |
| 4432 | i::SNPrintF(buffer, "%p" , static_cast<void*>(this)); |
| 4433 | auto string = |
| 4434 | v8::String::NewFromUtf8(isolate, buffer.start(), |
| 4435 | v8::NewStringType::kNormal).ToLocalChecked(); |
| 4436 | cell_.Reset(isolate, string); |
| 4437 | (*instance_counter_)++; |
| 4438 | } |
| 4439 | |
| 4440 | ~TwoPassCallbackData() { |
| 4441 | CHECK(first_pass_called_); |
| 4442 | CHECK(second_pass_called_); |
| 4443 | CHECK(cell_.IsEmpty()); |
| 4444 | (*instance_counter_)--; |
| 4445 | } |
| 4446 | |
| 4447 | void FirstPass() { |
| 4448 | CHECK(!first_pass_called_); |
| 4449 | CHECK(!second_pass_called_); |
| 4450 | CHECK(!cell_.IsEmpty()); |
| 4451 | cell_.Reset(); |
| 4452 | first_pass_called_ = true; |
| 4453 | } |
| 4454 | |
| 4455 | void SecondPass() { |
| 4456 | CHECK(first_pass_called_); |
| 4457 | CHECK(!second_pass_called_); |
| 4458 | CHECK(cell_.IsEmpty()); |
| 4459 | second_pass_called_ = true; |
| 4460 | delete this; |
| 4461 | } |
| 4462 | |
| 4463 | void SetWeak() { |
| 4464 | cell_.SetWeak(this, FirstPassCallback, v8::WeakCallbackType::kParameter); |
| 4465 | } |
| 4466 | |
| 4467 | void MarkTriggerGc() { trigger_gc_ = true; } |
| 4468 | bool trigger_gc() { return trigger_gc_; } |
| 4469 | |
| 4470 | int* instance_counter() { return instance_counter_; } |
| 4471 | |
| 4472 | private: |
| 4473 | bool first_pass_called_; |
| 4474 | bool second_pass_called_; |
| 4475 | bool trigger_gc_; |
| 4476 | v8::Global<v8::String> cell_; |
| 4477 | int* instance_counter_; |
| 4478 | }; |
| 4479 | |
| 4480 | |
| 4481 | void SecondPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) { |
| 4482 | ApiTestFuzzer::Fuzz(); |
| 4483 | bool trigger_gc = data.GetParameter()->trigger_gc(); |
| 4484 | int* instance_counter = data.GetParameter()->instance_counter(); |
| 4485 | data.GetParameter()->SecondPass(); |
| 4486 | if (!trigger_gc) return; |
| 4487 | auto data_2 = new TwoPassCallbackData(data.GetIsolate(), instance_counter); |
| 4488 | data_2->SetWeak(); |
| 4489 | CcTest::CollectAllGarbage(); |
| 4490 | } |
| 4491 | |
| 4492 | |
| 4493 | void FirstPassCallback(const v8::WeakCallbackInfo<TwoPassCallbackData>& data) { |
| 4494 | data.GetParameter()->FirstPass(); |
| 4495 | data.SetSecondPassCallback(SecondPassCallback); |
| 4496 | } |
| 4497 | |
| 4498 | } // namespace |
| 4499 | |
| 4500 | |
| 4501 | TEST(TwoPassPhantomCallbacks) { |
| 4502 | auto isolate = CcTest::isolate(); |
| 4503 | const size_t kLength = 20; |
| 4504 | int instance_counter = 0; |
| 4505 | for (size_t i = 0; i < kLength; ++i) { |
| 4506 | auto data = new TwoPassCallbackData(isolate, &instance_counter); |
| 4507 | data->SetWeak(); |
| 4508 | } |
| 4509 | CHECK_EQ(static_cast<int>(kLength), instance_counter); |
| 4510 | CcTest::CollectAllGarbage(); |
| 4511 | EmptyMessageQueues(isolate); |
| 4512 | CHECK_EQ(0, instance_counter); |
| 4513 | } |
| 4514 | |
| 4515 | |
| 4516 | TEST(TwoPassPhantomCallbacksNestedGc) { |
| 4517 | auto isolate = CcTest::isolate(); |
| 4518 | const size_t kLength = 20; |
| 4519 | TwoPassCallbackData* array[kLength]; |
| 4520 | int instance_counter = 0; |
| 4521 | for (size_t i = 0; i < kLength; ++i) { |
| 4522 | array[i] = new TwoPassCallbackData(isolate, &instance_counter); |
| 4523 | array[i]->SetWeak(); |
| 4524 | } |
| 4525 | array[5]->MarkTriggerGc(); |
| 4526 | array[10]->MarkTriggerGc(); |
| 4527 | array[15]->MarkTriggerGc(); |
| 4528 | CHECK_EQ(static_cast<int>(kLength), instance_counter); |
| 4529 | CcTest::CollectAllGarbage(); |
| 4530 | EmptyMessageQueues(isolate); |
| 4531 | CHECK_EQ(0, instance_counter); |
| 4532 | } |
| 4533 | |
| 4534 | |
| 4535 | namespace { |
| 4536 | |
| 4537 | void* IntKeyToVoidPointer(int key) { return reinterpret_cast<void*>(key << 1); } |
| 4538 | |
| 4539 | |
| 4540 | Local<v8::Object> NewObjectForIntKey( |
| 4541 | v8::Isolate* isolate, const v8::Global<v8::ObjectTemplate>& templ, |
| 4542 | int key) { |
| 4543 | auto local = Local<v8::ObjectTemplate>::New(isolate, templ); |
| 4544 | auto obj = local->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); |
| 4545 | obj->SetAlignedPointerInInternalField(0, IntKeyToVoidPointer(key)); |
| 4546 | return obj; |
| 4547 | } |
| 4548 | |
| 4549 | |
| 4550 | template <typename K, typename V> |
| 4551 | class PhantomStdMapTraits : public v8::StdMapTraits<K, V> { |
| 4552 | public: |
| 4553 | typedef typename v8::GlobalValueMap<K, V, PhantomStdMapTraits<K, V>> MapType; |
| 4554 | static const v8::PersistentContainerCallbackType kCallbackType = |
| 4555 | v8::kWeakWithInternalFields; |
| 4556 | struct WeakCallbackDataType { |
| 4557 | MapType* map; |
| 4558 | K key; |
| 4559 | }; |
| 4560 | static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key, |
| 4561 | Local<V> value) { |
| 4562 | WeakCallbackDataType* data = new WeakCallbackDataType; |
| 4563 | data->map = map; |
| 4564 | data->key = key; |
| 4565 | return data; |
| 4566 | } |
| 4567 | static MapType* MapFromWeakCallbackInfo( |
| 4568 | const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { |
| 4569 | return data.GetParameter()->map; |
| 4570 | } |
| 4571 | static K KeyFromWeakCallbackInfo( |
| 4572 | const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { |
| 4573 | return data.GetParameter()->key; |
| 4574 | } |
| 4575 | static void DisposeCallbackData(WeakCallbackDataType* data) { delete data; } |
| 4576 | static void Dispose(v8::Isolate* isolate, v8::Global<V> value, K key) { |
| 4577 | CHECK_EQ(IntKeyToVoidPointer(key), |
| 4578 | v8::Object::GetAlignedPointerFromInternalField(value, 0)); |
| 4579 | } |
| 4580 | static void OnWeakCallback( |
| 4581 | const v8::WeakCallbackInfo<WeakCallbackDataType>&) {} |
| 4582 | static void DisposeWeak( |
| 4583 | const v8::WeakCallbackInfo<WeakCallbackDataType>& info) { |
| 4584 | K key = KeyFromWeakCallbackInfo(info); |
| 4585 | CHECK_EQ(IntKeyToVoidPointer(key), info.GetInternalField(0)); |
| 4586 | DisposeCallbackData(info.GetParameter()); |
| 4587 | } |
| 4588 | }; |
| 4589 | |
| 4590 | |
| 4591 | template <typename Map> |
| 4592 | void TestGlobalValueMap() { |
| 4593 | LocalContext env; |
| 4594 | v8::Isolate* isolate = env->GetIsolate(); |
| 4595 | v8::Global<ObjectTemplate> templ; |
| 4596 | { |
| 4597 | HandleScope scope(isolate); |
| 4598 | auto t = ObjectTemplate::New(isolate); |
| 4599 | t->SetInternalFieldCount(1); |
| 4600 | templ.Reset(isolate, t); |
| 4601 | } |
| 4602 | Map map(isolate); |
| 4603 | v8::internal::GlobalHandles* global_handles = |
| 4604 | reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| 4605 | size_t initial_handle_count = global_handles->handles_count(); |
| 4606 | CHECK_EQ(0, static_cast<int>(map.Size())); |
| 4607 | { |
| 4608 | HandleScope scope(isolate); |
| 4609 | Local<v8::Object> obj = map.Get(7); |
| 4610 | CHECK(obj.IsEmpty()); |
| 4611 | Local<v8::Object> expected = v8::Object::New(isolate); |
| 4612 | map.Set(7, expected); |
| 4613 | CHECK_EQ(1, static_cast<int>(map.Size())); |
| 4614 | obj = map.Get(7); |
| 4615 | CHECK(expected->Equals(env.local(), obj).FromJust()); |
| 4616 | { |
| 4617 | typename Map::PersistentValueReference ref = map.GetReference(7); |
| 4618 | CHECK(expected->Equals(env.local(), ref.NewLocal(isolate)).FromJust()); |
| 4619 | } |
| 4620 | v8::Global<v8::Object> removed = map.Remove(7); |
| 4621 | CHECK_EQ(0, static_cast<int>(map.Size())); |
| 4622 | CHECK(expected == removed); |
| 4623 | removed = map.Remove(7); |
| 4624 | CHECK(removed.IsEmpty()); |
| 4625 | map.Set(8, expected); |
| 4626 | CHECK_EQ(1, static_cast<int>(map.Size())); |
| 4627 | map.Set(8, expected); |
| 4628 | CHECK_EQ(1, static_cast<int>(map.Size())); |
| 4629 | { |
| 4630 | typename Map::PersistentValueReference ref; |
| 4631 | Local<v8::Object> expected2 = NewObjectForIntKey(isolate, templ, 8); |
| 4632 | removed = map.Set(8, v8::Global<v8::Object>(isolate, expected2), &ref); |
| 4633 | CHECK_EQ(1, static_cast<int>(map.Size())); |
| 4634 | CHECK(expected == removed); |
| 4635 | CHECK(expected2->Equals(env.local(), ref.NewLocal(isolate)).FromJust()); |
| 4636 | } |
| 4637 | } |
| 4638 | CHECK_EQ(initial_handle_count + 1, global_handles->handles_count()); |
| 4639 | if (map.IsWeak()) { |
| 4640 | CcTest::PreciseCollectAllGarbage(); |
| 4641 | } else { |
| 4642 | map.Clear(); |
| 4643 | } |
| 4644 | CHECK_EQ(0, static_cast<int>(map.Size())); |
| 4645 | CHECK_EQ(initial_handle_count, global_handles->handles_count()); |
| 4646 | { |
| 4647 | HandleScope scope(isolate); |
| 4648 | Local<v8::Object> value = NewObjectForIntKey(isolate, templ, 9); |
| 4649 | map.Set(9, value); |
| 4650 | map.Clear(); |
| 4651 | } |
| 4652 | CHECK_EQ(0, static_cast<int>(map.Size())); |
| 4653 | CHECK_EQ(initial_handle_count, global_handles->handles_count()); |
| 4654 | } |
| 4655 | |
| 4656 | } // namespace |
| 4657 | |
| 4658 | |
| 4659 | TEST(GlobalValueMap) { |
| 4660 | // Default case, w/o weak callbacks: |
| 4661 | TestGlobalValueMap<v8::StdGlobalValueMap<int, v8::Object>>(); |
| 4662 | |
| 4663 | // Custom traits with weak callbacks: |
| 4664 | typedef v8::GlobalValueMap<int, v8::Object, |
| 4665 | PhantomStdMapTraits<int, v8::Object>> WeakMap; |
| 4666 | TestGlobalValueMap<WeakMap>(); |
| 4667 | } |
| 4668 | |
| 4669 | |
| 4670 | TEST(PersistentValueVector) { |
| 4671 | LocalContext env; |
| 4672 | v8::Isolate* isolate = env->GetIsolate(); |
| 4673 | v8::internal::GlobalHandles* global_handles = |
| 4674 | reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); |
| 4675 | size_t handle_count = global_handles->handles_count(); |
| 4676 | HandleScope scope(isolate); |
| 4677 | |
| 4678 | v8::PersistentValueVector<v8::Object> vector(isolate); |
| 4679 | |
| 4680 | Local<v8::Object> obj1 = v8::Object::New(isolate); |
| 4681 | Local<v8::Object> obj2 = v8::Object::New(isolate); |
| 4682 | v8::Global<v8::Object> obj3(isolate, v8::Object::New(isolate)); |
| 4683 | |
| 4684 | CHECK(vector.IsEmpty()); |
| 4685 | CHECK_EQ(0, static_cast<int>(vector.Size())); |
| 4686 | |
| 4687 | vector.ReserveCapacity(3); |
| 4688 | CHECK(vector.IsEmpty()); |
| 4689 | |
| 4690 | vector.Append(obj1); |
| 4691 | vector.Append(obj2); |
| 4692 | vector.Append(obj1); |
| 4693 | vector.Append(obj3.Pass()); |
| 4694 | vector.Append(obj1); |
| 4695 | |
| 4696 | CHECK(!vector.IsEmpty()); |
| 4697 | CHECK_EQ(5, static_cast<int>(vector.Size())); |
| 4698 | CHECK(obj3.IsEmpty()); |
| 4699 | CHECK(obj1->Equals(env.local(), vector.Get(0)).FromJust()); |
| 4700 | CHECK(obj1->Equals(env.local(), vector.Get(2)).FromJust()); |
| 4701 | CHECK(obj1->Equals(env.local(), vector.Get(4)).FromJust()); |
| 4702 | CHECK(obj2->Equals(env.local(), vector.Get(1)).FromJust()); |
| 4703 | |
| 4704 | CHECK_EQ(5 + handle_count, global_handles->handles_count()); |
| 4705 | |
| 4706 | vector.Clear(); |
| 4707 | CHECK(vector.IsEmpty()); |
| 4708 | CHECK_EQ(0, static_cast<int>(vector.Size())); |
| 4709 | CHECK_EQ(handle_count, global_handles->handles_count()); |
| 4710 | } |
| 4711 | |
| 4712 | |
| 4713 | THREADED_TEST(GlobalHandleUpcast) { |
| 4714 | v8::Isolate* isolate = CcTest::isolate(); |
| 4715 | v8::HandleScope scope(isolate); |
| 4716 | v8::Local<String> local = v8::Local<String>::New(isolate, v8_str("str" )); |
| 4717 | v8::Persistent<String> global_string(isolate, local); |
| 4718 | v8::Persistent<Value>& global_value = |
| 4719 | v8::Persistent<Value>::Cast(global_string); |
| 4720 | CHECK(v8::Local<v8::Value>::New(isolate, global_value)->IsString()); |
| 4721 | CHECK(global_string == v8::Persistent<String>::Cast(global_value)); |
| 4722 | global_string.Reset(); |
| 4723 | } |
| 4724 | |
| 4725 | |
| 4726 | THREADED_TEST(HandleEquality) { |
| 4727 | v8::Isolate* isolate = CcTest::isolate(); |
| 4728 | v8::Persistent<String> global1; |
| 4729 | v8::Persistent<String> global2; |
| 4730 | { |
| 4731 | v8::HandleScope scope(isolate); |
| 4732 | global1.Reset(isolate, v8_str("str" )); |
| 4733 | global2.Reset(isolate, v8_str("str2" )); |
| 4734 | } |
| 4735 | CHECK(global1 == global1); |
| 4736 | CHECK(!(global1 != global1)); |
| 4737 | { |
| 4738 | v8::HandleScope scope(isolate); |
| 4739 | Local<String> local1 = Local<String>::New(isolate, global1); |
| 4740 | Local<String> local2 = Local<String>::New(isolate, global2); |
| 4741 | |
| 4742 | CHECK(global1 == local1); |
| 4743 | CHECK(!(global1 != local1)); |
| 4744 | CHECK(local1 == global1); |
| 4745 | CHECK(!(local1 != global1)); |
| 4746 | |
| 4747 | CHECK(!(global1 == local2)); |
| 4748 | CHECK(global1 != local2); |
| 4749 | CHECK(!(local2 == global1)); |
| 4750 | CHECK(local2 != global1); |
| 4751 | |
| 4752 | CHECK(!(local1 == local2)); |
| 4753 | CHECK(local1 != local2); |
| 4754 | |
| 4755 | Local<String> anotherLocal1 = Local<String>::New(isolate, global1); |
| 4756 | CHECK(local1 == anotherLocal1); |
| 4757 | CHECK(!(local1 != anotherLocal1)); |
| 4758 | } |
| 4759 | global1.Reset(); |
| 4760 | global2.Reset(); |
| 4761 | } |
| 4762 | |
| 4763 | |
| 4764 | THREADED_TEST(LocalHandle) { |
| 4765 | v8::HandleScope scope(CcTest::isolate()); |
| 4766 | v8::Local<String> local = |
| 4767 | v8::Local<String>::New(CcTest::isolate(), v8_str("str" )); |
| 4768 | CHECK_EQ(3, local->Length()); |
| 4769 | } |
| 4770 | |
| 4771 | |
| 4772 | class WeakCallCounter { |
| 4773 | public: |
| 4774 | explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) {} |
| 4775 | int id() { return id_; } |
| 4776 | void increment() { number_of_weak_calls_++; } |
| 4777 | int NumberOfWeakCalls() { return number_of_weak_calls_; } |
| 4778 | |
| 4779 | private: |
| 4780 | int id_; |
| 4781 | int number_of_weak_calls_; |
| 4782 | }; |
| 4783 | |
| 4784 | |
| 4785 | template <typename T> |
| 4786 | struct WeakCallCounterAndPersistent { |
| 4787 | explicit WeakCallCounterAndPersistent(WeakCallCounter* counter) |
| 4788 | : counter(counter) {} |
| 4789 | WeakCallCounter* counter; |
| 4790 | v8::Persistent<T> handle; |
| 4791 | }; |
| 4792 | |
| 4793 | |
| 4794 | template <typename T> |
| 4795 | static void WeakPointerCallback( |
| 4796 | const v8::WeakCallbackInfo<WeakCallCounterAndPersistent<T>>& data) { |
| 4797 | CHECK_EQ(1234, data.GetParameter()->counter->id()); |
| 4798 | data.GetParameter()->counter->increment(); |
| 4799 | data.GetParameter()->handle.Reset(); |
| 4800 | } |
| 4801 | |
| 4802 | THREADED_TEST(ScriptException) { |
| 4803 | LocalContext env; |
| 4804 | v8::HandleScope scope(env->GetIsolate()); |
| 4805 | Local<Script> script = v8_compile("throw 'panama!';" ); |
| 4806 | v8::TryCatch try_catch(env->GetIsolate()); |
| 4807 | v8::MaybeLocal<Value> result = script->Run(env.local()); |
| 4808 | CHECK(result.IsEmpty()); |
| 4809 | CHECK(try_catch.HasCaught()); |
| 4810 | String::Utf8Value exception_value(env->GetIsolate(), try_catch.Exception()); |
| 4811 | CHECK_EQ(0, strcmp(*exception_value, "panama!" )); |
| 4812 | } |
| 4813 | |
| 4814 | |
| 4815 | TEST(TryCatchCustomException) { |
| 4816 | LocalContext env; |
| 4817 | v8::Isolate* isolate = env->GetIsolate(); |
| 4818 | v8::HandleScope scope(isolate); |
| 4819 | v8::TryCatch try_catch(isolate); |
| 4820 | CompileRun( |
| 4821 | "function CustomError() { this.a = 'b'; }" |
| 4822 | "(function f() { throw new CustomError(); })();" ); |
| 4823 | CHECK(try_catch.HasCaught()); |
| 4824 | CHECK(try_catch.Exception() |
| 4825 | ->ToObject(env.local()) |
| 4826 | .ToLocalChecked() |
| 4827 | ->Get(env.local(), v8_str("a" )) |
| 4828 | .ToLocalChecked() |
| 4829 | ->Equals(env.local(), v8_str("b" )) |
| 4830 | .FromJust()); |
| 4831 | } |
| 4832 | |
| 4833 | |
| 4834 | bool message_received; |
| 4835 | |
| 4836 | |
| 4837 | static void check_message_0(v8::Local<v8::Message> message, |
| 4838 | v8::Local<Value> data) { |
| 4839 | CHECK_EQ(5.76, data->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 4840 | .FromJust()); |
| 4841 | CHECK_EQ(6.75, message->GetScriptOrigin() |
| 4842 | .ResourceName() |
| 4843 | ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 4844 | .FromJust()); |
| 4845 | CHECK(!message->IsSharedCrossOrigin()); |
| 4846 | message_received = true; |
| 4847 | } |
| 4848 | |
| 4849 | |
| 4850 | THREADED_TEST(MessageHandler0) { |
| 4851 | message_received = false; |
| 4852 | v8::HandleScope scope(CcTest::isolate()); |
| 4853 | CHECK(!message_received); |
| 4854 | LocalContext context; |
| 4855 | CcTest::isolate()->AddMessageListener(check_message_0, v8_num(5.76)); |
| 4856 | v8::Local<v8::Script> script = |
| 4857 | CompileWithOrigin("throw 'error'" , "6.75" , false); |
| 4858 | CHECK(script->Run(context.local()).IsEmpty()); |
| 4859 | CHECK(message_received); |
| 4860 | // clear out the message listener |
| 4861 | CcTest::isolate()->RemoveMessageListeners(check_message_0); |
| 4862 | } |
| 4863 | |
| 4864 | |
| 4865 | static void check_message_1(v8::Local<v8::Message> message, |
| 4866 | v8::Local<Value> data) { |
| 4867 | CHECK(data->IsNumber()); |
| 4868 | CHECK_EQ(1337, |
| 4869 | data->Int32Value(CcTest::isolate()->GetCurrentContext()).FromJust()); |
| 4870 | CHECK(!message->IsSharedCrossOrigin()); |
| 4871 | message_received = true; |
| 4872 | } |
| 4873 | |
| 4874 | |
| 4875 | TEST(MessageHandler1) { |
| 4876 | message_received = false; |
| 4877 | v8::HandleScope scope(CcTest::isolate()); |
| 4878 | CHECK(!message_received); |
| 4879 | CcTest::isolate()->AddMessageListener(check_message_1); |
| 4880 | LocalContext context; |
| 4881 | CompileRun("throw 1337;" ); |
| 4882 | CHECK(message_received); |
| 4883 | // clear out the message listener |
| 4884 | CcTest::isolate()->RemoveMessageListeners(check_message_1); |
| 4885 | } |
| 4886 | |
| 4887 | |
| 4888 | static void check_message_2(v8::Local<v8::Message> message, |
| 4889 | v8::Local<Value> data) { |
| 4890 | LocalContext context; |
| 4891 | CHECK(data->IsObject()); |
| 4892 | v8::Local<v8::Value> hidden_property = |
| 4893 | v8::Object::Cast(*data) |
| 4894 | ->GetPrivate( |
| 4895 | context.local(), |
| 4896 | v8::Private::ForApi(CcTest::isolate(), v8_str("hidden key" ))) |
| 4897 | .ToLocalChecked(); |
| 4898 | CHECK(v8_str("hidden value" ) |
| 4899 | ->Equals(context.local(), hidden_property) |
| 4900 | .FromJust()); |
| 4901 | CHECK(!message->IsSharedCrossOrigin()); |
| 4902 | message_received = true; |
| 4903 | } |
| 4904 | |
| 4905 | |
| 4906 | TEST(MessageHandler2) { |
| 4907 | message_received = false; |
| 4908 | v8::HandleScope scope(CcTest::isolate()); |
| 4909 | CHECK(!message_received); |
| 4910 | CcTest::isolate()->AddMessageListener(check_message_2); |
| 4911 | LocalContext context; |
| 4912 | v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error" )); |
| 4913 | v8::Object::Cast(*error) |
| 4914 | ->SetPrivate(context.local(), |
| 4915 | v8::Private::ForApi(CcTest::isolate(), v8_str("hidden key" )), |
| 4916 | v8_str("hidden value" )) |
| 4917 | .FromJust(); |
| 4918 | CHECK(context->Global() |
| 4919 | ->Set(context.local(), v8_str("error" ), error) |
| 4920 | .FromJust()); |
| 4921 | CompileRun("throw error;" ); |
| 4922 | CHECK(message_received); |
| 4923 | // clear out the message listener |
| 4924 | CcTest::isolate()->RemoveMessageListeners(check_message_2); |
| 4925 | } |
| 4926 | |
| 4927 | |
| 4928 | static void check_message_3(v8::Local<v8::Message> message, |
| 4929 | v8::Local<Value> data) { |
| 4930 | CHECK(message->IsSharedCrossOrigin()); |
| 4931 | CHECK(message->GetScriptOrigin().Options().IsSharedCrossOrigin()); |
| 4932 | CHECK(message->GetScriptOrigin().Options().IsOpaque()); |
| 4933 | CHECK_EQ(6.75, message->GetScriptOrigin() |
| 4934 | .ResourceName() |
| 4935 | ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 4936 | .FromJust()); |
| 4937 | CHECK_EQ(7.40, message->GetScriptOrigin() |
| 4938 | .SourceMapUrl() |
| 4939 | ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 4940 | .FromJust()); |
| 4941 | message_received = true; |
| 4942 | } |
| 4943 | |
| 4944 | |
| 4945 | TEST(MessageHandler3) { |
| 4946 | message_received = false; |
| 4947 | v8::Isolate* isolate = CcTest::isolate(); |
| 4948 | v8::HandleScope scope(isolate); |
| 4949 | CHECK(!message_received); |
| 4950 | isolate->AddMessageListener(check_message_3); |
| 4951 | LocalContext context; |
| 4952 | v8::ScriptOrigin origin = |
| 4953 | v8::ScriptOrigin(v8_str("6.75" ), v8::Integer::New(isolate, 1), |
| 4954 | v8::Integer::New(isolate, 2), v8::True(isolate), |
| 4955 | Local<v8::Integer>(), v8_str("7.40" ), v8::True(isolate)); |
| 4956 | v8::Local<v8::Script> script = |
| 4957 | Script::Compile(context.local(), v8_str("throw 'error'" ), &origin) |
| 4958 | .ToLocalChecked(); |
| 4959 | CHECK(script->Run(context.local()).IsEmpty()); |
| 4960 | CHECK(message_received); |
| 4961 | // clear out the message listener |
| 4962 | isolate->RemoveMessageListeners(check_message_3); |
| 4963 | } |
| 4964 | |
| 4965 | |
| 4966 | static void check_message_4(v8::Local<v8::Message> message, |
| 4967 | v8::Local<Value> data) { |
| 4968 | CHECK(!message->IsSharedCrossOrigin()); |
| 4969 | CHECK_EQ(6.75, message->GetScriptOrigin() |
| 4970 | .ResourceName() |
| 4971 | ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 4972 | .FromJust()); |
| 4973 | message_received = true; |
| 4974 | } |
| 4975 | |
| 4976 | |
| 4977 | TEST(MessageHandler4) { |
| 4978 | message_received = false; |
| 4979 | v8::Isolate* isolate = CcTest::isolate(); |
| 4980 | v8::HandleScope scope(isolate); |
| 4981 | CHECK(!message_received); |
| 4982 | isolate->AddMessageListener(check_message_4); |
| 4983 | LocalContext context; |
| 4984 | v8::ScriptOrigin origin = |
| 4985 | v8::ScriptOrigin(v8_str("6.75" ), v8::Integer::New(isolate, 1), |
| 4986 | v8::Integer::New(isolate, 2), v8::False(isolate)); |
| 4987 | v8::Local<v8::Script> script = |
| 4988 | Script::Compile(context.local(), v8_str("throw 'error'" ), &origin) |
| 4989 | .ToLocalChecked(); |
| 4990 | CHECK(script->Run(context.local()).IsEmpty()); |
| 4991 | CHECK(message_received); |
| 4992 | // clear out the message listener |
| 4993 | isolate->RemoveMessageListeners(check_message_4); |
| 4994 | } |
| 4995 | |
| 4996 | |
| 4997 | static void check_message_5a(v8::Local<v8::Message> message, |
| 4998 | v8::Local<Value> data) { |
| 4999 | CHECK(message->IsSharedCrossOrigin()); |
| 5000 | CHECK_EQ(6.75, message->GetScriptOrigin() |
| 5001 | .ResourceName() |
| 5002 | ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 5003 | .FromJust()); |
| 5004 | message_received = true; |
| 5005 | } |
| 5006 | |
| 5007 | |
| 5008 | static void check_message_5b(v8::Local<v8::Message> message, |
| 5009 | v8::Local<Value> data) { |
| 5010 | CHECK(!message->IsSharedCrossOrigin()); |
| 5011 | CHECK_EQ(6.75, message->GetScriptOrigin() |
| 5012 | .ResourceName() |
| 5013 | ->NumberValue(CcTest::isolate()->GetCurrentContext()) |
| 5014 | .FromJust()); |
| 5015 | message_received = true; |
| 5016 | } |
| 5017 | |
| 5018 | |
| 5019 | TEST(MessageHandler5) { |
| 5020 | message_received = false; |
| 5021 | v8::Isolate* isolate = CcTest::isolate(); |
| 5022 | v8::HandleScope scope(isolate); |
| 5023 | CHECK(!message_received); |
| 5024 | isolate->AddMessageListener(check_message_5a); |
| 5025 | LocalContext context; |
| 5026 | v8::ScriptOrigin origin1 = |
| 5027 | v8::ScriptOrigin(v8_str("6.75" ), v8::Integer::New(isolate, 1), |
| 5028 | v8::Integer::New(isolate, 2), v8::True(isolate)); |
| 5029 | v8::Local<v8::Script> script = |
| 5030 | Script::Compile(context.local(), v8_str("throw 'error'" ), &origin1) |
| 5031 | .ToLocalChecked(); |
| 5032 | CHECK(script->Run(context.local()).IsEmpty()); |
| 5033 | CHECK(message_received); |
| 5034 | // clear out the message listener |
| 5035 | isolate->RemoveMessageListeners(check_message_5a); |
| 5036 | |
| 5037 | message_received = false; |
| 5038 | isolate->AddMessageListener(check_message_5b); |
| 5039 | v8::ScriptOrigin origin2 = |
| 5040 | v8::ScriptOrigin(v8_str("6.75" ), v8::Integer::New(isolate, 1), |
| 5041 | v8::Integer::New(isolate, 2), v8::False(isolate)); |
| 5042 | script = Script::Compile(context.local(), v8_str("throw 'error'" ), &origin2) |
| 5043 | .ToLocalChecked(); |
| 5044 | CHECK(script->Run(context.local()).IsEmpty()); |
| 5045 | CHECK(message_received); |
| 5046 | // clear out the message listener |
| 5047 | isolate->RemoveMessageListeners(check_message_5b); |
| 5048 | } |
| 5049 | |
| 5050 | |
| 5051 | THREADED_TEST(GetSetProperty) { |
| 5052 | LocalContext context; |
| 5053 | v8::Isolate* isolate = context->GetIsolate(); |
| 5054 | v8::HandleScope scope(isolate); |
| 5055 | CHECK(context->Global() |
| 5056 | ->Set(context.local(), v8_str("foo" ), v8_num(14)) |
| 5057 | .FromJust()); |
| 5058 | CHECK(context->Global() |
| 5059 | ->Set(context.local(), v8_str("12" ), v8_num(92)) |
| 5060 | .FromJust()); |
| 5061 | CHECK(context->Global() |
| 5062 | ->Set(context.local(), v8::Integer::New(isolate, 16), v8_num(32)) |
| 5063 | .FromJust()); |
| 5064 | CHECK(context->Global() |
| 5065 | ->Set(context.local(), v8_num(13), v8_num(56)) |
| 5066 | .FromJust()); |
| 5067 | Local<Value> foo = CompileRun("this.foo" ); |
| 5068 | CHECK_EQ(14, foo->Int32Value(context.local()).FromJust()); |
| 5069 | Local<Value> twelve = CompileRun("this[12]" ); |
| 5070 | CHECK_EQ(92, twelve->Int32Value(context.local()).FromJust()); |
| 5071 | Local<Value> sixteen = CompileRun("this[16]" ); |
| 5072 | CHECK_EQ(32, sixteen->Int32Value(context.local()).FromJust()); |
| 5073 | Local<Value> thirteen = CompileRun("this[13]" ); |
| 5074 | CHECK_EQ(56, thirteen->Int32Value(context.local()).FromJust()); |
| 5075 | CHECK_EQ(92, context->Global() |
| 5076 | ->Get(context.local(), v8::Integer::New(isolate, 12)) |
| 5077 | .ToLocalChecked() |
| 5078 | ->Int32Value(context.local()) |
| 5079 | .FromJust()); |
| 5080 | CHECK_EQ(92, context->Global() |
| 5081 | ->Get(context.local(), v8_str("12" )) |
| 5082 | .ToLocalChecked() |
| 5083 | ->Int32Value(context.local()) |
| 5084 | .FromJust()); |
| 5085 | CHECK_EQ(92, context->Global() |
| 5086 | ->Get(context.local(), v8_num(12)) |
| 5087 | .ToLocalChecked() |
| 5088 | ->Int32Value(context.local()) |
| 5089 | .FromJust()); |
| 5090 | CHECK_EQ(32, context->Global() |
| 5091 | ->Get(context.local(), v8::Integer::New(isolate, 16)) |
| 5092 | .ToLocalChecked() |
| 5093 | ->Int32Value(context.local()) |
| 5094 | .FromJust()); |
| 5095 | CHECK_EQ(32, context->Global() |
| 5096 | ->Get(context.local(), v8_str("16" )) |
| 5097 | .ToLocalChecked() |
| 5098 | ->Int32Value(context.local()) |
| 5099 | .FromJust()); |
| 5100 | CHECK_EQ(32, context->Global() |
| 5101 | ->Get(context.local(), v8_num(16)) |
| 5102 | .ToLocalChecked() |
| 5103 | ->Int32Value(context.local()) |
| 5104 | .FromJust()); |
| 5105 | CHECK_EQ(56, context->Global() |
| 5106 | ->Get(context.local(), v8::Integer::New(isolate, 13)) |
| 5107 | .ToLocalChecked() |
| 5108 | ->Int32Value(context.local()) |
| 5109 | .FromJust()); |
| 5110 | CHECK_EQ(56, context->Global() |
| 5111 | ->Get(context.local(), v8_str("13" )) |
| 5112 | .ToLocalChecked() |
| 5113 | ->Int32Value(context.local()) |
| 5114 | .FromJust()); |
| 5115 | CHECK_EQ(56, context->Global() |
| 5116 | ->Get(context.local(), v8_num(13)) |
| 5117 | .ToLocalChecked() |
| 5118 | ->Int32Value(context.local()) |
| 5119 | .FromJust()); |
| 5120 | } |
| 5121 | |
| 5122 | |
| 5123 | THREADED_TEST(PropertyAttributes) { |
| 5124 | LocalContext context; |
| 5125 | v8::HandleScope scope(context->GetIsolate()); |
| 5126 | // none |
| 5127 | Local<String> prop = v8_str("none" ); |
| 5128 | CHECK(context->Global()->Set(context.local(), prop, v8_num(7)).FromJust()); |
| 5129 | CHECK_EQ(v8::None, context->Global() |
| 5130 | ->GetPropertyAttributes(context.local(), prop) |
| 5131 | .FromJust()); |
| 5132 | // read-only |
| 5133 | prop = v8_str("read_only" ); |
| 5134 | context->Global() |
| 5135 | ->DefineOwnProperty(context.local(), prop, v8_num(7), v8::ReadOnly) |
| 5136 | .FromJust(); |
| 5137 | CHECK_EQ(7, context->Global() |
| 5138 | ->Get(context.local(), prop) |
| 5139 | .ToLocalChecked() |
| 5140 | ->Int32Value(context.local()) |
| 5141 | .FromJust()); |
| 5142 | CHECK_EQ(v8::ReadOnly, context->Global() |
| 5143 | ->GetPropertyAttributes(context.local(), prop) |
| 5144 | .FromJust()); |
| 5145 | CompileRun("read_only = 9" ); |
| 5146 | CHECK_EQ(7, context->Global() |
| 5147 | ->Get(context.local(), prop) |
| 5148 | .ToLocalChecked() |
| 5149 | ->Int32Value(context.local()) |
| 5150 | .FromJust()); |
| 5151 | CHECK(context->Global()->Set(context.local(), prop, v8_num(10)).FromJust()); |
| 5152 | CHECK_EQ(7, context->Global() |
| 5153 | ->Get(context.local(), prop) |
| 5154 | .ToLocalChecked() |
| 5155 | ->Int32Value(context.local()) |
| 5156 | .FromJust()); |
| 5157 | // dont-delete |
| 5158 | prop = v8_str("dont_delete" ); |
| 5159 | context->Global() |
| 5160 | ->DefineOwnProperty(context.local(), prop, v8_num(13), v8::DontDelete) |
| 5161 | .FromJust(); |
| 5162 | CHECK_EQ(13, context->Global() |
| 5163 | ->Get(context.local(), prop) |
| 5164 | .ToLocalChecked() |
| 5165 | ->Int32Value(context.local()) |
| 5166 | .FromJust()); |
| 5167 | CompileRun("delete dont_delete" ); |
| 5168 | CHECK_EQ(13, context->Global() |
| 5169 | ->Get(context.local(), prop) |
| 5170 | .ToLocalChecked() |
| 5171 | ->Int32Value(context.local()) |
| 5172 | .FromJust()); |
| 5173 | CHECK_EQ(v8::DontDelete, context->Global() |
| 5174 | ->GetPropertyAttributes(context.local(), prop) |
| 5175 | .FromJust()); |
| 5176 | // dont-enum |
| 5177 | prop = v8_str("dont_enum" ); |
| 5178 | context->Global() |
| 5179 | ->DefineOwnProperty(context.local(), prop, v8_num(28), v8::DontEnum) |
| 5180 | .FromJust(); |
| 5181 | CHECK_EQ(v8::DontEnum, context->Global() |
| 5182 | ->GetPropertyAttributes(context.local(), prop) |
| 5183 | .FromJust()); |
| 5184 | // absent |
| 5185 | prop = v8_str("absent" ); |
| 5186 | CHECK_EQ(v8::None, context->Global() |
| 5187 | ->GetPropertyAttributes(context.local(), prop) |
| 5188 | .FromJust()); |
| 5189 | Local<Value> fake_prop = v8_num(1); |
| 5190 | CHECK_EQ(v8::None, context->Global() |
| 5191 | ->GetPropertyAttributes(context.local(), fake_prop) |
| 5192 | .FromJust()); |
| 5193 | // exception |
| 5194 | TryCatch try_catch(context->GetIsolate()); |
| 5195 | Local<Value> exception = |
| 5196 | CompileRun("({ toString: function() { throw 'exception';} })" ); |
| 5197 | CHECK(context->Global() |
| 5198 | ->GetPropertyAttributes(context.local(), exception) |
| 5199 | .IsNothing()); |
| 5200 | CHECK(try_catch.HasCaught()); |
| 5201 | String::Utf8Value exception_value(context->GetIsolate(), |
| 5202 | try_catch.Exception()); |
| 5203 | CHECK_EQ(0, strcmp("exception" , *exception_value)); |
| 5204 | try_catch.Reset(); |
| 5205 | } |
| 5206 | |
| 5207 | |
| 5208 | THREADED_TEST(Array) { |
| 5209 | LocalContext context; |
| 5210 | v8::HandleScope scope(context->GetIsolate()); |
| 5211 | Local<v8::Array> array = v8::Array::New(context->GetIsolate()); |
| 5212 | CHECK_EQ(0u, array->Length()); |
| 5213 | CHECK(array->Get(context.local(), 0).ToLocalChecked()->IsUndefined()); |
| 5214 | CHECK(!array->Has(context.local(), 0).FromJust()); |
| 5215 | CHECK(array->Get(context.local(), 100).ToLocalChecked()->IsUndefined()); |
| 5216 | CHECK(!array->Has(context.local(), 100).FromJust()); |
| 5217 | CHECK(array->Set(context.local(), 2, v8_num(7)).FromJust()); |
| 5218 | CHECK_EQ(3u, array->Length()); |
| 5219 | CHECK(!array->Has(context.local(), 0).FromJust()); |
| 5220 | CHECK(!array->Has(context.local(), 1).FromJust()); |
| 5221 | CHECK(array->Has(context.local(), 2).FromJust()); |
| 5222 | CHECK_EQ(7, array->Get(context.local(), 2) |
| 5223 | .ToLocalChecked() |
| 5224 | ->Int32Value(context.local()) |
| 5225 | .FromJust()); |
| 5226 | Local<Value> obj = CompileRun("[1, 2, 3]" ); |
| 5227 | Local<v8::Array> arr = obj.As<v8::Array>(); |
| 5228 | CHECK_EQ(3u, arr->Length()); |
| 5229 | CHECK_EQ(1, arr->Get(context.local(), 0) |
| 5230 | .ToLocalChecked() |
| 5231 | ->Int32Value(context.local()) |
| 5232 | .FromJust()); |
| 5233 | CHECK_EQ(2, arr->Get(context.local(), 1) |
| 5234 | .ToLocalChecked() |
| 5235 | ->Int32Value(context.local()) |
| 5236 | .FromJust()); |
| 5237 | CHECK_EQ(3, arr->Get(context.local(), 2) |
| 5238 | .ToLocalChecked() |
| 5239 | ->Int32Value(context.local()) |
| 5240 | .FromJust()); |
| 5241 | array = v8::Array::New(context->GetIsolate(), 27); |
| 5242 | CHECK_EQ(27u, array->Length()); |
| 5243 | array = v8::Array::New(context->GetIsolate(), -27); |
| 5244 | CHECK_EQ(0u, array->Length()); |
| 5245 | |
| 5246 | std::vector<Local<Value>> vector = {v8_num(1), v8_num(2), v8_num(3)}; |
| 5247 | array = v8::Array::New(context->GetIsolate(), vector.data(), vector.size()); |
| 5248 | CHECK_EQ(vector.size(), array->Length()); |
| 5249 | CHECK_EQ(1, arr->Get(context.local(), 0) |
| 5250 | .ToLocalChecked() |
| 5251 | ->Int32Value(context.local()) |
| 5252 | .FromJust()); |
| 5253 | CHECK_EQ(2, arr->Get(context.local(), 1) |
| 5254 | .ToLocalChecked() |
| 5255 | ->Int32Value(context.local()) |
| 5256 | .FromJust()); |
| 5257 | CHECK_EQ(3, arr->Get(context.local(), 2) |
| 5258 | .ToLocalChecked() |
| 5259 | ->Int32Value(context.local()) |
| 5260 | .FromJust()); |
| 5261 | } |
| 5262 | |
| 5263 | |
| 5264 | void HandleF(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 5265 | v8::EscapableHandleScope scope(args.GetIsolate()); |
| 5266 | ApiTestFuzzer::Fuzz(); |
| 5267 | Local<v8::Array> result = v8::Array::New(args.GetIsolate(), args.Length()); |
| 5268 | for (int i = 0; i < args.Length(); i++) { |
| 5269 | CHECK(result->Set(CcTest::isolate()->GetCurrentContext(), i, args[i]) |
| 5270 | .FromJust()); |
| 5271 | } |
| 5272 | args.GetReturnValue().Set(scope.Escape(result)); |
| 5273 | } |
| 5274 | |
| 5275 | |
| 5276 | THREADED_TEST(Vector) { |
| 5277 | v8::Isolate* isolate = CcTest::isolate(); |
| 5278 | v8::HandleScope scope(isolate); |
| 5279 | Local<ObjectTemplate> global = ObjectTemplate::New(isolate); |
| 5280 | global->Set(v8_str("f" ), v8::FunctionTemplate::New(isolate, HandleF)); |
| 5281 | LocalContext context(nullptr, global); |
| 5282 | |
| 5283 | const char* fun = "f()" ; |
| 5284 | Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); |
| 5285 | CHECK_EQ(0u, a0->Length()); |
| 5286 | |
| 5287 | const char* fun2 = "f(11)" ; |
| 5288 | Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); |
| 5289 | CHECK_EQ(1u, a1->Length()); |
| 5290 | CHECK_EQ(11, a1->Get(context.local(), 0) |
| 5291 | .ToLocalChecked() |
| 5292 | ->Int32Value(context.local()) |
| 5293 | .FromJust()); |
| 5294 | |
| 5295 | const char* fun3 = "f(12, 13)" ; |
| 5296 | Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); |
| 5297 | CHECK_EQ(2u, a2->Length()); |
| 5298 | CHECK_EQ(12, a2->Get(context.local(), 0) |
| 5299 | .ToLocalChecked() |
| 5300 | ->Int32Value(context.local()) |
| 5301 | .FromJust()); |
| 5302 | CHECK_EQ(13, a2->Get(context.local(), 1) |
| 5303 | .ToLocalChecked() |
| 5304 | ->Int32Value(context.local()) |
| 5305 | .FromJust()); |
| 5306 | |
| 5307 | const char* fun4 = "f(14, 15, 16)" ; |
| 5308 | Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); |
| 5309 | CHECK_EQ(3u, a3->Length()); |
| 5310 | CHECK_EQ(14, a3->Get(context.local(), 0) |
| 5311 | .ToLocalChecked() |
| 5312 | ->Int32Value(context.local()) |
| 5313 | .FromJust()); |
| 5314 | CHECK_EQ(15, a3->Get(context.local(), 1) |
| 5315 | .ToLocalChecked() |
| 5316 | ->Int32Value(context.local()) |
| 5317 | .FromJust()); |
| 5318 | CHECK_EQ(16, a3->Get(context.local(), 2) |
| 5319 | .ToLocalChecked() |
| 5320 | ->Int32Value(context.local()) |
| 5321 | .FromJust()); |
| 5322 | |
| 5323 | const char* fun5 = "f(17, 18, 19, 20)" ; |
| 5324 | Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); |
| 5325 | CHECK_EQ(4u, a4->Length()); |
| 5326 | CHECK_EQ(17, a4->Get(context.local(), 0) |
| 5327 | .ToLocalChecked() |
| 5328 | ->Int32Value(context.local()) |
| 5329 | .FromJust()); |
| 5330 | CHECK_EQ(18, a4->Get(context.local(), 1) |
| 5331 | .ToLocalChecked() |
| 5332 | ->Int32Value(context.local()) |
| 5333 | .FromJust()); |
| 5334 | CHECK_EQ(19, a4->Get(context.local(), 2) |
| 5335 | .ToLocalChecked() |
| 5336 | ->Int32Value(context.local()) |
| 5337 | .FromJust()); |
| 5338 | CHECK_EQ(20, a4->Get(context.local(), 3) |
| 5339 | .ToLocalChecked() |
| 5340 | ->Int32Value(context.local()) |
| 5341 | .FromJust()); |
| 5342 | } |
| 5343 | |
| 5344 | |
| 5345 | THREADED_TEST(FunctionCall) { |
| 5346 | LocalContext context; |
| 5347 | v8::Isolate* isolate = context->GetIsolate(); |
| 5348 | v8::HandleScope scope(isolate); |
| 5349 | CompileRun( |
| 5350 | "function Foo() {" |
| 5351 | " var result = [];" |
| 5352 | " for (var i = 0; i < arguments.length; i++) {" |
| 5353 | " result.push(arguments[i]);" |
| 5354 | " }" |
| 5355 | " return result;" |
| 5356 | "}" |
| 5357 | "function ReturnThisSloppy() {" |
| 5358 | " return this;" |
| 5359 | "}" |
| 5360 | "function ReturnThisStrict() {" |
| 5361 | " 'use strict';" |
| 5362 | " return this;" |
| 5363 | "}" ); |
| 5364 | Local<Function> Foo = Local<Function>::Cast( |
| 5365 | context->Global()->Get(context.local(), v8_str("Foo" )).ToLocalChecked()); |
| 5366 | Local<Function> ReturnThisSloppy = Local<Function>::Cast( |
| 5367 | context->Global() |
| 5368 | ->Get(context.local(), v8_str("ReturnThisSloppy" )) |
| 5369 | .ToLocalChecked()); |
| 5370 | Local<Function> ReturnThisStrict = Local<Function>::Cast( |
| 5371 | context->Global() |
| 5372 | ->Get(context.local(), v8_str("ReturnThisStrict" )) |
| 5373 | .ToLocalChecked()); |
| 5374 | |
| 5375 | v8::Local<Value>* args0 = nullptr; |
| 5376 | Local<v8::Array> a0 = Local<v8::Array>::Cast( |
| 5377 | Foo->Call(context.local(), Foo, 0, args0).ToLocalChecked()); |
| 5378 | CHECK_EQ(0u, a0->Length()); |
| 5379 | |
| 5380 | v8::Local<Value> args1[] = {v8_num(1.1)}; |
| 5381 | Local<v8::Array> a1 = Local<v8::Array>::Cast( |
| 5382 | Foo->Call(context.local(), Foo, 1, args1).ToLocalChecked()); |
| 5383 | CHECK_EQ(1u, a1->Length()); |
| 5384 | CHECK_EQ(1.1, a1->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5385 | .ToLocalChecked() |
| 5386 | ->NumberValue(context.local()) |
| 5387 | .FromJust()); |
| 5388 | |
| 5389 | v8::Local<Value> args2[] = {v8_num(2.2), v8_num(3.3)}; |
| 5390 | Local<v8::Array> a2 = Local<v8::Array>::Cast( |
| 5391 | Foo->Call(context.local(), Foo, 2, args2).ToLocalChecked()); |
| 5392 | CHECK_EQ(2u, a2->Length()); |
| 5393 | CHECK_EQ(2.2, a2->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5394 | .ToLocalChecked() |
| 5395 | ->NumberValue(context.local()) |
| 5396 | .FromJust()); |
| 5397 | CHECK_EQ(3.3, a2->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 5398 | .ToLocalChecked() |
| 5399 | ->NumberValue(context.local()) |
| 5400 | .FromJust()); |
| 5401 | |
| 5402 | v8::Local<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)}; |
| 5403 | Local<v8::Array> a3 = Local<v8::Array>::Cast( |
| 5404 | Foo->Call(context.local(), Foo, 3, args3).ToLocalChecked()); |
| 5405 | CHECK_EQ(3u, a3->Length()); |
| 5406 | CHECK_EQ(4.4, a3->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5407 | .ToLocalChecked() |
| 5408 | ->NumberValue(context.local()) |
| 5409 | .FromJust()); |
| 5410 | CHECK_EQ(5.5, a3->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 5411 | .ToLocalChecked() |
| 5412 | ->NumberValue(context.local()) |
| 5413 | .FromJust()); |
| 5414 | CHECK_EQ(6.6, a3->Get(context.local(), v8::Integer::New(isolate, 2)) |
| 5415 | .ToLocalChecked() |
| 5416 | ->NumberValue(context.local()) |
| 5417 | .FromJust()); |
| 5418 | |
| 5419 | v8::Local<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9), |
| 5420 | v8_num(10.11)}; |
| 5421 | Local<v8::Array> a4 = Local<v8::Array>::Cast( |
| 5422 | Foo->Call(context.local(), Foo, 4, args4).ToLocalChecked()); |
| 5423 | CHECK_EQ(4u, a4->Length()); |
| 5424 | CHECK_EQ(7.7, a4->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5425 | .ToLocalChecked() |
| 5426 | ->NumberValue(context.local()) |
| 5427 | .FromJust()); |
| 5428 | CHECK_EQ(8.8, a4->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 5429 | .ToLocalChecked() |
| 5430 | ->NumberValue(context.local()) |
| 5431 | .FromJust()); |
| 5432 | CHECK_EQ(9.9, a4->Get(context.local(), v8::Integer::New(isolate, 2)) |
| 5433 | .ToLocalChecked() |
| 5434 | ->NumberValue(context.local()) |
| 5435 | .FromJust()); |
| 5436 | CHECK_EQ(10.11, a4->Get(context.local(), v8::Integer::New(isolate, 3)) |
| 5437 | .ToLocalChecked() |
| 5438 | ->NumberValue(context.local()) |
| 5439 | .FromJust()); |
| 5440 | |
| 5441 | Local<v8::Value> r1 = |
| 5442 | ReturnThisSloppy |
| 5443 | ->Call(context.local(), v8::Undefined(isolate), 0, nullptr) |
| 5444 | .ToLocalChecked(); |
| 5445 | CHECK(r1->StrictEquals(context->Global())); |
| 5446 | Local<v8::Value> r2 = |
| 5447 | ReturnThisSloppy->Call(context.local(), v8::Null(isolate), 0, nullptr) |
| 5448 | .ToLocalChecked(); |
| 5449 | CHECK(r2->StrictEquals(context->Global())); |
| 5450 | Local<v8::Value> r3 = |
| 5451 | ReturnThisSloppy->Call(context.local(), v8_num(42), 0, nullptr) |
| 5452 | .ToLocalChecked(); |
| 5453 | CHECK(r3->IsNumberObject()); |
| 5454 | CHECK_EQ(42.0, r3.As<v8::NumberObject>()->ValueOf()); |
| 5455 | Local<v8::Value> r4 = |
| 5456 | ReturnThisSloppy->Call(context.local(), v8_str("hello" ), 0, nullptr) |
| 5457 | .ToLocalChecked(); |
| 5458 | CHECK(r4->IsStringObject()); |
| 5459 | CHECK(r4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello" ))); |
| 5460 | Local<v8::Value> r5 = |
| 5461 | ReturnThisSloppy->Call(context.local(), v8::True(isolate), 0, nullptr) |
| 5462 | .ToLocalChecked(); |
| 5463 | CHECK(r5->IsBooleanObject()); |
| 5464 | CHECK(r5.As<v8::BooleanObject>()->ValueOf()); |
| 5465 | |
| 5466 | Local<v8::Value> r6 = |
| 5467 | ReturnThisStrict |
| 5468 | ->Call(context.local(), v8::Undefined(isolate), 0, nullptr) |
| 5469 | .ToLocalChecked(); |
| 5470 | CHECK(r6->IsUndefined()); |
| 5471 | Local<v8::Value> r7 = |
| 5472 | ReturnThisStrict->Call(context.local(), v8::Null(isolate), 0, nullptr) |
| 5473 | .ToLocalChecked(); |
| 5474 | CHECK(r7->IsNull()); |
| 5475 | Local<v8::Value> r8 = |
| 5476 | ReturnThisStrict->Call(context.local(), v8_num(42), 0, nullptr) |
| 5477 | .ToLocalChecked(); |
| 5478 | CHECK(r8->StrictEquals(v8_num(42))); |
| 5479 | Local<v8::Value> r9 = |
| 5480 | ReturnThisStrict->Call(context.local(), v8_str("hello" ), 0, nullptr) |
| 5481 | .ToLocalChecked(); |
| 5482 | CHECK(r9->StrictEquals(v8_str("hello" ))); |
| 5483 | Local<v8::Value> r10 = |
| 5484 | ReturnThisStrict->Call(context.local(), v8::True(isolate), 0, nullptr) |
| 5485 | .ToLocalChecked(); |
| 5486 | CHECK(r10->StrictEquals(v8::True(isolate))); |
| 5487 | } |
| 5488 | |
| 5489 | |
| 5490 | THREADED_TEST(ConstructCall) { |
| 5491 | LocalContext context; |
| 5492 | v8::Isolate* isolate = context->GetIsolate(); |
| 5493 | v8::HandleScope scope(isolate); |
| 5494 | CompileRun( |
| 5495 | "function Foo() {" |
| 5496 | " var result = [];" |
| 5497 | " for (var i = 0; i < arguments.length; i++) {" |
| 5498 | " result.push(arguments[i]);" |
| 5499 | " }" |
| 5500 | " return result;" |
| 5501 | "}" ); |
| 5502 | Local<Function> Foo = Local<Function>::Cast( |
| 5503 | context->Global()->Get(context.local(), v8_str("Foo" )).ToLocalChecked()); |
| 5504 | |
| 5505 | v8::Local<Value>* args0 = nullptr; |
| 5506 | Local<v8::Array> a0 = Local<v8::Array>::Cast( |
| 5507 | Foo->NewInstance(context.local(), 0, args0).ToLocalChecked()); |
| 5508 | CHECK_EQ(0u, a0->Length()); |
| 5509 | |
| 5510 | v8::Local<Value> args1[] = {v8_num(1.1)}; |
| 5511 | Local<v8::Array> a1 = Local<v8::Array>::Cast( |
| 5512 | Foo->NewInstance(context.local(), 1, args1).ToLocalChecked()); |
| 5513 | CHECK_EQ(1u, a1->Length()); |
| 5514 | CHECK_EQ(1.1, a1->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5515 | .ToLocalChecked() |
| 5516 | ->NumberValue(context.local()) |
| 5517 | .FromJust()); |
| 5518 | |
| 5519 | v8::Local<Value> args2[] = {v8_num(2.2), v8_num(3.3)}; |
| 5520 | Local<v8::Array> a2 = Local<v8::Array>::Cast( |
| 5521 | Foo->NewInstance(context.local(), 2, args2).ToLocalChecked()); |
| 5522 | CHECK_EQ(2u, a2->Length()); |
| 5523 | CHECK_EQ(2.2, a2->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5524 | .ToLocalChecked() |
| 5525 | ->NumberValue(context.local()) |
| 5526 | .FromJust()); |
| 5527 | CHECK_EQ(3.3, a2->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 5528 | .ToLocalChecked() |
| 5529 | ->NumberValue(context.local()) |
| 5530 | .FromJust()); |
| 5531 | |
| 5532 | v8::Local<Value> args3[] = {v8_num(4.4), v8_num(5.5), v8_num(6.6)}; |
| 5533 | Local<v8::Array> a3 = Local<v8::Array>::Cast( |
| 5534 | Foo->NewInstance(context.local(), 3, args3).ToLocalChecked()); |
| 5535 | CHECK_EQ(3u, a3->Length()); |
| 5536 | CHECK_EQ(4.4, a3->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5537 | .ToLocalChecked() |
| 5538 | ->NumberValue(context.local()) |
| 5539 | .FromJust()); |
| 5540 | CHECK_EQ(5.5, a3->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 5541 | .ToLocalChecked() |
| 5542 | ->NumberValue(context.local()) |
| 5543 | .FromJust()); |
| 5544 | CHECK_EQ(6.6, a3->Get(context.local(), v8::Integer::New(isolate, 2)) |
| 5545 | .ToLocalChecked() |
| 5546 | ->NumberValue(context.local()) |
| 5547 | .FromJust()); |
| 5548 | |
| 5549 | v8::Local<Value> args4[] = {v8_num(7.7), v8_num(8.8), v8_num(9.9), |
| 5550 | v8_num(10.11)}; |
| 5551 | Local<v8::Array> a4 = Local<v8::Array>::Cast( |
| 5552 | Foo->NewInstance(context.local(), 4, args4).ToLocalChecked()); |
| 5553 | CHECK_EQ(4u, a4->Length()); |
| 5554 | CHECK_EQ(7.7, a4->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 5555 | .ToLocalChecked() |
| 5556 | ->NumberValue(context.local()) |
| 5557 | .FromJust()); |
| 5558 | CHECK_EQ(8.8, a4->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 5559 | .ToLocalChecked() |
| 5560 | ->NumberValue(context.local()) |
| 5561 | .FromJust()); |
| 5562 | CHECK_EQ(9.9, a4->Get(context.local(), v8::Integer::New(isolate, 2)) |
| 5563 | .ToLocalChecked() |
| 5564 | ->NumberValue(context.local()) |
| 5565 | .FromJust()); |
| 5566 | CHECK_EQ(10.11, a4->Get(context.local(), v8::Integer::New(isolate, 3)) |
| 5567 | .ToLocalChecked() |
| 5568 | ->NumberValue(context.local()) |
| 5569 | .FromJust()); |
| 5570 | } |
| 5571 | |
| 5572 | |
| 5573 | THREADED_TEST(ConversionNumber) { |
| 5574 | LocalContext env; |
| 5575 | v8::Isolate* isolate = env->GetIsolate(); |
| 5576 | v8::HandleScope scope(isolate); |
| 5577 | // Very large number. |
| 5578 | CompileRun("var obj = Math.pow(2,32) * 1237;" ); |
| 5579 | Local<Value> obj = |
| 5580 | env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5581 | CHECK_EQ(5312874545152.0, |
| 5582 | obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5583 | CHECK_EQ(0, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5584 | CHECK_EQ(0, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5585 | // Large number. |
| 5586 | CompileRun("var obj = -1234567890123;" ); |
| 5587 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5588 | CHECK_EQ(-1234567890123.0, |
| 5589 | obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5590 | CHECK_EQ(-1912276171, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5591 | CHECK_EQ(2382691125, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5592 | // Small positive integer. |
| 5593 | CompileRun("var obj = 42;" ); |
| 5594 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5595 | CHECK_EQ(42.0, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5596 | CHECK_EQ(42, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5597 | CHECK_EQ(42, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5598 | // Negative integer. |
| 5599 | CompileRun("var obj = -37;" ); |
| 5600 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5601 | CHECK_EQ(-37.0, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5602 | CHECK_EQ(-37, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5603 | CHECK_EQ(4294967259, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5604 | // Positive non-int32 integer. |
| 5605 | CompileRun("var obj = 0x81234567;" ); |
| 5606 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5607 | CHECK_EQ(2166572391.0, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5608 | CHECK_EQ(-2128394905, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5609 | CHECK_EQ(2166572391, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5610 | // Fraction. |
| 5611 | CompileRun("var obj = 42.3;" ); |
| 5612 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5613 | CHECK_EQ(42.3, obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5614 | CHECK_EQ(42, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5615 | CHECK_EQ(42, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5616 | // Large negative fraction. |
| 5617 | CompileRun("var obj = -5726623061.75;" ); |
| 5618 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5619 | CHECK_EQ(-5726623061.75, |
| 5620 | obj->ToNumber(env.local()).ToLocalChecked()->Value()); |
| 5621 | CHECK_EQ(-1431655765, obj->ToInt32(env.local()).ToLocalChecked()->Value()); |
| 5622 | CHECK_EQ(2863311531, obj->ToUint32(env.local()).ToLocalChecked()->Value()); |
| 5623 | } |
| 5624 | |
| 5625 | |
| 5626 | THREADED_TEST(isNumberType) { |
| 5627 | LocalContext env; |
| 5628 | v8::HandleScope scope(env->GetIsolate()); |
| 5629 | // Very large number. |
| 5630 | CompileRun("var obj = Math.pow(2,32) * 1237;" ); |
| 5631 | Local<Value> obj = |
| 5632 | env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5633 | CHECK(!obj->IsInt32()); |
| 5634 | CHECK(!obj->IsUint32()); |
| 5635 | // Large negative number. |
| 5636 | CompileRun("var obj = -1234567890123;" ); |
| 5637 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5638 | CHECK(!obj->IsInt32()); |
| 5639 | CHECK(!obj->IsUint32()); |
| 5640 | // Small positive integer. |
| 5641 | CompileRun("var obj = 42;" ); |
| 5642 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5643 | CHECK(obj->IsInt32()); |
| 5644 | CHECK(obj->IsUint32()); |
| 5645 | // Negative integer. |
| 5646 | CompileRun("var obj = -37;" ); |
| 5647 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5648 | CHECK(obj->IsInt32()); |
| 5649 | CHECK(!obj->IsUint32()); |
| 5650 | // Positive non-int32 integer. |
| 5651 | CompileRun("var obj = 0x81234567;" ); |
| 5652 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5653 | CHECK(!obj->IsInt32()); |
| 5654 | CHECK(obj->IsUint32()); |
| 5655 | // Fraction. |
| 5656 | CompileRun("var obj = 42.3;" ); |
| 5657 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5658 | CHECK(!obj->IsInt32()); |
| 5659 | CHECK(!obj->IsUint32()); |
| 5660 | // Large negative fraction. |
| 5661 | CompileRun("var obj = -5726623061.75;" ); |
| 5662 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5663 | CHECK(!obj->IsInt32()); |
| 5664 | CHECK(!obj->IsUint32()); |
| 5665 | // Positive zero |
| 5666 | CompileRun("var obj = 0.0;" ); |
| 5667 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5668 | CHECK(obj->IsInt32()); |
| 5669 | CHECK(obj->IsUint32()); |
| 5670 | // Negative zero |
| 5671 | CompileRun("var obj = -0.0;" ); |
| 5672 | obj = env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5673 | CHECK(!obj->IsInt32()); |
| 5674 | CHECK(!obj->IsUint32()); |
| 5675 | } |
| 5676 | |
| 5677 | THREADED_TEST(IntegerType) { |
| 5678 | LocalContext env; |
| 5679 | v8::HandleScope scope(env->GetIsolate()); |
| 5680 | Local<Value> result; |
| 5681 | |
| 5682 | // Small positive integer |
| 5683 | result = CompileRun("42;" ); |
| 5684 | CHECK(result->IsNumber()); |
| 5685 | CHECK_EQ(42, result.As<v8::Integer>()->Value()); |
| 5686 | // Small negative integer |
| 5687 | result = CompileRun("-42;" ); |
| 5688 | CHECK(result->IsNumber()); |
| 5689 | CHECK_EQ(-42, result.As<v8::Integer>()->Value()); |
| 5690 | // Positive non-int32 integer |
| 5691 | result = CompileRun("1099511627776;" ); |
| 5692 | CHECK(result->IsNumber()); |
| 5693 | CHECK_EQ(1099511627776, result.As<v8::Integer>()->Value()); |
| 5694 | // Negative non-int32 integer |
| 5695 | result = CompileRun("-1099511627776;" ); |
| 5696 | CHECK(result->IsNumber()); |
| 5697 | CHECK_EQ(-1099511627776, result.As<v8::Integer>()->Value()); |
| 5698 | // Positive non-integer |
| 5699 | result = CompileRun("3.14;" ); |
| 5700 | CHECK(result->IsNumber()); |
| 5701 | CHECK_EQ(3, result.As<v8::Integer>()->Value()); |
| 5702 | // Negative non-integer |
| 5703 | result = CompileRun("-3.14;" ); |
| 5704 | CHECK(result->IsNumber()); |
| 5705 | CHECK_EQ(-3, result.As<v8::Integer>()->Value()); |
| 5706 | } |
| 5707 | |
| 5708 | static void CheckUncle(v8::Isolate* isolate, v8::TryCatch* try_catch) { |
| 5709 | CHECK(try_catch->HasCaught()); |
| 5710 | String::Utf8Value str_value(isolate, try_catch->Exception()); |
| 5711 | CHECK_EQ(0, strcmp(*str_value, "uncle?" )); |
| 5712 | try_catch->Reset(); |
| 5713 | } |
| 5714 | |
| 5715 | THREADED_TEST(ConversionException) { |
| 5716 | LocalContext env; |
| 5717 | v8::Isolate* isolate = env->GetIsolate(); |
| 5718 | v8::HandleScope scope(isolate); |
| 5719 | CompileRun( |
| 5720 | "function TestClass() { };" |
| 5721 | "TestClass.prototype.toString = function () { throw 'uncle?'; };" |
| 5722 | "var obj = new TestClass();" ); |
| 5723 | Local<Value> obj = |
| 5724 | env->Global()->Get(env.local(), v8_str("obj" )).ToLocalChecked(); |
| 5725 | |
| 5726 | v8::TryCatch try_catch(isolate); |
| 5727 | |
| 5728 | CHECK(obj->ToString(env.local()).IsEmpty()); |
| 5729 | CheckUncle(isolate, &try_catch); |
| 5730 | |
| 5731 | CHECK(obj->ToNumber(env.local()).IsEmpty()); |
| 5732 | CheckUncle(isolate, &try_catch); |
| 5733 | |
| 5734 | CHECK(obj->ToInteger(env.local()).IsEmpty()); |
| 5735 | CheckUncle(isolate, &try_catch); |
| 5736 | |
| 5737 | CHECK(obj->ToUint32(env.local()).IsEmpty()); |
| 5738 | CheckUncle(isolate, &try_catch); |
| 5739 | |
| 5740 | CHECK(obj->ToInt32(env.local()).IsEmpty()); |
| 5741 | CheckUncle(isolate, &try_catch); |
| 5742 | |
| 5743 | CHECK(v8::Undefined(isolate)->ToObject(env.local()).IsEmpty()); |
| 5744 | CHECK(try_catch.HasCaught()); |
| 5745 | try_catch.Reset(); |
| 5746 | |
| 5747 | CHECK(obj->Int32Value(env.local()).IsNothing()); |
| 5748 | CheckUncle(isolate, &try_catch); |
| 5749 | |
| 5750 | CHECK(obj->Uint32Value(env.local()).IsNothing()); |
| 5751 | CheckUncle(isolate, &try_catch); |
| 5752 | |
| 5753 | CHECK(obj->NumberValue(env.local()).IsNothing()); |
| 5754 | CheckUncle(isolate, &try_catch); |
| 5755 | |
| 5756 | CHECK(obj->IntegerValue(env.local()).IsNothing()); |
| 5757 | CheckUncle(isolate, &try_catch); |
| 5758 | } |
| 5759 | |
| 5760 | |
| 5761 | void ThrowFromC(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 5762 | ApiTestFuzzer::Fuzz(); |
| 5763 | args.GetIsolate()->ThrowException(v8_str("konto" )); |
| 5764 | } |
| 5765 | |
| 5766 | |
| 5767 | void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 5768 | if (args.Length() < 1) { |
| 5769 | args.GetReturnValue().Set(false); |
| 5770 | return; |
| 5771 | } |
| 5772 | v8::HandleScope scope(args.GetIsolate()); |
| 5773 | v8::TryCatch try_catch(args.GetIsolate()); |
| 5774 | Local<Value> result = |
| 5775 | CompileRun(args[0] |
| 5776 | ->ToString(args.GetIsolate()->GetCurrentContext()) |
| 5777 | .ToLocalChecked()); |
| 5778 | CHECK(!try_catch.HasCaught() || result.IsEmpty()); |
| 5779 | args.GetReturnValue().Set(try_catch.HasCaught()); |
| 5780 | } |
| 5781 | |
| 5782 | |
| 5783 | THREADED_TEST(APICatch) { |
| 5784 | v8::Isolate* isolate = CcTest::isolate(); |
| 5785 | v8::HandleScope scope(isolate); |
| 5786 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 5787 | templ->Set(v8_str("ThrowFromC" ), |
| 5788 | v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| 5789 | LocalContext context(nullptr, templ); |
| 5790 | CompileRun( |
| 5791 | "var thrown = false;" |
| 5792 | "try {" |
| 5793 | " ThrowFromC();" |
| 5794 | "} catch (e) {" |
| 5795 | " thrown = true;" |
| 5796 | "}" ); |
| 5797 | Local<Value> thrown = context->Global() |
| 5798 | ->Get(context.local(), v8_str("thrown" )) |
| 5799 | .ToLocalChecked(); |
| 5800 | CHECK(thrown->BooleanValue(isolate)); |
| 5801 | } |
| 5802 | |
| 5803 | |
| 5804 | THREADED_TEST(APIThrowTryCatch) { |
| 5805 | v8::Isolate* isolate = CcTest::isolate(); |
| 5806 | v8::HandleScope scope(isolate); |
| 5807 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 5808 | templ->Set(v8_str("ThrowFromC" ), |
| 5809 | v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| 5810 | LocalContext context(nullptr, templ); |
| 5811 | v8::TryCatch try_catch(isolate); |
| 5812 | CompileRun("ThrowFromC();" ); |
| 5813 | CHECK(try_catch.HasCaught()); |
| 5814 | } |
| 5815 | |
| 5816 | |
| 5817 | // Test that a try-finally block doesn't shadow a try-catch block |
| 5818 | // when setting up an external handler. |
| 5819 | // |
| 5820 | // BUG(271): Some of the exception propagation does not work on the |
| 5821 | // ARM simulator because the simulator separates the C++ stack and the |
| 5822 | // JS stack. This test therefore fails on the simulator. The test is |
| 5823 | // not threaded to allow the threading tests to run on the simulator. |
| 5824 | TEST(TryCatchInTryFinally) { |
| 5825 | v8::Isolate* isolate = CcTest::isolate(); |
| 5826 | v8::HandleScope scope(isolate); |
| 5827 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 5828 | templ->Set(v8_str("CCatcher" ), v8::FunctionTemplate::New(isolate, CCatcher)); |
| 5829 | LocalContext context(nullptr, templ); |
| 5830 | Local<Value> result = CompileRun( |
| 5831 | "try {" |
| 5832 | " try {" |
| 5833 | " CCatcher('throw 7;');" |
| 5834 | " } finally {" |
| 5835 | " }" |
| 5836 | "} catch (e) {" |
| 5837 | "}" ); |
| 5838 | CHECK(result->IsTrue()); |
| 5839 | } |
| 5840 | |
| 5841 | |
| 5842 | static void check_custom_error_tostring(v8::Local<v8::Message> message, |
| 5843 | v8::Local<v8::Value> data) { |
| 5844 | const char* uncaught_error = "Uncaught MyError toString" ; |
| 5845 | CHECK(message->Get() |
| 5846 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 5847 | v8_str(uncaught_error)) |
| 5848 | .FromJust()); |
| 5849 | } |
| 5850 | |
| 5851 | |
| 5852 | TEST(CustomErrorToString) { |
| 5853 | LocalContext context; |
| 5854 | v8::HandleScope scope(context->GetIsolate()); |
| 5855 | context->GetIsolate()->AddMessageListener(check_custom_error_tostring); |
| 5856 | CompileRun( |
| 5857 | "function MyError(name, message) { " |
| 5858 | " this.name = name; " |
| 5859 | " this.message = message; " |
| 5860 | "} " |
| 5861 | "MyError.prototype = Object.create(Error.prototype); " |
| 5862 | "MyError.prototype.toString = function() { " |
| 5863 | " return 'MyError toString'; " |
| 5864 | "}; " |
| 5865 | "throw new MyError('my name', 'my message'); " ); |
| 5866 | context->GetIsolate()->RemoveMessageListeners(check_custom_error_tostring); |
| 5867 | } |
| 5868 | |
| 5869 | |
| 5870 | static void check_custom_error_message(v8::Local<v8::Message> message, |
| 5871 | v8::Local<v8::Value> data) { |
| 5872 | const char* uncaught_error = "Uncaught MyError: my message" ; |
| 5873 | printf("%s\n" , *v8::String::Utf8Value(CcTest::isolate(), message->Get())); |
| 5874 | CHECK(message->Get() |
| 5875 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 5876 | v8_str(uncaught_error)) |
| 5877 | .FromJust()); |
| 5878 | } |
| 5879 | |
| 5880 | |
| 5881 | TEST(CustomErrorMessage) { |
| 5882 | LocalContext context; |
| 5883 | v8::HandleScope scope(context->GetIsolate()); |
| 5884 | context->GetIsolate()->AddMessageListener(check_custom_error_message); |
| 5885 | |
| 5886 | // Handlebars. |
| 5887 | CompileRun( |
| 5888 | "function MyError(msg) { " |
| 5889 | " this.name = 'MyError'; " |
| 5890 | " this.message = msg; " |
| 5891 | "} " |
| 5892 | "MyError.prototype = new Error(); " |
| 5893 | "throw new MyError('my message'); " ); |
| 5894 | |
| 5895 | // Closure. |
| 5896 | CompileRun( |
| 5897 | "function MyError(msg) { " |
| 5898 | " this.name = 'MyError'; " |
| 5899 | " this.message = msg; " |
| 5900 | "} " |
| 5901 | "inherits = function(childCtor, parentCtor) { " |
| 5902 | " function tempCtor() {}; " |
| 5903 | " tempCtor.prototype = parentCtor.prototype; " |
| 5904 | " childCtor.superClass_ = parentCtor.prototype; " |
| 5905 | " childCtor.prototype = new tempCtor(); " |
| 5906 | " childCtor.prototype.constructor = childCtor; " |
| 5907 | "}; " |
| 5908 | "inherits(MyError, Error); " |
| 5909 | "throw new MyError('my message'); " ); |
| 5910 | |
| 5911 | // Object.create. |
| 5912 | CompileRun( |
| 5913 | "function MyError(msg) { " |
| 5914 | " this.name = 'MyError'; " |
| 5915 | " this.message = msg; " |
| 5916 | "} " |
| 5917 | "MyError.prototype = Object.create(Error.prototype); " |
| 5918 | "throw new MyError('my message'); " ); |
| 5919 | |
| 5920 | context->GetIsolate()->RemoveMessageListeners(check_custom_error_message); |
| 5921 | } |
| 5922 | |
| 5923 | |
| 5924 | static void check_custom_rethrowing_message(v8::Local<v8::Message> message, |
| 5925 | v8::Local<v8::Value> data) { |
| 5926 | CHECK(data->IsExternal()); |
| 5927 | int* callcount = static_cast<int*>(data.As<v8::External>()->Value()); |
| 5928 | ++*callcount; |
| 5929 | |
| 5930 | const char* uncaught_error = "Uncaught exception" ; |
| 5931 | CHECK(message->Get() |
| 5932 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 5933 | v8_str(uncaught_error)) |
| 5934 | .FromJust()); |
| 5935 | // Test that compiling code inside a message handler works. |
| 5936 | CHECK(CompileRunChecked(CcTest::isolate(), "(function(a) { return a; })(42)" ) |
| 5937 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 5938 | v8::Integer::NewFromUnsigned(CcTest::isolate(), 42)) |
| 5939 | .FromJust()); |
| 5940 | } |
| 5941 | |
| 5942 | |
| 5943 | TEST(CustomErrorRethrowsOnToString) { |
| 5944 | int callcount = 0; |
| 5945 | LocalContext context; |
| 5946 | v8::Isolate* isolate = context->GetIsolate(); |
| 5947 | v8::HandleScope scope(isolate); |
| 5948 | context->GetIsolate()->AddMessageListener( |
| 5949 | check_custom_rethrowing_message, v8::External::New(isolate, &callcount)); |
| 5950 | |
| 5951 | CompileRun( |
| 5952 | "var e = { toString: function() { throw e; } };" |
| 5953 | "try { throw e; } finally {}" ); |
| 5954 | |
| 5955 | CHECK_EQ(callcount, 1); |
| 5956 | context->GetIsolate()->RemoveMessageListeners( |
| 5957 | check_custom_rethrowing_message); |
| 5958 | } |
| 5959 | |
| 5960 | TEST(CustomErrorRethrowsOnToStringInsideVerboseTryCatch) { |
| 5961 | int callcount = 0; |
| 5962 | LocalContext context; |
| 5963 | v8::Isolate* isolate = context->GetIsolate(); |
| 5964 | v8::HandleScope scope(isolate); |
| 5965 | v8::TryCatch try_catch(isolate); |
| 5966 | try_catch.SetVerbose(true); |
| 5967 | context->GetIsolate()->AddMessageListener( |
| 5968 | check_custom_rethrowing_message, v8::External::New(isolate, &callcount)); |
| 5969 | |
| 5970 | CompileRun( |
| 5971 | "var e = { toString: function() { throw e; } };" |
| 5972 | "try { throw e; } finally {}" ); |
| 5973 | |
| 5974 | CHECK_EQ(callcount, 1); |
| 5975 | context->GetIsolate()->RemoveMessageListeners( |
| 5976 | check_custom_rethrowing_message); |
| 5977 | } |
| 5978 | |
| 5979 | |
| 5980 | static void receive_message(v8::Local<v8::Message> message, |
| 5981 | v8::Local<v8::Value> data) { |
| 5982 | message->Get(); |
| 5983 | message_received = true; |
| 5984 | } |
| 5985 | |
| 5986 | |
| 5987 | TEST(APIThrowMessage) { |
| 5988 | message_received = false; |
| 5989 | v8::Isolate* isolate = CcTest::isolate(); |
| 5990 | v8::HandleScope scope(isolate); |
| 5991 | isolate->AddMessageListener(receive_message); |
| 5992 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 5993 | templ->Set(v8_str("ThrowFromC" ), |
| 5994 | v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| 5995 | LocalContext context(nullptr, templ); |
| 5996 | CompileRun("ThrowFromC();" ); |
| 5997 | CHECK(message_received); |
| 5998 | isolate->RemoveMessageListeners(receive_message); |
| 5999 | } |
| 6000 | |
| 6001 | |
| 6002 | TEST(APIThrowMessageAndVerboseTryCatch) { |
| 6003 | message_received = false; |
| 6004 | v8::Isolate* isolate = CcTest::isolate(); |
| 6005 | v8::HandleScope scope(isolate); |
| 6006 | isolate->AddMessageListener(receive_message); |
| 6007 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6008 | templ->Set(v8_str("ThrowFromC" ), |
| 6009 | v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| 6010 | LocalContext context(nullptr, templ); |
| 6011 | v8::TryCatch try_catch(isolate); |
| 6012 | try_catch.SetVerbose(true); |
| 6013 | Local<Value> result = CompileRun("ThrowFromC();" ); |
| 6014 | CHECK(try_catch.HasCaught()); |
| 6015 | CHECK(result.IsEmpty()); |
| 6016 | CHECK(message_received); |
| 6017 | isolate->RemoveMessageListeners(receive_message); |
| 6018 | } |
| 6019 | |
| 6020 | |
| 6021 | TEST(APIStackOverflowAndVerboseTryCatch) { |
| 6022 | message_received = false; |
| 6023 | LocalContext context; |
| 6024 | v8::HandleScope scope(context->GetIsolate()); |
| 6025 | context->GetIsolate()->AddMessageListener(receive_message); |
| 6026 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6027 | try_catch.SetVerbose(true); |
| 6028 | Local<Value> result = CompileRun("function foo() { foo(); } foo();" ); |
| 6029 | CHECK(try_catch.HasCaught()); |
| 6030 | CHECK(result.IsEmpty()); |
| 6031 | CHECK(message_received); |
| 6032 | context->GetIsolate()->RemoveMessageListeners(receive_message); |
| 6033 | } |
| 6034 | |
| 6035 | |
| 6036 | THREADED_TEST(ExternalScriptException) { |
| 6037 | v8::Isolate* isolate = CcTest::isolate(); |
| 6038 | v8::HandleScope scope(isolate); |
| 6039 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6040 | templ->Set(v8_str("ThrowFromC" ), |
| 6041 | v8::FunctionTemplate::New(isolate, ThrowFromC)); |
| 6042 | LocalContext context(nullptr, templ); |
| 6043 | |
| 6044 | v8::TryCatch try_catch(isolate); |
| 6045 | Local<Value> result = CompileRun("ThrowFromC(); throw 'panama';" ); |
| 6046 | CHECK(result.IsEmpty()); |
| 6047 | CHECK(try_catch.HasCaught()); |
| 6048 | String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| 6049 | CHECK_EQ(0, strcmp("konto" , *exception_value)); |
| 6050 | } |
| 6051 | |
| 6052 | |
| 6053 | void CThrowCountDown(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6054 | ApiTestFuzzer::Fuzz(); |
| 6055 | CHECK_EQ(4, args.Length()); |
| 6056 | v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| 6057 | int count = args[0]->Int32Value(context).FromJust(); |
| 6058 | int cInterval = args[2]->Int32Value(context).FromJust(); |
| 6059 | if (count == 0) { |
| 6060 | args.GetIsolate()->ThrowException(v8_str("FromC" )); |
| 6061 | return; |
| 6062 | } else { |
| 6063 | Local<v8::Object> global = context->Global(); |
| 6064 | Local<Value> fun = |
| 6065 | global->Get(context, v8_str("JSThrowCountDown" )).ToLocalChecked(); |
| 6066 | v8::Local<Value> argv[] = {v8_num(count - 1), args[1], args[2], args[3]}; |
| 6067 | if (count % cInterval == 0) { |
| 6068 | v8::TryCatch try_catch(args.GetIsolate()); |
| 6069 | Local<Value> result = fun.As<Function>() |
| 6070 | ->Call(context, global, 4, argv) |
| 6071 | .FromMaybe(Local<Value>()); |
| 6072 | int expected = args[3]->Int32Value(context).FromJust(); |
| 6073 | if (try_catch.HasCaught()) { |
| 6074 | CHECK_EQ(expected, count); |
| 6075 | CHECK(result.IsEmpty()); |
| 6076 | CHECK(!CcTest::i_isolate()->has_scheduled_exception()); |
| 6077 | } else { |
| 6078 | CHECK_NE(expected, count); |
| 6079 | } |
| 6080 | args.GetReturnValue().Set(result); |
| 6081 | return; |
| 6082 | } else { |
| 6083 | args.GetReturnValue().Set(fun.As<Function>() |
| 6084 | ->Call(context, global, 4, argv) |
| 6085 | .FromMaybe(v8::Local<v8::Value>())); |
| 6086 | return; |
| 6087 | } |
| 6088 | } |
| 6089 | } |
| 6090 | |
| 6091 | |
| 6092 | void JSCheck(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6093 | ApiTestFuzzer::Fuzz(); |
| 6094 | CHECK_EQ(3, args.Length()); |
| 6095 | v8::Isolate* isolate = args.GetIsolate(); |
| 6096 | v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 6097 | bool equality = args[0]->BooleanValue(isolate); |
| 6098 | int count = args[1]->Int32Value(context).FromJust(); |
| 6099 | int expected = args[2]->Int32Value(context).FromJust(); |
| 6100 | if (equality) { |
| 6101 | CHECK_EQ(count, expected); |
| 6102 | } else { |
| 6103 | CHECK_NE(count, expected); |
| 6104 | } |
| 6105 | } |
| 6106 | |
| 6107 | |
| 6108 | THREADED_TEST(EvalInTryFinally) { |
| 6109 | LocalContext context; |
| 6110 | v8::HandleScope scope(context->GetIsolate()); |
| 6111 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6112 | CompileRun( |
| 6113 | "(function() {" |
| 6114 | " try {" |
| 6115 | " eval('asldkf (*&^&*^');" |
| 6116 | " } finally {" |
| 6117 | " return;" |
| 6118 | " }" |
| 6119 | "})()" ); |
| 6120 | CHECK(!try_catch.HasCaught()); |
| 6121 | } |
| 6122 | |
| 6123 | |
| 6124 | // This test works by making a stack of alternating JavaScript and C |
| 6125 | // activations. These activations set up exception handlers with regular |
| 6126 | // intervals, one interval for C activations and another for JavaScript |
| 6127 | // activations. When enough activations have been created an exception is |
| 6128 | // thrown and we check that the right activation catches the exception and that |
| 6129 | // no other activations do. The right activation is always the topmost one with |
| 6130 | // a handler, regardless of whether it is in JavaScript or C. |
| 6131 | // |
| 6132 | // The notation used to describe a test case looks like this: |
| 6133 | // |
| 6134 | // *JS[4] *C[3] @JS[2] C[1] JS[0] |
| 6135 | // |
| 6136 | // Each entry is an activation, either JS or C. The index is the count at that |
| 6137 | // level. Stars identify activations with exception handlers, the @ identifies |
| 6138 | // the exception handler that should catch the exception. |
| 6139 | // |
| 6140 | // BUG(271): Some of the exception propagation does not work on the |
| 6141 | // ARM simulator because the simulator separates the C++ stack and the |
| 6142 | // JS stack. This test therefore fails on the simulator. The test is |
| 6143 | // not threaded to allow the threading tests to run on the simulator. |
| 6144 | TEST(ExceptionOrder) { |
| 6145 | v8::Isolate* isolate = CcTest::isolate(); |
| 6146 | v8::HandleScope scope(isolate); |
| 6147 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6148 | templ->Set(v8_str("check" ), v8::FunctionTemplate::New(isolate, JSCheck)); |
| 6149 | templ->Set(v8_str("CThrowCountDown" ), |
| 6150 | v8::FunctionTemplate::New(isolate, CThrowCountDown)); |
| 6151 | LocalContext context(nullptr, templ); |
| 6152 | CompileRun( |
| 6153 | "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" |
| 6154 | " if (count == 0) throw 'FromJS';" |
| 6155 | " if (count % jsInterval == 0) {" |
| 6156 | " try {" |
| 6157 | " var value = CThrowCountDown(count - 1," |
| 6158 | " jsInterval," |
| 6159 | " cInterval," |
| 6160 | " expected);" |
| 6161 | " check(false, count, expected);" |
| 6162 | " return value;" |
| 6163 | " } catch (e) {" |
| 6164 | " check(true, count, expected);" |
| 6165 | " }" |
| 6166 | " } else {" |
| 6167 | " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" |
| 6168 | " }" |
| 6169 | "}" ); |
| 6170 | Local<Function> fun = Local<Function>::Cast( |
| 6171 | context->Global() |
| 6172 | ->Get(context.local(), v8_str("JSThrowCountDown" )) |
| 6173 | .ToLocalChecked()); |
| 6174 | |
| 6175 | const int argc = 4; |
| 6176 | // count jsInterval cInterval expected |
| 6177 | |
| 6178 | // *JS[4] *C[3] @JS[2] C[1] JS[0] |
| 6179 | v8::Local<Value> a0[argc] = {v8_num(4), v8_num(2), v8_num(3), v8_num(2)}; |
| 6180 | fun->Call(context.local(), fun, argc, a0).ToLocalChecked(); |
| 6181 | |
| 6182 | // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] |
| 6183 | v8::Local<Value> a1[argc] = {v8_num(5), v8_num(6), v8_num(1), v8_num(2)}; |
| 6184 | fun->Call(context.local(), fun, argc, a1).ToLocalChecked(); |
| 6185 | |
| 6186 | // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] |
| 6187 | v8::Local<Value> a2[argc] = {v8_num(6), v8_num(7), v8_num(5), v8_num(5)}; |
| 6188 | fun->Call(context.local(), fun, argc, a2).ToLocalChecked(); |
| 6189 | |
| 6190 | // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] |
| 6191 | v8::Local<Value> a3[argc] = {v8_num(6), v8_num(6), v8_num(7), v8_num(6)}; |
| 6192 | fun->Call(context.local(), fun, argc, a3).ToLocalChecked(); |
| 6193 | |
| 6194 | // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] |
| 6195 | v8::Local<Value> a4[argc] = {v8_num(6), v8_num(4), v8_num(5), v8_num(4)}; |
| 6196 | fun->Call(context.local(), fun, argc, a4).ToLocalChecked(); |
| 6197 | |
| 6198 | // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] |
| 6199 | v8::Local<Value> a5[argc] = {v8_num(6), v8_num(4), v8_num(3), v8_num(3)}; |
| 6200 | fun->Call(context.local(), fun, argc, a5).ToLocalChecked(); |
| 6201 | } |
| 6202 | |
| 6203 | |
| 6204 | void ThrowValue(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6205 | ApiTestFuzzer::Fuzz(); |
| 6206 | CHECK_EQ(1, args.Length()); |
| 6207 | args.GetIsolate()->ThrowException(args[0]); |
| 6208 | } |
| 6209 | |
| 6210 | |
| 6211 | THREADED_TEST(ThrowValues) { |
| 6212 | v8::Isolate* isolate = CcTest::isolate(); |
| 6213 | v8::HandleScope scope(isolate); |
| 6214 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6215 | templ->Set(v8_str("Throw" ), v8::FunctionTemplate::New(isolate, ThrowValue)); |
| 6216 | LocalContext context(nullptr, templ); |
| 6217 | v8::Local<v8::Array> result = v8::Local<v8::Array>::Cast( |
| 6218 | CompileRun("function Run(obj) {" |
| 6219 | " try {" |
| 6220 | " Throw(obj);" |
| 6221 | " } catch (e) {" |
| 6222 | " return e;" |
| 6223 | " }" |
| 6224 | " return 'no exception';" |
| 6225 | "}" |
| 6226 | "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];" )); |
| 6227 | CHECK_EQ(5u, result->Length()); |
| 6228 | CHECK(result->Get(context.local(), v8::Integer::New(isolate, 0)) |
| 6229 | .ToLocalChecked() |
| 6230 | ->IsString()); |
| 6231 | CHECK(result->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 6232 | .ToLocalChecked() |
| 6233 | ->IsNumber()); |
| 6234 | CHECK_EQ(1, result->Get(context.local(), v8::Integer::New(isolate, 1)) |
| 6235 | .ToLocalChecked() |
| 6236 | ->Int32Value(context.local()) |
| 6237 | .FromJust()); |
| 6238 | CHECK(result->Get(context.local(), v8::Integer::New(isolate, 2)) |
| 6239 | .ToLocalChecked() |
| 6240 | ->IsNumber()); |
| 6241 | CHECK_EQ(0, result->Get(context.local(), v8::Integer::New(isolate, 2)) |
| 6242 | .ToLocalChecked() |
| 6243 | ->Int32Value(context.local()) |
| 6244 | .FromJust()); |
| 6245 | CHECK(result->Get(context.local(), v8::Integer::New(isolate, 3)) |
| 6246 | .ToLocalChecked() |
| 6247 | ->IsNull()); |
| 6248 | CHECK(result->Get(context.local(), v8::Integer::New(isolate, 4)) |
| 6249 | .ToLocalChecked() |
| 6250 | ->IsUndefined()); |
| 6251 | } |
| 6252 | |
| 6253 | |
| 6254 | THREADED_TEST(CatchZero) { |
| 6255 | LocalContext context; |
| 6256 | v8::HandleScope scope(context->GetIsolate()); |
| 6257 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6258 | CHECK(!try_catch.HasCaught()); |
| 6259 | CompileRun("throw 10" ); |
| 6260 | CHECK(try_catch.HasCaught()); |
| 6261 | CHECK_EQ(10, try_catch.Exception()->Int32Value(context.local()).FromJust()); |
| 6262 | try_catch.Reset(); |
| 6263 | CHECK(!try_catch.HasCaught()); |
| 6264 | CompileRun("throw 0" ); |
| 6265 | CHECK(try_catch.HasCaught()); |
| 6266 | CHECK_EQ(0, try_catch.Exception()->Int32Value(context.local()).FromJust()); |
| 6267 | } |
| 6268 | |
| 6269 | |
| 6270 | THREADED_TEST(CatchExceptionFromWith) { |
| 6271 | LocalContext context; |
| 6272 | v8::HandleScope scope(context->GetIsolate()); |
| 6273 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6274 | CHECK(!try_catch.HasCaught()); |
| 6275 | CompileRun("var o = {}; with (o) { throw 42; }" ); |
| 6276 | CHECK(try_catch.HasCaught()); |
| 6277 | } |
| 6278 | |
| 6279 | |
| 6280 | THREADED_TEST(TryCatchAndFinallyHidingException) { |
| 6281 | LocalContext context; |
| 6282 | v8::HandleScope scope(context->GetIsolate()); |
| 6283 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6284 | CHECK(!try_catch.HasCaught()); |
| 6285 | CompileRun("function f(k) { try { this[k]; } finally { return 0; } };" ); |
| 6286 | CompileRun("f({toString: function() { throw 42; }});" ); |
| 6287 | CHECK(!try_catch.HasCaught()); |
| 6288 | } |
| 6289 | |
| 6290 | |
| 6291 | void WithTryCatch(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6292 | v8::TryCatch try_catch(args.GetIsolate()); |
| 6293 | } |
| 6294 | |
| 6295 | |
| 6296 | THREADED_TEST(TryCatchAndFinally) { |
| 6297 | LocalContext context; |
| 6298 | v8::Isolate* isolate = context->GetIsolate(); |
| 6299 | v8::HandleScope scope(isolate); |
| 6300 | CHECK(context->Global() |
| 6301 | ->Set(context.local(), v8_str("native_with_try_catch" ), |
| 6302 | v8::FunctionTemplate::New(isolate, WithTryCatch) |
| 6303 | ->GetFunction(context.local()) |
| 6304 | .ToLocalChecked()) |
| 6305 | .FromJust()); |
| 6306 | v8::TryCatch try_catch(isolate); |
| 6307 | CHECK(!try_catch.HasCaught()); |
| 6308 | CompileRun( |
| 6309 | "try {\n" |
| 6310 | " throw new Error('a');\n" |
| 6311 | "} finally {\n" |
| 6312 | " native_with_try_catch();\n" |
| 6313 | "}\n" ); |
| 6314 | CHECK(try_catch.HasCaught()); |
| 6315 | } |
| 6316 | |
| 6317 | |
| 6318 | static void TryCatchNested1Helper(int depth) { |
| 6319 | if (depth > 0) { |
| 6320 | v8::TryCatch try_catch(CcTest::isolate()); |
| 6321 | try_catch.SetVerbose(true); |
| 6322 | TryCatchNested1Helper(depth - 1); |
| 6323 | CHECK(try_catch.HasCaught()); |
| 6324 | try_catch.ReThrow(); |
| 6325 | } else { |
| 6326 | CcTest::isolate()->ThrowException(v8_str("E1" )); |
| 6327 | } |
| 6328 | } |
| 6329 | |
| 6330 | |
| 6331 | static void TryCatchNested2Helper(int depth) { |
| 6332 | if (depth > 0) { |
| 6333 | v8::TryCatch try_catch(CcTest::isolate()); |
| 6334 | try_catch.SetVerbose(true); |
| 6335 | TryCatchNested2Helper(depth - 1); |
| 6336 | CHECK(try_catch.HasCaught()); |
| 6337 | try_catch.ReThrow(); |
| 6338 | } else { |
| 6339 | CompileRun("throw 'E2';" ); |
| 6340 | } |
| 6341 | } |
| 6342 | |
| 6343 | |
| 6344 | TEST(TryCatchNested) { |
| 6345 | v8::V8::Initialize(); |
| 6346 | LocalContext context; |
| 6347 | v8::HandleScope scope(context->GetIsolate()); |
| 6348 | |
| 6349 | { |
| 6350 | // Test nested try-catch with a native throw in the end. |
| 6351 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6352 | TryCatchNested1Helper(5); |
| 6353 | CHECK(try_catch.HasCaught()); |
| 6354 | CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(), |
| 6355 | try_catch.Exception()), |
| 6356 | "E1" )); |
| 6357 | } |
| 6358 | |
| 6359 | { |
| 6360 | // Test nested try-catch with a JavaScript throw in the end. |
| 6361 | v8::TryCatch try_catch(context->GetIsolate()); |
| 6362 | TryCatchNested2Helper(5); |
| 6363 | CHECK(try_catch.HasCaught()); |
| 6364 | CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(), |
| 6365 | try_catch.Exception()), |
| 6366 | "E2" )); |
| 6367 | } |
| 6368 | } |
| 6369 | |
| 6370 | |
| 6371 | void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) { |
| 6372 | CHECK(try_catch->HasCaught()); |
| 6373 | Local<Message> message = try_catch->Message(); |
| 6374 | Local<Value> resource = message->GetScriptOrigin().ResourceName(); |
| 6375 | CHECK_EQ( |
| 6376 | 0, strcmp(*v8::String::Utf8Value(CcTest::isolate(), resource), "inner" )); |
| 6377 | CHECK_EQ(0, strcmp(*v8::String::Utf8Value(CcTest::isolate(), message->Get()), |
| 6378 | "Uncaught Error: a" )); |
| 6379 | CHECK_EQ(1, message->GetLineNumber(CcTest::isolate()->GetCurrentContext()) |
| 6380 | .FromJust()); |
| 6381 | CHECK_EQ(0, message->GetStartColumn(CcTest::isolate()->GetCurrentContext()) |
| 6382 | .FromJust()); |
| 6383 | } |
| 6384 | |
| 6385 | |
| 6386 | void TryCatchMixedNestingHelper( |
| 6387 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6388 | ApiTestFuzzer::Fuzz(); |
| 6389 | v8::TryCatch try_catch(args.GetIsolate()); |
| 6390 | CompileRunWithOrigin("throw new Error('a');\n" , "inner" , 0, 0); |
| 6391 | CHECK(try_catch.HasCaught()); |
| 6392 | TryCatchMixedNestingCheck(&try_catch); |
| 6393 | try_catch.ReThrow(); |
| 6394 | } |
| 6395 | |
| 6396 | |
| 6397 | // This test ensures that an outer TryCatch in the following situation: |
| 6398 | // C++/TryCatch -> JS -> C++/TryCatch -> JS w/ SyntaxError |
| 6399 | // does not clobber the Message object generated for the inner TryCatch. |
| 6400 | // This exercises the ability of TryCatch.ReThrow() to restore the |
| 6401 | // inner pending Message before throwing the exception again. |
| 6402 | TEST(TryCatchMixedNesting) { |
| 6403 | v8::Isolate* isolate = CcTest::isolate(); |
| 6404 | v8::HandleScope scope(isolate); |
| 6405 | v8::V8::Initialize(); |
| 6406 | v8::TryCatch try_catch(isolate); |
| 6407 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6408 | templ->Set(v8_str("TryCatchMixedNestingHelper" ), |
| 6409 | v8::FunctionTemplate::New(isolate, TryCatchMixedNestingHelper)); |
| 6410 | LocalContext context(nullptr, templ); |
| 6411 | CompileRunWithOrigin("TryCatchMixedNestingHelper();\n" , "outer" , 1, 1); |
| 6412 | TryCatchMixedNestingCheck(&try_catch); |
| 6413 | } |
| 6414 | |
| 6415 | |
| 6416 | void TryCatchNativeHelper(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6417 | ApiTestFuzzer::Fuzz(); |
| 6418 | v8::TryCatch try_catch(args.GetIsolate()); |
| 6419 | args.GetIsolate()->ThrowException(v8_str("boom" )); |
| 6420 | CHECK(try_catch.HasCaught()); |
| 6421 | } |
| 6422 | |
| 6423 | |
| 6424 | TEST(TryCatchNative) { |
| 6425 | v8::Isolate* isolate = CcTest::isolate(); |
| 6426 | v8::HandleScope scope(isolate); |
| 6427 | v8::V8::Initialize(); |
| 6428 | v8::TryCatch try_catch(isolate); |
| 6429 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6430 | templ->Set(v8_str("TryCatchNativeHelper" ), |
| 6431 | v8::FunctionTemplate::New(isolate, TryCatchNativeHelper)); |
| 6432 | LocalContext context(nullptr, templ); |
| 6433 | CompileRun("TryCatchNativeHelper();" ); |
| 6434 | CHECK(!try_catch.HasCaught()); |
| 6435 | } |
| 6436 | |
| 6437 | |
| 6438 | void TryCatchNativeResetHelper( |
| 6439 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 6440 | ApiTestFuzzer::Fuzz(); |
| 6441 | v8::TryCatch try_catch(args.GetIsolate()); |
| 6442 | args.GetIsolate()->ThrowException(v8_str("boom" )); |
| 6443 | CHECK(try_catch.HasCaught()); |
| 6444 | try_catch.Reset(); |
| 6445 | CHECK(!try_catch.HasCaught()); |
| 6446 | } |
| 6447 | |
| 6448 | |
| 6449 | TEST(TryCatchNativeReset) { |
| 6450 | v8::Isolate* isolate = CcTest::isolate(); |
| 6451 | v8::HandleScope scope(isolate); |
| 6452 | v8::V8::Initialize(); |
| 6453 | v8::TryCatch try_catch(isolate); |
| 6454 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6455 | templ->Set(v8_str("TryCatchNativeResetHelper" ), |
| 6456 | v8::FunctionTemplate::New(isolate, TryCatchNativeResetHelper)); |
| 6457 | LocalContext context(nullptr, templ); |
| 6458 | CompileRun("TryCatchNativeResetHelper();" ); |
| 6459 | CHECK(!try_catch.HasCaught()); |
| 6460 | } |
| 6461 | |
| 6462 | |
| 6463 | THREADED_TEST(Equality) { |
| 6464 | LocalContext context; |
| 6465 | v8::Isolate* isolate = context->GetIsolate(); |
| 6466 | v8::HandleScope scope(context->GetIsolate()); |
| 6467 | // Check that equality works at all before relying on CHECK_EQ |
| 6468 | CHECK(v8_str("a" )->Equals(context.local(), v8_str("a" )).FromJust()); |
| 6469 | CHECK(!v8_str("a" )->Equals(context.local(), v8_str("b" )).FromJust()); |
| 6470 | |
| 6471 | CHECK(v8_str("a" )->Equals(context.local(), v8_str("a" )).FromJust()); |
| 6472 | CHECK(!v8_str("a" )->Equals(context.local(), v8_str("b" )).FromJust()); |
| 6473 | CHECK(v8_num(1)->Equals(context.local(), v8_num(1)).FromJust()); |
| 6474 | CHECK(v8_num(1.00)->Equals(context.local(), v8_num(1)).FromJust()); |
| 6475 | CHECK(!v8_num(1)->Equals(context.local(), v8_num(2)).FromJust()); |
| 6476 | |
| 6477 | // Assume String is not internalized. |
| 6478 | CHECK(v8_str("a" )->StrictEquals(v8_str("a" ))); |
| 6479 | CHECK(!v8_str("a" )->StrictEquals(v8_str("b" ))); |
| 6480 | CHECK(!v8_str("5" )->StrictEquals(v8_num(5))); |
| 6481 | CHECK(v8_num(1)->StrictEquals(v8_num(1))); |
| 6482 | CHECK(!v8_num(1)->StrictEquals(v8_num(2))); |
| 6483 | CHECK(v8_num(0.0)->StrictEquals(v8_num(-0.0))); |
| 6484 | Local<Value> not_a_number = v8_num(std::numeric_limits<double>::quiet_NaN()); |
| 6485 | CHECK(!not_a_number->StrictEquals(not_a_number)); |
| 6486 | CHECK(v8::False(isolate)->StrictEquals(v8::False(isolate))); |
| 6487 | CHECK(!v8::False(isolate)->StrictEquals(v8::Undefined(isolate))); |
| 6488 | |
| 6489 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 6490 | v8::Persistent<v8::Object> alias(isolate, obj); |
| 6491 | CHECK(v8::Local<v8::Object>::New(isolate, alias)->StrictEquals(obj)); |
| 6492 | alias.Reset(); |
| 6493 | |
| 6494 | CHECK(v8_str("a" )->SameValue(v8_str("a" ))); |
| 6495 | CHECK(!v8_str("a" )->SameValue(v8_str("b" ))); |
| 6496 | CHECK(!v8_str("5" )->SameValue(v8_num(5))); |
| 6497 | CHECK(v8_num(1)->SameValue(v8_num(1))); |
| 6498 | CHECK(!v8_num(1)->SameValue(v8_num(2))); |
| 6499 | CHECK(!v8_num(0.0)->SameValue(v8_num(-0.0))); |
| 6500 | CHECK(not_a_number->SameValue(not_a_number)); |
| 6501 | CHECK(v8::False(isolate)->SameValue(v8::False(isolate))); |
| 6502 | CHECK(!v8::False(isolate)->SameValue(v8::Undefined(isolate))); |
| 6503 | } |
| 6504 | |
| 6505 | THREADED_TEST(TypeOf) { |
| 6506 | LocalContext context; |
| 6507 | v8::Isolate* isolate = context->GetIsolate(); |
| 6508 | v8::HandleScope scope(context->GetIsolate()); |
| 6509 | |
| 6510 | Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| 6511 | Local<v8::Function> fun = t1->GetFunction(context.local()).ToLocalChecked(); |
| 6512 | |
| 6513 | CHECK(v8::Undefined(isolate) |
| 6514 | ->TypeOf(isolate) |
| 6515 | ->Equals(context.local(), v8_str("undefined" )) |
| 6516 | .FromJust()); |
| 6517 | CHECK(v8::Null(isolate) |
| 6518 | ->TypeOf(isolate) |
| 6519 | ->Equals(context.local(), v8_str("object" )) |
| 6520 | .FromJust()); |
| 6521 | CHECK(v8_str("str" ) |
| 6522 | ->TypeOf(isolate) |
| 6523 | ->Equals(context.local(), v8_str("string" )) |
| 6524 | .FromJust()); |
| 6525 | CHECK(v8_num(0.0) |
| 6526 | ->TypeOf(isolate) |
| 6527 | ->Equals(context.local(), v8_str("number" )) |
| 6528 | .FromJust()); |
| 6529 | CHECK(v8_num(1) |
| 6530 | ->TypeOf(isolate) |
| 6531 | ->Equals(context.local(), v8_str("number" )) |
| 6532 | .FromJust()); |
| 6533 | CHECK(v8::Object::New(isolate) |
| 6534 | ->TypeOf(isolate) |
| 6535 | ->Equals(context.local(), v8_str("object" )) |
| 6536 | .FromJust()); |
| 6537 | CHECK(v8::Boolean::New(isolate, true) |
| 6538 | ->TypeOf(isolate) |
| 6539 | ->Equals(context.local(), v8_str("boolean" )) |
| 6540 | .FromJust()); |
| 6541 | CHECK(fun->TypeOf(isolate) |
| 6542 | ->Equals(context.local(), v8_str("function" )) |
| 6543 | .FromJust()); |
| 6544 | } |
| 6545 | |
| 6546 | THREADED_TEST(InstanceOf) { |
| 6547 | LocalContext env; |
| 6548 | v8::HandleScope scope(env->GetIsolate()); |
| 6549 | CompileRun( |
| 6550 | "var A = {};" |
| 6551 | "var B = {};" |
| 6552 | "var C = {};" |
| 6553 | "B.__proto__ = A;" |
| 6554 | "C.__proto__ = B;" |
| 6555 | "function F() {}" |
| 6556 | "F.prototype = A;" |
| 6557 | "var G = { [Symbol.hasInstance] : null};" |
| 6558 | "var H = { [Symbol.hasInstance] : () => { throw new Error(); } };" |
| 6559 | "var J = { [Symbol.hasInstance] : () => true };" |
| 6560 | "class K {}" |
| 6561 | "var D = new K;" |
| 6562 | "class L extends K {}" |
| 6563 | "var E = new L" ); |
| 6564 | |
| 6565 | v8::Local<v8::Object> f = v8::Local<v8::Object>::Cast(CompileRun("F" )); |
| 6566 | v8::Local<v8::Object> g = v8::Local<v8::Object>::Cast(CompileRun("G" )); |
| 6567 | v8::Local<v8::Object> h = v8::Local<v8::Object>::Cast(CompileRun("H" )); |
| 6568 | v8::Local<v8::Object> j = v8::Local<v8::Object>::Cast(CompileRun("J" )); |
| 6569 | v8::Local<v8::Object> k = v8::Local<v8::Object>::Cast(CompileRun("K" )); |
| 6570 | v8::Local<v8::Object> l = v8::Local<v8::Object>::Cast(CompileRun("L" )); |
| 6571 | v8::Local<v8::Value> a = v8::Local<v8::Value>::Cast(CompileRun("A" )); |
| 6572 | v8::Local<v8::Value> b = v8::Local<v8::Value>::Cast(CompileRun("B" )); |
| 6573 | v8::Local<v8::Value> c = v8::Local<v8::Value>::Cast(CompileRun("C" )); |
| 6574 | v8::Local<v8::Value> d = v8::Local<v8::Value>::Cast(CompileRun("D" )); |
| 6575 | v8::Local<v8::Value> e = v8::Local<v8::Value>::Cast(CompileRun("E" )); |
| 6576 | |
| 6577 | v8::TryCatch try_catch(env->GetIsolate()); |
| 6578 | CHECK(!a->InstanceOf(env.local(), f).ToChecked()); |
| 6579 | CHECK(b->InstanceOf(env.local(), f).ToChecked()); |
| 6580 | CHECK(c->InstanceOf(env.local(), f).ToChecked()); |
| 6581 | CHECK(!d->InstanceOf(env.local(), f).ToChecked()); |
| 6582 | CHECK(!e->InstanceOf(env.local(), f).ToChecked()); |
| 6583 | CHECK(!try_catch.HasCaught()); |
| 6584 | |
| 6585 | CHECK(a->InstanceOf(env.local(), g).IsNothing()); |
| 6586 | CHECK(try_catch.HasCaught()); |
| 6587 | try_catch.Reset(); |
| 6588 | |
| 6589 | CHECK(b->InstanceOf(env.local(), h).IsNothing()); |
| 6590 | CHECK(try_catch.HasCaught()); |
| 6591 | try_catch.Reset(); |
| 6592 | |
| 6593 | CHECK(v8_num(1)->InstanceOf(env.local(), j).ToChecked()); |
| 6594 | CHECK(!try_catch.HasCaught()); |
| 6595 | |
| 6596 | CHECK(d->InstanceOf(env.local(), k).ToChecked()); |
| 6597 | CHECK(e->InstanceOf(env.local(), k).ToChecked()); |
| 6598 | CHECK(!d->InstanceOf(env.local(), l).ToChecked()); |
| 6599 | CHECK(e->InstanceOf(env.local(), l).ToChecked()); |
| 6600 | CHECK(!try_catch.HasCaught()); |
| 6601 | } |
| 6602 | |
| 6603 | THREADED_TEST(MultiRun) { |
| 6604 | LocalContext context; |
| 6605 | v8::HandleScope scope(context->GetIsolate()); |
| 6606 | Local<Script> script = v8_compile("x" ); |
| 6607 | for (int i = 0; i < 10; i++) { |
| 6608 | script->Run(context.local()).IsEmpty(); |
| 6609 | } |
| 6610 | } |
| 6611 | |
| 6612 | |
| 6613 | static void GetXValue(Local<Name> name, |
| 6614 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 6615 | ApiTestFuzzer::Fuzz(); |
| 6616 | CHECK(info.Data() |
| 6617 | ->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("donut" )) |
| 6618 | .FromJust()); |
| 6619 | CHECK(name->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("x" )) |
| 6620 | .FromJust()); |
| 6621 | info.GetReturnValue().Set(name); |
| 6622 | } |
| 6623 | |
| 6624 | |
| 6625 | THREADED_TEST(SimplePropertyRead) { |
| 6626 | LocalContext context; |
| 6627 | v8::Isolate* isolate = context->GetIsolate(); |
| 6628 | v8::HandleScope scope(isolate); |
| 6629 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6630 | templ->SetAccessor(v8_str("x" ), GetXValue, nullptr, v8_str("donut" )); |
| 6631 | CHECK(context->Global() |
| 6632 | ->Set(context.local(), v8_str("obj" ), |
| 6633 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6634 | .FromJust()); |
| 6635 | Local<Script> script = v8_compile("obj.x" ); |
| 6636 | for (int i = 0; i < 10; i++) { |
| 6637 | Local<Value> result = script->Run(context.local()).ToLocalChecked(); |
| 6638 | CHECK(result->Equals(context.local(), v8_str("x" )).FromJust()); |
| 6639 | } |
| 6640 | } |
| 6641 | |
| 6642 | |
| 6643 | THREADED_TEST(DefinePropertyOnAPIAccessor) { |
| 6644 | LocalContext context; |
| 6645 | v8::Isolate* isolate = context->GetIsolate(); |
| 6646 | v8::HandleScope scope(isolate); |
| 6647 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6648 | templ->SetAccessor(v8_str("x" ), GetXValue, nullptr, v8_str("donut" )); |
| 6649 | CHECK(context->Global() |
| 6650 | ->Set(context.local(), v8_str("obj" ), |
| 6651 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6652 | .FromJust()); |
| 6653 | |
| 6654 | // Uses getOwnPropertyDescriptor to check the configurable status |
| 6655 | Local<Script> script_desc = v8_compile( |
| 6656 | "var prop = Object.getOwnPropertyDescriptor( " |
| 6657 | "obj, 'x');" |
| 6658 | "prop.configurable;" ); |
| 6659 | Local<Value> result = script_desc->Run(context.local()).ToLocalChecked(); |
| 6660 | CHECK(result->BooleanValue(isolate)); |
| 6661 | |
| 6662 | // Redefine get - but still configurable |
| 6663 | Local<Script> script_define = v8_compile( |
| 6664 | "var desc = { get: function(){return 42; }," |
| 6665 | " configurable: true };" |
| 6666 | "Object.defineProperty(obj, 'x', desc);" |
| 6667 | "obj.x" ); |
| 6668 | result = script_define->Run(context.local()).ToLocalChecked(); |
| 6669 | CHECK(result->Equals(context.local(), v8_num(42)).FromJust()); |
| 6670 | |
| 6671 | // Check that the accessor is still configurable |
| 6672 | result = script_desc->Run(context.local()).ToLocalChecked(); |
| 6673 | CHECK(result->BooleanValue(isolate)); |
| 6674 | |
| 6675 | // Redefine to a non-configurable |
| 6676 | script_define = v8_compile( |
| 6677 | "var desc = { get: function(){return 43; }," |
| 6678 | " configurable: false };" |
| 6679 | "Object.defineProperty(obj, 'x', desc);" |
| 6680 | "obj.x" ); |
| 6681 | result = script_define->Run(context.local()).ToLocalChecked(); |
| 6682 | CHECK(result->Equals(context.local(), v8_num(43)).FromJust()); |
| 6683 | result = script_desc->Run(context.local()).ToLocalChecked(); |
| 6684 | CHECK(!result->BooleanValue(isolate)); |
| 6685 | |
| 6686 | // Make sure that it is not possible to redefine again |
| 6687 | v8::TryCatch try_catch(isolate); |
| 6688 | CHECK(script_define->Run(context.local()).IsEmpty()); |
| 6689 | CHECK(try_catch.HasCaught()); |
| 6690 | String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| 6691 | CHECK_EQ(0, |
| 6692 | strcmp(*exception_value, "TypeError: Cannot redefine property: x" )); |
| 6693 | } |
| 6694 | |
| 6695 | |
| 6696 | THREADED_TEST(DefinePropertyOnDefineGetterSetter) { |
| 6697 | v8::Isolate* isolate = CcTest::isolate(); |
| 6698 | v8::HandleScope scope(isolate); |
| 6699 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6700 | templ->SetAccessor(v8_str("x" ), GetXValue, nullptr, v8_str("donut" )); |
| 6701 | LocalContext context; |
| 6702 | CHECK(context->Global() |
| 6703 | ->Set(context.local(), v8_str("obj" ), |
| 6704 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6705 | .FromJust()); |
| 6706 | |
| 6707 | Local<Script> script_desc = v8_compile( |
| 6708 | "var prop =" |
| 6709 | "Object.getOwnPropertyDescriptor( " |
| 6710 | "obj, 'x');" |
| 6711 | "prop.configurable;" ); |
| 6712 | Local<Value> result = script_desc->Run(context.local()).ToLocalChecked(); |
| 6713 | CHECK(result->BooleanValue(isolate)); |
| 6714 | |
| 6715 | Local<Script> script_define = v8_compile( |
| 6716 | "var desc = {get: function(){return 42; }," |
| 6717 | " configurable: true };" |
| 6718 | "Object.defineProperty(obj, 'x', desc);" |
| 6719 | "obj.x" ); |
| 6720 | result = script_define->Run(context.local()).ToLocalChecked(); |
| 6721 | CHECK(result->Equals(context.local(), v8_num(42)).FromJust()); |
| 6722 | |
| 6723 | result = script_desc->Run(context.local()).ToLocalChecked(); |
| 6724 | CHECK(result->BooleanValue(isolate)); |
| 6725 | |
| 6726 | script_define = v8_compile( |
| 6727 | "var desc = {get: function(){return 43; }," |
| 6728 | " configurable: false };" |
| 6729 | "Object.defineProperty(obj, 'x', desc);" |
| 6730 | "obj.x" ); |
| 6731 | result = script_define->Run(context.local()).ToLocalChecked(); |
| 6732 | CHECK(result->Equals(context.local(), v8_num(43)).FromJust()); |
| 6733 | |
| 6734 | result = script_desc->Run(context.local()).ToLocalChecked(); |
| 6735 | CHECK(!result->BooleanValue(isolate)); |
| 6736 | |
| 6737 | v8::TryCatch try_catch(isolate); |
| 6738 | CHECK(script_define->Run(context.local()).IsEmpty()); |
| 6739 | CHECK(try_catch.HasCaught()); |
| 6740 | String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| 6741 | CHECK_EQ(0, |
| 6742 | strcmp(*exception_value, "TypeError: Cannot redefine property: x" )); |
| 6743 | } |
| 6744 | |
| 6745 | |
| 6746 | static v8::Local<v8::Object> GetGlobalProperty(LocalContext* context, |
| 6747 | char const* name) { |
| 6748 | return v8::Local<v8::Object>::Cast( |
| 6749 | (*context) |
| 6750 | ->Global() |
| 6751 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str(name)) |
| 6752 | .ToLocalChecked()); |
| 6753 | } |
| 6754 | |
| 6755 | |
| 6756 | THREADED_TEST(DefineAPIAccessorOnObject) { |
| 6757 | v8::Isolate* isolate = CcTest::isolate(); |
| 6758 | v8::HandleScope scope(isolate); |
| 6759 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6760 | LocalContext context; |
| 6761 | |
| 6762 | CHECK(context->Global() |
| 6763 | ->Set(context.local(), v8_str("obj1" ), |
| 6764 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6765 | .FromJust()); |
| 6766 | CompileRun("var obj2 = {};" ); |
| 6767 | |
| 6768 | CHECK(CompileRun("obj1.x" )->IsUndefined()); |
| 6769 | CHECK(CompileRun("obj2.x" )->IsUndefined()); |
| 6770 | |
| 6771 | CHECK(GetGlobalProperty(&context, "obj1" ) |
| 6772 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6773 | v8_str("donut" )) |
| 6774 | .FromJust()); |
| 6775 | |
| 6776 | ExpectString("obj1.x" , "x" ); |
| 6777 | CHECK(CompileRun("obj2.x" )->IsUndefined()); |
| 6778 | |
| 6779 | CHECK(GetGlobalProperty(&context, "obj2" ) |
| 6780 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6781 | v8_str("donut" )) |
| 6782 | .FromJust()); |
| 6783 | |
| 6784 | ExpectString("obj1.x" , "x" ); |
| 6785 | ExpectString("obj2.x" , "x" ); |
| 6786 | |
| 6787 | ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable" ); |
| 6788 | ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable" ); |
| 6789 | |
| 6790 | CompileRun( |
| 6791 | "Object.defineProperty(obj1, 'x'," |
| 6792 | "{ get: function() { return 'y'; }, configurable: true })" ); |
| 6793 | |
| 6794 | ExpectString("obj1.x" , "y" ); |
| 6795 | ExpectString("obj2.x" , "x" ); |
| 6796 | |
| 6797 | CompileRun( |
| 6798 | "Object.defineProperty(obj2, 'x'," |
| 6799 | "{ get: function() { return 'y'; }, configurable: true })" ); |
| 6800 | |
| 6801 | ExpectString("obj1.x" , "y" ); |
| 6802 | ExpectString("obj2.x" , "y" ); |
| 6803 | |
| 6804 | ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable" ); |
| 6805 | ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable" ); |
| 6806 | |
| 6807 | CHECK(GetGlobalProperty(&context, "obj1" ) |
| 6808 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6809 | v8_str("donut" )) |
| 6810 | .FromJust()); |
| 6811 | CHECK(GetGlobalProperty(&context, "obj2" ) |
| 6812 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6813 | v8_str("donut" )) |
| 6814 | .FromJust()); |
| 6815 | |
| 6816 | ExpectString("obj1.x" , "x" ); |
| 6817 | ExpectString("obj2.x" , "x" ); |
| 6818 | |
| 6819 | ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable" ); |
| 6820 | ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable" ); |
| 6821 | |
| 6822 | // Define getters/setters, but now make them not configurable. |
| 6823 | CompileRun( |
| 6824 | "Object.defineProperty(obj1, 'x'," |
| 6825 | "{ get: function() { return 'z'; }, configurable: false })" ); |
| 6826 | CompileRun( |
| 6827 | "Object.defineProperty(obj2, 'x'," |
| 6828 | "{ get: function() { return 'z'; }, configurable: false })" ); |
| 6829 | ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable" ); |
| 6830 | ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable" ); |
| 6831 | |
| 6832 | ExpectString("obj1.x" , "z" ); |
| 6833 | ExpectString("obj2.x" , "z" ); |
| 6834 | |
| 6835 | CHECK(!GetGlobalProperty(&context, "obj1" ) |
| 6836 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6837 | v8_str("donut" )) |
| 6838 | .FromJust()); |
| 6839 | CHECK(!GetGlobalProperty(&context, "obj2" ) |
| 6840 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6841 | v8_str("donut" )) |
| 6842 | .FromJust()); |
| 6843 | |
| 6844 | ExpectString("obj1.x" , "z" ); |
| 6845 | ExpectString("obj2.x" , "z" ); |
| 6846 | } |
| 6847 | |
| 6848 | |
| 6849 | THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { |
| 6850 | v8::Isolate* isolate = CcTest::isolate(); |
| 6851 | v8::HandleScope scope(isolate); |
| 6852 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6853 | LocalContext context; |
| 6854 | |
| 6855 | CHECK(context->Global() |
| 6856 | ->Set(context.local(), v8_str("obj1" ), |
| 6857 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6858 | .FromJust()); |
| 6859 | CompileRun("var obj2 = {};" ); |
| 6860 | |
| 6861 | CHECK(GetGlobalProperty(&context, "obj1" ) |
| 6862 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6863 | v8_str("donut" ), v8::DEFAULT, v8::DontDelete) |
| 6864 | .FromJust()); |
| 6865 | CHECK(GetGlobalProperty(&context, "obj2" ) |
| 6866 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6867 | v8_str("donut" ), v8::DEFAULT, v8::DontDelete) |
| 6868 | .FromJust()); |
| 6869 | |
| 6870 | ExpectString("obj1.x" , "x" ); |
| 6871 | ExpectString("obj2.x" , "x" ); |
| 6872 | |
| 6873 | ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable" ); |
| 6874 | ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable" ); |
| 6875 | |
| 6876 | CHECK(!GetGlobalProperty(&context, "obj1" ) |
| 6877 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6878 | v8_str("donut" )) |
| 6879 | .FromJust()); |
| 6880 | CHECK(!GetGlobalProperty(&context, "obj2" ) |
| 6881 | ->SetAccessor(context.local(), v8_str("x" ), GetXValue, nullptr, |
| 6882 | v8_str("donut" )) |
| 6883 | .FromJust()); |
| 6884 | |
| 6885 | { |
| 6886 | v8::TryCatch try_catch(isolate); |
| 6887 | CompileRun( |
| 6888 | "Object.defineProperty(obj1, 'x'," |
| 6889 | "{get: function() { return 'func'; }})" ); |
| 6890 | CHECK(try_catch.HasCaught()); |
| 6891 | String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| 6892 | CHECK_EQ( |
| 6893 | 0, strcmp(*exception_value, "TypeError: Cannot redefine property: x" )); |
| 6894 | } |
| 6895 | { |
| 6896 | v8::TryCatch try_catch(isolate); |
| 6897 | CompileRun( |
| 6898 | "Object.defineProperty(obj2, 'x'," |
| 6899 | "{get: function() { return 'func'; }})" ); |
| 6900 | CHECK(try_catch.HasCaught()); |
| 6901 | String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| 6902 | CHECK_EQ( |
| 6903 | 0, strcmp(*exception_value, "TypeError: Cannot redefine property: x" )); |
| 6904 | } |
| 6905 | } |
| 6906 | |
| 6907 | |
| 6908 | static void Get239Value(Local<Name> name, |
| 6909 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 6910 | ApiTestFuzzer::Fuzz(); |
| 6911 | CHECK(info.Data() |
| 6912 | ->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("donut" )) |
| 6913 | .FromJust()); |
| 6914 | CHECK(name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("239" )) |
| 6915 | .FromJust()); |
| 6916 | info.GetReturnValue().Set(name); |
| 6917 | } |
| 6918 | |
| 6919 | |
| 6920 | THREADED_TEST(ElementAPIAccessor) { |
| 6921 | v8::Isolate* isolate = CcTest::isolate(); |
| 6922 | v8::HandleScope scope(isolate); |
| 6923 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6924 | LocalContext context; |
| 6925 | |
| 6926 | CHECK(context->Global() |
| 6927 | ->Set(context.local(), v8_str("obj1" ), |
| 6928 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6929 | .FromJust()); |
| 6930 | CompileRun("var obj2 = {};" ); |
| 6931 | |
| 6932 | CHECK(GetGlobalProperty(&context, "obj1" ) |
| 6933 | ->SetAccessor(context.local(), v8_str("239" ), Get239Value, nullptr, |
| 6934 | v8_str("donut" )) |
| 6935 | .FromJust()); |
| 6936 | CHECK(GetGlobalProperty(&context, "obj2" ) |
| 6937 | ->SetAccessor(context.local(), v8_str("239" ), Get239Value, nullptr, |
| 6938 | v8_str("donut" )) |
| 6939 | .FromJust()); |
| 6940 | |
| 6941 | ExpectString("obj1[239]" , "239" ); |
| 6942 | ExpectString("obj2[239]" , "239" ); |
| 6943 | ExpectString("obj1['239']" , "239" ); |
| 6944 | ExpectString("obj2['239']" , "239" ); |
| 6945 | } |
| 6946 | |
| 6947 | |
| 6948 | v8::Persistent<Value> xValue; |
| 6949 | |
| 6950 | |
| 6951 | static void SetXValue(Local<Name> name, Local<Value> value, |
| 6952 | const v8::PropertyCallbackInfo<void>& info) { |
| 6953 | Local<Context> context = info.GetIsolate()->GetCurrentContext(); |
| 6954 | CHECK(value->Equals(context, v8_num(4)).FromJust()); |
| 6955 | CHECK(info.Data()->Equals(context, v8_str("donut" )).FromJust()); |
| 6956 | CHECK(name->Equals(context, v8_str("x" )).FromJust()); |
| 6957 | CHECK(xValue.IsEmpty()); |
| 6958 | xValue.Reset(info.GetIsolate(), value); |
| 6959 | } |
| 6960 | |
| 6961 | |
| 6962 | THREADED_TEST(SimplePropertyWrite) { |
| 6963 | v8::Isolate* isolate = CcTest::isolate(); |
| 6964 | v8::HandleScope scope(isolate); |
| 6965 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6966 | templ->SetAccessor(v8_str("x" ), GetXValue, SetXValue, v8_str("donut" )); |
| 6967 | LocalContext context; |
| 6968 | CHECK(context->Global() |
| 6969 | ->Set(context.local(), v8_str("obj" ), |
| 6970 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6971 | .FromJust()); |
| 6972 | Local<Script> script = v8_compile("obj.x = 4" ); |
| 6973 | for (int i = 0; i < 10; i++) { |
| 6974 | CHECK(xValue.IsEmpty()); |
| 6975 | script->Run(context.local()).ToLocalChecked(); |
| 6976 | CHECK(v8_num(4) |
| 6977 | ->Equals(context.local(), |
| 6978 | Local<Value>::New(CcTest::isolate(), xValue)) |
| 6979 | .FromJust()); |
| 6980 | xValue.Reset(); |
| 6981 | } |
| 6982 | } |
| 6983 | |
| 6984 | |
| 6985 | THREADED_TEST(SetterOnly) { |
| 6986 | v8::Isolate* isolate = CcTest::isolate(); |
| 6987 | v8::HandleScope scope(isolate); |
| 6988 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 6989 | templ->SetAccessor(v8_str("x" ), nullptr, SetXValue, v8_str("donut" )); |
| 6990 | LocalContext context; |
| 6991 | CHECK(context->Global() |
| 6992 | ->Set(context.local(), v8_str("obj" ), |
| 6993 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 6994 | .FromJust()); |
| 6995 | Local<Script> script = v8_compile("obj.x = 4; obj.x" ); |
| 6996 | for (int i = 0; i < 10; i++) { |
| 6997 | CHECK(xValue.IsEmpty()); |
| 6998 | script->Run(context.local()).ToLocalChecked(); |
| 6999 | CHECK(v8_num(4) |
| 7000 | ->Equals(context.local(), |
| 7001 | Local<Value>::New(CcTest::isolate(), xValue)) |
| 7002 | .FromJust()); |
| 7003 | xValue.Reset(); |
| 7004 | } |
| 7005 | } |
| 7006 | |
| 7007 | |
| 7008 | THREADED_TEST(NoAccessors) { |
| 7009 | v8::Isolate* isolate = CcTest::isolate(); |
| 7010 | v8::HandleScope scope(isolate); |
| 7011 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 7012 | templ->SetAccessor(v8_str("x" ), |
| 7013 | static_cast<v8::AccessorGetterCallback>(nullptr), nullptr, |
| 7014 | v8_str("donut" )); |
| 7015 | LocalContext context; |
| 7016 | CHECK(context->Global() |
| 7017 | ->Set(context.local(), v8_str("obj" ), |
| 7018 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 7019 | .FromJust()); |
| 7020 | Local<Script> script = v8_compile("obj.x = 4; obj.x" ); |
| 7021 | for (int i = 0; i < 10; i++) { |
| 7022 | script->Run(context.local()).ToLocalChecked(); |
| 7023 | } |
| 7024 | } |
| 7025 | |
| 7026 | |
| 7027 | THREADED_TEST(MultiContexts) { |
| 7028 | v8::Isolate* isolate = CcTest::isolate(); |
| 7029 | v8::HandleScope scope(isolate); |
| 7030 | v8::Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 7031 | templ->Set(v8_str("dummy" ), |
| 7032 | v8::FunctionTemplate::New(isolate, DummyCallHandler)); |
| 7033 | |
| 7034 | Local<String> password = v8_str("Password" ); |
| 7035 | |
| 7036 | // Create an environment |
| 7037 | LocalContext context0(nullptr, templ); |
| 7038 | context0->SetSecurityToken(password); |
| 7039 | v8::Local<v8::Object> global0 = context0->Global(); |
| 7040 | CHECK(global0->Set(context0.local(), v8_str("custom" ), v8_num(1234)) |
| 7041 | .FromJust()); |
| 7042 | CHECK_EQ(1234, global0->Get(context0.local(), v8_str("custom" )) |
| 7043 | .ToLocalChecked() |
| 7044 | ->Int32Value(context0.local()) |
| 7045 | .FromJust()); |
| 7046 | |
| 7047 | // Create an independent environment |
| 7048 | LocalContext context1(nullptr, templ); |
| 7049 | context1->SetSecurityToken(password); |
| 7050 | v8::Local<v8::Object> global1 = context1->Global(); |
| 7051 | CHECK(global1->Set(context1.local(), v8_str("custom" ), v8_num(1234)) |
| 7052 | .FromJust()); |
| 7053 | CHECK(!global0->Equals(context1.local(), global1).FromJust()); |
| 7054 | CHECK_EQ(1234, global0->Get(context1.local(), v8_str("custom" )) |
| 7055 | .ToLocalChecked() |
| 7056 | ->Int32Value(context0.local()) |
| 7057 | .FromJust()); |
| 7058 | CHECK_EQ(1234, global1->Get(context1.local(), v8_str("custom" )) |
| 7059 | .ToLocalChecked() |
| 7060 | ->Int32Value(context1.local()) |
| 7061 | .FromJust()); |
| 7062 | |
| 7063 | // Now create a new context with the old global |
| 7064 | LocalContext context2(nullptr, templ, global1); |
| 7065 | context2->SetSecurityToken(password); |
| 7066 | v8::Local<v8::Object> global2 = context2->Global(); |
| 7067 | CHECK(global1->Equals(context2.local(), global2).FromJust()); |
| 7068 | CHECK_EQ(0, global1->Get(context2.local(), v8_str("custom" )) |
| 7069 | .ToLocalChecked() |
| 7070 | ->Int32Value(context1.local()) |
| 7071 | .FromJust()); |
| 7072 | CHECK_EQ(0, global2->Get(context2.local(), v8_str("custom" )) |
| 7073 | .ToLocalChecked() |
| 7074 | ->Int32Value(context2.local()) |
| 7075 | .FromJust()); |
| 7076 | } |
| 7077 | |
| 7078 | |
| 7079 | THREADED_TEST(FunctionPrototypeAcrossContexts) { |
| 7080 | // Make sure that functions created by cloning boilerplates cannot |
| 7081 | // communicate through their __proto__ field. |
| 7082 | |
| 7083 | v8::HandleScope scope(CcTest::isolate()); |
| 7084 | |
| 7085 | LocalContext env0; |
| 7086 | v8::Local<v8::Object> global0 = env0->Global(); |
| 7087 | v8::Local<v8::Object> object0 = global0->Get(env0.local(), v8_str("Object" )) |
| 7088 | .ToLocalChecked() |
| 7089 | .As<v8::Object>(); |
| 7090 | v8::Local<v8::Object> tostring0 = |
| 7091 | object0->Get(env0.local(), v8_str("toString" )) |
| 7092 | .ToLocalChecked() |
| 7093 | .As<v8::Object>(); |
| 7094 | v8::Local<v8::Object> proto0 = |
| 7095 | tostring0->Get(env0.local(), v8_str("__proto__" )) |
| 7096 | .ToLocalChecked() |
| 7097 | .As<v8::Object>(); |
| 7098 | CHECK(proto0->Set(env0.local(), v8_str("custom" ), v8_num(1234)).FromJust()); |
| 7099 | |
| 7100 | LocalContext env1; |
| 7101 | v8::Local<v8::Object> global1 = env1->Global(); |
| 7102 | v8::Local<v8::Object> object1 = global1->Get(env1.local(), v8_str("Object" )) |
| 7103 | .ToLocalChecked() |
| 7104 | .As<v8::Object>(); |
| 7105 | v8::Local<v8::Object> tostring1 = |
| 7106 | object1->Get(env1.local(), v8_str("toString" )) |
| 7107 | .ToLocalChecked() |
| 7108 | .As<v8::Object>(); |
| 7109 | v8::Local<v8::Object> proto1 = |
| 7110 | tostring1->Get(env1.local(), v8_str("__proto__" )) |
| 7111 | .ToLocalChecked() |
| 7112 | .As<v8::Object>(); |
| 7113 | CHECK(!proto1->Has(env1.local(), v8_str("custom" )).FromJust()); |
| 7114 | } |
| 7115 | |
| 7116 | |
| 7117 | THREADED_TEST(Regress892105) { |
| 7118 | // Make sure that object and array literals created by cloning |
| 7119 | // boilerplates cannot communicate through their __proto__ |
| 7120 | // field. This is rather difficult to check, but we try to add stuff |
| 7121 | // to Object.prototype and Array.prototype and create a new |
| 7122 | // environment. This should succeed. |
| 7123 | |
| 7124 | v8::HandleScope scope(CcTest::isolate()); |
| 7125 | |
| 7126 | Local<String> source = v8_str( |
| 7127 | "Object.prototype.obj = 1234;" |
| 7128 | "Array.prototype.arr = 4567;" |
| 7129 | "8901" ); |
| 7130 | |
| 7131 | LocalContext env0; |
| 7132 | Local<Script> script0 = v8_compile(source); |
| 7133 | CHECK_EQ(8901.0, script0->Run(env0.local()) |
| 7134 | .ToLocalChecked() |
| 7135 | ->NumberValue(env0.local()) |
| 7136 | .FromJust()); |
| 7137 | |
| 7138 | LocalContext env1; |
| 7139 | Local<Script> script1 = v8_compile(source); |
| 7140 | CHECK_EQ(8901.0, script1->Run(env1.local()) |
| 7141 | .ToLocalChecked() |
| 7142 | ->NumberValue(env1.local()) |
| 7143 | .FromJust()); |
| 7144 | } |
| 7145 | |
| 7146 | static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 7147 | args.GetReturnValue().Set(args.This()); |
| 7148 | } |
| 7149 | |
| 7150 | THREADED_TEST(UndetectableObject) { |
| 7151 | LocalContext env; |
| 7152 | v8::HandleScope scope(env->GetIsolate()); |
| 7153 | |
| 7154 | Local<v8::FunctionTemplate> desc = |
| 7155 | v8::FunctionTemplate::New(env->GetIsolate()); |
| 7156 | desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| 7157 | desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| 7158 | |
| 7159 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 7160 | .ToLocalChecked() |
| 7161 | ->NewInstance(env.local()) |
| 7162 | .ToLocalChecked(); |
| 7163 | CHECK( |
| 7164 | env->Global()->Set(env.local(), v8_str("undetectable" ), obj).FromJust()); |
| 7165 | |
| 7166 | ExpectString("undetectable.toString()" , "[object Object]" ); |
| 7167 | ExpectString("typeof undetectable" , "undefined" ); |
| 7168 | ExpectString("typeof(undetectable)" , "undefined" ); |
| 7169 | ExpectBoolean("typeof undetectable == 'undefined'" , true); |
| 7170 | ExpectBoolean("typeof undetectable == 'object'" , false); |
| 7171 | ExpectBoolean("if (undetectable) { true; } else { false; }" , false); |
| 7172 | ExpectBoolean("!undetectable" , true); |
| 7173 | |
| 7174 | ExpectObject("true&&undetectable" , obj); |
| 7175 | ExpectBoolean("false&&undetectable" , false); |
| 7176 | ExpectBoolean("true||undetectable" , true); |
| 7177 | ExpectObject("false||undetectable" , obj); |
| 7178 | |
| 7179 | ExpectObject("undetectable&&true" , obj); |
| 7180 | ExpectObject("undetectable&&false" , obj); |
| 7181 | ExpectBoolean("undetectable||true" , true); |
| 7182 | ExpectBoolean("undetectable||false" , false); |
| 7183 | |
| 7184 | ExpectBoolean("undetectable==null" , true); |
| 7185 | ExpectBoolean("null==undetectable" , true); |
| 7186 | ExpectBoolean("undetectable==undefined" , true); |
| 7187 | ExpectBoolean("undefined==undetectable" , true); |
| 7188 | ExpectBoolean("undetectable==undetectable" , true); |
| 7189 | |
| 7190 | |
| 7191 | ExpectBoolean("undetectable===null" , false); |
| 7192 | ExpectBoolean("null===undetectable" , false); |
| 7193 | ExpectBoolean("undetectable===undefined" , false); |
| 7194 | ExpectBoolean("undefined===undetectable" , false); |
| 7195 | ExpectBoolean("undetectable===undetectable" , true); |
| 7196 | } |
| 7197 | |
| 7198 | |
| 7199 | THREADED_TEST(VoidLiteral) { |
| 7200 | LocalContext env; |
| 7201 | v8::Isolate* isolate = env->GetIsolate(); |
| 7202 | v8::HandleScope scope(isolate); |
| 7203 | |
| 7204 | Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| 7205 | desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| 7206 | desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| 7207 | |
| 7208 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 7209 | .ToLocalChecked() |
| 7210 | ->NewInstance(env.local()) |
| 7211 | .ToLocalChecked(); |
| 7212 | CHECK( |
| 7213 | env->Global()->Set(env.local(), v8_str("undetectable" ), obj).FromJust()); |
| 7214 | |
| 7215 | ExpectBoolean("undefined == void 0" , true); |
| 7216 | ExpectBoolean("undetectable == void 0" , true); |
| 7217 | ExpectBoolean("null == void 0" , true); |
| 7218 | ExpectBoolean("undefined === void 0" , true); |
| 7219 | ExpectBoolean("undetectable === void 0" , false); |
| 7220 | ExpectBoolean("null === void 0" , false); |
| 7221 | |
| 7222 | ExpectBoolean("void 0 == undefined" , true); |
| 7223 | ExpectBoolean("void 0 == undetectable" , true); |
| 7224 | ExpectBoolean("void 0 == null" , true); |
| 7225 | ExpectBoolean("void 0 === undefined" , true); |
| 7226 | ExpectBoolean("void 0 === undetectable" , false); |
| 7227 | ExpectBoolean("void 0 === null" , false); |
| 7228 | |
| 7229 | ExpectString( |
| 7230 | "(function() {" |
| 7231 | " try {" |
| 7232 | " return x === void 0;" |
| 7233 | " } catch(e) {" |
| 7234 | " return e.toString();" |
| 7235 | " }" |
| 7236 | "})()" , |
| 7237 | "ReferenceError: x is not defined" ); |
| 7238 | ExpectString( |
| 7239 | "(function() {" |
| 7240 | " try {" |
| 7241 | " return void 0 === x;" |
| 7242 | " } catch(e) {" |
| 7243 | " return e.toString();" |
| 7244 | " }" |
| 7245 | "})()" , |
| 7246 | "ReferenceError: x is not defined" ); |
| 7247 | } |
| 7248 | |
| 7249 | |
| 7250 | THREADED_TEST(ExtensibleOnUndetectable) { |
| 7251 | LocalContext env; |
| 7252 | v8::Isolate* isolate = env->GetIsolate(); |
| 7253 | v8::HandleScope scope(isolate); |
| 7254 | |
| 7255 | Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| 7256 | desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| 7257 | desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| 7258 | |
| 7259 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 7260 | .ToLocalChecked() |
| 7261 | ->NewInstance(env.local()) |
| 7262 | .ToLocalChecked(); |
| 7263 | CHECK( |
| 7264 | env->Global()->Set(env.local(), v8_str("undetectable" ), obj).FromJust()); |
| 7265 | |
| 7266 | Local<String> source = v8_str( |
| 7267 | "undetectable.x = 42;" |
| 7268 | "undetectable.x" ); |
| 7269 | |
| 7270 | Local<Script> script = v8_compile(source); |
| 7271 | |
| 7272 | CHECK(v8::Integer::New(isolate, 42) |
| 7273 | ->Equals(env.local(), script->Run(env.local()).ToLocalChecked()) |
| 7274 | .FromJust()); |
| 7275 | |
| 7276 | ExpectBoolean("Object.isExtensible(undetectable)" , true); |
| 7277 | |
| 7278 | source = v8_str("Object.preventExtensions(undetectable);" ); |
| 7279 | script = v8_compile(source); |
| 7280 | script->Run(env.local()).ToLocalChecked(); |
| 7281 | ExpectBoolean("Object.isExtensible(undetectable)" , false); |
| 7282 | |
| 7283 | source = v8_str("undetectable.y = 2000;" ); |
| 7284 | script = v8_compile(source); |
| 7285 | script->Run(env.local()).ToLocalChecked(); |
| 7286 | ExpectBoolean("undetectable.y == undefined" , true); |
| 7287 | } |
| 7288 | |
| 7289 | THREADED_TEST(ConstructCallWithUndetectable) { |
| 7290 | LocalContext env; |
| 7291 | v8::Isolate* isolate = env->GetIsolate(); |
| 7292 | v8::HandleScope scope(isolate); |
| 7293 | |
| 7294 | Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| 7295 | desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| 7296 | desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| 7297 | |
| 7298 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 7299 | .ToLocalChecked() |
| 7300 | ->NewInstance(env.local()) |
| 7301 | .ToLocalChecked(); |
| 7302 | CHECK( |
| 7303 | env->Global()->Set(env.local(), v8_str("undetectable" ), obj).FromJust()); |
| 7304 | |
| 7305 | // Undetectable object cannot be called as constructor. |
| 7306 | v8::TryCatch try_catch(env->GetIsolate()); |
| 7307 | CHECK(CompileRun("new undetectable()" ).IsEmpty()); |
| 7308 | CHECK(try_catch.HasCaught()); |
| 7309 | String::Utf8Value exception_value(env->GetIsolate(), try_catch.Exception()); |
| 7310 | CHECK_EQ(0, strcmp("TypeError: undetectable is not a constructor" , |
| 7311 | *exception_value)); |
| 7312 | } |
| 7313 | |
| 7314 | static int increment_callback_counter = 0; |
| 7315 | |
| 7316 | static void IncrementCounterConstructCallback( |
| 7317 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 7318 | increment_callback_counter++; |
| 7319 | CHECK(Local<Object>::Cast(args.NewTarget()) |
| 7320 | ->Set(args.GetIsolate()->GetCurrentContext(), v8_str("counter" ), |
| 7321 | v8_num(increment_callback_counter)) |
| 7322 | .FromJust()); |
| 7323 | args.GetReturnValue().Set(args.NewTarget()); |
| 7324 | } |
| 7325 | |
| 7326 | THREADED_TEST(SetCallAsFunctionHandlerConstructor) { |
| 7327 | LocalContext env; |
| 7328 | v8::Isolate* isolate = env->GetIsolate(); |
| 7329 | v8::HandleScope scope(isolate); |
| 7330 | |
| 7331 | Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| 7332 | desc->InstanceTemplate()->SetCallAsFunctionHandler( |
| 7333 | IncrementCounterConstructCallback); // callable |
| 7334 | |
| 7335 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 7336 | .ToLocalChecked() |
| 7337 | ->NewInstance(env.local()) |
| 7338 | .ToLocalChecked(); |
| 7339 | CHECK(env->Global()->Set(env.local(), v8_str("Counter" ), obj).FromJust()); |
| 7340 | |
| 7341 | ExpectInt32("(new Counter()).counter" , 1); |
| 7342 | CHECK_EQ(1, increment_callback_counter); |
| 7343 | ExpectInt32("(new Counter()).counter" , 2); |
| 7344 | CHECK_EQ(2, increment_callback_counter); |
| 7345 | } |
| 7346 | // The point of this test is type checking. We run it only so compilers |
| 7347 | // don't complain about an unused function. |
| 7348 | TEST(PersistentHandles) { |
| 7349 | LocalContext env; |
| 7350 | v8::Isolate* isolate = CcTest::isolate(); |
| 7351 | v8::HandleScope scope(isolate); |
| 7352 | Local<String> str = v8_str("foo" ); |
| 7353 | v8::Persistent<String> p_str(isolate, str); |
| 7354 | p_str.Reset(); |
| 7355 | Local<Script> scr = v8_compile("" ); |
| 7356 | v8::Persistent<Script> p_scr(isolate, scr); |
| 7357 | p_scr.Reset(); |
| 7358 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 7359 | v8::Persistent<ObjectTemplate> p_templ(isolate, templ); |
| 7360 | p_templ.Reset(); |
| 7361 | } |
| 7362 | |
| 7363 | |
| 7364 | static void HandleLogDelegator( |
| 7365 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 7366 | ApiTestFuzzer::Fuzz(); |
| 7367 | } |
| 7368 | |
| 7369 | |
| 7370 | THREADED_TEST(GlobalObjectTemplate) { |
| 7371 | v8::Isolate* isolate = CcTest::isolate(); |
| 7372 | v8::HandleScope handle_scope(isolate); |
| 7373 | Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); |
| 7374 | global_template->Set(v8_str("JSNI_Log" ), |
| 7375 | v8::FunctionTemplate::New(isolate, HandleLogDelegator)); |
| 7376 | v8::Local<Context> context = Context::New(isolate, nullptr, global_template); |
| 7377 | Context::Scope context_scope(context); |
| 7378 | CompileRun("JSNI_Log('LOG')" ); |
| 7379 | } |
| 7380 | |
| 7381 | |
| 7382 | static const char* kSimpleExtensionSource = |
| 7383 | "function Foo() {" |
| 7384 | " return 4;" |
| 7385 | "}" ; |
| 7386 | |
| 7387 | |
| 7388 | TEST(SimpleExtensions) { |
| 7389 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7390 | v8::RegisterExtension( |
| 7391 | v8::base::make_unique<Extension>("simpletest" , kSimpleExtensionSource)); |
| 7392 | const char* extension_names[] = {"simpletest" }; |
| 7393 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7394 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7395 | Context::Scope lock(context); |
| 7396 | v8::Local<Value> result = CompileRun("Foo()" ); |
| 7397 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4)) |
| 7398 | .FromJust()); |
| 7399 | } |
| 7400 | |
| 7401 | |
| 7402 | static const char* kStackTraceFromExtensionSource = |
| 7403 | "function foo() {" |
| 7404 | " throw new Error();" |
| 7405 | "}" |
| 7406 | "function bar() {" |
| 7407 | " foo();" |
| 7408 | "}" ; |
| 7409 | |
| 7410 | |
| 7411 | TEST(StackTraceInExtension) { |
| 7412 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7413 | v8::RegisterExtension(v8::base::make_unique<Extension>( |
| 7414 | "stacktracetest" , kStackTraceFromExtensionSource)); |
| 7415 | const char* extension_names[] = {"stacktracetest" }; |
| 7416 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7417 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7418 | Context::Scope lock(context); |
| 7419 | CompileRun( |
| 7420 | "function user() { bar(); }" |
| 7421 | "var error;" |
| 7422 | "try{ user(); } catch (e) { error = e; }" ); |
| 7423 | CHECK_EQ(-1, v8_run_int32value(v8_compile("error.stack.indexOf('foo')" ))); |
| 7424 | CHECK_EQ(-1, v8_run_int32value(v8_compile("error.stack.indexOf('bar')" ))); |
| 7425 | CHECK_NE(-1, v8_run_int32value(v8_compile("error.stack.indexOf('user')" ))); |
| 7426 | } |
| 7427 | |
| 7428 | |
| 7429 | TEST(NullExtensions) { |
| 7430 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7431 | v8::RegisterExtension(v8::base::make_unique<Extension>("nulltest" , nullptr)); |
| 7432 | const char* extension_names[] = {"nulltest" }; |
| 7433 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7434 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7435 | Context::Scope lock(context); |
| 7436 | v8::Local<Value> result = CompileRun("1+3" ); |
| 7437 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4)) |
| 7438 | .FromJust()); |
| 7439 | } |
| 7440 | |
| 7441 | static const char* kEmbeddedExtensionSource = |
| 7442 | "function Ret54321(){return 54321;}~~@@$" |
| 7443 | "$%% THIS IS A SERIES OF NON-nullptr-TERMINATED STRINGS." ; |
| 7444 | static const int kEmbeddedExtensionSourceValidLen = 34; |
| 7445 | |
| 7446 | |
| 7447 | TEST(ExtensionMissingSourceLength) { |
| 7448 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7449 | v8::RegisterExtension(v8::base::make_unique<Extension>( |
| 7450 | "srclentest_fail" , kEmbeddedExtensionSource)); |
| 7451 | const char* extension_names[] = {"srclentest_fail" }; |
| 7452 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7453 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7454 | CHECK_NULL(*context); |
| 7455 | } |
| 7456 | |
| 7457 | |
| 7458 | TEST(ExtensionWithSourceLength) { |
| 7459 | for (int source_len = kEmbeddedExtensionSourceValidLen - 1; |
| 7460 | source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { |
| 7461 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7462 | i::ScopedVector<char> extension_name(32); |
| 7463 | i::SNPrintF(extension_name, "ext #%d" , source_len); |
| 7464 | v8::RegisterExtension(v8::base::make_unique<Extension>( |
| 7465 | extension_name.start(), kEmbeddedExtensionSource, 0, nullptr, |
| 7466 | source_len)); |
| 7467 | const char* extension_names[1] = {extension_name.start()}; |
| 7468 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7469 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7470 | if (source_len == kEmbeddedExtensionSourceValidLen) { |
| 7471 | Context::Scope lock(context); |
| 7472 | v8::Local<Value> result = CompileRun("Ret54321()" ); |
| 7473 | CHECK(v8::Integer::New(CcTest::isolate(), 54321) |
| 7474 | ->Equals(context, result) |
| 7475 | .FromJust()); |
| 7476 | } else { |
| 7477 | // Anything but exactly the right length should fail to compile. |
| 7478 | CHECK_NULL(*context); |
| 7479 | } |
| 7480 | } |
| 7481 | } |
| 7482 | |
| 7483 | |
| 7484 | static const char* kEvalExtensionSource1 = |
| 7485 | "function UseEval1() {" |
| 7486 | " var x = 42;" |
| 7487 | " return eval('x');" |
| 7488 | "}" ; |
| 7489 | |
| 7490 | |
| 7491 | static const char* kEvalExtensionSource2 = |
| 7492 | "(function() {" |
| 7493 | " var x = 42;" |
| 7494 | " function e() {" |
| 7495 | " return eval('x');" |
| 7496 | " }" |
| 7497 | " this.UseEval2 = e;" |
| 7498 | "})()" ; |
| 7499 | |
| 7500 | |
| 7501 | TEST(UseEvalFromExtension) { |
| 7502 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7503 | v8::RegisterExtension( |
| 7504 | v8::base::make_unique<Extension>("evaltest1" , kEvalExtensionSource1)); |
| 7505 | v8::RegisterExtension( |
| 7506 | v8::base::make_unique<Extension>("evaltest2" , kEvalExtensionSource2)); |
| 7507 | const char* extension_names[] = {"evaltest1" , "evaltest2" }; |
| 7508 | v8::ExtensionConfiguration extensions(2, extension_names); |
| 7509 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7510 | Context::Scope lock(context); |
| 7511 | v8::Local<Value> result = CompileRun("UseEval1()" ); |
| 7512 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42)) |
| 7513 | .FromJust()); |
| 7514 | result = CompileRun("UseEval2()" ); |
| 7515 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42)) |
| 7516 | .FromJust()); |
| 7517 | } |
| 7518 | |
| 7519 | |
| 7520 | static const char* kWithExtensionSource1 = |
| 7521 | "function UseWith1() {" |
| 7522 | " var x = 42;" |
| 7523 | " with({x:87}) { return x; }" |
| 7524 | "}" ; |
| 7525 | |
| 7526 | |
| 7527 | static const char* kWithExtensionSource2 = |
| 7528 | "(function() {" |
| 7529 | " var x = 42;" |
| 7530 | " function e() {" |
| 7531 | " with ({x:87}) { return x; }" |
| 7532 | " }" |
| 7533 | " this.UseWith2 = e;" |
| 7534 | "})()" ; |
| 7535 | |
| 7536 | |
| 7537 | TEST(UseWithFromExtension) { |
| 7538 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7539 | v8::RegisterExtension( |
| 7540 | v8::base::make_unique<Extension>("withtest1" , kWithExtensionSource1)); |
| 7541 | v8::RegisterExtension( |
| 7542 | v8::base::make_unique<Extension>("withtest2" , kWithExtensionSource2)); |
| 7543 | const char* extension_names[] = {"withtest1" , "withtest2" }; |
| 7544 | v8::ExtensionConfiguration extensions(2, extension_names); |
| 7545 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7546 | Context::Scope lock(context); |
| 7547 | v8::Local<Value> result = CompileRun("UseWith1()" ); |
| 7548 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 87)) |
| 7549 | .FromJust()); |
| 7550 | result = CompileRun("UseWith2()" ); |
| 7551 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 87)) |
| 7552 | .FromJust()); |
| 7553 | } |
| 7554 | |
| 7555 | |
| 7556 | TEST(AutoExtensions) { |
| 7557 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7558 | auto extension = |
| 7559 | v8::base::make_unique<Extension>("autotest" , kSimpleExtensionSource); |
| 7560 | extension->set_auto_enable(true); |
| 7561 | v8::RegisterExtension(std::move(extension)); |
| 7562 | v8::Local<Context> context = Context::New(CcTest::isolate()); |
| 7563 | Context::Scope lock(context); |
| 7564 | v8::Local<Value> result = CompileRun("Foo()" ); |
| 7565 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 4)) |
| 7566 | .FromJust()); |
| 7567 | } |
| 7568 | |
| 7569 | |
| 7570 | static const char* kSyntaxErrorInExtensionSource = "[" ; |
| 7571 | |
| 7572 | |
| 7573 | // Test that a syntax error in an extension does not cause a fatal |
| 7574 | // error but results in an empty context. |
| 7575 | TEST(SyntaxErrorExtensions) { |
| 7576 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7577 | v8::RegisterExtension(v8::base::make_unique<Extension>( |
| 7578 | "syntaxerror" , kSyntaxErrorInExtensionSource)); |
| 7579 | const char* extension_names[] = {"syntaxerror" }; |
| 7580 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7581 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7582 | CHECK(context.IsEmpty()); |
| 7583 | } |
| 7584 | |
| 7585 | |
| 7586 | static const char* kExceptionInExtensionSource = "throw 42" ; |
| 7587 | |
| 7588 | |
| 7589 | // Test that an exception when installing an extension does not cause |
| 7590 | // a fatal error but results in an empty context. |
| 7591 | TEST(ExceptionExtensions) { |
| 7592 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7593 | v8::RegisterExtension(v8::base::make_unique<Extension>( |
| 7594 | "exception" , kExceptionInExtensionSource)); |
| 7595 | const char* extension_names[] = {"exception" }; |
| 7596 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7597 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7598 | CHECK(context.IsEmpty()); |
| 7599 | } |
| 7600 | |
| 7601 | static const char* kNativeCallInExtensionSource = |
| 7602 | "function call_runtime_last_index_of(x) {" |
| 7603 | " return %StringLastIndexOf(x, 'bob');" |
| 7604 | "}" ; |
| 7605 | |
| 7606 | static const char* kNativeCallTest = |
| 7607 | "call_runtime_last_index_of('bobbobboellebobboellebobbob');" ; |
| 7608 | |
| 7609 | // Test that a native runtime calls are supported in extensions. |
| 7610 | TEST(NativeCallInExtensions) { |
| 7611 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7612 | v8::RegisterExtension(v8::base::make_unique<Extension>( |
| 7613 | "nativecall" , kNativeCallInExtensionSource)); |
| 7614 | const char* extension_names[] = {"nativecall" }; |
| 7615 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7616 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7617 | Context::Scope lock(context); |
| 7618 | v8::Local<Value> result = CompileRun(kNativeCallTest); |
| 7619 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 24)) |
| 7620 | .FromJust()); |
| 7621 | } |
| 7622 | |
| 7623 | |
| 7624 | class NativeFunctionExtension : public Extension { |
| 7625 | public: |
| 7626 | NativeFunctionExtension(const char* name, const char* source, |
| 7627 | v8::FunctionCallback fun = &Echo) |
| 7628 | : Extension(name, source), function_(fun) {} |
| 7629 | |
| 7630 | v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| 7631 | v8::Isolate* isolate, v8::Local<v8::String> name) override { |
| 7632 | return v8::FunctionTemplate::New(isolate, function_); |
| 7633 | } |
| 7634 | |
| 7635 | static void Echo(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 7636 | if (args.Length() >= 1) args.GetReturnValue().Set(args[0]); |
| 7637 | } |
| 7638 | |
| 7639 | private: |
| 7640 | v8::FunctionCallback function_; |
| 7641 | }; |
| 7642 | |
| 7643 | |
| 7644 | TEST(NativeFunctionDeclaration) { |
| 7645 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7646 | const char* name = "nativedecl" ; |
| 7647 | v8::RegisterExtension(v8::base::make_unique<NativeFunctionExtension>( |
| 7648 | name, "native function foo();" )); |
| 7649 | const char* extension_names[] = {name}; |
| 7650 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7651 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7652 | Context::Scope lock(context); |
| 7653 | v8::Local<Value> result = CompileRun("foo(42);" ); |
| 7654 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 42)) |
| 7655 | .FromJust()); |
| 7656 | } |
| 7657 | |
| 7658 | |
| 7659 | TEST(NativeFunctionDeclarationError) { |
| 7660 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7661 | const char* name = "nativedeclerr" ; |
| 7662 | // Syntax error in extension code. |
| 7663 | v8::RegisterExtension(v8::base::make_unique<NativeFunctionExtension>( |
| 7664 | name, "native\nfunction foo();" )); |
| 7665 | const char* extension_names[] = {name}; |
| 7666 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7667 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7668 | CHECK(context.IsEmpty()); |
| 7669 | } |
| 7670 | |
| 7671 | |
| 7672 | TEST(NativeFunctionDeclarationErrorEscape) { |
| 7673 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7674 | const char* name = "nativedeclerresc" ; |
| 7675 | // Syntax error in extension code - escape code in "native" means that |
| 7676 | // it's not treated as a keyword. |
| 7677 | v8::RegisterExtension(v8::base::make_unique<NativeFunctionExtension>( |
| 7678 | name, "nativ\\u0065 function foo();" )); |
| 7679 | const char* extension_names[] = {name}; |
| 7680 | v8::ExtensionConfiguration extensions(1, extension_names); |
| 7681 | v8::Local<Context> context = Context::New(CcTest::isolate(), &extensions); |
| 7682 | CHECK(context.IsEmpty()); |
| 7683 | } |
| 7684 | |
| 7685 | |
| 7686 | static void CheckDependencies(const char* name, const char* expected) { |
| 7687 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7688 | v8::ExtensionConfiguration config(1, &name); |
| 7689 | LocalContext context(&config); |
| 7690 | CHECK( |
| 7691 | v8_str(expected) |
| 7692 | ->Equals(context.local(), context->Global() |
| 7693 | ->Get(context.local(), v8_str("loaded" )) |
| 7694 | .ToLocalChecked()) |
| 7695 | .FromJust()); |
| 7696 | } |
| 7697 | |
| 7698 | |
| 7699 | /* |
| 7700 | * Configuration: |
| 7701 | * |
| 7702 | * /-- B <--\ |
| 7703 | * A <- -- D <-- E |
| 7704 | * \-- C <--/ |
| 7705 | */ |
| 7706 | THREADED_TEST(ExtensionDependency) { |
| 7707 | static const char* kEDeps[] = {"D" }; |
| 7708 | v8::RegisterExtension( |
| 7709 | v8::base::make_unique<Extension>("E" , "this.loaded += 'E';" , 1, kEDeps)); |
| 7710 | static const char* kDDeps[] = {"B" , "C" }; |
| 7711 | v8::RegisterExtension( |
| 7712 | v8::base::make_unique<Extension>("D" , "this.loaded += 'D';" , 2, kDDeps)); |
| 7713 | static const char* kBCDeps[] = {"A" }; |
| 7714 | v8::RegisterExtension( |
| 7715 | v8::base::make_unique<Extension>("B" , "this.loaded += 'B';" , 1, kBCDeps)); |
| 7716 | v8::RegisterExtension( |
| 7717 | v8::base::make_unique<Extension>("C" , "this.loaded += 'C';" , 1, kBCDeps)); |
| 7718 | v8::RegisterExtension( |
| 7719 | v8::base::make_unique<Extension>("A" , "this.loaded += 'A';" )); |
| 7720 | CheckDependencies("A" , "undefinedA" ); |
| 7721 | CheckDependencies("B" , "undefinedAB" ); |
| 7722 | CheckDependencies("C" , "undefinedAC" ); |
| 7723 | CheckDependencies("D" , "undefinedABCD" ); |
| 7724 | CheckDependencies("E" , "undefinedABCDE" ); |
| 7725 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7726 | static const char* exts[2] = {"C" , "E" }; |
| 7727 | v8::ExtensionConfiguration config(2, exts); |
| 7728 | LocalContext context(&config); |
| 7729 | CHECK( |
| 7730 | v8_str("undefinedACBDE" ) |
| 7731 | ->Equals(context.local(), context->Global() |
| 7732 | ->Get(context.local(), v8_str("loaded" )) |
| 7733 | .ToLocalChecked()) |
| 7734 | .FromJust()); |
| 7735 | } |
| 7736 | |
| 7737 | |
| 7738 | static const char* kExtensionTestScript = |
| 7739 | "native function A();" |
| 7740 | "native function B();" |
| 7741 | "native function C();" |
| 7742 | "function Foo(i) {" |
| 7743 | " if (i == 0) return A();" |
| 7744 | " if (i == 1) return B();" |
| 7745 | " if (i == 2) return C();" |
| 7746 | "}" ; |
| 7747 | |
| 7748 | |
| 7749 | static void CallFun(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 7750 | ApiTestFuzzer::Fuzz(); |
| 7751 | if (args.IsConstructCall()) { |
| 7752 | CHECK(args.This() |
| 7753 | ->Set(args.GetIsolate()->GetCurrentContext(), v8_str("data" ), |
| 7754 | args.Data()) |
| 7755 | .FromJust()); |
| 7756 | args.GetReturnValue().SetNull(); |
| 7757 | return; |
| 7758 | } |
| 7759 | args.GetReturnValue().Set(args.Data()); |
| 7760 | } |
| 7761 | |
| 7762 | |
| 7763 | class FunctionExtension : public Extension { |
| 7764 | public: |
| 7765 | FunctionExtension() : Extension("functiontest" , kExtensionTestScript) {} |
| 7766 | v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| 7767 | v8::Isolate* isolate, v8::Local<String> name) override; |
| 7768 | }; |
| 7769 | |
| 7770 | |
| 7771 | static int lookup_count = 0; |
| 7772 | v8::Local<v8::FunctionTemplate> FunctionExtension::GetNativeFunctionTemplate( |
| 7773 | v8::Isolate* isolate, v8::Local<String> name) { |
| 7774 | lookup_count++; |
| 7775 | if (name->StrictEquals(v8_str("A" ))) { |
| 7776 | return v8::FunctionTemplate::New(isolate, CallFun, |
| 7777 | v8::Integer::New(isolate, 8)); |
| 7778 | } else if (name->StrictEquals(v8_str("B" ))) { |
| 7779 | return v8::FunctionTemplate::New(isolate, CallFun, |
| 7780 | v8::Integer::New(isolate, 7)); |
| 7781 | } else if (name->StrictEquals(v8_str("C" ))) { |
| 7782 | return v8::FunctionTemplate::New(isolate, CallFun, |
| 7783 | v8::Integer::New(isolate, 6)); |
| 7784 | } else { |
| 7785 | return v8::Local<v8::FunctionTemplate>(); |
| 7786 | } |
| 7787 | } |
| 7788 | |
| 7789 | |
| 7790 | THREADED_TEST(FunctionLookup) { |
| 7791 | v8::RegisterExtension(v8::base::make_unique<FunctionExtension>()); |
| 7792 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7793 | static const char* exts[1] = {"functiontest" }; |
| 7794 | v8::ExtensionConfiguration config(1, exts); |
| 7795 | LocalContext context(&config); |
| 7796 | CHECK_EQ(3, lookup_count); |
| 7797 | CHECK(v8::Integer::New(CcTest::isolate(), 8) |
| 7798 | ->Equals(context.local(), CompileRun("Foo(0)" )) |
| 7799 | .FromJust()); |
| 7800 | CHECK(v8::Integer::New(CcTest::isolate(), 7) |
| 7801 | ->Equals(context.local(), CompileRun("Foo(1)" )) |
| 7802 | .FromJust()); |
| 7803 | CHECK(v8::Integer::New(CcTest::isolate(), 6) |
| 7804 | ->Equals(context.local(), CompileRun("Foo(2)" )) |
| 7805 | .FromJust()); |
| 7806 | } |
| 7807 | |
| 7808 | |
| 7809 | THREADED_TEST(NativeFunctionConstructCall) { |
| 7810 | v8::RegisterExtension(v8::base::make_unique<FunctionExtension>()); |
| 7811 | v8::HandleScope handle_scope(CcTest::isolate()); |
| 7812 | static const char* exts[1] = {"functiontest" }; |
| 7813 | v8::ExtensionConfiguration config(1, exts); |
| 7814 | LocalContext context(&config); |
| 7815 | for (int i = 0; i < 10; i++) { |
| 7816 | // Run a few times to ensure that allocation of objects doesn't |
| 7817 | // change behavior of a constructor function. |
| 7818 | CHECK(v8::Integer::New(CcTest::isolate(), 8) |
| 7819 | ->Equals(context.local(), CompileRun("(new A()).data" )) |
| 7820 | .FromJust()); |
| 7821 | CHECK(v8::Integer::New(CcTest::isolate(), 7) |
| 7822 | ->Equals(context.local(), CompileRun("(new B()).data" )) |
| 7823 | .FromJust()); |
| 7824 | CHECK(v8::Integer::New(CcTest::isolate(), 6) |
| 7825 | ->Equals(context.local(), CompileRun("(new C()).data" )) |
| 7826 | .FromJust()); |
| 7827 | } |
| 7828 | } |
| 7829 | |
| 7830 | |
| 7831 | static const char* last_location; |
| 7832 | static const char* last_message; |
| 7833 | void StoringErrorCallback(const char* location, const char* message) { |
| 7834 | if (last_location == nullptr) { |
| 7835 | last_location = location; |
| 7836 | last_message = message; |
| 7837 | } |
| 7838 | } |
| 7839 | |
| 7840 | |
| 7841 | // ErrorReporting creates a circular extensions configuration and |
| 7842 | // tests that the fatal error handler gets called. This renders V8 |
| 7843 | // unusable and therefore this test cannot be run in parallel. |
| 7844 | TEST(ErrorReporting) { |
| 7845 | CcTest::isolate()->SetFatalErrorHandler(StoringErrorCallback); |
| 7846 | static const char* aDeps[] = {"B" }; |
| 7847 | v8::RegisterExtension(v8::base::make_unique<Extension>("A" , "" , 1, aDeps)); |
| 7848 | static const char* bDeps[] = {"A" }; |
| 7849 | v8::RegisterExtension(v8::base::make_unique<Extension>("B" , "" , 1, bDeps)); |
| 7850 | last_location = nullptr; |
| 7851 | v8::ExtensionConfiguration config(1, bDeps); |
| 7852 | v8::Local<Context> context = Context::New(CcTest::isolate(), &config); |
| 7853 | CHECK(context.IsEmpty()); |
| 7854 | CHECK(last_location); |
| 7855 | } |
| 7856 | |
| 7857 | static size_t dcheck_count; |
| 7858 | void DcheckErrorCallback(const char* file, int line, const char* message) { |
| 7859 | last_message = message; |
| 7860 | ++dcheck_count; |
| 7861 | } |
| 7862 | |
| 7863 | TEST(DcheckErrorHandler) { |
| 7864 | V8::SetDcheckErrorHandler(DcheckErrorCallback); |
| 7865 | |
| 7866 | last_message = nullptr; |
| 7867 | dcheck_count = 0; |
| 7868 | |
| 7869 | DCHECK(false && "w00t" ); |
| 7870 | #ifdef DEBUG |
| 7871 | CHECK_EQ(dcheck_count, 1); |
| 7872 | CHECK(last_message); |
| 7873 | CHECK(std::string(last_message).find("w00t" ) != std::string::npos); |
| 7874 | #else |
| 7875 | // The DCHECK should be a noop in non-DEBUG builds. |
| 7876 | CHECK_EQ(dcheck_count, 0); |
| 7877 | #endif |
| 7878 | } |
| 7879 | |
| 7880 | static void MissingScriptInfoMessageListener(v8::Local<v8::Message> message, |
| 7881 | v8::Local<Value> data) { |
| 7882 | v8::Isolate* isolate = CcTest::isolate(); |
| 7883 | Local<Context> context = isolate->GetCurrentContext(); |
| 7884 | CHECK(message->GetScriptOrigin().ResourceName()->IsUndefined()); |
| 7885 | CHECK(v8::Undefined(isolate) |
| 7886 | ->Equals(context, message->GetScriptOrigin().ResourceName()) |
| 7887 | .FromJust()); |
| 7888 | message->GetLineNumber(context).FromJust(); |
| 7889 | message->GetSourceLine(context).ToLocalChecked(); |
| 7890 | } |
| 7891 | |
| 7892 | |
| 7893 | THREADED_TEST(ErrorWithMissingScriptInfo) { |
| 7894 | LocalContext context; |
| 7895 | v8::HandleScope scope(context->GetIsolate()); |
| 7896 | context->GetIsolate()->AddMessageListener(MissingScriptInfoMessageListener); |
| 7897 | CompileRun("throw Error()" ); |
| 7898 | context->GetIsolate()->RemoveMessageListeners( |
| 7899 | MissingScriptInfoMessageListener); |
| 7900 | } |
| 7901 | |
| 7902 | |
| 7903 | struct FlagAndPersistent { |
| 7904 | bool flag; |
| 7905 | v8::Global<v8::Object> handle; |
| 7906 | }; |
| 7907 | |
| 7908 | static void SetFlag(const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| 7909 | data.GetParameter()->flag = true; |
| 7910 | data.GetParameter()->handle.Reset(); |
| 7911 | } |
| 7912 | |
| 7913 | static void IndependentWeakHandle(bool global_gc, bool interlinked) { |
| 7914 | i::FLAG_stress_incremental_marking = false; |
| 7915 | // Parallel scavenge introduces too much fragmentation. |
| 7916 | i::FLAG_parallel_scavenge = false; |
| 7917 | v8::Isolate* iso = CcTest::isolate(); |
| 7918 | v8::HandleScope scope(iso); |
| 7919 | v8::Local<Context> context = Context::New(iso); |
| 7920 | Context::Scope context_scope(context); |
| 7921 | |
| 7922 | FlagAndPersistent object_a, object_b; |
| 7923 | |
| 7924 | size_t big_heap_size = 0; |
| 7925 | size_t big_array_size = 0; |
| 7926 | |
| 7927 | { |
| 7928 | v8::HandleScope handle_scope(iso); |
| 7929 | Local<Object> a(v8::Object::New(iso)); |
| 7930 | Local<Object> b(v8::Object::New(iso)); |
| 7931 | object_a.handle.Reset(iso, a); |
| 7932 | object_b.handle.Reset(iso, b); |
| 7933 | if (interlinked) { |
| 7934 | a->Set(context, v8_str("x" ), b).FromJust(); |
| 7935 | b->Set(context, v8_str("x" ), a).FromJust(); |
| 7936 | } |
| 7937 | if (global_gc) { |
| 7938 | CcTest::CollectAllGarbage(); |
| 7939 | } else { |
| 7940 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 7941 | } |
| 7942 | v8::Local<Value> big_array = v8::Array::New(CcTest::isolate(), 5000); |
| 7943 | // Verify that we created an array where the space was reserved up front. |
| 7944 | big_array_size = |
| 7945 | v8::internal::JSArray::cast(*v8::Utils::OpenHandle(*big_array)) |
| 7946 | ->elements() |
| 7947 | ->Size(); |
| 7948 | CHECK_LE(20000, big_array_size); |
| 7949 | a->Set(context, v8_str("y" ), big_array).FromJust(); |
| 7950 | big_heap_size = CcTest::heap()->SizeOfObjects(); |
| 7951 | } |
| 7952 | |
| 7953 | object_a.flag = false; |
| 7954 | object_b.flag = false; |
| 7955 | object_a.handle.SetWeak(&object_a, &SetFlag, |
| 7956 | v8::WeakCallbackType::kParameter); |
| 7957 | object_b.handle.SetWeak(&object_b, &SetFlag, |
| 7958 | v8::WeakCallbackType::kParameter); |
| 7959 | #if __clang__ |
| 7960 | #pragma clang diagnostic push |
| 7961 | #pragma clang diagnostic ignored "-Wdeprecated" |
| 7962 | #endif |
| 7963 | // MarkIndependent is marked deprecated but we still rely on it temporarily. |
| 7964 | CHECK(!object_b.handle.IsIndependent()); |
| 7965 | object_a.handle.MarkIndependent(); |
| 7966 | object_b.handle.MarkIndependent(); |
| 7967 | CHECK(object_b.handle.IsIndependent()); |
| 7968 | #if __clang__ |
| 7969 | #pragma clang diagnostic pop |
| 7970 | #endif |
| 7971 | if (global_gc) { |
| 7972 | CcTest::CollectAllGarbage(); |
| 7973 | } else { |
| 7974 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 7975 | } |
| 7976 | // A single GC should be enough to reclaim the memory, since we are using |
| 7977 | // phantom handles. |
| 7978 | CHECK_GT(big_heap_size - big_array_size, CcTest::heap()->SizeOfObjects()); |
| 7979 | CHECK(object_a.flag); |
| 7980 | CHECK(object_b.flag); |
| 7981 | } |
| 7982 | |
| 7983 | TEST(IndependentWeakHandle) { |
| 7984 | IndependentWeakHandle(false, false); |
| 7985 | IndependentWeakHandle(false, true); |
| 7986 | IndependentWeakHandle(true, false); |
| 7987 | IndependentWeakHandle(true, true); |
| 7988 | } |
| 7989 | |
| 7990 | class Trivial { |
| 7991 | public: |
| 7992 | explicit Trivial(int x) : x_(x) {} |
| 7993 | |
| 7994 | int x() { return x_; } |
| 7995 | void set_x(int x) { x_ = x; } |
| 7996 | |
| 7997 | private: |
| 7998 | int x_; |
| 7999 | }; |
| 8000 | |
| 8001 | |
| 8002 | class Trivial2 { |
| 8003 | public: |
| 8004 | Trivial2(int x, int y) : y_(y), x_(x) {} |
| 8005 | |
| 8006 | int x() { return x_; } |
| 8007 | void set_x(int x) { x_ = x; } |
| 8008 | |
| 8009 | int y() { return y_; } |
| 8010 | void set_y(int y) { y_ = y; } |
| 8011 | |
| 8012 | private: |
| 8013 | int y_; |
| 8014 | int x_; |
| 8015 | }; |
| 8016 | |
| 8017 | void CheckInternalFields( |
| 8018 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 8019 | v8::Persistent<v8::Object>* handle = data.GetParameter(); |
| 8020 | handle->Reset(); |
| 8021 | Trivial* t1 = reinterpret_cast<Trivial*>(data.GetInternalField(0)); |
| 8022 | Trivial2* t2 = reinterpret_cast<Trivial2*>(data.GetInternalField(1)); |
| 8023 | CHECK_EQ(42, t1->x()); |
| 8024 | CHECK_EQ(103, t2->x()); |
| 8025 | t1->set_x(1729); |
| 8026 | t2->set_x(33550336); |
| 8027 | } |
| 8028 | |
| 8029 | void InternalFieldCallback(bool global_gc) { |
| 8030 | LocalContext env; |
| 8031 | v8::Isolate* isolate = env->GetIsolate(); |
| 8032 | v8::HandleScope scope(isolate); |
| 8033 | |
| 8034 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 8035 | Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 8036 | Trivial* t1; |
| 8037 | Trivial2* t2; |
| 8038 | instance_templ->SetInternalFieldCount(2); |
| 8039 | v8::Persistent<v8::Object> handle; |
| 8040 | { |
| 8041 | v8::HandleScope scope(isolate); |
| 8042 | Local<v8::Object> obj = templ->GetFunction(env.local()) |
| 8043 | .ToLocalChecked() |
| 8044 | ->NewInstance(env.local()) |
| 8045 | .ToLocalChecked(); |
| 8046 | handle.Reset(isolate, obj); |
| 8047 | CHECK_EQ(2, obj->InternalFieldCount()); |
| 8048 | CHECK(obj->GetInternalField(0)->IsUndefined()); |
| 8049 | t1 = new Trivial(42); |
| 8050 | t2 = new Trivial2(103, 9); |
| 8051 | |
| 8052 | obj->SetAlignedPointerInInternalField(0, t1); |
| 8053 | t1 = reinterpret_cast<Trivial*>(obj->GetAlignedPointerFromInternalField(0)); |
| 8054 | CHECK_EQ(42, t1->x()); |
| 8055 | |
| 8056 | obj->SetAlignedPointerInInternalField(1, t2); |
| 8057 | t2 = |
| 8058 | reinterpret_cast<Trivial2*>(obj->GetAlignedPointerFromInternalField(1)); |
| 8059 | CHECK_EQ(103, t2->x()); |
| 8060 | |
| 8061 | handle.SetWeak<v8::Persistent<v8::Object>>( |
| 8062 | &handle, CheckInternalFields, v8::WeakCallbackType::kInternalFields); |
| 8063 | } |
| 8064 | if (global_gc) { |
| 8065 | CcTest::CollectAllGarbage(); |
| 8066 | } else { |
| 8067 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 8068 | } |
| 8069 | |
| 8070 | CHECK_EQ(1729, t1->x()); |
| 8071 | CHECK_EQ(33550336, t2->x()); |
| 8072 | |
| 8073 | delete t1; |
| 8074 | delete t2; |
| 8075 | } |
| 8076 | |
| 8077 | THREADED_TEST(InternalFieldCallback) { |
| 8078 | InternalFieldCallback(false); |
| 8079 | InternalFieldCallback(true); |
| 8080 | } |
| 8081 | |
| 8082 | static void ResetUseValueAndSetFlag( |
| 8083 | const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| 8084 | // Blink will reset the handle, and then use the other handle, so they |
| 8085 | // can't use the same backing slot. |
| 8086 | data.GetParameter()->handle.Reset(); |
| 8087 | data.GetParameter()->flag = true; |
| 8088 | } |
| 8089 | |
| 8090 | void v8::internal::heap::HeapTester::ResetWeakHandle(bool global_gc) { |
| 8091 | using v8::Context; |
| 8092 | using v8::Local; |
| 8093 | using v8::Object; |
| 8094 | |
| 8095 | v8::Isolate* iso = CcTest::isolate(); |
| 8096 | v8::HandleScope scope(iso); |
| 8097 | v8::Local<Context> context = Context::New(iso); |
| 8098 | Context::Scope context_scope(context); |
| 8099 | |
| 8100 | FlagAndPersistent object_a, object_b; |
| 8101 | |
| 8102 | { |
| 8103 | v8::HandleScope handle_scope(iso); |
| 8104 | Local<Object> a(v8::Object::New(iso)); |
| 8105 | Local<Object> b(v8::Object::New(iso)); |
| 8106 | object_a.handle.Reset(iso, a); |
| 8107 | object_b.handle.Reset(iso, b); |
| 8108 | if (global_gc) { |
| 8109 | CcTest::PreciseCollectAllGarbage(); |
| 8110 | } else { |
| 8111 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 8112 | } |
| 8113 | } |
| 8114 | |
| 8115 | object_a.flag = false; |
| 8116 | object_b.flag = false; |
| 8117 | object_a.handle.SetWeak(&object_a, &ResetUseValueAndSetFlag, |
| 8118 | v8::WeakCallbackType::kParameter); |
| 8119 | object_b.handle.SetWeak(&object_b, &ResetUseValueAndSetFlag, |
| 8120 | v8::WeakCallbackType::kParameter); |
| 8121 | if (!global_gc) { |
| 8122 | #if __clang__ |
| 8123 | #pragma clang diagnostic push |
| 8124 | #pragma clang diagnostic ignored "-Wdeprecated" |
| 8125 | #endif |
| 8126 | // MarkIndependent is marked deprecated but we still rely on it temporarily. |
| 8127 | object_a.handle.MarkIndependent(); |
| 8128 | object_b.handle.MarkIndependent(); |
| 8129 | CHECK(object_b.handle.IsIndependent()); |
| 8130 | #if __clang__ |
| 8131 | #pragma clang diagnostic pop |
| 8132 | #endif |
| 8133 | } |
| 8134 | if (global_gc) { |
| 8135 | CcTest::PreciseCollectAllGarbage(); |
| 8136 | } else { |
| 8137 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 8138 | } |
| 8139 | CHECK(object_a.flag); |
| 8140 | CHECK(object_b.flag); |
| 8141 | } |
| 8142 | |
| 8143 | THREADED_HEAP_TEST(ResetWeakHandle) { |
| 8144 | v8::internal::heap::HeapTester::ResetWeakHandle(false); |
| 8145 | v8::internal::heap::HeapTester::ResetWeakHandle(true); |
| 8146 | } |
| 8147 | |
| 8148 | static void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); } |
| 8149 | |
| 8150 | static void InvokeMarkSweep() { CcTest::CollectAllGarbage(); } |
| 8151 | |
| 8152 | static void ForceScavenge2( |
| 8153 | const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| 8154 | data.GetParameter()->flag = true; |
| 8155 | InvokeScavenge(); |
| 8156 | } |
| 8157 | |
| 8158 | static void ForceScavenge1( |
| 8159 | const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| 8160 | data.GetParameter()->handle.Reset(); |
| 8161 | data.SetSecondPassCallback(ForceScavenge2); |
| 8162 | } |
| 8163 | |
| 8164 | static void ForceMarkSweep2( |
| 8165 | const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| 8166 | data.GetParameter()->flag = true; |
| 8167 | InvokeMarkSweep(); |
| 8168 | } |
| 8169 | |
| 8170 | static void ForceMarkSweep1( |
| 8171 | const v8::WeakCallbackInfo<FlagAndPersistent>& data) { |
| 8172 | data.GetParameter()->handle.Reset(); |
| 8173 | data.SetSecondPassCallback(ForceMarkSweep2); |
| 8174 | } |
| 8175 | |
| 8176 | THREADED_TEST(GCFromWeakCallbacks) { |
| 8177 | v8::Isolate* isolate = CcTest::isolate(); |
| 8178 | v8::Locker locker(CcTest::isolate()); |
| 8179 | v8::HandleScope scope(isolate); |
| 8180 | v8::Local<Context> context = Context::New(isolate); |
| 8181 | Context::Scope context_scope(context); |
| 8182 | |
| 8183 | static const int kNumberOfGCTypes = 2; |
| 8184 | typedef v8::WeakCallbackInfo<FlagAndPersistent>::Callback Callback; |
| 8185 | Callback gc_forcing_callback[kNumberOfGCTypes] = {&ForceScavenge1, |
| 8186 | &ForceMarkSweep1}; |
| 8187 | |
| 8188 | typedef void (*GCInvoker)(); |
| 8189 | GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; |
| 8190 | |
| 8191 | for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { |
| 8192 | for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { |
| 8193 | FlagAndPersistent object; |
| 8194 | { |
| 8195 | v8::HandleScope handle_scope(isolate); |
| 8196 | object.handle.Reset(isolate, v8::Object::New(isolate)); |
| 8197 | } |
| 8198 | object.flag = false; |
| 8199 | object.handle.SetWeak(&object, gc_forcing_callback[inner_gc], |
| 8200 | v8::WeakCallbackType::kParameter); |
| 8201 | #if __clang__ |
| 8202 | #pragma clang diagnostic push |
| 8203 | #pragma clang diagnostic ignored "-Wdeprecated" |
| 8204 | #endif |
| 8205 | // MarkIndependent is marked deprecated but we still rely on it |
| 8206 | // temporarily. |
| 8207 | object.handle.MarkIndependent(); |
| 8208 | #if __clang__ |
| 8209 | #pragma clang diagnostic pop |
| 8210 | #endif |
| 8211 | invoke_gc[outer_gc](); |
| 8212 | EmptyMessageQueues(isolate); |
| 8213 | CHECK(object.flag); |
| 8214 | } |
| 8215 | } |
| 8216 | } |
| 8217 | |
| 8218 | v8::Local<Function> args_fun; |
| 8219 | |
| 8220 | |
| 8221 | static void ArgumentsTestCallback( |
| 8222 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 8223 | ApiTestFuzzer::Fuzz(); |
| 8224 | v8::Isolate* isolate = args.GetIsolate(); |
| 8225 | Local<Context> context = isolate->GetCurrentContext(); |
| 8226 | CHECK_EQ(3, args.Length()); |
| 8227 | CHECK(v8::Integer::New(isolate, 1)->Equals(context, args[0]).FromJust()); |
| 8228 | CHECK(v8::Integer::New(isolate, 2)->Equals(context, args[1]).FromJust()); |
| 8229 | CHECK(v8::Integer::New(isolate, 3)->Equals(context, args[2]).FromJust()); |
| 8230 | CHECK(v8::Undefined(isolate)->Equals(context, args[3]).FromJust()); |
| 8231 | v8::HandleScope scope(args.GetIsolate()); |
| 8232 | CcTest::CollectAllGarbage(); |
| 8233 | } |
| 8234 | |
| 8235 | |
| 8236 | THREADED_TEST(Arguments) { |
| 8237 | v8::Isolate* isolate = CcTest::isolate(); |
| 8238 | v8::HandleScope scope(isolate); |
| 8239 | v8::Local<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); |
| 8240 | global->Set(v8_str("f" ), |
| 8241 | v8::FunctionTemplate::New(isolate, ArgumentsTestCallback)); |
| 8242 | LocalContext context(nullptr, global); |
| 8243 | args_fun = context->Global() |
| 8244 | ->Get(context.local(), v8_str("f" )) |
| 8245 | .ToLocalChecked() |
| 8246 | .As<Function>(); |
| 8247 | v8_compile("f(1, 2, 3)" )->Run(context.local()).ToLocalChecked(); |
| 8248 | } |
| 8249 | |
| 8250 | |
| 8251 | static int p_getter_count; |
| 8252 | static int p_getter_count2; |
| 8253 | |
| 8254 | |
| 8255 | static void PGetter(Local<Name> name, |
| 8256 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 8257 | ApiTestFuzzer::Fuzz(); |
| 8258 | p_getter_count++; |
| 8259 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 8260 | v8::Local<v8::Object> global = context->Global(); |
| 8261 | CHECK( |
| 8262 | info.Holder() |
| 8263 | ->Equals(context, global->Get(context, v8_str("o1" )).ToLocalChecked()) |
| 8264 | .FromJust()); |
| 8265 | if (name->Equals(context, v8_str("p1" )).FromJust()) { |
| 8266 | CHECK(info.This() |
| 8267 | ->Equals(context, |
| 8268 | global->Get(context, v8_str("o1" )).ToLocalChecked()) |
| 8269 | .FromJust()); |
| 8270 | } else if (name->Equals(context, v8_str("p2" )).FromJust()) { |
| 8271 | CHECK(info.This() |
| 8272 | ->Equals(context, |
| 8273 | global->Get(context, v8_str("o2" )).ToLocalChecked()) |
| 8274 | .FromJust()); |
| 8275 | } else if (name->Equals(context, v8_str("p3" )).FromJust()) { |
| 8276 | CHECK(info.This() |
| 8277 | ->Equals(context, |
| 8278 | global->Get(context, v8_str("o3" )).ToLocalChecked()) |
| 8279 | .FromJust()); |
| 8280 | } else if (name->Equals(context, v8_str("p4" )).FromJust()) { |
| 8281 | CHECK(info.This() |
| 8282 | ->Equals(context, |
| 8283 | global->Get(context, v8_str("o4" )).ToLocalChecked()) |
| 8284 | .FromJust()); |
| 8285 | } |
| 8286 | } |
| 8287 | |
| 8288 | |
| 8289 | static void RunHolderTest(v8::Local<v8::ObjectTemplate> obj) { |
| 8290 | ApiTestFuzzer::Fuzz(); |
| 8291 | LocalContext context; |
| 8292 | CHECK(context->Global() |
| 8293 | ->Set(context.local(), v8_str("o1" ), |
| 8294 | obj->NewInstance(context.local()).ToLocalChecked()) |
| 8295 | .FromJust()); |
| 8296 | CompileRun( |
| 8297 | "o1.__proto__ = { };" |
| 8298 | "var o2 = { __proto__: o1 };" |
| 8299 | "var o3 = { __proto__: o2 };" |
| 8300 | "var o4 = { __proto__: o3 };" |
| 8301 | "for (var i = 0; i < 10; i++) o4.p4;" |
| 8302 | "for (var i = 0; i < 10; i++) o3.p3;" |
| 8303 | "for (var i = 0; i < 10; i++) o2.p2;" |
| 8304 | "for (var i = 0; i < 10; i++) o1.p1;" ); |
| 8305 | } |
| 8306 | |
| 8307 | |
| 8308 | static void PGetter2(Local<Name> name, |
| 8309 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 8310 | ApiTestFuzzer::Fuzz(); |
| 8311 | p_getter_count2++; |
| 8312 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 8313 | v8::Local<v8::Object> global = context->Global(); |
| 8314 | CHECK( |
| 8315 | info.Holder() |
| 8316 | ->Equals(context, global->Get(context, v8_str("o1" )).ToLocalChecked()) |
| 8317 | .FromJust()); |
| 8318 | if (name->Equals(context, v8_str("p1" )).FromJust()) { |
| 8319 | CHECK(info.This() |
| 8320 | ->Equals(context, |
| 8321 | global->Get(context, v8_str("o1" )).ToLocalChecked()) |
| 8322 | .FromJust()); |
| 8323 | } else if (name->Equals(context, v8_str("p2" )).FromJust()) { |
| 8324 | CHECK(info.This() |
| 8325 | ->Equals(context, |
| 8326 | global->Get(context, v8_str("o2" )).ToLocalChecked()) |
| 8327 | .FromJust()); |
| 8328 | } else if (name->Equals(context, v8_str("p3" )).FromJust()) { |
| 8329 | CHECK(info.This() |
| 8330 | ->Equals(context, |
| 8331 | global->Get(context, v8_str("o3" )).ToLocalChecked()) |
| 8332 | .FromJust()); |
| 8333 | } else if (name->Equals(context, v8_str("p4" )).FromJust()) { |
| 8334 | CHECK(info.This() |
| 8335 | ->Equals(context, |
| 8336 | global->Get(context, v8_str("o4" )).ToLocalChecked()) |
| 8337 | .FromJust()); |
| 8338 | } |
| 8339 | } |
| 8340 | |
| 8341 | |
| 8342 | THREADED_TEST(GetterHolders) { |
| 8343 | v8::Isolate* isolate = CcTest::isolate(); |
| 8344 | v8::HandleScope scope(isolate); |
| 8345 | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| 8346 | obj->SetAccessor(v8_str("p1" ), PGetter); |
| 8347 | obj->SetAccessor(v8_str("p2" ), PGetter); |
| 8348 | obj->SetAccessor(v8_str("p3" ), PGetter); |
| 8349 | obj->SetAccessor(v8_str("p4" ), PGetter); |
| 8350 | p_getter_count = 0; |
| 8351 | RunHolderTest(obj); |
| 8352 | CHECK_EQ(40, p_getter_count); |
| 8353 | } |
| 8354 | |
| 8355 | |
| 8356 | THREADED_TEST(PreInterceptorHolders) { |
| 8357 | v8::Isolate* isolate = CcTest::isolate(); |
| 8358 | v8::HandleScope scope(isolate); |
| 8359 | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| 8360 | obj->SetHandler(v8::NamedPropertyHandlerConfiguration(PGetter2)); |
| 8361 | p_getter_count2 = 0; |
| 8362 | RunHolderTest(obj); |
| 8363 | CHECK_EQ(40, p_getter_count2); |
| 8364 | } |
| 8365 | |
| 8366 | |
| 8367 | THREADED_TEST(ObjectInstantiation) { |
| 8368 | v8::Isolate* isolate = CcTest::isolate(); |
| 8369 | v8::HandleScope scope(isolate); |
| 8370 | v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 8371 | templ->SetAccessor(v8_str("t" ), PGetter2); |
| 8372 | LocalContext context; |
| 8373 | CHECK(context->Global() |
| 8374 | ->Set(context.local(), v8_str("o" ), |
| 8375 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 8376 | .FromJust()); |
| 8377 | for (int i = 0; i < 100; i++) { |
| 8378 | v8::HandleScope inner_scope(CcTest::isolate()); |
| 8379 | v8::Local<v8::Object> obj = |
| 8380 | templ->NewInstance(context.local()).ToLocalChecked(); |
| 8381 | CHECK(!obj->Equals(context.local(), context->Global() |
| 8382 | ->Get(context.local(), v8_str("o" )) |
| 8383 | .ToLocalChecked()) |
| 8384 | .FromJust()); |
| 8385 | CHECK( |
| 8386 | context->Global()->Set(context.local(), v8_str("o2" ), obj).FromJust()); |
| 8387 | v8::Local<Value> value = CompileRun("o.__proto__ === o2.__proto__" ); |
| 8388 | CHECK(v8::True(isolate)->Equals(context.local(), value).FromJust()); |
| 8389 | CHECK(context->Global()->Set(context.local(), v8_str("o" ), obj).FromJust()); |
| 8390 | } |
| 8391 | } |
| 8392 | |
| 8393 | |
| 8394 | static int StrCmp16(uint16_t* a, uint16_t* b) { |
| 8395 | while (true) { |
| 8396 | if (*a == 0 && *b == 0) return 0; |
| 8397 | if (*a != *b) return 0 + *a - *b; |
| 8398 | a++; |
| 8399 | b++; |
| 8400 | } |
| 8401 | } |
| 8402 | |
| 8403 | |
| 8404 | static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { |
| 8405 | while (true) { |
| 8406 | if (n-- == 0) return 0; |
| 8407 | if (*a == 0 && *b == 0) return 0; |
| 8408 | if (*a != *b) return 0 + *a - *b; |
| 8409 | a++; |
| 8410 | b++; |
| 8411 | } |
| 8412 | } |
| 8413 | |
| 8414 | int GetUtf8Length(v8::Isolate* isolate, Local<String> str) { |
| 8415 | int len = str->Utf8Length(isolate); |
| 8416 | if (len < 0) { |
| 8417 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 8418 | i::Handle<i::String> istr(v8::Utils::OpenHandle(*str)); |
| 8419 | i::String::Flatten(i_isolate, istr); |
| 8420 | len = str->Utf8Length(isolate); |
| 8421 | } |
| 8422 | return len; |
| 8423 | } |
| 8424 | |
| 8425 | |
| 8426 | THREADED_TEST(StringWrite) { |
| 8427 | LocalContext context; |
| 8428 | v8::Isolate* isolate = context->GetIsolate(); |
| 8429 | v8::HandleScope scope(isolate); |
| 8430 | v8::Local<String> str = v8_str("abcde" ); |
| 8431 | // abc<Icelandic eth><Unicode snowman>. |
| 8432 | v8::Local<String> str2 = v8_str("abc\xC3\xB0\xE2\x98\x83" ); |
| 8433 | v8::Local<String> str3 = |
| 8434 | v8::String::NewFromUtf8(context->GetIsolate(), "abc\0def" , |
| 8435 | v8::NewStringType::kNormal, 7) |
| 8436 | .ToLocalChecked(); |
| 8437 | // "ab" + lead surrogate + "wx" + trail surrogate + "yz" |
| 8438 | uint16_t orphans[8] = {0x61, 0x62, 0xD800, 0x77, 0x78, 0xDC00, 0x79, 0x7A}; |
| 8439 | v8::Local<String> orphans_str = |
| 8440 | v8::String::NewFromTwoByte(context->GetIsolate(), orphans, |
| 8441 | v8::NewStringType::kNormal, 8) |
| 8442 | .ToLocalChecked(); |
| 8443 | // single lead surrogate |
| 8444 | uint16_t lead[1] = {0xD800}; |
| 8445 | v8::Local<String> lead_str = |
| 8446 | v8::String::NewFromTwoByte(context->GetIsolate(), lead, |
| 8447 | v8::NewStringType::kNormal, 1) |
| 8448 | .ToLocalChecked(); |
| 8449 | // single trail surrogate |
| 8450 | uint16_t trail[1] = {0xDC00}; |
| 8451 | v8::Local<String> trail_str = |
| 8452 | v8::String::NewFromTwoByte(context->GetIsolate(), trail, |
| 8453 | v8::NewStringType::kNormal, 1) |
| 8454 | .ToLocalChecked(); |
| 8455 | // surrogate pair |
| 8456 | uint16_t pair[2] = {0xD800, 0xDC00}; |
| 8457 | v8::Local<String> pair_str = |
| 8458 | v8::String::NewFromTwoByte(context->GetIsolate(), pair, |
| 8459 | v8::NewStringType::kNormal, 2) |
| 8460 | .ToLocalChecked(); |
| 8461 | const int kStride = 4; // Must match stride in for loops in JS below. |
| 8462 | CompileRun( |
| 8463 | "var left = '';" |
| 8464 | "for (var i = 0; i < 0xD800; i += 4) {" |
| 8465 | " left = left + String.fromCharCode(i);" |
| 8466 | "}" ); |
| 8467 | CompileRun( |
| 8468 | "var right = '';" |
| 8469 | "for (var i = 0; i < 0xD800; i += 4) {" |
| 8470 | " right = String.fromCharCode(i) + right;" |
| 8471 | "}" ); |
| 8472 | v8::Local<v8::Object> global = context->Global(); |
| 8473 | Local<String> left_tree = global->Get(context.local(), v8_str("left" )) |
| 8474 | .ToLocalChecked() |
| 8475 | .As<String>(); |
| 8476 | Local<String> right_tree = global->Get(context.local(), v8_str("right" )) |
| 8477 | .ToLocalChecked() |
| 8478 | .As<String>(); |
| 8479 | |
| 8480 | CHECK_EQ(5, str2->Length()); |
| 8481 | CHECK_EQ(0xD800 / kStride, left_tree->Length()); |
| 8482 | CHECK_EQ(0xD800 / kStride, right_tree->Length()); |
| 8483 | |
| 8484 | char buf[100]; |
| 8485 | char utf8buf[0xD800 * 3]; |
| 8486 | uint16_t wbuf[100]; |
| 8487 | int len; |
| 8488 | int charlen; |
| 8489 | |
| 8490 | memset(utf8buf, 0x1, 1000); |
| 8491 | len = v8::String::Empty(isolate)->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), |
| 8492 | &charlen); |
| 8493 | CHECK_EQ(1, len); |
| 8494 | CHECK_EQ(0, charlen); |
| 8495 | CHECK_EQ(0, strcmp(utf8buf, "" )); |
| 8496 | |
| 8497 | memset(utf8buf, 0x1, 1000); |
| 8498 | len = str2->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen); |
| 8499 | CHECK_EQ(9, len); |
| 8500 | CHECK_EQ(5, charlen); |
| 8501 | CHECK_EQ(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83" )); |
| 8502 | |
| 8503 | memset(utf8buf, 0x1, 1000); |
| 8504 | len = str2->WriteUtf8(isolate, utf8buf, 8, &charlen); |
| 8505 | CHECK_EQ(8, len); |
| 8506 | CHECK_EQ(5, charlen); |
| 8507 | CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83\x01" , 9)); |
| 8508 | |
| 8509 | memset(utf8buf, 0x1, 1000); |
| 8510 | len = str2->WriteUtf8(isolate, utf8buf, 7, &charlen); |
| 8511 | CHECK_EQ(5, len); |
| 8512 | CHECK_EQ(4, charlen); |
| 8513 | CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\x01" , 5)); |
| 8514 | |
| 8515 | memset(utf8buf, 0x1, 1000); |
| 8516 | len = str2->WriteUtf8(isolate, utf8buf, 6, &charlen); |
| 8517 | CHECK_EQ(5, len); |
| 8518 | CHECK_EQ(4, charlen); |
| 8519 | CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\x01" , 5)); |
| 8520 | |
| 8521 | memset(utf8buf, 0x1, 1000); |
| 8522 | len = str2->WriteUtf8(isolate, utf8buf, 5, &charlen); |
| 8523 | CHECK_EQ(5, len); |
| 8524 | CHECK_EQ(4, charlen); |
| 8525 | CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\x01" , 5)); |
| 8526 | |
| 8527 | memset(utf8buf, 0x1, 1000); |
| 8528 | len = str2->WriteUtf8(isolate, utf8buf, 4, &charlen); |
| 8529 | CHECK_EQ(3, len); |
| 8530 | CHECK_EQ(3, charlen); |
| 8531 | CHECK_EQ(0, strncmp(utf8buf, "abc\x01" , 4)); |
| 8532 | |
| 8533 | memset(utf8buf, 0x1, 1000); |
| 8534 | len = str2->WriteUtf8(isolate, utf8buf, 3, &charlen); |
| 8535 | CHECK_EQ(3, len); |
| 8536 | CHECK_EQ(3, charlen); |
| 8537 | CHECK_EQ(0, strncmp(utf8buf, "abc\x01" , 4)); |
| 8538 | |
| 8539 | memset(utf8buf, 0x1, 1000); |
| 8540 | len = str2->WriteUtf8(isolate, utf8buf, 2, &charlen); |
| 8541 | CHECK_EQ(2, len); |
| 8542 | CHECK_EQ(2, charlen); |
| 8543 | CHECK_EQ(0, strncmp(utf8buf, "ab\x01" , 3)); |
| 8544 | |
| 8545 | // allow orphan surrogates by default |
| 8546 | memset(utf8buf, 0x1, 1000); |
| 8547 | len = orphans_str->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen); |
| 8548 | CHECK_EQ(13, len); |
| 8549 | CHECK_EQ(8, charlen); |
| 8550 | CHECK_EQ(0, strcmp(utf8buf, "ab\xED\xA0\x80wx\xED\xB0\x80yz" )); |
| 8551 | |
| 8552 | // replace orphan surrogates with Unicode replacement character |
| 8553 | memset(utf8buf, 0x1, 1000); |
| 8554 | len = orphans_str->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen, |
| 8555 | String::REPLACE_INVALID_UTF8); |
| 8556 | CHECK_EQ(13, len); |
| 8557 | CHECK_EQ(8, charlen); |
| 8558 | CHECK_EQ(0, strcmp(utf8buf, "ab\xEF\xBF\xBDwx\xEF\xBF\xBDyz" )); |
| 8559 | |
| 8560 | // replace single lead surrogate with Unicode replacement character |
| 8561 | memset(utf8buf, 0x1, 1000); |
| 8562 | len = lead_str->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen, |
| 8563 | String::REPLACE_INVALID_UTF8); |
| 8564 | CHECK_EQ(4, len); |
| 8565 | CHECK_EQ(1, charlen); |
| 8566 | CHECK_EQ(0, strcmp(utf8buf, "\xEF\xBF\xBD" )); |
| 8567 | |
| 8568 | // replace single trail surrogate with Unicode replacement character |
| 8569 | memset(utf8buf, 0x1, 1000); |
| 8570 | len = trail_str->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen, |
| 8571 | String::REPLACE_INVALID_UTF8); |
| 8572 | CHECK_EQ(4, len); |
| 8573 | CHECK_EQ(1, charlen); |
| 8574 | CHECK_EQ(0, strcmp(utf8buf, "\xEF\xBF\xBD" )); |
| 8575 | |
| 8576 | // do not replace / write anything if surrogate pair does not fit the buffer |
| 8577 | // space |
| 8578 | memset(utf8buf, 0x1, 1000); |
| 8579 | len = pair_str->WriteUtf8(isolate, utf8buf, 3, &charlen, |
| 8580 | String::REPLACE_INVALID_UTF8); |
| 8581 | CHECK_EQ(0, len); |
| 8582 | CHECK_EQ(0, charlen); |
| 8583 | |
| 8584 | memset(utf8buf, 0x1, sizeof(utf8buf)); |
| 8585 | len = GetUtf8Length(isolate, left_tree); |
| 8586 | int utf8_expected = |
| 8587 | (0x80 + (0x800 - 0x80) * 2 + (0xD800 - 0x800) * 3) / kStride; |
| 8588 | CHECK_EQ(utf8_expected, len); |
| 8589 | len = left_tree->WriteUtf8(isolate, utf8buf, utf8_expected, &charlen); |
| 8590 | CHECK_EQ(utf8_expected, len); |
| 8591 | CHECK_EQ(0xD800 / kStride, charlen); |
| 8592 | CHECK_EQ(0xED, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); |
| 8593 | CHECK_EQ(0x9F, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); |
| 8594 | CHECK_EQ(0xC0 - kStride, |
| 8595 | static_cast<unsigned char>(utf8buf[utf8_expected - 1])); |
| 8596 | CHECK_EQ(1, utf8buf[utf8_expected]); |
| 8597 | |
| 8598 | memset(utf8buf, 0x1, sizeof(utf8buf)); |
| 8599 | len = GetUtf8Length(isolate, right_tree); |
| 8600 | CHECK_EQ(utf8_expected, len); |
| 8601 | len = right_tree->WriteUtf8(isolate, utf8buf, utf8_expected, &charlen); |
| 8602 | CHECK_EQ(utf8_expected, len); |
| 8603 | CHECK_EQ(0xD800 / kStride, charlen); |
| 8604 | CHECK_EQ(0xED, static_cast<unsigned char>(utf8buf[0])); |
| 8605 | CHECK_EQ(0x9F, static_cast<unsigned char>(utf8buf[1])); |
| 8606 | CHECK_EQ(0xC0 - kStride, static_cast<unsigned char>(utf8buf[2])); |
| 8607 | CHECK_EQ(1, utf8buf[utf8_expected]); |
| 8608 | |
| 8609 | memset(buf, 0x1, sizeof(buf)); |
| 8610 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8611 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf)); |
| 8612 | CHECK_EQ(5, len); |
| 8613 | len = str->Write(isolate, wbuf); |
| 8614 | CHECK_EQ(5, len); |
| 8615 | CHECK_EQ(0, strcmp("abcde" , buf)); |
| 8616 | uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| 8617 | CHECK_EQ(0, StrCmp16(answer1, wbuf)); |
| 8618 | |
| 8619 | memset(buf, 0x1, sizeof(buf)); |
| 8620 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8621 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 0, 4); |
| 8622 | CHECK_EQ(4, len); |
| 8623 | len = str->Write(isolate, wbuf, 0, 4); |
| 8624 | CHECK_EQ(4, len); |
| 8625 | CHECK_EQ(0, strncmp("abcd\x01" , buf, 5)); |
| 8626 | uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; |
| 8627 | CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); |
| 8628 | |
| 8629 | memset(buf, 0x1, sizeof(buf)); |
| 8630 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8631 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 0, 5); |
| 8632 | CHECK_EQ(5, len); |
| 8633 | len = str->Write(isolate, wbuf, 0, 5); |
| 8634 | CHECK_EQ(5, len); |
| 8635 | CHECK_EQ(0, strncmp("abcde\x01" , buf, 6)); |
| 8636 | uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; |
| 8637 | CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); |
| 8638 | |
| 8639 | memset(buf, 0x1, sizeof(buf)); |
| 8640 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8641 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 0, 6); |
| 8642 | CHECK_EQ(5, len); |
| 8643 | len = str->Write(isolate, wbuf, 0, 6); |
| 8644 | CHECK_EQ(5, len); |
| 8645 | CHECK_EQ(0, strcmp("abcde" , buf)); |
| 8646 | uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| 8647 | CHECK_EQ(0, StrCmp16(answer4, wbuf)); |
| 8648 | |
| 8649 | memset(buf, 0x1, sizeof(buf)); |
| 8650 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8651 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 4, -1); |
| 8652 | CHECK_EQ(1, len); |
| 8653 | len = str->Write(isolate, wbuf, 4, -1); |
| 8654 | CHECK_EQ(1, len); |
| 8655 | CHECK_EQ(0, strcmp("e" , buf)); |
| 8656 | uint16_t answer5[] = {'e', '\0'}; |
| 8657 | CHECK_EQ(0, StrCmp16(answer5, wbuf)); |
| 8658 | |
| 8659 | memset(buf, 0x1, sizeof(buf)); |
| 8660 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8661 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 4, 6); |
| 8662 | CHECK_EQ(1, len); |
| 8663 | len = str->Write(isolate, wbuf, 4, 6); |
| 8664 | CHECK_EQ(1, len); |
| 8665 | CHECK_EQ(0, strcmp("e" , buf)); |
| 8666 | CHECK_EQ(0, StrCmp16(answer5, wbuf)); |
| 8667 | |
| 8668 | memset(buf, 0x1, sizeof(buf)); |
| 8669 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8670 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 4, 1); |
| 8671 | CHECK_EQ(1, len); |
| 8672 | len = str->Write(isolate, wbuf, 4, 1); |
| 8673 | CHECK_EQ(1, len); |
| 8674 | CHECK_EQ(0, strncmp("e\x01" , buf, 2)); |
| 8675 | uint16_t answer6[] = {'e', 0x101}; |
| 8676 | CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); |
| 8677 | |
| 8678 | memset(buf, 0x1, sizeof(buf)); |
| 8679 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8680 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 3, 1); |
| 8681 | CHECK_EQ(1, len); |
| 8682 | len = str->Write(isolate, wbuf, 3, 1); |
| 8683 | CHECK_EQ(1, len); |
| 8684 | CHECK_EQ(0, strncmp("d\x01" , buf, 2)); |
| 8685 | uint16_t answer7[] = {'d', 0x101}; |
| 8686 | CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); |
| 8687 | |
| 8688 | memset(wbuf, 0x1, sizeof(wbuf)); |
| 8689 | wbuf[5] = 'X'; |
| 8690 | len = str->Write(isolate, wbuf, 0, 6, String::NO_NULL_TERMINATION); |
| 8691 | CHECK_EQ(5, len); |
| 8692 | CHECK_EQ('X', wbuf[5]); |
| 8693 | uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'}; |
| 8694 | uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'}; |
| 8695 | CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5)); |
| 8696 | CHECK_NE(0, StrCmp16(answer8b, wbuf)); |
| 8697 | wbuf[5] = '\0'; |
| 8698 | CHECK_EQ(0, StrCmp16(answer8b, wbuf)); |
| 8699 | |
| 8700 | memset(buf, 0x1, sizeof(buf)); |
| 8701 | buf[5] = 'X'; |
| 8702 | len = str->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf), 0, 6, |
| 8703 | String::NO_NULL_TERMINATION); |
| 8704 | CHECK_EQ(5, len); |
| 8705 | CHECK_EQ('X', buf[5]); |
| 8706 | CHECK_EQ(0, strncmp("abcde" , buf, 5)); |
| 8707 | CHECK_NE(0, strcmp("abcde" , buf)); |
| 8708 | buf[5] = '\0'; |
| 8709 | CHECK_EQ(0, strcmp("abcde" , buf)); |
| 8710 | |
| 8711 | memset(utf8buf, 0x1, sizeof(utf8buf)); |
| 8712 | utf8buf[8] = 'X'; |
| 8713 | len = str2->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen, |
| 8714 | String::NO_NULL_TERMINATION); |
| 8715 | CHECK_EQ(8, len); |
| 8716 | CHECK_EQ('X', utf8buf[8]); |
| 8717 | CHECK_EQ(5, charlen); |
| 8718 | CHECK_EQ(0, strncmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83" , 8)); |
| 8719 | CHECK_NE(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83" )); |
| 8720 | utf8buf[8] = '\0'; |
| 8721 | CHECK_EQ(0, strcmp(utf8buf, "abc\xC3\xB0\xE2\x98\x83" )); |
| 8722 | |
| 8723 | memset(utf8buf, 0x1, sizeof(utf8buf)); |
| 8724 | utf8buf[5] = 'X'; |
| 8725 | len = str->WriteUtf8(isolate, utf8buf, sizeof(utf8buf), &charlen, |
| 8726 | String::NO_NULL_TERMINATION); |
| 8727 | CHECK_EQ(5, len); |
| 8728 | CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched. |
| 8729 | CHECK_EQ(5, charlen); |
| 8730 | utf8buf[5] = '\0'; |
| 8731 | CHECK_EQ(0, strcmp(utf8buf, "abcde" )); |
| 8732 | |
| 8733 | memset(buf, 0x1, sizeof(buf)); |
| 8734 | len = str3->WriteOneByte(isolate, reinterpret_cast<uint8_t*>(buf)); |
| 8735 | CHECK_EQ(7, len); |
| 8736 | CHECK_EQ(0, strcmp("abc" , buf)); |
| 8737 | CHECK_EQ(0, buf[3]); |
| 8738 | CHECK_EQ(0, strcmp("def" , buf + 4)); |
| 8739 | |
| 8740 | CHECK_EQ(0, str->WriteOneByte(isolate, nullptr, 0, 0, |
| 8741 | String::NO_NULL_TERMINATION)); |
| 8742 | CHECK_EQ(0, str->WriteUtf8(isolate, nullptr, 0, nullptr, |
| 8743 | String::NO_NULL_TERMINATION)); |
| 8744 | CHECK_EQ(0, str->Write(isolate, nullptr, 0, 0, String::NO_NULL_TERMINATION)); |
| 8745 | } |
| 8746 | |
| 8747 | |
| 8748 | static void Utf16Helper( |
| 8749 | LocalContext& context, // NOLINT |
| 8750 | const char* name, |
| 8751 | const char* lengths_name, |
| 8752 | int len) { |
| 8753 | Local<v8::Array> a = Local<v8::Array>::Cast( |
| 8754 | context->Global()->Get(context.local(), v8_str(name)).ToLocalChecked()); |
| 8755 | Local<v8::Array> alens = |
| 8756 | Local<v8::Array>::Cast(context->Global() |
| 8757 | ->Get(context.local(), v8_str(lengths_name)) |
| 8758 | .ToLocalChecked()); |
| 8759 | for (int i = 0; i < len; i++) { |
| 8760 | Local<v8::String> string = |
| 8761 | Local<v8::String>::Cast(a->Get(context.local(), i).ToLocalChecked()); |
| 8762 | Local<v8::Number> expected_len = Local<v8::Number>::Cast( |
| 8763 | alens->Get(context.local(), i).ToLocalChecked()); |
| 8764 | int length = GetUtf8Length(context->GetIsolate(), string); |
| 8765 | CHECK_EQ(static_cast<int>(expected_len->Value()), length); |
| 8766 | } |
| 8767 | } |
| 8768 | |
| 8769 | void TestUtf8DecodingAgainstReference( |
| 8770 | v8::Isolate* isolate, const char* cases[], |
| 8771 | const std::vector<std::vector<uint16_t>>& unicode_expected) { |
| 8772 | for (size_t test_ix = 0; test_ix < unicode_expected.size(); ++test_ix) { |
| 8773 | v8::Local<String> str = v8_str(cases[test_ix]); |
| 8774 | CHECK_EQ(unicode_expected[test_ix].size(), str->Length()); |
| 8775 | |
| 8776 | std::unique_ptr<uint16_t[]> buffer(new uint16_t[str->Length()]); |
| 8777 | str->Write(isolate, buffer.get(), 0, -1, String::NO_NULL_TERMINATION); |
| 8778 | |
| 8779 | for (size_t i = 0; i < unicode_expected[test_ix].size(); ++i) { |
| 8780 | CHECK_EQ(unicode_expected[test_ix][i], buffer[i]); |
| 8781 | } |
| 8782 | } |
| 8783 | } |
| 8784 | |
| 8785 | THREADED_TEST(OverlongSequencesAndSurrogates) { |
| 8786 | LocalContext context; |
| 8787 | v8::HandleScope scope(context->GetIsolate()); |
| 8788 | |
| 8789 | const char* cases[] = { |
| 8790 | // Overlong 2-byte sequence. |
| 8791 | "X\xc0\xbfY\0" , |
| 8792 | // Another overlong 2-byte sequence. |
| 8793 | "X\xc1\xbfY\0" , |
| 8794 | // Overlong 3-byte sequence. |
| 8795 | "X\xe0\x9f\xbfY\0" , |
| 8796 | // Overlong 4-byte sequence. |
| 8797 | "X\xf0\x89\xbf\xbfY\0" , |
| 8798 | // Invalid 3-byte sequence (reserved for surrogates). |
| 8799 | "X\xed\xa0\x80Y\0" , |
| 8800 | // Invalid 4-bytes sequence (value out of range). |
| 8801 | "X\xf4\x90\x80\x80Y\0" , |
| 8802 | |
| 8803 | // Start of an overlong 3-byte sequence but not enough continuation bytes. |
| 8804 | "X\xe0\x9fY\0" , |
| 8805 | // Start of an overlong 4-byte sequence but not enough continuation bytes. |
| 8806 | "X\xf0\x89\xbfY\0" , |
| 8807 | // Start of an invalid 3-byte sequence (reserved for surrogates) but not |
| 8808 | // enough continuation bytes. |
| 8809 | "X\xed\xa0Y\0" , |
| 8810 | // Start of an invalid 4-bytes sequence (value out of range) but not |
| 8811 | // enough continuation bytes. |
| 8812 | "X\xf4\x90\x80Y\0" , |
| 8813 | }; |
| 8814 | const std::vector<std::vector<uint16_t>> unicode_expected = { |
| 8815 | {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| 8816 | {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| 8817 | {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| 8818 | {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| 8819 | {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| 8820 | {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| 8821 | {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| 8822 | {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| 8823 | {0x58, 0xFFFD, 0xFFFD, 0x59}, |
| 8824 | {0x58, 0xFFFD, 0xFFFD, 0xFFFD, 0x59}, |
| 8825 | }; |
| 8826 | CHECK_EQ(unicode_expected.size(), arraysize(cases)); |
| 8827 | TestUtf8DecodingAgainstReference(context->GetIsolate(), cases, |
| 8828 | unicode_expected); |
| 8829 | } |
| 8830 | |
| 8831 | THREADED_TEST(Utf16) { |
| 8832 | LocalContext context; |
| 8833 | v8::HandleScope scope(context->GetIsolate()); |
| 8834 | CompileRun( |
| 8835 | "var pad = '01234567890123456789';" |
| 8836 | "var p = [];" |
| 8837 | "var plens = [20, 3, 3];" |
| 8838 | "p.push('01234567890123456789');" |
| 8839 | "var lead = 0xD800;" |
| 8840 | "var trail = 0xDC00;" |
| 8841 | "p.push(String.fromCharCode(0xD800));" |
| 8842 | "p.push(String.fromCharCode(0xDC00));" |
| 8843 | "var a = [];" |
| 8844 | "var b = [];" |
| 8845 | "var c = [];" |
| 8846 | "var alens = [];" |
| 8847 | "for (var i = 0; i < 3; i++) {" |
| 8848 | " p[1] = String.fromCharCode(lead++);" |
| 8849 | " for (var j = 0; j < 3; j++) {" |
| 8850 | " p[2] = String.fromCharCode(trail++);" |
| 8851 | " a.push(p[i] + p[j]);" |
| 8852 | " b.push(p[i] + p[j]);" |
| 8853 | " c.push(p[i] + p[j]);" |
| 8854 | " alens.push(plens[i] + plens[j]);" |
| 8855 | " }" |
| 8856 | "}" |
| 8857 | "alens[5] -= 2;" // Here the surrogate pairs match up. |
| 8858 | "var a2 = [];" |
| 8859 | "var b2 = [];" |
| 8860 | "var c2 = [];" |
| 8861 | "var a2lens = [];" |
| 8862 | "for (var m = 0; m < 9; m++) {" |
| 8863 | " for (var n = 0; n < 9; n++) {" |
| 8864 | " a2.push(a[m] + a[n]);" |
| 8865 | " b2.push(b[m] + b[n]);" |
| 8866 | " var newc = 'x' + c[m] + c[n] + 'y';" |
| 8867 | " c2.push(newc.substring(1, newc.length - 1));" |
| 8868 | " var utf = alens[m] + alens[n];" // And here. |
| 8869 | // The 'n's that start with 0xDC.. |
| 8870 | // are 6-8 The 'm's that end with |
| 8871 | // 0xD8.. are 1, 4 and 7 |
| 8872 | " if ((m % 3) == 1 && n >= 6) utf -= 2;" |
| 8873 | " a2lens.push(utf);" |
| 8874 | " }" |
| 8875 | "}" ); |
| 8876 | Utf16Helper(context, "a" , "alens" , 9); |
| 8877 | Utf16Helper(context, "a2" , "a2lens" , 81); |
| 8878 | } |
| 8879 | |
| 8880 | |
| 8881 | static bool SameSymbol(Local<String> s1, Local<String> s2) { |
| 8882 | i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1)); |
| 8883 | i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2)); |
| 8884 | return *is1 == *is2; |
| 8885 | } |
| 8886 | |
| 8887 | |
| 8888 | THREADED_TEST(Utf16Symbol) { |
| 8889 | LocalContext context; |
| 8890 | v8::HandleScope scope(context->GetIsolate()); |
| 8891 | |
| 8892 | Local<String> symbol1 = |
| 8893 | v8::String::NewFromUtf8(context->GetIsolate(), "abc" , |
| 8894 | v8::NewStringType::kInternalized) |
| 8895 | .ToLocalChecked(); |
| 8896 | Local<String> symbol2 = |
| 8897 | v8::String::NewFromUtf8(context->GetIsolate(), "abc" , |
| 8898 | v8::NewStringType::kInternalized) |
| 8899 | .ToLocalChecked(); |
| 8900 | CHECK(SameSymbol(symbol1, symbol2)); |
| 8901 | |
| 8902 | CompileRun( |
| 8903 | "var sym0 = 'benedictus';" |
| 8904 | "var sym0b = 'S\xC3\xB8ren';" |
| 8905 | "var sym1 = '\xED\xA0\x81\xED\xB0\x87';" |
| 8906 | "var sym2 = '\xF0\x90\x90\x88';" |
| 8907 | "var sym3 = 'x\xED\xA0\x81\xED\xB0\x87';" |
| 8908 | "var sym4 = 'x\xF0\x90\x90\x88';" |
| 8909 | "if (sym1.length != 2) throw sym1;" |
| 8910 | "if (sym1.charCodeAt(1) != 0xDC07) throw sym1.charCodeAt(1);" |
| 8911 | "if (sym2.length != 2) throw sym2;" |
| 8912 | "if (sym2.charCodeAt(1) != 0xDC08) throw sym2.charCodeAt(2);" |
| 8913 | "if (sym3.length != 3) throw sym3;" |
| 8914 | "if (sym3.charCodeAt(2) != 0xDC07) throw sym1.charCodeAt(2);" |
| 8915 | "if (sym4.length != 3) throw sym4;" |
| 8916 | "if (sym4.charCodeAt(2) != 0xDC08) throw sym2.charCodeAt(2);" ); |
| 8917 | Local<String> sym0 = |
| 8918 | v8::String::NewFromUtf8(context->GetIsolate(), "benedictus" , |
| 8919 | v8::NewStringType::kInternalized) |
| 8920 | .ToLocalChecked(); |
| 8921 | Local<String> sym0b = |
| 8922 | v8::String::NewFromUtf8(context->GetIsolate(), "S\xC3\xB8ren" , |
| 8923 | v8::NewStringType::kInternalized) |
| 8924 | .ToLocalChecked(); |
| 8925 | Local<String> sym1 = |
| 8926 | v8::String::NewFromUtf8(context->GetIsolate(), "\xED\xA0\x81\xED\xB0\x87" , |
| 8927 | v8::NewStringType::kInternalized) |
| 8928 | .ToLocalChecked(); |
| 8929 | Local<String> sym2 = |
| 8930 | v8::String::NewFromUtf8(context->GetIsolate(), "\xF0\x90\x90\x88" , |
| 8931 | v8::NewStringType::kInternalized) |
| 8932 | .ToLocalChecked(); |
| 8933 | Local<String> sym3 = v8::String::NewFromUtf8(context->GetIsolate(), |
| 8934 | "x\xED\xA0\x81\xED\xB0\x87" , |
| 8935 | v8::NewStringType::kInternalized) |
| 8936 | .ToLocalChecked(); |
| 8937 | Local<String> sym4 = |
| 8938 | v8::String::NewFromUtf8(context->GetIsolate(), "x\xF0\x90\x90\x88" , |
| 8939 | v8::NewStringType::kInternalized) |
| 8940 | .ToLocalChecked(); |
| 8941 | v8::Local<v8::Object> global = context->Global(); |
| 8942 | Local<Value> s0 = |
| 8943 | global->Get(context.local(), v8_str("sym0" )).ToLocalChecked(); |
| 8944 | Local<Value> s0b = |
| 8945 | global->Get(context.local(), v8_str("sym0b" )).ToLocalChecked(); |
| 8946 | Local<Value> s1 = |
| 8947 | global->Get(context.local(), v8_str("sym1" )).ToLocalChecked(); |
| 8948 | Local<Value> s2 = |
| 8949 | global->Get(context.local(), v8_str("sym2" )).ToLocalChecked(); |
| 8950 | Local<Value> s3 = |
| 8951 | global->Get(context.local(), v8_str("sym3" )).ToLocalChecked(); |
| 8952 | Local<Value> s4 = |
| 8953 | global->Get(context.local(), v8_str("sym4" )).ToLocalChecked(); |
| 8954 | CHECK(SameSymbol(sym0, Local<String>::Cast(s0))); |
| 8955 | CHECK(SameSymbol(sym0b, Local<String>::Cast(s0b))); |
| 8956 | CHECK(SameSymbol(sym1, Local<String>::Cast(s1))); |
| 8957 | CHECK(SameSymbol(sym2, Local<String>::Cast(s2))); |
| 8958 | CHECK(SameSymbol(sym3, Local<String>::Cast(s3))); |
| 8959 | CHECK(SameSymbol(sym4, Local<String>::Cast(s4))); |
| 8960 | } |
| 8961 | |
| 8962 | |
| 8963 | THREADED_TEST(Utf16MissingTrailing) { |
| 8964 | LocalContext context; |
| 8965 | v8::HandleScope scope(context->GetIsolate()); |
| 8966 | |
| 8967 | // Make sure it will go past the buffer, so it will call `WriteUtf16Slow` |
| 8968 | int size = 1024 * 64; |
| 8969 | uint8_t* buffer = new uint8_t[size]; |
| 8970 | for (int i = 0; i < size; i += 4) { |
| 8971 | buffer[i] = 0xF0; |
| 8972 | buffer[i + 1] = 0x9D; |
| 8973 | buffer[i + 2] = 0x80; |
| 8974 | buffer[i + 3] = 0x9E; |
| 8975 | } |
| 8976 | |
| 8977 | // Now invoke the decoder without last 3 bytes |
| 8978 | v8::Local<v8::String> str = |
| 8979 | v8::String::NewFromUtf8( |
| 8980 | context->GetIsolate(), reinterpret_cast<char*>(buffer), |
| 8981 | v8::NewStringType::kNormal, size - 3).ToLocalChecked(); |
| 8982 | USE(str); |
| 8983 | delete[] buffer; |
| 8984 | } |
| 8985 | |
| 8986 | |
| 8987 | THREADED_TEST(Utf16Trailing3Byte) { |
| 8988 | LocalContext context; |
| 8989 | v8::Isolate* isolate = context->GetIsolate(); |
| 8990 | v8::HandleScope scope(isolate); |
| 8991 | |
| 8992 | // Make sure it will go past the buffer, so it will call `WriteUtf16Slow` |
| 8993 | int size = 1024 * 63; |
| 8994 | uint8_t* buffer = new uint8_t[size]; |
| 8995 | for (int i = 0; i < size; i += 3) { |
| 8996 | buffer[i] = 0xE2; |
| 8997 | buffer[i + 1] = 0x80; |
| 8998 | buffer[i + 2] = 0xA6; |
| 8999 | } |
| 9000 | |
| 9001 | // Now invoke the decoder without last 3 bytes |
| 9002 | v8::Local<v8::String> str = |
| 9003 | v8::String::NewFromUtf8(isolate, reinterpret_cast<char*>(buffer), |
| 9004 | v8::NewStringType::kNormal, size) |
| 9005 | .ToLocalChecked(); |
| 9006 | |
| 9007 | v8::String::Value value(isolate, str); |
| 9008 | CHECK_EQ(value.length(), size / 3); |
| 9009 | CHECK_EQ((*value)[value.length() - 1], 0x2026); |
| 9010 | |
| 9011 | delete[] buffer; |
| 9012 | } |
| 9013 | |
| 9014 | |
| 9015 | THREADED_TEST(ToArrayIndex) { |
| 9016 | LocalContext context; |
| 9017 | v8::Isolate* isolate = context->GetIsolate(); |
| 9018 | v8::HandleScope scope(isolate); |
| 9019 | |
| 9020 | v8::Local<String> str = v8_str("42" ); |
| 9021 | v8::MaybeLocal<v8::Uint32> index = str->ToArrayIndex(context.local()); |
| 9022 | CHECK(!index.IsEmpty()); |
| 9023 | CHECK_EQ(42.0, |
| 9024 | index.ToLocalChecked()->Uint32Value(context.local()).FromJust()); |
| 9025 | str = v8_str("42asdf" ); |
| 9026 | index = str->ToArrayIndex(context.local()); |
| 9027 | CHECK(index.IsEmpty()); |
| 9028 | str = v8_str("-42" ); |
| 9029 | index = str->ToArrayIndex(context.local()); |
| 9030 | CHECK(index.IsEmpty()); |
| 9031 | str = v8_str("4294967294" ); |
| 9032 | index = str->ToArrayIndex(context.local()); |
| 9033 | CHECK(!index.IsEmpty()); |
| 9034 | CHECK_EQ(4294967294.0, |
| 9035 | index.ToLocalChecked()->Uint32Value(context.local()).FromJust()); |
| 9036 | v8::Local<v8::Number> num = v8::Number::New(isolate, 1); |
| 9037 | index = num->ToArrayIndex(context.local()); |
| 9038 | CHECK(!index.IsEmpty()); |
| 9039 | CHECK_EQ(1.0, |
| 9040 | index.ToLocalChecked()->Uint32Value(context.local()).FromJust()); |
| 9041 | num = v8::Number::New(isolate, -1); |
| 9042 | index = num->ToArrayIndex(context.local()); |
| 9043 | CHECK(index.IsEmpty()); |
| 9044 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 9045 | index = obj->ToArrayIndex(context.local()); |
| 9046 | CHECK(index.IsEmpty()); |
| 9047 | } |
| 9048 | |
| 9049 | static v8::MaybeLocal<Value> PrepareStackTrace42(v8::Local<Context> context, |
| 9050 | v8::Local<Value> error, |
| 9051 | v8::Local<Array> trace) { |
| 9052 | return v8::Number::New(context->GetIsolate(), 42); |
| 9053 | } |
| 9054 | |
| 9055 | static v8::MaybeLocal<Value> PrepareStackTraceThrow(v8::Local<Context> context, |
| 9056 | v8::Local<Value> error, |
| 9057 | v8::Local<Array> trace) { |
| 9058 | v8::Isolate* isolate = context->GetIsolate(); |
| 9059 | v8::Local<String> message = v8_str("42" ); |
| 9060 | isolate->ThrowException(v8::Exception::Error(message)); |
| 9061 | return v8::MaybeLocal<Value>(); |
| 9062 | } |
| 9063 | |
| 9064 | THREADED_TEST(IsolatePrepareStackTrace) { |
| 9065 | LocalContext context; |
| 9066 | v8::Isolate* isolate = context->GetIsolate(); |
| 9067 | v8::HandleScope scope(isolate); |
| 9068 | |
| 9069 | isolate->SetPrepareStackTraceCallback(PrepareStackTrace42); |
| 9070 | |
| 9071 | v8::Local<Value> v = CompileRun("new Error().stack" ); |
| 9072 | |
| 9073 | CHECK(v->IsNumber()); |
| 9074 | CHECK_EQ(v.As<v8::Number>()->Int32Value(context.local()).FromJust(), 42); |
| 9075 | } |
| 9076 | |
| 9077 | THREADED_TEST(IsolatePrepareStackTraceThrow) { |
| 9078 | LocalContext context; |
| 9079 | v8::Isolate* isolate = context->GetIsolate(); |
| 9080 | v8::HandleScope scope(isolate); |
| 9081 | |
| 9082 | isolate->SetPrepareStackTraceCallback(PrepareStackTraceThrow); |
| 9083 | |
| 9084 | v8::Local<Value> v = CompileRun("try { new Error().stack } catch (e) { e }" ); |
| 9085 | |
| 9086 | CHECK(v->IsNativeError()); |
| 9087 | |
| 9088 | v8::Local<String> message = v8::Exception::CreateMessage(isolate, v)->Get(); |
| 9089 | |
| 9090 | CHECK(message->StrictEquals(v8_str("Uncaught Error: 42" ))); |
| 9091 | } |
| 9092 | |
| 9093 | THREADED_TEST(ErrorConstruction) { |
| 9094 | LocalContext context; |
| 9095 | v8::HandleScope scope(context->GetIsolate()); |
| 9096 | |
| 9097 | v8::Local<String> foo = v8_str("foo" ); |
| 9098 | v8::Local<String> message = v8_str("message" ); |
| 9099 | v8::Local<Value> range_error = v8::Exception::RangeError(foo); |
| 9100 | CHECK(range_error->IsObject()); |
| 9101 | CHECK(range_error.As<v8::Object>() |
| 9102 | ->Get(context.local(), message) |
| 9103 | .ToLocalChecked() |
| 9104 | ->Equals(context.local(), foo) |
| 9105 | .FromJust()); |
| 9106 | v8::Local<Value> reference_error = v8::Exception::ReferenceError(foo); |
| 9107 | CHECK(reference_error->IsObject()); |
| 9108 | CHECK(reference_error.As<v8::Object>() |
| 9109 | ->Get(context.local(), message) |
| 9110 | .ToLocalChecked() |
| 9111 | ->Equals(context.local(), foo) |
| 9112 | .FromJust()); |
| 9113 | v8::Local<Value> syntax_error = v8::Exception::SyntaxError(foo); |
| 9114 | CHECK(syntax_error->IsObject()); |
| 9115 | CHECK(syntax_error.As<v8::Object>() |
| 9116 | ->Get(context.local(), message) |
| 9117 | .ToLocalChecked() |
| 9118 | ->Equals(context.local(), foo) |
| 9119 | .FromJust()); |
| 9120 | v8::Local<Value> type_error = v8::Exception::TypeError(foo); |
| 9121 | CHECK(type_error->IsObject()); |
| 9122 | CHECK(type_error.As<v8::Object>() |
| 9123 | ->Get(context.local(), message) |
| 9124 | .ToLocalChecked() |
| 9125 | ->Equals(context.local(), foo) |
| 9126 | .FromJust()); |
| 9127 | v8::Local<Value> error = v8::Exception::Error(foo); |
| 9128 | CHECK(error->IsObject()); |
| 9129 | CHECK(error.As<v8::Object>() |
| 9130 | ->Get(context.local(), message) |
| 9131 | .ToLocalChecked() |
| 9132 | ->Equals(context.local(), foo) |
| 9133 | .FromJust()); |
| 9134 | } |
| 9135 | |
| 9136 | |
| 9137 | static void ThrowV8Exception(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 9138 | ApiTestFuzzer::Fuzz(); |
| 9139 | v8::Local<String> foo = v8_str("foo" ); |
| 9140 | v8::Local<String> message = v8_str("message" ); |
| 9141 | v8::Local<Value> error = v8::Exception::Error(foo); |
| 9142 | CHECK(error->IsObject()); |
| 9143 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 9144 | CHECK(error.As<v8::Object>() |
| 9145 | ->Get(context, message) |
| 9146 | .ToLocalChecked() |
| 9147 | ->Equals(context, foo) |
| 9148 | .FromJust()); |
| 9149 | info.GetIsolate()->ThrowException(error); |
| 9150 | info.GetReturnValue().SetUndefined(); |
| 9151 | } |
| 9152 | |
| 9153 | |
| 9154 | THREADED_TEST(ExceptionCreateMessage) { |
| 9155 | LocalContext context; |
| 9156 | v8::HandleScope scope(context->GetIsolate()); |
| 9157 | v8::Local<String> foo_str = v8_str("foo" ); |
| 9158 | v8::Local<String> message_str = v8_str("message" ); |
| 9159 | |
| 9160 | context->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(true); |
| 9161 | |
| 9162 | Local<v8::FunctionTemplate> fun = |
| 9163 | v8::FunctionTemplate::New(context->GetIsolate(), ThrowV8Exception); |
| 9164 | v8::Local<v8::Object> global = context->Global(); |
| 9165 | CHECK(global->Set(context.local(), v8_str("throwV8Exception" ), |
| 9166 | fun->GetFunction(context.local()).ToLocalChecked()) |
| 9167 | .FromJust()); |
| 9168 | |
| 9169 | TryCatch try_catch(context->GetIsolate()); |
| 9170 | CompileRun( |
| 9171 | "function f1() {\n" |
| 9172 | " throwV8Exception();\n" |
| 9173 | "};\n" |
| 9174 | "f1();" ); |
| 9175 | CHECK(try_catch.HasCaught()); |
| 9176 | |
| 9177 | v8::Local<v8::Value> error = try_catch.Exception(); |
| 9178 | CHECK(error->IsObject()); |
| 9179 | CHECK(error.As<v8::Object>() |
| 9180 | ->Get(context.local(), message_str) |
| 9181 | .ToLocalChecked() |
| 9182 | ->Equals(context.local(), foo_str) |
| 9183 | .FromJust()); |
| 9184 | |
| 9185 | v8::Local<v8::Message> message = |
| 9186 | v8::Exception::CreateMessage(context->GetIsolate(), error); |
| 9187 | CHECK(!message.IsEmpty()); |
| 9188 | CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust()); |
| 9189 | CHECK_EQ(2, message->GetStartColumn(context.local()).FromJust()); |
| 9190 | |
| 9191 | v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); |
| 9192 | CHECK(!stackTrace.IsEmpty()); |
| 9193 | CHECK_EQ(2, stackTrace->GetFrameCount()); |
| 9194 | |
| 9195 | stackTrace = v8::Exception::GetStackTrace(error); |
| 9196 | CHECK(!stackTrace.IsEmpty()); |
| 9197 | CHECK_EQ(2, stackTrace->GetFrameCount()); |
| 9198 | |
| 9199 | context->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(false); |
| 9200 | |
| 9201 | // Now check message location when SetCaptureStackTraceForUncaughtExceptions |
| 9202 | // is false. |
| 9203 | try_catch.Reset(); |
| 9204 | |
| 9205 | CompileRun( |
| 9206 | "function f2() {\n" |
| 9207 | " return throwV8Exception();\n" |
| 9208 | "};\n" |
| 9209 | "f2();" ); |
| 9210 | CHECK(try_catch.HasCaught()); |
| 9211 | |
| 9212 | error = try_catch.Exception(); |
| 9213 | CHECK(error->IsObject()); |
| 9214 | CHECK(error.As<v8::Object>() |
| 9215 | ->Get(context.local(), message_str) |
| 9216 | .ToLocalChecked() |
| 9217 | ->Equals(context.local(), foo_str) |
| 9218 | .FromJust()); |
| 9219 | |
| 9220 | message = v8::Exception::CreateMessage(context->GetIsolate(), error); |
| 9221 | CHECK(!message.IsEmpty()); |
| 9222 | CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust()); |
| 9223 | CHECK_EQ(9, message->GetStartColumn(context.local()).FromJust()); |
| 9224 | |
| 9225 | // Should be empty stack trace. |
| 9226 | stackTrace = message->GetStackTrace(); |
| 9227 | CHECK(stackTrace.IsEmpty()); |
| 9228 | CHECK(v8::Exception::GetStackTrace(error).IsEmpty()); |
| 9229 | } |
| 9230 | |
| 9231 | |
| 9232 | THREADED_TEST(ExceptionCreateMessageLength) { |
| 9233 | LocalContext context; |
| 9234 | v8::HandleScope scope(context->GetIsolate()); |
| 9235 | |
| 9236 | // Test that the message is not truncated. |
| 9237 | TryCatch try_catch(context->GetIsolate()); |
| 9238 | CompileRun( |
| 9239 | "var message = 'm';" |
| 9240 | "while (message.length < 1000) message += message;" |
| 9241 | "throw message;" ); |
| 9242 | CHECK(try_catch.HasCaught()); |
| 9243 | |
| 9244 | CHECK_LT(1000, try_catch.Message()->Get()->Length()); |
| 9245 | } |
| 9246 | |
| 9247 | |
| 9248 | static void YGetter(Local<String> name, |
| 9249 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 9250 | ApiTestFuzzer::Fuzz(); |
| 9251 | info.GetReturnValue().Set(v8_num(10)); |
| 9252 | } |
| 9253 | |
| 9254 | |
| 9255 | static void YSetter(Local<String> name, |
| 9256 | Local<Value> value, |
| 9257 | const v8::PropertyCallbackInfo<void>& info) { |
| 9258 | Local<Object> this_obj = Local<Object>::Cast(info.This()); |
| 9259 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 9260 | if (this_obj->Has(context, name).FromJust()) |
| 9261 | this_obj->Delete(context, name).FromJust(); |
| 9262 | CHECK(this_obj->Set(context, name, value).FromJust()); |
| 9263 | } |
| 9264 | |
| 9265 | |
| 9266 | THREADED_TEST(DeleteAccessor) { |
| 9267 | v8::Isolate* isolate = CcTest::isolate(); |
| 9268 | v8::HandleScope scope(isolate); |
| 9269 | v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); |
| 9270 | obj->SetAccessor(v8_str("y" ), YGetter, YSetter); |
| 9271 | LocalContext context; |
| 9272 | v8::Local<v8::Object> holder = |
| 9273 | obj->NewInstance(context.local()).ToLocalChecked(); |
| 9274 | CHECK(context->Global() |
| 9275 | ->Set(context.local(), v8_str("holder" ), holder) |
| 9276 | .FromJust()); |
| 9277 | v8::Local<Value> result = |
| 9278 | CompileRun("holder.y = 11; holder.y = 12; holder.y" ); |
| 9279 | CHECK_EQ(12u, result->Uint32Value(context.local()).FromJust()); |
| 9280 | } |
| 9281 | |
| 9282 | |
| 9283 | static int trouble_nesting = 0; |
| 9284 | static void TroubleCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 9285 | ApiTestFuzzer::Fuzz(); |
| 9286 | trouble_nesting++; |
| 9287 | |
| 9288 | // Call a JS function that throws an uncaught exception. |
| 9289 | Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| 9290 | Local<v8::Object> arg_this = context->Global(); |
| 9291 | Local<Value> trouble_callee = |
| 9292 | (trouble_nesting == 3) |
| 9293 | ? arg_this->Get(context, v8_str("trouble_callee" )).ToLocalChecked() |
| 9294 | : arg_this->Get(context, v8_str("trouble_caller" )).ToLocalChecked(); |
| 9295 | CHECK(trouble_callee->IsFunction()); |
| 9296 | args.GetReturnValue().Set(Function::Cast(*trouble_callee) |
| 9297 | ->Call(context, arg_this, 0, nullptr) |
| 9298 | .FromMaybe(v8::Local<v8::Value>())); |
| 9299 | } |
| 9300 | |
| 9301 | |
| 9302 | static int report_count = 0; |
| 9303 | static void ApiUncaughtExceptionTestListener(v8::Local<v8::Message>, |
| 9304 | v8::Local<Value>) { |
| 9305 | report_count++; |
| 9306 | } |
| 9307 | |
| 9308 | |
| 9309 | // Counts uncaught exceptions, but other tests running in parallel |
| 9310 | // also have uncaught exceptions. |
| 9311 | TEST(ApiUncaughtException) { |
| 9312 | report_count = 0; |
| 9313 | LocalContext env; |
| 9314 | v8::Isolate* isolate = env->GetIsolate(); |
| 9315 | v8::HandleScope scope(isolate); |
| 9316 | isolate->AddMessageListener(ApiUncaughtExceptionTestListener); |
| 9317 | |
| 9318 | Local<v8::FunctionTemplate> fun = |
| 9319 | v8::FunctionTemplate::New(isolate, TroubleCallback); |
| 9320 | v8::Local<v8::Object> global = env->Global(); |
| 9321 | CHECK(global->Set(env.local(), v8_str("trouble" ), |
| 9322 | fun->GetFunction(env.local()).ToLocalChecked()) |
| 9323 | .FromJust()); |
| 9324 | |
| 9325 | CompileRun( |
| 9326 | "function trouble_callee() {" |
| 9327 | " var x = null;" |
| 9328 | " return x.foo;" |
| 9329 | "};" |
| 9330 | "function trouble_caller() {" |
| 9331 | " trouble();" |
| 9332 | "};" ); |
| 9333 | Local<Value> trouble = |
| 9334 | global->Get(env.local(), v8_str("trouble" )).ToLocalChecked(); |
| 9335 | CHECK(trouble->IsFunction()); |
| 9336 | Local<Value> trouble_callee = |
| 9337 | global->Get(env.local(), v8_str("trouble_callee" )).ToLocalChecked(); |
| 9338 | CHECK(trouble_callee->IsFunction()); |
| 9339 | Local<Value> trouble_caller = |
| 9340 | global->Get(env.local(), v8_str("trouble_caller" )).ToLocalChecked(); |
| 9341 | CHECK(trouble_caller->IsFunction()); |
| 9342 | Function::Cast(*trouble_caller) |
| 9343 | ->Call(env.local(), global, 0, nullptr) |
| 9344 | .FromMaybe(v8::Local<v8::Value>()); |
| 9345 | CHECK_EQ(1, report_count); |
| 9346 | isolate->RemoveMessageListeners(ApiUncaughtExceptionTestListener); |
| 9347 | } |
| 9348 | |
| 9349 | |
| 9350 | static const char* script_resource_name = "ExceptionInNativeScript.js" ; |
| 9351 | static void ExceptionInNativeScriptTestListener(v8::Local<v8::Message> message, |
| 9352 | v8::Local<Value>) { |
| 9353 | v8::Local<v8::Value> name_val = message->GetScriptOrigin().ResourceName(); |
| 9354 | CHECK(!name_val.IsEmpty() && name_val->IsString()); |
| 9355 | v8::String::Utf8Value name(v8::Isolate::GetCurrent(), |
| 9356 | message->GetScriptOrigin().ResourceName()); |
| 9357 | CHECK_EQ(0, strcmp(script_resource_name, *name)); |
| 9358 | v8::Local<v8::Context> context = |
| 9359 | v8::Isolate::GetCurrent()->GetCurrentContext(); |
| 9360 | CHECK_EQ(3, message->GetLineNumber(context).FromJust()); |
| 9361 | v8::String::Utf8Value source_line( |
| 9362 | v8::Isolate::GetCurrent(), |
| 9363 | message->GetSourceLine(context).ToLocalChecked()); |
| 9364 | CHECK_EQ(0, strcmp(" new o.foo();" , *source_line)); |
| 9365 | } |
| 9366 | |
| 9367 | |
| 9368 | TEST(ExceptionInNativeScript) { |
| 9369 | LocalContext env; |
| 9370 | v8::Isolate* isolate = env->GetIsolate(); |
| 9371 | v8::HandleScope scope(isolate); |
| 9372 | isolate->AddMessageListener(ExceptionInNativeScriptTestListener); |
| 9373 | |
| 9374 | Local<v8::FunctionTemplate> fun = |
| 9375 | v8::FunctionTemplate::New(isolate, TroubleCallback); |
| 9376 | v8::Local<v8::Object> global = env->Global(); |
| 9377 | CHECK(global->Set(env.local(), v8_str("trouble" ), |
| 9378 | fun->GetFunction(env.local()).ToLocalChecked()) |
| 9379 | .FromJust()); |
| 9380 | |
| 9381 | CompileRunWithOrigin( |
| 9382 | "function trouble() {\n" |
| 9383 | " var o = {};\n" |
| 9384 | " new o.foo();\n" |
| 9385 | "};" , |
| 9386 | script_resource_name); |
| 9387 | Local<Value> trouble = |
| 9388 | global->Get(env.local(), v8_str("trouble" )).ToLocalChecked(); |
| 9389 | CHECK(trouble->IsFunction()); |
| 9390 | CHECK(Function::Cast(*trouble) |
| 9391 | ->Call(env.local(), global, 0, nullptr) |
| 9392 | .IsEmpty()); |
| 9393 | isolate->RemoveMessageListeners(ExceptionInNativeScriptTestListener); |
| 9394 | } |
| 9395 | |
| 9396 | |
| 9397 | TEST(CompilationErrorUsingTryCatchHandler) { |
| 9398 | LocalContext env; |
| 9399 | v8::HandleScope scope(env->GetIsolate()); |
| 9400 | v8::TryCatch try_catch(env->GetIsolate()); |
| 9401 | v8_compile("This doesn't &*&@#$&*^ compile." ); |
| 9402 | CHECK(*try_catch.Exception()); |
| 9403 | CHECK(try_catch.HasCaught()); |
| 9404 | } |
| 9405 | |
| 9406 | |
| 9407 | TEST(TryCatchFinallyUsingTryCatchHandler) { |
| 9408 | LocalContext env; |
| 9409 | v8::HandleScope scope(env->GetIsolate()); |
| 9410 | v8::TryCatch try_catch(env->GetIsolate()); |
| 9411 | CompileRun("try { throw ''; } catch (e) {}" ); |
| 9412 | CHECK(!try_catch.HasCaught()); |
| 9413 | CompileRun("try { throw ''; } finally {}" ); |
| 9414 | CHECK(try_catch.HasCaught()); |
| 9415 | try_catch.Reset(); |
| 9416 | CompileRun( |
| 9417 | "(function() {" |
| 9418 | "try { throw ''; } finally { return; }" |
| 9419 | "})()" ); |
| 9420 | CHECK(!try_catch.HasCaught()); |
| 9421 | CompileRun( |
| 9422 | "(function()" |
| 9423 | " { try { throw ''; } finally { throw 0; }" |
| 9424 | "})()" ); |
| 9425 | CHECK(try_catch.HasCaught()); |
| 9426 | } |
| 9427 | |
| 9428 | |
| 9429 | void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 9430 | v8::HandleScope scope(args.GetIsolate()); |
| 9431 | CompileRun(args[0] |
| 9432 | ->ToString(args.GetIsolate()->GetCurrentContext()) |
| 9433 | .ToLocalChecked()); |
| 9434 | } |
| 9435 | |
| 9436 | |
| 9437 | TEST(TryCatchFinallyStoresMessageUsingTryCatchHandler) { |
| 9438 | v8::Isolate* isolate = CcTest::isolate(); |
| 9439 | v8::HandleScope scope(isolate); |
| 9440 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 9441 | templ->Set(v8_str("CEvaluate" ), |
| 9442 | v8::FunctionTemplate::New(isolate, CEvaluate)); |
| 9443 | LocalContext context(nullptr, templ); |
| 9444 | v8::TryCatch try_catch(isolate); |
| 9445 | CompileRun("try {" |
| 9446 | " CEvaluate('throw 1;');" |
| 9447 | "} finally {" |
| 9448 | "}" ); |
| 9449 | CHECK(try_catch.HasCaught()); |
| 9450 | CHECK(!try_catch.Message().IsEmpty()); |
| 9451 | String::Utf8Value exception_value(isolate, try_catch.Exception()); |
| 9452 | CHECK_EQ(0, strcmp(*exception_value, "1" )); |
| 9453 | try_catch.Reset(); |
| 9454 | CompileRun("try {" |
| 9455 | " CEvaluate('throw 1;');" |
| 9456 | "} finally {" |
| 9457 | " throw 2;" |
| 9458 | "}" ); |
| 9459 | CHECK(try_catch.HasCaught()); |
| 9460 | CHECK(!try_catch.Message().IsEmpty()); |
| 9461 | String::Utf8Value finally_exception_value(isolate, try_catch.Exception()); |
| 9462 | CHECK_EQ(0, strcmp(*finally_exception_value, "2" )); |
| 9463 | } |
| 9464 | |
| 9465 | |
| 9466 | // For use within the TestSecurityHandler() test. |
| 9467 | static bool g_security_callback_result = false; |
| 9468 | static bool SecurityTestCallback(Local<v8::Context> accessing_context, |
| 9469 | Local<v8::Object> accessed_object, |
| 9470 | Local<v8::Value> data) { |
| 9471 | printf("a\n" ); |
| 9472 | CHECK(!data.IsEmpty() && data->IsInt32()); |
| 9473 | CHECK_EQ(42, data->Int32Value(accessing_context).FromJust()); |
| 9474 | return g_security_callback_result; |
| 9475 | } |
| 9476 | |
| 9477 | |
| 9478 | // SecurityHandler can't be run twice |
| 9479 | TEST(SecurityHandler) { |
| 9480 | v8::Isolate* isolate = CcTest::isolate(); |
| 9481 | v8::HandleScope scope0(isolate); |
| 9482 | v8::Local<v8::ObjectTemplate> global_template = |
| 9483 | v8::ObjectTemplate::New(isolate); |
| 9484 | global_template->SetAccessCheckCallback(SecurityTestCallback, v8_num(42)); |
| 9485 | // Create an environment |
| 9486 | v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| 9487 | context0->Enter(); |
| 9488 | |
| 9489 | v8::Local<v8::Object> global0 = context0->Global(); |
| 9490 | v8::Local<Script> script0 = v8_compile("foo = 111" ); |
| 9491 | script0->Run(context0).ToLocalChecked(); |
| 9492 | CHECK(global0->Set(context0, v8_str("0" ), v8_num(999)).FromJust()); |
| 9493 | v8::Local<Value> foo0 = |
| 9494 | global0->Get(context0, v8_str("foo" )).ToLocalChecked(); |
| 9495 | CHECK_EQ(111, foo0->Int32Value(context0).FromJust()); |
| 9496 | v8::Local<Value> z0 = global0->Get(context0, v8_str("0" )).ToLocalChecked(); |
| 9497 | CHECK_EQ(999, z0->Int32Value(context0).FromJust()); |
| 9498 | |
| 9499 | // Create another environment, should fail security checks. |
| 9500 | v8::HandleScope scope1(isolate); |
| 9501 | |
| 9502 | v8::Local<Context> context1 = Context::New(isolate, nullptr, global_template); |
| 9503 | context1->Enter(); |
| 9504 | |
| 9505 | v8::Local<v8::Object> global1 = context1->Global(); |
| 9506 | global1->Set(context1, v8_str("othercontext" ), global0).FromJust(); |
| 9507 | // This set will fail the security check. |
| 9508 | v8::Local<Script> script1 = |
| 9509 | v8_compile("othercontext.foo = 222; othercontext[0] = 888;" ); |
| 9510 | CHECK(script1->Run(context1).IsEmpty()); |
| 9511 | g_security_callback_result = true; |
| 9512 | // This read will pass the security check. |
| 9513 | v8::Local<Value> foo1 = |
| 9514 | global0->Get(context1, v8_str("foo" )).ToLocalChecked(); |
| 9515 | CHECK_EQ(111, foo1->Int32Value(context0).FromJust()); |
| 9516 | // This read will pass the security check. |
| 9517 | v8::Local<Value> z1 = global0->Get(context1, v8_str("0" )).ToLocalChecked(); |
| 9518 | CHECK_EQ(999, z1->Int32Value(context1).FromJust()); |
| 9519 | |
| 9520 | // Create another environment, should pass security checks. |
| 9521 | { |
| 9522 | v8::HandleScope scope2(isolate); |
| 9523 | LocalContext context2; |
| 9524 | v8::Local<v8::Object> global2 = context2->Global(); |
| 9525 | CHECK(global2->Set(context2.local(), v8_str("othercontext" ), global0) |
| 9526 | .FromJust()); |
| 9527 | v8::Local<Script> script2 = |
| 9528 | v8_compile("othercontext.foo = 333; othercontext[0] = 888;" ); |
| 9529 | script2->Run(context2.local()).ToLocalChecked(); |
| 9530 | v8::Local<Value> foo2 = |
| 9531 | global0->Get(context2.local(), v8_str("foo" )).ToLocalChecked(); |
| 9532 | CHECK_EQ(333, foo2->Int32Value(context2.local()).FromJust()); |
| 9533 | v8::Local<Value> z2 = |
| 9534 | global0->Get(context2.local(), v8_str("0" )).ToLocalChecked(); |
| 9535 | CHECK_EQ(888, z2->Int32Value(context2.local()).FromJust()); |
| 9536 | } |
| 9537 | |
| 9538 | context1->Exit(); |
| 9539 | context0->Exit(); |
| 9540 | } |
| 9541 | |
| 9542 | |
| 9543 | THREADED_TEST(SecurityChecks) { |
| 9544 | LocalContext env1; |
| 9545 | v8::HandleScope handle_scope(env1->GetIsolate()); |
| 9546 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9547 | |
| 9548 | Local<Value> foo = v8_str("foo" ); |
| 9549 | Local<Value> bar = v8_str("bar" ); |
| 9550 | |
| 9551 | // Set to the same domain. |
| 9552 | env1->SetSecurityToken(foo); |
| 9553 | |
| 9554 | // Create a function in env1. |
| 9555 | CompileRun("spy=function(){return spy;}" ); |
| 9556 | Local<Value> spy = |
| 9557 | env1->Global()->Get(env1.local(), v8_str("spy" )).ToLocalChecked(); |
| 9558 | CHECK(spy->IsFunction()); |
| 9559 | |
| 9560 | // Create another function accessing global objects. |
| 9561 | CompileRun("spy2=function(){return new this.Array();}" ); |
| 9562 | Local<Value> spy2 = |
| 9563 | env1->Global()->Get(env1.local(), v8_str("spy2" )).ToLocalChecked(); |
| 9564 | CHECK(spy2->IsFunction()); |
| 9565 | |
| 9566 | // Switch to env2 in the same domain and invoke spy on env2. |
| 9567 | { |
| 9568 | env2->SetSecurityToken(foo); |
| 9569 | // Enter env2 |
| 9570 | Context::Scope scope_env2(env2); |
| 9571 | Local<Value> result = Function::Cast(*spy) |
| 9572 | ->Call(env2, env2->Global(), 0, nullptr) |
| 9573 | .ToLocalChecked(); |
| 9574 | CHECK(result->IsFunction()); |
| 9575 | } |
| 9576 | |
| 9577 | { |
| 9578 | env2->SetSecurityToken(bar); |
| 9579 | Context::Scope scope_env2(env2); |
| 9580 | |
| 9581 | // Call cross_domain_call, it should throw an exception |
| 9582 | v8::TryCatch try_catch(env1->GetIsolate()); |
| 9583 | CHECK(Function::Cast(*spy2) |
| 9584 | ->Call(env2, env2->Global(), 0, nullptr) |
| 9585 | .IsEmpty()); |
| 9586 | CHECK(try_catch.HasCaught()); |
| 9587 | } |
| 9588 | } |
| 9589 | |
| 9590 | |
| 9591 | // Regression test case for issue 1183439. |
| 9592 | THREADED_TEST(SecurityChecksForPrototypeChain) { |
| 9593 | LocalContext current; |
| 9594 | v8::HandleScope scope(current->GetIsolate()); |
| 9595 | v8::Local<Context> other = Context::New(current->GetIsolate()); |
| 9596 | |
| 9597 | // Change context to be able to get to the Object function in the |
| 9598 | // other context without hitting the security checks. |
| 9599 | v8::Local<Value> other_object; |
| 9600 | { Context::Scope scope(other); |
| 9601 | other_object = |
| 9602 | other->Global()->Get(other, v8_str("Object" )).ToLocalChecked(); |
| 9603 | CHECK(other->Global()->Set(other, v8_num(42), v8_num(87)).FromJust()); |
| 9604 | } |
| 9605 | |
| 9606 | CHECK(current->Global() |
| 9607 | ->Set(current.local(), v8_str("other" ), other->Global()) |
| 9608 | .FromJust()); |
| 9609 | CHECK(v8_compile("other" ) |
| 9610 | ->Run(current.local()) |
| 9611 | .ToLocalChecked() |
| 9612 | ->Equals(current.local(), other->Global()) |
| 9613 | .FromJust()); |
| 9614 | |
| 9615 | // Make sure the security check fails here and we get an undefined |
| 9616 | // result instead of getting the Object function. Repeat in a loop |
| 9617 | // to make sure to exercise the IC code. |
| 9618 | v8::Local<Script> access_other0 = v8_compile("other.Object" ); |
| 9619 | v8::Local<Script> access_other1 = v8_compile("other[42]" ); |
| 9620 | for (int i = 0; i < 5; i++) { |
| 9621 | CHECK(access_other0->Run(current.local()).IsEmpty()); |
| 9622 | CHECK(access_other1->Run(current.local()).IsEmpty()); |
| 9623 | } |
| 9624 | |
| 9625 | // Create an object that has 'other' in its prototype chain and make |
| 9626 | // sure we cannot access the Object function indirectly through |
| 9627 | // that. Repeat in a loop to make sure to exercise the IC code. |
| 9628 | v8_compile( |
| 9629 | "function F() { };" |
| 9630 | "F.prototype = other;" |
| 9631 | "var f = new F();" ) |
| 9632 | ->Run(current.local()) |
| 9633 | .ToLocalChecked(); |
| 9634 | v8::Local<Script> access_f0 = v8_compile("f.Object" ); |
| 9635 | v8::Local<Script> access_f1 = v8_compile("f[42]" ); |
| 9636 | for (int j = 0; j < 5; j++) { |
| 9637 | CHECK(access_f0->Run(current.local()).IsEmpty()); |
| 9638 | CHECK(access_f1->Run(current.local()).IsEmpty()); |
| 9639 | } |
| 9640 | |
| 9641 | // Now it gets hairy: Set the prototype for the other global object |
| 9642 | // to be the current global object. The prototype chain for 'f' now |
| 9643 | // goes through 'other' but ends up in the current global object. |
| 9644 | { Context::Scope scope(other); |
| 9645 | CHECK(other->Global() |
| 9646 | ->Set(other, v8_str("__proto__" ), current->Global()) |
| 9647 | .FromJust()); |
| 9648 | } |
| 9649 | // Set a named and an index property on the current global |
| 9650 | // object. To force the lookup to go through the other global object, |
| 9651 | // the properties must not exist in the other global object. |
| 9652 | CHECK(current->Global() |
| 9653 | ->Set(current.local(), v8_str("foo" ), v8_num(100)) |
| 9654 | .FromJust()); |
| 9655 | CHECK(current->Global() |
| 9656 | ->Set(current.local(), v8_num(99), v8_num(101)) |
| 9657 | .FromJust()); |
| 9658 | // Try to read the properties from f and make sure that the access |
| 9659 | // gets stopped by the security checks on the other global object. |
| 9660 | Local<Script> access_f2 = v8_compile("f.foo" ); |
| 9661 | Local<Script> access_f3 = v8_compile("f[99]" ); |
| 9662 | for (int k = 0; k < 5; k++) { |
| 9663 | CHECK(access_f2->Run(current.local()).IsEmpty()); |
| 9664 | CHECK(access_f3->Run(current.local()).IsEmpty()); |
| 9665 | } |
| 9666 | } |
| 9667 | |
| 9668 | |
| 9669 | static bool security_check_with_gc_called; |
| 9670 | |
| 9671 | static bool SecurityTestCallbackWithGC(Local<v8::Context> accessing_context, |
| 9672 | Local<v8::Object> accessed_object, |
| 9673 | Local<v8::Value> data) { |
| 9674 | CcTest::CollectAllGarbage(); |
| 9675 | security_check_with_gc_called = true; |
| 9676 | return true; |
| 9677 | } |
| 9678 | |
| 9679 | |
| 9680 | TEST(SecurityTestGCAllowed) { |
| 9681 | v8::Isolate* isolate = CcTest::isolate(); |
| 9682 | v8::HandleScope handle_scope(isolate); |
| 9683 | v8::Local<v8::ObjectTemplate> object_template = |
| 9684 | v8::ObjectTemplate::New(isolate); |
| 9685 | object_template->SetAccessCheckCallback(SecurityTestCallbackWithGC); |
| 9686 | |
| 9687 | v8::Local<Context> context = Context::New(isolate); |
| 9688 | v8::Context::Scope context_scope(context); |
| 9689 | |
| 9690 | CHECK(context->Global() |
| 9691 | ->Set(context, v8_str("obj" ), |
| 9692 | object_template->NewInstance(context).ToLocalChecked()) |
| 9693 | .FromJust()); |
| 9694 | |
| 9695 | security_check_with_gc_called = false; |
| 9696 | CompileRun("obj[0] = new String(1002);" ); |
| 9697 | CHECK(security_check_with_gc_called); |
| 9698 | |
| 9699 | security_check_with_gc_called = false; |
| 9700 | CHECK(CompileRun("obj[0]" ) |
| 9701 | ->ToString(context) |
| 9702 | .ToLocalChecked() |
| 9703 | ->Equals(context, v8_str("1002" )) |
| 9704 | .FromJust()); |
| 9705 | CHECK(security_check_with_gc_called); |
| 9706 | } |
| 9707 | |
| 9708 | |
| 9709 | THREADED_TEST(CrossDomainDelete) { |
| 9710 | LocalContext env1; |
| 9711 | v8::HandleScope handle_scope(env1->GetIsolate()); |
| 9712 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9713 | |
| 9714 | Local<Value> foo = v8_str("foo" ); |
| 9715 | Local<Value> bar = v8_str("bar" ); |
| 9716 | |
| 9717 | // Set to the same domain. |
| 9718 | env1->SetSecurityToken(foo); |
| 9719 | env2->SetSecurityToken(foo); |
| 9720 | |
| 9721 | CHECK( |
| 9722 | env1->Global()->Set(env1.local(), v8_str("prop" ), v8_num(3)).FromJust()); |
| 9723 | CHECK(env2->Global()->Set(env2, v8_str("env1" ), env1->Global()).FromJust()); |
| 9724 | |
| 9725 | // Change env2 to a different domain and delete env1.prop. |
| 9726 | env2->SetSecurityToken(bar); |
| 9727 | { |
| 9728 | Context::Scope scope_env2(env2); |
| 9729 | Local<Value> result = |
| 9730 | CompileRun("delete env1.prop" ); |
| 9731 | CHECK(result.IsEmpty()); |
| 9732 | } |
| 9733 | |
| 9734 | // Check that env1.prop still exists. |
| 9735 | Local<Value> v = |
| 9736 | env1->Global()->Get(env1.local(), v8_str("prop" )).ToLocalChecked(); |
| 9737 | CHECK(v->IsNumber()); |
| 9738 | CHECK_EQ(3, v->Int32Value(env1.local()).FromJust()); |
| 9739 | } |
| 9740 | |
| 9741 | |
| 9742 | THREADED_TEST(CrossDomainPropertyIsEnumerable) { |
| 9743 | LocalContext env1; |
| 9744 | v8::HandleScope handle_scope(env1->GetIsolate()); |
| 9745 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9746 | |
| 9747 | Local<Value> foo = v8_str("foo" ); |
| 9748 | Local<Value> bar = v8_str("bar" ); |
| 9749 | |
| 9750 | // Set to the same domain. |
| 9751 | env1->SetSecurityToken(foo); |
| 9752 | env2->SetSecurityToken(foo); |
| 9753 | |
| 9754 | CHECK( |
| 9755 | env1->Global()->Set(env1.local(), v8_str("prop" ), v8_num(3)).FromJust()); |
| 9756 | CHECK(env2->Global()->Set(env2, v8_str("env1" ), env1->Global()).FromJust()); |
| 9757 | |
| 9758 | // env1.prop is enumerable in env2. |
| 9759 | Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')" ); |
| 9760 | { |
| 9761 | Context::Scope scope_env2(env2); |
| 9762 | Local<Value> result = CompileRun(test); |
| 9763 | CHECK(result->IsTrue()); |
| 9764 | } |
| 9765 | |
| 9766 | // Change env2 to a different domain and test again. |
| 9767 | env2->SetSecurityToken(bar); |
| 9768 | { |
| 9769 | Context::Scope scope_env2(env2); |
| 9770 | Local<Value> result = CompileRun(test); |
| 9771 | CHECK(result.IsEmpty()); |
| 9772 | } |
| 9773 | } |
| 9774 | |
| 9775 | |
| 9776 | THREADED_TEST(CrossDomainFor) { |
| 9777 | LocalContext env1; |
| 9778 | v8::HandleScope handle_scope(env1->GetIsolate()); |
| 9779 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9780 | |
| 9781 | Local<Value> foo = v8_str("foo" ); |
| 9782 | Local<Value> bar = v8_str("bar" ); |
| 9783 | |
| 9784 | // Set to the same domain. |
| 9785 | env1->SetSecurityToken(foo); |
| 9786 | env2->SetSecurityToken(foo); |
| 9787 | |
| 9788 | CHECK( |
| 9789 | env1->Global()->Set(env1.local(), v8_str("prop" ), v8_num(3)).FromJust()); |
| 9790 | CHECK(env2->Global()->Set(env2, v8_str("env1" ), env1->Global()).FromJust()); |
| 9791 | |
| 9792 | // Change env2 to a different domain and set env1's global object |
| 9793 | // as the __proto__ of an object in env2 and enumerate properties |
| 9794 | // in for-in. It shouldn't enumerate properties on env1's global |
| 9795 | // object. It shouldn't throw either, just silently ignore them. |
| 9796 | env2->SetSecurityToken(bar); |
| 9797 | { |
| 9798 | Context::Scope scope_env2(env2); |
| 9799 | Local<Value> result = CompileRun( |
| 9800 | "(function() {" |
| 9801 | " try {" |
| 9802 | " for (var p in env1) {" |
| 9803 | " if (p == 'prop') return false;" |
| 9804 | " }" |
| 9805 | " return true;" |
| 9806 | " } catch (e) {" |
| 9807 | " return false;" |
| 9808 | " }" |
| 9809 | "})()" ); |
| 9810 | CHECK(result->IsTrue()); |
| 9811 | } |
| 9812 | } |
| 9813 | |
| 9814 | |
| 9815 | THREADED_TEST(CrossDomainForInOnPrototype) { |
| 9816 | LocalContext env1; |
| 9817 | v8::HandleScope handle_scope(env1->GetIsolate()); |
| 9818 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9819 | |
| 9820 | Local<Value> foo = v8_str("foo" ); |
| 9821 | Local<Value> bar = v8_str("bar" ); |
| 9822 | |
| 9823 | // Set to the same domain. |
| 9824 | env1->SetSecurityToken(foo); |
| 9825 | env2->SetSecurityToken(foo); |
| 9826 | |
| 9827 | CHECK( |
| 9828 | env1->Global()->Set(env1.local(), v8_str("prop" ), v8_num(3)).FromJust()); |
| 9829 | CHECK(env2->Global()->Set(env2, v8_str("env1" ), env1->Global()).FromJust()); |
| 9830 | |
| 9831 | // Change env2 to a different domain and set env1's global object |
| 9832 | // as the __proto__ of an object in env2 and enumerate properties |
| 9833 | // in for-in. It shouldn't enumerate properties on env1's global |
| 9834 | // object. |
| 9835 | env2->SetSecurityToken(bar); |
| 9836 | { |
| 9837 | Context::Scope scope_env2(env2); |
| 9838 | Local<Value> result = CompileRun( |
| 9839 | "(function() {" |
| 9840 | " var obj = { '__proto__': env1 };" |
| 9841 | " try {" |
| 9842 | " for (var p in obj) {" |
| 9843 | " if (p == 'prop') return false;" |
| 9844 | " }" |
| 9845 | " return true;" |
| 9846 | " } catch (e) {" |
| 9847 | " return false;" |
| 9848 | " }" |
| 9849 | "})()" ); |
| 9850 | CHECK(result->IsTrue()); |
| 9851 | } |
| 9852 | } |
| 9853 | |
| 9854 | |
| 9855 | TEST(ContextDetachGlobal) { |
| 9856 | LocalContext env1; |
| 9857 | v8::HandleScope handle_scope(env1->GetIsolate()); |
| 9858 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9859 | |
| 9860 | |
| 9861 | Local<Value> foo = v8_str("foo" ); |
| 9862 | |
| 9863 | // Set to the same domain. |
| 9864 | env1->SetSecurityToken(foo); |
| 9865 | env2->SetSecurityToken(foo); |
| 9866 | |
| 9867 | // Enter env2 |
| 9868 | env2->Enter(); |
| 9869 | |
| 9870 | // Create a function in env2 and add a reference to it in env1. |
| 9871 | Local<v8::Object> global2 = env2->Global(); |
| 9872 | CHECK(global2->Set(env2, v8_str("prop" ), |
| 9873 | v8::Integer::New(env2->GetIsolate(), 1)) |
| 9874 | .FromJust()); |
| 9875 | CompileRun("function getProp() {return prop;}" ); |
| 9876 | |
| 9877 | CHECK(env1->Global() |
| 9878 | ->Set(env1.local(), v8_str("getProp" ), |
| 9879 | global2->Get(env2, v8_str("getProp" )).ToLocalChecked()) |
| 9880 | .FromJust()); |
| 9881 | |
| 9882 | // Detach env2's global, and reuse the global object of env2 |
| 9883 | env2->Exit(); |
| 9884 | env2->DetachGlobal(); |
| 9885 | |
| 9886 | v8::Local<Context> env3 = Context::New( |
| 9887 | env1->GetIsolate(), nullptr, v8::Local<v8::ObjectTemplate>(), global2); |
| 9888 | env3->SetSecurityToken(v8_str("bar" )); |
| 9889 | |
| 9890 | env3->Enter(); |
| 9891 | Local<v8::Object> global3 = env3->Global(); |
| 9892 | CHECK(global2->Equals(env3, global3).FromJust()); |
| 9893 | CHECK(global3->Get(env3, v8_str("prop" )).ToLocalChecked()->IsUndefined()); |
| 9894 | CHECK(global3->Get(env3, v8_str("getProp" )).ToLocalChecked()->IsUndefined()); |
| 9895 | CHECK(global3->Set(env3, v8_str("prop" ), |
| 9896 | v8::Integer::New(env3->GetIsolate(), -1)) |
| 9897 | .FromJust()); |
| 9898 | CHECK(global3->Set(env3, v8_str("prop2" ), |
| 9899 | v8::Integer::New(env3->GetIsolate(), 2)) |
| 9900 | .FromJust()); |
| 9901 | env3->Exit(); |
| 9902 | |
| 9903 | // Call getProp in env1, and it should return the value 1 |
| 9904 | { |
| 9905 | Local<v8::Object> global1 = env1->Global(); |
| 9906 | Local<Value> get_prop = |
| 9907 | global1->Get(env1.local(), v8_str("getProp" )).ToLocalChecked(); |
| 9908 | CHECK(get_prop->IsFunction()); |
| 9909 | v8::TryCatch try_catch(env1->GetIsolate()); |
| 9910 | Local<Value> r = Function::Cast(*get_prop) |
| 9911 | ->Call(env1.local(), global1, 0, nullptr) |
| 9912 | .ToLocalChecked(); |
| 9913 | CHECK(!try_catch.HasCaught()); |
| 9914 | CHECK_EQ(1, r->Int32Value(env1.local()).FromJust()); |
| 9915 | } |
| 9916 | |
| 9917 | // Check that env3 is not accessible from env1 |
| 9918 | { |
| 9919 | v8::MaybeLocal<Value> r = global3->Get(env1.local(), v8_str("prop2" )); |
| 9920 | CHECK(r.IsEmpty()); |
| 9921 | } |
| 9922 | } |
| 9923 | |
| 9924 | |
| 9925 | TEST(DetachGlobal) { |
| 9926 | LocalContext env1; |
| 9927 | v8::HandleScope scope(env1->GetIsolate()); |
| 9928 | |
| 9929 | // Create second environment. |
| 9930 | v8::Local<Context> env2 = Context::New(env1->GetIsolate()); |
| 9931 | |
| 9932 | Local<Value> foo = v8_str("foo" ); |
| 9933 | |
| 9934 | // Set same security token for env1 and env2. |
| 9935 | env1->SetSecurityToken(foo); |
| 9936 | env2->SetSecurityToken(foo); |
| 9937 | |
| 9938 | // Create a property on the global object in env2. |
| 9939 | { |
| 9940 | v8::Context::Scope scope(env2); |
| 9941 | CHECK(env2->Global() |
| 9942 | ->Set(env2, v8_str("p" ), v8::Integer::New(env2->GetIsolate(), 42)) |
| 9943 | .FromJust()); |
| 9944 | } |
| 9945 | |
| 9946 | // Create a reference to env2 global from env1 global. |
| 9947 | CHECK(env1->Global() |
| 9948 | ->Set(env1.local(), v8_str("other" ), env2->Global()) |
| 9949 | .FromJust()); |
| 9950 | |
| 9951 | // Check that we have access to other.p in env2 from env1. |
| 9952 | Local<Value> result = CompileRun("other.p" ); |
| 9953 | CHECK(result->IsInt32()); |
| 9954 | CHECK_EQ(42, result->Int32Value(env1.local()).FromJust()); |
| 9955 | |
| 9956 | // Hold on to global from env2 and detach global from env2. |
| 9957 | Local<v8::Object> global2 = env2->Global(); |
| 9958 | env2->DetachGlobal(); |
| 9959 | |
| 9960 | // Check that the global has been detached. No other.p property can |
| 9961 | // be found. |
| 9962 | result = CompileRun("other.p" ); |
| 9963 | CHECK(result.IsEmpty()); |
| 9964 | |
| 9965 | // Reuse global2 for env3. |
| 9966 | v8::Local<Context> env3 = Context::New( |
| 9967 | env1->GetIsolate(), nullptr, v8::Local<v8::ObjectTemplate>(), global2); |
| 9968 | CHECK(global2->Equals(env1.local(), env3->Global()).FromJust()); |
| 9969 | |
| 9970 | // Start by using the same security token for env3 as for env1 and env2. |
| 9971 | env3->SetSecurityToken(foo); |
| 9972 | |
| 9973 | // Create a property on the global object in env3. |
| 9974 | { |
| 9975 | v8::Context::Scope scope(env3); |
| 9976 | CHECK(env3->Global() |
| 9977 | ->Set(env3, v8_str("p" ), v8::Integer::New(env3->GetIsolate(), 24)) |
| 9978 | .FromJust()); |
| 9979 | } |
| 9980 | |
| 9981 | // Check that other.p is now the property in env3 and that we have access. |
| 9982 | result = CompileRun("other.p" ); |
| 9983 | CHECK(result->IsInt32()); |
| 9984 | CHECK_EQ(24, result->Int32Value(env3).FromJust()); |
| 9985 | } |
| 9986 | |
| 9987 | |
| 9988 | void GetThisX(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 9989 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 9990 | info.GetReturnValue().Set( |
| 9991 | context->Global()->Get(context, v8_str("x" )).ToLocalChecked()); |
| 9992 | } |
| 9993 | |
| 9994 | |
| 9995 | TEST(DetachedAccesses) { |
| 9996 | LocalContext env1; |
| 9997 | v8::HandleScope scope(env1->GetIsolate()); |
| 9998 | |
| 9999 | // Create second environment. |
| 10000 | Local<ObjectTemplate> inner_global_template = |
| 10001 | FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate(); |
| 10002 | inner_global_template ->SetAccessorProperty( |
| 10003 | v8_str("this_x" ), FunctionTemplate::New(env1->GetIsolate(), GetThisX)); |
| 10004 | v8::Local<Context> env2 = |
| 10005 | Context::New(env1->GetIsolate(), nullptr, inner_global_template); |
| 10006 | |
| 10007 | Local<Value> foo = v8_str("foo" ); |
| 10008 | |
| 10009 | // Set same security token for env1 and env2. |
| 10010 | env1->SetSecurityToken(foo); |
| 10011 | env2->SetSecurityToken(foo); |
| 10012 | |
| 10013 | CHECK(env1->Global() |
| 10014 | ->Set(env1.local(), v8_str("x" ), v8_str("env1_x" )) |
| 10015 | .FromJust()); |
| 10016 | |
| 10017 | { |
| 10018 | v8::Context::Scope scope(env2); |
| 10019 | CHECK(env2->Global()->Set(env2, v8_str("x" ), v8_str("env2_x" )).FromJust()); |
| 10020 | CompileRun( |
| 10021 | "function bound_x() { return x; }" |
| 10022 | "function get_x() { return this.x; }" |
| 10023 | "function get_x_w() { return (function() {return this.x;})(); }" ); |
| 10024 | CHECK(env1->Global() |
| 10025 | ->Set(env1.local(), v8_str("bound_x" ), CompileRun("bound_x" )) |
| 10026 | .FromJust()); |
| 10027 | CHECK(env1->Global() |
| 10028 | ->Set(env1.local(), v8_str("get_x" ), CompileRun("get_x" )) |
| 10029 | .FromJust()); |
| 10030 | CHECK(env1->Global() |
| 10031 | ->Set(env1.local(), v8_str("get_x_w" ), CompileRun("get_x_w" )) |
| 10032 | .FromJust()); |
| 10033 | env1->Global() |
| 10034 | ->Set(env1.local(), v8_str("this_x" ), |
| 10035 | CompileRun("Object.getOwnPropertyDescriptor(this, 'this_x').get" )) |
| 10036 | .FromJust(); |
| 10037 | } |
| 10038 | |
| 10039 | Local<Object> env2_global = env2->Global(); |
| 10040 | env2->DetachGlobal(); |
| 10041 | |
| 10042 | Local<Value> result; |
| 10043 | result = CompileRun("bound_x()" ); |
| 10044 | CHECK(v8_str("env2_x" )->Equals(env1.local(), result).FromJust()); |
| 10045 | result = CompileRun("get_x()" ); |
| 10046 | CHECK(result.IsEmpty()); |
| 10047 | result = CompileRun("get_x_w()" ); |
| 10048 | CHECK(result.IsEmpty()); |
| 10049 | result = CompileRun("this_x()" ); |
| 10050 | CHECK(v8_str("env2_x" )->Equals(env1.local(), result).FromJust()); |
| 10051 | |
| 10052 | // Reattach env2's proxy |
| 10053 | env2 = Context::New(env1->GetIsolate(), nullptr, |
| 10054 | v8::Local<v8::ObjectTemplate>(), env2_global); |
| 10055 | env2->SetSecurityToken(foo); |
| 10056 | { |
| 10057 | v8::Context::Scope scope(env2); |
| 10058 | CHECK(env2->Global()->Set(env2, v8_str("x" ), v8_str("env3_x" )).FromJust()); |
| 10059 | CHECK(env2->Global()->Set(env2, v8_str("env1" ), env1->Global()).FromJust()); |
| 10060 | result = CompileRun( |
| 10061 | "results = [];" |
| 10062 | "for (var i = 0; i < 4; i++ ) {" |
| 10063 | " results.push(env1.bound_x());" |
| 10064 | " results.push(env1.get_x());" |
| 10065 | " results.push(env1.get_x_w());" |
| 10066 | " results.push(env1.this_x());" |
| 10067 | "}" |
| 10068 | "results" ); |
| 10069 | Local<v8::Array> results = Local<v8::Array>::Cast(result); |
| 10070 | CHECK_EQ(16u, results->Length()); |
| 10071 | for (int i = 0; i < 16; i += 4) { |
| 10072 | CHECK(v8_str("env2_x" ) |
| 10073 | ->Equals(env2, results->Get(env2, i + 0).ToLocalChecked()) |
| 10074 | .FromJust()); |
| 10075 | CHECK(v8_str("env1_x" ) |
| 10076 | ->Equals(env2, results->Get(env2, i + 1).ToLocalChecked()) |
| 10077 | .FromJust()); |
| 10078 | CHECK(v8_str("env3_x" ) |
| 10079 | ->Equals(env2, results->Get(env2, i + 2).ToLocalChecked()) |
| 10080 | .FromJust()); |
| 10081 | CHECK(v8_str("env2_x" ) |
| 10082 | ->Equals(env2, results->Get(env2, i + 3).ToLocalChecked()) |
| 10083 | .FromJust()); |
| 10084 | } |
| 10085 | } |
| 10086 | |
| 10087 | result = CompileRun( |
| 10088 | "results = [];" |
| 10089 | "for (var i = 0; i < 4; i++ ) {" |
| 10090 | " results.push(bound_x());" |
| 10091 | " results.push(get_x());" |
| 10092 | " results.push(get_x_w());" |
| 10093 | " results.push(this_x());" |
| 10094 | "}" |
| 10095 | "results" ); |
| 10096 | Local<v8::Array> results = Local<v8::Array>::Cast(result); |
| 10097 | CHECK_EQ(16u, results->Length()); |
| 10098 | for (int i = 0; i < 16; i += 4) { |
| 10099 | CHECK(v8_str("env2_x" ) |
| 10100 | ->Equals(env1.local(), |
| 10101 | results->Get(env1.local(), i + 0).ToLocalChecked()) |
| 10102 | .FromJust()); |
| 10103 | CHECK(v8_str("env3_x" ) |
| 10104 | ->Equals(env1.local(), |
| 10105 | results->Get(env1.local(), i + 1).ToLocalChecked()) |
| 10106 | .FromJust()); |
| 10107 | CHECK(v8_str("env3_x" ) |
| 10108 | ->Equals(env1.local(), |
| 10109 | results->Get(env1.local(), i + 2).ToLocalChecked()) |
| 10110 | .FromJust()); |
| 10111 | CHECK(v8_str("env2_x" ) |
| 10112 | ->Equals(env1.local(), |
| 10113 | results->Get(env1.local(), i + 3).ToLocalChecked()) |
| 10114 | .FromJust()); |
| 10115 | } |
| 10116 | |
| 10117 | result = CompileRun( |
| 10118 | "results = [];" |
| 10119 | "for (var i = 0; i < 4; i++ ) {" |
| 10120 | " results.push(this.bound_x());" |
| 10121 | " results.push(this.get_x());" |
| 10122 | " results.push(this.get_x_w());" |
| 10123 | " results.push(this.this_x());" |
| 10124 | "}" |
| 10125 | "results" ); |
| 10126 | results = Local<v8::Array>::Cast(result); |
| 10127 | CHECK_EQ(16u, results->Length()); |
| 10128 | for (int i = 0; i < 16; i += 4) { |
| 10129 | CHECK(v8_str("env2_x" ) |
| 10130 | ->Equals(env1.local(), |
| 10131 | results->Get(env1.local(), i + 0).ToLocalChecked()) |
| 10132 | .FromJust()); |
| 10133 | CHECK(v8_str("env1_x" ) |
| 10134 | ->Equals(env1.local(), |
| 10135 | results->Get(env1.local(), i + 1).ToLocalChecked()) |
| 10136 | .FromJust()); |
| 10137 | CHECK(v8_str("env3_x" ) |
| 10138 | ->Equals(env1.local(), |
| 10139 | results->Get(env1.local(), i + 2).ToLocalChecked()) |
| 10140 | .FromJust()); |
| 10141 | CHECK(v8_str("env2_x" ) |
| 10142 | ->Equals(env1.local(), |
| 10143 | results->Get(env1.local(), i + 3).ToLocalChecked()) |
| 10144 | .FromJust()); |
| 10145 | } |
| 10146 | } |
| 10147 | |
| 10148 | |
| 10149 | static bool allowed_access = false; |
| 10150 | static bool AccessBlocker(Local<v8::Context> accessing_context, |
| 10151 | Local<v8::Object> accessed_object, |
| 10152 | Local<v8::Value> data) { |
| 10153 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 10154 | return context->Global()->Equals(context, accessed_object).FromJust() || |
| 10155 | allowed_access; |
| 10156 | } |
| 10157 | |
| 10158 | |
| 10159 | static int g_echo_value = -1; |
| 10160 | |
| 10161 | |
| 10162 | static void EchoGetter( |
| 10163 | Local<String> name, |
| 10164 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 10165 | info.GetReturnValue().Set(v8_num(g_echo_value)); |
| 10166 | } |
| 10167 | |
| 10168 | |
| 10169 | static void EchoSetter(Local<String> name, Local<Value> value, |
| 10170 | const v8::PropertyCallbackInfo<void>& args) { |
| 10171 | if (value->IsNumber()) |
| 10172 | g_echo_value = |
| 10173 | value->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust(); |
| 10174 | } |
| 10175 | |
| 10176 | |
| 10177 | static void UnreachableGetter( |
| 10178 | Local<String> name, |
| 10179 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 10180 | UNREACHABLE(); // This function should not be called.. |
| 10181 | } |
| 10182 | |
| 10183 | |
| 10184 | static void UnreachableSetter(Local<String>, |
| 10185 | Local<Value>, |
| 10186 | const v8::PropertyCallbackInfo<void>&) { |
| 10187 | UNREACHABLE(); // This function should not be called. |
| 10188 | } |
| 10189 | |
| 10190 | |
| 10191 | static void UnreachableFunction( |
| 10192 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 10193 | UNREACHABLE(); // This function should not be called.. |
| 10194 | } |
| 10195 | |
| 10196 | |
| 10197 | TEST(AccessControl) { |
| 10198 | v8::Isolate* isolate = CcTest::isolate(); |
| 10199 | v8::HandleScope handle_scope(isolate); |
| 10200 | v8::Local<v8::ObjectTemplate> global_template = |
| 10201 | v8::ObjectTemplate::New(isolate); |
| 10202 | |
| 10203 | global_template->SetAccessCheckCallback(AccessBlocker); |
| 10204 | |
| 10205 | // Add an accessor accessible by cross-domain JS code. |
| 10206 | global_template->SetAccessor( |
| 10207 | v8_str("accessible_prop" ), EchoGetter, EchoSetter, v8::Local<Value>(), |
| 10208 | v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| 10209 | |
| 10210 | |
| 10211 | // Add an accessor that is not accessible by cross-domain JS code. |
| 10212 | global_template->SetAccessor(v8_str("blocked_prop" ), UnreachableGetter, |
| 10213 | UnreachableSetter, v8::Local<Value>(), |
| 10214 | v8::DEFAULT); |
| 10215 | |
| 10216 | global_template->SetAccessorProperty( |
| 10217 | v8_str("blocked_js_prop" ), |
| 10218 | v8::FunctionTemplate::New(isolate, UnreachableFunction), |
| 10219 | v8::FunctionTemplate::New(isolate, UnreachableFunction), |
| 10220 | v8::None, |
| 10221 | v8::DEFAULT); |
| 10222 | |
| 10223 | // Create an environment |
| 10224 | v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| 10225 | context0->Enter(); |
| 10226 | |
| 10227 | v8::Local<v8::Object> global0 = context0->Global(); |
| 10228 | |
| 10229 | // Define a property with JS getter and setter. |
| 10230 | CompileRun( |
| 10231 | "function getter() { return 'getter'; };\n" |
| 10232 | "function setter() { return 'setter'; }\n" |
| 10233 | "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})" ); |
| 10234 | |
| 10235 | Local<Value> getter = |
| 10236 | global0->Get(context0, v8_str("getter" )).ToLocalChecked(); |
| 10237 | Local<Value> setter = |
| 10238 | global0->Get(context0, v8_str("setter" )).ToLocalChecked(); |
| 10239 | |
| 10240 | // And define normal element. |
| 10241 | CHECK(global0->Set(context0, 239, v8_str("239" )).FromJust()); |
| 10242 | |
| 10243 | // Define an element with JS getter and setter. |
| 10244 | CompileRun( |
| 10245 | "function el_getter() { return 'el_getter'; };\n" |
| 10246 | "function el_setter() { return 'el_setter'; };\n" |
| 10247 | "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});" ); |
| 10248 | |
| 10249 | Local<Value> el_getter = |
| 10250 | global0->Get(context0, v8_str("el_getter" )).ToLocalChecked(); |
| 10251 | Local<Value> el_setter = |
| 10252 | global0->Get(context0, v8_str("el_setter" )).ToLocalChecked(); |
| 10253 | |
| 10254 | v8::HandleScope scope1(isolate); |
| 10255 | |
| 10256 | v8::Local<Context> context1 = Context::New(isolate); |
| 10257 | context1->Enter(); |
| 10258 | |
| 10259 | v8::Local<v8::Object> global1 = context1->Global(); |
| 10260 | CHECK(global1->Set(context1, v8_str("other" ), global0).FromJust()); |
| 10261 | |
| 10262 | // Access blocked property. |
| 10263 | CompileRun("other.blocked_prop = 1" ); |
| 10264 | |
| 10265 | CHECK(CompileRun("other.blocked_prop" ).IsEmpty()); |
| 10266 | CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')" ) |
| 10267 | .IsEmpty()); |
| 10268 | CHECK( |
| 10269 | CompileRun("propertyIsEnumerable.call(other, 'blocked_prop')" ).IsEmpty()); |
| 10270 | |
| 10271 | // Access blocked element. |
| 10272 | CHECK(CompileRun("other[239] = 1" ).IsEmpty()); |
| 10273 | |
| 10274 | CHECK(CompileRun("other[239]" ).IsEmpty()); |
| 10275 | CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '239')" ).IsEmpty()); |
| 10276 | CHECK(CompileRun("propertyIsEnumerable.call(other, '239')" ).IsEmpty()); |
| 10277 | |
| 10278 | allowed_access = true; |
| 10279 | // Now we can enumerate the property. |
| 10280 | ExpectTrue("propertyIsEnumerable.call(other, '239')" ); |
| 10281 | allowed_access = false; |
| 10282 | |
| 10283 | // Access a property with JS accessor. |
| 10284 | CHECK(CompileRun("other.js_accessor_p = 2" ).IsEmpty()); |
| 10285 | |
| 10286 | CHECK(CompileRun("other.js_accessor_p" ).IsEmpty()); |
| 10287 | CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'js_accessor_p')" ) |
| 10288 | .IsEmpty()); |
| 10289 | |
| 10290 | allowed_access = true; |
| 10291 | |
| 10292 | ExpectString("other.js_accessor_p" , "getter" ); |
| 10293 | ExpectObject( |
| 10294 | "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get" , getter); |
| 10295 | ExpectObject( |
| 10296 | "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set" , setter); |
| 10297 | ExpectUndefined( |
| 10298 | "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value" ); |
| 10299 | |
| 10300 | allowed_access = false; |
| 10301 | |
| 10302 | // Access an element with JS accessor. |
| 10303 | CHECK(CompileRun("other[42] = 2" ).IsEmpty()); |
| 10304 | |
| 10305 | CHECK(CompileRun("other[42]" ).IsEmpty()); |
| 10306 | CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, '42')" ).IsEmpty()); |
| 10307 | |
| 10308 | allowed_access = true; |
| 10309 | |
| 10310 | ExpectString("other[42]" , "el_getter" ); |
| 10311 | ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get" , el_getter); |
| 10312 | ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set" , el_setter); |
| 10313 | ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value" ); |
| 10314 | |
| 10315 | allowed_access = false; |
| 10316 | |
| 10317 | v8::Local<Value> value; |
| 10318 | |
| 10319 | // Access accessible property |
| 10320 | value = CompileRun("other.accessible_prop = 3" ); |
| 10321 | CHECK(value->IsNumber()); |
| 10322 | CHECK_EQ(3, value->Int32Value(context1).FromJust()); |
| 10323 | CHECK_EQ(3, g_echo_value); |
| 10324 | |
| 10325 | value = CompileRun("other.accessible_prop" ); |
| 10326 | CHECK(value->IsNumber()); |
| 10327 | CHECK_EQ(3, value->Int32Value(context1).FromJust()); |
| 10328 | |
| 10329 | value = CompileRun( |
| 10330 | "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value" ); |
| 10331 | CHECK(value->IsNumber()); |
| 10332 | CHECK_EQ(3, value->Int32Value(context1).FromJust()); |
| 10333 | |
| 10334 | value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')" ); |
| 10335 | CHECK(value->IsTrue()); |
| 10336 | |
| 10337 | // Enumeration doesn't enumerate accessors from inaccessible objects in |
| 10338 | // the prototype chain even if the accessors are in themselves accessible. |
| 10339 | // Enumeration doesn't throw, it silently ignores what it can't access. |
| 10340 | value = CompileRun( |
| 10341 | "(function() {" |
| 10342 | " var obj = { '__proto__': other };" |
| 10343 | " try {" |
| 10344 | " for (var p in obj) {" |
| 10345 | " if (p == 'accessible_prop' ||" |
| 10346 | " p == 'blocked_js_prop' ||" |
| 10347 | " p == 'blocked_js_prop') {" |
| 10348 | " return false;" |
| 10349 | " }" |
| 10350 | " }" |
| 10351 | " return true;" |
| 10352 | " } catch (e) {" |
| 10353 | " return false;" |
| 10354 | " }" |
| 10355 | "})()" ); |
| 10356 | CHECK(value->IsTrue()); |
| 10357 | |
| 10358 | // Test that preventExtensions fails on a non-accessible object even if that |
| 10359 | // object is already non-extensible. |
| 10360 | CHECK(global1->Set(context1, v8_str("checked_object" ), |
| 10361 | global_template->NewInstance(context1).ToLocalChecked()) |
| 10362 | .FromJust()); |
| 10363 | allowed_access = true; |
| 10364 | CompileRun("Object.preventExtensions(checked_object)" ); |
| 10365 | ExpectFalse("Object.isExtensible(checked_object)" ); |
| 10366 | allowed_access = false; |
| 10367 | CHECK(CompileRun("Object.preventExtensions(checked_object)" ).IsEmpty()); |
| 10368 | |
| 10369 | context1->Exit(); |
| 10370 | context0->Exit(); |
| 10371 | } |
| 10372 | |
| 10373 | |
| 10374 | TEST(AccessControlES5) { |
| 10375 | v8::Isolate* isolate = CcTest::isolate(); |
| 10376 | v8::HandleScope handle_scope(isolate); |
| 10377 | v8::Local<v8::ObjectTemplate> global_template = |
| 10378 | v8::ObjectTemplate::New(isolate); |
| 10379 | |
| 10380 | global_template->SetAccessCheckCallback(AccessBlocker); |
| 10381 | |
| 10382 | // Add accessible accessor. |
| 10383 | global_template->SetAccessor( |
| 10384 | v8_str("accessible_prop" ), EchoGetter, EchoSetter, v8::Local<Value>(), |
| 10385 | v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| 10386 | |
| 10387 | |
| 10388 | // Add an accessor that is not accessible by cross-domain JS code. |
| 10389 | global_template->SetAccessor(v8_str("blocked_prop" ), UnreachableGetter, |
| 10390 | UnreachableSetter, v8::Local<Value>(), |
| 10391 | v8::DEFAULT); |
| 10392 | |
| 10393 | // Create an environment |
| 10394 | v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| 10395 | context0->Enter(); |
| 10396 | |
| 10397 | v8::Local<v8::Object> global0 = context0->Global(); |
| 10398 | |
| 10399 | v8::Local<Context> context1 = Context::New(isolate); |
| 10400 | context1->Enter(); |
| 10401 | v8::Local<v8::Object> global1 = context1->Global(); |
| 10402 | CHECK(global1->Set(context1, v8_str("other" ), global0).FromJust()); |
| 10403 | |
| 10404 | // Regression test for issue 1154. |
| 10405 | CHECK(CompileRun("Object.keys(other).length == 1" )->BooleanValue(isolate)); |
| 10406 | CHECK(CompileRun("Object.keys(other)[0] == 'accessible_prop'" ) |
| 10407 | ->BooleanValue(isolate)); |
| 10408 | CHECK(CompileRun("other.blocked_prop" ).IsEmpty()); |
| 10409 | |
| 10410 | // Regression test for issue 1027. |
| 10411 | CompileRun("Object.defineProperty(\n" |
| 10412 | " other, 'blocked_prop', {configurable: false})" ); |
| 10413 | CHECK(CompileRun("other.blocked_prop" ).IsEmpty()); |
| 10414 | CHECK(CompileRun("Object.getOwnPropertyDescriptor(other, 'blocked_prop')" ) |
| 10415 | .IsEmpty()); |
| 10416 | |
| 10417 | // Regression test for issue 1171. |
| 10418 | ExpectTrue("Object.isExtensible(other)" ); |
| 10419 | CompileRun("Object.preventExtensions(other)" ); |
| 10420 | ExpectTrue("Object.isExtensible(other)" ); |
| 10421 | |
| 10422 | // Object.seal and Object.freeze. |
| 10423 | CompileRun("Object.freeze(other)" ); |
| 10424 | ExpectTrue("Object.isExtensible(other)" ); |
| 10425 | |
| 10426 | CompileRun("Object.seal(other)" ); |
| 10427 | ExpectTrue("Object.isExtensible(other)" ); |
| 10428 | |
| 10429 | // Regression test for issue 1250. |
| 10430 | // Make sure that we can set the accessible accessors value using normal |
| 10431 | // assignment. |
| 10432 | CompileRun("other.accessible_prop = 42" ); |
| 10433 | CHECK_EQ(42, g_echo_value); |
| 10434 | |
| 10435 | // [[DefineOwnProperty]] always throws for access-checked objects. |
| 10436 | CHECK( |
| 10437 | CompileRun("Object.defineProperty(other, 'accessible_prop', {value: 43})" ) |
| 10438 | .IsEmpty()); |
| 10439 | CHECK(CompileRun("other.accessible_prop == 42" )->IsTrue()); |
| 10440 | CHECK_EQ(42, g_echo_value); // Make sure we didn't call the setter. |
| 10441 | } |
| 10442 | |
| 10443 | static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context, |
| 10444 | Local<v8::Object> global, |
| 10445 | Local<v8::Value> data) { |
| 10446 | i::PrintF("Access blocked.\n" ); |
| 10447 | return false; |
| 10448 | } |
| 10449 | |
| 10450 | static bool AccessAlwaysAllowed(Local<v8::Context> accessing_context, |
| 10451 | Local<v8::Object> global, |
| 10452 | Local<v8::Value> data) { |
| 10453 | i::PrintF("Access allowed.\n" ); |
| 10454 | return true; |
| 10455 | } |
| 10456 | |
| 10457 | THREADED_TEST(AccessControlGetOwnPropertyNames) { |
| 10458 | v8::Isolate* isolate = CcTest::isolate(); |
| 10459 | v8::HandleScope handle_scope(isolate); |
| 10460 | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); |
| 10461 | |
| 10462 | obj_template->Set(v8_str("x" ), v8::Integer::New(isolate, 42)); |
| 10463 | obj_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 10464 | |
| 10465 | // Add an accessor accessible by cross-domain JS code. |
| 10466 | obj_template->SetAccessor( |
| 10467 | v8_str("accessible_prop" ), EchoGetter, EchoSetter, v8::Local<Value>(), |
| 10468 | v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); |
| 10469 | |
| 10470 | // Create an environment |
| 10471 | v8::Local<Context> context0 = Context::New(isolate, nullptr, obj_template); |
| 10472 | context0->Enter(); |
| 10473 | |
| 10474 | v8::Local<v8::Object> global0 = context0->Global(); |
| 10475 | |
| 10476 | v8::HandleScope scope1(CcTest::isolate()); |
| 10477 | |
| 10478 | v8::Local<Context> context1 = Context::New(isolate); |
| 10479 | context1->Enter(); |
| 10480 | |
| 10481 | v8::Local<v8::Object> global1 = context1->Global(); |
| 10482 | CHECK(global1->Set(context1, v8_str("other" ), global0).FromJust()); |
| 10483 | CHECK(global1->Set(context1, v8_str("object" ), |
| 10484 | obj_template->NewInstance(context1).ToLocalChecked()) |
| 10485 | .FromJust()); |
| 10486 | |
| 10487 | v8::Local<Value> value; |
| 10488 | |
| 10489 | // Attempt to get the property names of the other global object and |
| 10490 | // of an object that requires access checks. Accessing the other |
| 10491 | // global object should be blocked by access checks on the global |
| 10492 | // proxy object. Accessing the object that requires access checks |
| 10493 | // is blocked by the access checks on the object itself. |
| 10494 | value = CompileRun( |
| 10495 | "var names = Object.getOwnPropertyNames(other);" |
| 10496 | "names.length == 1 && names[0] == 'accessible_prop';" ); |
| 10497 | CHECK(value->BooleanValue(isolate)); |
| 10498 | |
| 10499 | value = CompileRun( |
| 10500 | "var names = Object.getOwnPropertyNames(object);" |
| 10501 | "names.length == 1 && names[0] == 'accessible_prop';" ); |
| 10502 | CHECK(value->BooleanValue(isolate)); |
| 10503 | |
| 10504 | context1->Exit(); |
| 10505 | context0->Exit(); |
| 10506 | } |
| 10507 | |
| 10508 | |
| 10509 | TEST(Regress470113) { |
| 10510 | v8::Isolate* isolate = CcTest::isolate(); |
| 10511 | v8::HandleScope handle_scope(isolate); |
| 10512 | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); |
| 10513 | obj_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 10514 | LocalContext env; |
| 10515 | CHECK(env->Global() |
| 10516 | ->Set(env.local(), v8_str("prohibited" ), |
| 10517 | obj_template->NewInstance(env.local()).ToLocalChecked()) |
| 10518 | .FromJust()); |
| 10519 | |
| 10520 | { |
| 10521 | v8::TryCatch try_catch(isolate); |
| 10522 | CompileRun( |
| 10523 | "'use strict';\n" |
| 10524 | "class C extends Object {\n" |
| 10525 | " m() { super.powned = 'Powned!'; }\n" |
| 10526 | "}\n" |
| 10527 | "let c = new C();\n" |
| 10528 | "c.m.call(prohibited)" ); |
| 10529 | |
| 10530 | CHECK(try_catch.HasCaught()); |
| 10531 | } |
| 10532 | } |
| 10533 | |
| 10534 | |
| 10535 | static void ConstTenGetter(Local<String> name, |
| 10536 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 10537 | info.GetReturnValue().Set(v8_num(10)); |
| 10538 | } |
| 10539 | |
| 10540 | |
| 10541 | THREADED_TEST(CrossDomainAccessors) { |
| 10542 | v8::Isolate* isolate = CcTest::isolate(); |
| 10543 | v8::HandleScope handle_scope(isolate); |
| 10544 | |
| 10545 | v8::Local<v8::FunctionTemplate> func_template = |
| 10546 | v8::FunctionTemplate::New(isolate); |
| 10547 | |
| 10548 | v8::Local<v8::ObjectTemplate> global_template = |
| 10549 | func_template->InstanceTemplate(); |
| 10550 | |
| 10551 | v8::Local<v8::ObjectTemplate> proto_template = |
| 10552 | func_template->PrototypeTemplate(); |
| 10553 | |
| 10554 | // Add an accessor to proto that's accessible by cross-domain JS code. |
| 10555 | proto_template->SetAccessor(v8_str("accessible" ), ConstTenGetter, nullptr, |
| 10556 | v8::Local<Value>(), v8::ALL_CAN_READ); |
| 10557 | |
| 10558 | // Add an accessor that is not accessible by cross-domain JS code. |
| 10559 | global_template->SetAccessor(v8_str("unreachable" ), UnreachableGetter, |
| 10560 | nullptr, v8::Local<Value>(), v8::DEFAULT); |
| 10561 | |
| 10562 | v8::Local<Context> context0 = Context::New(isolate, nullptr, global_template); |
| 10563 | context0->Enter(); |
| 10564 | |
| 10565 | Local<v8::Object> global = context0->Global(); |
| 10566 | // Add a normal property that shadows 'accessible' |
| 10567 | CHECK(global->Set(context0, v8_str("accessible" ), v8_num(11)).FromJust()); |
| 10568 | |
| 10569 | // Enter a new context. |
| 10570 | v8::HandleScope scope1(CcTest::isolate()); |
| 10571 | v8::Local<Context> context1 = Context::New(isolate); |
| 10572 | context1->Enter(); |
| 10573 | |
| 10574 | v8::Local<v8::Object> global1 = context1->Global(); |
| 10575 | CHECK(global1->Set(context1, v8_str("other" ), global).FromJust()); |
| 10576 | |
| 10577 | // Should return 10, instead of 11 |
| 10578 | v8::Local<Value> value = |
| 10579 | v8_compile("other.accessible" )->Run(context1).ToLocalChecked(); |
| 10580 | CHECK(value->IsNumber()); |
| 10581 | CHECK_EQ(10, value->Int32Value(context1).FromJust()); |
| 10582 | |
| 10583 | v8::MaybeLocal<v8::Value> maybe_value = |
| 10584 | v8_compile("other.unreachable" )->Run(context1); |
| 10585 | CHECK(maybe_value.IsEmpty()); |
| 10586 | |
| 10587 | context1->Exit(); |
| 10588 | context0->Exit(); |
| 10589 | } |
| 10590 | |
| 10591 | |
| 10592 | static int access_count = 0; |
| 10593 | |
| 10594 | static bool AccessCounter(Local<v8::Context> accessing_context, |
| 10595 | Local<v8::Object> accessed_object, |
| 10596 | Local<v8::Value> data) { |
| 10597 | access_count++; |
| 10598 | return true; |
| 10599 | } |
| 10600 | |
| 10601 | |
| 10602 | // This one is too easily disturbed by other tests. |
| 10603 | TEST(AccessControlIC) { |
| 10604 | access_count = 0; |
| 10605 | |
| 10606 | v8::Isolate* isolate = CcTest::isolate(); |
| 10607 | v8::HandleScope handle_scope(isolate); |
| 10608 | |
| 10609 | // Create an environment. |
| 10610 | v8::Local<Context> context0 = Context::New(isolate); |
| 10611 | context0->Enter(); |
| 10612 | |
| 10613 | // Create an object that requires access-check functions to be |
| 10614 | // called for cross-domain access. |
| 10615 | v8::Local<v8::ObjectTemplate> object_template = |
| 10616 | v8::ObjectTemplate::New(isolate); |
| 10617 | object_template->SetAccessCheckCallback(AccessCounter); |
| 10618 | Local<v8::Object> object = |
| 10619 | object_template->NewInstance(context0).ToLocalChecked(); |
| 10620 | |
| 10621 | v8::HandleScope scope1(isolate); |
| 10622 | |
| 10623 | // Create another environment. |
| 10624 | v8::Local<Context> context1 = Context::New(isolate); |
| 10625 | context1->Enter(); |
| 10626 | |
| 10627 | // Make easy access to the object from the other environment. |
| 10628 | v8::Local<v8::Object> global1 = context1->Global(); |
| 10629 | CHECK(global1->Set(context1, v8_str("obj" ), object).FromJust()); |
| 10630 | |
| 10631 | v8::Local<Value> value; |
| 10632 | |
| 10633 | // Check that the named access-control function is called every time. |
| 10634 | CompileRun("function testProp(obj) {" |
| 10635 | " for (var i = 0; i < 10; i++) obj.prop = 1;" |
| 10636 | " for (var j = 0; j < 10; j++) obj.prop;" |
| 10637 | " return obj.prop" |
| 10638 | "}" ); |
| 10639 | value = CompileRun("testProp(obj)" ); |
| 10640 | CHECK(value->IsNumber()); |
| 10641 | CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| 10642 | CHECK_EQ(21, access_count); |
| 10643 | |
| 10644 | // Check that the named access-control function is called every time. |
| 10645 | CompileRun("var p = 'prop';" |
| 10646 | "function testKeyed(obj) {" |
| 10647 | " for (var i = 0; i < 10; i++) obj[p] = 1;" |
| 10648 | " for (var j = 0; j < 10; j++) obj[p];" |
| 10649 | " return obj[p];" |
| 10650 | "}" ); |
| 10651 | // Use obj which requires access checks. No inline caching is used |
| 10652 | // in that case. |
| 10653 | value = CompileRun("testKeyed(obj)" ); |
| 10654 | CHECK(value->IsNumber()); |
| 10655 | CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| 10656 | CHECK_EQ(42, access_count); |
| 10657 | // Force the inline caches into generic state and try again. |
| 10658 | CompileRun("testKeyed({ a: 0 })" ); |
| 10659 | CompileRun("testKeyed({ b: 0 })" ); |
| 10660 | value = CompileRun("testKeyed(obj)" ); |
| 10661 | CHECK(value->IsNumber()); |
| 10662 | CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| 10663 | CHECK_EQ(63, access_count); |
| 10664 | |
| 10665 | // Check that the indexed access-control function is called every time. |
| 10666 | access_count = 0; |
| 10667 | |
| 10668 | CompileRun("function testIndexed(obj) {" |
| 10669 | " for (var i = 0; i < 10; i++) obj[0] = 1;" |
| 10670 | " for (var j = 0; j < 10; j++) obj[0];" |
| 10671 | " return obj[0]" |
| 10672 | "}" ); |
| 10673 | value = CompileRun("testIndexed(obj)" ); |
| 10674 | CHECK(value->IsNumber()); |
| 10675 | CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| 10676 | CHECK_EQ(21, access_count); |
| 10677 | // Force the inline caches into generic state. |
| 10678 | CompileRun("testIndexed(new Array(1))" ); |
| 10679 | // Test that the indexed access check is called. |
| 10680 | value = CompileRun("testIndexed(obj)" ); |
| 10681 | CHECK(value->IsNumber()); |
| 10682 | CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| 10683 | CHECK_EQ(42, access_count); |
| 10684 | |
| 10685 | access_count = 0; |
| 10686 | // Check that the named access check is called when invoking |
| 10687 | // functions on an object that requires access checks. |
| 10688 | CompileRun("obj.f = function() {}" ); |
| 10689 | CompileRun("function testCallNormal(obj) {" |
| 10690 | " for (var i = 0; i < 10; i++) obj.f();" |
| 10691 | "}" ); |
| 10692 | CompileRun("testCallNormal(obj)" ); |
| 10693 | printf("%i\n" , access_count); |
| 10694 | CHECK_EQ(11, access_count); |
| 10695 | |
| 10696 | // Force obj into slow case. |
| 10697 | value = CompileRun("delete obj.prop" ); |
| 10698 | CHECK(value->BooleanValue(isolate)); |
| 10699 | // Force inline caches into dictionary probing mode. |
| 10700 | CompileRun("var o = { x: 0 }; delete o.x; testProp(o);" ); |
| 10701 | // Test that the named access check is called. |
| 10702 | value = CompileRun("testProp(obj);" ); |
| 10703 | CHECK(value->IsNumber()); |
| 10704 | CHECK_EQ(1, value->Int32Value(context1).FromJust()); |
| 10705 | CHECK_EQ(33, access_count); |
| 10706 | |
| 10707 | // Force the call inline cache into dictionary probing mode. |
| 10708 | CompileRun("o.f = function() {}; testCallNormal(o)" ); |
| 10709 | // Test that the named access check is still called for each |
| 10710 | // invocation of the function. |
| 10711 | value = CompileRun("testCallNormal(obj)" ); |
| 10712 | CHECK_EQ(43, access_count); |
| 10713 | |
| 10714 | context1->Exit(); |
| 10715 | context0->Exit(); |
| 10716 | } |
| 10717 | |
| 10718 | |
| 10719 | THREADED_TEST(Version) { v8::V8::GetVersion(); } |
| 10720 | |
| 10721 | |
| 10722 | static void InstanceFunctionCallback( |
| 10723 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 10724 | ApiTestFuzzer::Fuzz(); |
| 10725 | args.GetReturnValue().Set(v8_num(12)); |
| 10726 | } |
| 10727 | |
| 10728 | |
| 10729 | THREADED_TEST(InstanceProperties) { |
| 10730 | LocalContext context; |
| 10731 | v8::Isolate* isolate = context->GetIsolate(); |
| 10732 | v8::HandleScope handle_scope(isolate); |
| 10733 | |
| 10734 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 10735 | Local<ObjectTemplate> instance = t->InstanceTemplate(); |
| 10736 | |
| 10737 | instance->Set(v8_str("x" ), v8_num(42)); |
| 10738 | instance->Set(v8_str("f" ), |
| 10739 | v8::FunctionTemplate::New(isolate, InstanceFunctionCallback)); |
| 10740 | |
| 10741 | Local<Value> o = t->GetFunction(context.local()) |
| 10742 | .ToLocalChecked() |
| 10743 | ->NewInstance(context.local()) |
| 10744 | .ToLocalChecked(); |
| 10745 | |
| 10746 | CHECK(context->Global()->Set(context.local(), v8_str("i" ), o).FromJust()); |
| 10747 | Local<Value> value = CompileRun("i.x" ); |
| 10748 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 10749 | |
| 10750 | value = CompileRun("i.f()" ); |
| 10751 | CHECK_EQ(12, value->Int32Value(context.local()).FromJust()); |
| 10752 | } |
| 10753 | |
| 10754 | |
| 10755 | static void GlobalObjectInstancePropertiesGet( |
| 10756 | Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) { |
| 10757 | ApiTestFuzzer::Fuzz(); |
| 10758 | } |
| 10759 | |
| 10760 | |
| 10761 | THREADED_TEST(GlobalObjectInstanceProperties) { |
| 10762 | v8::Isolate* isolate = CcTest::isolate(); |
| 10763 | v8::HandleScope handle_scope(isolate); |
| 10764 | |
| 10765 | Local<Value> global_object; |
| 10766 | |
| 10767 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 10768 | t->InstanceTemplate()->SetHandler( |
| 10769 | v8::NamedPropertyHandlerConfiguration(GlobalObjectInstancePropertiesGet)); |
| 10770 | Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| 10771 | instance_template->Set(v8_str("x" ), v8_num(42)); |
| 10772 | instance_template->Set(v8_str("f" ), |
| 10773 | v8::FunctionTemplate::New(isolate, |
| 10774 | InstanceFunctionCallback)); |
| 10775 | |
| 10776 | // The script to check how TurboFan compiles missing global function |
| 10777 | // invocations. function g is not defined and should throw on call. |
| 10778 | const char* script = |
| 10779 | "function wrapper(call) {" |
| 10780 | " var x = 0, y = 1;" |
| 10781 | " for (var i = 0; i < 1000; i++) {" |
| 10782 | " x += i * 100;" |
| 10783 | " y += i * 100;" |
| 10784 | " }" |
| 10785 | " if (call) g();" |
| 10786 | "}" |
| 10787 | "for (var i = 0; i < 17; i++) wrapper(false);" |
| 10788 | "var thrown = 0;" |
| 10789 | "try { wrapper(true); } catch (e) { thrown = 1; };" |
| 10790 | "thrown" ; |
| 10791 | |
| 10792 | { |
| 10793 | LocalContext env(nullptr, instance_template); |
| 10794 | // Hold on to the global object so it can be used again in another |
| 10795 | // environment initialization. |
| 10796 | global_object = env->Global(); |
| 10797 | |
| 10798 | Local<Value> value = CompileRun("x" ); |
| 10799 | CHECK_EQ(42, value->Int32Value(env.local()).FromJust()); |
| 10800 | value = CompileRun("f()" ); |
| 10801 | CHECK_EQ(12, value->Int32Value(env.local()).FromJust()); |
| 10802 | value = CompileRun(script); |
| 10803 | CHECK_EQ(1, value->Int32Value(env.local()).FromJust()); |
| 10804 | } |
| 10805 | |
| 10806 | { |
| 10807 | // Create new environment reusing the global object. |
| 10808 | LocalContext env(nullptr, instance_template, global_object); |
| 10809 | Local<Value> value = CompileRun("x" ); |
| 10810 | CHECK_EQ(42, value->Int32Value(env.local()).FromJust()); |
| 10811 | value = CompileRun("f()" ); |
| 10812 | CHECK_EQ(12, value->Int32Value(env.local()).FromJust()); |
| 10813 | value = CompileRun(script); |
| 10814 | CHECK_EQ(1, value->Int32Value(env.local()).FromJust()); |
| 10815 | } |
| 10816 | } |
| 10817 | |
| 10818 | THREADED_TEST(ObjectGetOwnPropertyNames) { |
| 10819 | LocalContext context; |
| 10820 | v8::Isolate* isolate = context->GetIsolate(); |
| 10821 | v8::HandleScope handle_scope(isolate); |
| 10822 | |
| 10823 | v8::Local<v8::Object> value = v8::Local<v8::Object>::Cast( |
| 10824 | v8::StringObject::New(CcTest::isolate(), v8_str("test" ))); |
| 10825 | v8::Local<v8::Array> properties; |
| 10826 | |
| 10827 | CHECK(value |
| 10828 | ->GetOwnPropertyNames(context.local(), |
| 10829 | static_cast<v8::PropertyFilter>( |
| 10830 | v8::PropertyFilter::ALL_PROPERTIES | |
| 10831 | v8::PropertyFilter::SKIP_SYMBOLS), |
| 10832 | v8::KeyConversionMode::kKeepNumbers) |
| 10833 | .ToLocal(&properties)); |
| 10834 | CHECK_EQ(5u, properties->Length()); |
| 10835 | v8::Local<v8::Value> property; |
| 10836 | CHECK(properties->Get(context.local(), 4).ToLocal(&property) && |
| 10837 | property->IsString()); |
| 10838 | CHECK(property.As<v8::String>() |
| 10839 | ->Equals(context.local(), v8_str("length" )) |
| 10840 | .FromMaybe(false)); |
| 10841 | for (int i = 0; i < 4; ++i) { |
| 10842 | v8::Local<v8::Value> property; |
| 10843 | CHECK(properties->Get(context.local(), i).ToLocal(&property) && |
| 10844 | property->IsInt32()); |
| 10845 | CHECK_EQ(property.As<v8::Int32>()->Value(), i); |
| 10846 | } |
| 10847 | |
| 10848 | CHECK(value |
| 10849 | ->GetOwnPropertyNames(context.local(), |
| 10850 | v8::PropertyFilter::ONLY_ENUMERABLE, |
| 10851 | v8::KeyConversionMode::kKeepNumbers) |
| 10852 | .ToLocal(&properties)); |
| 10853 | v8::Local<v8::Array> number_properties; |
| 10854 | CHECK(value |
| 10855 | ->GetOwnPropertyNames(context.local(), |
| 10856 | v8::PropertyFilter::ONLY_ENUMERABLE, |
| 10857 | v8::KeyConversionMode::kConvertToString) |
| 10858 | .ToLocal(&number_properties)); |
| 10859 | CHECK_EQ(4u, properties->Length()); |
| 10860 | for (int i = 0; i < 4; ++i) { |
| 10861 | v8::Local<v8::Value> property_index; |
| 10862 | v8::Local<v8::Value> property_name; |
| 10863 | |
| 10864 | CHECK(number_properties->Get(context.local(), i).ToLocal(&property_name)); |
| 10865 | CHECK(property_name->IsString()); |
| 10866 | |
| 10867 | CHECK(properties->Get(context.local(), i).ToLocal(&property_index)); |
| 10868 | CHECK(property_index->IsInt32()); |
| 10869 | |
| 10870 | CHECK_EQ(property_index.As<v8::Int32>()->Value(), i); |
| 10871 | CHECK_EQ(property_name->ToNumber(context.local()) |
| 10872 | .ToLocalChecked() |
| 10873 | .As<v8::Int32>() |
| 10874 | ->Value(), |
| 10875 | i); |
| 10876 | } |
| 10877 | |
| 10878 | value = value->GetPrototype().As<v8::Object>(); |
| 10879 | CHECK(value |
| 10880 | ->GetOwnPropertyNames(context.local(), |
| 10881 | static_cast<v8::PropertyFilter>( |
| 10882 | v8::PropertyFilter::ALL_PROPERTIES | |
| 10883 | v8::PropertyFilter::SKIP_SYMBOLS)) |
| 10884 | .ToLocal(&properties)); |
| 10885 | bool concat_found = false; |
| 10886 | bool starts_with_found = false; |
| 10887 | for (uint32_t i = 0; i < properties->Length(); ++i) { |
| 10888 | v8::Local<v8::Value> property; |
| 10889 | CHECK(properties->Get(context.local(), i).ToLocal(&property)); |
| 10890 | if (!property->IsString()) continue; |
| 10891 | if (!concat_found) |
| 10892 | concat_found = property.As<v8::String>() |
| 10893 | ->Equals(context.local(), v8_str("concat" )) |
| 10894 | .FromMaybe(false); |
| 10895 | if (!starts_with_found) |
| 10896 | starts_with_found = property.As<v8::String>() |
| 10897 | ->Equals(context.local(), v8_str("startsWith" )) |
| 10898 | .FromMaybe(false); |
| 10899 | } |
| 10900 | CHECK(concat_found && starts_with_found); |
| 10901 | } |
| 10902 | |
| 10903 | THREADED_TEST(CallKnownGlobalReceiver) { |
| 10904 | v8::Isolate* isolate = CcTest::isolate(); |
| 10905 | v8::HandleScope handle_scope(isolate); |
| 10906 | |
| 10907 | Local<Value> global_object; |
| 10908 | |
| 10909 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 10910 | Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| 10911 | |
| 10912 | // The script to check that we leave global object not |
| 10913 | // global object proxy on stack when we deoptimize from inside |
| 10914 | // arguments evaluation. |
| 10915 | // To provoke error we need to both force deoptimization |
| 10916 | // from arguments evaluation and to force CallIC to take |
| 10917 | // CallIC_Miss code path that can't cope with global proxy. |
| 10918 | const char* script = |
| 10919 | "function bar(x, y) { try { } finally { } }" |
| 10920 | "function baz(x) { try { } finally { } }" |
| 10921 | "function bom(x) { try { } finally { } }" |
| 10922 | "function foo(x) { bar([x], bom(2)); }" |
| 10923 | "for (var i = 0; i < 10000; i++) foo(1);" |
| 10924 | "foo" ; |
| 10925 | |
| 10926 | Local<Value> foo; |
| 10927 | { |
| 10928 | LocalContext env(nullptr, instance_template); |
| 10929 | // Hold on to the global object so it can be used again in another |
| 10930 | // environment initialization. |
| 10931 | global_object = env->Global(); |
| 10932 | foo = CompileRun(script); |
| 10933 | } |
| 10934 | |
| 10935 | { |
| 10936 | // Create new environment reusing the global object. |
| 10937 | LocalContext env(nullptr, instance_template, global_object); |
| 10938 | CHECK(env->Global()->Set(env.local(), v8_str("foo" ), foo).FromJust()); |
| 10939 | CompileRun("foo()" ); |
| 10940 | } |
| 10941 | } |
| 10942 | |
| 10943 | |
| 10944 | static void ShadowFunctionCallback( |
| 10945 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 10946 | ApiTestFuzzer::Fuzz(); |
| 10947 | args.GetReturnValue().Set(v8_num(42)); |
| 10948 | } |
| 10949 | |
| 10950 | |
| 10951 | static int shadow_y; |
| 10952 | static int shadow_y_setter_call_count; |
| 10953 | static int shadow_y_getter_call_count; |
| 10954 | |
| 10955 | |
| 10956 | static void ShadowYSetter(Local<String>, |
| 10957 | Local<Value>, |
| 10958 | const v8::PropertyCallbackInfo<void>&) { |
| 10959 | shadow_y_setter_call_count++; |
| 10960 | shadow_y = 42; |
| 10961 | } |
| 10962 | |
| 10963 | |
| 10964 | static void ShadowYGetter(Local<String> name, |
| 10965 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 10966 | ApiTestFuzzer::Fuzz(); |
| 10967 | shadow_y_getter_call_count++; |
| 10968 | info.GetReturnValue().Set(v8_num(shadow_y)); |
| 10969 | } |
| 10970 | |
| 10971 | |
| 10972 | static void ShadowIndexedGet(uint32_t index, |
| 10973 | const v8::PropertyCallbackInfo<v8::Value>&) { |
| 10974 | } |
| 10975 | |
| 10976 | |
| 10977 | static void ShadowNamedGet(Local<Name> key, |
| 10978 | const v8::PropertyCallbackInfo<v8::Value>&) {} |
| 10979 | |
| 10980 | THREADED_TEST(ShadowObject) { |
| 10981 | shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; |
| 10982 | v8::Isolate* isolate = CcTest::isolate(); |
| 10983 | v8::HandleScope handle_scope(isolate); |
| 10984 | |
| 10985 | Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| 10986 | LocalContext context(nullptr, global_template); |
| 10987 | |
| 10988 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 10989 | t->InstanceTemplate()->SetHandler( |
| 10990 | v8::NamedPropertyHandlerConfiguration(ShadowNamedGet)); |
| 10991 | t->InstanceTemplate()->SetHandler( |
| 10992 | v8::IndexedPropertyHandlerConfiguration(ShadowIndexedGet)); |
| 10993 | Local<ObjectTemplate> proto = t->PrototypeTemplate(); |
| 10994 | Local<ObjectTemplate> instance = t->InstanceTemplate(); |
| 10995 | |
| 10996 | proto->Set(v8_str("f" ), |
| 10997 | v8::FunctionTemplate::New(isolate, |
| 10998 | ShadowFunctionCallback, |
| 10999 | Local<Value>())); |
| 11000 | proto->Set(v8_str("x" ), v8_num(12)); |
| 11001 | |
| 11002 | instance->SetAccessor(v8_str("y" ), ShadowYGetter, ShadowYSetter); |
| 11003 | |
| 11004 | Local<Value> o = t->GetFunction(context.local()) |
| 11005 | .ToLocalChecked() |
| 11006 | ->NewInstance(context.local()) |
| 11007 | .ToLocalChecked(); |
| 11008 | CHECK(context->Global() |
| 11009 | ->Set(context.local(), v8_str("__proto__" ), o) |
| 11010 | .FromJust()); |
| 11011 | |
| 11012 | Local<Value> value = |
| 11013 | CompileRun("this.propertyIsEnumerable(0)" ); |
| 11014 | CHECK(value->IsBoolean()); |
| 11015 | CHECK(!value->BooleanValue(isolate)); |
| 11016 | |
| 11017 | value = CompileRun("x" ); |
| 11018 | CHECK_EQ(12, value->Int32Value(context.local()).FromJust()); |
| 11019 | |
| 11020 | value = CompileRun("f()" ); |
| 11021 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 11022 | |
| 11023 | CompileRun("y = 43" ); |
| 11024 | CHECK_EQ(1, shadow_y_setter_call_count); |
| 11025 | value = CompileRun("y" ); |
| 11026 | CHECK_EQ(1, shadow_y_getter_call_count); |
| 11027 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 11028 | } |
| 11029 | |
| 11030 | THREADED_TEST(ShadowObjectAndDataProperty) { |
| 11031 | // Lite mode doesn't make use of feedback vectors, which is what we |
| 11032 | // want to ensure has the correct form. |
| 11033 | if (i::FLAG_lite_mode) return; |
| 11034 | // This test mimics the kind of shadow property the Chromium embedder |
| 11035 | // uses for undeclared globals. The IC subsystem has special handling |
| 11036 | // for this case, using a PREMONOMORPHIC state to delay entering |
| 11037 | // MONOMORPHIC state until enough information is available to support |
| 11038 | // efficient access and good feedback for optimization. |
| 11039 | v8::Isolate* isolate = CcTest::isolate(); |
| 11040 | v8::HandleScope handle_scope(isolate); |
| 11041 | |
| 11042 | Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| 11043 | LocalContext context(nullptr, global_template); |
| 11044 | |
| 11045 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11046 | t->InstanceTemplate()->SetHandler( |
| 11047 | v8::NamedPropertyHandlerConfiguration(ShadowNamedGet)); |
| 11048 | |
| 11049 | Local<Value> o = t->GetFunction(context.local()) |
| 11050 | .ToLocalChecked() |
| 11051 | ->NewInstance(context.local()) |
| 11052 | .ToLocalChecked(); |
| 11053 | CHECK(context->Global() |
| 11054 | ->Set(context.local(), v8_str("__proto__" ), o) |
| 11055 | .FromJust()); |
| 11056 | |
| 11057 | CompileRun( |
| 11058 | "function foo(x) { i = x; }" |
| 11059 | "foo(0)" ); |
| 11060 | |
| 11061 | i::Handle<i::JSFunction> foo(i::Handle<i::JSFunction>::cast( |
| 11062 | v8::Utils::OpenHandle(*context->Global() |
| 11063 | ->Get(context.local(), v8_str("foo" )) |
| 11064 | .ToLocalChecked()))); |
| 11065 | CHECK(foo->has_feedback_vector()); |
| 11066 | i::FeedbackSlot slot = i::FeedbackVector::ToSlot(0); |
| 11067 | i::FeedbackNexus nexus(foo->feedback_vector(), slot); |
| 11068 | CHECK_EQ(i::FeedbackSlotKind::kStoreGlobalSloppy, nexus.kind()); |
| 11069 | CHECK_EQ(i::PREMONOMORPHIC, nexus.ic_state()); |
| 11070 | CompileRun("foo(1)" ); |
| 11071 | CHECK_EQ(i::MONOMORPHIC, nexus.ic_state()); |
| 11072 | // We go a bit further, checking that the form of monomorphism is |
| 11073 | // a PropertyCell in the vector. This is because we want to make sure |
| 11074 | // we didn't settle for a "poor man's monomorphism," such as a |
| 11075 | // slow_stub bailout which would mean a trip to the runtime on all |
| 11076 | // subsequent stores, and a lack of feedback for the optimizing |
| 11077 | // compiler downstream. |
| 11078 | i::HeapObject heap_object; |
| 11079 | CHECK(nexus.GetFeedback().GetHeapObject(&heap_object)); |
| 11080 | CHECK(heap_object->IsPropertyCell()); |
| 11081 | } |
| 11082 | |
| 11083 | THREADED_TEST(ShadowObjectAndDataPropertyTurbo) { |
| 11084 | // This test is the same as the previous one except that it triggers |
| 11085 | // optimization of {foo} after its first invocation. |
| 11086 | i::FLAG_allow_natives_syntax = true; |
| 11087 | |
| 11088 | if (i::FLAG_lite_mode) return; |
| 11089 | v8::Isolate* isolate = CcTest::isolate(); |
| 11090 | v8::HandleScope handle_scope(isolate); |
| 11091 | |
| 11092 | Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| 11093 | LocalContext context(nullptr, global_template); |
| 11094 | |
| 11095 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11096 | t->InstanceTemplate()->SetHandler( |
| 11097 | v8::NamedPropertyHandlerConfiguration(ShadowNamedGet)); |
| 11098 | |
| 11099 | Local<Value> o = t->GetFunction(context.local()) |
| 11100 | .ToLocalChecked() |
| 11101 | ->NewInstance(context.local()) |
| 11102 | .ToLocalChecked(); |
| 11103 | CHECK(context->Global() |
| 11104 | ->Set(context.local(), v8_str("__proto__" ), o) |
| 11105 | .FromJust()); |
| 11106 | |
| 11107 | CompileRun( |
| 11108 | "function foo(x) { i = x; }" |
| 11109 | "foo(0)" ); |
| 11110 | |
| 11111 | i::Handle<i::JSFunction> foo(i::Handle<i::JSFunction>::cast( |
| 11112 | v8::Utils::OpenHandle(*context->Global() |
| 11113 | ->Get(context.local(), v8_str("foo" )) |
| 11114 | .ToLocalChecked()))); |
| 11115 | CHECK(foo->has_feedback_vector()); |
| 11116 | i::FeedbackSlot slot = i::FeedbackVector::ToSlot(0); |
| 11117 | i::FeedbackNexus nexus(foo->feedback_vector(), slot); |
| 11118 | CHECK_EQ(i::FeedbackSlotKind::kStoreGlobalSloppy, nexus.kind()); |
| 11119 | CHECK_EQ(i::PREMONOMORPHIC, nexus.ic_state()); |
| 11120 | CompileRun("%OptimizeFunctionOnNextCall(foo); foo(1)" ); |
| 11121 | CHECK_EQ(i::MONOMORPHIC, nexus.ic_state()); |
| 11122 | i::HeapObject heap_object; |
| 11123 | CHECK(nexus.GetFeedback().GetHeapObject(&heap_object)); |
| 11124 | CHECK(heap_object->IsPropertyCell()); |
| 11125 | } |
| 11126 | |
| 11127 | THREADED_TEST(SetPrototype) { |
| 11128 | LocalContext context; |
| 11129 | v8::Isolate* isolate = context->GetIsolate(); |
| 11130 | v8::HandleScope handle_scope(isolate); |
| 11131 | |
| 11132 | Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(isolate); |
| 11133 | t0->InstanceTemplate()->Set(v8_str("x" ), v8_num(0)); |
| 11134 | Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| 11135 | t1->InstanceTemplate()->Set(v8_str("y" ), v8_num(1)); |
| 11136 | Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| 11137 | t2->InstanceTemplate()->Set(v8_str("z" ), v8_num(2)); |
| 11138 | Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| 11139 | t3->InstanceTemplate()->Set(v8_str("u" ), v8_num(3)); |
| 11140 | |
| 11141 | Local<v8::Object> o0 = t0->GetFunction(context.local()) |
| 11142 | .ToLocalChecked() |
| 11143 | ->NewInstance(context.local()) |
| 11144 | .ToLocalChecked(); |
| 11145 | Local<v8::Object> o1 = t1->GetFunction(context.local()) |
| 11146 | .ToLocalChecked() |
| 11147 | ->NewInstance(context.local()) |
| 11148 | .ToLocalChecked(); |
| 11149 | Local<v8::Object> o2 = t2->GetFunction(context.local()) |
| 11150 | .ToLocalChecked() |
| 11151 | ->NewInstance(context.local()) |
| 11152 | .ToLocalChecked(); |
| 11153 | Local<v8::Object> o3 = t3->GetFunction(context.local()) |
| 11154 | .ToLocalChecked() |
| 11155 | ->NewInstance(context.local()) |
| 11156 | .ToLocalChecked(); |
| 11157 | |
| 11158 | CHECK_EQ(0, o0->Get(context.local(), v8_str("x" )) |
| 11159 | .ToLocalChecked() |
| 11160 | ->Int32Value(context.local()) |
| 11161 | .FromJust()); |
| 11162 | CHECK(o0->SetPrototype(context.local(), o1).FromJust()); |
| 11163 | CHECK_EQ(0, o0->Get(context.local(), v8_str("x" )) |
| 11164 | .ToLocalChecked() |
| 11165 | ->Int32Value(context.local()) |
| 11166 | .FromJust()); |
| 11167 | CHECK_EQ(1, o0->Get(context.local(), v8_str("y" )) |
| 11168 | .ToLocalChecked() |
| 11169 | ->Int32Value(context.local()) |
| 11170 | .FromJust()); |
| 11171 | CHECK(o1->SetPrototype(context.local(), o2).FromJust()); |
| 11172 | CHECK_EQ(0, o0->Get(context.local(), v8_str("x" )) |
| 11173 | .ToLocalChecked() |
| 11174 | ->Int32Value(context.local()) |
| 11175 | .FromJust()); |
| 11176 | CHECK_EQ(1, o0->Get(context.local(), v8_str("y" )) |
| 11177 | .ToLocalChecked() |
| 11178 | ->Int32Value(context.local()) |
| 11179 | .FromJust()); |
| 11180 | CHECK_EQ(2, o0->Get(context.local(), v8_str("z" )) |
| 11181 | .ToLocalChecked() |
| 11182 | ->Int32Value(context.local()) |
| 11183 | .FromJust()); |
| 11184 | CHECK(o2->SetPrototype(context.local(), o3).FromJust()); |
| 11185 | CHECK_EQ(0, o0->Get(context.local(), v8_str("x" )) |
| 11186 | .ToLocalChecked() |
| 11187 | ->Int32Value(context.local()) |
| 11188 | .FromJust()); |
| 11189 | CHECK_EQ(1, o0->Get(context.local(), v8_str("y" )) |
| 11190 | .ToLocalChecked() |
| 11191 | ->Int32Value(context.local()) |
| 11192 | .FromJust()); |
| 11193 | CHECK_EQ(2, o0->Get(context.local(), v8_str("z" )) |
| 11194 | .ToLocalChecked() |
| 11195 | ->Int32Value(context.local()) |
| 11196 | .FromJust()); |
| 11197 | CHECK_EQ(3, o0->Get(context.local(), v8_str("u" )) |
| 11198 | .ToLocalChecked() |
| 11199 | ->Int32Value(context.local()) |
| 11200 | .FromJust()); |
| 11201 | |
| 11202 | Local<Value> proto = |
| 11203 | o0->Get(context.local(), v8_str("__proto__" )).ToLocalChecked(); |
| 11204 | CHECK(proto->IsObject()); |
| 11205 | CHECK(proto.As<v8::Object>()->Equals(context.local(), o1).FromJust()); |
| 11206 | |
| 11207 | Local<Value> proto0 = o0->GetPrototype(); |
| 11208 | CHECK(proto0->IsObject()); |
| 11209 | CHECK(proto0.As<v8::Object>()->Equals(context.local(), o1).FromJust()); |
| 11210 | |
| 11211 | Local<Value> proto1 = o1->GetPrototype(); |
| 11212 | CHECK(proto1->IsObject()); |
| 11213 | CHECK(proto1.As<v8::Object>()->Equals(context.local(), o2).FromJust()); |
| 11214 | |
| 11215 | Local<Value> proto2 = o2->GetPrototype(); |
| 11216 | CHECK(proto2->IsObject()); |
| 11217 | CHECK(proto2.As<v8::Object>()->Equals(context.local(), o3).FromJust()); |
| 11218 | } |
| 11219 | |
| 11220 | |
| 11221 | // Getting property names of an object with a prototype chain that |
| 11222 | // triggers dictionary elements in GetOwnPropertyNames() shouldn't |
| 11223 | // crash the runtime. |
| 11224 | THREADED_TEST(Regress91517) { |
| 11225 | i::FLAG_allow_natives_syntax = true; |
| 11226 | LocalContext context; |
| 11227 | v8::Isolate* isolate = context->GetIsolate(); |
| 11228 | v8::HandleScope handle_scope(isolate); |
| 11229 | |
| 11230 | Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| 11231 | t1->InstanceTemplate()->Set(v8_str("foo" ), v8_num(1)); |
| 11232 | Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| 11233 | t2->InstanceTemplate()->Set(v8_str("fuz1" ), v8_num(2)); |
| 11234 | t2->InstanceTemplate()->Set(v8_str("objects" ), |
| 11235 | v8::ObjectTemplate::New(isolate)); |
| 11236 | t2->InstanceTemplate()->Set(v8_str("fuz2" ), v8_num(2)); |
| 11237 | Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(isolate); |
| 11238 | t3->InstanceTemplate()->Set(v8_str("boo" ), v8_num(3)); |
| 11239 | Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(isolate); |
| 11240 | t4->InstanceTemplate()->Set(v8_str("baz" ), v8_num(4)); |
| 11241 | |
| 11242 | // Force dictionary-based properties. |
| 11243 | i::ScopedVector<char> name_buf(1024); |
| 11244 | for (int i = 1; i <= 1000; i++) { |
| 11245 | i::SNPrintF(name_buf, "sdf%d" , i); |
| 11246 | t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); |
| 11247 | } |
| 11248 | |
| 11249 | Local<v8::Object> o1 = t1->GetFunction(context.local()) |
| 11250 | .ToLocalChecked() |
| 11251 | ->NewInstance(context.local()) |
| 11252 | .ToLocalChecked(); |
| 11253 | Local<v8::Object> o2 = t2->GetFunction(context.local()) |
| 11254 | .ToLocalChecked() |
| 11255 | ->NewInstance(context.local()) |
| 11256 | .ToLocalChecked(); |
| 11257 | Local<v8::Object> o3 = t3->GetFunction(context.local()) |
| 11258 | .ToLocalChecked() |
| 11259 | ->NewInstance(context.local()) |
| 11260 | .ToLocalChecked(); |
| 11261 | Local<v8::Object> o4 = t4->GetFunction(context.local()) |
| 11262 | .ToLocalChecked() |
| 11263 | ->NewInstance(context.local()) |
| 11264 | .ToLocalChecked(); |
| 11265 | |
| 11266 | CHECK(o4->SetPrototype(context.local(), o3).FromJust()); |
| 11267 | CHECK(o3->SetPrototype(context.local(), o2).FromJust()); |
| 11268 | CHECK(o2->SetPrototype(context.local(), o1).FromJust()); |
| 11269 | |
| 11270 | // Call the runtime version of GetOwnPropertyNames() on the natively |
| 11271 | // created object through JavaScript. |
| 11272 | CHECK(context->Global()->Set(context.local(), v8_str("obj" ), o4).FromJust()); |
| 11273 | // PROPERTY_FILTER_NONE = 0 |
| 11274 | CompileRun("var names = %GetOwnPropertyKeys(obj, 0);" ); |
| 11275 | |
| 11276 | ExpectInt32("names.length" , 1); |
| 11277 | ExpectTrue("names.indexOf(\"baz\") >= 0" ); |
| 11278 | ExpectFalse("names.indexOf(\"boo\") >= 0" ); |
| 11279 | ExpectFalse("names.indexOf(\"foo\") >= 0" ); |
| 11280 | ExpectFalse("names.indexOf(\"fuz1\") >= 0" ); |
| 11281 | ExpectFalse("names.indexOf(\"objects\") >= 0" ); |
| 11282 | ExpectFalse("names.indexOf(\"fuz2\") >= 0" ); |
| 11283 | ExpectTrue("names[1005] == undefined" ); |
| 11284 | } |
| 11285 | |
| 11286 | |
| 11287 | THREADED_TEST(FunctionReadOnlyPrototype) { |
| 11288 | LocalContext context; |
| 11289 | v8::Isolate* isolate = context->GetIsolate(); |
| 11290 | v8::HandleScope handle_scope(isolate); |
| 11291 | |
| 11292 | Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| 11293 | t1->PrototypeTemplate()->Set(v8_str("x" ), v8::Integer::New(isolate, 42)); |
| 11294 | t1->ReadOnlyPrototype(); |
| 11295 | CHECK(context->Global() |
| 11296 | ->Set(context.local(), v8_str("func1" ), |
| 11297 | t1->GetFunction(context.local()).ToLocalChecked()) |
| 11298 | .FromJust()); |
| 11299 | // Configured value of ReadOnly flag. |
| 11300 | CHECK( |
| 11301 | CompileRun( |
| 11302 | "(function() {" |
| 11303 | " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" |
| 11304 | " return (descriptor['writable'] == false);" |
| 11305 | "})()" ) |
| 11306 | ->BooleanValue(isolate)); |
| 11307 | CHECK_EQ( |
| 11308 | 42, |
| 11309 | CompileRun("func1.prototype.x" )->Int32Value(context.local()).FromJust()); |
| 11310 | CHECK_EQ(42, CompileRun("func1.prototype = {}; func1.prototype.x" ) |
| 11311 | ->Int32Value(context.local()) |
| 11312 | .FromJust()); |
| 11313 | |
| 11314 | Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(isolate); |
| 11315 | t2->PrototypeTemplate()->Set(v8_str("x" ), v8::Integer::New(isolate, 42)); |
| 11316 | CHECK(context->Global() |
| 11317 | ->Set(context.local(), v8_str("func2" ), |
| 11318 | t2->GetFunction(context.local()).ToLocalChecked()) |
| 11319 | .FromJust()); |
| 11320 | // Default value of ReadOnly flag. |
| 11321 | CHECK( |
| 11322 | CompileRun( |
| 11323 | "(function() {" |
| 11324 | " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" |
| 11325 | " return (descriptor['writable'] == true);" |
| 11326 | "})()" ) |
| 11327 | ->BooleanValue(isolate)); |
| 11328 | CHECK_EQ( |
| 11329 | 42, |
| 11330 | CompileRun("func2.prototype.x" )->Int32Value(context.local()).FromJust()); |
| 11331 | } |
| 11332 | |
| 11333 | |
| 11334 | THREADED_TEST(SetPrototypeThrows) { |
| 11335 | LocalContext context; |
| 11336 | v8::Isolate* isolate = context->GetIsolate(); |
| 11337 | v8::HandleScope handle_scope(isolate); |
| 11338 | |
| 11339 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11340 | |
| 11341 | Local<v8::Object> o0 = t->GetFunction(context.local()) |
| 11342 | .ToLocalChecked() |
| 11343 | ->NewInstance(context.local()) |
| 11344 | .ToLocalChecked(); |
| 11345 | Local<v8::Object> o1 = t->GetFunction(context.local()) |
| 11346 | .ToLocalChecked() |
| 11347 | ->NewInstance(context.local()) |
| 11348 | .ToLocalChecked(); |
| 11349 | |
| 11350 | CHECK(o0->SetPrototype(context.local(), o1).FromJust()); |
| 11351 | // If setting the prototype leads to the cycle, SetPrototype should |
| 11352 | // return false and keep VM in sane state. |
| 11353 | v8::TryCatch try_catch(isolate); |
| 11354 | CHECK(o1->SetPrototype(context.local(), o0).IsNothing()); |
| 11355 | CHECK(!try_catch.HasCaught()); |
| 11356 | CHECK(!CcTest::i_isolate()->has_pending_exception()); |
| 11357 | |
| 11358 | CHECK_EQ(42, CompileRun("function f() { return 42; }; f()" ) |
| 11359 | ->Int32Value(context.local()) |
| 11360 | .FromJust()); |
| 11361 | } |
| 11362 | |
| 11363 | |
| 11364 | THREADED_TEST(FunctionRemovePrototype) { |
| 11365 | LocalContext context; |
| 11366 | v8::Isolate* isolate = context->GetIsolate(); |
| 11367 | v8::HandleScope handle_scope(isolate); |
| 11368 | |
| 11369 | Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(isolate); |
| 11370 | t1->RemovePrototype(); |
| 11371 | Local<v8::Function> fun = t1->GetFunction(context.local()).ToLocalChecked(); |
| 11372 | CHECK(!fun->IsConstructor()); |
| 11373 | CHECK(context->Global()->Set(context.local(), v8_str("fun" ), fun).FromJust()); |
| 11374 | CHECK(!CompileRun("'prototype' in fun" )->BooleanValue(isolate)); |
| 11375 | |
| 11376 | v8::TryCatch try_catch(isolate); |
| 11377 | CompileRun("new fun()" ); |
| 11378 | CHECK(try_catch.HasCaught()); |
| 11379 | |
| 11380 | try_catch.Reset(); |
| 11381 | CHECK(fun->NewInstance(context.local()).IsEmpty()); |
| 11382 | CHECK(try_catch.HasCaught()); |
| 11383 | } |
| 11384 | |
| 11385 | |
| 11386 | THREADED_TEST(GetterSetterExceptions) { |
| 11387 | LocalContext context; |
| 11388 | v8::Isolate* isolate = context->GetIsolate(); |
| 11389 | v8::HandleScope handle_scope(isolate); |
| 11390 | CompileRun( |
| 11391 | "function Foo() { };" |
| 11392 | "function Throw() { throw 5; };" |
| 11393 | "var x = { };" |
| 11394 | "x.__defineSetter__('set', Throw);" |
| 11395 | "x.__defineGetter__('get', Throw);" ); |
| 11396 | Local<v8::Object> x = Local<v8::Object>::Cast( |
| 11397 | context->Global()->Get(context.local(), v8_str("x" )).ToLocalChecked()); |
| 11398 | v8::TryCatch try_catch(isolate); |
| 11399 | CHECK(x->Set(context.local(), v8_str("set" ), v8::Integer::New(isolate, 8)) |
| 11400 | .IsNothing()); |
| 11401 | CHECK(x->Get(context.local(), v8_str("get" )).IsEmpty()); |
| 11402 | CHECK(x->Set(context.local(), v8_str("set" ), v8::Integer::New(isolate, 8)) |
| 11403 | .IsNothing()); |
| 11404 | CHECK(x->Get(context.local(), v8_str("get" )).IsEmpty()); |
| 11405 | CHECK(x->Set(context.local(), v8_str("set" ), v8::Integer::New(isolate, 8)) |
| 11406 | .IsNothing()); |
| 11407 | CHECK(x->Get(context.local(), v8_str("get" )).IsEmpty()); |
| 11408 | CHECK(x->Set(context.local(), v8_str("set" ), v8::Integer::New(isolate, 8)) |
| 11409 | .IsNothing()); |
| 11410 | CHECK(x->Get(context.local(), v8_str("get" )).IsEmpty()); |
| 11411 | } |
| 11412 | |
| 11413 | |
| 11414 | THREADED_TEST(Constructor) { |
| 11415 | LocalContext context; |
| 11416 | v8::Isolate* isolate = context->GetIsolate(); |
| 11417 | v8::HandleScope handle_scope(isolate); |
| 11418 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 11419 | templ->SetClassName(v8_str("Fun" )); |
| 11420 | Local<Function> cons = templ->GetFunction(context.local()).ToLocalChecked(); |
| 11421 | CHECK( |
| 11422 | context->Global()->Set(context.local(), v8_str("Fun" ), cons).FromJust()); |
| 11423 | Local<v8::Object> inst = cons->NewInstance(context.local()).ToLocalChecked(); |
| 11424 | i::Handle<i::JSReceiver> obj(v8::Utils::OpenHandle(*inst)); |
| 11425 | CHECK(obj->IsJSObject()); |
| 11426 | Local<Value> value = CompileRun("(new Fun()).constructor === Fun" ); |
| 11427 | CHECK(value->BooleanValue(isolate)); |
| 11428 | } |
| 11429 | |
| 11430 | |
| 11431 | THREADED_TEST(FunctionDescriptorException) { |
| 11432 | LocalContext context; |
| 11433 | v8::Isolate* isolate = context->GetIsolate(); |
| 11434 | v8::HandleScope handle_scope(isolate); |
| 11435 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 11436 | templ->SetClassName(v8_str("Fun" )); |
| 11437 | Local<Function> cons = templ->GetFunction(context.local()).ToLocalChecked(); |
| 11438 | CHECK( |
| 11439 | context->Global()->Set(context.local(), v8_str("Fun" ), cons).FromJust()); |
| 11440 | Local<Value> value = CompileRun( |
| 11441 | "function test() {" |
| 11442 | " try {" |
| 11443 | " (new Fun()).blah()" |
| 11444 | " } catch (e) {" |
| 11445 | " var str = String(e);" |
| 11446 | // " if (str.indexOf('TypeError') == -1) return 1;" |
| 11447 | // " if (str.indexOf('[object Fun]') != -1) return 2;" |
| 11448 | // " if (str.indexOf('#<Fun>') == -1) return 3;" |
| 11449 | " return 0;" |
| 11450 | " }" |
| 11451 | " return 4;" |
| 11452 | "}" |
| 11453 | "test();" ); |
| 11454 | CHECK_EQ(0, value->Int32Value(context.local()).FromJust()); |
| 11455 | } |
| 11456 | |
| 11457 | |
| 11458 | THREADED_TEST(EvalAliasedDynamic) { |
| 11459 | LocalContext current; |
| 11460 | v8::HandleScope scope(current->GetIsolate()); |
| 11461 | |
| 11462 | // Tests where aliased eval can only be resolved dynamically. |
| 11463 | Local<Script> script = v8_compile( |
| 11464 | "function f(x) { " |
| 11465 | " var foo = 2;" |
| 11466 | " with (x) { return eval('foo'); }" |
| 11467 | "}" |
| 11468 | "foo = 0;" |
| 11469 | "result1 = f(new Object());" |
| 11470 | "result2 = f(this);" |
| 11471 | "var x = new Object();" |
| 11472 | "x.eval = function(x) { return 1; };" |
| 11473 | "result3 = f(x);" ); |
| 11474 | script->Run(current.local()).ToLocalChecked(); |
| 11475 | CHECK_EQ(2, current->Global() |
| 11476 | ->Get(current.local(), v8_str("result1" )) |
| 11477 | .ToLocalChecked() |
| 11478 | ->Int32Value(current.local()) |
| 11479 | .FromJust()); |
| 11480 | CHECK_EQ(0, current->Global() |
| 11481 | ->Get(current.local(), v8_str("result2" )) |
| 11482 | .ToLocalChecked() |
| 11483 | ->Int32Value(current.local()) |
| 11484 | .FromJust()); |
| 11485 | CHECK_EQ(1, current->Global() |
| 11486 | ->Get(current.local(), v8_str("result3" )) |
| 11487 | .ToLocalChecked() |
| 11488 | ->Int32Value(current.local()) |
| 11489 | .FromJust()); |
| 11490 | |
| 11491 | v8::TryCatch try_catch(current->GetIsolate()); |
| 11492 | script = v8_compile( |
| 11493 | "function f(x) { " |
| 11494 | " var bar = 2;" |
| 11495 | " with (x) { return eval('bar'); }" |
| 11496 | "}" |
| 11497 | "result4 = f(this)" ); |
| 11498 | script->Run(current.local()).ToLocalChecked(); |
| 11499 | CHECK(!try_catch.HasCaught()); |
| 11500 | CHECK_EQ(2, current->Global() |
| 11501 | ->Get(current.local(), v8_str("result4" )) |
| 11502 | .ToLocalChecked() |
| 11503 | ->Int32Value(current.local()) |
| 11504 | .FromJust()); |
| 11505 | |
| 11506 | try_catch.Reset(); |
| 11507 | } |
| 11508 | |
| 11509 | |
| 11510 | THREADED_TEST(CrossEval) { |
| 11511 | v8::HandleScope scope(CcTest::isolate()); |
| 11512 | LocalContext other; |
| 11513 | LocalContext current; |
| 11514 | |
| 11515 | Local<String> token = v8_str("<security token>" ); |
| 11516 | other->SetSecurityToken(token); |
| 11517 | current->SetSecurityToken(token); |
| 11518 | |
| 11519 | // Set up reference from current to other. |
| 11520 | CHECK(current->Global() |
| 11521 | ->Set(current.local(), v8_str("other" ), other->Global()) |
| 11522 | .FromJust()); |
| 11523 | |
| 11524 | // Check that new variables are introduced in other context. |
| 11525 | Local<Script> script = v8_compile("other.eval('var foo = 1234')" ); |
| 11526 | script->Run(current.local()).ToLocalChecked(); |
| 11527 | Local<Value> foo = |
| 11528 | other->Global()->Get(current.local(), v8_str("foo" )).ToLocalChecked(); |
| 11529 | CHECK_EQ(1234, foo->Int32Value(other.local()).FromJust()); |
| 11530 | CHECK(!current->Global()->Has(current.local(), v8_str("foo" )).FromJust()); |
| 11531 | |
| 11532 | // Check that writing to non-existing properties introduces them in |
| 11533 | // the other context. |
| 11534 | script = v8_compile("other.eval('na = 1234')" ); |
| 11535 | script->Run(current.local()).ToLocalChecked(); |
| 11536 | CHECK_EQ(1234, other->Global() |
| 11537 | ->Get(current.local(), v8_str("na" )) |
| 11538 | .ToLocalChecked() |
| 11539 | ->Int32Value(other.local()) |
| 11540 | .FromJust()); |
| 11541 | CHECK(!current->Global()->Has(current.local(), v8_str("na" )).FromJust()); |
| 11542 | |
| 11543 | // Check that global variables in current context are not visible in other |
| 11544 | // context. |
| 11545 | v8::TryCatch try_catch(CcTest::isolate()); |
| 11546 | script = v8_compile("var bar = 42; other.eval('bar');" ); |
| 11547 | CHECK(script->Run(current.local()).IsEmpty()); |
| 11548 | CHECK(try_catch.HasCaught()); |
| 11549 | try_catch.Reset(); |
| 11550 | |
| 11551 | // Check that local variables in current context are not visible in other |
| 11552 | // context. |
| 11553 | script = v8_compile( |
| 11554 | "(function() { " |
| 11555 | " var baz = 87;" |
| 11556 | " return other.eval('baz');" |
| 11557 | "})();" ); |
| 11558 | CHECK(script->Run(current.local()).IsEmpty()); |
| 11559 | CHECK(try_catch.HasCaught()); |
| 11560 | try_catch.Reset(); |
| 11561 | |
| 11562 | // Check that global variables in the other environment are visible |
| 11563 | // when evaluting code. |
| 11564 | CHECK(other->Global() |
| 11565 | ->Set(other.local(), v8_str("bis" ), v8_num(1234)) |
| 11566 | .FromJust()); |
| 11567 | script = v8_compile("other.eval('bis')" ); |
| 11568 | CHECK_EQ(1234, script->Run(current.local()) |
| 11569 | .ToLocalChecked() |
| 11570 | ->Int32Value(current.local()) |
| 11571 | .FromJust()); |
| 11572 | CHECK(!try_catch.HasCaught()); |
| 11573 | |
| 11574 | // Check that the 'this' pointer points to the global object evaluating |
| 11575 | // code. |
| 11576 | CHECK(other->Global() |
| 11577 | ->Set(current.local(), v8_str("t" ), other->Global()) |
| 11578 | .FromJust()); |
| 11579 | script = v8_compile("other.eval('this == t')" ); |
| 11580 | Local<Value> result = script->Run(current.local()).ToLocalChecked(); |
| 11581 | CHECK(result->IsTrue()); |
| 11582 | CHECK(!try_catch.HasCaught()); |
| 11583 | |
| 11584 | // Check that variables introduced in with-statement are not visible in |
| 11585 | // other context. |
| 11586 | script = v8_compile("with({x:2}){other.eval('x')}" ); |
| 11587 | CHECK(script->Run(current.local()).IsEmpty()); |
| 11588 | CHECK(try_catch.HasCaught()); |
| 11589 | try_catch.Reset(); |
| 11590 | |
| 11591 | // Check that you cannot use 'eval.call' with another object than the |
| 11592 | // current global object. |
| 11593 | script = v8_compile("other.y = 1; eval.call(other, 'y')" ); |
| 11594 | CHECK(script->Run(current.local()).IsEmpty()); |
| 11595 | CHECK(try_catch.HasCaught()); |
| 11596 | } |
| 11597 | |
| 11598 | |
| 11599 | // Test that calling eval in a context which has been detached from |
| 11600 | // its global proxy works. |
| 11601 | THREADED_TEST(EvalInDetachedGlobal) { |
| 11602 | v8::Isolate* isolate = CcTest::isolate(); |
| 11603 | v8::HandleScope scope(isolate); |
| 11604 | |
| 11605 | v8::Local<Context> context0 = Context::New(isolate); |
| 11606 | v8::Local<Context> context1 = Context::New(isolate); |
| 11607 | Local<String> token = v8_str("<security token>" ); |
| 11608 | context0->SetSecurityToken(token); |
| 11609 | context1->SetSecurityToken(token); |
| 11610 | |
| 11611 | // Set up function in context0 that uses eval from context0. |
| 11612 | context0->Enter(); |
| 11613 | v8::Local<v8::Value> fun = CompileRun( |
| 11614 | "var x = 42;" |
| 11615 | "(function() {" |
| 11616 | " var e = eval;" |
| 11617 | " return function(s) { return e(s); }" |
| 11618 | "})()" ); |
| 11619 | context0->Exit(); |
| 11620 | |
| 11621 | // Put the function into context1 and call it before and after |
| 11622 | // detaching the global. Before detaching, the call succeeds and |
| 11623 | // after detaching undefined is returned. |
| 11624 | context1->Enter(); |
| 11625 | CHECK(context1->Global()->Set(context1, v8_str("fun" ), fun).FromJust()); |
| 11626 | v8::Local<v8::Value> x_value = CompileRun("fun('x')" ); |
| 11627 | CHECK_EQ(42, x_value->Int32Value(context1).FromJust()); |
| 11628 | context0->DetachGlobal(); |
| 11629 | x_value = CompileRun("fun('x')" ); |
| 11630 | CHECK(x_value->IsUndefined()); |
| 11631 | context1->Exit(); |
| 11632 | } |
| 11633 | |
| 11634 | |
| 11635 | THREADED_TEST(CrossLazyLoad) { |
| 11636 | v8::HandleScope scope(CcTest::isolate()); |
| 11637 | LocalContext other; |
| 11638 | LocalContext current; |
| 11639 | |
| 11640 | Local<String> token = v8_str("<security token>" ); |
| 11641 | other->SetSecurityToken(token); |
| 11642 | current->SetSecurityToken(token); |
| 11643 | |
| 11644 | // Set up reference from current to other. |
| 11645 | CHECK(current->Global() |
| 11646 | ->Set(current.local(), v8_str("other" ), other->Global()) |
| 11647 | .FromJust()); |
| 11648 | |
| 11649 | // Trigger lazy loading in other context. |
| 11650 | Local<Script> script = v8_compile("other.eval('new Date(42)')" ); |
| 11651 | Local<Value> value = script->Run(current.local()).ToLocalChecked(); |
| 11652 | CHECK_EQ(42.0, value->NumberValue(current.local()).FromJust()); |
| 11653 | } |
| 11654 | |
| 11655 | |
| 11656 | static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 11657 | ApiTestFuzzer::Fuzz(); |
| 11658 | if (args.IsConstructCall()) { |
| 11659 | if (args[0]->IsInt32()) { |
| 11660 | args.GetReturnValue().Set( |
| 11661 | v8_num(-args[0] |
| 11662 | ->Int32Value(args.GetIsolate()->GetCurrentContext()) |
| 11663 | .FromJust())); |
| 11664 | return; |
| 11665 | } |
| 11666 | } |
| 11667 | |
| 11668 | args.GetReturnValue().Set(args[0]); |
| 11669 | } |
| 11670 | |
| 11671 | |
| 11672 | // Test that a call handler can be set for objects which will allow |
| 11673 | // non-function objects created through the API to be called as |
| 11674 | // functions. |
| 11675 | THREADED_TEST(CallAsFunction) { |
| 11676 | LocalContext context; |
| 11677 | v8::Isolate* isolate = context->GetIsolate(); |
| 11678 | v8::HandleScope scope(isolate); |
| 11679 | |
| 11680 | { |
| 11681 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11682 | Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| 11683 | instance_template->SetCallAsFunctionHandler(call_as_function); |
| 11684 | Local<v8::Object> instance = t->GetFunction(context.local()) |
| 11685 | .ToLocalChecked() |
| 11686 | ->NewInstance(context.local()) |
| 11687 | .ToLocalChecked(); |
| 11688 | CHECK(context->Global() |
| 11689 | ->Set(context.local(), v8_str("obj" ), instance) |
| 11690 | .FromJust()); |
| 11691 | v8::TryCatch try_catch(isolate); |
| 11692 | Local<Value> value; |
| 11693 | CHECK(!try_catch.HasCaught()); |
| 11694 | |
| 11695 | value = CompileRun("obj(42)" ); |
| 11696 | CHECK(!try_catch.HasCaught()); |
| 11697 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 11698 | |
| 11699 | value = CompileRun("(function(o){return o(49)})(obj)" ); |
| 11700 | CHECK(!try_catch.HasCaught()); |
| 11701 | CHECK_EQ(49, value->Int32Value(context.local()).FromJust()); |
| 11702 | |
| 11703 | // test special case of call as function |
| 11704 | value = CompileRun("[obj]['0'](45)" ); |
| 11705 | CHECK(!try_catch.HasCaught()); |
| 11706 | CHECK_EQ(45, value->Int32Value(context.local()).FromJust()); |
| 11707 | |
| 11708 | value = CompileRun( |
| 11709 | "obj.call = Function.prototype.call;" |
| 11710 | "obj.call(null, 87)" ); |
| 11711 | CHECK(!try_catch.HasCaught()); |
| 11712 | CHECK_EQ(87, value->Int32Value(context.local()).FromJust()); |
| 11713 | |
| 11714 | // Regression tests for bug #1116356: Calling call through call/apply |
| 11715 | // must work for non-function receivers. |
| 11716 | const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])" ; |
| 11717 | value = CompileRun(apply_99); |
| 11718 | CHECK(!try_catch.HasCaught()); |
| 11719 | CHECK_EQ(99, value->Int32Value(context.local()).FromJust()); |
| 11720 | |
| 11721 | const char* call_17 = "Function.prototype.call.call(obj, this, 17)" ; |
| 11722 | value = CompileRun(call_17); |
| 11723 | CHECK(!try_catch.HasCaught()); |
| 11724 | CHECK_EQ(17, value->Int32Value(context.local()).FromJust()); |
| 11725 | |
| 11726 | // Check that the call-as-function handler can be called through new. |
| 11727 | value = CompileRun("new obj(43)" ); |
| 11728 | CHECK(!try_catch.HasCaught()); |
| 11729 | CHECK_EQ(-43, value->Int32Value(context.local()).FromJust()); |
| 11730 | |
| 11731 | // Check that the call-as-function handler can be called through |
| 11732 | // the API. |
| 11733 | v8::Local<Value> args[] = {v8_num(28)}; |
| 11734 | value = instance->CallAsFunction(context.local(), instance, 1, args) |
| 11735 | .ToLocalChecked(); |
| 11736 | CHECK(!try_catch.HasCaught()); |
| 11737 | CHECK_EQ(28, value->Int32Value(context.local()).FromJust()); |
| 11738 | } |
| 11739 | |
| 11740 | { |
| 11741 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11742 | Local<ObjectTemplate> instance_template(t->InstanceTemplate()); |
| 11743 | USE(instance_template); |
| 11744 | Local<v8::Object> instance = t->GetFunction(context.local()) |
| 11745 | .ToLocalChecked() |
| 11746 | ->NewInstance(context.local()) |
| 11747 | .ToLocalChecked(); |
| 11748 | CHECK(context->Global() |
| 11749 | ->Set(context.local(), v8_str("obj2" ), instance) |
| 11750 | .FromJust()); |
| 11751 | v8::TryCatch try_catch(isolate); |
| 11752 | Local<Value> value; |
| 11753 | CHECK(!try_catch.HasCaught()); |
| 11754 | |
| 11755 | // Call an object without call-as-function handler through the JS |
| 11756 | value = CompileRun("obj2(28)" ); |
| 11757 | CHECK(value.IsEmpty()); |
| 11758 | CHECK(try_catch.HasCaught()); |
| 11759 | String::Utf8Value exception_value1(isolate, try_catch.Exception()); |
| 11760 | // TODO(verwaest): Better message |
| 11761 | CHECK_EQ(0, strcmp("TypeError: obj2 is not a function" , *exception_value1)); |
| 11762 | try_catch.Reset(); |
| 11763 | |
| 11764 | // Call an object without call-as-function handler through the API |
| 11765 | v8::Local<Value> args[] = {v8_num(28)}; |
| 11766 | CHECK( |
| 11767 | instance->CallAsFunction(context.local(), instance, 1, args).IsEmpty()); |
| 11768 | CHECK(try_catch.HasCaught()); |
| 11769 | String::Utf8Value exception_value2(isolate, try_catch.Exception()); |
| 11770 | CHECK_EQ(0, |
| 11771 | strcmp("TypeError: object is not a function" , *exception_value2)); |
| 11772 | try_catch.Reset(); |
| 11773 | } |
| 11774 | |
| 11775 | { |
| 11776 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11777 | Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| 11778 | instance_template->SetCallAsFunctionHandler(ThrowValue); |
| 11779 | Local<v8::Object> instance = t->GetFunction(context.local()) |
| 11780 | .ToLocalChecked() |
| 11781 | ->NewInstance(context.local()) |
| 11782 | .ToLocalChecked(); |
| 11783 | CHECK(context->Global() |
| 11784 | ->Set(context.local(), v8_str("obj3" ), instance) |
| 11785 | .FromJust()); |
| 11786 | v8::TryCatch try_catch(isolate); |
| 11787 | Local<Value> value; |
| 11788 | CHECK(!try_catch.HasCaught()); |
| 11789 | |
| 11790 | // Catch the exception which is thrown by call-as-function handler |
| 11791 | value = CompileRun("obj3(22)" ); |
| 11792 | CHECK(try_catch.HasCaught()); |
| 11793 | String::Utf8Value exception_value1(isolate, try_catch.Exception()); |
| 11794 | CHECK_EQ(0, strcmp("22" , *exception_value1)); |
| 11795 | try_catch.Reset(); |
| 11796 | |
| 11797 | v8::Local<Value> args[] = {v8_num(23)}; |
| 11798 | CHECK( |
| 11799 | instance->CallAsFunction(context.local(), instance, 1, args).IsEmpty()); |
| 11800 | CHECK(try_catch.HasCaught()); |
| 11801 | String::Utf8Value exception_value2(isolate, try_catch.Exception()); |
| 11802 | CHECK_EQ(0, strcmp("23" , *exception_value2)); |
| 11803 | try_catch.Reset(); |
| 11804 | } |
| 11805 | |
| 11806 | { |
| 11807 | Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate); |
| 11808 | Local<ObjectTemplate> instance_template = t->InstanceTemplate(); |
| 11809 | instance_template->SetCallAsFunctionHandler(ReturnThis); |
| 11810 | Local<v8::Object> instance = t->GetFunction(context.local()) |
| 11811 | .ToLocalChecked() |
| 11812 | ->NewInstance(context.local()) |
| 11813 | .ToLocalChecked(); |
| 11814 | |
| 11815 | Local<v8::Value> a1 = |
| 11816 | instance |
| 11817 | ->CallAsFunction(context.local(), v8::Undefined(isolate), 0, |
| 11818 | nullptr) |
| 11819 | .ToLocalChecked(); |
| 11820 | CHECK(a1->StrictEquals(instance)); |
| 11821 | Local<v8::Value> a2 = |
| 11822 | instance->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr) |
| 11823 | .ToLocalChecked(); |
| 11824 | CHECK(a2->StrictEquals(instance)); |
| 11825 | Local<v8::Value> a3 = |
| 11826 | instance->CallAsFunction(context.local(), v8_num(42), 0, nullptr) |
| 11827 | .ToLocalChecked(); |
| 11828 | CHECK(a3->StrictEquals(instance)); |
| 11829 | Local<v8::Value> a4 = |
| 11830 | instance->CallAsFunction(context.local(), v8_str("hello" ), 0, nullptr) |
| 11831 | .ToLocalChecked(); |
| 11832 | CHECK(a4->StrictEquals(instance)); |
| 11833 | Local<v8::Value> a5 = |
| 11834 | instance->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr) |
| 11835 | .ToLocalChecked(); |
| 11836 | CHECK(a5->StrictEquals(instance)); |
| 11837 | } |
| 11838 | |
| 11839 | { |
| 11840 | CompileRun( |
| 11841 | "function ReturnThisSloppy() {" |
| 11842 | " return this;" |
| 11843 | "}" |
| 11844 | "function ReturnThisStrict() {" |
| 11845 | " 'use strict';" |
| 11846 | " return this;" |
| 11847 | "}" ); |
| 11848 | Local<Function> ReturnThisSloppy = Local<Function>::Cast( |
| 11849 | context->Global() |
| 11850 | ->Get(context.local(), v8_str("ReturnThisSloppy" )) |
| 11851 | .ToLocalChecked()); |
| 11852 | Local<Function> ReturnThisStrict = Local<Function>::Cast( |
| 11853 | context->Global() |
| 11854 | ->Get(context.local(), v8_str("ReturnThisStrict" )) |
| 11855 | .ToLocalChecked()); |
| 11856 | |
| 11857 | Local<v8::Value> a1 = |
| 11858 | ReturnThisSloppy |
| 11859 | ->CallAsFunction(context.local(), v8::Undefined(isolate), 0, |
| 11860 | nullptr) |
| 11861 | .ToLocalChecked(); |
| 11862 | CHECK(a1->StrictEquals(context->Global())); |
| 11863 | Local<v8::Value> a2 = |
| 11864 | ReturnThisSloppy |
| 11865 | ->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr) |
| 11866 | .ToLocalChecked(); |
| 11867 | CHECK(a2->StrictEquals(context->Global())); |
| 11868 | Local<v8::Value> a3 = |
| 11869 | ReturnThisSloppy |
| 11870 | ->CallAsFunction(context.local(), v8_num(42), 0, nullptr) |
| 11871 | .ToLocalChecked(); |
| 11872 | CHECK(a3->IsNumberObject()); |
| 11873 | CHECK_EQ(42.0, a3.As<v8::NumberObject>()->ValueOf()); |
| 11874 | Local<v8::Value> a4 = |
| 11875 | ReturnThisSloppy |
| 11876 | ->CallAsFunction(context.local(), v8_str("hello" ), 0, nullptr) |
| 11877 | .ToLocalChecked(); |
| 11878 | CHECK(a4->IsStringObject()); |
| 11879 | CHECK(a4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello" ))); |
| 11880 | Local<v8::Value> a5 = |
| 11881 | ReturnThisSloppy |
| 11882 | ->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr) |
| 11883 | .ToLocalChecked(); |
| 11884 | CHECK(a5->IsBooleanObject()); |
| 11885 | CHECK(a5.As<v8::BooleanObject>()->ValueOf()); |
| 11886 | |
| 11887 | Local<v8::Value> a6 = |
| 11888 | ReturnThisStrict |
| 11889 | ->CallAsFunction(context.local(), v8::Undefined(isolate), 0, |
| 11890 | nullptr) |
| 11891 | .ToLocalChecked(); |
| 11892 | CHECK(a6->IsUndefined()); |
| 11893 | Local<v8::Value> a7 = |
| 11894 | ReturnThisStrict |
| 11895 | ->CallAsFunction(context.local(), v8::Null(isolate), 0, nullptr) |
| 11896 | .ToLocalChecked(); |
| 11897 | CHECK(a7->IsNull()); |
| 11898 | Local<v8::Value> a8 = |
| 11899 | ReturnThisStrict |
| 11900 | ->CallAsFunction(context.local(), v8_num(42), 0, nullptr) |
| 11901 | .ToLocalChecked(); |
| 11902 | CHECK(a8->StrictEquals(v8_num(42))); |
| 11903 | Local<v8::Value> a9 = |
| 11904 | ReturnThisStrict |
| 11905 | ->CallAsFunction(context.local(), v8_str("hello" ), 0, nullptr) |
| 11906 | .ToLocalChecked(); |
| 11907 | CHECK(a9->StrictEquals(v8_str("hello" ))); |
| 11908 | Local<v8::Value> a10 = |
| 11909 | ReturnThisStrict |
| 11910 | ->CallAsFunction(context.local(), v8::True(isolate), 0, nullptr) |
| 11911 | .ToLocalChecked(); |
| 11912 | CHECK(a10->StrictEquals(v8::True(isolate))); |
| 11913 | } |
| 11914 | } |
| 11915 | |
| 11916 | |
| 11917 | // Check whether a non-function object is callable. |
| 11918 | THREADED_TEST(CallableObject) { |
| 11919 | LocalContext context; |
| 11920 | v8::Isolate* isolate = context->GetIsolate(); |
| 11921 | v8::HandleScope scope(isolate); |
| 11922 | |
| 11923 | { |
| 11924 | Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| 11925 | instance_template->SetCallAsFunctionHandler(call_as_function); |
| 11926 | Local<Object> instance = |
| 11927 | instance_template->NewInstance(context.local()).ToLocalChecked(); |
| 11928 | v8::TryCatch try_catch(isolate); |
| 11929 | |
| 11930 | CHECK(instance->IsCallable()); |
| 11931 | CHECK(!try_catch.HasCaught()); |
| 11932 | } |
| 11933 | |
| 11934 | { |
| 11935 | Local<ObjectTemplate> instance_template = ObjectTemplate::New(isolate); |
| 11936 | Local<Object> instance = |
| 11937 | instance_template->NewInstance(context.local()).ToLocalChecked(); |
| 11938 | v8::TryCatch try_catch(isolate); |
| 11939 | |
| 11940 | CHECK(!instance->IsCallable()); |
| 11941 | CHECK(!try_catch.HasCaught()); |
| 11942 | } |
| 11943 | |
| 11944 | { |
| 11945 | Local<FunctionTemplate> function_template = |
| 11946 | FunctionTemplate::New(isolate, call_as_function); |
| 11947 | Local<Function> function = |
| 11948 | function_template->GetFunction(context.local()).ToLocalChecked(); |
| 11949 | Local<Object> instance = function; |
| 11950 | v8::TryCatch try_catch(isolate); |
| 11951 | |
| 11952 | CHECK(instance->IsCallable()); |
| 11953 | CHECK(!try_catch.HasCaught()); |
| 11954 | } |
| 11955 | |
| 11956 | { |
| 11957 | Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate); |
| 11958 | Local<Function> function = |
| 11959 | function_template->GetFunction(context.local()).ToLocalChecked(); |
| 11960 | Local<Object> instance = function; |
| 11961 | v8::TryCatch try_catch(isolate); |
| 11962 | |
| 11963 | CHECK(instance->IsCallable()); |
| 11964 | CHECK(!try_catch.HasCaught()); |
| 11965 | } |
| 11966 | } |
| 11967 | |
| 11968 | |
| 11969 | THREADED_TEST(Regress567998) { |
| 11970 | LocalContext env; |
| 11971 | v8::HandleScope scope(env->GetIsolate()); |
| 11972 | |
| 11973 | Local<v8::FunctionTemplate> desc = |
| 11974 | v8::FunctionTemplate::New(env->GetIsolate()); |
| 11975 | desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable |
| 11976 | desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable |
| 11977 | |
| 11978 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 11979 | .ToLocalChecked() |
| 11980 | ->NewInstance(env.local()) |
| 11981 | .ToLocalChecked(); |
| 11982 | CHECK( |
| 11983 | env->Global()->Set(env.local(), v8_str("undetectable" ), obj).FromJust()); |
| 11984 | |
| 11985 | ExpectString("undetectable.toString()" , "[object Object]" ); |
| 11986 | ExpectString("typeof undetectable" , "undefined" ); |
| 11987 | ExpectString("typeof(undetectable)" , "undefined" ); |
| 11988 | ExpectBoolean("typeof undetectable == 'undefined'" , true); |
| 11989 | ExpectBoolean("typeof undetectable == 'object'" , false); |
| 11990 | ExpectBoolean("if (undetectable) { true; } else { false; }" , false); |
| 11991 | ExpectBoolean("!undetectable" , true); |
| 11992 | |
| 11993 | ExpectObject("true&&undetectable" , obj); |
| 11994 | ExpectBoolean("false&&undetectable" , false); |
| 11995 | ExpectBoolean("true||undetectable" , true); |
| 11996 | ExpectObject("false||undetectable" , obj); |
| 11997 | |
| 11998 | ExpectObject("undetectable&&true" , obj); |
| 11999 | ExpectObject("undetectable&&false" , obj); |
| 12000 | ExpectBoolean("undetectable||true" , true); |
| 12001 | ExpectBoolean("undetectable||false" , false); |
| 12002 | |
| 12003 | ExpectBoolean("undetectable==null" , true); |
| 12004 | ExpectBoolean("null==undetectable" , true); |
| 12005 | ExpectBoolean("undetectable==undefined" , true); |
| 12006 | ExpectBoolean("undefined==undetectable" , true); |
| 12007 | ExpectBoolean("undetectable==undetectable" , true); |
| 12008 | |
| 12009 | ExpectBoolean("undetectable===null" , false); |
| 12010 | ExpectBoolean("null===undetectable" , false); |
| 12011 | ExpectBoolean("undetectable===undefined" , false); |
| 12012 | ExpectBoolean("undefined===undetectable" , false); |
| 12013 | ExpectBoolean("undetectable===undetectable" , true); |
| 12014 | } |
| 12015 | |
| 12016 | |
| 12017 | static int Recurse(v8::Isolate* isolate, int depth, int iterations) { |
| 12018 | v8::HandleScope scope(isolate); |
| 12019 | if (depth == 0) return v8::HandleScope::NumberOfHandles(isolate); |
| 12020 | for (int i = 0; i < iterations; i++) { |
| 12021 | Local<v8::Number> n(v8::Integer::New(isolate, 42)); |
| 12022 | } |
| 12023 | return Recurse(isolate, depth - 1, iterations); |
| 12024 | } |
| 12025 | |
| 12026 | |
| 12027 | THREADED_TEST(HandleIteration) { |
| 12028 | static const int kIterations = 500; |
| 12029 | static const int kNesting = 200; |
| 12030 | LocalContext context; |
| 12031 | v8::Isolate* isolate = context->GetIsolate(); |
| 12032 | v8::HandleScope scope0(isolate); |
| 12033 | CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| 12034 | { |
| 12035 | v8::HandleScope scope1(isolate); |
| 12036 | CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| 12037 | for (int i = 0; i < kIterations; i++) { |
| 12038 | Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42)); |
| 12039 | CHECK_EQ(i + 1, v8::HandleScope::NumberOfHandles(isolate)); |
| 12040 | } |
| 12041 | |
| 12042 | CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate)); |
| 12043 | { |
| 12044 | v8::HandleScope scope2(CcTest::isolate()); |
| 12045 | for (int j = 0; j < kIterations; j++) { |
| 12046 | Local<v8::Number> n(v8::Integer::New(CcTest::isolate(), 42)); |
| 12047 | CHECK_EQ(j + 1 + kIterations, |
| 12048 | v8::HandleScope::NumberOfHandles(isolate)); |
| 12049 | } |
| 12050 | } |
| 12051 | CHECK_EQ(kIterations, v8::HandleScope::NumberOfHandles(isolate)); |
| 12052 | } |
| 12053 | CHECK_EQ(0, v8::HandleScope::NumberOfHandles(isolate)); |
| 12054 | CHECK_EQ(kNesting * kIterations, Recurse(isolate, kNesting, kIterations)); |
| 12055 | } |
| 12056 | |
| 12057 | |
| 12058 | static void InterceptorCallICFastApi( |
| 12059 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12060 | ApiTestFuzzer::Fuzz(); |
| 12061 | CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi)); |
| 12062 | int* call_count = |
| 12063 | reinterpret_cast<int*>(v8::External::Cast(*info.Data())->Value()); |
| 12064 | ++(*call_count); |
| 12065 | if ((*call_count) % 20 == 0) { |
| 12066 | CcTest::CollectAllGarbage(); |
| 12067 | } |
| 12068 | } |
| 12069 | |
| 12070 | static void FastApiCallback_TrivialSignature( |
| 12071 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12072 | ApiTestFuzzer::Fuzz(); |
| 12073 | CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_TrivialSignature)); |
| 12074 | v8::Isolate* isolate = CcTest::isolate(); |
| 12075 | CHECK_EQ(isolate, args.GetIsolate()); |
| 12076 | CHECK(args.This() |
| 12077 | ->Equals(isolate->GetCurrentContext(), args.Holder()) |
| 12078 | .FromJust()); |
| 12079 | CHECK(args.Data() |
| 12080 | ->Equals(isolate->GetCurrentContext(), v8_str("method_data" )) |
| 12081 | .FromJust()); |
| 12082 | args.GetReturnValue().Set( |
| 12083 | args[0]->Int32Value(isolate->GetCurrentContext()).FromJust() + 1); |
| 12084 | } |
| 12085 | |
| 12086 | static void FastApiCallback_SimpleSignature( |
| 12087 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12088 | ApiTestFuzzer::Fuzz(); |
| 12089 | CheckReturnValue(args, FUNCTION_ADDR(FastApiCallback_SimpleSignature)); |
| 12090 | v8::Isolate* isolate = CcTest::isolate(); |
| 12091 | CHECK_EQ(isolate, args.GetIsolate()); |
| 12092 | CHECK(args.This() |
| 12093 | ->GetPrototype() |
| 12094 | ->Equals(isolate->GetCurrentContext(), args.Holder()) |
| 12095 | .FromJust()); |
| 12096 | CHECK(args.Data() |
| 12097 | ->Equals(isolate->GetCurrentContext(), v8_str("method_data" )) |
| 12098 | .FromJust()); |
| 12099 | // Note, we're using HasRealNamedProperty instead of Has to avoid |
| 12100 | // invoking the interceptor again. |
| 12101 | CHECK(args.Holder() |
| 12102 | ->HasRealNamedProperty(isolate->GetCurrentContext(), v8_str("foo" )) |
| 12103 | .FromJust()); |
| 12104 | args.GetReturnValue().Set( |
| 12105 | args[0]->Int32Value(isolate->GetCurrentContext()).FromJust() + 1); |
| 12106 | } |
| 12107 | |
| 12108 | |
| 12109 | // Helper to maximize the odds of object moving. |
| 12110 | static void GenerateSomeGarbage() { |
| 12111 | CompileRun( |
| 12112 | "var garbage;" |
| 12113 | "for (var i = 0; i < 1000; i++) {" |
| 12114 | " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" |
| 12115 | "}" |
| 12116 | "garbage = undefined;" ); |
| 12117 | } |
| 12118 | |
| 12119 | |
| 12120 | void DirectApiCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12121 | static int count = 0; |
| 12122 | if (count++ % 3 == 0) { |
| 12123 | CcTest::CollectAllGarbage(); |
| 12124 | // This should move the stub |
| 12125 | GenerateSomeGarbage(); // This should ensure the old stub memory is flushed |
| 12126 | } |
| 12127 | } |
| 12128 | |
| 12129 | |
| 12130 | THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { |
| 12131 | LocalContext context; |
| 12132 | v8::Isolate* isolate = context->GetIsolate(); |
| 12133 | v8::HandleScope scope(isolate); |
| 12134 | v8::Local<v8::ObjectTemplate> nativeobject_templ = |
| 12135 | v8::ObjectTemplate::New(isolate); |
| 12136 | nativeobject_templ->Set(isolate, "callback" , |
| 12137 | v8::FunctionTemplate::New(isolate, |
| 12138 | DirectApiCallback)); |
| 12139 | v8::Local<v8::Object> nativeobject_obj = |
| 12140 | nativeobject_templ->NewInstance(context.local()).ToLocalChecked(); |
| 12141 | CHECK(context->Global() |
| 12142 | ->Set(context.local(), v8_str("nativeobject" ), nativeobject_obj) |
| 12143 | .FromJust()); |
| 12144 | // call the api function multiple times to ensure direct call stub creation. |
| 12145 | CompileRun( |
| 12146 | "function f() {" |
| 12147 | " for (var i = 1; i <= 30; i++) {" |
| 12148 | " nativeobject.callback();" |
| 12149 | " }" |
| 12150 | "}" |
| 12151 | "f();" ); |
| 12152 | } |
| 12153 | |
| 12154 | void ThrowingDirectApiCallback( |
| 12155 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12156 | args.GetIsolate()->ThrowException(v8_str("g" )); |
| 12157 | } |
| 12158 | |
| 12159 | THREADED_TEST(CallICFastApi_DirectCall_Throw) { |
| 12160 | LocalContext context; |
| 12161 | v8::Isolate* isolate = context->GetIsolate(); |
| 12162 | v8::HandleScope scope(isolate); |
| 12163 | v8::Local<v8::ObjectTemplate> nativeobject_templ = |
| 12164 | v8::ObjectTemplate::New(isolate); |
| 12165 | nativeobject_templ->Set( |
| 12166 | isolate, "callback" , |
| 12167 | v8::FunctionTemplate::New(isolate, ThrowingDirectApiCallback)); |
| 12168 | v8::Local<v8::Object> nativeobject_obj = |
| 12169 | nativeobject_templ->NewInstance(context.local()).ToLocalChecked(); |
| 12170 | CHECK(context->Global() |
| 12171 | ->Set(context.local(), v8_str("nativeobject" ), nativeobject_obj) |
| 12172 | .FromJust()); |
| 12173 | // call the api function multiple times to ensure direct call stub creation. |
| 12174 | v8::Local<Value> result = CompileRun( |
| 12175 | "var result = '';" |
| 12176 | "function f() {" |
| 12177 | " for (var i = 1; i <= 5; i++) {" |
| 12178 | " try { nativeobject.callback(); } catch (e) { result += e; }" |
| 12179 | " }" |
| 12180 | "}" |
| 12181 | "f(); result;" ); |
| 12182 | CHECK(v8_str("ggggg" )->Equals(context.local(), result).FromJust()); |
| 12183 | } |
| 12184 | |
| 12185 | static int p_getter_count_3; |
| 12186 | |
| 12187 | static Local<Value> DoDirectGetter() { |
| 12188 | if (++p_getter_count_3 % 3 == 0) { |
| 12189 | CcTest::CollectAllGarbage(); |
| 12190 | GenerateSomeGarbage(); |
| 12191 | } |
| 12192 | return v8_str("Direct Getter Result" ); |
| 12193 | } |
| 12194 | |
| 12195 | static void DirectGetterCallback( |
| 12196 | Local<String> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12197 | CheckReturnValue(info, FUNCTION_ADDR(DirectGetterCallback)); |
| 12198 | info.GetReturnValue().Set(DoDirectGetter()); |
| 12199 | } |
| 12200 | |
| 12201 | template <typename Accessor> |
| 12202 | static void LoadICFastApi_DirectCall_GCMoveStub(Accessor accessor) { |
| 12203 | LocalContext context; |
| 12204 | v8::Isolate* isolate = context->GetIsolate(); |
| 12205 | v8::HandleScope scope(isolate); |
| 12206 | v8::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate); |
| 12207 | obj->SetAccessor(v8_str("p1" ), accessor); |
| 12208 | CHECK(context->Global() |
| 12209 | ->Set(context.local(), v8_str("o1" ), |
| 12210 | obj->NewInstance(context.local()).ToLocalChecked()) |
| 12211 | .FromJust()); |
| 12212 | p_getter_count_3 = 0; |
| 12213 | v8::Local<v8::Value> result = CompileRun( |
| 12214 | "function f() {" |
| 12215 | " for (var i = 0; i < 30; i++) o1.p1;" |
| 12216 | " return o1.p1" |
| 12217 | "}" |
| 12218 | "f();" ); |
| 12219 | CHECK(v8_str("Direct Getter Result" ) |
| 12220 | ->Equals(context.local(), result) |
| 12221 | .FromJust()); |
| 12222 | CHECK_EQ(31, p_getter_count_3); |
| 12223 | } |
| 12224 | |
| 12225 | THREADED_PROFILED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { |
| 12226 | LoadICFastApi_DirectCall_GCMoveStub(DirectGetterCallback); |
| 12227 | } |
| 12228 | |
| 12229 | void ThrowingDirectGetterCallback( |
| 12230 | Local<String> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12231 | info.GetIsolate()->ThrowException(v8_str("g" )); |
| 12232 | } |
| 12233 | |
| 12234 | THREADED_TEST(LoadICFastApi_DirectCall_Throw) { |
| 12235 | LocalContext context; |
| 12236 | v8::Isolate* isolate = context->GetIsolate(); |
| 12237 | v8::HandleScope scope(isolate); |
| 12238 | v8::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate); |
| 12239 | obj->SetAccessor(v8_str("p1" ), ThrowingDirectGetterCallback); |
| 12240 | CHECK(context->Global() |
| 12241 | ->Set(context.local(), v8_str("o1" ), |
| 12242 | obj->NewInstance(context.local()).ToLocalChecked()) |
| 12243 | .FromJust()); |
| 12244 | v8::Local<Value> result = CompileRun( |
| 12245 | "var result = '';" |
| 12246 | "for (var i = 0; i < 5; i++) {" |
| 12247 | " try { o1.p1; } catch (e) { result += e; }" |
| 12248 | "}" |
| 12249 | "result;" ); |
| 12250 | CHECK(v8_str("ggggg" )->Equals(context.local(), result).FromJust()); |
| 12251 | } |
| 12252 | |
| 12253 | THREADED_PROFILED_TEST(InterceptorCallICFastApi_TrivialSignature) { |
| 12254 | int interceptor_call_count = 0; |
| 12255 | v8::Isolate* isolate = CcTest::isolate(); |
| 12256 | v8::HandleScope scope(isolate); |
| 12257 | v8::Local<v8::FunctionTemplate> fun_templ = |
| 12258 | v8::FunctionTemplate::New(isolate); |
| 12259 | v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| 12260 | isolate, FastApiCallback_TrivialSignature, v8_str("method_data" ), |
| 12261 | v8::Local<v8::Signature>()); |
| 12262 | v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| 12263 | proto_templ->Set(v8_str("method" ), method_templ); |
| 12264 | v8::Local<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); |
| 12265 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 12266 | InterceptorCallICFastApi, nullptr, nullptr, nullptr, nullptr, |
| 12267 | v8::External::New(isolate, &interceptor_call_count))); |
| 12268 | LocalContext context; |
| 12269 | v8::Local<v8::Function> fun = |
| 12270 | fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| 12271 | GenerateSomeGarbage(); |
| 12272 | CHECK(context->Global() |
| 12273 | ->Set(context.local(), v8_str("o" ), |
| 12274 | fun->NewInstance(context.local()).ToLocalChecked()) |
| 12275 | .FromJust()); |
| 12276 | CompileRun( |
| 12277 | "var result = 0;" |
| 12278 | "for (var i = 0; i < 100; i++) {" |
| 12279 | " result = o.method(41);" |
| 12280 | "}" ); |
| 12281 | CHECK_EQ(42, context->Global() |
| 12282 | ->Get(context.local(), v8_str("result" )) |
| 12283 | .ToLocalChecked() |
| 12284 | ->Int32Value(context.local()) |
| 12285 | .FromJust()); |
| 12286 | CHECK_EQ(100, interceptor_call_count); |
| 12287 | } |
| 12288 | |
| 12289 | THREADED_PROFILED_TEST(CallICFastApi_TrivialSignature) { |
| 12290 | v8::Isolate* isolate = CcTest::isolate(); |
| 12291 | v8::HandleScope scope(isolate); |
| 12292 | v8::Local<v8::FunctionTemplate> fun_templ = |
| 12293 | v8::FunctionTemplate::New(isolate); |
| 12294 | v8::Local<v8::FunctionTemplate> method_templ = v8::FunctionTemplate::New( |
| 12295 | isolate, FastApiCallback_TrivialSignature, v8_str("method_data" ), |
| 12296 | v8::Local<v8::Signature>()); |
| 12297 | v8::Local<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); |
| 12298 | proto_templ->Set(v8_str("method" ), method_templ); |
| 12299 | v8::Local<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); |
| 12300 | USE(templ); |
| 12301 | LocalContext context; |
| 12302 | v8::Local<v8::Function> fun = |
| 12303 | fun_templ->GetFunction(context.local()).ToLocalChecked(); |
| 12304 | GenerateSomeGarbage(); |
| 12305 | CHECK(context->Global() |
| 12306 | ->Set(context.local(), v8_str("o" ), |
| 12307 | fun->NewInstance(context.local()).ToLocalChecked()) |
| 12308 | .FromJust()); |
| 12309 | CompileRun( |
| 12310 | "var result = 0;" |
| 12311 | "for (var i = 0; i < 100; i++) {" |
| 12312 | " result = o.method(41);" |
| 12313 | "}" ); |
| 12314 | |
| 12315 | CHECK_EQ(42, context->Global() |
| 12316 | ->Get(context.local(), v8_str("result" )) |
| 12317 | .ToLocalChecked() |
| 12318 | ->Int32Value(context.local()) |
| 12319 | .FromJust()); |
| 12320 | } |
| 12321 | |
| 12322 | static void ThrowingGetter(Local<String> name, |
| 12323 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12324 | ApiTestFuzzer::Fuzz(); |
| 12325 | info.GetIsolate()->ThrowException(Local<Value>()); |
| 12326 | info.GetReturnValue().SetUndefined(); |
| 12327 | } |
| 12328 | |
| 12329 | |
| 12330 | THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { |
| 12331 | LocalContext context; |
| 12332 | HandleScope scope(context->GetIsolate()); |
| 12333 | |
| 12334 | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| 12335 | Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 12336 | instance_templ->SetAccessor(v8_str("f" ), ThrowingGetter); |
| 12337 | |
| 12338 | Local<Object> instance = templ->GetFunction(context.local()) |
| 12339 | .ToLocalChecked() |
| 12340 | ->NewInstance(context.local()) |
| 12341 | .ToLocalChecked(); |
| 12342 | |
| 12343 | Local<Object> another = Object::New(context->GetIsolate()); |
| 12344 | CHECK(another->SetPrototype(context.local(), instance).FromJust()); |
| 12345 | |
| 12346 | Local<Object> with_js_getter = CompileRun( |
| 12347 | "o = {};\n" |
| 12348 | "o.__defineGetter__('f', function() { throw undefined; });\n" |
| 12349 | "o\n" ).As<Object>(); |
| 12350 | CHECK(!with_js_getter.IsEmpty()); |
| 12351 | |
| 12352 | TryCatch try_catch(context->GetIsolate()); |
| 12353 | |
| 12354 | v8::MaybeLocal<Value> result = |
| 12355 | instance->GetRealNamedProperty(context.local(), v8_str("f" )); |
| 12356 | CHECK(try_catch.HasCaught()); |
| 12357 | try_catch.Reset(); |
| 12358 | CHECK(result.IsEmpty()); |
| 12359 | |
| 12360 | Maybe<PropertyAttribute> attr = |
| 12361 | instance->GetRealNamedPropertyAttributes(context.local(), v8_str("f" )); |
| 12362 | CHECK(!try_catch.HasCaught()); |
| 12363 | CHECK(Just(None) == attr); |
| 12364 | |
| 12365 | result = another->GetRealNamedProperty(context.local(), v8_str("f" )); |
| 12366 | CHECK(try_catch.HasCaught()); |
| 12367 | try_catch.Reset(); |
| 12368 | CHECK(result.IsEmpty()); |
| 12369 | |
| 12370 | attr = another->GetRealNamedPropertyAttributes(context.local(), v8_str("f" )); |
| 12371 | CHECK(!try_catch.HasCaught()); |
| 12372 | CHECK(Just(None) == attr); |
| 12373 | |
| 12374 | result = another->GetRealNamedPropertyInPrototypeChain(context.local(), |
| 12375 | v8_str("f" )); |
| 12376 | CHECK(try_catch.HasCaught()); |
| 12377 | try_catch.Reset(); |
| 12378 | CHECK(result.IsEmpty()); |
| 12379 | |
| 12380 | attr = another->GetRealNamedPropertyAttributesInPrototypeChain( |
| 12381 | context.local(), v8_str("f" )); |
| 12382 | CHECK(!try_catch.HasCaught()); |
| 12383 | CHECK(Just(None) == attr); |
| 12384 | |
| 12385 | result = another->Get(context.local(), v8_str("f" )); |
| 12386 | CHECK(try_catch.HasCaught()); |
| 12387 | try_catch.Reset(); |
| 12388 | CHECK(result.IsEmpty()); |
| 12389 | |
| 12390 | result = with_js_getter->GetRealNamedProperty(context.local(), v8_str("f" )); |
| 12391 | CHECK(try_catch.HasCaught()); |
| 12392 | try_catch.Reset(); |
| 12393 | CHECK(result.IsEmpty()); |
| 12394 | |
| 12395 | attr = with_js_getter->GetRealNamedPropertyAttributes(context.local(), |
| 12396 | v8_str("f" )); |
| 12397 | CHECK(!try_catch.HasCaught()); |
| 12398 | CHECK(Just(None) == attr); |
| 12399 | |
| 12400 | result = with_js_getter->Get(context.local(), v8_str("f" )); |
| 12401 | CHECK(try_catch.HasCaught()); |
| 12402 | try_catch.Reset(); |
| 12403 | CHECK(result.IsEmpty()); |
| 12404 | |
| 12405 | Local<Object> target = CompileRun("({})" ).As<Object>(); |
| 12406 | Local<Object> handler = CompileRun("({})" ).As<Object>(); |
| 12407 | Local<v8::Proxy> proxy = |
| 12408 | v8::Proxy::New(context.local(), target, handler).ToLocalChecked(); |
| 12409 | |
| 12410 | result = target->GetRealNamedProperty(context.local(), v8_str("f" )); |
| 12411 | CHECK(!try_catch.HasCaught()); |
| 12412 | CHECK(result.IsEmpty()); |
| 12413 | |
| 12414 | result = proxy->GetRealNamedProperty(context.local(), v8_str("f" )); |
| 12415 | CHECK(!try_catch.HasCaught()); |
| 12416 | CHECK(result.IsEmpty()); |
| 12417 | } |
| 12418 | |
| 12419 | |
| 12420 | static void ThrowingCallbackWithTryCatch( |
| 12421 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12422 | TryCatch try_catch(args.GetIsolate()); |
| 12423 | // Verboseness is important: it triggers message delivery which can call into |
| 12424 | // external code. |
| 12425 | try_catch.SetVerbose(true); |
| 12426 | CompileRun("throw 'from JS';" ); |
| 12427 | CHECK(try_catch.HasCaught()); |
| 12428 | CHECK(!CcTest::i_isolate()->has_pending_exception()); |
| 12429 | CHECK(!CcTest::i_isolate()->has_scheduled_exception()); |
| 12430 | } |
| 12431 | |
| 12432 | |
| 12433 | static int call_depth; |
| 12434 | |
| 12435 | |
| 12436 | static void WithTryCatch(Local<Message> message, Local<Value> data) { |
| 12437 | TryCatch try_catch(CcTest::isolate()); |
| 12438 | } |
| 12439 | |
| 12440 | |
| 12441 | static void ThrowFromJS(Local<Message> message, Local<Value> data) { |
| 12442 | if (--call_depth) CompileRun("throw 'ThrowInJS';" ); |
| 12443 | } |
| 12444 | |
| 12445 | |
| 12446 | static void ThrowViaApi(Local<Message> message, Local<Value> data) { |
| 12447 | if (--call_depth) CcTest::isolate()->ThrowException(v8_str("ThrowViaApi" )); |
| 12448 | } |
| 12449 | |
| 12450 | |
| 12451 | static void WebKitLike(Local<Message> message, Local<Value> data) { |
| 12452 | Local<String> errorMessageString = message->Get(); |
| 12453 | CHECK(!errorMessageString.IsEmpty()); |
| 12454 | message->GetStackTrace(); |
| 12455 | message->GetScriptOrigin().ResourceName(); |
| 12456 | } |
| 12457 | |
| 12458 | |
| 12459 | THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { |
| 12460 | LocalContext context; |
| 12461 | v8::Isolate* isolate = context->GetIsolate(); |
| 12462 | HandleScope scope(isolate); |
| 12463 | |
| 12464 | Local<Function> func = |
| 12465 | FunctionTemplate::New(isolate, ThrowingCallbackWithTryCatch) |
| 12466 | ->GetFunction(context.local()) |
| 12467 | .ToLocalChecked(); |
| 12468 | CHECK( |
| 12469 | context->Global()->Set(context.local(), v8_str("func" ), func).FromJust()); |
| 12470 | |
| 12471 | MessageCallback callbacks[] = {nullptr, WebKitLike, ThrowViaApi, ThrowFromJS, |
| 12472 | WithTryCatch}; |
| 12473 | for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { |
| 12474 | MessageCallback callback = callbacks[i]; |
| 12475 | if (callback != nullptr) { |
| 12476 | isolate->AddMessageListener(callback); |
| 12477 | } |
| 12478 | // Some small number to control number of times message handler should |
| 12479 | // throw an exception. |
| 12480 | call_depth = 5; |
| 12481 | ExpectFalse( |
| 12482 | "var thrown = false;\n" |
| 12483 | "try { func(); } catch(e) { thrown = true; }\n" |
| 12484 | "thrown\n" ); |
| 12485 | if (callback != nullptr) { |
| 12486 | isolate->RemoveMessageListeners(callback); |
| 12487 | } |
| 12488 | } |
| 12489 | } |
| 12490 | |
| 12491 | |
| 12492 | static void ParentGetter(Local<String> name, |
| 12493 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12494 | ApiTestFuzzer::Fuzz(); |
| 12495 | info.GetReturnValue().Set(v8_num(1)); |
| 12496 | } |
| 12497 | |
| 12498 | |
| 12499 | static void ChildGetter(Local<String> name, |
| 12500 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12501 | ApiTestFuzzer::Fuzz(); |
| 12502 | info.GetReturnValue().Set(v8_num(42)); |
| 12503 | } |
| 12504 | |
| 12505 | |
| 12506 | THREADED_TEST(Overriding) { |
| 12507 | LocalContext context; |
| 12508 | v8::Isolate* isolate = context->GetIsolate(); |
| 12509 | v8::HandleScope scope(isolate); |
| 12510 | |
| 12511 | // Parent template. |
| 12512 | Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(isolate); |
| 12513 | Local<ObjectTemplate> parent_instance_templ = |
| 12514 | parent_templ->InstanceTemplate(); |
| 12515 | parent_instance_templ->SetAccessor(v8_str("f" ), ParentGetter); |
| 12516 | |
| 12517 | // Template that inherits from the parent template. |
| 12518 | Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(isolate); |
| 12519 | Local<ObjectTemplate> child_instance_templ = |
| 12520 | child_templ->InstanceTemplate(); |
| 12521 | child_templ->Inherit(parent_templ); |
| 12522 | // Override 'f'. The child version of 'f' should get called for child |
| 12523 | // instances. |
| 12524 | child_instance_templ->SetAccessor(v8_str("f" ), ChildGetter); |
| 12525 | // Add 'g' twice. The 'g' added last should get called for instances. |
| 12526 | child_instance_templ->SetAccessor(v8_str("g" ), ParentGetter); |
| 12527 | child_instance_templ->SetAccessor(v8_str("g" ), ChildGetter); |
| 12528 | |
| 12529 | // Add 'h' as an accessor to the proto template with ReadOnly attributes |
| 12530 | // so 'h' can be shadowed on the instance object. |
| 12531 | Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); |
| 12532 | child_proto_templ->SetAccessor(v8_str("h" ), ParentGetter, nullptr, |
| 12533 | v8::Local<Value>(), v8::DEFAULT, v8::ReadOnly); |
| 12534 | |
| 12535 | // Add 'i' as an accessor to the instance template with ReadOnly attributes |
| 12536 | // but the attribute does not have effect because it is duplicated with |
| 12537 | // nullptr setter. |
| 12538 | child_instance_templ->SetAccessor(v8_str("i" ), ChildGetter, nullptr, |
| 12539 | v8::Local<Value>(), v8::DEFAULT, |
| 12540 | v8::ReadOnly); |
| 12541 | |
| 12542 | // Instantiate the child template. |
| 12543 | Local<v8::Object> instance = child_templ->GetFunction(context.local()) |
| 12544 | .ToLocalChecked() |
| 12545 | ->NewInstance(context.local()) |
| 12546 | .ToLocalChecked(); |
| 12547 | |
| 12548 | // Check that the child function overrides the parent one. |
| 12549 | CHECK(context->Global() |
| 12550 | ->Set(context.local(), v8_str("o" ), instance) |
| 12551 | .FromJust()); |
| 12552 | Local<Value> value = v8_compile("o.f" )->Run(context.local()).ToLocalChecked(); |
| 12553 | // Check that the 'g' that was added last is hit. |
| 12554 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 12555 | value = v8_compile("o.g" )->Run(context.local()).ToLocalChecked(); |
| 12556 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 12557 | |
| 12558 | // Check that 'h' cannot be shadowed. |
| 12559 | value = v8_compile("o.h = 3; o.h" )->Run(context.local()).ToLocalChecked(); |
| 12560 | CHECK_EQ(1, value->Int32Value(context.local()).FromJust()); |
| 12561 | |
| 12562 | // Check that 'i' cannot be shadowed or changed. |
| 12563 | value = v8_compile("o.i = 3; o.i" )->Run(context.local()).ToLocalChecked(); |
| 12564 | CHECK_EQ(42, value->Int32Value(context.local()).FromJust()); |
| 12565 | } |
| 12566 | |
| 12567 | |
| 12568 | static void ShouldThrowOnErrorGetter( |
| 12569 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 12570 | ApiTestFuzzer::Fuzz(); |
| 12571 | v8::Isolate* isolate = info.GetIsolate(); |
| 12572 | Local<Boolean> should_throw_on_error = |
| 12573 | Boolean::New(isolate, info.ShouldThrowOnError()); |
| 12574 | info.GetReturnValue().Set(should_throw_on_error); |
| 12575 | } |
| 12576 | |
| 12577 | |
| 12578 | template <typename T> |
| 12579 | static void ShouldThrowOnErrorSetter(Local<Name> name, Local<v8::Value> value, |
| 12580 | const v8::PropertyCallbackInfo<T>& info) { |
| 12581 | ApiTestFuzzer::Fuzz(); |
| 12582 | v8::Isolate* isolate = info.GetIsolate(); |
| 12583 | auto context = isolate->GetCurrentContext(); |
| 12584 | Local<Boolean> should_throw_on_error_value = |
| 12585 | Boolean::New(isolate, info.ShouldThrowOnError()); |
| 12586 | CHECK(context->Global() |
| 12587 | ->Set(isolate->GetCurrentContext(), v8_str("should_throw_setter" ), |
| 12588 | should_throw_on_error_value) |
| 12589 | .FromJust()); |
| 12590 | } |
| 12591 | |
| 12592 | |
| 12593 | THREADED_TEST(AccessorShouldThrowOnError) { |
| 12594 | LocalContext context; |
| 12595 | v8::Isolate* isolate = context->GetIsolate(); |
| 12596 | v8::HandleScope scope(isolate); |
| 12597 | Local<Object> global = context->Global(); |
| 12598 | |
| 12599 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 12600 | Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); |
| 12601 | instance_templ->SetAccessor(v8_str("f" ), ShouldThrowOnErrorGetter, |
| 12602 | ShouldThrowOnErrorSetter<void>); |
| 12603 | |
| 12604 | Local<v8::Object> instance = templ->GetFunction(context.local()) |
| 12605 | .ToLocalChecked() |
| 12606 | ->NewInstance(context.local()) |
| 12607 | .ToLocalChecked(); |
| 12608 | |
| 12609 | CHECK(global->Set(context.local(), v8_str("o" ), instance).FromJust()); |
| 12610 | |
| 12611 | // SLOPPY mode |
| 12612 | Local<Value> value = v8_compile("o.f" )->Run(context.local()).ToLocalChecked(); |
| 12613 | CHECK(value->IsFalse()); |
| 12614 | v8_compile("o.f = 153" )->Run(context.local()).ToLocalChecked(); |
| 12615 | value = global->Get(context.local(), v8_str("should_throw_setter" )) |
| 12616 | .ToLocalChecked(); |
| 12617 | CHECK(value->IsFalse()); |
| 12618 | |
| 12619 | // STRICT mode |
| 12620 | value = v8_compile("'use strict';o.f" )->Run(context.local()).ToLocalChecked(); |
| 12621 | CHECK(value->IsFalse()); |
| 12622 | v8_compile("'use strict'; o.f = 153" )->Run(context.local()).ToLocalChecked(); |
| 12623 | value = global->Get(context.local(), v8_str("should_throw_setter" )) |
| 12624 | .ToLocalChecked(); |
| 12625 | CHECK(value->IsTrue()); |
| 12626 | } |
| 12627 | |
| 12628 | |
| 12629 | static void ShouldThrowOnErrorQuery( |
| 12630 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 12631 | ApiTestFuzzer::Fuzz(); |
| 12632 | v8::Isolate* isolate = info.GetIsolate(); |
| 12633 | info.GetReturnValue().Set(v8::None); |
| 12634 | |
| 12635 | auto context = isolate->GetCurrentContext(); |
| 12636 | Local<Boolean> should_throw_on_error_value = |
| 12637 | Boolean::New(isolate, info.ShouldThrowOnError()); |
| 12638 | CHECK(context->Global() |
| 12639 | ->Set(isolate->GetCurrentContext(), v8_str("should_throw_query" ), |
| 12640 | should_throw_on_error_value) |
| 12641 | .FromJust()); |
| 12642 | } |
| 12643 | |
| 12644 | |
| 12645 | static void ShouldThrowOnErrorDeleter( |
| 12646 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| 12647 | ApiTestFuzzer::Fuzz(); |
| 12648 | v8::Isolate* isolate = info.GetIsolate(); |
| 12649 | info.GetReturnValue().Set(v8::True(isolate)); |
| 12650 | |
| 12651 | auto context = isolate->GetCurrentContext(); |
| 12652 | Local<Boolean> should_throw_on_error_value = |
| 12653 | Boolean::New(isolate, info.ShouldThrowOnError()); |
| 12654 | CHECK(context->Global() |
| 12655 | ->Set(isolate->GetCurrentContext(), v8_str("should_throw_deleter" ), |
| 12656 | should_throw_on_error_value) |
| 12657 | .FromJust()); |
| 12658 | } |
| 12659 | |
| 12660 | |
| 12661 | static void ShouldThrowOnErrorPropertyEnumerator( |
| 12662 | const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 12663 | ApiTestFuzzer::Fuzz(); |
| 12664 | v8::Isolate* isolate = info.GetIsolate(); |
| 12665 | Local<v8::Array> names = v8::Array::New(isolate, 1); |
| 12666 | CHECK(names->Set(isolate->GetCurrentContext(), names, v8_num(1)).FromJust()); |
| 12667 | info.GetReturnValue().Set(names); |
| 12668 | |
| 12669 | auto context = isolate->GetCurrentContext(); |
| 12670 | Local<Boolean> should_throw_on_error_value = |
| 12671 | Boolean::New(isolate, info.ShouldThrowOnError()); |
| 12672 | CHECK(context->Global() |
| 12673 | ->Set(isolate->GetCurrentContext(), |
| 12674 | v8_str("should_throw_enumerator" ), |
| 12675 | should_throw_on_error_value) |
| 12676 | .FromJust()); |
| 12677 | } |
| 12678 | |
| 12679 | |
| 12680 | THREADED_TEST(InterceptorShouldThrowOnError) { |
| 12681 | LocalContext context; |
| 12682 | v8::Isolate* isolate = context->GetIsolate(); |
| 12683 | v8::HandleScope scope(isolate); |
| 12684 | Local<Object> global = context->Global(); |
| 12685 | |
| 12686 | auto interceptor_templ = v8::ObjectTemplate::New(isolate); |
| 12687 | v8::NamedPropertyHandlerConfiguration handler( |
| 12688 | ShouldThrowOnErrorGetter, ShouldThrowOnErrorSetter<Value>, |
| 12689 | ShouldThrowOnErrorQuery, ShouldThrowOnErrorDeleter, |
| 12690 | ShouldThrowOnErrorPropertyEnumerator); |
| 12691 | interceptor_templ->SetHandler(handler); |
| 12692 | |
| 12693 | Local<v8::Object> instance = |
| 12694 | interceptor_templ->NewInstance(context.local()).ToLocalChecked(); |
| 12695 | |
| 12696 | CHECK(global->Set(context.local(), v8_str("o" ), instance).FromJust()); |
| 12697 | |
| 12698 | // SLOPPY mode |
| 12699 | Local<Value> value = v8_compile("o.f" )->Run(context.local()).ToLocalChecked(); |
| 12700 | CHECK(value->IsFalse()); |
| 12701 | v8_compile("o.f = 153" )->Run(context.local()).ToLocalChecked(); |
| 12702 | value = global->Get(context.local(), v8_str("should_throw_setter" )) |
| 12703 | .ToLocalChecked(); |
| 12704 | CHECK(value->IsFalse()); |
| 12705 | |
| 12706 | v8_compile("delete o.f" )->Run(context.local()).ToLocalChecked(); |
| 12707 | value = global->Get(context.local(), v8_str("should_throw_deleter" )) |
| 12708 | .ToLocalChecked(); |
| 12709 | CHECK(value->IsFalse()); |
| 12710 | |
| 12711 | v8_compile("Object.getOwnPropertyNames(o)" ) |
| 12712 | ->Run(context.local()) |
| 12713 | .ToLocalChecked(); |
| 12714 | value = global->Get(context.local(), v8_str("should_throw_enumerator" )) |
| 12715 | .ToLocalChecked(); |
| 12716 | CHECK(value->IsFalse()); |
| 12717 | |
| 12718 | // STRICT mode |
| 12719 | value = v8_compile("'use strict';o.f" )->Run(context.local()).ToLocalChecked(); |
| 12720 | CHECK(value->IsFalse()); |
| 12721 | v8_compile("'use strict'; o.f = 153" )->Run(context.local()).ToLocalChecked(); |
| 12722 | value = global->Get(context.local(), v8_str("should_throw_setter" )) |
| 12723 | .ToLocalChecked(); |
| 12724 | CHECK(value->IsTrue()); |
| 12725 | |
| 12726 | v8_compile("'use strict'; delete o.f" )->Run(context.local()).ToLocalChecked(); |
| 12727 | value = global->Get(context.local(), v8_str("should_throw_deleter" )) |
| 12728 | .ToLocalChecked(); |
| 12729 | CHECK(value->IsTrue()); |
| 12730 | |
| 12731 | v8_compile("'use strict'; Object.getOwnPropertyNames(o)" ) |
| 12732 | ->Run(context.local()) |
| 12733 | .ToLocalChecked(); |
| 12734 | value = global->Get(context.local(), v8_str("should_throw_enumerator" )) |
| 12735 | .ToLocalChecked(); |
| 12736 | CHECK(value->IsFalse()); |
| 12737 | } |
| 12738 | |
| 12739 | static void EmptyHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {} |
| 12740 | |
| 12741 | TEST(CallHandlerHasNoSideEffect) { |
| 12742 | v8::Isolate* isolate = CcTest::isolate(); |
| 12743 | v8::HandleScope scope(isolate); |
| 12744 | LocalContext context; |
| 12745 | |
| 12746 | // Function template with call handler. |
| 12747 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 12748 | templ->SetCallHandler(EmptyHandler); |
| 12749 | CHECK(context->Global() |
| 12750 | ->Set(context.local(), v8_str("f" ), |
| 12751 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 12752 | .FromJust()); |
| 12753 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()" ), true).IsEmpty()); |
| 12754 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()" ), true).IsEmpty()); |
| 12755 | |
| 12756 | // Side-effect-free version. |
| 12757 | Local<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(isolate); |
| 12758 | templ2->SetCallHandler(EmptyHandler, v8::Local<Value>(), |
| 12759 | v8::SideEffectType::kHasNoSideEffect); |
| 12760 | CHECK(context->Global() |
| 12761 | ->Set(context.local(), v8_str("f2" ), |
| 12762 | templ2->GetFunction(context.local()).ToLocalChecked()) |
| 12763 | .FromJust()); |
| 12764 | v8::debug::EvaluateGlobal(isolate, v8_str("f2()" ), true).ToLocalChecked(); |
| 12765 | v8::debug::EvaluateGlobal(isolate, v8_str("new f2()" ), true).ToLocalChecked(); |
| 12766 | } |
| 12767 | |
| 12768 | TEST(FunctionTemplateNewHasNoSideEffect) { |
| 12769 | v8::Isolate* isolate = CcTest::isolate(); |
| 12770 | v8::HandleScope scope(isolate); |
| 12771 | LocalContext context; |
| 12772 | |
| 12773 | // Function template with call handler. |
| 12774 | Local<v8::FunctionTemplate> templ = |
| 12775 | v8::FunctionTemplate::New(isolate, EmptyHandler); |
| 12776 | CHECK(context->Global() |
| 12777 | ->Set(context.local(), v8_str("f" ), |
| 12778 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 12779 | .FromJust()); |
| 12780 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()" ), true).IsEmpty()); |
| 12781 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()" ), true).IsEmpty()); |
| 12782 | |
| 12783 | // Side-effect-free version. |
| 12784 | Local<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New( |
| 12785 | isolate, EmptyHandler, v8::Local<Value>(), v8::Local<v8::Signature>(), 0, |
| 12786 | v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasNoSideEffect); |
| 12787 | CHECK(context->Global() |
| 12788 | ->Set(context.local(), v8_str("f2" ), |
| 12789 | templ2->GetFunction(context.local()).ToLocalChecked()) |
| 12790 | .FromJust()); |
| 12791 | v8::debug::EvaluateGlobal(isolate, v8_str("f2()" ), true).ToLocalChecked(); |
| 12792 | v8::debug::EvaluateGlobal(isolate, v8_str("new f2()" ), true).ToLocalChecked(); |
| 12793 | } |
| 12794 | |
| 12795 | TEST(FunctionTemplateNewWithCacheHasNoSideEffect) { |
| 12796 | v8::Isolate* isolate = CcTest::isolate(); |
| 12797 | v8::HandleScope scope(isolate); |
| 12798 | LocalContext context; |
| 12799 | v8::Local<v8::Private> priv = |
| 12800 | v8::Private::ForApi(isolate, v8_str("Foo#draft" )); |
| 12801 | |
| 12802 | // Function template with call handler. |
| 12803 | Local<v8::FunctionTemplate> templ = |
| 12804 | v8::FunctionTemplate::NewWithCache(isolate, EmptyHandler, priv); |
| 12805 | CHECK(context->Global() |
| 12806 | ->Set(context.local(), v8_str("f" ), |
| 12807 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 12808 | .FromJust()); |
| 12809 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()" ), true).IsEmpty()); |
| 12810 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()" ), true).IsEmpty()); |
| 12811 | |
| 12812 | // Side-effect-free version. |
| 12813 | Local<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::NewWithCache( |
| 12814 | isolate, EmptyHandler, priv, v8::Local<Value>(), |
| 12815 | v8::Local<v8::Signature>(), 0, v8::SideEffectType::kHasNoSideEffect); |
| 12816 | CHECK(context->Global() |
| 12817 | ->Set(context.local(), v8_str("f2" ), |
| 12818 | templ2->GetFunction(context.local()).ToLocalChecked()) |
| 12819 | .FromJust()); |
| 12820 | v8::debug::EvaluateGlobal(isolate, v8_str("f2()" ), true).ToLocalChecked(); |
| 12821 | v8::debug::EvaluateGlobal(isolate, v8_str("new f2()" ), true).ToLocalChecked(); |
| 12822 | } |
| 12823 | |
| 12824 | TEST(FunctionNewHasNoSideEffect) { |
| 12825 | v8::Isolate* isolate = CcTest::isolate(); |
| 12826 | v8::HandleScope scope(isolate); |
| 12827 | LocalContext context; |
| 12828 | |
| 12829 | // Function with side-effect. |
| 12830 | Local<Function> func = |
| 12831 | Function::New(context.local(), EmptyHandler).ToLocalChecked(); |
| 12832 | CHECK(context->Global()->Set(context.local(), v8_str("f" ), func).FromJust()); |
| 12833 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()" ), true).IsEmpty()); |
| 12834 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("new f()" ), true).IsEmpty()); |
| 12835 | |
| 12836 | // Side-effect-free version. |
| 12837 | Local<Function> func2 = |
| 12838 | Function::New(context.local(), EmptyHandler, Local<Value>(), 0, |
| 12839 | v8::ConstructorBehavior::kAllow, |
| 12840 | v8::SideEffectType::kHasNoSideEffect) |
| 12841 | .ToLocalChecked(); |
| 12842 | CHECK( |
| 12843 | context->Global()->Set(context.local(), v8_str("f2" ), func2).FromJust()); |
| 12844 | v8::debug::EvaluateGlobal(isolate, v8_str("f2()" ), true).ToLocalChecked(); |
| 12845 | v8::debug::EvaluateGlobal(isolate, v8_str("new f2()" ), true).ToLocalChecked(); |
| 12846 | } |
| 12847 | |
| 12848 | // These handlers instantiate a function the embedder considers safe in some |
| 12849 | // cases (e.g. "building object wrappers"), but those functions themselves were |
| 12850 | // not explicitly marked as side-effect-free. |
| 12851 | static void DefaultConstructHandler( |
| 12852 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 12853 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 12854 | v8::Context::Scope context_scope(context); |
| 12855 | v8::MaybeLocal<v8::Object> instance = Function::New(context, EmptyHandler) |
| 12856 | .ToLocalChecked() |
| 12857 | ->NewInstance(context, 0, nullptr); |
| 12858 | USE(instance); |
| 12859 | } |
| 12860 | |
| 12861 | static void NoSideEffectConstructHandler( |
| 12862 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 12863 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 12864 | v8::Context::Scope context_scope(context); |
| 12865 | v8::MaybeLocal<v8::Object> instance = |
| 12866 | Function::New(context, EmptyHandler) |
| 12867 | .ToLocalChecked() |
| 12868 | ->NewInstanceWithSideEffectType(context, 0, nullptr, |
| 12869 | v8::SideEffectType::kHasNoSideEffect); |
| 12870 | USE(instance); |
| 12871 | } |
| 12872 | |
| 12873 | static void NoSideEffectAndSideEffectConstructHandler( |
| 12874 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 12875 | v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
| 12876 | v8::Context::Scope context_scope(context); |
| 12877 | // Constructs an instance in a side-effect-free way, followed by another with |
| 12878 | // side effects. |
| 12879 | v8::MaybeLocal<v8::Object> instance = |
| 12880 | Function::New(context, EmptyHandler) |
| 12881 | .ToLocalChecked() |
| 12882 | ->NewInstanceWithSideEffectType(context, 0, nullptr, |
| 12883 | v8::SideEffectType::kHasNoSideEffect); |
| 12884 | v8::MaybeLocal<v8::Object> instance2 = Function::New(context, EmptyHandler) |
| 12885 | .ToLocalChecked() |
| 12886 | ->NewInstance(context, 0, nullptr); |
| 12887 | USE(instance); |
| 12888 | USE(instance2); |
| 12889 | } |
| 12890 | |
| 12891 | TEST(FunctionNewInstanceHasNoSideEffect) { |
| 12892 | v8::Isolate* isolate = CcTest::isolate(); |
| 12893 | v8::HandleScope scope(isolate); |
| 12894 | LocalContext context; |
| 12895 | |
| 12896 | // A whitelisted function that creates a new object with both side-effect |
| 12897 | // free/full instantiations. Should throw. |
| 12898 | Local<Function> func0 = |
| 12899 | Function::New(context.local(), NoSideEffectAndSideEffectConstructHandler, |
| 12900 | Local<Value>(), 0, v8::ConstructorBehavior::kAllow, |
| 12901 | v8::SideEffectType::kHasNoSideEffect) |
| 12902 | .ToLocalChecked(); |
| 12903 | CHECK(context->Global()->Set(context.local(), v8_str("f" ), func0).FromJust()); |
| 12904 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()" ), true).IsEmpty()); |
| 12905 | |
| 12906 | // A whitelisted function that creates a new object. Should throw. |
| 12907 | Local<Function> func = |
| 12908 | Function::New(context.local(), DefaultConstructHandler, Local<Value>(), 0, |
| 12909 | v8::ConstructorBehavior::kAllow, |
| 12910 | v8::SideEffectType::kHasNoSideEffect) |
| 12911 | .ToLocalChecked(); |
| 12912 | CHECK(context->Global()->Set(context.local(), v8_str("f" ), func).FromJust()); |
| 12913 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f()" ), true).IsEmpty()); |
| 12914 | |
| 12915 | // A whitelisted function that creates a new object with explicit intent to |
| 12916 | // have no side-effects (e.g. building an "object wrapper"). Should not throw. |
| 12917 | Local<Function> func2 = |
| 12918 | Function::New(context.local(), NoSideEffectConstructHandler, |
| 12919 | Local<Value>(), 0, v8::ConstructorBehavior::kAllow, |
| 12920 | v8::SideEffectType::kHasNoSideEffect) |
| 12921 | .ToLocalChecked(); |
| 12922 | CHECK( |
| 12923 | context->Global()->Set(context.local(), v8_str("f2" ), func2).FromJust()); |
| 12924 | v8::debug::EvaluateGlobal(isolate, v8_str("f2()" ), true).ToLocalChecked(); |
| 12925 | |
| 12926 | // Check that side effect skipping did not leak outside to future evaluations. |
| 12927 | Local<Function> func3 = |
| 12928 | Function::New(context.local(), EmptyHandler).ToLocalChecked(); |
| 12929 | CHECK( |
| 12930 | context->Global()->Set(context.local(), v8_str("f3" ), func3).FromJust()); |
| 12931 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("f3()" ), true).IsEmpty()); |
| 12932 | |
| 12933 | // Check that using side effect free NewInstance works in normal evaluation |
| 12934 | // (without throwOnSideEffect). |
| 12935 | v8::debug::EvaluateGlobal(isolate, v8_str("f2()" ), false).ToLocalChecked(); |
| 12936 | } |
| 12937 | |
| 12938 | TEST(CallHandlerAsFunctionHasNoSideEffectNotSupported) { |
| 12939 | v8::Isolate* isolate = CcTest::isolate(); |
| 12940 | v8::HandleScope scope(isolate); |
| 12941 | LocalContext context; |
| 12942 | |
| 12943 | // Object template with call as function handler. |
| 12944 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 12945 | templ->SetCallAsFunctionHandler(EmptyHandler); |
| 12946 | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); |
| 12947 | CHECK(context->Global()->Set(context.local(), v8_str("obj" ), obj).FromJust()); |
| 12948 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj()" ), true).IsEmpty()); |
| 12949 | |
| 12950 | // Side-effect-free version is not supported. |
| 12951 | i::FunctionTemplateInfo cons = i::FunctionTemplateInfo::cast( |
| 12952 | v8::Utils::OpenHandle(*templ)->constructor()); |
| 12953 | i::Heap* heap = reinterpret_cast<i::Isolate*>(isolate)->heap(); |
| 12954 | i::CallHandlerInfo handler_info = |
| 12955 | i::CallHandlerInfo::cast(cons->GetInstanceCallHandler()); |
| 12956 | CHECK(!handler_info->IsSideEffectFreeCallHandlerInfo()); |
| 12957 | handler_info->set_map( |
| 12958 | i::ReadOnlyRoots(heap).side_effect_free_call_handler_info_map()); |
| 12959 | CHECK(v8::debug::EvaluateGlobal(isolate, v8_str("obj()" ), true).IsEmpty()); |
| 12960 | } |
| 12961 | |
| 12962 | static void IsConstructHandler( |
| 12963 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12964 | ApiTestFuzzer::Fuzz(); |
| 12965 | args.GetReturnValue().Set(args.IsConstructCall()); |
| 12966 | } |
| 12967 | |
| 12968 | |
| 12969 | THREADED_TEST(IsConstructCall) { |
| 12970 | v8::Isolate* isolate = CcTest::isolate(); |
| 12971 | v8::HandleScope scope(isolate); |
| 12972 | |
| 12973 | // Function template with call handler. |
| 12974 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 12975 | templ->SetCallHandler(IsConstructHandler); |
| 12976 | |
| 12977 | LocalContext context; |
| 12978 | |
| 12979 | CHECK(context->Global() |
| 12980 | ->Set(context.local(), v8_str("f" ), |
| 12981 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 12982 | .FromJust()); |
| 12983 | Local<Value> value = v8_compile("f()" )->Run(context.local()).ToLocalChecked(); |
| 12984 | CHECK(!value->BooleanValue(isolate)); |
| 12985 | value = v8_compile("new f()" )->Run(context.local()).ToLocalChecked(); |
| 12986 | CHECK(value->BooleanValue(isolate)); |
| 12987 | } |
| 12988 | |
| 12989 | static void NewTargetHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 12990 | ApiTestFuzzer::Fuzz(); |
| 12991 | args.GetReturnValue().Set(args.NewTarget()); |
| 12992 | } |
| 12993 | |
| 12994 | THREADED_TEST(NewTargetHandler) { |
| 12995 | v8::Isolate* isolate = CcTest::isolate(); |
| 12996 | v8::HandleScope scope(isolate); |
| 12997 | |
| 12998 | // Function template with call handler. |
| 12999 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 13000 | templ->SetCallHandler(NewTargetHandler); |
| 13001 | |
| 13002 | LocalContext context; |
| 13003 | |
| 13004 | Local<Function> function = |
| 13005 | templ->GetFunction(context.local()).ToLocalChecked(); |
| 13006 | CHECK(context->Global() |
| 13007 | ->Set(context.local(), v8_str("f" ), function) |
| 13008 | .FromJust()); |
| 13009 | Local<Value> value = CompileRun("f()" ); |
| 13010 | CHECK(value->IsUndefined()); |
| 13011 | value = CompileRun("new f()" ); |
| 13012 | CHECK(value->IsFunction()); |
| 13013 | CHECK(value == function); |
| 13014 | Local<Value> subclass = CompileRun("var g = class extends f { }; g" ); |
| 13015 | CHECK(subclass->IsFunction()); |
| 13016 | value = CompileRun("new g()" ); |
| 13017 | CHECK(value->IsFunction()); |
| 13018 | CHECK(value == subclass); |
| 13019 | value = CompileRun("Reflect.construct(f, [], Array)" ); |
| 13020 | CHECK(value->IsFunction()); |
| 13021 | CHECK(value == |
| 13022 | context->Global() |
| 13023 | ->Get(context.local(), v8_str("Array" )) |
| 13024 | .ToLocalChecked()); |
| 13025 | } |
| 13026 | |
| 13027 | THREADED_TEST(ObjectProtoToString) { |
| 13028 | v8::Isolate* isolate = CcTest::isolate(); |
| 13029 | v8::HandleScope scope(isolate); |
| 13030 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 13031 | templ->SetClassName(v8_str("MyClass" )); |
| 13032 | |
| 13033 | LocalContext context; |
| 13034 | |
| 13035 | Local<String> customized_tostring = v8_str("customized toString" ); |
| 13036 | |
| 13037 | // Replace Object.prototype.toString |
| 13038 | v8_compile( |
| 13039 | "Object.prototype.toString = function() {" |
| 13040 | " return 'customized toString';" |
| 13041 | "}" ) |
| 13042 | ->Run(context.local()) |
| 13043 | .ToLocalChecked(); |
| 13044 | |
| 13045 | // Normal ToString call should call replaced Object.prototype.toString |
| 13046 | Local<v8::Object> instance = templ->GetFunction(context.local()) |
| 13047 | .ToLocalChecked() |
| 13048 | ->NewInstance(context.local()) |
| 13049 | .ToLocalChecked(); |
| 13050 | Local<String> value = instance->ToString(context.local()).ToLocalChecked(); |
| 13051 | CHECK(value->IsString() && |
| 13052 | value->Equals(context.local(), customized_tostring).FromJust()); |
| 13053 | |
| 13054 | // ObjectProtoToString should not call replace toString function. |
| 13055 | value = instance->ObjectProtoToString(context.local()).ToLocalChecked(); |
| 13056 | CHECK(value->IsString() && |
| 13057 | value->Equals(context.local(), v8_str("[object MyClass]" )).FromJust()); |
| 13058 | |
| 13059 | // Check global |
| 13060 | value = |
| 13061 | context->Global()->ObjectProtoToString(context.local()).ToLocalChecked(); |
| 13062 | CHECK(value->IsString() && |
| 13063 | value->Equals(context.local(), v8_str("[object Object]" )).FromJust()); |
| 13064 | |
| 13065 | // Check ordinary object |
| 13066 | Local<Value> object = |
| 13067 | v8_compile("new Object()" )->Run(context.local()).ToLocalChecked(); |
| 13068 | value = object.As<v8::Object>() |
| 13069 | ->ObjectProtoToString(context.local()) |
| 13070 | .ToLocalChecked(); |
| 13071 | CHECK(value->IsString() && |
| 13072 | value->Equals(context.local(), v8_str("[object Object]" )).FromJust()); |
| 13073 | } |
| 13074 | |
| 13075 | |
| 13076 | TEST(ObjectProtoToStringES6) { |
| 13077 | LocalContext context; |
| 13078 | v8::Isolate* isolate = CcTest::isolate(); |
| 13079 | v8::HandleScope scope(isolate); |
| 13080 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 13081 | templ->SetClassName(v8_str("MyClass" )); |
| 13082 | |
| 13083 | Local<String> customized_tostring = v8_str("customized toString" ); |
| 13084 | |
| 13085 | // Replace Object.prototype.toString |
| 13086 | CompileRun( |
| 13087 | "Object.prototype.toString = function() {" |
| 13088 | " return 'customized toString';" |
| 13089 | "}" ); |
| 13090 | |
| 13091 | // Normal ToString call should call replaced Object.prototype.toString |
| 13092 | Local<v8::Object> instance = templ->GetFunction(context.local()) |
| 13093 | .ToLocalChecked() |
| 13094 | ->NewInstance(context.local()) |
| 13095 | .ToLocalChecked(); |
| 13096 | Local<String> value = instance->ToString(context.local()).ToLocalChecked(); |
| 13097 | CHECK(value->IsString() && |
| 13098 | value->Equals(context.local(), customized_tostring).FromJust()); |
| 13099 | |
| 13100 | // ObjectProtoToString should not call replace toString function. |
| 13101 | value = instance->ObjectProtoToString(context.local()).ToLocalChecked(); |
| 13102 | CHECK(value->IsString() && |
| 13103 | value->Equals(context.local(), v8_str("[object MyClass]" )).FromJust()); |
| 13104 | |
| 13105 | // Check global |
| 13106 | value = |
| 13107 | context->Global()->ObjectProtoToString(context.local()).ToLocalChecked(); |
| 13108 | CHECK(value->IsString() && |
| 13109 | value->Equals(context.local(), v8_str("[object Object]" )).FromJust()); |
| 13110 | |
| 13111 | // Check ordinary object |
| 13112 | Local<Value> object = CompileRun("new Object()" ); |
| 13113 | value = object.As<v8::Object>() |
| 13114 | ->ObjectProtoToString(context.local()) |
| 13115 | .ToLocalChecked(); |
| 13116 | CHECK(value->IsString() && |
| 13117 | value->Equals(context.local(), v8_str("[object Object]" )).FromJust()); |
| 13118 | |
| 13119 | // Check that ES6 semantics using @@toStringTag work |
| 13120 | Local<v8::Symbol> toStringTag = v8::Symbol::GetToStringTag(isolate); |
| 13121 | |
| 13122 | #define TEST_TOSTRINGTAG(type, tag, expected) \ |
| 13123 | do { \ |
| 13124 | object = CompileRun("new " #type "()"); \ |
| 13125 | CHECK(object.As<v8::Object>() \ |
| 13126 | ->Set(context.local(), toStringTag, v8_str(#tag)) \ |
| 13127 | .FromJust()); \ |
| 13128 | value = object.As<v8::Object>() \ |
| 13129 | ->ObjectProtoToString(context.local()) \ |
| 13130 | .ToLocalChecked(); \ |
| 13131 | CHECK(value->IsString() && \ |
| 13132 | value->Equals(context.local(), v8_str("[object " #expected "]")) \ |
| 13133 | .FromJust()); \ |
| 13134 | } while (false) |
| 13135 | |
| 13136 | TEST_TOSTRINGTAG(Array, Object, Object); |
| 13137 | TEST_TOSTRINGTAG(Object, Arguments, Arguments); |
| 13138 | TEST_TOSTRINGTAG(Object, Array, Array); |
| 13139 | TEST_TOSTRINGTAG(Object, Boolean, Boolean); |
| 13140 | TEST_TOSTRINGTAG(Object, Date, Date); |
| 13141 | TEST_TOSTRINGTAG(Object, Error, Error); |
| 13142 | TEST_TOSTRINGTAG(Object, Function, Function); |
| 13143 | TEST_TOSTRINGTAG(Object, Number, Number); |
| 13144 | TEST_TOSTRINGTAG(Object, RegExp, RegExp); |
| 13145 | TEST_TOSTRINGTAG(Object, String, String); |
| 13146 | TEST_TOSTRINGTAG(Object, Foo, Foo); |
| 13147 | |
| 13148 | #undef TEST_TOSTRINGTAG |
| 13149 | |
| 13150 | Local<v8::RegExp> valueRegExp = |
| 13151 | v8::RegExp::New(context.local(), v8_str("^$" ), v8::RegExp::kNone) |
| 13152 | .ToLocalChecked(); |
| 13153 | Local<Value> valueNumber = v8_num(123); |
| 13154 | Local<v8::Symbol> valueSymbol = v8_symbol("TestSymbol" ); |
| 13155 | Local<v8::Function> valueFunction = |
| 13156 | CompileRun("(function fn() {})" ).As<v8::Function>(); |
| 13157 | Local<v8::Object> valueObject = v8::Object::New(v8::Isolate::GetCurrent()); |
| 13158 | Local<v8::Primitive> valueNull = v8::Null(v8::Isolate::GetCurrent()); |
| 13159 | Local<v8::Primitive> valueUndef = v8::Undefined(v8::Isolate::GetCurrent()); |
| 13160 | |
| 13161 | #define TEST_TOSTRINGTAG(type, tagValue, expected) \ |
| 13162 | do { \ |
| 13163 | object = CompileRun("new " #type "()"); \ |
| 13164 | CHECK(object.As<v8::Object>() \ |
| 13165 | ->Set(context.local(), toStringTag, tagValue) \ |
| 13166 | .FromJust()); \ |
| 13167 | value = object.As<v8::Object>() \ |
| 13168 | ->ObjectProtoToString(context.local()) \ |
| 13169 | .ToLocalChecked(); \ |
| 13170 | CHECK(value->IsString() && \ |
| 13171 | value->Equals(context.local(), v8_str("[object " #expected "]")) \ |
| 13172 | .FromJust()); \ |
| 13173 | } while (false) |
| 13174 | |
| 13175 | #define TEST_TOSTRINGTAG_TYPES(tagValue) \ |
| 13176 | TEST_TOSTRINGTAG(Array, tagValue, Array); \ |
| 13177 | TEST_TOSTRINGTAG(Object, tagValue, Object); \ |
| 13178 | TEST_TOSTRINGTAG(Function, tagValue, Function); \ |
| 13179 | TEST_TOSTRINGTAG(Date, tagValue, Date); \ |
| 13180 | TEST_TOSTRINGTAG(RegExp, tagValue, RegExp); \ |
| 13181 | TEST_TOSTRINGTAG(Error, tagValue, Error); \ |
| 13182 | |
| 13183 | // Test non-String-valued @@toStringTag |
| 13184 | TEST_TOSTRINGTAG_TYPES(valueRegExp); |
| 13185 | TEST_TOSTRINGTAG_TYPES(valueNumber); |
| 13186 | TEST_TOSTRINGTAG_TYPES(valueSymbol); |
| 13187 | TEST_TOSTRINGTAG_TYPES(valueFunction); |
| 13188 | TEST_TOSTRINGTAG_TYPES(valueObject); |
| 13189 | TEST_TOSTRINGTAG_TYPES(valueNull); |
| 13190 | TEST_TOSTRINGTAG_TYPES(valueUndef); |
| 13191 | |
| 13192 | #undef TEST_TOSTRINGTAG |
| 13193 | #undef TEST_TOSTRINGTAG_TYPES |
| 13194 | |
| 13195 | // @@toStringTag getter throws |
| 13196 | Local<Value> obj = v8::Object::New(isolate); |
| 13197 | obj.As<v8::Object>() |
| 13198 | ->SetAccessor(context.local(), toStringTag, ThrowingSymbolAccessorGetter) |
| 13199 | .FromJust(); |
| 13200 | { |
| 13201 | TryCatch try_catch(isolate); |
| 13202 | CHECK(obj.As<v8::Object>()->ObjectProtoToString(context.local()).IsEmpty()); |
| 13203 | CHECK(try_catch.HasCaught()); |
| 13204 | } |
| 13205 | |
| 13206 | // @@toStringTag getter does not throw |
| 13207 | obj = v8::Object::New(isolate); |
| 13208 | obj.As<v8::Object>() |
| 13209 | ->SetAccessor(context.local(), toStringTag, |
| 13210 | SymbolAccessorGetterReturnsDefault, nullptr, v8_str("Test" )) |
| 13211 | .FromJust(); |
| 13212 | { |
| 13213 | TryCatch try_catch(isolate); |
| 13214 | value = obj.As<v8::Object>() |
| 13215 | ->ObjectProtoToString(context.local()) |
| 13216 | .ToLocalChecked(); |
| 13217 | CHECK(value->IsString() && |
| 13218 | value->Equals(context.local(), v8_str("[object Test]" )).FromJust()); |
| 13219 | CHECK(!try_catch.HasCaught()); |
| 13220 | } |
| 13221 | |
| 13222 | // JS @@toStringTag value |
| 13223 | obj = CompileRun("obj = {}; obj[Symbol.toStringTag] = 'Test'; obj" ); |
| 13224 | { |
| 13225 | TryCatch try_catch(isolate); |
| 13226 | value = obj.As<v8::Object>() |
| 13227 | ->ObjectProtoToString(context.local()) |
| 13228 | .ToLocalChecked(); |
| 13229 | CHECK(value->IsString() && |
| 13230 | value->Equals(context.local(), v8_str("[object Test]" )).FromJust()); |
| 13231 | CHECK(!try_catch.HasCaught()); |
| 13232 | } |
| 13233 | |
| 13234 | // JS @@toStringTag getter throws |
| 13235 | obj = CompileRun( |
| 13236 | "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {" |
| 13237 | " get: function() { throw 'Test'; }" |
| 13238 | "}); obj" ); |
| 13239 | { |
| 13240 | TryCatch try_catch(isolate); |
| 13241 | CHECK(obj.As<v8::Object>()->ObjectProtoToString(context.local()).IsEmpty()); |
| 13242 | CHECK(try_catch.HasCaught()); |
| 13243 | } |
| 13244 | |
| 13245 | // JS @@toStringTag getter does not throw |
| 13246 | obj = CompileRun( |
| 13247 | "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {" |
| 13248 | " get: function() { return 'Test'; }" |
| 13249 | "}); obj" ); |
| 13250 | { |
| 13251 | TryCatch try_catch(isolate); |
| 13252 | value = obj.As<v8::Object>() |
| 13253 | ->ObjectProtoToString(context.local()) |
| 13254 | .ToLocalChecked(); |
| 13255 | CHECK(value->IsString() && |
| 13256 | value->Equals(context.local(), v8_str("[object Test]" )).FromJust()); |
| 13257 | CHECK(!try_catch.HasCaught()); |
| 13258 | } |
| 13259 | } |
| 13260 | |
| 13261 | |
| 13262 | THREADED_TEST(ObjectGetConstructorName) { |
| 13263 | v8::Isolate* isolate = CcTest::isolate(); |
| 13264 | LocalContext context; |
| 13265 | v8::HandleScope scope(isolate); |
| 13266 | v8_compile( |
| 13267 | "function Parent() {};" |
| 13268 | "function Child() {};" |
| 13269 | "Child.prototype = new Parent();" |
| 13270 | "Child.prototype.constructor = Child;" |
| 13271 | "var outer = { inner: (0, function() { }) };" |
| 13272 | "var p = new Parent();" |
| 13273 | "var c = new Child();" |
| 13274 | "var x = new outer.inner();" |
| 13275 | "var proto = Child.prototype;" ) |
| 13276 | ->Run(context.local()) |
| 13277 | .ToLocalChecked(); |
| 13278 | |
| 13279 | Local<v8::Value> p = |
| 13280 | context->Global()->Get(context.local(), v8_str("p" )).ToLocalChecked(); |
| 13281 | CHECK(p->IsObject() && |
| 13282 | p->ToObject(context.local()) |
| 13283 | .ToLocalChecked() |
| 13284 | ->GetConstructorName() |
| 13285 | ->Equals(context.local(), v8_str("Parent" )) |
| 13286 | .FromJust()); |
| 13287 | |
| 13288 | Local<v8::Value> c = |
| 13289 | context->Global()->Get(context.local(), v8_str("c" )).ToLocalChecked(); |
| 13290 | CHECK(c->IsObject() && |
| 13291 | c->ToObject(context.local()) |
| 13292 | .ToLocalChecked() |
| 13293 | ->GetConstructorName() |
| 13294 | ->Equals(context.local(), v8_str("Child" )) |
| 13295 | .FromJust()); |
| 13296 | |
| 13297 | Local<v8::Value> x = |
| 13298 | context->Global()->Get(context.local(), v8_str("x" )).ToLocalChecked(); |
| 13299 | CHECK(x->IsObject() && |
| 13300 | x->ToObject(context.local()) |
| 13301 | .ToLocalChecked() |
| 13302 | ->GetConstructorName() |
| 13303 | ->Equals(context.local(), v8_str("outer.inner" )) |
| 13304 | .FromJust()); |
| 13305 | |
| 13306 | Local<v8::Value> child_prototype = |
| 13307 | context->Global()->Get(context.local(), v8_str("proto" )).ToLocalChecked(); |
| 13308 | CHECK(child_prototype->IsObject() && |
| 13309 | child_prototype->ToObject(context.local()) |
| 13310 | .ToLocalChecked() |
| 13311 | ->GetConstructorName() |
| 13312 | ->Equals(context.local(), v8_str("Parent" )) |
| 13313 | .FromJust()); |
| 13314 | } |
| 13315 | |
| 13316 | |
| 13317 | THREADED_TEST(SubclassGetConstructorName) { |
| 13318 | v8::Isolate* isolate = CcTest::isolate(); |
| 13319 | LocalContext context; |
| 13320 | v8::HandleScope scope(isolate); |
| 13321 | v8_compile( |
| 13322 | "\"use strict\";" |
| 13323 | "class Parent {}" |
| 13324 | "class Child extends Parent {}" |
| 13325 | "var p = new Parent();" |
| 13326 | "var c = new Child();" ) |
| 13327 | ->Run(context.local()) |
| 13328 | .ToLocalChecked(); |
| 13329 | |
| 13330 | Local<v8::Value> p = |
| 13331 | context->Global()->Get(context.local(), v8_str("p" )).ToLocalChecked(); |
| 13332 | CHECK(p->IsObject() && |
| 13333 | p->ToObject(context.local()) |
| 13334 | .ToLocalChecked() |
| 13335 | ->GetConstructorName() |
| 13336 | ->Equals(context.local(), v8_str("Parent" )) |
| 13337 | .FromJust()); |
| 13338 | |
| 13339 | Local<v8::Value> c = |
| 13340 | context->Global()->Get(context.local(), v8_str("c" )).ToLocalChecked(); |
| 13341 | CHECK(c->IsObject() && |
| 13342 | c->ToObject(context.local()) |
| 13343 | .ToLocalChecked() |
| 13344 | ->GetConstructorName() |
| 13345 | ->Equals(context.local(), v8_str("Child" )) |
| 13346 | .FromJust()); |
| 13347 | } |
| 13348 | |
| 13349 | |
| 13350 | bool ApiTestFuzzer::fuzzing_ = false; |
| 13351 | v8::base::Semaphore ApiTestFuzzer::all_tests_done_(0); |
| 13352 | int ApiTestFuzzer::active_tests_; |
| 13353 | int ApiTestFuzzer::tests_being_run_; |
| 13354 | int ApiTestFuzzer::current_; |
| 13355 | |
| 13356 | |
| 13357 | // We are in a callback and want to switch to another thread (if we |
| 13358 | // are currently running the thread fuzzing test). |
| 13359 | void ApiTestFuzzer::Fuzz() { |
| 13360 | if (!fuzzing_) return; |
| 13361 | ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; |
| 13362 | test->ContextSwitch(); |
| 13363 | } |
| 13364 | |
| 13365 | |
| 13366 | // Let the next thread go. Since it is also waiting on the V8 lock it may |
| 13367 | // not start immediately. |
| 13368 | bool ApiTestFuzzer::NextThread() { |
| 13369 | int test_position = GetNextTestNumber(); |
| 13370 | const char* test_name = RegisterThreadedTest::nth(current_)->name(); |
| 13371 | if (test_position == current_) { |
| 13372 | if (kLogThreading) |
| 13373 | printf("Stay with %s\n" , test_name); |
| 13374 | return false; |
| 13375 | } |
| 13376 | if (kLogThreading) { |
| 13377 | printf("Switch from %s to %s\n" , |
| 13378 | test_name, |
| 13379 | RegisterThreadedTest::nth(test_position)->name()); |
| 13380 | } |
| 13381 | current_ = test_position; |
| 13382 | RegisterThreadedTest::nth(current_)->fuzzer_->gate_.Signal(); |
| 13383 | return true; |
| 13384 | } |
| 13385 | |
| 13386 | |
| 13387 | void ApiTestFuzzer::Run() { |
| 13388 | // When it is our turn... |
| 13389 | gate_.Wait(); |
| 13390 | { |
| 13391 | // ... get the V8 lock and start running the test. |
| 13392 | v8::Locker locker(CcTest::isolate()); |
| 13393 | CallTest(); |
| 13394 | } |
| 13395 | // This test finished. |
| 13396 | active_ = false; |
| 13397 | active_tests_--; |
| 13398 | // If it was the last then signal that fact. |
| 13399 | if (active_tests_ == 0) { |
| 13400 | all_tests_done_.Signal(); |
| 13401 | } else { |
| 13402 | // Otherwise select a new test and start that. |
| 13403 | NextThread(); |
| 13404 | } |
| 13405 | } |
| 13406 | |
| 13407 | |
| 13408 | static unsigned linear_congruential_generator; |
| 13409 | |
| 13410 | |
| 13411 | void ApiTestFuzzer::SetUp(PartOfTest part) { |
| 13412 | linear_congruential_generator = i::FLAG_testing_prng_seed; |
| 13413 | fuzzing_ = true; |
| 13414 | int count = RegisterThreadedTest::count(); |
| 13415 | int start = count * part / (LAST_PART + 1); |
| 13416 | int end = (count * (part + 1) / (LAST_PART + 1)) - 1; |
| 13417 | active_tests_ = tests_being_run_ = end - start + 1; |
| 13418 | for (int i = 0; i < tests_being_run_; i++) { |
| 13419 | RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); |
| 13420 | } |
| 13421 | for (int i = 0; i < active_tests_; i++) { |
| 13422 | RegisterThreadedTest::nth(i)->fuzzer_->Start(); |
| 13423 | } |
| 13424 | } |
| 13425 | |
| 13426 | |
| 13427 | static void CallTestNumber(int test_number) { |
| 13428 | (RegisterThreadedTest::nth(test_number)->callback())(); |
| 13429 | } |
| 13430 | |
| 13431 | |
| 13432 | void ApiTestFuzzer::RunAllTests() { |
| 13433 | // Set off the first test. |
| 13434 | current_ = -1; |
| 13435 | NextThread(); |
| 13436 | // Wait till they are all done. |
| 13437 | all_tests_done_.Wait(); |
| 13438 | } |
| 13439 | |
| 13440 | |
| 13441 | int ApiTestFuzzer::GetNextTestNumber() { |
| 13442 | int next_test; |
| 13443 | do { |
| 13444 | next_test = (linear_congruential_generator >> 16) % tests_being_run_; |
| 13445 | linear_congruential_generator *= 1664525u; |
| 13446 | linear_congruential_generator += 1013904223u; |
| 13447 | } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); |
| 13448 | return next_test; |
| 13449 | } |
| 13450 | |
| 13451 | |
| 13452 | void ApiTestFuzzer::ContextSwitch() { |
| 13453 | // If the new thread is the same as the current thread there is nothing to do. |
| 13454 | if (NextThread()) { |
| 13455 | // Now it can start. |
| 13456 | v8::Unlocker unlocker(CcTest::isolate()); |
| 13457 | // Wait till someone starts us again. |
| 13458 | gate_.Wait(); |
| 13459 | // And we're off. |
| 13460 | } |
| 13461 | } |
| 13462 | |
| 13463 | |
| 13464 | void ApiTestFuzzer::TearDown() { |
| 13465 | fuzzing_ = false; |
| 13466 | for (int i = 0; i < RegisterThreadedTest::count(); i++) { |
| 13467 | ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; |
| 13468 | if (fuzzer != nullptr) fuzzer->Join(); |
| 13469 | } |
| 13470 | } |
| 13471 | |
| 13472 | void ApiTestFuzzer::CallTest() { |
| 13473 | v8::Isolate::Scope scope(CcTest::isolate()); |
| 13474 | if (kLogThreading) |
| 13475 | printf("Start test %s #%d\n" , |
| 13476 | RegisterThreadedTest::nth(test_number_)->name(), test_number_); |
| 13477 | CallTestNumber(test_number_); |
| 13478 | if (kLogThreading) |
| 13479 | printf("End test %s #%d\n" , RegisterThreadedTest::nth(test_number_)->name(), |
| 13480 | test_number_); |
| 13481 | } |
| 13482 | |
| 13483 | #define THREADING_TEST(INDEX, NAME) \ |
| 13484 | TEST(Threading##INDEX) { \ |
| 13485 | ApiTestFuzzer::SetUp(ApiTestFuzzer::NAME); \ |
| 13486 | ApiTestFuzzer::RunAllTests(); \ |
| 13487 | ApiTestFuzzer::TearDown(); \ |
| 13488 | } |
| 13489 | |
| 13490 | THREADING_TEST(1, FIRST_PART) |
| 13491 | THREADING_TEST(2, SECOND_PART) |
| 13492 | THREADING_TEST(3, THIRD_PART) |
| 13493 | THREADING_TEST(4, FOURTH_PART) |
| 13494 | THREADING_TEST(5, FIFTH_PART) |
| 13495 | THREADING_TEST(6, SIXTH_PART) |
| 13496 | THREADING_TEST(7, SEVENTH_PART) |
| 13497 | THREADING_TEST(8, EIGHTH_PART) |
| 13498 | |
| 13499 | #undef THREADING_TEST |
| 13500 | |
| 13501 | static void ThrowInJS(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 13502 | v8::Isolate* isolate = args.GetIsolate(); |
| 13503 | CHECK(v8::Locker::IsLocked(isolate)); |
| 13504 | ApiTestFuzzer::Fuzz(); |
| 13505 | v8::Unlocker unlocker(isolate); |
| 13506 | const char* code = "throw 7;" ; |
| 13507 | { |
| 13508 | v8::Locker nested_locker(isolate); |
| 13509 | v8::HandleScope scope(isolate); |
| 13510 | v8::Local<Value> exception; |
| 13511 | { |
| 13512 | v8::TryCatch try_catch(isolate); |
| 13513 | v8::Local<Value> value = CompileRun(code); |
| 13514 | CHECK(value.IsEmpty()); |
| 13515 | CHECK(try_catch.HasCaught()); |
| 13516 | // Make sure to wrap the exception in a new handle because |
| 13517 | // the handle returned from the TryCatch is destroyed |
| 13518 | // when the TryCatch is destroyed. |
| 13519 | exception = Local<Value>::New(isolate, try_catch.Exception()); |
| 13520 | } |
| 13521 | args.GetIsolate()->ThrowException(exception); |
| 13522 | } |
| 13523 | } |
| 13524 | |
| 13525 | |
| 13526 | static void ThrowInJSNoCatch(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 13527 | CHECK(v8::Locker::IsLocked(CcTest::isolate())); |
| 13528 | ApiTestFuzzer::Fuzz(); |
| 13529 | v8::Unlocker unlocker(CcTest::isolate()); |
| 13530 | const char* code = "throw 7;" ; |
| 13531 | { |
| 13532 | v8::Locker nested_locker(CcTest::isolate()); |
| 13533 | v8::HandleScope scope(args.GetIsolate()); |
| 13534 | v8::Local<Value> value = CompileRun(code); |
| 13535 | CHECK(value.IsEmpty()); |
| 13536 | args.GetReturnValue().Set(v8_str("foo" )); |
| 13537 | } |
| 13538 | } |
| 13539 | |
| 13540 | |
| 13541 | // These are locking tests that don't need to be run again |
| 13542 | // as part of the locking aggregation tests. |
| 13543 | TEST(NestedLockers) { |
| 13544 | v8::Isolate* isolate = CcTest::isolate(); |
| 13545 | v8::Locker locker(isolate); |
| 13546 | CHECK(v8::Locker::IsLocked(isolate)); |
| 13547 | LocalContext env; |
| 13548 | v8::HandleScope scope(env->GetIsolate()); |
| 13549 | Local<v8::FunctionTemplate> fun_templ = |
| 13550 | v8::FunctionTemplate::New(isolate, ThrowInJS); |
| 13551 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 13552 | CHECK(env->Global()->Set(env.local(), v8_str("throw_in_js" ), fun).FromJust()); |
| 13553 | Local<Script> script = v8_compile("(function () {" |
| 13554 | " try {" |
| 13555 | " throw_in_js();" |
| 13556 | " return 42;" |
| 13557 | " } catch (e) {" |
| 13558 | " return e * 13;" |
| 13559 | " }" |
| 13560 | "})();" ); |
| 13561 | CHECK_EQ(91, script->Run(env.local()) |
| 13562 | .ToLocalChecked() |
| 13563 | ->Int32Value(env.local()) |
| 13564 | .FromJust()); |
| 13565 | } |
| 13566 | |
| 13567 | |
| 13568 | // These are locking tests that don't need to be run again |
| 13569 | // as part of the locking aggregation tests. |
| 13570 | TEST(NestedLockersNoTryCatch) { |
| 13571 | v8::Locker locker(CcTest::isolate()); |
| 13572 | LocalContext env; |
| 13573 | v8::HandleScope scope(env->GetIsolate()); |
| 13574 | Local<v8::FunctionTemplate> fun_templ = |
| 13575 | v8::FunctionTemplate::New(env->GetIsolate(), ThrowInJSNoCatch); |
| 13576 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 13577 | CHECK(env->Global()->Set(env.local(), v8_str("throw_in_js" ), fun).FromJust()); |
| 13578 | Local<Script> script = v8_compile("(function () {" |
| 13579 | " try {" |
| 13580 | " throw_in_js();" |
| 13581 | " return 42;" |
| 13582 | " } catch (e) {" |
| 13583 | " return e * 13;" |
| 13584 | " }" |
| 13585 | "})();" ); |
| 13586 | CHECK_EQ(91, script->Run(env.local()) |
| 13587 | .ToLocalChecked() |
| 13588 | ->Int32Value(env.local()) |
| 13589 | .FromJust()); |
| 13590 | } |
| 13591 | |
| 13592 | |
| 13593 | THREADED_TEST(RecursiveLocking) { |
| 13594 | v8::Locker locker(CcTest::isolate()); |
| 13595 | { |
| 13596 | v8::Locker locker2(CcTest::isolate()); |
| 13597 | CHECK(v8::Locker::IsLocked(CcTest::isolate())); |
| 13598 | } |
| 13599 | } |
| 13600 | |
| 13601 | |
| 13602 | static void UnlockForAMoment(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 13603 | ApiTestFuzzer::Fuzz(); |
| 13604 | v8::Unlocker unlocker(CcTest::isolate()); |
| 13605 | } |
| 13606 | |
| 13607 | |
| 13608 | THREADED_TEST(LockUnlockLock) { |
| 13609 | { |
| 13610 | v8::Locker locker(CcTest::isolate()); |
| 13611 | v8::HandleScope scope(CcTest::isolate()); |
| 13612 | LocalContext env; |
| 13613 | Local<v8::FunctionTemplate> fun_templ = |
| 13614 | v8::FunctionTemplate::New(CcTest::isolate(), UnlockForAMoment); |
| 13615 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 13616 | CHECK(env->Global() |
| 13617 | ->Set(env.local(), v8_str("unlock_for_a_moment" ), fun) |
| 13618 | .FromJust()); |
| 13619 | Local<Script> script = v8_compile("(function () {" |
| 13620 | " unlock_for_a_moment();" |
| 13621 | " return 42;" |
| 13622 | "})();" ); |
| 13623 | CHECK_EQ(42, script->Run(env.local()) |
| 13624 | .ToLocalChecked() |
| 13625 | ->Int32Value(env.local()) |
| 13626 | .FromJust()); |
| 13627 | } |
| 13628 | { |
| 13629 | v8::Locker locker(CcTest::isolate()); |
| 13630 | v8::HandleScope scope(CcTest::isolate()); |
| 13631 | LocalContext env; |
| 13632 | Local<v8::FunctionTemplate> fun_templ = |
| 13633 | v8::FunctionTemplate::New(CcTest::isolate(), UnlockForAMoment); |
| 13634 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 13635 | CHECK(env->Global() |
| 13636 | ->Set(env.local(), v8_str("unlock_for_a_moment" ), fun) |
| 13637 | .FromJust()); |
| 13638 | Local<Script> script = v8_compile("(function () {" |
| 13639 | " unlock_for_a_moment();" |
| 13640 | " return 42;" |
| 13641 | "})();" ); |
| 13642 | CHECK_EQ(42, script->Run(env.local()) |
| 13643 | .ToLocalChecked() |
| 13644 | ->Int32Value(env.local()) |
| 13645 | .FromJust()); |
| 13646 | } |
| 13647 | } |
| 13648 | |
| 13649 | |
| 13650 | static int GetGlobalObjectsCount() { |
| 13651 | int count = 0; |
| 13652 | i::HeapIterator it(CcTest::heap()); |
| 13653 | for (i::HeapObject object = it.next(); !object.is_null(); |
| 13654 | object = it.next()) { |
| 13655 | if (object->IsJSGlobalObject()) { |
| 13656 | i::JSGlobalObject g = i::JSGlobalObject::cast(object); |
| 13657 | // Skip dummy global object. |
| 13658 | if (g->global_dictionary()->NumberOfElements() != 0) { |
| 13659 | count++; |
| 13660 | } |
| 13661 | } |
| 13662 | } |
| 13663 | return count; |
| 13664 | } |
| 13665 | |
| 13666 | |
| 13667 | static void CheckSurvivingGlobalObjectsCount(int expected) { |
| 13668 | // We need to collect all garbage twice to be sure that everything |
| 13669 | // has been collected. This is because inline caches are cleared in |
| 13670 | // the first garbage collection but some of the maps have already |
| 13671 | // been marked at that point. Therefore some of the maps are not |
| 13672 | // collected until the second garbage collection. |
| 13673 | CcTest::CollectAllGarbage(); |
| 13674 | CcTest::CollectAllGarbage(); |
| 13675 | int count = GetGlobalObjectsCount(); |
| 13676 | CHECK_EQ(expected, count); |
| 13677 | } |
| 13678 | |
| 13679 | |
| 13680 | TEST(DontLeakGlobalObjects) { |
| 13681 | // Regression test for issues 1139850 and 1174891. |
| 13682 | |
| 13683 | i::FLAG_expose_gc = true; |
| 13684 | v8::V8::Initialize(); |
| 13685 | |
| 13686 | for (int i = 0; i < 5; i++) { |
| 13687 | { v8::HandleScope scope(CcTest::isolate()); |
| 13688 | LocalContext context; |
| 13689 | } |
| 13690 | CcTest::isolate()->ContextDisposedNotification(); |
| 13691 | CheckSurvivingGlobalObjectsCount(0); |
| 13692 | |
| 13693 | { v8::HandleScope scope(CcTest::isolate()); |
| 13694 | LocalContext context; |
| 13695 | v8_compile("Date" )->Run(context.local()).ToLocalChecked(); |
| 13696 | } |
| 13697 | CcTest::isolate()->ContextDisposedNotification(); |
| 13698 | CheckSurvivingGlobalObjectsCount(0); |
| 13699 | |
| 13700 | { v8::HandleScope scope(CcTest::isolate()); |
| 13701 | LocalContext context; |
| 13702 | v8_compile("/aaa/" )->Run(context.local()).ToLocalChecked(); |
| 13703 | } |
| 13704 | CcTest::isolate()->ContextDisposedNotification(); |
| 13705 | CheckSurvivingGlobalObjectsCount(0); |
| 13706 | |
| 13707 | { v8::HandleScope scope(CcTest::isolate()); |
| 13708 | const char* extension_list[] = { "v8/gc" }; |
| 13709 | v8::ExtensionConfiguration extensions(1, extension_list); |
| 13710 | LocalContext context(&extensions); |
| 13711 | v8_compile("gc();" )->Run(context.local()).ToLocalChecked(); |
| 13712 | } |
| 13713 | CcTest::isolate()->ContextDisposedNotification(); |
| 13714 | CheckSurvivingGlobalObjectsCount(0); |
| 13715 | } |
| 13716 | } |
| 13717 | |
| 13718 | |
| 13719 | TEST(CopyablePersistent) { |
| 13720 | LocalContext context; |
| 13721 | v8::Isolate* isolate = context->GetIsolate(); |
| 13722 | i::GlobalHandles* globals = |
| 13723 | reinterpret_cast<i::Isolate*>(isolate)->global_handles(); |
| 13724 | size_t initial_handles = globals->handles_count(); |
| 13725 | typedef v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > |
| 13726 | CopyableObject; |
| 13727 | { |
| 13728 | CopyableObject handle1; |
| 13729 | { |
| 13730 | v8::HandleScope scope(isolate); |
| 13731 | handle1.Reset(isolate, v8::Object::New(isolate)); |
| 13732 | } |
| 13733 | CHECK_EQ(initial_handles + 1, globals->handles_count()); |
| 13734 | CopyableObject handle2; |
| 13735 | handle2 = handle1; |
| 13736 | CHECK(handle1 == handle2); |
| 13737 | CHECK_EQ(initial_handles + 2, globals->handles_count()); |
| 13738 | CopyableObject handle3(handle2); |
| 13739 | CHECK(handle1 == handle3); |
| 13740 | CHECK_EQ(initial_handles + 3, globals->handles_count()); |
| 13741 | } |
| 13742 | // Verify autodispose |
| 13743 | CHECK_EQ(initial_handles, globals->handles_count()); |
| 13744 | } |
| 13745 | |
| 13746 | |
| 13747 | static void WeakApiCallback( |
| 13748 | const v8::WeakCallbackInfo<Persistent<v8::Object>>& data) { |
| 13749 | data.GetParameter()->Reset(); |
| 13750 | delete data.GetParameter(); |
| 13751 | } |
| 13752 | |
| 13753 | |
| 13754 | TEST(WeakCallbackApi) { |
| 13755 | LocalContext context; |
| 13756 | v8::Isolate* isolate = context->GetIsolate(); |
| 13757 | i::GlobalHandles* globals = |
| 13758 | reinterpret_cast<i::Isolate*>(isolate)->global_handles(); |
| 13759 | size_t initial_handles = globals->handles_count(); |
| 13760 | { |
| 13761 | v8::HandleScope scope(isolate); |
| 13762 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 13763 | CHECK( |
| 13764 | obj->Set(context.local(), v8_str("key" ), v8::Integer::New(isolate, 231)) |
| 13765 | .FromJust()); |
| 13766 | v8::Persistent<v8::Object>* handle = |
| 13767 | new v8::Persistent<v8::Object>(isolate, obj); |
| 13768 | handle->SetWeak<v8::Persistent<v8::Object>>( |
| 13769 | handle, WeakApiCallback, v8::WeakCallbackType::kParameter); |
| 13770 | } |
| 13771 | CcTest::PreciseCollectAllGarbage(); |
| 13772 | // Verify disposed. |
| 13773 | CHECK_EQ(initial_handles, globals->handles_count()); |
| 13774 | } |
| 13775 | |
| 13776 | |
| 13777 | v8::Persistent<v8::Object> some_object; |
| 13778 | v8::Persistent<v8::Object> bad_handle; |
| 13779 | |
| 13780 | |
| 13781 | void NewPersistentHandleCallback2( |
| 13782 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13783 | v8::HandleScope scope(data.GetIsolate()); |
| 13784 | bad_handle.Reset(data.GetIsolate(), some_object); |
| 13785 | } |
| 13786 | |
| 13787 | |
| 13788 | void NewPersistentHandleCallback1( |
| 13789 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13790 | data.GetParameter()->Reset(); |
| 13791 | data.SetSecondPassCallback(NewPersistentHandleCallback2); |
| 13792 | } |
| 13793 | |
| 13794 | |
| 13795 | THREADED_TEST(NewPersistentHandleFromWeakCallback) { |
| 13796 | LocalContext context; |
| 13797 | v8::Isolate* isolate = context->GetIsolate(); |
| 13798 | |
| 13799 | v8::Persistent<v8::Object> handle1, handle2; |
| 13800 | { |
| 13801 | v8::HandleScope scope(isolate); |
| 13802 | some_object.Reset(isolate, v8::Object::New(isolate)); |
| 13803 | handle1.Reset(isolate, v8::Object::New(isolate)); |
| 13804 | handle2.Reset(isolate, v8::Object::New(isolate)); |
| 13805 | } |
| 13806 | // Note: order is implementation dependent alas: currently |
| 13807 | // global handle nodes are processed by PostGarbageCollectionProcessing |
| 13808 | // in reverse allocation order, so if second allocated handle is deleted, |
| 13809 | // weak callback of the first handle would be able to 'reallocate' it. |
| 13810 | handle1.SetWeak(&handle1, NewPersistentHandleCallback1, |
| 13811 | v8::WeakCallbackType::kParameter); |
| 13812 | handle2.Reset(); |
| 13813 | CcTest::CollectAllGarbage(); |
| 13814 | } |
| 13815 | |
| 13816 | |
| 13817 | v8::Persistent<v8::Object> to_be_disposed; |
| 13818 | |
| 13819 | |
| 13820 | void DisposeAndForceGcCallback2( |
| 13821 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13822 | to_be_disposed.Reset(); |
| 13823 | CcTest::CollectAllGarbage(); |
| 13824 | } |
| 13825 | |
| 13826 | |
| 13827 | void DisposeAndForceGcCallback1( |
| 13828 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13829 | data.GetParameter()->Reset(); |
| 13830 | data.SetSecondPassCallback(DisposeAndForceGcCallback2); |
| 13831 | } |
| 13832 | |
| 13833 | |
| 13834 | THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { |
| 13835 | LocalContext context; |
| 13836 | v8::Isolate* isolate = context->GetIsolate(); |
| 13837 | |
| 13838 | v8::Persistent<v8::Object> handle1, handle2; |
| 13839 | { |
| 13840 | v8::HandleScope scope(isolate); |
| 13841 | handle1.Reset(isolate, v8::Object::New(isolate)); |
| 13842 | handle2.Reset(isolate, v8::Object::New(isolate)); |
| 13843 | } |
| 13844 | handle1.SetWeak(&handle1, DisposeAndForceGcCallback1, |
| 13845 | v8::WeakCallbackType::kParameter); |
| 13846 | to_be_disposed.Reset(isolate, handle2); |
| 13847 | CcTest::CollectAllGarbage(); |
| 13848 | } |
| 13849 | |
| 13850 | void DisposingCallback( |
| 13851 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13852 | data.GetParameter()->Reset(); |
| 13853 | } |
| 13854 | |
| 13855 | void HandleCreatingCallback2( |
| 13856 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13857 | v8::HandleScope scope(data.GetIsolate()); |
| 13858 | v8::Global<v8::Object>(data.GetIsolate(), v8::Object::New(data.GetIsolate())); |
| 13859 | } |
| 13860 | |
| 13861 | |
| 13862 | void HandleCreatingCallback1( |
| 13863 | const v8::WeakCallbackInfo<v8::Persistent<v8::Object>>& data) { |
| 13864 | data.GetParameter()->Reset(); |
| 13865 | data.SetSecondPassCallback(HandleCreatingCallback2); |
| 13866 | } |
| 13867 | |
| 13868 | |
| 13869 | THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { |
| 13870 | v8::Locker locker(CcTest::isolate()); |
| 13871 | LocalContext context; |
| 13872 | v8::Isolate* isolate = context->GetIsolate(); |
| 13873 | |
| 13874 | v8::Persistent<v8::Object> handle1, handle2, handle3; |
| 13875 | { |
| 13876 | v8::HandleScope scope(isolate); |
| 13877 | handle3.Reset(isolate, v8::Object::New(isolate)); |
| 13878 | handle2.Reset(isolate, v8::Object::New(isolate)); |
| 13879 | handle1.Reset(isolate, v8::Object::New(isolate)); |
| 13880 | } |
| 13881 | handle2.SetWeak(&handle2, DisposingCallback, |
| 13882 | v8::WeakCallbackType::kParameter); |
| 13883 | handle3.SetWeak(&handle3, HandleCreatingCallback1, |
| 13884 | v8::WeakCallbackType::kParameter); |
| 13885 | CcTest::CollectAllGarbage(); |
| 13886 | EmptyMessageQueues(isolate); |
| 13887 | } |
| 13888 | |
| 13889 | |
| 13890 | THREADED_TEST(CheckForCrossContextObjectLiterals) { |
| 13891 | v8::V8::Initialize(); |
| 13892 | |
| 13893 | const int nof = 2; |
| 13894 | const char* sources[nof] = { |
| 13895 | "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }" , |
| 13896 | "Object()" |
| 13897 | }; |
| 13898 | |
| 13899 | for (int i = 0; i < nof; i++) { |
| 13900 | const char* source = sources[i]; |
| 13901 | { v8::HandleScope scope(CcTest::isolate()); |
| 13902 | LocalContext context; |
| 13903 | CompileRun(source); |
| 13904 | } |
| 13905 | { v8::HandleScope scope(CcTest::isolate()); |
| 13906 | LocalContext context; |
| 13907 | CompileRun(source); |
| 13908 | } |
| 13909 | } |
| 13910 | } |
| 13911 | |
| 13912 | |
| 13913 | static v8::Local<Value> NestedScope(v8::Local<Context> env) { |
| 13914 | v8::EscapableHandleScope inner(env->GetIsolate()); |
| 13915 | env->Enter(); |
| 13916 | v8::Local<Value> three = v8_num(3); |
| 13917 | v8::Local<Value> value = inner.Escape(three); |
| 13918 | env->Exit(); |
| 13919 | return value; |
| 13920 | } |
| 13921 | |
| 13922 | |
| 13923 | THREADED_TEST(NestedHandleScopeAndContexts) { |
| 13924 | v8::Isolate* isolate = CcTest::isolate(); |
| 13925 | v8::HandleScope outer(isolate); |
| 13926 | v8::Local<Context> env = Context::New(isolate); |
| 13927 | env->Enter(); |
| 13928 | v8::Local<Value> value = NestedScope(env); |
| 13929 | v8::Local<String> str(value->ToString(env).ToLocalChecked()); |
| 13930 | CHECK(!str.IsEmpty()); |
| 13931 | env->Exit(); |
| 13932 | } |
| 13933 | |
| 13934 | static v8::base::HashMap* code_map = nullptr; |
| 13935 | static v8::base::HashMap* jitcode_line_info = nullptr; |
| 13936 | static int saw_bar = 0; |
| 13937 | static int move_events = 0; |
| 13938 | |
| 13939 | |
| 13940 | static bool FunctionNameIs(const char* expected, |
| 13941 | const v8::JitCodeEvent* event) { |
| 13942 | // Log lines for functions are of the general form: |
| 13943 | // "LazyCompile:<type><function_name>" or Function:<type><function_name>, |
| 13944 | // where the type is one of "*", "~" or "". |
| 13945 | static const char* kPreamble; |
| 13946 | if (!i::FLAG_lazy) { |
| 13947 | kPreamble = "Function:" ; |
| 13948 | } else { |
| 13949 | kPreamble = "LazyCompile:" ; |
| 13950 | } |
| 13951 | static size_t kPreambleLen = strlen(kPreamble); |
| 13952 | |
| 13953 | if (event->name.len < kPreambleLen || |
| 13954 | strncmp(kPreamble, event->name.str, kPreambleLen) != 0) { |
| 13955 | return false; |
| 13956 | } |
| 13957 | |
| 13958 | const char* tail = event->name.str + kPreambleLen; |
| 13959 | size_t tail_len = event->name.len - kPreambleLen; |
| 13960 | size_t expected_len = strlen(expected); |
| 13961 | if (tail_len > 1 && (*tail == '*' || *tail == '~')) { |
| 13962 | --tail_len; |
| 13963 | ++tail; |
| 13964 | } |
| 13965 | |
| 13966 | // Check for tails like 'bar :1'. |
| 13967 | if (tail_len > expected_len + 2 && |
| 13968 | tail[expected_len] == ' ' && |
| 13969 | tail[expected_len + 1] == ':' && |
| 13970 | tail[expected_len + 2] && |
| 13971 | !strncmp(tail, expected, expected_len)) { |
| 13972 | return true; |
| 13973 | } |
| 13974 | |
| 13975 | if (tail_len != expected_len) |
| 13976 | return false; |
| 13977 | |
| 13978 | return strncmp(tail, expected, expected_len) == 0; |
| 13979 | } |
| 13980 | |
| 13981 | |
| 13982 | static void event_handler(const v8::JitCodeEvent* event) { |
| 13983 | CHECK_NOT_NULL(event); |
| 13984 | CHECK_NOT_NULL(code_map); |
| 13985 | CHECK_NOT_NULL(jitcode_line_info); |
| 13986 | |
| 13987 | class DummyJitCodeLineInfo { |
| 13988 | }; |
| 13989 | |
| 13990 | switch (event->type) { |
| 13991 | case v8::JitCodeEvent::CODE_ADDED: { |
| 13992 | CHECK_NOT_NULL(event->code_start); |
| 13993 | CHECK_NE(0, static_cast<int>(event->code_len)); |
| 13994 | CHECK_NOT_NULL(event->name.str); |
| 13995 | v8::base::HashMap::Entry* entry = code_map->LookupOrInsert( |
| 13996 | event->code_start, i::ComputePointerHash(event->code_start)); |
| 13997 | entry->value = reinterpret_cast<void*>(event->code_len); |
| 13998 | |
| 13999 | if (FunctionNameIs("bar" , event)) { |
| 14000 | ++saw_bar; |
| 14001 | } |
| 14002 | } |
| 14003 | break; |
| 14004 | |
| 14005 | case v8::JitCodeEvent::CODE_MOVED: { |
| 14006 | uint32_t hash = i::ComputePointerHash(event->code_start); |
| 14007 | // We would like to never see code move that we haven't seen before, |
| 14008 | // but the code creation event does not happen until the line endings |
| 14009 | // have been calculated (this is so that we can report the line in the |
| 14010 | // script at which the function source is found, see |
| 14011 | // Compiler::RecordFunctionCompilation) and the line endings |
| 14012 | // calculations can cause a GC, which can move the newly created code |
| 14013 | // before its existence can be logged. |
| 14014 | v8::base::HashMap::Entry* entry = |
| 14015 | code_map->Lookup(event->code_start, hash); |
| 14016 | if (entry != nullptr) { |
| 14017 | ++move_events; |
| 14018 | |
| 14019 | CHECK_EQ(reinterpret_cast<void*>(event->code_len), entry->value); |
| 14020 | code_map->Remove(event->code_start, hash); |
| 14021 | |
| 14022 | entry = code_map->LookupOrInsert( |
| 14023 | event->new_code_start, |
| 14024 | i::ComputePointerHash(event->new_code_start)); |
| 14025 | entry->value = reinterpret_cast<void*>(event->code_len); |
| 14026 | } |
| 14027 | } |
| 14028 | break; |
| 14029 | |
| 14030 | case v8::JitCodeEvent::CODE_REMOVED: |
| 14031 | // Object/code removal events are currently not dispatched from the GC. |
| 14032 | UNREACHABLE(); |
| 14033 | |
| 14034 | // For CODE_START_LINE_INFO_RECORDING event, we will create one |
| 14035 | // DummyJitCodeLineInfo data structure pointed by event->user_dat. We |
| 14036 | // record it in jitcode_line_info. |
| 14037 | case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: { |
| 14038 | DummyJitCodeLineInfo* line_info = new DummyJitCodeLineInfo(); |
| 14039 | v8::JitCodeEvent* temp_event = const_cast<v8::JitCodeEvent*>(event); |
| 14040 | temp_event->user_data = line_info; |
| 14041 | v8::base::HashMap::Entry* entry = jitcode_line_info->LookupOrInsert( |
| 14042 | line_info, i::ComputePointerHash(line_info)); |
| 14043 | entry->value = reinterpret_cast<void*>(line_info); |
| 14044 | } |
| 14045 | break; |
| 14046 | // For these two events, we will check whether the event->user_data |
| 14047 | // data structure is created before during CODE_START_LINE_INFO_RECORDING |
| 14048 | // event. And delete it in CODE_END_LINE_INFO_RECORDING event handling. |
| 14049 | case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: { |
| 14050 | CHECK_NOT_NULL(event->user_data); |
| 14051 | uint32_t hash = i::ComputePointerHash(event->user_data); |
| 14052 | v8::base::HashMap::Entry* entry = |
| 14053 | jitcode_line_info->Lookup(event->user_data, hash); |
| 14054 | CHECK_NOT_NULL(entry); |
| 14055 | delete reinterpret_cast<DummyJitCodeLineInfo*>(event->user_data); |
| 14056 | } |
| 14057 | break; |
| 14058 | |
| 14059 | case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: { |
| 14060 | CHECK_NOT_NULL(event->user_data); |
| 14061 | uint32_t hash = i::ComputePointerHash(event->user_data); |
| 14062 | v8::base::HashMap::Entry* entry = |
| 14063 | jitcode_line_info->Lookup(event->user_data, hash); |
| 14064 | CHECK_NOT_NULL(entry); |
| 14065 | } |
| 14066 | break; |
| 14067 | |
| 14068 | default: |
| 14069 | // Impossible event. |
| 14070 | UNREACHABLE(); |
| 14071 | } |
| 14072 | } |
| 14073 | |
| 14074 | |
| 14075 | UNINITIALIZED_TEST(SetJitCodeEventHandler) { |
| 14076 | i::FLAG_stress_compaction = true; |
| 14077 | i::FLAG_incremental_marking = false; |
| 14078 | if (i::FLAG_never_compact) return; |
| 14079 | const char* script = |
| 14080 | "function bar() {" |
| 14081 | " var sum = 0;" |
| 14082 | " for (i = 0; i < 10; ++i)" |
| 14083 | " sum = foo(i);" |
| 14084 | " return sum;" |
| 14085 | "}" |
| 14086 | "function foo(i) { return i; };" |
| 14087 | "bar();" ; |
| 14088 | |
| 14089 | // Run this test in a new isolate to make sure we don't |
| 14090 | // have remnants of state from other code. |
| 14091 | v8::Isolate::CreateParams create_params; |
| 14092 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 14093 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 14094 | isolate->Enter(); |
| 14095 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 14096 | i::Heap* heap = i_isolate->heap(); |
| 14097 | |
| 14098 | // Start with a clean slate. |
| 14099 | heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting); |
| 14100 | |
| 14101 | { |
| 14102 | v8::HandleScope scope(isolate); |
| 14103 | v8::base::HashMap code; |
| 14104 | code_map = &code; |
| 14105 | |
| 14106 | v8::base::HashMap lineinfo; |
| 14107 | jitcode_line_info = &lineinfo; |
| 14108 | |
| 14109 | saw_bar = 0; |
| 14110 | move_events = 0; |
| 14111 | |
| 14112 | isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, event_handler); |
| 14113 | |
| 14114 | // Generate new code objects sparsely distributed across several |
| 14115 | // different fragmented code-space pages. |
| 14116 | const int kIterations = 10; |
| 14117 | for (int i = 0; i < kIterations; ++i) { |
| 14118 | LocalContext env(isolate); |
| 14119 | i::AlwaysAllocateScope always_allocate(i_isolate); |
| 14120 | CompileRun(script); |
| 14121 | |
| 14122 | // Keep a strong reference to the code object in the handle scope. |
| 14123 | i::Handle<i::JSFunction> bar(i::Handle<i::JSFunction>::cast( |
| 14124 | v8::Utils::OpenHandle(*env->Global() |
| 14125 | ->Get(env.local(), v8_str("bar" )) |
| 14126 | .ToLocalChecked()))); |
| 14127 | i::Handle<i::JSFunction> foo(i::Handle<i::JSFunction>::cast( |
| 14128 | v8::Utils::OpenHandle(*env->Global() |
| 14129 | ->Get(env.local(), v8_str("foo" )) |
| 14130 | .ToLocalChecked()))); |
| 14131 | |
| 14132 | i::PagedSpace* foo_owning_space = reinterpret_cast<i::PagedSpace*>( |
| 14133 | i::Page::FromHeapObject(foo->abstract_code())->owner()); |
| 14134 | i::PagedSpace* bar_owning_space = reinterpret_cast<i::PagedSpace*>( |
| 14135 | i::Page::FromHeapObject(bar->abstract_code())->owner()); |
| 14136 | CHECK_EQ(foo_owning_space, bar_owning_space); |
| 14137 | i::heap::SimulateFullSpace(foo_owning_space); |
| 14138 | |
| 14139 | // Clear the compilation cache to get more wastage. |
| 14140 | reinterpret_cast<i::Isolate*>(isolate)->compilation_cache()->Clear(); |
| 14141 | } |
| 14142 | |
| 14143 | // Force code movement. |
| 14144 | heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting); |
| 14145 | |
| 14146 | isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, nullptr); |
| 14147 | |
| 14148 | CHECK_LE(kIterations, saw_bar); |
| 14149 | CHECK_LT(0, move_events); |
| 14150 | |
| 14151 | code_map = nullptr; |
| 14152 | jitcode_line_info = nullptr; |
| 14153 | } |
| 14154 | |
| 14155 | isolate->Exit(); |
| 14156 | isolate->Dispose(); |
| 14157 | |
| 14158 | // Do this in a new isolate. |
| 14159 | isolate = v8::Isolate::New(create_params); |
| 14160 | isolate->Enter(); |
| 14161 | |
| 14162 | // Verify that we get callbacks for existing code objects when we |
| 14163 | // request enumeration of existing code. |
| 14164 | { |
| 14165 | v8::HandleScope scope(isolate); |
| 14166 | LocalContext env(isolate); |
| 14167 | CompileRun(script); |
| 14168 | |
| 14169 | // Now get code through initial iteration. |
| 14170 | v8::base::HashMap code; |
| 14171 | code_map = &code; |
| 14172 | |
| 14173 | v8::base::HashMap lineinfo; |
| 14174 | jitcode_line_info = &lineinfo; |
| 14175 | |
| 14176 | isolate->SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting, |
| 14177 | event_handler); |
| 14178 | isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, nullptr); |
| 14179 | |
| 14180 | jitcode_line_info = nullptr; |
| 14181 | // We expect that we got some events. Note that if we could get code removal |
| 14182 | // notifications, we could compare two collections, one created by listening |
| 14183 | // from the time of creation of an isolate, and the other by subscribing |
| 14184 | // with EnumExisting. |
| 14185 | CHECK_LT(0u, code.occupancy()); |
| 14186 | |
| 14187 | code_map = nullptr; |
| 14188 | } |
| 14189 | |
| 14190 | isolate->Exit(); |
| 14191 | isolate->Dispose(); |
| 14192 | } |
| 14193 | |
| 14194 | TEST(ExternalAllocatedMemory) { |
| 14195 | v8::Isolate* isolate = CcTest::isolate(); |
| 14196 | v8::HandleScope outer(isolate); |
| 14197 | v8::Local<Context> env(Context::New(isolate)); |
| 14198 | CHECK(!env.IsEmpty()); |
| 14199 | const int64_t kSize = 1024*1024; |
| 14200 | int64_t baseline = isolate->AdjustAmountOfExternalAllocatedMemory(0); |
| 14201 | CHECK_EQ(baseline + kSize, |
| 14202 | isolate->AdjustAmountOfExternalAllocatedMemory(kSize)); |
| 14203 | CHECK_EQ(baseline, |
| 14204 | isolate->AdjustAmountOfExternalAllocatedMemory(-kSize)); |
| 14205 | const int64_t kTriggerGCSize = |
| 14206 | CcTest::i_isolate()->heap()->external_memory_hard_limit() + 1; |
| 14207 | CHECK_EQ(baseline + kTriggerGCSize, |
| 14208 | isolate->AdjustAmountOfExternalAllocatedMemory(kTriggerGCSize)); |
| 14209 | CHECK_EQ(baseline, |
| 14210 | isolate->AdjustAmountOfExternalAllocatedMemory(-kTriggerGCSize)); |
| 14211 | } |
| 14212 | |
| 14213 | |
| 14214 | TEST(Regress51719) { |
| 14215 | i::FLAG_incremental_marking = false; |
| 14216 | CcTest::InitializeVM(); |
| 14217 | |
| 14218 | const int64_t kTriggerGCSize = |
| 14219 | CcTest::i_isolate()->heap()->external_memory_hard_limit() + 1; |
| 14220 | v8::Isolate* isolate = CcTest::isolate(); |
| 14221 | isolate->AdjustAmountOfExternalAllocatedMemory(kTriggerGCSize); |
| 14222 | } |
| 14223 | |
| 14224 | // Regression test for issue 54, object templates with embedder fields |
| 14225 | // but no accessors or interceptors did not get their embedder field |
| 14226 | // count set on instances. |
| 14227 | THREADED_TEST(Regress54) { |
| 14228 | LocalContext context; |
| 14229 | v8::Isolate* isolate = context->GetIsolate(); |
| 14230 | v8::HandleScope outer(isolate); |
| 14231 | static v8::Persistent<v8::ObjectTemplate> templ; |
| 14232 | if (templ.IsEmpty()) { |
| 14233 | v8::EscapableHandleScope inner(isolate); |
| 14234 | v8::Local<v8::ObjectTemplate> local = v8::ObjectTemplate::New(isolate); |
| 14235 | local->SetInternalFieldCount(1); |
| 14236 | templ.Reset(isolate, inner.Escape(local)); |
| 14237 | } |
| 14238 | v8::Local<v8::Object> result = |
| 14239 | v8::Local<v8::ObjectTemplate>::New(isolate, templ) |
| 14240 | ->NewInstance(context.local()) |
| 14241 | .ToLocalChecked(); |
| 14242 | CHECK_EQ(1, result->InternalFieldCount()); |
| 14243 | } |
| 14244 | |
| 14245 | |
| 14246 | // If part of the threaded tests, this test makes ThreadingTest fail |
| 14247 | // on mac. |
| 14248 | TEST(CatchStackOverflow) { |
| 14249 | LocalContext context; |
| 14250 | v8::HandleScope scope(context->GetIsolate()); |
| 14251 | v8::TryCatch try_catch(context->GetIsolate()); |
| 14252 | v8::Local<v8::Value> result = CompileRun( |
| 14253 | "function f() {" |
| 14254 | " return f();" |
| 14255 | "}" |
| 14256 | "" |
| 14257 | "f();" ); |
| 14258 | CHECK(result.IsEmpty()); |
| 14259 | } |
| 14260 | |
| 14261 | |
| 14262 | static void CheckTryCatchSourceInfo(v8::Local<v8::Script> script, |
| 14263 | const char* resource_name, |
| 14264 | int line_offset) { |
| 14265 | v8::HandleScope scope(CcTest::isolate()); |
| 14266 | v8::TryCatch try_catch(CcTest::isolate()); |
| 14267 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 14268 | CHECK(script->Run(context).IsEmpty()); |
| 14269 | CHECK(try_catch.HasCaught()); |
| 14270 | v8::Local<v8::Message> message = try_catch.Message(); |
| 14271 | CHECK(!message.IsEmpty()); |
| 14272 | CHECK_EQ(10 + line_offset, message->GetLineNumber(context).FromJust()); |
| 14273 | CHECK_EQ(91, message->GetStartPosition()); |
| 14274 | CHECK_EQ(92, message->GetEndPosition()); |
| 14275 | CHECK_EQ(2, message->GetStartColumn(context).FromJust()); |
| 14276 | CHECK_EQ(3, message->GetEndColumn(context).FromJust()); |
| 14277 | v8::String::Utf8Value line(CcTest::isolate(), |
| 14278 | message->GetSourceLine(context).ToLocalChecked()); |
| 14279 | CHECK_EQ(0, strcmp(" throw 'nirk';" , *line)); |
| 14280 | v8::String::Utf8Value name(CcTest::isolate(), |
| 14281 | message->GetScriptOrigin().ResourceName()); |
| 14282 | CHECK_EQ(0, strcmp(resource_name, *name)); |
| 14283 | } |
| 14284 | |
| 14285 | |
| 14286 | THREADED_TEST(TryCatchSourceInfo) { |
| 14287 | LocalContext context; |
| 14288 | v8::HandleScope scope(context->GetIsolate()); |
| 14289 | v8::Local<v8::String> source = v8_str( |
| 14290 | "function Foo() {\n" |
| 14291 | " return Bar();\n" |
| 14292 | "}\n" |
| 14293 | "\n" |
| 14294 | "function Bar() {\n" |
| 14295 | " return Baz();\n" |
| 14296 | "}\n" |
| 14297 | "\n" |
| 14298 | "function Baz() {\n" |
| 14299 | " throw 'nirk';\n" |
| 14300 | "}\n" |
| 14301 | "\n" |
| 14302 | "Foo();\n" ); |
| 14303 | |
| 14304 | const char* resource_name; |
| 14305 | v8::Local<v8::Script> script; |
| 14306 | resource_name = "test.js" ; |
| 14307 | script = CompileWithOrigin(source, resource_name, false); |
| 14308 | CheckTryCatchSourceInfo(script, resource_name, 0); |
| 14309 | |
| 14310 | resource_name = "test1.js" ; |
| 14311 | v8::ScriptOrigin origin1(v8_str(resource_name)); |
| 14312 | script = |
| 14313 | v8::Script::Compile(context.local(), source, &origin1).ToLocalChecked(); |
| 14314 | CheckTryCatchSourceInfo(script, resource_name, 0); |
| 14315 | |
| 14316 | resource_name = "test2.js" ; |
| 14317 | v8::ScriptOrigin origin2(v8_str(resource_name), |
| 14318 | v8::Integer::New(context->GetIsolate(), 7)); |
| 14319 | script = |
| 14320 | v8::Script::Compile(context.local(), source, &origin2).ToLocalChecked(); |
| 14321 | CheckTryCatchSourceInfo(script, resource_name, 7); |
| 14322 | } |
| 14323 | |
| 14324 | |
| 14325 | THREADED_TEST(TryCatchSourceInfoForEOSError) { |
| 14326 | LocalContext context; |
| 14327 | v8::HandleScope scope(context->GetIsolate()); |
| 14328 | v8::TryCatch try_catch(context->GetIsolate()); |
| 14329 | CHECK(v8::Script::Compile(context.local(), v8_str("!\n" )).IsEmpty()); |
| 14330 | CHECK(try_catch.HasCaught()); |
| 14331 | v8::Local<v8::Message> message = try_catch.Message(); |
| 14332 | CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust()); |
| 14333 | CHECK_EQ(0, message->GetStartColumn(context.local()).FromJust()); |
| 14334 | } |
| 14335 | |
| 14336 | |
| 14337 | THREADED_TEST(CompilationCache) { |
| 14338 | LocalContext context; |
| 14339 | v8::HandleScope scope(context->GetIsolate()); |
| 14340 | v8::Local<v8::String> source0 = v8_str("1234" ); |
| 14341 | v8::Local<v8::String> source1 = v8_str("1234" ); |
| 14342 | v8::Local<v8::Script> script0 = CompileWithOrigin(source0, "test.js" , false); |
| 14343 | v8::Local<v8::Script> script1 = CompileWithOrigin(source1, "test.js" , false); |
| 14344 | v8::Local<v8::Script> script2 = v8::Script::Compile(context.local(), source0) |
| 14345 | .ToLocalChecked(); // different origin |
| 14346 | CHECK_EQ(1234, script0->Run(context.local()) |
| 14347 | .ToLocalChecked() |
| 14348 | ->Int32Value(context.local()) |
| 14349 | .FromJust()); |
| 14350 | CHECK_EQ(1234, script1->Run(context.local()) |
| 14351 | .ToLocalChecked() |
| 14352 | ->Int32Value(context.local()) |
| 14353 | .FromJust()); |
| 14354 | CHECK_EQ(1234, script2->Run(context.local()) |
| 14355 | .ToLocalChecked() |
| 14356 | ->Int32Value(context.local()) |
| 14357 | .FromJust()); |
| 14358 | } |
| 14359 | |
| 14360 | |
| 14361 | static void FunctionNameCallback( |
| 14362 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 14363 | ApiTestFuzzer::Fuzz(); |
| 14364 | args.GetReturnValue().Set(v8_num(42)); |
| 14365 | } |
| 14366 | |
| 14367 | |
| 14368 | THREADED_TEST(CallbackFunctionName) { |
| 14369 | LocalContext context; |
| 14370 | v8::Isolate* isolate = context->GetIsolate(); |
| 14371 | v8::HandleScope scope(isolate); |
| 14372 | Local<ObjectTemplate> t = ObjectTemplate::New(isolate); |
| 14373 | t->Set(v8_str("asdf" ), |
| 14374 | v8::FunctionTemplate::New(isolate, FunctionNameCallback)); |
| 14375 | CHECK(context->Global() |
| 14376 | ->Set(context.local(), v8_str("obj" ), |
| 14377 | t->NewInstance(context.local()).ToLocalChecked()) |
| 14378 | .FromJust()); |
| 14379 | v8::Local<v8::Value> value = CompileRun("obj.asdf.name" ); |
| 14380 | CHECK(value->IsString()); |
| 14381 | v8::String::Utf8Value name(isolate, value); |
| 14382 | CHECK_EQ(0, strcmp("asdf" , *name)); |
| 14383 | } |
| 14384 | |
| 14385 | |
| 14386 | THREADED_TEST(DateAccess) { |
| 14387 | LocalContext context; |
| 14388 | v8::HandleScope scope(context->GetIsolate()); |
| 14389 | v8::Local<v8::Value> date = |
| 14390 | v8::Date::New(context.local(), 1224744689038.0).ToLocalChecked(); |
| 14391 | CHECK(date->IsDate()); |
| 14392 | CHECK_EQ(1224744689038.0, date.As<v8::Date>()->ValueOf()); |
| 14393 | } |
| 14394 | |
| 14395 | void CheckIsSymbolAt(v8::Isolate* isolate, v8::Local<v8::Array> properties, |
| 14396 | unsigned index, const char* name) { |
| 14397 | v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 14398 | v8::Local<v8::Value> value = |
| 14399 | properties->Get(context, v8::Integer::New(isolate, index)) |
| 14400 | .ToLocalChecked(); |
| 14401 | CHECK(value->IsSymbol()); |
| 14402 | v8::String::Utf8Value symbol_name(isolate, |
| 14403 | Local<Symbol>::Cast(value)->Name()); |
| 14404 | if (strcmp(name, *symbol_name) != 0) { |
| 14405 | FATAL("properties[%u] was Symbol('%s') instead of Symbol('%s')." , index, |
| 14406 | name, *symbol_name); |
| 14407 | } |
| 14408 | } |
| 14409 | |
| 14410 | void CheckStringArray(v8::Isolate* isolate, v8::Local<v8::Array> properties, |
| 14411 | unsigned length, const char* names[]) { |
| 14412 | v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 14413 | CHECK_EQ(length, properties->Length()); |
| 14414 | for (unsigned i = 0; i < length; i++) { |
| 14415 | v8::Local<v8::Value> value = |
| 14416 | properties->Get(context, v8::Integer::New(isolate, i)).ToLocalChecked(); |
| 14417 | if (names[i] == nullptr) { |
| 14418 | DCHECK(value->IsSymbol()); |
| 14419 | } else { |
| 14420 | v8::String::Utf8Value elm(isolate, value); |
| 14421 | if (strcmp(names[i], *elm) != 0) { |
| 14422 | FATAL("properties[%u] was '%s' instead of '%s'." , i, *elm, names[i]); |
| 14423 | } |
| 14424 | } |
| 14425 | } |
| 14426 | } |
| 14427 | |
| 14428 | void CheckProperties(v8::Isolate* isolate, v8::Local<v8::Value> val, |
| 14429 | unsigned length, const char* names[]) { |
| 14430 | v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 14431 | v8::Local<v8::Object> obj = val.As<v8::Object>(); |
| 14432 | v8::Local<v8::Array> props = obj->GetPropertyNames(context).ToLocalChecked(); |
| 14433 | CheckStringArray(isolate, props, length, names); |
| 14434 | } |
| 14435 | |
| 14436 | |
| 14437 | void CheckOwnProperties(v8::Isolate* isolate, v8::Local<v8::Value> val, |
| 14438 | unsigned elmc, const char* elmv[]) { |
| 14439 | v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| 14440 | v8::Local<v8::Object> obj = val.As<v8::Object>(); |
| 14441 | v8::Local<v8::Array> props = |
| 14442 | obj->GetOwnPropertyNames(context).ToLocalChecked(); |
| 14443 | CHECK_EQ(elmc, props->Length()); |
| 14444 | for (unsigned i = 0; i < elmc; i++) { |
| 14445 | v8::String::Utf8Value elm( |
| 14446 | isolate, |
| 14447 | props->Get(context, v8::Integer::New(isolate, i)).ToLocalChecked()); |
| 14448 | CHECK_EQ(0, strcmp(elmv[i], *elm)); |
| 14449 | } |
| 14450 | } |
| 14451 | |
| 14452 | |
| 14453 | THREADED_TEST(PropertyEnumeration) { |
| 14454 | LocalContext context; |
| 14455 | v8::Isolate* isolate = context->GetIsolate(); |
| 14456 | v8::HandleScope scope(isolate); |
| 14457 | v8::Local<v8::Value> obj = CompileRun( |
| 14458 | "var result = [];" |
| 14459 | "result[0] = {};" |
| 14460 | "result[1] = {a: 1, b: 2};" |
| 14461 | "result[2] = [1, 2, 3];" |
| 14462 | "var proto = {x: 1, y: 2, z: 3};" |
| 14463 | "var x = { __proto__: proto, w: 0, z: 1 };" |
| 14464 | "result[3] = x;" |
| 14465 | "result[4] = {21350:1};" |
| 14466 | "x = Object.create(null);" |
| 14467 | "x.a = 1; x[12345678] = 1;" |
| 14468 | "result[5] = x;" |
| 14469 | "result;" ); |
| 14470 | v8::Local<v8::Array> elms = obj.As<v8::Array>(); |
| 14471 | CHECK_EQ(6u, elms->Length()); |
| 14472 | int elmc0 = 0; |
| 14473 | const char** elmv0 = nullptr; |
| 14474 | CheckProperties( |
| 14475 | isolate, |
| 14476 | elms->Get(context.local(), v8::Integer::New(isolate, 0)).ToLocalChecked(), |
| 14477 | elmc0, elmv0); |
| 14478 | CheckOwnProperties( |
| 14479 | isolate, |
| 14480 | elms->Get(context.local(), v8::Integer::New(isolate, 0)).ToLocalChecked(), |
| 14481 | elmc0, elmv0); |
| 14482 | int elmc1 = 2; |
| 14483 | const char* elmv1[] = {"a" , "b" }; |
| 14484 | CheckProperties( |
| 14485 | isolate, |
| 14486 | elms->Get(context.local(), v8::Integer::New(isolate, 1)).ToLocalChecked(), |
| 14487 | elmc1, elmv1); |
| 14488 | CheckOwnProperties( |
| 14489 | isolate, |
| 14490 | elms->Get(context.local(), v8::Integer::New(isolate, 1)).ToLocalChecked(), |
| 14491 | elmc1, elmv1); |
| 14492 | int elmc2 = 3; |
| 14493 | const char* elmv2[] = {"0" , "1" , "2" }; |
| 14494 | CheckProperties( |
| 14495 | isolate, |
| 14496 | elms->Get(context.local(), v8::Integer::New(isolate, 2)).ToLocalChecked(), |
| 14497 | elmc2, elmv2); |
| 14498 | CheckOwnProperties( |
| 14499 | isolate, |
| 14500 | elms->Get(context.local(), v8::Integer::New(isolate, 2)).ToLocalChecked(), |
| 14501 | elmc2, elmv2); |
| 14502 | int elmc3 = 4; |
| 14503 | const char* elmv3[] = {"w" , "z" , "x" , "y" }; |
| 14504 | CheckProperties( |
| 14505 | isolate, |
| 14506 | elms->Get(context.local(), v8::Integer::New(isolate, 3)).ToLocalChecked(), |
| 14507 | elmc3, elmv3); |
| 14508 | int elmc4 = 2; |
| 14509 | const char* elmv4[] = {"w" , "z" }; |
| 14510 | CheckOwnProperties( |
| 14511 | isolate, |
| 14512 | elms->Get(context.local(), v8::Integer::New(isolate, 3)).ToLocalChecked(), |
| 14513 | elmc4, elmv4); |
| 14514 | // Dictionary elements. |
| 14515 | int elmc5 = 1; |
| 14516 | const char* elmv5[] = {"21350" }; |
| 14517 | CheckProperties( |
| 14518 | isolate, |
| 14519 | elms->Get(context.local(), v8::Integer::New(isolate, 4)).ToLocalChecked(), |
| 14520 | elmc5, elmv5); |
| 14521 | // Dictionary properties. |
| 14522 | int elmc6 = 2; |
| 14523 | const char* elmv6[] = {"12345678" , "a" }; |
| 14524 | CheckProperties( |
| 14525 | isolate, |
| 14526 | elms->Get(context.local(), v8::Integer::New(isolate, 5)).ToLocalChecked(), |
| 14527 | elmc6, elmv6); |
| 14528 | } |
| 14529 | |
| 14530 | |
| 14531 | THREADED_TEST(PropertyEnumeration2) { |
| 14532 | LocalContext context; |
| 14533 | v8::Isolate* isolate = context->GetIsolate(); |
| 14534 | v8::HandleScope scope(isolate); |
| 14535 | v8::Local<v8::Value> obj = CompileRun( |
| 14536 | "var result = [];" |
| 14537 | "result[0] = {};" |
| 14538 | "result[1] = {a: 1, b: 2};" |
| 14539 | "result[2] = [1, 2, 3];" |
| 14540 | "var proto = {x: 1, y: 2, z: 3};" |
| 14541 | "var x = { __proto__: proto, w: 0, z: 1 };" |
| 14542 | "result[3] = x;" |
| 14543 | "result;" ); |
| 14544 | v8::Local<v8::Array> elms = obj.As<v8::Array>(); |
| 14545 | CHECK_EQ(4u, elms->Length()); |
| 14546 | int elmc0 = 0; |
| 14547 | const char** elmv0 = nullptr; |
| 14548 | CheckProperties( |
| 14549 | isolate, |
| 14550 | elms->Get(context.local(), v8::Integer::New(isolate, 0)).ToLocalChecked(), |
| 14551 | elmc0, elmv0); |
| 14552 | |
| 14553 | v8::Local<v8::Value> val = |
| 14554 | elms->Get(context.local(), v8::Integer::New(isolate, 0)).ToLocalChecked(); |
| 14555 | v8::Local<v8::Array> props = |
| 14556 | val.As<v8::Object>()->GetPropertyNames(context.local()).ToLocalChecked(); |
| 14557 | CHECK_EQ(0u, props->Length()); |
| 14558 | for (uint32_t i = 0; i < props->Length(); i++) { |
| 14559 | printf("p[%u]\n" , i); |
| 14560 | } |
| 14561 | } |
| 14562 | |
| 14563 | THREADED_TEST(GetPropertyNames) { |
| 14564 | LocalContext context; |
| 14565 | v8::Isolate* isolate = context->GetIsolate(); |
| 14566 | v8::HandleScope scope(isolate); |
| 14567 | v8::Local<v8::Value> result = CompileRun( |
| 14568 | "var result = {0: 0, 1: 1, a: 2, b: 3};" |
| 14569 | "result[2**32] = '4294967296';" |
| 14570 | "result[2**32-1] = '4294967295';" |
| 14571 | "result[2**32-2] = '4294967294';" |
| 14572 | "result[Symbol('symbol')] = true;" |
| 14573 | "result.__proto__ = {__proto__:null, 2: 4, 3: 5, c: 6, d: 7};" |
| 14574 | "result;" ); |
| 14575 | v8::Local<v8::Object> object = result.As<v8::Object>(); |
| 14576 | v8::PropertyFilter default_filter = |
| 14577 | static_cast<v8::PropertyFilter>(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS); |
| 14578 | v8::PropertyFilter include_symbols_filter = v8::ONLY_ENUMERABLE; |
| 14579 | |
| 14580 | v8::Local<v8::Array> properties = |
| 14581 | object->GetPropertyNames(context.local()).ToLocalChecked(); |
| 14582 | const char* expected_properties1[] = {"0" , "1" , "4294967294" , "a" , |
| 14583 | "b" , "4294967296" , "4294967295" , "2" , |
| 14584 | "3" , "c" , "d" }; |
| 14585 | CheckStringArray(isolate, properties, 11, expected_properties1); |
| 14586 | |
| 14587 | properties = |
| 14588 | object |
| 14589 | ->GetPropertyNames(context.local(), |
| 14590 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14591 | default_filter, v8::IndexFilter::kIncludeIndices) |
| 14592 | .ToLocalChecked(); |
| 14593 | CheckStringArray(isolate, properties, 11, expected_properties1); |
| 14594 | |
| 14595 | properties = object |
| 14596 | ->GetPropertyNames(context.local(), |
| 14597 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14598 | include_symbols_filter, |
| 14599 | v8::IndexFilter::kIncludeIndices) |
| 14600 | .ToLocalChecked(); |
| 14601 | const char* expected_properties1_1[] = { |
| 14602 | "0" , "1" , "4294967294" , "a" , "b" , "4294967296" , |
| 14603 | "4294967295" , nullptr, "2" , "3" , "c" , "d" }; |
| 14604 | CheckStringArray(isolate, properties, 12, expected_properties1_1); |
| 14605 | CheckIsSymbolAt(isolate, properties, 7, "symbol" ); |
| 14606 | |
| 14607 | properties = |
| 14608 | object |
| 14609 | ->GetPropertyNames(context.local(), |
| 14610 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14611 | default_filter, v8::IndexFilter::kSkipIndices) |
| 14612 | .ToLocalChecked(); |
| 14613 | const char* expected_properties2[] = {"a" , "b" , "4294967296" , |
| 14614 | "4294967295" , "c" , "d" }; |
| 14615 | CheckStringArray(isolate, properties, 6, expected_properties2); |
| 14616 | |
| 14617 | properties = object |
| 14618 | ->GetPropertyNames(context.local(), |
| 14619 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14620 | include_symbols_filter, |
| 14621 | v8::IndexFilter::kSkipIndices) |
| 14622 | .ToLocalChecked(); |
| 14623 | const char* expected_properties2_1[] = { |
| 14624 | "a" , "b" , "4294967296" , "4294967295" , nullptr, "c" , "d" }; |
| 14625 | CheckStringArray(isolate, properties, 7, expected_properties2_1); |
| 14626 | CheckIsSymbolAt(isolate, properties, 4, "symbol" ); |
| 14627 | |
| 14628 | properties = |
| 14629 | object |
| 14630 | ->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14631 | default_filter, v8::IndexFilter::kIncludeIndices) |
| 14632 | .ToLocalChecked(); |
| 14633 | const char* expected_properties3[] = { |
| 14634 | "0" , "1" , "4294967294" , "a" , "b" , "4294967296" , "4294967295" , |
| 14635 | }; |
| 14636 | CheckStringArray(isolate, properties, 7, expected_properties3); |
| 14637 | |
| 14638 | properties = object |
| 14639 | ->GetPropertyNames( |
| 14640 | context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14641 | include_symbols_filter, v8::IndexFilter::kIncludeIndices) |
| 14642 | .ToLocalChecked(); |
| 14643 | const char* expected_properties3_1[] = { |
| 14644 | "0" , "1" , "4294967294" , "a" , "b" , "4294967296" , "4294967295" , nullptr}; |
| 14645 | CheckStringArray(isolate, properties, 8, expected_properties3_1); |
| 14646 | CheckIsSymbolAt(isolate, properties, 7, "symbol" ); |
| 14647 | |
| 14648 | properties = |
| 14649 | object |
| 14650 | ->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14651 | default_filter, v8::IndexFilter::kSkipIndices) |
| 14652 | .ToLocalChecked(); |
| 14653 | const char* expected_properties4[] = {"a" , "b" , "4294967296" , "4294967295" }; |
| 14654 | CheckStringArray(isolate, properties, 4, expected_properties4); |
| 14655 | |
| 14656 | properties = object |
| 14657 | ->GetPropertyNames( |
| 14658 | context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14659 | include_symbols_filter, v8::IndexFilter::kSkipIndices) |
| 14660 | .ToLocalChecked(); |
| 14661 | const char* expected_properties4_1[] = {"a" , "b" , "4294967296" , "4294967295" , |
| 14662 | nullptr}; |
| 14663 | CheckStringArray(isolate, properties, 5, expected_properties4_1); |
| 14664 | CheckIsSymbolAt(isolate, properties, 4, "symbol" ); |
| 14665 | } |
| 14666 | |
| 14667 | THREADED_TEST(ProxyGetPropertyNames) { |
| 14668 | LocalContext context; |
| 14669 | v8::Isolate* isolate = context->GetIsolate(); |
| 14670 | v8::HandleScope scope(isolate); |
| 14671 | v8::Local<v8::Value> result = CompileRun( |
| 14672 | "var target = {0: 0, 1: 1, a: 2, b: 3};" |
| 14673 | "target[2**32] = '4294967296';" |
| 14674 | "target[2**32-1] = '4294967295';" |
| 14675 | "target[2**32-2] = '4294967294';" |
| 14676 | "target[Symbol('symbol')] = true;" |
| 14677 | "target.__proto__ = {__proto__:null, 2: 4, 3: 5, c: 6, d: 7};" |
| 14678 | "var result = new Proxy(target, {});" |
| 14679 | "result;" ); |
| 14680 | v8::Local<v8::Object> object = result.As<v8::Object>(); |
| 14681 | v8::PropertyFilter default_filter = |
| 14682 | static_cast<v8::PropertyFilter>(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS); |
| 14683 | v8::PropertyFilter include_symbols_filter = v8::ONLY_ENUMERABLE; |
| 14684 | |
| 14685 | v8::Local<v8::Array> properties = |
| 14686 | object->GetPropertyNames(context.local()).ToLocalChecked(); |
| 14687 | const char* expected_properties1[] = {"0" , "1" , "4294967294" , "a" , |
| 14688 | "b" , "4294967296" , "4294967295" , "2" , |
| 14689 | "3" , "c" , "d" }; |
| 14690 | CheckStringArray(isolate, properties, 11, expected_properties1); |
| 14691 | |
| 14692 | properties = |
| 14693 | object |
| 14694 | ->GetPropertyNames(context.local(), |
| 14695 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14696 | default_filter, v8::IndexFilter::kIncludeIndices) |
| 14697 | .ToLocalChecked(); |
| 14698 | CheckStringArray(isolate, properties, 11, expected_properties1); |
| 14699 | |
| 14700 | properties = object |
| 14701 | ->GetPropertyNames(context.local(), |
| 14702 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14703 | include_symbols_filter, |
| 14704 | v8::IndexFilter::kIncludeIndices) |
| 14705 | .ToLocalChecked(); |
| 14706 | const char* expected_properties1_1[] = { |
| 14707 | "0" , "1" , "4294967294" , "a" , "b" , "4294967296" , |
| 14708 | "4294967295" , nullptr, "2" , "3" , "c" , "d" }; |
| 14709 | CheckStringArray(isolate, properties, 12, expected_properties1_1); |
| 14710 | CheckIsSymbolAt(isolate, properties, 7, "symbol" ); |
| 14711 | |
| 14712 | properties = |
| 14713 | object |
| 14714 | ->GetPropertyNames(context.local(), |
| 14715 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14716 | default_filter, v8::IndexFilter::kSkipIndices) |
| 14717 | .ToLocalChecked(); |
| 14718 | const char* expected_properties2[] = {"a" , "b" , "4294967296" , |
| 14719 | "4294967295" , "c" , "d" }; |
| 14720 | CheckStringArray(isolate, properties, 6, expected_properties2); |
| 14721 | |
| 14722 | properties = object |
| 14723 | ->GetPropertyNames(context.local(), |
| 14724 | v8::KeyCollectionMode::kIncludePrototypes, |
| 14725 | include_symbols_filter, |
| 14726 | v8::IndexFilter::kSkipIndices) |
| 14727 | .ToLocalChecked(); |
| 14728 | const char* expected_properties2_1[] = { |
| 14729 | "a" , "b" , "4294967296" , "4294967295" , nullptr, "c" , "d" }; |
| 14730 | CheckStringArray(isolate, properties, 7, expected_properties2_1); |
| 14731 | CheckIsSymbolAt(isolate, properties, 4, "symbol" ); |
| 14732 | |
| 14733 | properties = |
| 14734 | object |
| 14735 | ->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14736 | default_filter, v8::IndexFilter::kIncludeIndices) |
| 14737 | .ToLocalChecked(); |
| 14738 | const char* expected_properties3[] = {"0" , "1" , "4294967294" , "a" , |
| 14739 | "b" , "4294967296" , "4294967295" }; |
| 14740 | CheckStringArray(isolate, properties, 7, expected_properties3); |
| 14741 | |
| 14742 | properties = object |
| 14743 | ->GetPropertyNames( |
| 14744 | context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14745 | include_symbols_filter, v8::IndexFilter::kIncludeIndices) |
| 14746 | .ToLocalChecked(); |
| 14747 | const char* expected_properties3_1[] = { |
| 14748 | "0" , "1" , "4294967294" , "a" , "b" , "4294967296" , "4294967295" , nullptr}; |
| 14749 | CheckStringArray(isolate, properties, 8, expected_properties3_1); |
| 14750 | CheckIsSymbolAt(isolate, properties, 7, "symbol" ); |
| 14751 | |
| 14752 | properties = |
| 14753 | object |
| 14754 | ->GetPropertyNames(context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14755 | default_filter, v8::IndexFilter::kSkipIndices) |
| 14756 | .ToLocalChecked(); |
| 14757 | const char* expected_properties4[] = {"a" , "b" , "4294967296" , "4294967295" }; |
| 14758 | CheckStringArray(isolate, properties, 4, expected_properties4); |
| 14759 | |
| 14760 | properties = object |
| 14761 | ->GetPropertyNames( |
| 14762 | context.local(), v8::KeyCollectionMode::kOwnOnly, |
| 14763 | include_symbols_filter, v8::IndexFilter::kSkipIndices) |
| 14764 | .ToLocalChecked(); |
| 14765 | const char* expected_properties4_1[] = {"a" , "b" , "4294967296" , "4294967295" , |
| 14766 | nullptr}; |
| 14767 | CheckStringArray(isolate, properties, 5, expected_properties4_1); |
| 14768 | CheckIsSymbolAt(isolate, properties, 4, "symbol" ); |
| 14769 | } |
| 14770 | |
| 14771 | THREADED_TEST(AccessChecksReenabledCorrectly) { |
| 14772 | LocalContext context; |
| 14773 | v8::Isolate* isolate = context->GetIsolate(); |
| 14774 | v8::HandleScope scope(isolate); |
| 14775 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 14776 | templ->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 14777 | templ->Set(v8_str("a" ), v8_str("a" )); |
| 14778 | // Add more than 8 (see kMaxFastProperties) properties |
| 14779 | // so that the constructor will force copying map. |
| 14780 | // Cannot sprintf, gcc complains unsafety. |
| 14781 | char buf[4]; |
| 14782 | for (char i = '0'; i <= '9' ; i++) { |
| 14783 | buf[0] = i; |
| 14784 | for (char j = '0'; j <= '9'; j++) { |
| 14785 | buf[1] = j; |
| 14786 | for (char k = '0'; k <= '9'; k++) { |
| 14787 | buf[2] = k; |
| 14788 | buf[3] = 0; |
| 14789 | templ->Set(v8_str(buf), v8::Number::New(isolate, k)); |
| 14790 | } |
| 14791 | } |
| 14792 | } |
| 14793 | |
| 14794 | Local<v8::Object> instance_1 = |
| 14795 | templ->NewInstance(context.local()).ToLocalChecked(); |
| 14796 | CHECK(context->Global() |
| 14797 | ->Set(context.local(), v8_str("obj_1" ), instance_1) |
| 14798 | .FromJust()); |
| 14799 | |
| 14800 | Local<Value> value_1 = CompileRun("obj_1.a" ); |
| 14801 | CHECK(value_1.IsEmpty()); |
| 14802 | |
| 14803 | Local<v8::Object> instance_2 = |
| 14804 | templ->NewInstance(context.local()).ToLocalChecked(); |
| 14805 | CHECK(context->Global() |
| 14806 | ->Set(context.local(), v8_str("obj_2" ), instance_2) |
| 14807 | .FromJust()); |
| 14808 | |
| 14809 | Local<Value> value_2 = CompileRun("obj_2.a" ); |
| 14810 | CHECK(value_2.IsEmpty()); |
| 14811 | } |
| 14812 | |
| 14813 | |
| 14814 | // This tests that we do not allow dictionary load/call inline caches |
| 14815 | // to use functions that have not yet been compiled. The potential |
| 14816 | // problem of loading a function that has not yet been compiled can |
| 14817 | // arise because we share code between contexts via the compilation |
| 14818 | // cache. |
| 14819 | THREADED_TEST(DictionaryICLoadedFunction) { |
| 14820 | v8::HandleScope scope(CcTest::isolate()); |
| 14821 | // Test LoadIC. |
| 14822 | for (int i = 0; i < 2; i++) { |
| 14823 | LocalContext context; |
| 14824 | CHECK(context->Global() |
| 14825 | ->Set(context.local(), v8_str("tmp" ), v8::True(CcTest::isolate())) |
| 14826 | .FromJust()); |
| 14827 | context->Global()->Delete(context.local(), v8_str("tmp" )).FromJust(); |
| 14828 | CompileRun("for (var j = 0; j < 10; j++) new RegExp('');" ); |
| 14829 | } |
| 14830 | // Test CallIC. |
| 14831 | for (int i = 0; i < 2; i++) { |
| 14832 | LocalContext context; |
| 14833 | CHECK(context->Global() |
| 14834 | ->Set(context.local(), v8_str("tmp" ), v8::True(CcTest::isolate())) |
| 14835 | .FromJust()); |
| 14836 | context->Global()->Delete(context.local(), v8_str("tmp" )).FromJust(); |
| 14837 | CompileRun("for (var j = 0; j < 10; j++) RegExp('')" ); |
| 14838 | } |
| 14839 | } |
| 14840 | |
| 14841 | |
| 14842 | // Test that cross-context new calls use the context of the callee to |
| 14843 | // create the new JavaScript object. |
| 14844 | THREADED_TEST(CrossContextNew) { |
| 14845 | v8::Isolate* isolate = CcTest::isolate(); |
| 14846 | v8::HandleScope scope(isolate); |
| 14847 | v8::Local<Context> context0 = Context::New(isolate); |
| 14848 | v8::Local<Context> context1 = Context::New(isolate); |
| 14849 | |
| 14850 | // Allow cross-domain access. |
| 14851 | Local<String> token = v8_str("<security token>" ); |
| 14852 | context0->SetSecurityToken(token); |
| 14853 | context1->SetSecurityToken(token); |
| 14854 | |
| 14855 | // Set an 'x' property on the Object prototype and define a |
| 14856 | // constructor function in context0. |
| 14857 | context0->Enter(); |
| 14858 | CompileRun("Object.prototype.x = 42; function C() {};" ); |
| 14859 | context0->Exit(); |
| 14860 | |
| 14861 | // Call the constructor function from context0 and check that the |
| 14862 | // result has the 'x' property. |
| 14863 | context1->Enter(); |
| 14864 | CHECK(context1->Global() |
| 14865 | ->Set(context1, v8_str("other" ), context0->Global()) |
| 14866 | .FromJust()); |
| 14867 | Local<Value> value = CompileRun("var instance = new other.C(); instance.x" ); |
| 14868 | CHECK(value->IsInt32()); |
| 14869 | CHECK_EQ(42, value->Int32Value(context1).FromJust()); |
| 14870 | context1->Exit(); |
| 14871 | } |
| 14872 | |
| 14873 | |
| 14874 | // Verify that we can clone an object |
| 14875 | TEST(ObjectClone) { |
| 14876 | LocalContext env; |
| 14877 | v8::Isolate* isolate = env->GetIsolate(); |
| 14878 | v8::HandleScope scope(isolate); |
| 14879 | |
| 14880 | const char* sample = |
| 14881 | "var rv = {};" \ |
| 14882 | "rv.alpha = 'hello';" \ |
| 14883 | "rv.beta = 123;" \ |
| 14884 | "rv;" ; |
| 14885 | |
| 14886 | // Create an object, verify basics. |
| 14887 | Local<Value> val = CompileRun(sample); |
| 14888 | CHECK(val->IsObject()); |
| 14889 | Local<v8::Object> obj = val.As<v8::Object>(); |
| 14890 | obj->Set(env.local(), v8_str("gamma" ), v8_str("cloneme" )).FromJust(); |
| 14891 | |
| 14892 | CHECK(v8_str("hello" ) |
| 14893 | ->Equals(env.local(), |
| 14894 | obj->Get(env.local(), v8_str("alpha" )).ToLocalChecked()) |
| 14895 | .FromJust()); |
| 14896 | CHECK(v8::Integer::New(isolate, 123) |
| 14897 | ->Equals(env.local(), |
| 14898 | obj->Get(env.local(), v8_str("beta" )).ToLocalChecked()) |
| 14899 | .FromJust()); |
| 14900 | CHECK(v8_str("cloneme" ) |
| 14901 | ->Equals(env.local(), |
| 14902 | obj->Get(env.local(), v8_str("gamma" )).ToLocalChecked()) |
| 14903 | .FromJust()); |
| 14904 | |
| 14905 | // Clone it. |
| 14906 | Local<v8::Object> clone = obj->Clone(); |
| 14907 | CHECK(v8_str("hello" ) |
| 14908 | ->Equals(env.local(), |
| 14909 | clone->Get(env.local(), v8_str("alpha" )).ToLocalChecked()) |
| 14910 | .FromJust()); |
| 14911 | CHECK(v8::Integer::New(isolate, 123) |
| 14912 | ->Equals(env.local(), |
| 14913 | clone->Get(env.local(), v8_str("beta" )).ToLocalChecked()) |
| 14914 | .FromJust()); |
| 14915 | CHECK(v8_str("cloneme" ) |
| 14916 | ->Equals(env.local(), |
| 14917 | clone->Get(env.local(), v8_str("gamma" )).ToLocalChecked()) |
| 14918 | .FromJust()); |
| 14919 | |
| 14920 | // Set a property on the clone, verify each object. |
| 14921 | CHECK(clone->Set(env.local(), v8_str("beta" ), v8::Integer::New(isolate, 456)) |
| 14922 | .FromJust()); |
| 14923 | CHECK(v8::Integer::New(isolate, 123) |
| 14924 | ->Equals(env.local(), |
| 14925 | obj->Get(env.local(), v8_str("beta" )).ToLocalChecked()) |
| 14926 | .FromJust()); |
| 14927 | CHECK(v8::Integer::New(isolate, 456) |
| 14928 | ->Equals(env.local(), |
| 14929 | clone->Get(env.local(), v8_str("beta" )).ToLocalChecked()) |
| 14930 | .FromJust()); |
| 14931 | } |
| 14932 | |
| 14933 | |
| 14934 | class OneByteVectorResource : public v8::String::ExternalOneByteStringResource { |
| 14935 | public: |
| 14936 | explicit OneByteVectorResource(i::Vector<const char> vector) |
| 14937 | : data_(vector) {} |
| 14938 | ~OneByteVectorResource() override = default; |
| 14939 | size_t length() const override { return data_.length(); } |
| 14940 | const char* data() const override { return data_.start(); } |
| 14941 | void Dispose() override {} |
| 14942 | |
| 14943 | private: |
| 14944 | i::Vector<const char> data_; |
| 14945 | }; |
| 14946 | |
| 14947 | |
| 14948 | class UC16VectorResource : public v8::String::ExternalStringResource { |
| 14949 | public: |
| 14950 | explicit UC16VectorResource(i::Vector<const i::uc16> vector) |
| 14951 | : data_(vector) {} |
| 14952 | ~UC16VectorResource() override = default; |
| 14953 | size_t length() const override { return data_.length(); } |
| 14954 | const i::uc16* data() const override { return data_.start(); } |
| 14955 | void Dispose() override {} |
| 14956 | |
| 14957 | private: |
| 14958 | i::Vector<const i::uc16> data_; |
| 14959 | }; |
| 14960 | |
| 14961 | static void MorphAString(i::String string, |
| 14962 | OneByteVectorResource* one_byte_resource, |
| 14963 | UC16VectorResource* uc16_resource) { |
| 14964 | i::Isolate* isolate = CcTest::i_isolate(); |
| 14965 | CHECK(i::StringShape(string).IsExternal()); |
| 14966 | i::ReadOnlyRoots roots(CcTest::heap()); |
| 14967 | if (string->IsOneByteRepresentation()) { |
| 14968 | // Check old map is not internalized or long. |
| 14969 | CHECK(string->map() == roots.external_one_byte_string_map()); |
| 14970 | // Morph external string to be TwoByte string. |
| 14971 | string->set_map(roots.external_string_map()); |
| 14972 | i::ExternalTwoByteString morphed = i::ExternalTwoByteString::cast(string); |
| 14973 | CcTest::heap()->UpdateExternalString(morphed, string->length(), 0); |
| 14974 | morphed->SetResource(isolate, uc16_resource); |
| 14975 | } else { |
| 14976 | // Check old map is not internalized or long. |
| 14977 | CHECK(string->map() == roots.external_string_map()); |
| 14978 | // Morph external string to be one-byte string. |
| 14979 | string->set_map(roots.external_one_byte_string_map()); |
| 14980 | i::ExternalOneByteString morphed = i::ExternalOneByteString::cast(string); |
| 14981 | CcTest::heap()->UpdateExternalString(morphed, string->length(), 0); |
| 14982 | morphed->SetResource(isolate, one_byte_resource); |
| 14983 | } |
| 14984 | } |
| 14985 | |
| 14986 | // Test that we can still flatten a string if the components it is built up |
| 14987 | // from have been turned into 16 bit strings in the mean time. |
| 14988 | THREADED_TEST(MorphCompositeStringTest) { |
| 14989 | char utf_buffer[129]; |
| 14990 | const char* c_string = "Now is the time for all good men" |
| 14991 | " to come to the aid of the party" ; |
| 14992 | uint16_t* two_byte_string = AsciiToTwoByteString(c_string); |
| 14993 | { |
| 14994 | LocalContext env; |
| 14995 | i::Factory* factory = CcTest::i_isolate()->factory(); |
| 14996 | v8::Isolate* isolate = env->GetIsolate(); |
| 14997 | i::Isolate* i_isolate = CcTest::i_isolate(); |
| 14998 | v8::HandleScope scope(isolate); |
| 14999 | OneByteVectorResource one_byte_resource( |
| 15000 | i::Vector<const char>(c_string, i::StrLength(c_string))); |
| 15001 | UC16VectorResource uc16_resource( |
| 15002 | i::Vector<const uint16_t>(two_byte_string, i::StrLength(c_string))); |
| 15003 | |
| 15004 | Local<String> lhs(v8::Utils::ToLocal( |
| 15005 | factory->NewExternalStringFromOneByte(&one_byte_resource) |
| 15006 | .ToHandleChecked())); |
| 15007 | Local<String> rhs(v8::Utils::ToLocal( |
| 15008 | factory->NewExternalStringFromOneByte(&one_byte_resource) |
| 15009 | .ToHandleChecked())); |
| 15010 | |
| 15011 | CHECK(env->Global()->Set(env.local(), v8_str("lhs" ), lhs).FromJust()); |
| 15012 | CHECK(env->Global()->Set(env.local(), v8_str("rhs" ), rhs).FromJust()); |
| 15013 | |
| 15014 | CompileRun( |
| 15015 | "var cons = lhs + rhs;" |
| 15016 | "var slice = lhs.substring(1, lhs.length - 1);" |
| 15017 | "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);" ); |
| 15018 | |
| 15019 | CHECK(lhs->IsOneByte()); |
| 15020 | CHECK(rhs->IsOneByte()); |
| 15021 | |
| 15022 | i::String ilhs = *v8::Utils::OpenHandle(*lhs); |
| 15023 | i::String irhs = *v8::Utils::OpenHandle(*rhs); |
| 15024 | MorphAString(ilhs, &one_byte_resource, &uc16_resource); |
| 15025 | MorphAString(irhs, &one_byte_resource, &uc16_resource); |
| 15026 | |
| 15027 | // This should UTF-8 without flattening, since everything is ASCII. |
| 15028 | Local<String> cons = |
| 15029 | v8_compile("cons" )->Run(env.local()).ToLocalChecked().As<String>(); |
| 15030 | CHECK_EQ(128, cons->Utf8Length(isolate)); |
| 15031 | int nchars = -1; |
| 15032 | CHECK_EQ(129, cons->WriteUtf8(isolate, utf_buffer, -1, &nchars)); |
| 15033 | CHECK_EQ(128, nchars); |
| 15034 | CHECK_EQ(0, strcmp( |
| 15035 | utf_buffer, |
| 15036 | "Now is the time for all good men to come to the aid of the party" |
| 15037 | "Now is the time for all good men to come to the aid of the party" )); |
| 15038 | |
| 15039 | // Now do some stuff to make sure the strings are flattened, etc. |
| 15040 | CompileRun( |
| 15041 | "/[^a-z]/.test(cons);" |
| 15042 | "/[^a-z]/.test(slice);" |
| 15043 | "/[^a-z]/.test(slice_on_cons);" ); |
| 15044 | const char* expected_cons = |
| 15045 | "Now is the time for all good men to come to the aid of the party" |
| 15046 | "Now is the time for all good men to come to the aid of the party" ; |
| 15047 | const char* expected_slice = |
| 15048 | "ow is the time for all good men to come to the aid of the part" ; |
| 15049 | const char* expected_slice_on_cons = |
| 15050 | "ow is the time for all good men to come to the aid of the party" |
| 15051 | "Now is the time for all good men to come to the aid of the part" ; |
| 15052 | CHECK(v8_str(expected_cons) |
| 15053 | ->Equals(env.local(), env->Global() |
| 15054 | ->Get(env.local(), v8_str("cons" )) |
| 15055 | .ToLocalChecked()) |
| 15056 | .FromJust()); |
| 15057 | CHECK(v8_str(expected_slice) |
| 15058 | ->Equals(env.local(), env->Global() |
| 15059 | ->Get(env.local(), v8_str("slice" )) |
| 15060 | .ToLocalChecked()) |
| 15061 | .FromJust()); |
| 15062 | CHECK(v8_str(expected_slice_on_cons) |
| 15063 | ->Equals(env.local(), |
| 15064 | env->Global() |
| 15065 | ->Get(env.local(), v8_str("slice_on_cons" )) |
| 15066 | .ToLocalChecked()) |
| 15067 | .FromJust()); |
| 15068 | |
| 15069 | // This avoids the GC from trying to free a stack allocated resource. |
| 15070 | if (ilhs->IsExternalOneByteString()) |
| 15071 | i::ExternalOneByteString::cast(ilhs)->SetResource(i_isolate, nullptr); |
| 15072 | else |
| 15073 | i::ExternalTwoByteString::cast(ilhs)->SetResource(i_isolate, nullptr); |
| 15074 | if (irhs->IsExternalOneByteString()) |
| 15075 | i::ExternalOneByteString::cast(irhs)->SetResource(i_isolate, nullptr); |
| 15076 | else |
| 15077 | i::ExternalTwoByteString::cast(irhs)->SetResource(i_isolate, nullptr); |
| 15078 | } |
| 15079 | i::DeleteArray(two_byte_string); |
| 15080 | } |
| 15081 | |
| 15082 | |
| 15083 | TEST(CompileExternalTwoByteSource) { |
| 15084 | LocalContext context; |
| 15085 | v8::HandleScope scope(context->GetIsolate()); |
| 15086 | |
| 15087 | // This is a very short list of sources, which currently is to check for a |
| 15088 | // regression caused by r2703. |
| 15089 | const char* one_byte_sources[] = { |
| 15090 | "0.5" , |
| 15091 | "-0.5" , // This mainly testes PushBack in the Scanner. |
| 15092 | "--0.5" , // This mainly testes PushBack in the Scanner. |
| 15093 | nullptr}; |
| 15094 | |
| 15095 | // Compile the sources as external two byte strings. |
| 15096 | for (int i = 0; one_byte_sources[i] != nullptr; i++) { |
| 15097 | uint16_t* two_byte_string = AsciiToTwoByteString(one_byte_sources[i]); |
| 15098 | TestResource* uc16_resource = new TestResource(two_byte_string); |
| 15099 | v8::Local<v8::String> source = |
| 15100 | v8::String::NewExternalTwoByte(context->GetIsolate(), uc16_resource) |
| 15101 | .ToLocalChecked(); |
| 15102 | v8::Script::Compile(context.local(), source).FromMaybe(Local<Script>()); |
| 15103 | } |
| 15104 | } |
| 15105 | |
| 15106 | // Test that we cannot set a property on the global object if there |
| 15107 | // is a read-only property in the prototype chain. |
| 15108 | TEST(ReadOnlyPropertyInGlobalProto) { |
| 15109 | v8::Isolate* isolate = CcTest::isolate(); |
| 15110 | v8::HandleScope scope(isolate); |
| 15111 | v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| 15112 | LocalContext context(nullptr, templ); |
| 15113 | v8::Local<v8::Object> global = context->Global(); |
| 15114 | v8::Local<v8::Object> global_proto = v8::Local<v8::Object>::Cast( |
| 15115 | global->Get(context.local(), v8_str("__proto__" )).ToLocalChecked()); |
| 15116 | global_proto->DefineOwnProperty(context.local(), v8_str("x" ), |
| 15117 | v8::Integer::New(isolate, 0), v8::ReadOnly) |
| 15118 | .FromJust(); |
| 15119 | global_proto->DefineOwnProperty(context.local(), v8_str("y" ), |
| 15120 | v8::Integer::New(isolate, 0), v8::ReadOnly) |
| 15121 | .FromJust(); |
| 15122 | // Check without 'eval' or 'with'. |
| 15123 | v8::Local<v8::Value> res = |
| 15124 | CompileRun("function f() { x = 42; return x; }; f()" ); |
| 15125 | CHECK(v8::Integer::New(isolate, 0)->Equals(context.local(), res).FromJust()); |
| 15126 | // Check with 'eval'. |
| 15127 | res = CompileRun("function f() { eval('1'); y = 43; return y; }; f()" ); |
| 15128 | CHECK(v8::Integer::New(isolate, 0)->Equals(context.local(), res).FromJust()); |
| 15129 | // Check with 'with'. |
| 15130 | res = CompileRun("function f() { with (this) { y = 44 }; return y; }; f()" ); |
| 15131 | CHECK(v8::Integer::New(isolate, 0)->Equals(context.local(), res).FromJust()); |
| 15132 | } |
| 15133 | |
| 15134 | |
| 15135 | TEST(CreateDataProperty) { |
| 15136 | LocalContext env; |
| 15137 | v8::Isolate* isolate = env->GetIsolate(); |
| 15138 | v8::HandleScope handle_scope(isolate); |
| 15139 | |
| 15140 | CompileRun( |
| 15141 | "var a = {};" |
| 15142 | "var b = [];" |
| 15143 | "Object.defineProperty(a, 'foo', {value: 23});" |
| 15144 | "Object.defineProperty(a, 'bar', {value: 23, configurable: true});" ); |
| 15145 | |
| 15146 | v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast( |
| 15147 | env->Global()->Get(env.local(), v8_str("a" )).ToLocalChecked()); |
| 15148 | v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast( |
| 15149 | env->Global()->Get(env.local(), v8_str("b" )).ToLocalChecked()); |
| 15150 | { |
| 15151 | // Can't change a non-configurable properties. |
| 15152 | v8::TryCatch try_catch(isolate); |
| 15153 | CHECK(!obj->CreateDataProperty(env.local(), v8_str("foo" ), |
| 15154 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15155 | CHECK(!try_catch.HasCaught()); |
| 15156 | CHECK(obj->CreateDataProperty(env.local(), v8_str("bar" ), |
| 15157 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15158 | CHECK(!try_catch.HasCaught()); |
| 15159 | v8::Local<v8::Value> val = |
| 15160 | obj->Get(env.local(), v8_str("bar" )).ToLocalChecked(); |
| 15161 | CHECK(val->IsNumber()); |
| 15162 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15163 | } |
| 15164 | |
| 15165 | { |
| 15166 | // Set a regular property. |
| 15167 | v8::TryCatch try_catch(isolate); |
| 15168 | CHECK(obj->CreateDataProperty(env.local(), v8_str("blub" ), |
| 15169 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15170 | CHECK(!try_catch.HasCaught()); |
| 15171 | v8::Local<v8::Value> val = |
| 15172 | obj->Get(env.local(), v8_str("blub" )).ToLocalChecked(); |
| 15173 | CHECK(val->IsNumber()); |
| 15174 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15175 | } |
| 15176 | |
| 15177 | { |
| 15178 | // Set an indexed property. |
| 15179 | v8::TryCatch try_catch(isolate); |
| 15180 | CHECK(obj->CreateDataProperty(env.local(), v8_str("1" ), |
| 15181 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15182 | CHECK(!try_catch.HasCaught()); |
| 15183 | v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked(); |
| 15184 | CHECK(val->IsNumber()); |
| 15185 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15186 | } |
| 15187 | |
| 15188 | { |
| 15189 | // Special cases for arrays. |
| 15190 | v8::TryCatch try_catch(isolate); |
| 15191 | CHECK(!arr->CreateDataProperty(env.local(), v8_str("length" ), |
| 15192 | v8::Integer::New(isolate, 1)).FromJust()); |
| 15193 | CHECK(!try_catch.HasCaught()); |
| 15194 | } |
| 15195 | { |
| 15196 | // Special cases for arrays: index exceeds the array's length |
| 15197 | v8::TryCatch try_catch(isolate); |
| 15198 | CHECK(arr->CreateDataProperty(env.local(), 1, v8::Integer::New(isolate, 23)) |
| 15199 | .FromJust()); |
| 15200 | CHECK(!try_catch.HasCaught()); |
| 15201 | CHECK_EQ(2U, arr->Length()); |
| 15202 | v8::Local<v8::Value> val = arr->Get(env.local(), 1).ToLocalChecked(); |
| 15203 | CHECK(val->IsNumber()); |
| 15204 | CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust()); |
| 15205 | |
| 15206 | // Set an existing entry. |
| 15207 | CHECK(arr->CreateDataProperty(env.local(), 0, v8::Integer::New(isolate, 42)) |
| 15208 | .FromJust()); |
| 15209 | CHECK(!try_catch.HasCaught()); |
| 15210 | val = arr->Get(env.local(), 0).ToLocalChecked(); |
| 15211 | CHECK(val->IsNumber()); |
| 15212 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15213 | } |
| 15214 | |
| 15215 | CompileRun("Object.freeze(a);" ); |
| 15216 | { |
| 15217 | // Can't change non-extensible objects. |
| 15218 | v8::TryCatch try_catch(isolate); |
| 15219 | CHECK(!obj->CreateDataProperty(env.local(), v8_str("baz" ), |
| 15220 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15221 | CHECK(!try_catch.HasCaught()); |
| 15222 | } |
| 15223 | |
| 15224 | v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| 15225 | templ->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 15226 | v8::Local<v8::Object> access_checked = |
| 15227 | templ->NewInstance(env.local()).ToLocalChecked(); |
| 15228 | { |
| 15229 | v8::TryCatch try_catch(isolate); |
| 15230 | CHECK(access_checked->CreateDataProperty(env.local(), v8_str("foo" ), |
| 15231 | v8::Integer::New(isolate, 42)) |
| 15232 | .IsNothing()); |
| 15233 | CHECK(try_catch.HasCaught()); |
| 15234 | } |
| 15235 | } |
| 15236 | |
| 15237 | |
| 15238 | TEST(DefineOwnProperty) { |
| 15239 | LocalContext env; |
| 15240 | v8::Isolate* isolate = env->GetIsolate(); |
| 15241 | v8::HandleScope handle_scope(isolate); |
| 15242 | |
| 15243 | CompileRun( |
| 15244 | "var a = {};" |
| 15245 | "var b = [];" |
| 15246 | "Object.defineProperty(a, 'foo', {value: 23});" |
| 15247 | "Object.defineProperty(a, 'bar', {value: 23, configurable: true});" ); |
| 15248 | |
| 15249 | v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast( |
| 15250 | env->Global()->Get(env.local(), v8_str("a" )).ToLocalChecked()); |
| 15251 | v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast( |
| 15252 | env->Global()->Get(env.local(), v8_str("b" )).ToLocalChecked()); |
| 15253 | { |
| 15254 | // Can't change a non-configurable properties. |
| 15255 | v8::TryCatch try_catch(isolate); |
| 15256 | CHECK(!obj->DefineOwnProperty(env.local(), v8_str("foo" ), |
| 15257 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15258 | CHECK(!try_catch.HasCaught()); |
| 15259 | CHECK(obj->DefineOwnProperty(env.local(), v8_str("bar" ), |
| 15260 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15261 | CHECK(!try_catch.HasCaught()); |
| 15262 | v8::Local<v8::Value> val = |
| 15263 | obj->Get(env.local(), v8_str("bar" )).ToLocalChecked(); |
| 15264 | CHECK(val->IsNumber()); |
| 15265 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15266 | } |
| 15267 | |
| 15268 | { |
| 15269 | // Set a regular property. |
| 15270 | v8::TryCatch try_catch(isolate); |
| 15271 | CHECK(obj->DefineOwnProperty(env.local(), v8_str("blub" ), |
| 15272 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15273 | CHECK(!try_catch.HasCaught()); |
| 15274 | v8::Local<v8::Value> val = |
| 15275 | obj->Get(env.local(), v8_str("blub" )).ToLocalChecked(); |
| 15276 | CHECK(val->IsNumber()); |
| 15277 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15278 | } |
| 15279 | |
| 15280 | { |
| 15281 | // Set an indexed property. |
| 15282 | v8::TryCatch try_catch(isolate); |
| 15283 | CHECK(obj->DefineOwnProperty(env.local(), v8_str("1" ), |
| 15284 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15285 | CHECK(!try_catch.HasCaught()); |
| 15286 | v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked(); |
| 15287 | CHECK(val->IsNumber()); |
| 15288 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15289 | } |
| 15290 | |
| 15291 | { |
| 15292 | // Special cases for arrays. |
| 15293 | v8::TryCatch try_catch(isolate); |
| 15294 | CHECK(!arr->DefineOwnProperty(env.local(), v8_str("length" ), |
| 15295 | v8::Integer::New(isolate, 1)).FromJust()); |
| 15296 | CHECK(!try_catch.HasCaught()); |
| 15297 | } |
| 15298 | { |
| 15299 | // Special cases for arrays: index exceeds the array's length |
| 15300 | v8::TryCatch try_catch(isolate); |
| 15301 | CHECK(arr->DefineOwnProperty(env.local(), v8_str("1" ), |
| 15302 | v8::Integer::New(isolate, 23)).FromJust()); |
| 15303 | CHECK(!try_catch.HasCaught()); |
| 15304 | CHECK_EQ(2U, arr->Length()); |
| 15305 | v8::Local<v8::Value> val = arr->Get(env.local(), 1).ToLocalChecked(); |
| 15306 | CHECK(val->IsNumber()); |
| 15307 | CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust()); |
| 15308 | |
| 15309 | // Set an existing entry. |
| 15310 | CHECK(arr->DefineOwnProperty(env.local(), v8_str("0" ), |
| 15311 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15312 | CHECK(!try_catch.HasCaught()); |
| 15313 | val = arr->Get(env.local(), 0).ToLocalChecked(); |
| 15314 | CHECK(val->IsNumber()); |
| 15315 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15316 | } |
| 15317 | |
| 15318 | { |
| 15319 | // Set a non-writable property. |
| 15320 | v8::TryCatch try_catch(isolate); |
| 15321 | CHECK(obj->DefineOwnProperty(env.local(), v8_str("lala" ), |
| 15322 | v8::Integer::New(isolate, 42), |
| 15323 | v8::ReadOnly).FromJust()); |
| 15324 | CHECK(!try_catch.HasCaught()); |
| 15325 | v8::Local<v8::Value> val = |
| 15326 | obj->Get(env.local(), v8_str("lala" )).ToLocalChecked(); |
| 15327 | CHECK(val->IsNumber()); |
| 15328 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15329 | CHECK_EQ(v8::ReadOnly, obj->GetPropertyAttributes( |
| 15330 | env.local(), v8_str("lala" )).FromJust()); |
| 15331 | CHECK(!try_catch.HasCaught()); |
| 15332 | } |
| 15333 | |
| 15334 | CompileRun("Object.freeze(a);" ); |
| 15335 | { |
| 15336 | // Can't change non-extensible objects. |
| 15337 | v8::TryCatch try_catch(isolate); |
| 15338 | CHECK(!obj->DefineOwnProperty(env.local(), v8_str("baz" ), |
| 15339 | v8::Integer::New(isolate, 42)).FromJust()); |
| 15340 | CHECK(!try_catch.HasCaught()); |
| 15341 | } |
| 15342 | |
| 15343 | v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| 15344 | templ->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 15345 | v8::Local<v8::Object> access_checked = |
| 15346 | templ->NewInstance(env.local()).ToLocalChecked(); |
| 15347 | { |
| 15348 | v8::TryCatch try_catch(isolate); |
| 15349 | CHECK(access_checked->DefineOwnProperty(env.local(), v8_str("foo" ), |
| 15350 | v8::Integer::New(isolate, 42)) |
| 15351 | .IsNothing()); |
| 15352 | CHECK(try_catch.HasCaught()); |
| 15353 | } |
| 15354 | } |
| 15355 | |
| 15356 | TEST(DefineProperty) { |
| 15357 | LocalContext env; |
| 15358 | v8::Isolate* isolate = env->GetIsolate(); |
| 15359 | v8::HandleScope handle_scope(isolate); |
| 15360 | |
| 15361 | v8::Local<v8::Name> p; |
| 15362 | |
| 15363 | CompileRun( |
| 15364 | "var a = {};" |
| 15365 | "var b = [];" |
| 15366 | "Object.defineProperty(a, 'v1', {value: 23});" |
| 15367 | "Object.defineProperty(a, 'v2', {value: 23, configurable: true});" ); |
| 15368 | |
| 15369 | v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast( |
| 15370 | env->Global()->Get(env.local(), v8_str("a" )).ToLocalChecked()); |
| 15371 | v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast( |
| 15372 | env->Global()->Get(env.local(), v8_str("b" )).ToLocalChecked()); |
| 15373 | |
| 15374 | v8::PropertyDescriptor desc(v8_num(42)); |
| 15375 | { |
| 15376 | // Use a data descriptor. |
| 15377 | |
| 15378 | // Cannot change a non-configurable property. |
| 15379 | p = v8_str("v1" ); |
| 15380 | v8::TryCatch try_catch(isolate); |
| 15381 | CHECK(!obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15382 | CHECK(!try_catch.HasCaught()); |
| 15383 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15384 | CHECK(val->IsNumber()); |
| 15385 | CHECK_EQ(23.0, val->NumberValue(env.local()).FromJust()); |
| 15386 | |
| 15387 | // Change a configurable property. |
| 15388 | p = v8_str("v2" ); |
| 15389 | obj->DefineProperty(env.local(), p, desc).FromJust(); |
| 15390 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15391 | CHECK(!try_catch.HasCaught()); |
| 15392 | val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15393 | CHECK(val->IsNumber()); |
| 15394 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15395 | |
| 15396 | // Check that missing writable has default value false. |
| 15397 | p = v8_str("v12" ); |
| 15398 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15399 | CHECK(!try_catch.HasCaught()); |
| 15400 | val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15401 | CHECK(val->IsNumber()); |
| 15402 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15403 | v8::PropertyDescriptor desc2(v8_num(43)); |
| 15404 | CHECK(!obj->DefineProperty(env.local(), p, desc2).FromJust()); |
| 15405 | val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15406 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15407 | CHECK(!try_catch.HasCaught()); |
| 15408 | } |
| 15409 | |
| 15410 | { |
| 15411 | // Set a regular property. |
| 15412 | p = v8_str("v3" ); |
| 15413 | v8::TryCatch try_catch(isolate); |
| 15414 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15415 | CHECK(!try_catch.HasCaught()); |
| 15416 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15417 | CHECK(val->IsNumber()); |
| 15418 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15419 | } |
| 15420 | |
| 15421 | { |
| 15422 | // Set an indexed property. |
| 15423 | v8::TryCatch try_catch(isolate); |
| 15424 | CHECK(obj->DefineProperty(env.local(), v8_str("1" ), desc).FromJust()); |
| 15425 | CHECK(!try_catch.HasCaught()); |
| 15426 | v8::Local<v8::Value> val = obj->Get(env.local(), 1).ToLocalChecked(); |
| 15427 | CHECK(val->IsNumber()); |
| 15428 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15429 | } |
| 15430 | |
| 15431 | { |
| 15432 | // No special case when changing array length. |
| 15433 | v8::TryCatch try_catch(isolate); |
| 15434 | // Use a writable descriptor, otherwise the next test, that changes |
| 15435 | // the array length will fail. |
| 15436 | v8::PropertyDescriptor desc(v8_num(42), true); |
| 15437 | CHECK(arr->DefineProperty(env.local(), v8_str("length" ), desc).FromJust()); |
| 15438 | CHECK(!try_catch.HasCaught()); |
| 15439 | } |
| 15440 | |
| 15441 | { |
| 15442 | // Special cases for arrays: index exceeds the array's length. |
| 15443 | v8::TryCatch try_catch(isolate); |
| 15444 | CHECK(arr->DefineProperty(env.local(), v8_str("100" ), desc).FromJust()); |
| 15445 | CHECK(!try_catch.HasCaught()); |
| 15446 | CHECK_EQ(101U, arr->Length()); |
| 15447 | v8::Local<v8::Value> val = arr->Get(env.local(), 100).ToLocalChecked(); |
| 15448 | CHECK(val->IsNumber()); |
| 15449 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15450 | |
| 15451 | // Set an existing entry. |
| 15452 | CHECK(arr->DefineProperty(env.local(), v8_str("0" ), desc).FromJust()); |
| 15453 | CHECK(!try_catch.HasCaught()); |
| 15454 | val = arr->Get(env.local(), 0).ToLocalChecked(); |
| 15455 | CHECK(val->IsNumber()); |
| 15456 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15457 | } |
| 15458 | |
| 15459 | { |
| 15460 | // Use a generic descriptor. |
| 15461 | v8::PropertyDescriptor desc_generic; |
| 15462 | |
| 15463 | p = v8_str("v4" ); |
| 15464 | v8::TryCatch try_catch(isolate); |
| 15465 | CHECK(obj->DefineProperty(env.local(), p, desc_generic).FromJust()); |
| 15466 | CHECK(!try_catch.HasCaught()); |
| 15467 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15468 | CHECK(val->IsUndefined()); |
| 15469 | |
| 15470 | obj->Set(env.local(), p, v8_num(1)).FromJust(); |
| 15471 | CHECK(!try_catch.HasCaught()); |
| 15472 | |
| 15473 | val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15474 | CHECK(val->IsUndefined()); |
| 15475 | CHECK(!try_catch.HasCaught()); |
| 15476 | } |
| 15477 | |
| 15478 | { |
| 15479 | // Use a data descriptor with undefined value. |
| 15480 | v8::PropertyDescriptor desc_empty(v8::Undefined(isolate)); |
| 15481 | |
| 15482 | v8::TryCatch try_catch(isolate); |
| 15483 | CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); |
| 15484 | CHECK(!try_catch.HasCaught()); |
| 15485 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15486 | CHECK(val->IsUndefined()); |
| 15487 | CHECK(!try_catch.HasCaught()); |
| 15488 | } |
| 15489 | |
| 15490 | { |
| 15491 | // Use a descriptor with attribute == v8::ReadOnly. |
| 15492 | v8::PropertyDescriptor desc_read_only(v8_num(42), false); |
| 15493 | desc_read_only.set_enumerable(true); |
| 15494 | desc_read_only.set_configurable(true); |
| 15495 | |
| 15496 | p = v8_str("v5" ); |
| 15497 | v8::TryCatch try_catch(isolate); |
| 15498 | CHECK(obj->DefineProperty(env.local(), p, desc_read_only).FromJust()); |
| 15499 | CHECK(!try_catch.HasCaught()); |
| 15500 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15501 | CHECK(val->IsNumber()); |
| 15502 | CHECK_EQ(42.0, val->NumberValue(env.local()).FromJust()); |
| 15503 | CHECK_EQ(v8::ReadOnly, |
| 15504 | obj->GetPropertyAttributes(env.local(), p).FromJust()); |
| 15505 | CHECK(!try_catch.HasCaught()); |
| 15506 | } |
| 15507 | |
| 15508 | { |
| 15509 | // Use an accessor descriptor with empty handles. |
| 15510 | v8::PropertyDescriptor desc_empty(v8::Undefined(isolate), |
| 15511 | v8::Undefined(isolate)); |
| 15512 | |
| 15513 | p = v8_str("v6" ); |
| 15514 | v8::TryCatch try_catch(isolate); |
| 15515 | CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); |
| 15516 | CHECK(!try_catch.HasCaught()); |
| 15517 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15518 | CHECK(val->IsUndefined()); |
| 15519 | CHECK(!try_catch.HasCaught()); |
| 15520 | } |
| 15521 | |
| 15522 | { |
| 15523 | // Use an accessor descriptor. |
| 15524 | CompileRun( |
| 15525 | "var set = function(x) {this.val = 2*x;};" |
| 15526 | "var get = function() {return this.val || 0;};" ); |
| 15527 | |
| 15528 | v8::Local<v8::Function> get = v8::Local<v8::Function>::Cast( |
| 15529 | env->Global()->Get(env.local(), v8_str("get" )).ToLocalChecked()); |
| 15530 | v8::Local<v8::Function> set = v8::Local<v8::Function>::Cast( |
| 15531 | env->Global()->Get(env.local(), v8_str("set" )).ToLocalChecked()); |
| 15532 | v8::PropertyDescriptor desc(get, set); |
| 15533 | |
| 15534 | p = v8_str("v7" ); |
| 15535 | v8::TryCatch try_catch(isolate); |
| 15536 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15537 | CHECK(!try_catch.HasCaught()); |
| 15538 | |
| 15539 | v8::Local<v8::Value> val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15540 | CHECK(val->IsNumber()); |
| 15541 | CHECK_EQ(0.0, val->NumberValue(env.local()).FromJust()); |
| 15542 | CHECK(!try_catch.HasCaught()); |
| 15543 | |
| 15544 | obj->Set(env.local(), p, v8_num(7)).FromJust(); |
| 15545 | CHECK(!try_catch.HasCaught()); |
| 15546 | |
| 15547 | val = obj->Get(env.local(), p).ToLocalChecked(); |
| 15548 | CHECK(val->IsNumber()); |
| 15549 | CHECK_EQ(14.0, val->NumberValue(env.local()).FromJust()); |
| 15550 | CHECK(!try_catch.HasCaught()); |
| 15551 | } |
| 15552 | |
| 15553 | { |
| 15554 | // Redefine an existing property. |
| 15555 | |
| 15556 | // desc = {value: 42, enumerable: true} |
| 15557 | v8::PropertyDescriptor desc(v8_num(42)); |
| 15558 | desc.set_enumerable(true); |
| 15559 | |
| 15560 | p = v8_str("v8" ); |
| 15561 | v8::TryCatch try_catch(isolate); |
| 15562 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15563 | CHECK(!try_catch.HasCaught()); |
| 15564 | |
| 15565 | // desc = {enumerable: true} |
| 15566 | v8::PropertyDescriptor desc_true((v8::Local<v8::Value>())); |
| 15567 | desc_true.set_enumerable(true); |
| 15568 | |
| 15569 | // Successful redefinition because all present attributes have the same |
| 15570 | // value as the current descriptor. |
| 15571 | CHECK(obj->DefineProperty(env.local(), p, desc_true).FromJust()); |
| 15572 | CHECK(!try_catch.HasCaught()); |
| 15573 | |
| 15574 | // desc = {} |
| 15575 | v8::PropertyDescriptor desc_empty; |
| 15576 | // Successful redefinition because no attributes are overwritten in the |
| 15577 | // current descriptor. |
| 15578 | CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); |
| 15579 | CHECK(!try_catch.HasCaught()); |
| 15580 | |
| 15581 | // desc = {enumerable: false} |
| 15582 | v8::PropertyDescriptor desc_false((v8::Local<v8::Value>())); |
| 15583 | desc_false.set_enumerable(false); |
| 15584 | // Not successful because we cannot define a different value for enumerable. |
| 15585 | CHECK(!obj->DefineProperty(env.local(), p, desc_false).FromJust()); |
| 15586 | CHECK(!try_catch.HasCaught()); |
| 15587 | } |
| 15588 | |
| 15589 | { |
| 15590 | // Redefine a property that has a getter. |
| 15591 | CompileRun("var get = function() {};" ); |
| 15592 | v8::Local<v8::Function> get = v8::Local<v8::Function>::Cast( |
| 15593 | env->Global()->Get(env.local(), v8_str("get" )).ToLocalChecked()); |
| 15594 | |
| 15595 | // desc = {get: function() {}} |
| 15596 | v8::PropertyDescriptor desc(get, v8::Local<v8::Function>()); |
| 15597 | v8::TryCatch try_catch(isolate); |
| 15598 | |
| 15599 | p = v8_str("v9" ); |
| 15600 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15601 | CHECK(!try_catch.HasCaught()); |
| 15602 | |
| 15603 | // desc_empty = {} |
| 15604 | // Successful because we are not redefining the current getter. |
| 15605 | v8::PropertyDescriptor desc_empty; |
| 15606 | CHECK(obj->DefineProperty(env.local(), p, desc_empty).FromJust()); |
| 15607 | CHECK(!try_catch.HasCaught()); |
| 15608 | |
| 15609 | // desc = {get: function() {}} |
| 15610 | // Successful because we redefine the getter with its current value. |
| 15611 | CHECK(obj->DefineProperty(env.local(), p, desc).FromJust()); |
| 15612 | CHECK(!try_catch.HasCaught()); |
| 15613 | |
| 15614 | // desc = {get: undefined} |
| 15615 | v8::PropertyDescriptor desc_undefined(v8::Undefined(isolate), |
| 15616 | v8::Local<v8::Function>()); |
| 15617 | // Not successful because we cannot redefine with the current value of get |
| 15618 | // with undefined. |
| 15619 | CHECK(!obj->DefineProperty(env.local(), p, desc_undefined).FromJust()); |
| 15620 | CHECK(!try_catch.HasCaught()); |
| 15621 | } |
| 15622 | |
| 15623 | CompileRun("Object.freeze(a);" ); |
| 15624 | { |
| 15625 | // We cannot change non-extensible objects. |
| 15626 | v8::TryCatch try_catch(isolate); |
| 15627 | CHECK(!obj->DefineProperty(env.local(), v8_str("v10" ), desc).FromJust()); |
| 15628 | CHECK(!try_catch.HasCaught()); |
| 15629 | } |
| 15630 | |
| 15631 | v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| 15632 | templ->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 15633 | v8::Local<v8::Object> access_checked = |
| 15634 | templ->NewInstance(env.local()).ToLocalChecked(); |
| 15635 | { |
| 15636 | v8::TryCatch try_catch(isolate); |
| 15637 | CHECK(access_checked->DefineProperty(env.local(), v8_str("v11" ), desc) |
| 15638 | .IsNothing()); |
| 15639 | CHECK(try_catch.HasCaught()); |
| 15640 | } |
| 15641 | } |
| 15642 | |
| 15643 | THREADED_TEST(GetCurrentContextWhenNotInContext) { |
| 15644 | i::Isolate* isolate = CcTest::i_isolate(); |
| 15645 | CHECK_NOT_NULL(isolate); |
| 15646 | CHECK(isolate->context().is_null()); |
| 15647 | v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); |
| 15648 | v8::HandleScope scope(v8_isolate); |
| 15649 | // The following should not crash, but return an empty handle. |
| 15650 | v8::Local<v8::Context> current = v8_isolate->GetCurrentContext(); |
| 15651 | CHECK(current.IsEmpty()); |
| 15652 | } |
| 15653 | |
| 15654 | |
| 15655 | // Check that a variable declaration with no explicit initialization |
| 15656 | // value does shadow an existing property in the prototype chain. |
| 15657 | THREADED_TEST(InitGlobalVarInProtoChain) { |
| 15658 | LocalContext context; |
| 15659 | v8::HandleScope scope(context->GetIsolate()); |
| 15660 | // Introduce a variable in the prototype chain. |
| 15661 | CompileRun("__proto__.x = 42" ); |
| 15662 | v8::Local<v8::Value> result = CompileRun("var x = 43; x" ); |
| 15663 | CHECK(!result->IsUndefined()); |
| 15664 | CHECK_EQ(43, result->Int32Value(context.local()).FromJust()); |
| 15665 | } |
| 15666 | |
| 15667 | |
| 15668 | // Regression test for issue 398. |
| 15669 | // If a function is added to an object, creating a constant function |
| 15670 | // field, and the result is cloned, replacing the constant function on the |
| 15671 | // original should not affect the clone. |
| 15672 | // See http://code.google.com/p/v8/issues/detail?id=398 |
| 15673 | THREADED_TEST(ReplaceConstantFunction) { |
| 15674 | LocalContext context; |
| 15675 | v8::Isolate* isolate = context->GetIsolate(); |
| 15676 | v8::HandleScope scope(isolate); |
| 15677 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 15678 | v8::Local<v8::FunctionTemplate> func_templ = |
| 15679 | v8::FunctionTemplate::New(isolate); |
| 15680 | v8::Local<v8::String> foo_string = v8_str("foo" ); |
| 15681 | obj->Set(context.local(), foo_string, |
| 15682 | func_templ->GetFunction(context.local()).ToLocalChecked()) |
| 15683 | .FromJust(); |
| 15684 | v8::Local<v8::Object> obj_clone = obj->Clone(); |
| 15685 | obj_clone->Set(context.local(), foo_string, v8_str("Hello" )).FromJust(); |
| 15686 | CHECK(!obj->Get(context.local(), foo_string).ToLocalChecked()->IsUndefined()); |
| 15687 | } |
| 15688 | |
| 15689 | |
| 15690 | static void CheckElementValue(i::Isolate* isolate, |
| 15691 | int expected, |
| 15692 | i::Handle<i::Object> obj, |
| 15693 | int offset) { |
| 15694 | i::Object element = |
| 15695 | *i::Object::GetElement(isolate, obj, offset).ToHandleChecked(); |
| 15696 | CHECK_EQ(expected, i::Smi::ToInt(element)); |
| 15697 | } |
| 15698 | |
| 15699 | |
| 15700 | template <class ExternalArrayClass, class ElementType> |
| 15701 | static void ObjectWithExternalArrayTestHelper(Local<Context> context, |
| 15702 | v8::Local<Object> obj, |
| 15703 | int element_count, |
| 15704 | i::ExternalArrayType array_type, |
| 15705 | int64_t low, int64_t high) { |
| 15706 | i::Handle<i::JSReceiver> jsobj = v8::Utils::OpenHandle(*obj); |
| 15707 | v8::Isolate* v8_isolate = context->GetIsolate(); |
| 15708 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); |
| 15709 | obj->Set(context, v8_str("field" ), v8::Int32::New(v8_isolate, 1503)) |
| 15710 | .FromJust(); |
| 15711 | CHECK(context->Global()->Set(context, v8_str("ext_array" ), obj).FromJust()); |
| 15712 | v8::Local<v8::Value> result = CompileRun("ext_array.field" ); |
| 15713 | CHECK_EQ(1503, result->Int32Value(context).FromJust()); |
| 15714 | result = CompileRun("ext_array[1]" ); |
| 15715 | CHECK_EQ(1, result->Int32Value(context).FromJust()); |
| 15716 | |
| 15717 | // Check assigned smis |
| 15718 | result = CompileRun("for (var i = 0; i < 8; i++) {" |
| 15719 | " ext_array[i] = i;" |
| 15720 | "}" |
| 15721 | "var sum = 0;" |
| 15722 | "for (var i = 0; i < 8; i++) {" |
| 15723 | " sum += ext_array[i];" |
| 15724 | "}" |
| 15725 | "sum;" ); |
| 15726 | |
| 15727 | CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| 15728 | // Check pass through of assigned smis |
| 15729 | result = CompileRun("var sum = 0;" |
| 15730 | "for (var i = 0; i < 8; i++) {" |
| 15731 | " sum += ext_array[i] = ext_array[i] = -i;" |
| 15732 | "}" |
| 15733 | "sum;" ); |
| 15734 | CHECK_EQ(-28, result->Int32Value(context).FromJust()); |
| 15735 | |
| 15736 | |
| 15737 | // Check assigned smis in reverse order |
| 15738 | result = CompileRun("for (var i = 8; --i >= 0; ) {" |
| 15739 | " ext_array[i] = i;" |
| 15740 | "}" |
| 15741 | "var sum = 0;" |
| 15742 | "for (var i = 0; i < 8; i++) {" |
| 15743 | " sum += ext_array[i];" |
| 15744 | "}" |
| 15745 | "sum;" ); |
| 15746 | CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| 15747 | |
| 15748 | // Check pass through of assigned HeapNumbers |
| 15749 | result = CompileRun("var sum = 0;" |
| 15750 | "for (var i = 0; i < 16; i+=2) {" |
| 15751 | " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" |
| 15752 | "}" |
| 15753 | "sum;" ); |
| 15754 | CHECK_EQ(-28, result->Int32Value(context).FromJust()); |
| 15755 | |
| 15756 | // Check assigned HeapNumbers |
| 15757 | result = CompileRun("for (var i = 0; i < 16; i+=2) {" |
| 15758 | " ext_array[i] = (i * 0.5);" |
| 15759 | "}" |
| 15760 | "var sum = 0;" |
| 15761 | "for (var i = 0; i < 16; i+=2) {" |
| 15762 | " sum += ext_array[i];" |
| 15763 | "}" |
| 15764 | "sum;" ); |
| 15765 | CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| 15766 | |
| 15767 | // Check assigned HeapNumbers in reverse order |
| 15768 | result = CompileRun("for (var i = 14; i >= 0; i-=2) {" |
| 15769 | " ext_array[i] = (i * 0.5);" |
| 15770 | "}" |
| 15771 | "var sum = 0;" |
| 15772 | "for (var i = 0; i < 16; i+=2) {" |
| 15773 | " sum += ext_array[i];" |
| 15774 | "}" |
| 15775 | "sum;" ); |
| 15776 | CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| 15777 | |
| 15778 | i::ScopedVector<char> test_buf(1024); |
| 15779 | |
| 15780 | // Check legal boundary conditions. |
| 15781 | // The repeated loads and stores ensure the ICs are exercised. |
| 15782 | const char* boundary_program = |
| 15783 | "var res = 0;" |
| 15784 | "for (var i = 0; i < 16; i++) {" |
| 15785 | " ext_array[i] = %lld;" |
| 15786 | " if (i > 8) {" |
| 15787 | " res = ext_array[i];" |
| 15788 | " }" |
| 15789 | "}" |
| 15790 | "res;" ; |
| 15791 | i::SNPrintF(test_buf, |
| 15792 | boundary_program, |
| 15793 | low); |
| 15794 | result = CompileRun(test_buf.start()); |
| 15795 | CHECK_EQ(low, result->IntegerValue(context).FromJust()); |
| 15796 | |
| 15797 | i::SNPrintF(test_buf, |
| 15798 | boundary_program, |
| 15799 | high); |
| 15800 | result = CompileRun(test_buf.start()); |
| 15801 | CHECK_EQ(high, result->IntegerValue(context).FromJust()); |
| 15802 | |
| 15803 | // Check misprediction of type in IC. |
| 15804 | result = CompileRun("var tmp_array = ext_array;" |
| 15805 | "var sum = 0;" |
| 15806 | "for (var i = 0; i < 8; i++) {" |
| 15807 | " tmp_array[i] = i;" |
| 15808 | " sum += tmp_array[i];" |
| 15809 | " if (i == 4) {" |
| 15810 | " tmp_array = {};" |
| 15811 | " }" |
| 15812 | "}" |
| 15813 | "sum;" ); |
| 15814 | // Force GC to trigger verification. |
| 15815 | CcTest::CollectAllGarbage(); |
| 15816 | CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| 15817 | |
| 15818 | // Make sure out-of-range loads do not throw. |
| 15819 | i::SNPrintF(test_buf, |
| 15820 | "var caught_exception = false;" |
| 15821 | "try {" |
| 15822 | " ext_array[%d];" |
| 15823 | "} catch (e) {" |
| 15824 | " caught_exception = true;" |
| 15825 | "}" |
| 15826 | "caught_exception;" , |
| 15827 | element_count); |
| 15828 | result = CompileRun(test_buf.start()); |
| 15829 | CHECK(!result->BooleanValue(v8_isolate)); |
| 15830 | |
| 15831 | // Make sure out-of-range stores do not throw. |
| 15832 | i::SNPrintF(test_buf, |
| 15833 | "var caught_exception = false;" |
| 15834 | "try {" |
| 15835 | " ext_array[%d] = 1;" |
| 15836 | "} catch (e) {" |
| 15837 | " caught_exception = true;" |
| 15838 | "}" |
| 15839 | "caught_exception;" , |
| 15840 | element_count); |
| 15841 | result = CompileRun(test_buf.start()); |
| 15842 | CHECK(!result->BooleanValue(v8_isolate)); |
| 15843 | |
| 15844 | // Check other boundary conditions, values and operations. |
| 15845 | result = CompileRun("for (var i = 0; i < 8; i++) {" |
| 15846 | " ext_array[7] = undefined;" |
| 15847 | "}" |
| 15848 | "ext_array[7];" ); |
| 15849 | CHECK_EQ(0, result->Int32Value(context).FromJust()); |
| 15850 | if (array_type == i::kExternalFloat64Array || |
| 15851 | array_type == i::kExternalFloat32Array) { |
| 15852 | CHECK(std::isnan( |
| 15853 | i::Object::GetElement(isolate, jsobj, 7).ToHandleChecked()->Number())); |
| 15854 | } else { |
| 15855 | CheckElementValue(isolate, 0, jsobj, 7); |
| 15856 | } |
| 15857 | |
| 15858 | result = CompileRun("for (var i = 0; i < 8; i++) {" |
| 15859 | " ext_array[6] = '2.3';" |
| 15860 | "}" |
| 15861 | "ext_array[6];" ); |
| 15862 | CHECK_EQ(2, result->Int32Value(context).FromJust()); |
| 15863 | CHECK_EQ(2, |
| 15864 | static_cast<int>( |
| 15865 | i::Object::GetElement( |
| 15866 | isolate, jsobj, 6).ToHandleChecked()->Number())); |
| 15867 | |
| 15868 | if (array_type != i::kExternalFloat32Array && |
| 15869 | array_type != i::kExternalFloat64Array) { |
| 15870 | // Though the specification doesn't state it, be explicit about |
| 15871 | // converting NaNs and +/-Infinity to zero. |
| 15872 | result = CompileRun("for (var i = 0; i < 8; i++) {" |
| 15873 | " ext_array[i] = 5;" |
| 15874 | "}" |
| 15875 | "for (var i = 0; i < 8; i++) {" |
| 15876 | " ext_array[i] = NaN;" |
| 15877 | "}" |
| 15878 | "ext_array[5];" ); |
| 15879 | CHECK_EQ(0, result->Int32Value(context).FromJust()); |
| 15880 | CheckElementValue(isolate, 0, jsobj, 5); |
| 15881 | |
| 15882 | result = CompileRun("for (var i = 0; i < 8; i++) {" |
| 15883 | " ext_array[i] = 5;" |
| 15884 | "}" |
| 15885 | "for (var i = 0; i < 8; i++) {" |
| 15886 | " ext_array[i] = Infinity;" |
| 15887 | "}" |
| 15888 | "ext_array[5];" ); |
| 15889 | int expected_value = |
| 15890 | (array_type == i::kExternalUint8ClampedArray) ? 255 : 0; |
| 15891 | CHECK_EQ(expected_value, result->Int32Value(context).FromJust()); |
| 15892 | CheckElementValue(isolate, expected_value, jsobj, 5); |
| 15893 | |
| 15894 | result = CompileRun("for (var i = 0; i < 8; i++) {" |
| 15895 | " ext_array[i] = 5;" |
| 15896 | "}" |
| 15897 | "for (var i = 0; i < 8; i++) {" |
| 15898 | " ext_array[i] = -Infinity;" |
| 15899 | "}" |
| 15900 | "ext_array[5];" ); |
| 15901 | CHECK_EQ(0, result->Int32Value(context).FromJust()); |
| 15902 | CheckElementValue(isolate, 0, jsobj, 5); |
| 15903 | |
| 15904 | // Check truncation behavior of integral arrays. |
| 15905 | const char* unsigned_data = |
| 15906 | "var source_data = [0.6, 10.6];" |
| 15907 | "var expected_results = [0, 10];" ; |
| 15908 | const char* signed_data = |
| 15909 | "var source_data = [0.6, 10.6, -0.6, -10.6];" |
| 15910 | "var expected_results = [0, 10, 0, -10];" ; |
| 15911 | const char* pixel_data = |
| 15912 | "var source_data = [0.6, 10.6];" |
| 15913 | "var expected_results = [1, 11];" ; |
| 15914 | bool is_unsigned = (array_type == i::kExternalUint8Array || |
| 15915 | array_type == i::kExternalUint16Array || |
| 15916 | array_type == i::kExternalUint32Array); |
| 15917 | bool is_pixel_data = array_type == i::kExternalUint8ClampedArray; |
| 15918 | |
| 15919 | i::SNPrintF(test_buf, |
| 15920 | "%s" |
| 15921 | "var all_passed = true;" |
| 15922 | "for (var i = 0; i < source_data.length; i++) {" |
| 15923 | " for (var j = 0; j < 8; j++) {" |
| 15924 | " ext_array[j] = source_data[i];" |
| 15925 | " }" |
| 15926 | " all_passed = all_passed &&" |
| 15927 | " (ext_array[5] == expected_results[i]);" |
| 15928 | "}" |
| 15929 | "all_passed;" , |
| 15930 | (is_unsigned ? |
| 15931 | unsigned_data : |
| 15932 | (is_pixel_data ? pixel_data : signed_data))); |
| 15933 | result = CompileRun(test_buf.start()); |
| 15934 | CHECK(result->BooleanValue(v8_isolate)); |
| 15935 | } |
| 15936 | |
| 15937 | i::Handle<ExternalArrayClass> array( |
| 15938 | ExternalArrayClass::cast(i::Handle<i::JSObject>::cast(jsobj)->elements()), |
| 15939 | isolate); |
| 15940 | for (int i = 0; i < element_count; i++) { |
| 15941 | array->set(i, static_cast<ElementType>(i)); |
| 15942 | } |
| 15943 | |
| 15944 | bool old_natives_flag_sentry = i::FLAG_allow_natives_syntax; |
| 15945 | i::FLAG_allow_natives_syntax = true; |
| 15946 | |
| 15947 | // Test complex assignments |
| 15948 | result = CompileRun( |
| 15949 | "function ee_op_test_complex_func(sum) {" |
| 15950 | " for (var i = 0; i < 40; ++i) {" |
| 15951 | " sum += (ext_array[i] += 1);" |
| 15952 | " sum += (ext_array[i] -= 1);" |
| 15953 | " } " |
| 15954 | " return sum;" |
| 15955 | "}" |
| 15956 | "sum=0;" |
| 15957 | "sum=ee_op_test_complex_func(sum);" |
| 15958 | "sum=ee_op_test_complex_func(sum);" |
| 15959 | "%OptimizeFunctionOnNextCall(ee_op_test_complex_func);" |
| 15960 | "sum=ee_op_test_complex_func(sum);" |
| 15961 | "sum;" ); |
| 15962 | CHECK_EQ(4800, result->Int32Value(context).FromJust()); |
| 15963 | |
| 15964 | // Test count operations |
| 15965 | result = CompileRun( |
| 15966 | "function ee_op_test_count_func(sum) {" |
| 15967 | " for (var i = 0; i < 40; ++i) {" |
| 15968 | " sum += (++ext_array[i]);" |
| 15969 | " sum += (--ext_array[i]);" |
| 15970 | " } " |
| 15971 | " return sum;" |
| 15972 | "}" |
| 15973 | "sum=0;" |
| 15974 | "sum=ee_op_test_count_func(sum);" |
| 15975 | "sum=ee_op_test_count_func(sum);" |
| 15976 | "%OptimizeFunctionOnNextCall(ee_op_test_count_func);" |
| 15977 | "sum=ee_op_test_count_func(sum);" |
| 15978 | "sum;" ); |
| 15979 | CHECK_EQ(4800, result->Int32Value(context).FromJust()); |
| 15980 | |
| 15981 | i::FLAG_allow_natives_syntax = old_natives_flag_sentry; |
| 15982 | |
| 15983 | result = CompileRun("ext_array[3] = 33;" |
| 15984 | "delete ext_array[3];" |
| 15985 | "ext_array[3];" ); |
| 15986 | CHECK_EQ(33, result->Int32Value(context).FromJust()); |
| 15987 | |
| 15988 | result = CompileRun( |
| 15989 | "ext_array[0] = 10; ext_array[1] = 11;" |
| 15990 | "ext_array[2] = 12; ext_array[3] = 13;" |
| 15991 | "try { ext_array.__defineGetter__('2', function() { return 120; }); }" |
| 15992 | "catch (e) { }" |
| 15993 | "ext_array[2];" ); |
| 15994 | CHECK_EQ(12, result->Int32Value(context).FromJust()); |
| 15995 | |
| 15996 | result = CompileRun("var js_array = new Array(40);" |
| 15997 | "js_array[0] = 77;" |
| 15998 | "js_array;" ); |
| 15999 | CHECK_EQ(77, v8::Object::Cast(*result) |
| 16000 | ->Get(context, v8_str("0" )) |
| 16001 | .ToLocalChecked() |
| 16002 | ->Int32Value(context) |
| 16003 | .FromJust()); |
| 16004 | |
| 16005 | result = CompileRun("ext_array[1] = 23;" |
| 16006 | "ext_array.__proto__ = [];" |
| 16007 | "js_array.__proto__ = ext_array;" |
| 16008 | "js_array.concat(ext_array);" ); |
| 16009 | CHECK_EQ(77, v8::Object::Cast(*result) |
| 16010 | ->Get(context, v8_str("0" )) |
| 16011 | .ToLocalChecked() |
| 16012 | ->Int32Value(context) |
| 16013 | .FromJust()); |
| 16014 | CHECK_EQ(23, v8::Object::Cast(*result) |
| 16015 | ->Get(context, v8_str("1" )) |
| 16016 | .ToLocalChecked() |
| 16017 | ->Int32Value(context) |
| 16018 | .FromJust()); |
| 16019 | |
| 16020 | result = CompileRun("ext_array[1] = 23;" ); |
| 16021 | CHECK_EQ(23, result->Int32Value(context).FromJust()); |
| 16022 | } |
| 16023 | |
| 16024 | |
| 16025 | template <class FixedTypedArrayClass, i::ElementsKind elements_kind, |
| 16026 | class ElementType> |
| 16027 | static void FixedTypedArrayTestHelper(i::ExternalArrayType array_type, |
| 16028 | ElementType low, ElementType high) { |
| 16029 | i::FLAG_allow_natives_syntax = true; |
| 16030 | LocalContext context; |
| 16031 | i::Isolate* isolate = CcTest::i_isolate(); |
| 16032 | i::Factory* factory = isolate->factory(); |
| 16033 | v8::HandleScope scope(context->GetIsolate()); |
| 16034 | const int kElementCount = 260; |
| 16035 | i::Handle<i::JSTypedArray> jsobj = |
| 16036 | factory->NewJSTypedArray(elements_kind, kElementCount); |
| 16037 | i::Handle<FixedTypedArrayClass> fixed_array( |
| 16038 | FixedTypedArrayClass::cast(jsobj->elements()), isolate); |
| 16039 | CHECK_EQ(FixedTypedArrayClass::kInstanceType, |
| 16040 | fixed_array->map()->instance_type()); |
| 16041 | CHECK_EQ(kElementCount, fixed_array->length()); |
| 16042 | CcTest::CollectAllGarbage(); |
| 16043 | for (int i = 0; i < kElementCount; i++) { |
| 16044 | fixed_array->set(i, static_cast<ElementType>(i)); |
| 16045 | } |
| 16046 | // Force GC to trigger verification. |
| 16047 | CcTest::CollectAllGarbage(); |
| 16048 | for (int i = 0; i < kElementCount; i++) { |
| 16049 | CHECK_EQ(static_cast<int64_t>(static_cast<ElementType>(i)), |
| 16050 | static_cast<int64_t>(fixed_array->get_scalar(i))); |
| 16051 | } |
| 16052 | v8::Local<v8::Object> obj = v8::Utils::ToLocal(jsobj); |
| 16053 | |
| 16054 | ObjectWithExternalArrayTestHelper<FixedTypedArrayClass, ElementType>( |
| 16055 | context.local(), obj, kElementCount, array_type, |
| 16056 | static_cast<int64_t>(low), |
| 16057 | static_cast<int64_t>(high)); |
| 16058 | } |
| 16059 | |
| 16060 | |
| 16061 | THREADED_TEST(FixedUint8Array) { |
| 16062 | FixedTypedArrayTestHelper<i::FixedUint8Array, i::UINT8_ELEMENTS, uint8_t>( |
| 16063 | i::kExternalUint8Array, 0x0, 0xFF); |
| 16064 | } |
| 16065 | |
| 16066 | |
| 16067 | THREADED_TEST(FixedUint8ClampedArray) { |
| 16068 | FixedTypedArrayTestHelper<i::FixedUint8ClampedArray, |
| 16069 | i::UINT8_CLAMPED_ELEMENTS, uint8_t>( |
| 16070 | i::kExternalUint8ClampedArray, 0x0, 0xFF); |
| 16071 | } |
| 16072 | |
| 16073 | |
| 16074 | THREADED_TEST(FixedInt8Array) { |
| 16075 | FixedTypedArrayTestHelper<i::FixedInt8Array, i::INT8_ELEMENTS, int8_t>( |
| 16076 | i::kExternalInt8Array, -0x80, 0x7F); |
| 16077 | } |
| 16078 | |
| 16079 | |
| 16080 | THREADED_TEST(FixedUint16Array) { |
| 16081 | FixedTypedArrayTestHelper<i::FixedUint16Array, i::UINT16_ELEMENTS, uint16_t>( |
| 16082 | i::kExternalUint16Array, 0x0, 0xFFFF); |
| 16083 | } |
| 16084 | |
| 16085 | |
| 16086 | THREADED_TEST(FixedInt16Array) { |
| 16087 | FixedTypedArrayTestHelper<i::FixedInt16Array, i::INT16_ELEMENTS, int16_t>( |
| 16088 | i::kExternalInt16Array, -0x8000, 0x7FFF); |
| 16089 | } |
| 16090 | |
| 16091 | |
| 16092 | THREADED_TEST(FixedUint32Array) { |
| 16093 | FixedTypedArrayTestHelper<i::FixedUint32Array, i::UINT32_ELEMENTS, uint32_t>( |
| 16094 | i::kExternalUint32Array, 0x0, UINT_MAX); |
| 16095 | } |
| 16096 | |
| 16097 | |
| 16098 | THREADED_TEST(FixedInt32Array) { |
| 16099 | FixedTypedArrayTestHelper<i::FixedInt32Array, i::INT32_ELEMENTS, int32_t>( |
| 16100 | i::kExternalInt32Array, INT_MIN, INT_MAX); |
| 16101 | } |
| 16102 | |
| 16103 | |
| 16104 | THREADED_TEST(FixedFloat32Array) { |
| 16105 | FixedTypedArrayTestHelper<i::FixedFloat32Array, i::FLOAT32_ELEMENTS, float>( |
| 16106 | i::kExternalFloat32Array, -500, 500); |
| 16107 | } |
| 16108 | |
| 16109 | |
| 16110 | THREADED_TEST(FixedFloat64Array) { |
| 16111 | FixedTypedArrayTestHelper<i::FixedFloat64Array, i::FLOAT64_ELEMENTS, float>( |
| 16112 | i::kExternalFloat64Array, -500, 500); |
| 16113 | } |
| 16114 | |
| 16115 | |
| 16116 | template <typename ElementType, typename TypedArray, class ExternalArrayClass, |
| 16117 | class ArrayBufferType> |
| 16118 | void TypedArrayTestHelper(i::ExternalArrayType array_type, int64_t low, |
| 16119 | int64_t high) { |
| 16120 | const int kElementCount = 50; |
| 16121 | |
| 16122 | i::ScopedVector<ElementType> backing_store(kElementCount+2); |
| 16123 | |
| 16124 | LocalContext env; |
| 16125 | v8::Isolate* isolate = env->GetIsolate(); |
| 16126 | v8::HandleScope handle_scope(isolate); |
| 16127 | |
| 16128 | Local<ArrayBufferType> ab = |
| 16129 | ArrayBufferType::New(isolate, backing_store.start(), |
| 16130 | (kElementCount + 2) * sizeof(ElementType)); |
| 16131 | Local<TypedArray> ta = |
| 16132 | TypedArray::New(ab, 2*sizeof(ElementType), kElementCount); |
| 16133 | CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta); |
| 16134 | CHECK_EQ(kElementCount, static_cast<int>(ta->Length())); |
| 16135 | CHECK_EQ(2 * sizeof(ElementType), ta->ByteOffset()); |
| 16136 | CHECK_EQ(kElementCount * sizeof(ElementType), ta->ByteLength()); |
| 16137 | CHECK(ab->Equals(env.local(), ta->Buffer()).FromJust()); |
| 16138 | |
| 16139 | ElementType* data = backing_store.start() + 2; |
| 16140 | for (int i = 0; i < kElementCount; i++) { |
| 16141 | data[i] = static_cast<ElementType>(i); |
| 16142 | } |
| 16143 | |
| 16144 | ObjectWithExternalArrayTestHelper<ExternalArrayClass, ElementType>( |
| 16145 | env.local(), ta, kElementCount, array_type, low, high); |
| 16146 | } |
| 16147 | |
| 16148 | |
| 16149 | THREADED_TEST(Uint8Array) { |
| 16150 | TypedArrayTestHelper<uint8_t, v8::Uint8Array, i::FixedUint8Array, |
| 16151 | v8::ArrayBuffer>(i::kExternalUint8Array, 0, 0xFF); |
| 16152 | } |
| 16153 | |
| 16154 | |
| 16155 | THREADED_TEST(Int8Array) { |
| 16156 | TypedArrayTestHelper<int8_t, v8::Int8Array, i::FixedInt8Array, |
| 16157 | v8::ArrayBuffer>(i::kExternalInt8Array, -0x80, 0x7F); |
| 16158 | } |
| 16159 | |
| 16160 | |
| 16161 | THREADED_TEST(Uint16Array) { |
| 16162 | TypedArrayTestHelper<uint16_t, v8::Uint16Array, i::FixedUint16Array, |
| 16163 | v8::ArrayBuffer>(i::kExternalUint16Array, 0, 0xFFFF); |
| 16164 | } |
| 16165 | |
| 16166 | |
| 16167 | THREADED_TEST(Int16Array) { |
| 16168 | TypedArrayTestHelper<int16_t, v8::Int16Array, i::FixedInt16Array, |
| 16169 | v8::ArrayBuffer>(i::kExternalInt16Array, -0x8000, |
| 16170 | 0x7FFF); |
| 16171 | } |
| 16172 | |
| 16173 | |
| 16174 | THREADED_TEST(Uint32Array) { |
| 16175 | TypedArrayTestHelper<uint32_t, v8::Uint32Array, i::FixedUint32Array, |
| 16176 | v8::ArrayBuffer>(i::kExternalUint32Array, 0, UINT_MAX); |
| 16177 | } |
| 16178 | |
| 16179 | |
| 16180 | THREADED_TEST(Int32Array) { |
| 16181 | TypedArrayTestHelper<int32_t, v8::Int32Array, i::FixedInt32Array, |
| 16182 | v8::ArrayBuffer>(i::kExternalInt32Array, INT_MIN, |
| 16183 | INT_MAX); |
| 16184 | } |
| 16185 | |
| 16186 | |
| 16187 | THREADED_TEST(Float32Array) { |
| 16188 | TypedArrayTestHelper<float, v8::Float32Array, i::FixedFloat32Array, |
| 16189 | v8::ArrayBuffer>(i::kExternalFloat32Array, -500, 500); |
| 16190 | } |
| 16191 | |
| 16192 | |
| 16193 | THREADED_TEST(Float64Array) { |
| 16194 | TypedArrayTestHelper<double, v8::Float64Array, i::FixedFloat64Array, |
| 16195 | v8::ArrayBuffer>(i::kExternalFloat64Array, -500, 500); |
| 16196 | } |
| 16197 | |
| 16198 | |
| 16199 | THREADED_TEST(Uint8ClampedArray) { |
| 16200 | TypedArrayTestHelper<uint8_t, v8::Uint8ClampedArray, |
| 16201 | i::FixedUint8ClampedArray, v8::ArrayBuffer>( |
| 16202 | i::kExternalUint8ClampedArray, 0, 0xFF); |
| 16203 | } |
| 16204 | |
| 16205 | |
| 16206 | THREADED_TEST(DataView) { |
| 16207 | const int kSize = 50; |
| 16208 | |
| 16209 | i::ScopedVector<uint8_t> backing_store(kSize+2); |
| 16210 | |
| 16211 | LocalContext env; |
| 16212 | v8::Isolate* isolate = env->GetIsolate(); |
| 16213 | v8::HandleScope handle_scope(isolate); |
| 16214 | |
| 16215 | Local<v8::ArrayBuffer> ab = |
| 16216 | v8::ArrayBuffer::New(isolate, backing_store.start(), 2 + kSize); |
| 16217 | Local<v8::DataView> dv = v8::DataView::New(ab, 2, kSize); |
| 16218 | CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| 16219 | CHECK_EQ(2u, dv->ByteOffset()); |
| 16220 | CHECK_EQ(kSize, static_cast<int>(dv->ByteLength())); |
| 16221 | CHECK(ab->Equals(env.local(), dv->Buffer()).FromJust()); |
| 16222 | } |
| 16223 | |
| 16224 | |
| 16225 | THREADED_TEST(SkipArrayBufferBackingStoreDuringGC) { |
| 16226 | LocalContext env; |
| 16227 | v8::Isolate* isolate = env->GetIsolate(); |
| 16228 | v8::HandleScope handle_scope(isolate); |
| 16229 | |
| 16230 | // Make sure the pointer looks like a heap object |
| 16231 | uint8_t* store_ptr = reinterpret_cast<uint8_t*>(i::kHeapObjectTag); |
| 16232 | |
| 16233 | // Create ArrayBuffer with pointer-that-cannot-be-visited in the backing store |
| 16234 | Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store_ptr, 8); |
| 16235 | |
| 16236 | // Should not crash |
| 16237 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 16238 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 16239 | CcTest::CollectAllGarbage(); |
| 16240 | CcTest::CollectAllGarbage(); |
| 16241 | |
| 16242 | // Should not move the pointer |
| 16243 | CHECK_EQ(ab->GetContents().Data(), store_ptr); |
| 16244 | } |
| 16245 | |
| 16246 | |
| 16247 | THREADED_TEST(SkipArrayBufferDuringScavenge) { |
| 16248 | LocalContext env; |
| 16249 | v8::Isolate* isolate = env->GetIsolate(); |
| 16250 | v8::HandleScope handle_scope(isolate); |
| 16251 | |
| 16252 | // Make sure the pointer looks like a heap object |
| 16253 | Local<v8::Object> tmp = v8::Object::New(isolate); |
| 16254 | uint8_t* store_ptr = |
| 16255 | reinterpret_cast<uint8_t*>(*reinterpret_cast<uintptr_t*>(*tmp)); |
| 16256 | |
| 16257 | // Make `store_ptr` point to from space |
| 16258 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 16259 | |
| 16260 | // Create ArrayBuffer with pointer-that-cannot-be-visited in the backing store |
| 16261 | Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store_ptr, 8); |
| 16262 | |
| 16263 | // Should not crash, |
| 16264 | // i.e. backing store pointer should not be treated as a heap object pointer |
| 16265 | CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 16266 | CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now |
| 16267 | |
| 16268 | // Use `ab` to silence compiler warning |
| 16269 | CHECK_EQ(ab->GetContents().Data(), store_ptr); |
| 16270 | } |
| 16271 | |
| 16272 | |
| 16273 | THREADED_TEST(SharedUint8Array) { |
| 16274 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16275 | TypedArrayTestHelper<uint8_t, v8::Uint8Array, i::FixedUint8Array, |
| 16276 | v8::SharedArrayBuffer>(i::kExternalUint8Array, 0, 0xFF); |
| 16277 | } |
| 16278 | |
| 16279 | |
| 16280 | THREADED_TEST(SharedInt8Array) { |
| 16281 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16282 | TypedArrayTestHelper<int8_t, v8::Int8Array, i::FixedInt8Array, |
| 16283 | v8::SharedArrayBuffer>(i::kExternalInt8Array, -0x80, |
| 16284 | 0x7F); |
| 16285 | } |
| 16286 | |
| 16287 | |
| 16288 | THREADED_TEST(SharedUint16Array) { |
| 16289 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16290 | TypedArrayTestHelper<uint16_t, v8::Uint16Array, i::FixedUint16Array, |
| 16291 | v8::SharedArrayBuffer>(i::kExternalUint16Array, 0, |
| 16292 | 0xFFFF); |
| 16293 | } |
| 16294 | |
| 16295 | |
| 16296 | THREADED_TEST(SharedInt16Array) { |
| 16297 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16298 | TypedArrayTestHelper<int16_t, v8::Int16Array, i::FixedInt16Array, |
| 16299 | v8::SharedArrayBuffer>(i::kExternalInt16Array, -0x8000, |
| 16300 | 0x7FFF); |
| 16301 | } |
| 16302 | |
| 16303 | |
| 16304 | THREADED_TEST(SharedUint32Array) { |
| 16305 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16306 | TypedArrayTestHelper<uint32_t, v8::Uint32Array, i::FixedUint32Array, |
| 16307 | v8::SharedArrayBuffer>(i::kExternalUint32Array, 0, |
| 16308 | UINT_MAX); |
| 16309 | } |
| 16310 | |
| 16311 | |
| 16312 | THREADED_TEST(SharedInt32Array) { |
| 16313 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16314 | TypedArrayTestHelper<int32_t, v8::Int32Array, i::FixedInt32Array, |
| 16315 | v8::SharedArrayBuffer>(i::kExternalInt32Array, INT_MIN, |
| 16316 | INT_MAX); |
| 16317 | } |
| 16318 | |
| 16319 | |
| 16320 | THREADED_TEST(SharedFloat32Array) { |
| 16321 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16322 | TypedArrayTestHelper<float, v8::Float32Array, i::FixedFloat32Array, |
| 16323 | v8::SharedArrayBuffer>(i::kExternalFloat32Array, -500, |
| 16324 | 500); |
| 16325 | } |
| 16326 | |
| 16327 | |
| 16328 | THREADED_TEST(SharedFloat64Array) { |
| 16329 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16330 | TypedArrayTestHelper<double, v8::Float64Array, i::FixedFloat64Array, |
| 16331 | v8::SharedArrayBuffer>(i::kExternalFloat64Array, -500, |
| 16332 | 500); |
| 16333 | } |
| 16334 | |
| 16335 | |
| 16336 | THREADED_TEST(SharedUint8ClampedArray) { |
| 16337 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16338 | TypedArrayTestHelper<uint8_t, v8::Uint8ClampedArray, |
| 16339 | i::FixedUint8ClampedArray, v8::SharedArrayBuffer>( |
| 16340 | i::kExternalUint8ClampedArray, 0, 0xFF); |
| 16341 | } |
| 16342 | |
| 16343 | |
| 16344 | THREADED_TEST(SharedDataView) { |
| 16345 | i::FLAG_harmony_sharedarraybuffer = true; |
| 16346 | const int kSize = 50; |
| 16347 | |
| 16348 | i::ScopedVector<uint8_t> backing_store(kSize + 2); |
| 16349 | |
| 16350 | LocalContext env; |
| 16351 | v8::Isolate* isolate = env->GetIsolate(); |
| 16352 | v8::HandleScope handle_scope(isolate); |
| 16353 | |
| 16354 | Local<v8::SharedArrayBuffer> ab = |
| 16355 | v8::SharedArrayBuffer::New(isolate, backing_store.start(), 2 + kSize); |
| 16356 | Local<v8::DataView> dv = |
| 16357 | v8::DataView::New(ab, 2, kSize); |
| 16358 | CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| 16359 | CHECK_EQ(2u, dv->ByteOffset()); |
| 16360 | CHECK_EQ(kSize, static_cast<int>(dv->ByteLength())); |
| 16361 | CHECK(ab->Equals(env.local(), dv->Buffer()).FromJust()); |
| 16362 | } |
| 16363 | |
| 16364 | #define IS_ARRAY_BUFFER_VIEW_TEST(View) \ |
| 16365 | THREADED_TEST(Is##View) { \ |
| 16366 | LocalContext env; \ |
| 16367 | v8::Isolate* isolate = env->GetIsolate(); \ |
| 16368 | v8::HandleScope handle_scope(isolate); \ |
| 16369 | \ |
| 16370 | Local<Value> result = CompileRun( \ |
| 16371 | "var ab = new ArrayBuffer(128);" \ |
| 16372 | "new " #View "(ab)"); \ |
| 16373 | CHECK(result->IsArrayBufferView()); \ |
| 16374 | CHECK(result->Is##View()); \ |
| 16375 | CheckInternalFieldsAreZero<v8::ArrayBufferView>(result.As<v8::View>()); \ |
| 16376 | } |
| 16377 | |
| 16378 | IS_ARRAY_BUFFER_VIEW_TEST(Uint8Array) |
| 16379 | IS_ARRAY_BUFFER_VIEW_TEST(Int8Array) |
| 16380 | IS_ARRAY_BUFFER_VIEW_TEST(Uint16Array) |
| 16381 | IS_ARRAY_BUFFER_VIEW_TEST(Int16Array) |
| 16382 | IS_ARRAY_BUFFER_VIEW_TEST(Uint32Array) |
| 16383 | IS_ARRAY_BUFFER_VIEW_TEST(Int32Array) |
| 16384 | IS_ARRAY_BUFFER_VIEW_TEST(Float32Array) |
| 16385 | IS_ARRAY_BUFFER_VIEW_TEST(Float64Array) |
| 16386 | IS_ARRAY_BUFFER_VIEW_TEST(Uint8ClampedArray) |
| 16387 | IS_ARRAY_BUFFER_VIEW_TEST(DataView) |
| 16388 | |
| 16389 | #undef IS_ARRAY_BUFFER_VIEW_TEST |
| 16390 | |
| 16391 | |
| 16392 | |
| 16393 | THREADED_TEST(ScriptContextDependence) { |
| 16394 | LocalContext c1; |
| 16395 | v8::HandleScope scope(c1->GetIsolate()); |
| 16396 | const char *source = "foo" ; |
| 16397 | v8::Local<v8::Script> dep = v8_compile(source); |
| 16398 | v8::ScriptCompiler::Source script_source( |
| 16399 | v8::String::NewFromUtf8(c1->GetIsolate(), source, |
| 16400 | v8::NewStringType::kNormal) |
| 16401 | .ToLocalChecked()); |
| 16402 | v8::Local<v8::UnboundScript> indep = |
| 16403 | v8::ScriptCompiler::CompileUnboundScript(c1->GetIsolate(), &script_source) |
| 16404 | .ToLocalChecked(); |
| 16405 | c1->Global() |
| 16406 | ->Set(c1.local(), v8::String::NewFromUtf8(c1->GetIsolate(), "foo" , |
| 16407 | v8::NewStringType::kNormal) |
| 16408 | .ToLocalChecked(), |
| 16409 | v8::Integer::New(c1->GetIsolate(), 100)) |
| 16410 | .FromJust(); |
| 16411 | CHECK_EQ( |
| 16412 | dep->Run(c1.local()).ToLocalChecked()->Int32Value(c1.local()).FromJust(), |
| 16413 | 100); |
| 16414 | CHECK_EQ(indep->BindToCurrentContext() |
| 16415 | ->Run(c1.local()) |
| 16416 | .ToLocalChecked() |
| 16417 | ->Int32Value(c1.local()) |
| 16418 | .FromJust(), |
| 16419 | 100); |
| 16420 | LocalContext c2; |
| 16421 | c2->Global() |
| 16422 | ->Set(c2.local(), v8::String::NewFromUtf8(c2->GetIsolate(), "foo" , |
| 16423 | v8::NewStringType::kNormal) |
| 16424 | .ToLocalChecked(), |
| 16425 | v8::Integer::New(c2->GetIsolate(), 101)) |
| 16426 | .FromJust(); |
| 16427 | CHECK_EQ( |
| 16428 | dep->Run(c2.local()).ToLocalChecked()->Int32Value(c2.local()).FromJust(), |
| 16429 | 100); |
| 16430 | CHECK_EQ(indep->BindToCurrentContext() |
| 16431 | ->Run(c2.local()) |
| 16432 | .ToLocalChecked() |
| 16433 | ->Int32Value(c2.local()) |
| 16434 | .FromJust(), |
| 16435 | 101); |
| 16436 | } |
| 16437 | |
| 16438 | |
| 16439 | THREADED_TEST(StackTrace) { |
| 16440 | LocalContext context; |
| 16441 | v8::HandleScope scope(context->GetIsolate()); |
| 16442 | v8::TryCatch try_catch(context->GetIsolate()); |
| 16443 | const char *source = "function foo() { FAIL.FAIL; }; foo();" ; |
| 16444 | v8::Local<v8::String> src = v8_str(source); |
| 16445 | v8::Local<v8::String> origin = v8_str("stack-trace-test" ); |
| 16446 | v8::ScriptCompiler::Source script_source(src, v8::ScriptOrigin(origin)); |
| 16447 | CHECK(v8::ScriptCompiler::CompileUnboundScript(context->GetIsolate(), |
| 16448 | &script_source) |
| 16449 | .ToLocalChecked() |
| 16450 | ->BindToCurrentContext() |
| 16451 | ->Run(context.local()) |
| 16452 | .IsEmpty()); |
| 16453 | CHECK(try_catch.HasCaught()); |
| 16454 | v8::String::Utf8Value stack( |
| 16455 | context->GetIsolate(), |
| 16456 | try_catch.StackTrace(context.local()).ToLocalChecked()); |
| 16457 | CHECK_NOT_NULL(strstr(*stack, "at foo (stack-trace-test" )); |
| 16458 | } |
| 16459 | |
| 16460 | |
| 16461 | // Checks that a StackFrame has certain expected values. |
| 16462 | void checkStackFrame(const char* expected_script_name, |
| 16463 | const char* expected_func_name, int expected_line_number, |
| 16464 | int expected_column, bool is_eval, bool is_constructor, |
| 16465 | v8::Local<v8::StackFrame> frame) { |
| 16466 | v8::HandleScope scope(CcTest::isolate()); |
| 16467 | v8::String::Utf8Value func_name(CcTest::isolate(), frame->GetFunctionName()); |
| 16468 | v8::String::Utf8Value script_name(CcTest::isolate(), frame->GetScriptName()); |
| 16469 | if (*script_name == nullptr) { |
| 16470 | // The situation where there is no associated script, like for evals. |
| 16471 | CHECK_NULL(expected_script_name); |
| 16472 | } else { |
| 16473 | CHECK_NOT_NULL(strstr(*script_name, expected_script_name)); |
| 16474 | } |
| 16475 | CHECK_NOT_NULL(strstr(*func_name, expected_func_name)); |
| 16476 | CHECK_EQ(expected_line_number, frame->GetLineNumber()); |
| 16477 | CHECK_EQ(expected_column, frame->GetColumn()); |
| 16478 | CHECK_EQ(is_eval, frame->IsEval()); |
| 16479 | CHECK_EQ(is_constructor, frame->IsConstructor()); |
| 16480 | } |
| 16481 | |
| 16482 | |
| 16483 | void AnalyzeStackInNativeCode(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 16484 | v8::HandleScope scope(args.GetIsolate()); |
| 16485 | const char* origin = "capture-stack-trace-test" ; |
| 16486 | const int kOverviewTest = 1; |
| 16487 | const int kDetailedTest = 2; |
| 16488 | const int kFunctionName = 3; |
| 16489 | const int kDisplayName = 4; |
| 16490 | const int kFunctionNameAndDisplayName = 5; |
| 16491 | const int kDisplayNameIsNotString = 6; |
| 16492 | const int kFunctionNameIsNotString = 7; |
| 16493 | |
| 16494 | CHECK_EQ(args.Length(), 1); |
| 16495 | |
| 16496 | v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| 16497 | v8::Isolate* isolate = args.GetIsolate(); |
| 16498 | int testGroup = args[0]->Int32Value(context).FromJust(); |
| 16499 | if (testGroup == kOverviewTest) { |
| 16500 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16501 | args.GetIsolate(), 10, v8::StackTrace::kOverview); |
| 16502 | CHECK_EQ(4, stackTrace->GetFrameCount()); |
| 16503 | checkStackFrame(origin, "bar" , 2, 10, false, false, |
| 16504 | stackTrace->GetFrame(args.GetIsolate(), 0)); |
| 16505 | checkStackFrame(origin, "foo" , 6, 3, false, true, |
| 16506 | stackTrace->GetFrame(isolate, 1)); |
| 16507 | // This is the source string inside the eval which has the call to foo. |
| 16508 | checkStackFrame(nullptr, "" , 1, 1, true, false, |
| 16509 | stackTrace->GetFrame(isolate, 2)); |
| 16510 | // The last frame is an anonymous function which has the initial eval call. |
| 16511 | checkStackFrame(origin, "" , 8, 7, false, false, |
| 16512 | stackTrace->GetFrame(isolate, 3)); |
| 16513 | } else if (testGroup == kDetailedTest) { |
| 16514 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16515 | args.GetIsolate(), 10, v8::StackTrace::kDetailed); |
| 16516 | CHECK_EQ(4, stackTrace->GetFrameCount()); |
| 16517 | checkStackFrame(origin, "bat" , 4, 22, false, false, |
| 16518 | stackTrace->GetFrame(isolate, 0)); |
| 16519 | checkStackFrame(origin, "baz" , 8, 3, false, true, |
| 16520 | stackTrace->GetFrame(isolate, 1)); |
| 16521 | bool is_eval = true; |
| 16522 | // This is the source string inside the eval which has the call to baz. |
| 16523 | checkStackFrame(nullptr, "" , 1, 1, is_eval, false, |
| 16524 | stackTrace->GetFrame(isolate, 2)); |
| 16525 | // The last frame is an anonymous function which has the initial eval call. |
| 16526 | checkStackFrame(origin, "" , 10, 1, false, false, |
| 16527 | stackTrace->GetFrame(isolate, 3)); |
| 16528 | } else if (testGroup == kFunctionName) { |
| 16529 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16530 | args.GetIsolate(), 5, v8::StackTrace::kOverview); |
| 16531 | CHECK_EQ(3, stackTrace->GetFrameCount()); |
| 16532 | checkStackFrame(nullptr, "function.name" , 3, 1, true, false, |
| 16533 | stackTrace->GetFrame(isolate, 0)); |
| 16534 | } else if (testGroup == kDisplayName) { |
| 16535 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16536 | args.GetIsolate(), 5, v8::StackTrace::kOverview); |
| 16537 | CHECK_EQ(3, stackTrace->GetFrameCount()); |
| 16538 | checkStackFrame(nullptr, "function.displayName" , 3, 1, true, false, |
| 16539 | stackTrace->GetFrame(isolate, 0)); |
| 16540 | } else if (testGroup == kFunctionNameAndDisplayName) { |
| 16541 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16542 | args.GetIsolate(), 5, v8::StackTrace::kOverview); |
| 16543 | CHECK_EQ(3, stackTrace->GetFrameCount()); |
| 16544 | checkStackFrame(nullptr, "function.displayName" , 3, 1, true, false, |
| 16545 | stackTrace->GetFrame(isolate, 0)); |
| 16546 | } else if (testGroup == kDisplayNameIsNotString) { |
| 16547 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16548 | args.GetIsolate(), 5, v8::StackTrace::kOverview); |
| 16549 | CHECK_EQ(3, stackTrace->GetFrameCount()); |
| 16550 | checkStackFrame(nullptr, "function.name" , 3, 1, true, false, |
| 16551 | stackTrace->GetFrame(isolate, 0)); |
| 16552 | } else if (testGroup == kFunctionNameIsNotString) { |
| 16553 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 16554 | args.GetIsolate(), 5, v8::StackTrace::kOverview); |
| 16555 | CHECK_EQ(3, stackTrace->GetFrameCount()); |
| 16556 | checkStackFrame(nullptr, "" , 3, 1, true, false, |
| 16557 | stackTrace->GetFrame(isolate, 0)); |
| 16558 | } |
| 16559 | } |
| 16560 | |
| 16561 | |
| 16562 | // Tests the C++ StackTrace API. |
| 16563 | // TODO(3074796): Reenable this as a THREADED_TEST once it passes. |
| 16564 | // THREADED_TEST(CaptureStackTrace) { |
| 16565 | TEST(CaptureStackTrace) { |
| 16566 | v8::Isolate* isolate = CcTest::isolate(); |
| 16567 | v8::HandleScope scope(isolate); |
| 16568 | v8::Local<v8::String> origin = v8_str("capture-stack-trace-test" ); |
| 16569 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 16570 | templ->Set(v8_str("AnalyzeStackInNativeCode" ), |
| 16571 | v8::FunctionTemplate::New(isolate, AnalyzeStackInNativeCode)); |
| 16572 | LocalContext context(nullptr, templ); |
| 16573 | |
| 16574 | // Test getting OVERVIEW information. Should ignore information that is not |
| 16575 | // script name, function name, line number, and column offset. |
| 16576 | const char *overview_source = |
| 16577 | "function bar() {\n" |
| 16578 | " var y; AnalyzeStackInNativeCode(1);\n" |
| 16579 | "}\n" |
| 16580 | "function foo() {\n" |
| 16581 | "\n" |
| 16582 | " bar();\n" |
| 16583 | "}\n" |
| 16584 | "var x;eval('new foo();');" ; |
| 16585 | v8::Local<v8::String> overview_src = v8_str(overview_source); |
| 16586 | v8::ScriptCompiler::Source script_source(overview_src, |
| 16587 | v8::ScriptOrigin(origin)); |
| 16588 | v8::Local<Value> overview_result( |
| 16589 | v8::ScriptCompiler::CompileUnboundScript(isolate, &script_source) |
| 16590 | .ToLocalChecked() |
| 16591 | ->BindToCurrentContext() |
| 16592 | ->Run(context.local()) |
| 16593 | .ToLocalChecked()); |
| 16594 | CHECK(!overview_result.IsEmpty()); |
| 16595 | CHECK(overview_result->IsObject()); |
| 16596 | |
| 16597 | // Test getting DETAILED information. |
| 16598 | const char *detailed_source = |
| 16599 | "function bat() {AnalyzeStackInNativeCode(2);\n" |
| 16600 | "}\n" |
| 16601 | "\n" |
| 16602 | "function baz() {\n" |
| 16603 | " bat();\n" |
| 16604 | "}\n" |
| 16605 | "eval('new baz();');" ; |
| 16606 | v8::Local<v8::String> detailed_src = v8_str(detailed_source); |
| 16607 | // Make the script using a non-zero line and column offset. |
| 16608 | v8::Local<v8::Integer> line_offset = v8::Integer::New(isolate, 3); |
| 16609 | v8::Local<v8::Integer> column_offset = v8::Integer::New(isolate, 5); |
| 16610 | v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); |
| 16611 | v8::ScriptCompiler::Source script_source2(detailed_src, detailed_origin); |
| 16612 | v8::Local<v8::UnboundScript> detailed_script( |
| 16613 | v8::ScriptCompiler::CompileUnboundScript(isolate, &script_source2) |
| 16614 | .ToLocalChecked()); |
| 16615 | v8::Local<Value> detailed_result(detailed_script->BindToCurrentContext() |
| 16616 | ->Run(context.local()) |
| 16617 | .ToLocalChecked()); |
| 16618 | CHECK(!detailed_result.IsEmpty()); |
| 16619 | CHECK(detailed_result->IsObject()); |
| 16620 | |
| 16621 | // Test using function.name and function.displayName in stack trace |
| 16622 | const char* function_name_source = |
| 16623 | "function bar(function_name, display_name, testGroup) {\n" |
| 16624 | " var f = new Function(`AnalyzeStackInNativeCode(${testGroup});`);\n" |
| 16625 | " if (function_name) {\n" |
| 16626 | " Object.defineProperty(f, 'name', { value: function_name });\n" |
| 16627 | " }\n" |
| 16628 | " if (display_name) {\n" |
| 16629 | " f.displayName = display_name;" |
| 16630 | " }\n" |
| 16631 | " f()\n" |
| 16632 | "}\n" |
| 16633 | "bar('function.name', undefined, 3);\n" |
| 16634 | "bar(undefined, 'function.displayName', 4);\n" |
| 16635 | "bar('function.name', 'function.displayName', 5);\n" |
| 16636 | "bar('function.name', 239, 6);\n" |
| 16637 | "bar(239, undefined, 7);\n" ; |
| 16638 | v8::Local<v8::String> function_name_src = |
| 16639 | v8::String::NewFromUtf8(isolate, function_name_source, |
| 16640 | v8::NewStringType::kNormal) |
| 16641 | .ToLocalChecked(); |
| 16642 | v8::ScriptCompiler::Source script_source3(function_name_src, |
| 16643 | v8::ScriptOrigin(origin)); |
| 16644 | v8::Local<Value> function_name_result( |
| 16645 | v8::ScriptCompiler::CompileUnboundScript(isolate, &script_source3) |
| 16646 | .ToLocalChecked() |
| 16647 | ->BindToCurrentContext() |
| 16648 | ->Run(context.local()) |
| 16649 | .ToLocalChecked()); |
| 16650 | CHECK(!function_name_result.IsEmpty()); |
| 16651 | } |
| 16652 | |
| 16653 | |
| 16654 | static void StackTraceForUncaughtExceptionListener( |
| 16655 | v8::Local<v8::Message> message, v8::Local<Value>) { |
| 16656 | report_count++; |
| 16657 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 16658 | CHECK_EQ(2, stack_trace->GetFrameCount()); |
| 16659 | checkStackFrame("origin" , "foo" , 2, 3, false, false, |
| 16660 | stack_trace->GetFrame(message->GetIsolate(), 0)); |
| 16661 | checkStackFrame("origin" , "bar" , 5, 3, false, false, |
| 16662 | stack_trace->GetFrame(message->GetIsolate(), 1)); |
| 16663 | } |
| 16664 | |
| 16665 | |
| 16666 | TEST(CaptureStackTraceForUncaughtException) { |
| 16667 | report_count = 0; |
| 16668 | LocalContext env; |
| 16669 | v8::Isolate* isolate = env->GetIsolate(); |
| 16670 | v8::HandleScope scope(isolate); |
| 16671 | isolate->AddMessageListener(StackTraceForUncaughtExceptionListener); |
| 16672 | isolate->SetCaptureStackTraceForUncaughtExceptions(true); |
| 16673 | |
| 16674 | CompileRunWithOrigin( |
| 16675 | "function foo() {\n" |
| 16676 | " throw 1;\n" |
| 16677 | "};\n" |
| 16678 | "function bar() {\n" |
| 16679 | " foo();\n" |
| 16680 | "};" , |
| 16681 | "origin" ); |
| 16682 | v8::Local<v8::Object> global = env->Global(); |
| 16683 | Local<Value> trouble = |
| 16684 | global->Get(env.local(), v8_str("bar" )).ToLocalChecked(); |
| 16685 | CHECK(trouble->IsFunction()); |
| 16686 | CHECK(Function::Cast(*trouble) |
| 16687 | ->Call(env.local(), global, 0, nullptr) |
| 16688 | .IsEmpty()); |
| 16689 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16690 | isolate->RemoveMessageListeners(StackTraceForUncaughtExceptionListener); |
| 16691 | CHECK_EQ(1, report_count); |
| 16692 | } |
| 16693 | |
| 16694 | TEST(CaptureStackTraceForUncaughtExceptionAndSetters) { |
| 16695 | LocalContext env; |
| 16696 | v8::Isolate* isolate = env->GetIsolate(); |
| 16697 | v8::HandleScope scope(isolate); |
| 16698 | isolate->SetCaptureStackTraceForUncaughtExceptions(true, 1024, |
| 16699 | v8::StackTrace::kDetailed); |
| 16700 | |
| 16701 | CompileRun( |
| 16702 | "var setters = ['column', 'lineNumber', 'scriptName',\n" |
| 16703 | " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n" |
| 16704 | " 'isConstructor'];\n" |
| 16705 | "for (var i = 0; i < setters.length; i++) {\n" |
| 16706 | " var prop = setters[i];\n" |
| 16707 | " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n" |
| 16708 | "}\n" ); |
| 16709 | CompileRun("throw 'exception';" ); |
| 16710 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16711 | } |
| 16712 | |
| 16713 | static int asm_warning_triggered = 0; |
| 16714 | |
| 16715 | static void AsmJsWarningListener(v8::Local<v8::Message> message, |
| 16716 | v8::Local<Value>) { |
| 16717 | DCHECK_EQ(v8::Isolate::kMessageWarning, message->ErrorLevel()); |
| 16718 | asm_warning_triggered = 1; |
| 16719 | } |
| 16720 | |
| 16721 | TEST(AsmJsWarning) { |
| 16722 | i::FLAG_validate_asm = true; |
| 16723 | if (i::FLAG_suppress_asm_messages) return; |
| 16724 | |
| 16725 | LocalContext env; |
| 16726 | v8::Isolate* isolate = env->GetIsolate(); |
| 16727 | v8::HandleScope scope(isolate); |
| 16728 | |
| 16729 | asm_warning_triggered = 0; |
| 16730 | isolate->AddMessageListenerWithErrorLevel(AsmJsWarningListener, |
| 16731 | v8::Isolate::kMessageAll); |
| 16732 | CompileRun( |
| 16733 | "function module() {\n" |
| 16734 | " 'use asm';\n" |
| 16735 | " var x = 'hi';\n" |
| 16736 | " return {};\n" |
| 16737 | "}\n" |
| 16738 | "module();" ); |
| 16739 | DCHECK_EQ(1, asm_warning_triggered); |
| 16740 | isolate->RemoveMessageListeners(AsmJsWarningListener); |
| 16741 | } |
| 16742 | |
| 16743 | static int error_level_message_count = 0; |
| 16744 | static int expected_error_level = 0; |
| 16745 | |
| 16746 | static void ErrorLevelListener(v8::Local<v8::Message> message, |
| 16747 | v8::Local<Value>) { |
| 16748 | DCHECK_EQ(expected_error_level, message->ErrorLevel()); |
| 16749 | ++error_level_message_count; |
| 16750 | } |
| 16751 | |
| 16752 | TEST(ErrorLevelWarning) { |
| 16753 | LocalContext env; |
| 16754 | v8::Isolate* isolate = env->GetIsolate(); |
| 16755 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 16756 | v8::HandleScope scope(isolate); |
| 16757 | |
| 16758 | const char* source = "fake = 1;" ; |
| 16759 | v8::Local<v8::Script> lscript = CompileWithOrigin(source, "test" , false); |
| 16760 | i::Handle<i::SharedFunctionInfo> obj = i::Handle<i::SharedFunctionInfo>::cast( |
| 16761 | v8::Utils::OpenHandle(*lscript->GetUnboundScript())); |
| 16762 | CHECK(obj->script()->IsScript()); |
| 16763 | i::Handle<i::Script> script(i::Script::cast(obj->script()), i_isolate); |
| 16764 | |
| 16765 | int levels[] = { |
| 16766 | v8::Isolate::kMessageLog, v8::Isolate::kMessageInfo, |
| 16767 | v8::Isolate::kMessageDebug, v8::Isolate::kMessageWarning, |
| 16768 | }; |
| 16769 | error_level_message_count = 0; |
| 16770 | isolate->AddMessageListenerWithErrorLevel(ErrorLevelListener, |
| 16771 | v8::Isolate::kMessageAll); |
| 16772 | for (size_t i = 0; i < arraysize(levels); i++) { |
| 16773 | i::MessageLocation location(script, 0, 0); |
| 16774 | i::Handle<i::String> msg(i_isolate->factory()->InternalizeOneByteString( |
| 16775 | i::StaticCharVector("test" ))); |
| 16776 | i::Handle<i::JSMessageObject> message = |
| 16777 | i::MessageHandler::MakeMessageObject( |
| 16778 | i_isolate, i::MessageTemplate::kAsmJsInvalid, &location, msg, |
| 16779 | i::Handle<i::FixedArray>::null()); |
| 16780 | message->set_error_level(levels[i]); |
| 16781 | expected_error_level = levels[i]; |
| 16782 | i::MessageHandler::ReportMessage(i_isolate, &location, message); |
| 16783 | } |
| 16784 | isolate->RemoveMessageListeners(ErrorLevelListener); |
| 16785 | DCHECK_EQ(arraysize(levels), error_level_message_count); |
| 16786 | } |
| 16787 | |
| 16788 | static void StackTraceFunctionNameListener(v8::Local<v8::Message> message, |
| 16789 | v8::Local<Value>) { |
| 16790 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 16791 | v8::Isolate* isolate = message->GetIsolate(); |
| 16792 | CHECK_EQ(5, stack_trace->GetFrameCount()); |
| 16793 | checkStackFrame("origin" , "foo:0" , 4, 7, false, false, |
| 16794 | stack_trace->GetFrame(isolate, 0)); |
| 16795 | checkStackFrame("origin" , "foo:1" , 5, 27, false, false, |
| 16796 | stack_trace->GetFrame(isolate, 1)); |
| 16797 | checkStackFrame("origin" , "foo" , 5, 27, false, false, |
| 16798 | stack_trace->GetFrame(isolate, 2)); |
| 16799 | checkStackFrame("origin" , "foo" , 5, 27, false, false, |
| 16800 | stack_trace->GetFrame(isolate, 3)); |
| 16801 | checkStackFrame("origin" , "" , 1, 14, false, false, |
| 16802 | stack_trace->GetFrame(isolate, 4)); |
| 16803 | } |
| 16804 | |
| 16805 | |
| 16806 | TEST(GetStackTraceContainsFunctionsWithFunctionName) { |
| 16807 | LocalContext env; |
| 16808 | v8::Isolate* isolate = env->GetIsolate(); |
| 16809 | v8::HandleScope scope(isolate); |
| 16810 | |
| 16811 | CompileRunWithOrigin( |
| 16812 | "function gen(name, counter) {\n" |
| 16813 | " var f = function foo() {\n" |
| 16814 | " if (counter === 0)\n" |
| 16815 | " throw 1;\n" |
| 16816 | " gen(name, counter - 1)();\n" |
| 16817 | " };\n" |
| 16818 | " if (counter == 3) {\n" |
| 16819 | " Object.defineProperty(f, 'name', {get: function(){ throw 239; }});\n" |
| 16820 | " } else {\n" |
| 16821 | " Object.defineProperty(f, 'name', {writable:true});\n" |
| 16822 | " if (counter == 2)\n" |
| 16823 | " f.name = 42;\n" |
| 16824 | " else\n" |
| 16825 | " f.name = name + ':' + counter;\n" |
| 16826 | " }\n" |
| 16827 | " return f;\n" |
| 16828 | "};" , |
| 16829 | "origin" ); |
| 16830 | |
| 16831 | isolate->AddMessageListener(StackTraceFunctionNameListener); |
| 16832 | isolate->SetCaptureStackTraceForUncaughtExceptions(true); |
| 16833 | CompileRunWithOrigin("gen('foo', 3)();" , "origin" ); |
| 16834 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16835 | isolate->RemoveMessageListeners(StackTraceFunctionNameListener); |
| 16836 | } |
| 16837 | |
| 16838 | |
| 16839 | static void RethrowStackTraceHandler(v8::Local<v8::Message> message, |
| 16840 | v8::Local<v8::Value> data) { |
| 16841 | // Use the frame where JavaScript is called from. |
| 16842 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 16843 | CHECK(!stack_trace.IsEmpty()); |
| 16844 | int frame_count = stack_trace->GetFrameCount(); |
| 16845 | CHECK_EQ(3, frame_count); |
| 16846 | int line_number[] = {1, 2, 5}; |
| 16847 | for (int i = 0; i < frame_count; i++) { |
| 16848 | CHECK_EQ(line_number[i], |
| 16849 | stack_trace->GetFrame(message->GetIsolate(), i)->GetLineNumber()); |
| 16850 | } |
| 16851 | } |
| 16852 | |
| 16853 | |
| 16854 | // Test that we only return the stack trace at the site where the exception |
| 16855 | // is first thrown (not where it is rethrown). |
| 16856 | TEST(RethrowStackTrace) { |
| 16857 | LocalContext env; |
| 16858 | v8::Isolate* isolate = env->GetIsolate(); |
| 16859 | v8::HandleScope scope(isolate); |
| 16860 | // We make sure that |
| 16861 | // - the stack trace of the ReferenceError in g() is reported. |
| 16862 | // - the stack trace is not overwritten when e1 is rethrown by t(). |
| 16863 | // - the stack trace of e2 does not overwrite that of e1. |
| 16864 | const char* source = |
| 16865 | "function g() { error; } \n" |
| 16866 | "function f() { g(); } \n" |
| 16867 | "function t(e) { throw e; } \n" |
| 16868 | "try { \n" |
| 16869 | " f(); \n" |
| 16870 | "} catch (e1) { \n" |
| 16871 | " try { \n" |
| 16872 | " error; \n" |
| 16873 | " } catch (e2) { \n" |
| 16874 | " t(e1); \n" |
| 16875 | " } \n" |
| 16876 | "} \n" ; |
| 16877 | isolate->AddMessageListener(RethrowStackTraceHandler); |
| 16878 | isolate->SetCaptureStackTraceForUncaughtExceptions(true); |
| 16879 | CompileRun(source); |
| 16880 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16881 | isolate->RemoveMessageListeners(RethrowStackTraceHandler); |
| 16882 | } |
| 16883 | |
| 16884 | |
| 16885 | static void RethrowPrimitiveStackTraceHandler(v8::Local<v8::Message> message, |
| 16886 | v8::Local<v8::Value> data) { |
| 16887 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 16888 | CHECK(!stack_trace.IsEmpty()); |
| 16889 | int frame_count = stack_trace->GetFrameCount(); |
| 16890 | CHECK_EQ(2, frame_count); |
| 16891 | int line_number[] = {3, 7}; |
| 16892 | for (int i = 0; i < frame_count; i++) { |
| 16893 | CHECK_EQ(line_number[i], |
| 16894 | stack_trace->GetFrame(message->GetIsolate(), i)->GetLineNumber()); |
| 16895 | } |
| 16896 | } |
| 16897 | |
| 16898 | |
| 16899 | // Test that we do not recognize identity for primitive exceptions. |
| 16900 | TEST(RethrowPrimitiveStackTrace) { |
| 16901 | LocalContext env; |
| 16902 | v8::Isolate* isolate = env->GetIsolate(); |
| 16903 | v8::HandleScope scope(isolate); |
| 16904 | // We do not capture stack trace for non Error objects on creation time. |
| 16905 | // Instead, we capture the stack trace on last throw. |
| 16906 | const char* source = |
| 16907 | "function g() { throw 404; } \n" |
| 16908 | "function f() { g(); } \n" |
| 16909 | "function t(e) { throw e; } \n" |
| 16910 | "try { \n" |
| 16911 | " f(); \n" |
| 16912 | "} catch (e1) { \n" |
| 16913 | " t(e1) \n" |
| 16914 | "} \n" ; |
| 16915 | isolate->AddMessageListener(RethrowPrimitiveStackTraceHandler); |
| 16916 | isolate->SetCaptureStackTraceForUncaughtExceptions(true); |
| 16917 | CompileRun(source); |
| 16918 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16919 | isolate->RemoveMessageListeners(RethrowPrimitiveStackTraceHandler); |
| 16920 | } |
| 16921 | |
| 16922 | |
| 16923 | static void RethrowExistingStackTraceHandler(v8::Local<v8::Message> message, |
| 16924 | v8::Local<v8::Value> data) { |
| 16925 | // Use the frame where JavaScript is called from. |
| 16926 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 16927 | CHECK(!stack_trace.IsEmpty()); |
| 16928 | CHECK_EQ(1, stack_trace->GetFrameCount()); |
| 16929 | CHECK_EQ(1, stack_trace->GetFrame(message->GetIsolate(), 0)->GetLineNumber()); |
| 16930 | } |
| 16931 | |
| 16932 | |
| 16933 | // Test that the stack trace is captured when the error object is created and |
| 16934 | // not where it is thrown. |
| 16935 | TEST(RethrowExistingStackTrace) { |
| 16936 | LocalContext env; |
| 16937 | v8::Isolate* isolate = env->GetIsolate(); |
| 16938 | v8::HandleScope scope(isolate); |
| 16939 | const char* source = |
| 16940 | "var e = new Error(); \n" |
| 16941 | "throw e; \n" ; |
| 16942 | isolate->AddMessageListener(RethrowExistingStackTraceHandler); |
| 16943 | isolate->SetCaptureStackTraceForUncaughtExceptions(true); |
| 16944 | CompileRun(source); |
| 16945 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16946 | isolate->RemoveMessageListeners(RethrowExistingStackTraceHandler); |
| 16947 | } |
| 16948 | |
| 16949 | |
| 16950 | static void RethrowBogusErrorStackTraceHandler(v8::Local<v8::Message> message, |
| 16951 | v8::Local<v8::Value> data) { |
| 16952 | // Use the frame where JavaScript is called from. |
| 16953 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 16954 | CHECK(!stack_trace.IsEmpty()); |
| 16955 | CHECK_EQ(1, stack_trace->GetFrameCount()); |
| 16956 | CHECK_EQ(2, stack_trace->GetFrame(message->GetIsolate(), 0)->GetLineNumber()); |
| 16957 | } |
| 16958 | |
| 16959 | |
| 16960 | // Test that the stack trace is captured where the bogus Error object is thrown. |
| 16961 | TEST(RethrowBogusErrorStackTrace) { |
| 16962 | LocalContext env; |
| 16963 | v8::Isolate* isolate = env->GetIsolate(); |
| 16964 | v8::HandleScope scope(isolate); |
| 16965 | const char* source = |
| 16966 | "var e = {__proto__: new Error()} \n" |
| 16967 | "throw e; \n" ; |
| 16968 | isolate->AddMessageListener(RethrowBogusErrorStackTraceHandler); |
| 16969 | isolate->SetCaptureStackTraceForUncaughtExceptions(true); |
| 16970 | CompileRun(source); |
| 16971 | isolate->SetCaptureStackTraceForUncaughtExceptions(false); |
| 16972 | isolate->RemoveMessageListeners(RethrowBogusErrorStackTraceHandler); |
| 16973 | } |
| 16974 | |
| 16975 | |
| 16976 | v8::PromiseRejectEvent reject_event = v8::kPromiseRejectWithNoHandler; |
| 16977 | int promise_reject_counter = 0; |
| 16978 | int promise_revoke_counter = 0; |
| 16979 | int promise_reject_after_resolved_counter = 0; |
| 16980 | int promise_resolve_after_resolved_counter = 0; |
| 16981 | int promise_reject_msg_line_number = -1; |
| 16982 | int promise_reject_msg_column_number = -1; |
| 16983 | int promise_reject_line_number = -1; |
| 16984 | int promise_reject_column_number = -1; |
| 16985 | int promise_reject_frame_count = -1; |
| 16986 | bool promise_reject_is_shared_cross_origin = false; |
| 16987 | |
| 16988 | void PromiseRejectCallback(v8::PromiseRejectMessage reject_message) { |
| 16989 | v8::Local<v8::Object> global = CcTest::global(); |
| 16990 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 16991 | CHECK_NE(v8::Promise::PromiseState::kPending, |
| 16992 | reject_message.GetPromise()->State()); |
| 16993 | switch (reject_message.GetEvent()) { |
| 16994 | case v8::kPromiseRejectWithNoHandler: { |
| 16995 | promise_reject_counter++; |
| 16996 | global->Set(context, v8_str("rejected" ), reject_message.GetPromise()) |
| 16997 | .FromJust(); |
| 16998 | global->Set(context, v8_str("value" ), reject_message.GetValue()) |
| 16999 | .FromJust(); |
| 17000 | v8::Local<v8::Message> message = v8::Exception::CreateMessage( |
| 17001 | CcTest::isolate(), reject_message.GetValue()); |
| 17002 | v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace(); |
| 17003 | |
| 17004 | promise_reject_msg_line_number = |
| 17005 | message->GetLineNumber(context).FromJust(); |
| 17006 | promise_reject_msg_column_number = |
| 17007 | message->GetStartColumn(context).FromJust() + 1; |
| 17008 | promise_reject_is_shared_cross_origin = |
| 17009 | message->IsSharedCrossOrigin(); |
| 17010 | |
| 17011 | if (!stack_trace.IsEmpty()) { |
| 17012 | promise_reject_frame_count = stack_trace->GetFrameCount(); |
| 17013 | if (promise_reject_frame_count > 0) { |
| 17014 | CHECK(stack_trace->GetFrame(CcTest::isolate(), 0) |
| 17015 | ->GetScriptName() |
| 17016 | ->Equals(context, v8_str("pro" )) |
| 17017 | .FromJust()); |
| 17018 | promise_reject_line_number = |
| 17019 | stack_trace->GetFrame(CcTest::isolate(), 0)->GetLineNumber(); |
| 17020 | promise_reject_column_number = |
| 17021 | stack_trace->GetFrame(CcTest::isolate(), 0)->GetColumn(); |
| 17022 | } else { |
| 17023 | promise_reject_line_number = -1; |
| 17024 | promise_reject_column_number = -1; |
| 17025 | } |
| 17026 | } |
| 17027 | break; |
| 17028 | } |
| 17029 | case v8::kPromiseHandlerAddedAfterReject: { |
| 17030 | promise_revoke_counter++; |
| 17031 | global->Set(context, v8_str("revoked" ), reject_message.GetPromise()) |
| 17032 | .FromJust(); |
| 17033 | CHECK(reject_message.GetValue().IsEmpty()); |
| 17034 | break; |
| 17035 | } |
| 17036 | case v8::kPromiseRejectAfterResolved: { |
| 17037 | promise_reject_after_resolved_counter++; |
| 17038 | break; |
| 17039 | } |
| 17040 | case v8::kPromiseResolveAfterResolved: { |
| 17041 | promise_resolve_after_resolved_counter++; |
| 17042 | break; |
| 17043 | } |
| 17044 | } |
| 17045 | } |
| 17046 | |
| 17047 | |
| 17048 | v8::Local<v8::Promise> GetPromise(const char* name) { |
| 17049 | return v8::Local<v8::Promise>::Cast( |
| 17050 | CcTest::global() |
| 17051 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str(name)) |
| 17052 | .ToLocalChecked()); |
| 17053 | } |
| 17054 | |
| 17055 | |
| 17056 | v8::Local<v8::Value> RejectValue() { |
| 17057 | return CcTest::global() |
| 17058 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value" )) |
| 17059 | .ToLocalChecked(); |
| 17060 | } |
| 17061 | |
| 17062 | |
| 17063 | void ResetPromiseStates() { |
| 17064 | promise_reject_counter = 0; |
| 17065 | promise_revoke_counter = 0; |
| 17066 | promise_reject_after_resolved_counter = 0; |
| 17067 | promise_resolve_after_resolved_counter = 0; |
| 17068 | promise_reject_msg_line_number = -1; |
| 17069 | promise_reject_msg_column_number = -1; |
| 17070 | promise_reject_line_number = -1; |
| 17071 | promise_reject_column_number = -1; |
| 17072 | promise_reject_frame_count = -1; |
| 17073 | |
| 17074 | v8::Local<v8::Object> global = CcTest::global(); |
| 17075 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 17076 | global->Set(context, v8_str("rejected" ), v8_str("" )).FromJust(); |
| 17077 | global->Set(context, v8_str("value" ), v8_str("" )).FromJust(); |
| 17078 | global->Set(context, v8_str("revoked" ), v8_str("" )).FromJust(); |
| 17079 | } |
| 17080 | |
| 17081 | |
| 17082 | TEST(PromiseRejectCallback) { |
| 17083 | LocalContext env; |
| 17084 | v8::Isolate* isolate = env->GetIsolate(); |
| 17085 | v8::HandleScope scope(isolate); |
| 17086 | |
| 17087 | isolate->SetPromiseRejectCallback(PromiseRejectCallback); |
| 17088 | |
| 17089 | ResetPromiseStates(); |
| 17090 | |
| 17091 | // Create promise p0. |
| 17092 | CompileRun( |
| 17093 | "var reject; \n" |
| 17094 | "var p0 = new Promise( \n" |
| 17095 | " function(res, rej) { \n" |
| 17096 | " reject = rej; \n" |
| 17097 | " } \n" |
| 17098 | "); \n" ); |
| 17099 | CHECK(!GetPromise("p0" )->HasHandler()); |
| 17100 | CHECK_EQ(0, promise_reject_counter); |
| 17101 | CHECK_EQ(0, promise_revoke_counter); |
| 17102 | |
| 17103 | // Add resolve handler (and default reject handler) to p0. |
| 17104 | CompileRun("var p1 = p0.then(function(){});" ); |
| 17105 | CHECK(GetPromise("p0" )->HasHandler()); |
| 17106 | CHECK(!GetPromise("p1" )->HasHandler()); |
| 17107 | CHECK_EQ(0, promise_reject_counter); |
| 17108 | CHECK_EQ(0, promise_revoke_counter); |
| 17109 | |
| 17110 | // Reject p0. |
| 17111 | CompileRun("reject('ppp');" ); |
| 17112 | CHECK(GetPromise("p0" )->HasHandler()); |
| 17113 | CHECK(!GetPromise("p1" )->HasHandler()); |
| 17114 | CHECK_EQ(1, promise_reject_counter); |
| 17115 | CHECK_EQ(0, promise_revoke_counter); |
| 17116 | CHECK_EQ(v8::kPromiseRejectWithNoHandler, reject_event); |
| 17117 | CHECK( |
| 17118 | GetPromise("rejected" )->Equals(env.local(), GetPromise("p1" )).FromJust()); |
| 17119 | CHECK(RejectValue()->Equals(env.local(), v8_str("ppp" )).FromJust()); |
| 17120 | |
| 17121 | // Reject p0 again. Callback is not triggered again. |
| 17122 | CompileRun("reject();" ); |
| 17123 | CHECK(GetPromise("p0" )->HasHandler()); |
| 17124 | CHECK(!GetPromise("p1" )->HasHandler()); |
| 17125 | CHECK_EQ(1, promise_reject_counter); |
| 17126 | CHECK_EQ(0, promise_revoke_counter); |
| 17127 | |
| 17128 | // Add resolve handler to p1. |
| 17129 | CompileRun("var p2 = p1.then(function(){});" ); |
| 17130 | CHECK(GetPromise("p0" )->HasHandler()); |
| 17131 | CHECK(GetPromise("p1" )->HasHandler()); |
| 17132 | CHECK(!GetPromise("p2" )->HasHandler()); |
| 17133 | CHECK_EQ(2, promise_reject_counter); |
| 17134 | CHECK_EQ(1, promise_revoke_counter); |
| 17135 | CHECK( |
| 17136 | GetPromise("rejected" )->Equals(env.local(), GetPromise("p2" )).FromJust()); |
| 17137 | CHECK(RejectValue()->Equals(env.local(), v8_str("ppp" )).FromJust()); |
| 17138 | CHECK( |
| 17139 | GetPromise("revoked" )->Equals(env.local(), GetPromise("p1" )).FromJust()); |
| 17140 | |
| 17141 | ResetPromiseStates(); |
| 17142 | |
| 17143 | // Create promise q0. |
| 17144 | CompileRun( |
| 17145 | "var q0 = new Promise( \n" |
| 17146 | " function(res, rej) { \n" |
| 17147 | " reject = rej; \n" |
| 17148 | " } \n" |
| 17149 | "); \n" ); |
| 17150 | CHECK(!GetPromise("q0" )->HasHandler()); |
| 17151 | CHECK_EQ(0, promise_reject_counter); |
| 17152 | CHECK_EQ(0, promise_revoke_counter); |
| 17153 | |
| 17154 | // Add reject handler to q0. |
| 17155 | CompileRun("var q1 = q0.catch(function() {});" ); |
| 17156 | CHECK(GetPromise("q0" )->HasHandler()); |
| 17157 | CHECK(!GetPromise("q1" )->HasHandler()); |
| 17158 | CHECK_EQ(0, promise_reject_counter); |
| 17159 | CHECK_EQ(0, promise_revoke_counter); |
| 17160 | |
| 17161 | // Reject q0. |
| 17162 | CompileRun("reject('qq')" ); |
| 17163 | CHECK(GetPromise("q0" )->HasHandler()); |
| 17164 | CHECK(!GetPromise("q1" )->HasHandler()); |
| 17165 | CHECK_EQ(0, promise_reject_counter); |
| 17166 | CHECK_EQ(0, promise_revoke_counter); |
| 17167 | |
| 17168 | // Add a new reject handler, which rejects by returning Promise.reject(). |
| 17169 | // The returned promise q_ triggers a reject callback at first, only to |
| 17170 | // revoke it when returning it causes q2 to be rejected. |
| 17171 | CompileRun( |
| 17172 | "var q_;" |
| 17173 | "var q2 = q0.catch( \n" |
| 17174 | " function() { \n" |
| 17175 | " q_ = Promise.reject('qqq'); \n" |
| 17176 | " return q_; \n" |
| 17177 | " } \n" |
| 17178 | "); \n" ); |
| 17179 | CHECK(GetPromise("q0" )->HasHandler()); |
| 17180 | CHECK(!GetPromise("q1" )->HasHandler()); |
| 17181 | CHECK(!GetPromise("q2" )->HasHandler()); |
| 17182 | CHECK(GetPromise("q_" )->HasHandler()); |
| 17183 | CHECK_EQ(2, promise_reject_counter); |
| 17184 | CHECK_EQ(1, promise_revoke_counter); |
| 17185 | CHECK( |
| 17186 | GetPromise("rejected" )->Equals(env.local(), GetPromise("q2" )).FromJust()); |
| 17187 | CHECK( |
| 17188 | GetPromise("revoked" )->Equals(env.local(), GetPromise("q_" )).FromJust()); |
| 17189 | CHECK(RejectValue()->Equals(env.local(), v8_str("qqq" )).FromJust()); |
| 17190 | |
| 17191 | // Add a reject handler to the resolved q1, which rejects by throwing. |
| 17192 | CompileRun( |
| 17193 | "var q3 = q1.then( \n" |
| 17194 | " function() { \n" |
| 17195 | " throw 'qqqq'; \n" |
| 17196 | " } \n" |
| 17197 | "); \n" ); |
| 17198 | CHECK(GetPromise("q0" )->HasHandler()); |
| 17199 | CHECK(GetPromise("q1" )->HasHandler()); |
| 17200 | CHECK(!GetPromise("q2" )->HasHandler()); |
| 17201 | CHECK(!GetPromise("q3" )->HasHandler()); |
| 17202 | CHECK_EQ(3, promise_reject_counter); |
| 17203 | CHECK_EQ(1, promise_revoke_counter); |
| 17204 | CHECK( |
| 17205 | GetPromise("rejected" )->Equals(env.local(), GetPromise("q3" )).FromJust()); |
| 17206 | CHECK(RejectValue()->Equals(env.local(), v8_str("qqqq" )).FromJust()); |
| 17207 | |
| 17208 | ResetPromiseStates(); |
| 17209 | |
| 17210 | // Create promise r0, which has three handlers, two of which handle rejects. |
| 17211 | CompileRun( |
| 17212 | "var r0 = new Promise( \n" |
| 17213 | " function(res, rej) { \n" |
| 17214 | " reject = rej; \n" |
| 17215 | " } \n" |
| 17216 | "); \n" |
| 17217 | "var r1 = r0.catch(function() {}); \n" |
| 17218 | "var r2 = r0.then(function() {}); \n" |
| 17219 | "var r3 = r0.then(function() {}, \n" |
| 17220 | " function() {}); \n" ); |
| 17221 | CHECK(GetPromise("r0" )->HasHandler()); |
| 17222 | CHECK(!GetPromise("r1" )->HasHandler()); |
| 17223 | CHECK(!GetPromise("r2" )->HasHandler()); |
| 17224 | CHECK(!GetPromise("r3" )->HasHandler()); |
| 17225 | CHECK_EQ(0, promise_reject_counter); |
| 17226 | CHECK_EQ(0, promise_revoke_counter); |
| 17227 | |
| 17228 | // Reject r0. |
| 17229 | CompileRun("reject('rrr')" ); |
| 17230 | CHECK(GetPromise("r0" )->HasHandler()); |
| 17231 | CHECK(!GetPromise("r1" )->HasHandler()); |
| 17232 | CHECK(!GetPromise("r2" )->HasHandler()); |
| 17233 | CHECK(!GetPromise("r3" )->HasHandler()); |
| 17234 | CHECK_EQ(1, promise_reject_counter); |
| 17235 | CHECK_EQ(0, promise_revoke_counter); |
| 17236 | CHECK( |
| 17237 | GetPromise("rejected" )->Equals(env.local(), GetPromise("r2" )).FromJust()); |
| 17238 | CHECK(RejectValue()->Equals(env.local(), v8_str("rrr" )).FromJust()); |
| 17239 | |
| 17240 | // Add reject handler to r2. |
| 17241 | CompileRun("var r4 = r2.catch(function() {});" ); |
| 17242 | CHECK(GetPromise("r0" )->HasHandler()); |
| 17243 | CHECK(!GetPromise("r1" )->HasHandler()); |
| 17244 | CHECK(GetPromise("r2" )->HasHandler()); |
| 17245 | CHECK(!GetPromise("r3" )->HasHandler()); |
| 17246 | CHECK(!GetPromise("r4" )->HasHandler()); |
| 17247 | CHECK_EQ(1, promise_reject_counter); |
| 17248 | CHECK_EQ(1, promise_revoke_counter); |
| 17249 | CHECK( |
| 17250 | GetPromise("revoked" )->Equals(env.local(), GetPromise("r2" )).FromJust()); |
| 17251 | CHECK(RejectValue()->Equals(env.local(), v8_str("rrr" )).FromJust()); |
| 17252 | |
| 17253 | // Add reject handlers to r4. |
| 17254 | CompileRun("var r5 = r4.then(function() {}, function() {});" ); |
| 17255 | CHECK(GetPromise("r0" )->HasHandler()); |
| 17256 | CHECK(!GetPromise("r1" )->HasHandler()); |
| 17257 | CHECK(GetPromise("r2" )->HasHandler()); |
| 17258 | CHECK(!GetPromise("r3" )->HasHandler()); |
| 17259 | CHECK(GetPromise("r4" )->HasHandler()); |
| 17260 | CHECK(!GetPromise("r5" )->HasHandler()); |
| 17261 | CHECK_EQ(1, promise_reject_counter); |
| 17262 | CHECK_EQ(1, promise_revoke_counter); |
| 17263 | |
| 17264 | ResetPromiseStates(); |
| 17265 | |
| 17266 | // Create promise s0, which has three handlers, none of which handle rejects. |
| 17267 | CompileRun( |
| 17268 | "var s0 = new Promise( \n" |
| 17269 | " function(res, rej) { \n" |
| 17270 | " reject = rej; \n" |
| 17271 | " } \n" |
| 17272 | "); \n" |
| 17273 | "var s1 = s0.then(function() {}); \n" |
| 17274 | "var s2 = s0.then(function() {}); \n" |
| 17275 | "var s3 = s0.then(function() {}); \n" ); |
| 17276 | CHECK(GetPromise("s0" )->HasHandler()); |
| 17277 | CHECK(!GetPromise("s1" )->HasHandler()); |
| 17278 | CHECK(!GetPromise("s2" )->HasHandler()); |
| 17279 | CHECK(!GetPromise("s3" )->HasHandler()); |
| 17280 | CHECK_EQ(0, promise_reject_counter); |
| 17281 | CHECK_EQ(0, promise_revoke_counter); |
| 17282 | |
| 17283 | // Reject s0. |
| 17284 | CompileRun("reject('sss')" ); |
| 17285 | CHECK(GetPromise("s0" )->HasHandler()); |
| 17286 | CHECK(!GetPromise("s1" )->HasHandler()); |
| 17287 | CHECK(!GetPromise("s2" )->HasHandler()); |
| 17288 | CHECK(!GetPromise("s3" )->HasHandler()); |
| 17289 | CHECK_EQ(3, promise_reject_counter); |
| 17290 | CHECK_EQ(0, promise_revoke_counter); |
| 17291 | CHECK(RejectValue()->Equals(env.local(), v8_str("sss" )).FromJust()); |
| 17292 | |
| 17293 | ResetPromiseStates(); |
| 17294 | |
| 17295 | // Swallowed exceptions in the Promise constructor. |
| 17296 | CompileRun( |
| 17297 | "var v0 = new Promise(\n" |
| 17298 | " function(res, rej) {\n" |
| 17299 | " res(1);\n" |
| 17300 | " throw new Error();\n" |
| 17301 | " }\n" |
| 17302 | ");\n" ); |
| 17303 | CHECK(!GetPromise("v0" )->HasHandler()); |
| 17304 | CHECK_EQ(0, promise_reject_counter); |
| 17305 | CHECK_EQ(0, promise_revoke_counter); |
| 17306 | CHECK_EQ(1, promise_reject_after_resolved_counter); |
| 17307 | CHECK_EQ(0, promise_resolve_after_resolved_counter); |
| 17308 | |
| 17309 | ResetPromiseStates(); |
| 17310 | |
| 17311 | // Duplication resolve. |
| 17312 | CompileRun( |
| 17313 | "var r;\n" |
| 17314 | "var y0 = new Promise(\n" |
| 17315 | " function(res, rej) {\n" |
| 17316 | " r = res;\n" |
| 17317 | " throw new Error();\n" |
| 17318 | " }\n" |
| 17319 | ");\n" |
| 17320 | "r(1);\n" ); |
| 17321 | CHECK(!GetPromise("y0" )->HasHandler()); |
| 17322 | CHECK_EQ(1, promise_reject_counter); |
| 17323 | CHECK_EQ(0, promise_revoke_counter); |
| 17324 | CHECK_EQ(0, promise_reject_after_resolved_counter); |
| 17325 | CHECK_EQ(1, promise_resolve_after_resolved_counter); |
| 17326 | |
| 17327 | // Test stack frames. |
| 17328 | env->GetIsolate()->SetCaptureStackTraceForUncaughtExceptions(true); |
| 17329 | |
| 17330 | ResetPromiseStates(); |
| 17331 | |
| 17332 | // Create promise t0, which is rejected in the constructor with an error. |
| 17333 | CompileRunWithOrigin( |
| 17334 | "var t0 = new Promise( \n" |
| 17335 | " function(res, rej) { \n" |
| 17336 | " reference_error; \n" |
| 17337 | " } \n" |
| 17338 | "); \n" , |
| 17339 | "pro" , 0, 0); |
| 17340 | CHECK(!GetPromise("t0" )->HasHandler()); |
| 17341 | CHECK_EQ(1, promise_reject_counter); |
| 17342 | CHECK_EQ(0, promise_revoke_counter); |
| 17343 | CHECK_EQ(2, promise_reject_frame_count); |
| 17344 | CHECK_EQ(3, promise_reject_line_number); |
| 17345 | CHECK_EQ(5, promise_reject_column_number); |
| 17346 | CHECK_EQ(3, promise_reject_msg_line_number); |
| 17347 | CHECK_EQ(5, promise_reject_msg_column_number); |
| 17348 | |
| 17349 | ResetPromiseStates(); |
| 17350 | |
| 17351 | // Create promise u0 and chain u1 to it, which is rejected via throw. |
| 17352 | CompileRunWithOrigin( |
| 17353 | "var u0 = Promise.resolve(); \n" |
| 17354 | "var u1 = u0.then( \n" |
| 17355 | " function() { \n" |
| 17356 | " (function() { \n" |
| 17357 | " throw new Error(); \n" |
| 17358 | " })(); \n" |
| 17359 | " } \n" |
| 17360 | " ); \n" , |
| 17361 | "pro" , 0, 0); |
| 17362 | CHECK(GetPromise("u0" )->HasHandler()); |
| 17363 | CHECK(!GetPromise("u1" )->HasHandler()); |
| 17364 | CHECK_EQ(1, promise_reject_counter); |
| 17365 | CHECK_EQ(0, promise_revoke_counter); |
| 17366 | CHECK_EQ(2, promise_reject_frame_count); |
| 17367 | CHECK_EQ(5, promise_reject_line_number); |
| 17368 | CHECK_EQ(23, promise_reject_column_number); |
| 17369 | CHECK_EQ(5, promise_reject_msg_line_number); |
| 17370 | CHECK_EQ(23, promise_reject_msg_column_number); |
| 17371 | |
| 17372 | // Throw in u3, which handles u1's rejection. |
| 17373 | CompileRunWithOrigin( |
| 17374 | "function f() { \n" |
| 17375 | " return (function() { \n" |
| 17376 | " return new Error(); \n" |
| 17377 | " })(); \n" |
| 17378 | "} \n" |
| 17379 | "var u2 = Promise.reject(f()); \n" |
| 17380 | "var u3 = u1.catch( \n" |
| 17381 | " function() { \n" |
| 17382 | " return u2; \n" |
| 17383 | " } \n" |
| 17384 | " ); \n" , |
| 17385 | "pro" , 0, 0); |
| 17386 | CHECK(GetPromise("u0" )->HasHandler()); |
| 17387 | CHECK(GetPromise("u1" )->HasHandler()); |
| 17388 | CHECK(GetPromise("u2" )->HasHandler()); |
| 17389 | CHECK(!GetPromise("u3" )->HasHandler()); |
| 17390 | CHECK_EQ(3, promise_reject_counter); |
| 17391 | CHECK_EQ(2, promise_revoke_counter); |
| 17392 | CHECK_EQ(3, promise_reject_frame_count); |
| 17393 | CHECK_EQ(3, promise_reject_line_number); |
| 17394 | CHECK_EQ(12, promise_reject_column_number); |
| 17395 | CHECK_EQ(3, promise_reject_msg_line_number); |
| 17396 | CHECK_EQ(12, promise_reject_msg_column_number); |
| 17397 | |
| 17398 | ResetPromiseStates(); |
| 17399 | |
| 17400 | // Create promise rejected promise v0, which is incorrectly handled by v1 |
| 17401 | // via chaining cycle. |
| 17402 | CompileRunWithOrigin( |
| 17403 | "var v0 = Promise.reject(); \n" |
| 17404 | "var v1 = v0.catch( \n" |
| 17405 | " function() { \n" |
| 17406 | " return v1; \n" |
| 17407 | " } \n" |
| 17408 | " ); \n" , |
| 17409 | "pro" , 0, 0); |
| 17410 | CHECK(GetPromise("v0" )->HasHandler()); |
| 17411 | CHECK(!GetPromise("v1" )->HasHandler()); |
| 17412 | CHECK_EQ(2, promise_reject_counter); |
| 17413 | CHECK_EQ(1, promise_revoke_counter); |
| 17414 | CHECK_EQ(0, promise_reject_frame_count); |
| 17415 | CHECK_EQ(-1, promise_reject_line_number); |
| 17416 | CHECK_EQ(-1, promise_reject_column_number); |
| 17417 | |
| 17418 | ResetPromiseStates(); |
| 17419 | |
| 17420 | // Create promise t1, which rejects by throwing syntax error from eval. |
| 17421 | CompileRunWithOrigin( |
| 17422 | "var t1 = new Promise( \n" |
| 17423 | " function(res, rej) { \n" |
| 17424 | " var content = '\\n\\\n" |
| 17425 | " }'; \n" |
| 17426 | " eval(content); \n" |
| 17427 | " } \n" |
| 17428 | "); \n" , |
| 17429 | "pro" , 0, 0); |
| 17430 | CHECK(!GetPromise("t1" )->HasHandler()); |
| 17431 | CHECK_EQ(1, promise_reject_counter); |
| 17432 | CHECK_EQ(0, promise_revoke_counter); |
| 17433 | CHECK_EQ(2, promise_reject_frame_count); |
| 17434 | CHECK_EQ(5, promise_reject_line_number); |
| 17435 | CHECK_EQ(10, promise_reject_column_number); |
| 17436 | CHECK_EQ(2, promise_reject_msg_line_number); |
| 17437 | CHECK_EQ(7, promise_reject_msg_column_number); |
| 17438 | } |
| 17439 | |
| 17440 | TEST(PromiseRejectIsSharedCrossOrigin) { |
| 17441 | LocalContext env; |
| 17442 | v8::Isolate* isolate = env->GetIsolate(); |
| 17443 | v8::HandleScope scope(isolate); |
| 17444 | |
| 17445 | isolate->SetPromiseRejectCallback(PromiseRejectCallback); |
| 17446 | |
| 17447 | ResetPromiseStates(); |
| 17448 | |
| 17449 | // Create promise p0. |
| 17450 | CompileRun( |
| 17451 | "var reject; \n" |
| 17452 | "var p0 = new Promise( \n" |
| 17453 | " function(res, rej) { \n" |
| 17454 | " reject = rej; \n" |
| 17455 | " } \n" |
| 17456 | "); \n" ); |
| 17457 | CHECK(!GetPromise("p0" )->HasHandler()); |
| 17458 | CHECK_EQ(0, promise_reject_counter); |
| 17459 | CHECK_EQ(0, promise_revoke_counter); |
| 17460 | // Not set because it's not yet rejected. |
| 17461 | CHECK(!promise_reject_is_shared_cross_origin); |
| 17462 | |
| 17463 | // Reject p0. |
| 17464 | CompileRun("reject('ppp');" ); |
| 17465 | CHECK_EQ(1, promise_reject_counter); |
| 17466 | CHECK_EQ(0, promise_revoke_counter); |
| 17467 | // Not set because the ScriptOriginOptions is from the script. |
| 17468 | CHECK(!promise_reject_is_shared_cross_origin); |
| 17469 | |
| 17470 | ResetPromiseStates(); |
| 17471 | |
| 17472 | // Create promise p1 |
| 17473 | CompileRun( |
| 17474 | "var reject; \n" |
| 17475 | "var p1 = new Promise( \n" |
| 17476 | " function(res, rej) { \n" |
| 17477 | " reject = rej; \n" |
| 17478 | " } \n" |
| 17479 | "); \n" ); |
| 17480 | CHECK(!GetPromise("p1" )->HasHandler()); |
| 17481 | CHECK_EQ(0, promise_reject_counter); |
| 17482 | CHECK_EQ(0, promise_revoke_counter); |
| 17483 | // Not set because it's not yet rejected. |
| 17484 | CHECK(!promise_reject_is_shared_cross_origin); |
| 17485 | |
| 17486 | // Add resolve handler (and default reject handler) to p1. |
| 17487 | CompileRun("var p2 = p1.then(function(){});" ); |
| 17488 | CHECK(GetPromise("p1" )->HasHandler()); |
| 17489 | CHECK(!GetPromise("p2" )->HasHandler()); |
| 17490 | CHECK_EQ(0, promise_reject_counter); |
| 17491 | CHECK_EQ(0, promise_revoke_counter); |
| 17492 | |
| 17493 | // Reject p1. |
| 17494 | CompileRun("reject('ppp');" ); |
| 17495 | CHECK_EQ(1, promise_reject_counter); |
| 17496 | CHECK_EQ(0, promise_revoke_counter); |
| 17497 | // Set because the event is from an empty script. |
| 17498 | CHECK(promise_reject_is_shared_cross_origin); |
| 17499 | } |
| 17500 | |
| 17501 | TEST(PromiseRejectMarkAsHandled) { |
| 17502 | LocalContext env; |
| 17503 | v8::Isolate* isolate = env->GetIsolate(); |
| 17504 | v8::HandleScope scope(isolate); |
| 17505 | |
| 17506 | isolate->SetPromiseRejectCallback(PromiseRejectCallback); |
| 17507 | |
| 17508 | ResetPromiseStates(); |
| 17509 | |
| 17510 | // Create promise p0. |
| 17511 | CompileRun( |
| 17512 | "var reject; \n" |
| 17513 | "var p0 = new Promise( \n" |
| 17514 | " function(res, rej) { \n" |
| 17515 | " reject = rej; \n" |
| 17516 | " } \n" |
| 17517 | "); \n" ); |
| 17518 | CHECK(!GetPromise("p0" )->HasHandler()); |
| 17519 | CHECK_EQ(0, promise_reject_counter); |
| 17520 | CHECK_EQ(0, promise_revoke_counter); |
| 17521 | GetPromise("p0" )->MarkAsHandled(); |
| 17522 | |
| 17523 | // Reject p0. promise_reject_counter shouldn't be incremented because |
| 17524 | // it's marked as handled. |
| 17525 | CompileRun("reject('ppp');" ); |
| 17526 | CHECK_EQ(0, promise_reject_counter); |
| 17527 | CHECK_EQ(0, promise_revoke_counter); |
| 17528 | } |
| 17529 | void PromiseRejectCallbackConstructError( |
| 17530 | v8::PromiseRejectMessage reject_message) { |
| 17531 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 17532 | CHECK_EQ(v8::Promise::PromiseState::kRejected, |
| 17533 | reject_message.GetPromise()->State()); |
| 17534 | USE(v8::Script::Compile(context, v8_str("new Error('test')" )) |
| 17535 | .ToLocalChecked() |
| 17536 | ->Run(context)); |
| 17537 | } |
| 17538 | |
| 17539 | TEST(PromiseRejectCallbackConstructError) { |
| 17540 | i::FLAG_allow_natives_syntax = true; |
| 17541 | LocalContext env; |
| 17542 | v8::Isolate* isolate = env->GetIsolate(); |
| 17543 | v8::HandleScope scope(isolate); |
| 17544 | |
| 17545 | isolate->SetPromiseRejectCallback(PromiseRejectCallbackConstructError); |
| 17546 | |
| 17547 | ResetPromiseStates(); |
| 17548 | CompileRun( |
| 17549 | "function f(p) {" |
| 17550 | " p.catch(() => {});" |
| 17551 | "}" |
| 17552 | "f(Promise.reject());" |
| 17553 | "f(Promise.reject());" |
| 17554 | "%OptimizeFunctionOnNextCall(f);" |
| 17555 | "let p = Promise.reject();" |
| 17556 | "f(p);" ); |
| 17557 | } |
| 17558 | |
| 17559 | void AnalyzeStackOfEvalWithSourceURL( |
| 17560 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 17561 | v8::HandleScope scope(args.GetIsolate()); |
| 17562 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 17563 | args.GetIsolate(), 10, v8::StackTrace::kDetailed); |
| 17564 | CHECK_EQ(5, stackTrace->GetFrameCount()); |
| 17565 | v8::Local<v8::String> url = v8_str("eval_url" ); |
| 17566 | for (int i = 0; i < 3; i++) { |
| 17567 | v8::Local<v8::String> name = |
| 17568 | stackTrace->GetFrame(args.GetIsolate(), i)->GetScriptNameOrSourceURL(); |
| 17569 | CHECK(!name.IsEmpty()); |
| 17570 | CHECK(url->Equals(args.GetIsolate()->GetCurrentContext(), name).FromJust()); |
| 17571 | } |
| 17572 | } |
| 17573 | |
| 17574 | |
| 17575 | TEST(SourceURLInStackTrace) { |
| 17576 | v8::Isolate* isolate = CcTest::isolate(); |
| 17577 | v8::HandleScope scope(isolate); |
| 17578 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 17579 | templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL" ), |
| 17580 | v8::FunctionTemplate::New(isolate, |
| 17581 | AnalyzeStackOfEvalWithSourceURL)); |
| 17582 | LocalContext context(nullptr, templ); |
| 17583 | |
| 17584 | const char *source = |
| 17585 | "function outer() {\n" |
| 17586 | "function bar() {\n" |
| 17587 | " AnalyzeStackOfEvalWithSourceURL();\n" |
| 17588 | "}\n" |
| 17589 | "function foo() {\n" |
| 17590 | "\n" |
| 17591 | " bar();\n" |
| 17592 | "}\n" |
| 17593 | "foo();\n" |
| 17594 | "}\n" |
| 17595 | "eval('(' + outer +')()%s');" ; |
| 17596 | |
| 17597 | i::ScopedVector<char> code(1024); |
| 17598 | i::SNPrintF(code, source, "//# sourceURL=eval_url" ); |
| 17599 | CHECK(CompileRun(code.start())->IsUndefined()); |
| 17600 | i::SNPrintF(code, source, "//@ sourceURL=eval_url" ); |
| 17601 | CHECK(CompileRun(code.start())->IsUndefined()); |
| 17602 | } |
| 17603 | |
| 17604 | |
| 17605 | static int scriptIdInStack[2]; |
| 17606 | |
| 17607 | void AnalyzeScriptIdInStack( |
| 17608 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 17609 | v8::HandleScope scope(args.GetIsolate()); |
| 17610 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 17611 | args.GetIsolate(), 10, v8::StackTrace::kScriptId); |
| 17612 | CHECK_EQ(2, stackTrace->GetFrameCount()); |
| 17613 | for (int i = 0; i < 2; i++) { |
| 17614 | scriptIdInStack[i] = |
| 17615 | stackTrace->GetFrame(args.GetIsolate(), i)->GetScriptId(); |
| 17616 | } |
| 17617 | } |
| 17618 | |
| 17619 | |
| 17620 | TEST(ScriptIdInStackTrace) { |
| 17621 | v8::Isolate* isolate = CcTest::isolate(); |
| 17622 | v8::HandleScope scope(isolate); |
| 17623 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 17624 | templ->Set(v8_str("AnalyzeScriptIdInStack" ), |
| 17625 | v8::FunctionTemplate::New(isolate, AnalyzeScriptIdInStack)); |
| 17626 | LocalContext context(nullptr, templ); |
| 17627 | |
| 17628 | v8::Local<v8::String> scriptSource = v8_str( |
| 17629 | "function foo() {\n" |
| 17630 | " AnalyzeScriptIdInStack();" |
| 17631 | "}\n" |
| 17632 | "foo();\n" ); |
| 17633 | v8::Local<v8::Script> script = CompileWithOrigin(scriptSource, "test" , false); |
| 17634 | script->Run(context.local()).ToLocalChecked(); |
| 17635 | for (int i = 0; i < 2; i++) { |
| 17636 | CHECK_NE(scriptIdInStack[i], v8::Message::kNoScriptIdInfo); |
| 17637 | CHECK_EQ(scriptIdInStack[i], script->GetUnboundScript()->GetId()); |
| 17638 | } |
| 17639 | } |
| 17640 | |
| 17641 | |
| 17642 | void AnalyzeStackOfInlineScriptWithSourceURL( |
| 17643 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 17644 | v8::HandleScope scope(args.GetIsolate()); |
| 17645 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 17646 | args.GetIsolate(), 10, v8::StackTrace::kDetailed); |
| 17647 | CHECK_EQ(4, stackTrace->GetFrameCount()); |
| 17648 | v8::Local<v8::String> url = v8_str("source_url" ); |
| 17649 | for (int i = 0; i < 3; i++) { |
| 17650 | v8::Local<v8::String> name = |
| 17651 | stackTrace->GetFrame(args.GetIsolate(), i)->GetScriptNameOrSourceURL(); |
| 17652 | CHECK(!name.IsEmpty()); |
| 17653 | CHECK(url->Equals(args.GetIsolate()->GetCurrentContext(), name).FromJust()); |
| 17654 | } |
| 17655 | } |
| 17656 | |
| 17657 | |
| 17658 | TEST(InlineScriptWithSourceURLInStackTrace) { |
| 17659 | v8::Isolate* isolate = CcTest::isolate(); |
| 17660 | v8::HandleScope scope(isolate); |
| 17661 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 17662 | templ->Set(v8_str("AnalyzeStackOfInlineScriptWithSourceURL" ), |
| 17663 | v8::FunctionTemplate::New( |
| 17664 | CcTest::isolate(), AnalyzeStackOfInlineScriptWithSourceURL)); |
| 17665 | LocalContext context(nullptr, templ); |
| 17666 | |
| 17667 | const char *source = |
| 17668 | "function outer() {\n" |
| 17669 | "function bar() {\n" |
| 17670 | " AnalyzeStackOfInlineScriptWithSourceURL();\n" |
| 17671 | "}\n" |
| 17672 | "function foo() {\n" |
| 17673 | "\n" |
| 17674 | " bar();\n" |
| 17675 | "}\n" |
| 17676 | "foo();\n" |
| 17677 | "}\n" |
| 17678 | "outer()\n%s" ; |
| 17679 | |
| 17680 | i::ScopedVector<char> code(1024); |
| 17681 | i::SNPrintF(code, source, "//# sourceURL=source_url" ); |
| 17682 | CHECK(CompileRunWithOrigin(code.start(), "url" , 0, 1)->IsUndefined()); |
| 17683 | i::SNPrintF(code, source, "//@ sourceURL=source_url" ); |
| 17684 | CHECK(CompileRunWithOrigin(code.start(), "url" , 0, 1)->IsUndefined()); |
| 17685 | } |
| 17686 | |
| 17687 | void SetPromise(const char* name, v8::Local<v8::Promise> promise) { |
| 17688 | CcTest::global() |
| 17689 | ->Set(CcTest::isolate()->GetCurrentContext(), v8_str(name), promise) |
| 17690 | .FromJust(); |
| 17691 | } |
| 17692 | |
| 17693 | class PromiseHookData { |
| 17694 | public: |
| 17695 | int before_hook_count = 0; |
| 17696 | int after_hook_count = 0; |
| 17697 | int promise_hook_count = 0; |
| 17698 | int parent_promise_count = 0; |
| 17699 | bool check_value = true; |
| 17700 | std::string promise_hook_value; |
| 17701 | |
| 17702 | void Reset() { |
| 17703 | before_hook_count = 0; |
| 17704 | after_hook_count = 0; |
| 17705 | promise_hook_count = 0; |
| 17706 | parent_promise_count = 0; |
| 17707 | check_value = true; |
| 17708 | promise_hook_value = "" ; |
| 17709 | } |
| 17710 | }; |
| 17711 | |
| 17712 | PromiseHookData* promise_hook_data; |
| 17713 | |
| 17714 | void CustomPromiseHook(v8::PromiseHookType type, v8::Local<v8::Promise> promise, |
| 17715 | v8::Local<v8::Value> parentPromise) { |
| 17716 | promise_hook_data->promise_hook_count++; |
| 17717 | switch (type) { |
| 17718 | case v8::PromiseHookType::kInit: |
| 17719 | SetPromise("init" , promise); |
| 17720 | |
| 17721 | if (!parentPromise->IsUndefined()) { |
| 17722 | promise_hook_data->parent_promise_count++; |
| 17723 | SetPromise("parent" , v8::Local<v8::Promise>::Cast(parentPromise)); |
| 17724 | } |
| 17725 | |
| 17726 | break; |
| 17727 | case v8::PromiseHookType::kResolve: |
| 17728 | SetPromise("resolve" , promise); |
| 17729 | break; |
| 17730 | case v8::PromiseHookType::kBefore: |
| 17731 | promise_hook_data->before_hook_count++; |
| 17732 | CHECK(promise_hook_data->before_hook_count > |
| 17733 | promise_hook_data->after_hook_count); |
| 17734 | CHECK(CcTest::global() |
| 17735 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value" )) |
| 17736 | .ToLocalChecked() |
| 17737 | ->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("" )) |
| 17738 | .FromJust()); |
| 17739 | SetPromise("before" , promise); |
| 17740 | break; |
| 17741 | case v8::PromiseHookType::kAfter: |
| 17742 | promise_hook_data->after_hook_count++; |
| 17743 | CHECK(promise_hook_data->after_hook_count <= |
| 17744 | promise_hook_data->before_hook_count); |
| 17745 | if (promise_hook_data->check_value) { |
| 17746 | CHECK( |
| 17747 | CcTest::global() |
| 17748 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value" )) |
| 17749 | .ToLocalChecked() |
| 17750 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 17751 | v8_str(promise_hook_data->promise_hook_value.c_str())) |
| 17752 | .FromJust()); |
| 17753 | } |
| 17754 | SetPromise("after" , promise); |
| 17755 | break; |
| 17756 | } |
| 17757 | } |
| 17758 | |
| 17759 | TEST(PromiseHook) { |
| 17760 | LocalContext env; |
| 17761 | v8::Isolate* isolate = env->GetIsolate(); |
| 17762 | v8::HandleScope scope(isolate); |
| 17763 | |
| 17764 | v8::Local<v8::Object> global = CcTest::global(); |
| 17765 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 17766 | |
| 17767 | promise_hook_data = new PromiseHookData(); |
| 17768 | isolate->SetPromiseHook(CustomPromiseHook); |
| 17769 | |
| 17770 | // Test that an initialized promise is passed to init. Other hooks |
| 17771 | // can not have un initialized promise. |
| 17772 | promise_hook_data->check_value = false; |
| 17773 | CompileRun("var p = new Promise(() => {});" ); |
| 17774 | |
| 17775 | auto init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17776 | CHECK(GetPromise("p" )->Equals(env.local(), init_promise).FromJust()); |
| 17777 | auto init_promise_obj = v8::Local<v8::Promise>::Cast(init_promise); |
| 17778 | CHECK_EQ(init_promise_obj->State(), v8::Promise::PromiseState::kPending); |
| 17779 | CHECK(!init_promise_obj->HasHandler()); |
| 17780 | |
| 17781 | promise_hook_data->Reset(); |
| 17782 | promise_hook_data->promise_hook_value = "fulfilled" ; |
| 17783 | const char* source = |
| 17784 | "var resolve, value = ''; \n" |
| 17785 | "var p = new Promise(r => resolve = r); \n" ; |
| 17786 | |
| 17787 | CompileRun(source); |
| 17788 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17789 | CHECK(GetPromise("p" )->Equals(env.local(), init_promise).FromJust()); |
| 17790 | CHECK_EQ(1, promise_hook_data->promise_hook_count); |
| 17791 | CHECK_EQ(0, promise_hook_data->parent_promise_count); |
| 17792 | |
| 17793 | CompileRun("var p1 = p.then(() => { value = 'fulfilled'; }); \n" ); |
| 17794 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17795 | auto parent_promise = global->Get(context, v8_str("parent" )).ToLocalChecked(); |
| 17796 | CHECK(GetPromise("p1" )->Equals(env.local(), init_promise).FromJust()); |
| 17797 | CHECK(GetPromise("p" )->Equals(env.local(), parent_promise).FromJust()); |
| 17798 | CHECK_EQ(2, promise_hook_data->promise_hook_count); |
| 17799 | CHECK_EQ(1, promise_hook_data->parent_promise_count); |
| 17800 | |
| 17801 | CompileRun("resolve(); \n" ); |
| 17802 | auto resolve_promise = |
| 17803 | global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17804 | auto before_promise = global->Get(context, v8_str("before" )).ToLocalChecked(); |
| 17805 | auto after_promise = global->Get(context, v8_str("after" )).ToLocalChecked(); |
| 17806 | CHECK(GetPromise("p1" )->Equals(env.local(), before_promise).FromJust()); |
| 17807 | CHECK(GetPromise("p1" )->Equals(env.local(), after_promise).FromJust()); |
| 17808 | CHECK(GetPromise("p1" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17809 | CHECK_EQ(6, promise_hook_data->promise_hook_count); |
| 17810 | |
| 17811 | CompileRun("value = ''; var p2 = p1.then(() => { value = 'fulfilled' }); \n" ); |
| 17812 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17813 | parent_promise = global->Get(context, v8_str("parent" )).ToLocalChecked(); |
| 17814 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17815 | before_promise = global->Get(context, v8_str("before" )).ToLocalChecked(); |
| 17816 | after_promise = global->Get(context, v8_str("after" )).ToLocalChecked(); |
| 17817 | CHECK(GetPromise("p2" )->Equals(env.local(), init_promise).FromJust()); |
| 17818 | CHECK(GetPromise("p1" )->Equals(env.local(), parent_promise).FromJust()); |
| 17819 | CHECK(GetPromise("p2" )->Equals(env.local(), before_promise).FromJust()); |
| 17820 | CHECK(GetPromise("p2" )->Equals(env.local(), after_promise).FromJust()); |
| 17821 | CHECK(GetPromise("p2" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17822 | CHECK_EQ(10, promise_hook_data->promise_hook_count); |
| 17823 | |
| 17824 | promise_hook_data->Reset(); |
| 17825 | promise_hook_data->promise_hook_value = "rejected" ; |
| 17826 | source = |
| 17827 | "var reject, value = ''; \n" |
| 17828 | "var p = new Promise((_, r) => reject = r); \n" ; |
| 17829 | |
| 17830 | CompileRun(source); |
| 17831 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17832 | CHECK(GetPromise("p" )->Equals(env.local(), init_promise).FromJust()); |
| 17833 | CHECK_EQ(1, promise_hook_data->promise_hook_count); |
| 17834 | CHECK_EQ(0, promise_hook_data->parent_promise_count); |
| 17835 | |
| 17836 | CompileRun("var p1 = p.catch(() => { value = 'rejected'; }); \n" ); |
| 17837 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17838 | parent_promise = global->Get(context, v8_str("parent" )).ToLocalChecked(); |
| 17839 | CHECK(GetPromise("p1" )->Equals(env.local(), init_promise).FromJust()); |
| 17840 | CHECK(GetPromise("p" )->Equals(env.local(), parent_promise).FromJust()); |
| 17841 | CHECK_EQ(2, promise_hook_data->promise_hook_count); |
| 17842 | CHECK_EQ(1, promise_hook_data->parent_promise_count); |
| 17843 | |
| 17844 | CompileRun("reject(); \n" ); |
| 17845 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17846 | before_promise = global->Get(context, v8_str("before" )).ToLocalChecked(); |
| 17847 | after_promise = global->Get(context, v8_str("after" )).ToLocalChecked(); |
| 17848 | CHECK(GetPromise("p1" )->Equals(env.local(), before_promise).FromJust()); |
| 17849 | CHECK(GetPromise("p1" )->Equals(env.local(), after_promise).FromJust()); |
| 17850 | CHECK(GetPromise("p1" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17851 | CHECK_EQ(6, promise_hook_data->promise_hook_count); |
| 17852 | |
| 17853 | promise_hook_data->Reset(); |
| 17854 | promise_hook_data->promise_hook_value = "Promise.resolve" ; |
| 17855 | source = |
| 17856 | "var value = ''; \n" |
| 17857 | "var p = Promise.resolve('Promise.resolve'); \n" ; |
| 17858 | |
| 17859 | CompileRun(source); |
| 17860 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17861 | CHECK(GetPromise("p" )->Equals(env.local(), init_promise).FromJust()); |
| 17862 | // init hook and resolve hook |
| 17863 | CHECK_EQ(2, promise_hook_data->promise_hook_count); |
| 17864 | CHECK_EQ(0, promise_hook_data->parent_promise_count); |
| 17865 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17866 | CHECK(GetPromise("p" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17867 | |
| 17868 | CompileRun("var p1 = p.then((v) => { value = v; }); \n" ); |
| 17869 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17870 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17871 | parent_promise = global->Get(context, v8_str("parent" )).ToLocalChecked(); |
| 17872 | before_promise = global->Get(context, v8_str("before" )).ToLocalChecked(); |
| 17873 | after_promise = global->Get(context, v8_str("after" )).ToLocalChecked(); |
| 17874 | CHECK(GetPromise("p1" )->Equals(env.local(), init_promise).FromJust()); |
| 17875 | CHECK(GetPromise("p1" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17876 | CHECK(GetPromise("p" )->Equals(env.local(), parent_promise).FromJust()); |
| 17877 | CHECK(GetPromise("p1" )->Equals(env.local(), before_promise).FromJust()); |
| 17878 | CHECK(GetPromise("p1" )->Equals(env.local(), after_promise).FromJust()); |
| 17879 | CHECK_EQ(6, promise_hook_data->promise_hook_count); |
| 17880 | CHECK_EQ(1, promise_hook_data->parent_promise_count); |
| 17881 | |
| 17882 | promise_hook_data->Reset(); |
| 17883 | source = |
| 17884 | "var resolve, value = ''; \n" |
| 17885 | "var p = new Promise((_, r) => resolve = r); \n" ; |
| 17886 | |
| 17887 | CompileRun(source); |
| 17888 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17889 | CHECK(GetPromise("p" )->Equals(env.local(), init_promise).FromJust()); |
| 17890 | CHECK_EQ(1, promise_hook_data->promise_hook_count); |
| 17891 | CHECK_EQ(0, promise_hook_data->parent_promise_count); |
| 17892 | |
| 17893 | CompileRun("resolve(); \n" ); |
| 17894 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17895 | CHECK(GetPromise("p" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17896 | CHECK_EQ(2, promise_hook_data->promise_hook_count); |
| 17897 | |
| 17898 | promise_hook_data->Reset(); |
| 17899 | source = |
| 17900 | "var reject, value = ''; \n" |
| 17901 | "var p = new Promise((_, r) => reject = r); \n" ; |
| 17902 | |
| 17903 | CompileRun(source); |
| 17904 | init_promise = global->Get(context, v8_str("init" )).ToLocalChecked(); |
| 17905 | CHECK(GetPromise("p" )->Equals(env.local(), init_promise).FromJust()); |
| 17906 | CHECK_EQ(1, promise_hook_data->promise_hook_count); |
| 17907 | CHECK_EQ(0, promise_hook_data->parent_promise_count); |
| 17908 | |
| 17909 | CompileRun("reject(); \n" ); |
| 17910 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17911 | CHECK(GetPromise("p" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17912 | CHECK_EQ(2, promise_hook_data->promise_hook_count); |
| 17913 | |
| 17914 | promise_hook_data->Reset(); |
| 17915 | // This test triggers after callbacks right after each other, so |
| 17916 | // lets just check the value at the end. |
| 17917 | promise_hook_data->check_value = false; |
| 17918 | promise_hook_data->promise_hook_value = "Promise.all" ; |
| 17919 | source = |
| 17920 | "var resolve, value = ''; \n" |
| 17921 | "var tempPromise = new Promise(r => resolve = r); \n" |
| 17922 | "var p = Promise.all([tempPromise]);\n " |
| 17923 | "var p1 = p.then(v => value = v[0]); \n" ; |
| 17924 | |
| 17925 | CompileRun(source); |
| 17926 | // 1) init hook (tempPromise) |
| 17927 | // 2) init hook (p) |
| 17928 | // 3) init hook (throwaway Promise in Promise.all, p) |
| 17929 | // 4) init hook (p1, p) |
| 17930 | CHECK_EQ(4, promise_hook_data->promise_hook_count); |
| 17931 | CHECK_EQ(2, promise_hook_data->parent_promise_count); |
| 17932 | |
| 17933 | promise_hook_data->promise_hook_value = "Promise.all" ; |
| 17934 | CompileRun("resolve('Promise.all'); \n" ); |
| 17935 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17936 | CHECK(GetPromise("p1" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17937 | // 5) resolve hook (tempPromise) |
| 17938 | // 6) resolve hook (throwaway Promise in Promise.all) |
| 17939 | // 6) before hook (throwaway Promise in Promise.all) |
| 17940 | // 7) after hook (throwaway Promise in Promise.all) |
| 17941 | // 8) before hook (p) |
| 17942 | // 9) after hook (p) |
| 17943 | // 10) resolve hook (p1) |
| 17944 | // 11) before hook (p1) |
| 17945 | // 12) after hook (p1) |
| 17946 | CHECK_EQ(12, promise_hook_data->promise_hook_count); |
| 17947 | CHECK(CcTest::global() |
| 17948 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value" )) |
| 17949 | .ToLocalChecked() |
| 17950 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 17951 | v8_str(promise_hook_data->promise_hook_value.c_str())) |
| 17952 | .FromJust()); |
| 17953 | |
| 17954 | promise_hook_data->Reset(); |
| 17955 | // This test triggers after callbacks right after each other, so |
| 17956 | // lets just check the value at the end. |
| 17957 | promise_hook_data->check_value = false; |
| 17958 | promise_hook_data->promise_hook_value = "Promise.race" ; |
| 17959 | source = |
| 17960 | "var resolve, value = ''; \n" |
| 17961 | "var tempPromise = new Promise(r => resolve = r); \n" |
| 17962 | "var p = Promise.race([tempPromise]);\n " |
| 17963 | "var p1 = p.then(v => value = v); \n" ; |
| 17964 | |
| 17965 | CompileRun(source); |
| 17966 | // 1) init hook (tempPromise) |
| 17967 | // 2) init hook (p) |
| 17968 | // 3) init hook (throwaway Promise in Promise.race, p) |
| 17969 | // 4) init hook (p1, p) |
| 17970 | CHECK_EQ(4, promise_hook_data->promise_hook_count); |
| 17971 | CHECK_EQ(2, promise_hook_data->parent_promise_count); |
| 17972 | |
| 17973 | promise_hook_data->promise_hook_value = "Promise.race" ; |
| 17974 | CompileRun("resolve('Promise.race'); \n" ); |
| 17975 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 17976 | CHECK(GetPromise("p1" )->Equals(env.local(), resolve_promise).FromJust()); |
| 17977 | // 5) resolve hook (tempPromise) |
| 17978 | // 6) resolve hook (throwaway Promise in Promise.race) |
| 17979 | // 6) before hook (throwaway Promise in Promise.race) |
| 17980 | // 7) after hook (throwaway Promise in Promise.race) |
| 17981 | // 8) before hook (p) |
| 17982 | // 9) after hook (p) |
| 17983 | // 10) resolve hook (p1) |
| 17984 | // 11) before hook (p1) |
| 17985 | // 12) after hook (p1) |
| 17986 | CHECK_EQ(12, promise_hook_data->promise_hook_count); |
| 17987 | CHECK(CcTest::global() |
| 17988 | ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value" )) |
| 17989 | .ToLocalChecked() |
| 17990 | ->Equals(CcTest::isolate()->GetCurrentContext(), |
| 17991 | v8_str(promise_hook_data->promise_hook_value.c_str())) |
| 17992 | .FromJust()); |
| 17993 | |
| 17994 | promise_hook_data->Reset(); |
| 17995 | promise_hook_data->promise_hook_value = "subclass" ; |
| 17996 | source = |
| 17997 | "var resolve, value = '';\n" |
| 17998 | "class MyPromise extends Promise { \n" |
| 17999 | " then(onFulfilled, onRejected) { \n" |
| 18000 | " return super.then(onFulfilled, onRejected); \n" |
| 18001 | " };\n" |
| 18002 | "};\n" |
| 18003 | "var p = new MyPromise(r => resolve = r);\n" ; |
| 18004 | |
| 18005 | CompileRun(source); |
| 18006 | // 1) init hook (p) |
| 18007 | CHECK_EQ(1, promise_hook_data->promise_hook_count); |
| 18008 | |
| 18009 | CompileRun("var p1 = p.then(() => value = 'subclass');\n" ); |
| 18010 | // 2) init hook (p1) |
| 18011 | CHECK_EQ(2, promise_hook_data->promise_hook_count); |
| 18012 | |
| 18013 | CompileRun("resolve();\n" ); |
| 18014 | resolve_promise = global->Get(context, v8_str("resolve" )).ToLocalChecked(); |
| 18015 | before_promise = global->Get(context, v8_str("before" )).ToLocalChecked(); |
| 18016 | after_promise = global->Get(context, v8_str("after" )).ToLocalChecked(); |
| 18017 | CHECK(GetPromise("p1" )->Equals(env.local(), before_promise).FromJust()); |
| 18018 | CHECK(GetPromise("p1" )->Equals(env.local(), after_promise).FromJust()); |
| 18019 | CHECK(GetPromise("p1" )->Equals(env.local(), resolve_promise).FromJust()); |
| 18020 | // 3) resolve hook (p) |
| 18021 | // 4) before hook (p) |
| 18022 | // 5) after hook (p) |
| 18023 | // 6) resolve hook (p1) |
| 18024 | CHECK_EQ(6, promise_hook_data->promise_hook_count); |
| 18025 | |
| 18026 | promise_hook_data->Reset(); |
| 18027 | source = |
| 18028 | "class X extends Promise {\n" |
| 18029 | " static get [Symbol.species]() {\n" |
| 18030 | " return Y;\n" |
| 18031 | " }\n" |
| 18032 | "}\n" |
| 18033 | "class Y {\n" |
| 18034 | " constructor(executor) {\n" |
| 18035 | " return new Proxy(new Promise(executor), {});\n" |
| 18036 | " }\n" |
| 18037 | "}\n" |
| 18038 | "var x = X.resolve().then(() => {});\n" ; |
| 18039 | |
| 18040 | CompileRun(source); |
| 18041 | |
| 18042 | promise_hook_data->Reset(); |
| 18043 | source = |
| 18044 | "var resolve, value = '';\n" |
| 18045 | "var p = new Promise(r => resolve = r);\n" ; |
| 18046 | |
| 18047 | CompileRun(source); |
| 18048 | CHECK_EQ(v8::Promise::kPending, GetPromise("p" )->State()); |
| 18049 | CompileRun("resolve(Promise.resolve(value));\n" ); |
| 18050 | CHECK_EQ(v8::Promise::kFulfilled, GetPromise("p" )->State()); |
| 18051 | CHECK_EQ(9, promise_hook_data->promise_hook_count); |
| 18052 | |
| 18053 | delete promise_hook_data; |
| 18054 | isolate->SetPromiseHook(nullptr); |
| 18055 | } |
| 18056 | |
| 18057 | void AnalyzeStackOfDynamicScriptWithSourceURL( |
| 18058 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 18059 | v8::HandleScope scope(args.GetIsolate()); |
| 18060 | v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace( |
| 18061 | args.GetIsolate(), 10, v8::StackTrace::kDetailed); |
| 18062 | CHECK_EQ(4, stackTrace->GetFrameCount()); |
| 18063 | v8::Local<v8::String> url = v8_str("source_url" ); |
| 18064 | for (int i = 0; i < 3; i++) { |
| 18065 | v8::Local<v8::String> name = |
| 18066 | stackTrace->GetFrame(args.GetIsolate(), i)->GetScriptNameOrSourceURL(); |
| 18067 | CHECK(!name.IsEmpty()); |
| 18068 | CHECK(url->Equals(args.GetIsolate()->GetCurrentContext(), name).FromJust()); |
| 18069 | } |
| 18070 | } |
| 18071 | |
| 18072 | |
| 18073 | TEST(DynamicWithSourceURLInStackTrace) { |
| 18074 | v8::Isolate* isolate = CcTest::isolate(); |
| 18075 | v8::HandleScope scope(isolate); |
| 18076 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 18077 | templ->Set(v8_str("AnalyzeStackOfDynamicScriptWithSourceURL" ), |
| 18078 | v8::FunctionTemplate::New( |
| 18079 | CcTest::isolate(), AnalyzeStackOfDynamicScriptWithSourceURL)); |
| 18080 | LocalContext context(nullptr, templ); |
| 18081 | |
| 18082 | const char *source = |
| 18083 | "function outer() {\n" |
| 18084 | "function bar() {\n" |
| 18085 | " AnalyzeStackOfDynamicScriptWithSourceURL();\n" |
| 18086 | "}\n" |
| 18087 | "function foo() {\n" |
| 18088 | "\n" |
| 18089 | " bar();\n" |
| 18090 | "}\n" |
| 18091 | "foo();\n" |
| 18092 | "}\n" |
| 18093 | "outer()\n%s" ; |
| 18094 | |
| 18095 | i::ScopedVector<char> code(1024); |
| 18096 | i::SNPrintF(code, source, "//# sourceURL=source_url" ); |
| 18097 | CHECK(CompileRunWithOrigin(code.start(), "url" , 0, 0)->IsUndefined()); |
| 18098 | i::SNPrintF(code, source, "//@ sourceURL=source_url" ); |
| 18099 | CHECK(CompileRunWithOrigin(code.start(), "url" , 0, 0)->IsUndefined()); |
| 18100 | } |
| 18101 | |
| 18102 | |
| 18103 | TEST(DynamicWithSourceURLInStackTraceString) { |
| 18104 | LocalContext context; |
| 18105 | v8::HandleScope scope(context->GetIsolate()); |
| 18106 | |
| 18107 | const char *source = |
| 18108 | "function outer() {\n" |
| 18109 | " function foo() {\n" |
| 18110 | " FAIL.FAIL;\n" |
| 18111 | " }\n" |
| 18112 | " foo();\n" |
| 18113 | "}\n" |
| 18114 | "outer()\n%s" ; |
| 18115 | |
| 18116 | i::ScopedVector<char> code(1024); |
| 18117 | i::SNPrintF(code, source, "//# sourceURL=source_url" ); |
| 18118 | v8::TryCatch try_catch(context->GetIsolate()); |
| 18119 | CompileRunWithOrigin(code.start(), "" , 0, 0); |
| 18120 | CHECK(try_catch.HasCaught()); |
| 18121 | v8::String::Utf8Value stack( |
| 18122 | context->GetIsolate(), |
| 18123 | try_catch.StackTrace(context.local()).ToLocalChecked()); |
| 18124 | CHECK_NOT_NULL(strstr(*stack, "at foo (source_url:3:5)" )); |
| 18125 | } |
| 18126 | |
| 18127 | |
| 18128 | TEST(EvalWithSourceURLInMessageScriptResourceNameOrSourceURL) { |
| 18129 | LocalContext context; |
| 18130 | v8::HandleScope scope(context->GetIsolate()); |
| 18131 | |
| 18132 | const char *source = |
| 18133 | "function outer() {\n" |
| 18134 | " var scriptContents = \"function foo() { FAIL.FAIL; }\\\n" |
| 18135 | " //# sourceURL=source_url\";\n" |
| 18136 | " eval(scriptContents);\n" |
| 18137 | " foo(); }\n" |
| 18138 | "outer();\n" |
| 18139 | "//# sourceURL=outer_url" ; |
| 18140 | |
| 18141 | v8::TryCatch try_catch(context->GetIsolate()); |
| 18142 | CompileRun(source); |
| 18143 | CHECK(try_catch.HasCaught()); |
| 18144 | |
| 18145 | Local<v8::Message> message = try_catch.Message(); |
| 18146 | Local<Value> sourceURL = message->GetScriptOrigin().ResourceName(); |
| 18147 | CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(), sourceURL), |
| 18148 | "source_url" )); |
| 18149 | } |
| 18150 | |
| 18151 | |
| 18152 | TEST(RecursionWithSourceURLInMessageScriptResourceNameOrSourceURL) { |
| 18153 | LocalContext context; |
| 18154 | v8::HandleScope scope(context->GetIsolate()); |
| 18155 | |
| 18156 | const char *source = |
| 18157 | "function outer() {\n" |
| 18158 | " var scriptContents = \"function boo(){ boo(); }\\\n" |
| 18159 | " //# sourceURL=source_url\";\n" |
| 18160 | " eval(scriptContents);\n" |
| 18161 | " boo(); }\n" |
| 18162 | "outer();\n" |
| 18163 | "//# sourceURL=outer_url" ; |
| 18164 | |
| 18165 | v8::TryCatch try_catch(context->GetIsolate()); |
| 18166 | CompileRun(source); |
| 18167 | CHECK(try_catch.HasCaught()); |
| 18168 | |
| 18169 | Local<v8::Message> message = try_catch.Message(); |
| 18170 | Local<Value> sourceURL = message->GetScriptOrigin().ResourceName(); |
| 18171 | CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(), sourceURL), |
| 18172 | "source_url" )); |
| 18173 | } |
| 18174 | |
| 18175 | |
| 18176 | static void CreateGarbageInOldSpace() { |
| 18177 | i::Factory* factory = CcTest::i_isolate()->factory(); |
| 18178 | v8::HandleScope scope(CcTest::isolate()); |
| 18179 | i::AlwaysAllocateScope always_allocate(CcTest::i_isolate()); |
| 18180 | for (int i = 0; i < 1000; i++) { |
| 18181 | factory->NewFixedArray(1000, i::AllocationType::kOld); |
| 18182 | } |
| 18183 | } |
| 18184 | |
| 18185 | |
| 18186 | // Test that idle notification can be handled and eventually collects garbage. |
| 18187 | TEST(TestIdleNotification) { |
| 18188 | if (!i::FLAG_incremental_marking) return; |
| 18189 | ManualGCScope manual_gc_scope; |
| 18190 | const intptr_t MB = 1024 * 1024; |
| 18191 | const double IdlePauseInSeconds = 1.0; |
| 18192 | LocalContext env; |
| 18193 | v8::HandleScope scope(env->GetIsolate()); |
| 18194 | intptr_t initial_size = CcTest::heap()->SizeOfObjects(); |
| 18195 | CreateGarbageInOldSpace(); |
| 18196 | intptr_t size_with_garbage = CcTest::heap()->SizeOfObjects(); |
| 18197 | CHECK_GT(size_with_garbage, initial_size + MB); |
| 18198 | bool finished = false; |
| 18199 | for (int i = 0; i < 200 && !finished; i++) { |
| 18200 | if (i < 10 && CcTest::heap()->incremental_marking()->IsStopped()) { |
| 18201 | CcTest::heap()->StartIdleIncrementalMarking( |
| 18202 | i::GarbageCollectionReason::kTesting); |
| 18203 | } |
| 18204 | finished = env->GetIsolate()->IdleNotificationDeadline( |
| 18205 | (v8::base::TimeTicks::HighResolutionNow().ToInternalValue() / |
| 18206 | static_cast<double>(v8::base::Time::kMicrosecondsPerSecond)) + |
| 18207 | IdlePauseInSeconds); |
| 18208 | if (CcTest::heap()->mark_compact_collector()->sweeping_in_progress()) { |
| 18209 | CcTest::heap()->mark_compact_collector()->EnsureSweepingCompleted(); |
| 18210 | } |
| 18211 | } |
| 18212 | intptr_t final_size = CcTest::heap()->SizeOfObjects(); |
| 18213 | CHECK(finished); |
| 18214 | CHECK_LT(final_size, initial_size + 1); |
| 18215 | } |
| 18216 | |
| 18217 | TEST(TestMemorySavingsMode) { |
| 18218 | LocalContext context; |
| 18219 | v8::Isolate* isolate = context->GetIsolate(); |
| 18220 | v8::internal::Isolate* i_isolate = |
| 18221 | reinterpret_cast<v8::internal::Isolate*>(isolate); |
| 18222 | CHECK(!i_isolate->IsMemorySavingsModeActive()); |
| 18223 | isolate->EnableMemorySavingsMode(); |
| 18224 | CHECK(i_isolate->IsMemorySavingsModeActive()); |
| 18225 | isolate->DisableMemorySavingsMode(); |
| 18226 | CHECK(!i_isolate->IsMemorySavingsModeActive()); |
| 18227 | } |
| 18228 | |
| 18229 | TEST(Regress2333) { |
| 18230 | LocalContext env; |
| 18231 | for (int i = 0; i < 3; i++) { |
| 18232 | CcTest::CollectGarbage(i::NEW_SPACE); |
| 18233 | } |
| 18234 | } |
| 18235 | |
| 18236 | static uint32_t* stack_limit; |
| 18237 | |
| 18238 | static void GetStackLimitCallback( |
| 18239 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 18240 | stack_limit = reinterpret_cast<uint32_t*>( |
| 18241 | CcTest::i_isolate()->stack_guard()->real_climit()); |
| 18242 | } |
| 18243 | |
| 18244 | |
| 18245 | // Uses the address of a local variable to determine the stack top now. |
| 18246 | // Given a size, returns an address that is that far from the current |
| 18247 | // top of stack. |
| 18248 | static uint32_t* ComputeStackLimit(uint32_t size) { |
| 18249 | uint32_t* answer = &size - (size / sizeof(size)); |
| 18250 | // If the size is very large and the stack is very near the bottom of |
| 18251 | // memory then the calculation above may wrap around and give an address |
| 18252 | // that is above the (downwards-growing) stack. In that case we return |
| 18253 | // a very low address. |
| 18254 | if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); |
| 18255 | return answer; |
| 18256 | } |
| 18257 | |
| 18258 | |
| 18259 | // We need at least 165kB for an x64 debug build with clang and ASAN. |
| 18260 | static const int stack_breathing_room = 256 * i::KB; |
| 18261 | |
| 18262 | |
| 18263 | TEST(SetStackLimit) { |
| 18264 | uint32_t* set_limit = ComputeStackLimit(stack_breathing_room); |
| 18265 | |
| 18266 | // Set stack limit. |
| 18267 | CcTest::isolate()->SetStackLimit(reinterpret_cast<uintptr_t>(set_limit)); |
| 18268 | |
| 18269 | // Execute a script. |
| 18270 | LocalContext env; |
| 18271 | v8::HandleScope scope(env->GetIsolate()); |
| 18272 | Local<v8::FunctionTemplate> fun_templ = |
| 18273 | v8::FunctionTemplate::New(env->GetIsolate(), GetStackLimitCallback); |
| 18274 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 18275 | CHECK(env->Global() |
| 18276 | ->Set(env.local(), v8_str("get_stack_limit" ), fun) |
| 18277 | .FromJust()); |
| 18278 | CompileRun("get_stack_limit();" ); |
| 18279 | |
| 18280 | CHECK(stack_limit == set_limit); |
| 18281 | } |
| 18282 | |
| 18283 | |
| 18284 | TEST(SetStackLimitInThread) { |
| 18285 | uint32_t* set_limit; |
| 18286 | { |
| 18287 | v8::Locker locker(CcTest::isolate()); |
| 18288 | set_limit = ComputeStackLimit(stack_breathing_room); |
| 18289 | |
| 18290 | // Set stack limit. |
| 18291 | CcTest::isolate()->SetStackLimit(reinterpret_cast<uintptr_t>(set_limit)); |
| 18292 | |
| 18293 | // Execute a script. |
| 18294 | v8::HandleScope scope(CcTest::isolate()); |
| 18295 | LocalContext env; |
| 18296 | Local<v8::FunctionTemplate> fun_templ = |
| 18297 | v8::FunctionTemplate::New(CcTest::isolate(), GetStackLimitCallback); |
| 18298 | Local<Function> fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); |
| 18299 | CHECK(env->Global() |
| 18300 | ->Set(env.local(), v8_str("get_stack_limit" ), fun) |
| 18301 | .FromJust()); |
| 18302 | CompileRun("get_stack_limit();" ); |
| 18303 | |
| 18304 | CHECK(stack_limit == set_limit); |
| 18305 | } |
| 18306 | { |
| 18307 | v8::Locker locker(CcTest::isolate()); |
| 18308 | CHECK(stack_limit == set_limit); |
| 18309 | } |
| 18310 | } |
| 18311 | |
| 18312 | THREADED_TEST(GetHeapStatistics) { |
| 18313 | LocalContext c1; |
| 18314 | v8::HandleScope scope(c1->GetIsolate()); |
| 18315 | v8::HeapStatistics heap_statistics; |
| 18316 | CHECK_EQ(0u, heap_statistics.total_heap_size()); |
| 18317 | CHECK_EQ(0u, heap_statistics.used_heap_size()); |
| 18318 | c1->GetIsolate()->GetHeapStatistics(&heap_statistics); |
| 18319 | CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); |
| 18320 | CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); |
| 18321 | } |
| 18322 | |
| 18323 | TEST(GetHeapSpaceStatistics) { |
| 18324 | LocalContext c1; |
| 18325 | v8::Isolate* isolate = c1->GetIsolate(); |
| 18326 | v8::HandleScope scope(isolate); |
| 18327 | v8::HeapStatistics heap_statistics; |
| 18328 | |
| 18329 | // Force allocation in LO_SPACE so that every space has non-zero size. |
| 18330 | v8::internal::Isolate* i_isolate = |
| 18331 | reinterpret_cast<v8::internal::Isolate*>(isolate); |
| 18332 | auto unused = i_isolate->factory()->TryNewFixedArray(512 * 1024, |
| 18333 | i::AllocationType::kOld); |
| 18334 | USE(unused); |
| 18335 | |
| 18336 | isolate->GetHeapStatistics(&heap_statistics); |
| 18337 | |
| 18338 | // Ensure that the sum of all the spaces matches the totals from |
| 18339 | // GetHeapSpaceStatics. |
| 18340 | size_t total_size = 0u; |
| 18341 | size_t total_used_size = 0u; |
| 18342 | size_t total_available_size = 0u; |
| 18343 | size_t total_physical_size = 0u; |
| 18344 | for (size_t i = 0; i < isolate->NumberOfHeapSpaces(); ++i) { |
| 18345 | v8::HeapSpaceStatistics space_statistics; |
| 18346 | isolate->GetHeapSpaceStatistics(&space_statistics, i); |
| 18347 | CHECK_NOT_NULL(space_statistics.space_name()); |
| 18348 | total_size += space_statistics.space_size(); |
| 18349 | total_used_size += space_statistics.space_used_size(); |
| 18350 | total_available_size += space_statistics.space_available_size(); |
| 18351 | total_physical_size += space_statistics.physical_space_size(); |
| 18352 | } |
| 18353 | total_available_size += CcTest::heap()->memory_allocator()->Available(); |
| 18354 | |
| 18355 | CHECK_EQ(total_size, heap_statistics.total_heap_size()); |
| 18356 | CHECK_EQ(total_used_size, heap_statistics.used_heap_size()); |
| 18357 | CHECK_EQ(total_available_size, heap_statistics.total_available_size()); |
| 18358 | CHECK_EQ(total_physical_size, heap_statistics.total_physical_size()); |
| 18359 | } |
| 18360 | |
| 18361 | TEST(NumberOfNativeContexts) { |
| 18362 | static const size_t kNumTestContexts = 10; |
| 18363 | i::Isolate* isolate = CcTest::i_isolate(); |
| 18364 | i::HandleScope scope(isolate); |
| 18365 | v8::Global<v8::Context> context[kNumTestContexts]; |
| 18366 | v8::HeapStatistics heap_statistics; |
| 18367 | CHECK_EQ(0u, heap_statistics.number_of_native_contexts()); |
| 18368 | CcTest::isolate()->GetHeapStatistics(&heap_statistics); |
| 18369 | CHECK_EQ(0u, heap_statistics.number_of_native_contexts()); |
| 18370 | for (size_t i = 0; i < kNumTestContexts; i++) { |
| 18371 | i::HandleScope inner(isolate); |
| 18372 | context[i].Reset(CcTest::isolate(), v8::Context::New(CcTest::isolate())); |
| 18373 | CcTest::isolate()->GetHeapStatistics(&heap_statistics); |
| 18374 | CHECK_EQ(i + 1, heap_statistics.number_of_native_contexts()); |
| 18375 | } |
| 18376 | for (size_t i = 0; i < kNumTestContexts; i++) { |
| 18377 | context[i].Reset(); |
| 18378 | CcTest::PreciseCollectAllGarbage(); |
| 18379 | CcTest::isolate()->GetHeapStatistics(&heap_statistics); |
| 18380 | CHECK_EQ(kNumTestContexts - i - 1u, |
| 18381 | heap_statistics.number_of_native_contexts()); |
| 18382 | } |
| 18383 | } |
| 18384 | |
| 18385 | TEST(NumberOfDetachedContexts) { |
| 18386 | static const size_t kNumTestContexts = 10; |
| 18387 | i::Isolate* isolate = CcTest::i_isolate(); |
| 18388 | i::HandleScope scope(isolate); |
| 18389 | v8::Global<v8::Context> context[kNumTestContexts]; |
| 18390 | v8::HeapStatistics heap_statistics; |
| 18391 | CHECK_EQ(0u, heap_statistics.number_of_detached_contexts()); |
| 18392 | CcTest::isolate()->GetHeapStatistics(&heap_statistics); |
| 18393 | CHECK_EQ(0u, heap_statistics.number_of_detached_contexts()); |
| 18394 | for (size_t i = 0; i < kNumTestContexts; i++) { |
| 18395 | i::HandleScope inner(isolate); |
| 18396 | v8::Local<v8::Context> local = v8::Context::New(CcTest::isolate()); |
| 18397 | context[i].Reset(CcTest::isolate(), local); |
| 18398 | local->DetachGlobal(); |
| 18399 | CcTest::isolate()->GetHeapStatistics(&heap_statistics); |
| 18400 | CHECK_EQ(i + 1, heap_statistics.number_of_detached_contexts()); |
| 18401 | } |
| 18402 | for (size_t i = 0; i < kNumTestContexts; i++) { |
| 18403 | context[i].Reset(); |
| 18404 | CcTest::PreciseCollectAllGarbage(); |
| 18405 | CcTest::isolate()->GetHeapStatistics(&heap_statistics); |
| 18406 | CHECK_EQ(kNumTestContexts - i - 1u, |
| 18407 | heap_statistics.number_of_detached_contexts()); |
| 18408 | } |
| 18409 | } |
| 18410 | |
| 18411 | class VisitorImpl : public v8::ExternalResourceVisitor { |
| 18412 | public: |
| 18413 | explicit VisitorImpl(TestResource** resource) { |
| 18414 | for (int i = 0; i < 4; i++) { |
| 18415 | resource_[i] = resource[i]; |
| 18416 | found_resource_[i] = false; |
| 18417 | } |
| 18418 | } |
| 18419 | ~VisitorImpl() override = default; |
| 18420 | void VisitExternalString(v8::Local<v8::String> string) override { |
| 18421 | if (!string->IsExternal()) { |
| 18422 | CHECK(string->IsExternalOneByte()); |
| 18423 | return; |
| 18424 | } |
| 18425 | v8::String::ExternalStringResource* resource = |
| 18426 | string->GetExternalStringResource(); |
| 18427 | CHECK(resource); |
| 18428 | for (int i = 0; i < 4; i++) { |
| 18429 | if (resource_[i] == resource) { |
| 18430 | CHECK(!found_resource_[i]); |
| 18431 | found_resource_[i] = true; |
| 18432 | } |
| 18433 | } |
| 18434 | } |
| 18435 | void CheckVisitedResources() { |
| 18436 | for (int i = 0; i < 4; i++) { |
| 18437 | CHECK(found_resource_[i]); |
| 18438 | } |
| 18439 | } |
| 18440 | |
| 18441 | private: |
| 18442 | v8::String::ExternalStringResource* resource_[4]; |
| 18443 | bool found_resource_[4]; |
| 18444 | }; |
| 18445 | |
| 18446 | |
| 18447 | TEST(ExternalizeOldSpaceTwoByteCons) { |
| 18448 | v8::Isolate* isolate = CcTest::isolate(); |
| 18449 | LocalContext env; |
| 18450 | v8::HandleScope scope(isolate); |
| 18451 | v8::Local<v8::String> cons = |
| 18452 | CompileRun("'Romeo Montague ' + 'Juliet Capulet'" ) |
| 18453 | ->ToString(env.local()) |
| 18454 | .ToLocalChecked(); |
| 18455 | CHECK(v8::Utils::OpenHandle(*cons)->IsConsString()); |
| 18456 | CcTest::CollectAllAvailableGarbage(); |
| 18457 | CHECK(CcTest::heap()->old_space()->Contains(*v8::Utils::OpenHandle(*cons))); |
| 18458 | |
| 18459 | TestResource* resource = new TestResource( |
| 18460 | AsciiToTwoByteString("Romeo Montague Juliet Capulet" )); |
| 18461 | cons->MakeExternal(resource); |
| 18462 | |
| 18463 | CHECK(cons->IsExternal()); |
| 18464 | CHECK_EQ(resource, cons->GetExternalStringResource()); |
| 18465 | String::Encoding encoding; |
| 18466 | CHECK_EQ(resource, cons->GetExternalStringResourceBase(&encoding)); |
| 18467 | CHECK_EQ(String::TWO_BYTE_ENCODING, encoding); |
| 18468 | } |
| 18469 | |
| 18470 | |
| 18471 | TEST(ExternalizeOldSpaceOneByteCons) { |
| 18472 | v8::Isolate* isolate = CcTest::isolate(); |
| 18473 | LocalContext env; |
| 18474 | v8::HandleScope scope(isolate); |
| 18475 | v8::Local<v8::String> cons = |
| 18476 | CompileRun("'Romeo Montague ' + 'Juliet Capulet'" ) |
| 18477 | ->ToString(env.local()) |
| 18478 | .ToLocalChecked(); |
| 18479 | CHECK(v8::Utils::OpenHandle(*cons)->IsConsString()); |
| 18480 | CcTest::CollectAllAvailableGarbage(); |
| 18481 | CHECK(CcTest::heap()->old_space()->Contains(*v8::Utils::OpenHandle(*cons))); |
| 18482 | |
| 18483 | TestOneByteResource* resource = |
| 18484 | new TestOneByteResource(i::StrDup("Romeo Montague Juliet Capulet" )); |
| 18485 | cons->MakeExternal(resource); |
| 18486 | |
| 18487 | CHECK(cons->IsExternalOneByte()); |
| 18488 | CHECK_EQ(resource, cons->GetExternalOneByteStringResource()); |
| 18489 | String::Encoding encoding; |
| 18490 | CHECK_EQ(resource, cons->GetExternalStringResourceBase(&encoding)); |
| 18491 | CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); |
| 18492 | } |
| 18493 | |
| 18494 | |
| 18495 | TEST(VisitExternalStrings) { |
| 18496 | v8::Isolate* isolate = CcTest::isolate(); |
| 18497 | LocalContext env; |
| 18498 | v8::HandleScope scope(isolate); |
| 18499 | const char* string = "Some string" ; |
| 18500 | uint16_t* two_byte_string = AsciiToTwoByteString(string); |
| 18501 | TestResource* resource[4]; |
| 18502 | resource[0] = new TestResource(two_byte_string); |
| 18503 | v8::Local<v8::String> string0 = |
| 18504 | v8::String::NewExternalTwoByte(env->GetIsolate(), resource[0]) |
| 18505 | .ToLocalChecked(); |
| 18506 | resource[1] = new TestResource(two_byte_string, nullptr, false); |
| 18507 | v8::Local<v8::String> string1 = |
| 18508 | v8::String::NewExternalTwoByte(env->GetIsolate(), resource[1]) |
| 18509 | .ToLocalChecked(); |
| 18510 | |
| 18511 | // Externalized symbol. |
| 18512 | resource[2] = new TestResource(two_byte_string, nullptr, false); |
| 18513 | v8::Local<v8::String> string2 = |
| 18514 | v8::String::NewFromUtf8(env->GetIsolate(), string, |
| 18515 | v8::NewStringType::kInternalized) |
| 18516 | .ToLocalChecked(); |
| 18517 | CHECK(string2->MakeExternal(resource[2])); |
| 18518 | |
| 18519 | // Symbolized External. |
| 18520 | resource[3] = new TestResource(AsciiToTwoByteString("Some other string" )); |
| 18521 | v8::Local<v8::String> string3 = |
| 18522 | v8::String::NewExternalTwoByte(env->GetIsolate(), resource[3]) |
| 18523 | .ToLocalChecked(); |
| 18524 | CcTest::CollectAllAvailableGarbage(); // Tenure string. |
| 18525 | // Turn into a symbol. |
| 18526 | i::Handle<i::String> string3_i = v8::Utils::OpenHandle(*string3); |
| 18527 | CHECK(!CcTest::i_isolate()->factory()->InternalizeString( |
| 18528 | string3_i).is_null()); |
| 18529 | CHECK(string3_i->IsInternalizedString()); |
| 18530 | |
| 18531 | // We need to add usages for string* to avoid warnings in GCC 4.7 |
| 18532 | CHECK(string0->IsExternal()); |
| 18533 | CHECK(string1->IsExternal()); |
| 18534 | CHECK(string2->IsExternal()); |
| 18535 | CHECK(string3->IsExternal()); |
| 18536 | |
| 18537 | VisitorImpl visitor(resource); |
| 18538 | isolate->VisitExternalResources(&visitor); |
| 18539 | visitor.CheckVisitedResources(); |
| 18540 | } |
| 18541 | |
| 18542 | |
| 18543 | TEST(ExternalStringCollectedAtTearDown) { |
| 18544 | int destroyed = 0; |
| 18545 | v8::Isolate::CreateParams create_params; |
| 18546 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 18547 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 18548 | { v8::Isolate::Scope isolate_scope(isolate); |
| 18549 | v8::HandleScope handle_scope(isolate); |
| 18550 | const char* s = "One string to test them all, one string to find them." ; |
| 18551 | TestOneByteResource* inscription = |
| 18552 | new TestOneByteResource(i::StrDup(s), &destroyed); |
| 18553 | v8::Local<v8::String> ring = |
| 18554 | v8::String::NewExternalOneByte(isolate, inscription).ToLocalChecked(); |
| 18555 | // Ring is still alive. Orcs are roaming freely across our lands. |
| 18556 | CHECK_EQ(0, destroyed); |
| 18557 | USE(ring); |
| 18558 | } |
| 18559 | |
| 18560 | isolate->Dispose(); |
| 18561 | // Ring has been destroyed. Free Peoples of Middle-earth Rejoice. |
| 18562 | CHECK_EQ(1, destroyed); |
| 18563 | } |
| 18564 | |
| 18565 | |
| 18566 | TEST(ExternalInternalizedStringCollectedAtTearDown) { |
| 18567 | int destroyed = 0; |
| 18568 | v8::Isolate::CreateParams create_params; |
| 18569 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 18570 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 18571 | { v8::Isolate::Scope isolate_scope(isolate); |
| 18572 | LocalContext env(isolate); |
| 18573 | v8::HandleScope handle_scope(isolate); |
| 18574 | CompileRun("var ring = 'One string to test them all';" ); |
| 18575 | const char* s = "One string to test them all" ; |
| 18576 | TestOneByteResource* inscription = |
| 18577 | new TestOneByteResource(i::StrDup(s), &destroyed); |
| 18578 | v8::Local<v8::String> ring = |
| 18579 | CompileRun("ring" )->ToString(env.local()).ToLocalChecked(); |
| 18580 | CHECK(v8::Utils::OpenHandle(*ring)->IsInternalizedString()); |
| 18581 | ring->MakeExternal(inscription); |
| 18582 | // Ring is still alive. Orcs are roaming freely across our lands. |
| 18583 | CHECK_EQ(0, destroyed); |
| 18584 | USE(ring); |
| 18585 | } |
| 18586 | |
| 18587 | isolate->Dispose(); |
| 18588 | // Ring has been destroyed. Free Peoples of Middle-earth Rejoice. |
| 18589 | CHECK_EQ(1, destroyed); |
| 18590 | } |
| 18591 | |
| 18592 | |
| 18593 | TEST(ExternalInternalizedStringCollectedAtGC) { |
| 18594 | int destroyed = 0; |
| 18595 | { LocalContext env; |
| 18596 | v8::HandleScope handle_scope(env->GetIsolate()); |
| 18597 | CompileRun("var ring = 'One string to test them all';" ); |
| 18598 | const char* s = "One string to test them all" ; |
| 18599 | TestOneByteResource* inscription = |
| 18600 | new TestOneByteResource(i::StrDup(s), &destroyed); |
| 18601 | v8::Local<v8::String> ring = CompileRun("ring" ).As<v8::String>(); |
| 18602 | CHECK(v8::Utils::OpenHandle(*ring)->IsInternalizedString()); |
| 18603 | ring->MakeExternal(inscription); |
| 18604 | // Ring is still alive. Orcs are roaming freely across our lands. |
| 18605 | CHECK_EQ(0, destroyed); |
| 18606 | USE(ring); |
| 18607 | } |
| 18608 | |
| 18609 | // Garbage collector deals swift blows to evil. |
| 18610 | CcTest::i_isolate()->compilation_cache()->Clear(); |
| 18611 | CcTest::CollectAllAvailableGarbage(); |
| 18612 | |
| 18613 | // Ring has been destroyed. Free Peoples of Middle-earth Rejoice. |
| 18614 | CHECK_EQ(1, destroyed); |
| 18615 | } |
| 18616 | |
| 18617 | |
| 18618 | static double DoubleFromBits(uint64_t value) { |
| 18619 | double target; |
| 18620 | i::MemCopy(&target, &value, sizeof(target)); |
| 18621 | return target; |
| 18622 | } |
| 18623 | |
| 18624 | |
| 18625 | static uint64_t DoubleToBits(double value) { |
| 18626 | uint64_t target; |
| 18627 | i::MemCopy(&target, &value, sizeof(target)); |
| 18628 | return target; |
| 18629 | } |
| 18630 | |
| 18631 | |
| 18632 | static double DoubleToDateTime(double input) { |
| 18633 | double date_limit = 864e13; |
| 18634 | if (std::isnan(input) || input < -date_limit || input > date_limit) { |
| 18635 | return std::numeric_limits<double>::quiet_NaN(); |
| 18636 | } |
| 18637 | return (input < 0) ? -(std::floor(-input)) : std::floor(input); |
| 18638 | } |
| 18639 | |
| 18640 | |
| 18641 | // We don't have a consistent way to write 64-bit constants syntactically, so we |
| 18642 | // split them into two 32-bit constants and combine them programmatically. |
| 18643 | static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { |
| 18644 | return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); |
| 18645 | } |
| 18646 | |
| 18647 | |
| 18648 | THREADED_TEST(QuietSignalingNaNs) { |
| 18649 | LocalContext context; |
| 18650 | v8::Isolate* isolate = context->GetIsolate(); |
| 18651 | v8::HandleScope scope(isolate); |
| 18652 | v8::TryCatch try_catch(isolate); |
| 18653 | |
| 18654 | // Special double values. |
| 18655 | double snan = DoubleFromBits(0x7FF00000, 0x00000001); |
| 18656 | double qnan = DoubleFromBits(0x7FF80000, 0x00000000); |
| 18657 | double infinity = DoubleFromBits(0x7FF00000, 0x00000000); |
| 18658 | double max_normal = DoubleFromBits(0x7FEFFFFF, 0xFFFFFFFFu); |
| 18659 | double min_normal = DoubleFromBits(0x00100000, 0x00000000); |
| 18660 | double max_denormal = DoubleFromBits(0x000FFFFF, 0xFFFFFFFFu); |
| 18661 | double min_denormal = DoubleFromBits(0x00000000, 0x00000001); |
| 18662 | |
| 18663 | // Date values are capped at +/-100000000 days (times 864e5 ms per day) |
| 18664 | // on either side of the epoch. |
| 18665 | double date_limit = 864e13; |
| 18666 | |
| 18667 | double test_values[] = { |
| 18668 | snan, |
| 18669 | qnan, |
| 18670 | infinity, |
| 18671 | max_normal, |
| 18672 | date_limit + 1, |
| 18673 | date_limit, |
| 18674 | min_normal, |
| 18675 | max_denormal, |
| 18676 | min_denormal, |
| 18677 | 0, |
| 18678 | -0, |
| 18679 | -min_denormal, |
| 18680 | -max_denormal, |
| 18681 | -min_normal, |
| 18682 | -date_limit, |
| 18683 | -date_limit - 1, |
| 18684 | -max_normal, |
| 18685 | -infinity, |
| 18686 | -qnan, |
| 18687 | -snan |
| 18688 | }; |
| 18689 | int num_test_values = 20; |
| 18690 | |
| 18691 | for (int i = 0; i < num_test_values; i++) { |
| 18692 | double test_value = test_values[i]; |
| 18693 | |
| 18694 | // Check that Number::New preserves non-NaNs and quiets SNaNs. |
| 18695 | v8::Local<v8::Value> number = v8::Number::New(isolate, test_value); |
| 18696 | double stored_number = number->NumberValue(context.local()).FromJust(); |
| 18697 | if (!std::isnan(test_value)) { |
| 18698 | CHECK_EQ(test_value, stored_number); |
| 18699 | } else { |
| 18700 | uint64_t stored_bits = DoubleToBits(stored_number); |
| 18701 | // Check if quiet nan (bits 51..62 all set). |
| 18702 | #if (defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_MIPS64)) && \ |
| 18703 | !defined(_MIPS_ARCH_MIPS64R6) && !defined(_MIPS_ARCH_MIPS32R6) && \ |
| 18704 | !defined(USE_SIMULATOR) |
| 18705 | // Most significant fraction bit for quiet nan is set to 0 |
| 18706 | // on MIPS architecture. Allowed by IEEE-754. |
| 18707 | CHECK_EQ(0xFFE, static_cast<int>((stored_bits >> 51) & 0xFFF)); |
| 18708 | #else |
| 18709 | CHECK_EQ(0xFFF, static_cast<int>((stored_bits >> 51) & 0xFFF)); |
| 18710 | #endif |
| 18711 | } |
| 18712 | |
| 18713 | // Check that Date::New preserves non-NaNs in the date range and |
| 18714 | // quiets SNaNs. |
| 18715 | v8::Local<v8::Value> date = |
| 18716 | v8::Date::New(context.local(), test_value).ToLocalChecked(); |
| 18717 | double expected_stored_date = DoubleToDateTime(test_value); |
| 18718 | double stored_date = date->NumberValue(context.local()).FromJust(); |
| 18719 | if (!std::isnan(expected_stored_date)) { |
| 18720 | CHECK_EQ(expected_stored_date, stored_date); |
| 18721 | } else { |
| 18722 | uint64_t stored_bits = DoubleToBits(stored_date); |
| 18723 | // Check if quiet nan (bits 51..62 all set). |
| 18724 | #if (defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_MIPS64)) && \ |
| 18725 | !defined(_MIPS_ARCH_MIPS64R6) && !defined(_MIPS_ARCH_MIPS32R6) && \ |
| 18726 | !defined(USE_SIMULATOR) |
| 18727 | // Most significant fraction bit for quiet nan is set to 0 |
| 18728 | // on MIPS architecture. Allowed by IEEE-754. |
| 18729 | CHECK_EQ(0xFFE, static_cast<int>((stored_bits >> 51) & 0xFFF)); |
| 18730 | #else |
| 18731 | CHECK_EQ(0xFFF, static_cast<int>((stored_bits >> 51) & 0xFFF)); |
| 18732 | #endif |
| 18733 | } |
| 18734 | } |
| 18735 | } |
| 18736 | |
| 18737 | |
| 18738 | static void SpaghettiIncident( |
| 18739 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 18740 | v8::HandleScope scope(args.GetIsolate()); |
| 18741 | v8::TryCatch tc(args.GetIsolate()); |
| 18742 | v8::MaybeLocal<v8::String> str( |
| 18743 | args[0]->ToString(args.GetIsolate()->GetCurrentContext())); |
| 18744 | USE(str); |
| 18745 | if (tc.HasCaught()) |
| 18746 | tc.ReThrow(); |
| 18747 | } |
| 18748 | |
| 18749 | |
| 18750 | // Test that an exception can be propagated down through a spaghetti |
| 18751 | // stack using ReThrow. |
| 18752 | THREADED_TEST(SpaghettiStackReThrow) { |
| 18753 | v8::Isolate* isolate = CcTest::isolate(); |
| 18754 | v8::HandleScope scope(isolate); |
| 18755 | LocalContext context; |
| 18756 | context->Global() |
| 18757 | ->Set(context.local(), v8_str("s" ), |
| 18758 | v8::FunctionTemplate::New(isolate, SpaghettiIncident) |
| 18759 | ->GetFunction(context.local()) |
| 18760 | .ToLocalChecked()) |
| 18761 | .FromJust(); |
| 18762 | v8::TryCatch try_catch(isolate); |
| 18763 | CompileRun( |
| 18764 | "var i = 0;" |
| 18765 | "var o = {" |
| 18766 | " toString: function () {" |
| 18767 | " if (i == 10) {" |
| 18768 | " throw 'Hey!';" |
| 18769 | " } else {" |
| 18770 | " i++;" |
| 18771 | " return s(o);" |
| 18772 | " }" |
| 18773 | " }" |
| 18774 | "};" |
| 18775 | "s(o);" ); |
| 18776 | CHECK(try_catch.HasCaught()); |
| 18777 | v8::String::Utf8Value value(isolate, try_catch.Exception()); |
| 18778 | CHECK_EQ(0, strcmp(*value, "Hey!" )); |
| 18779 | } |
| 18780 | |
| 18781 | |
| 18782 | TEST(Regress528) { |
| 18783 | ManualGCScope manual_gc_scope; |
| 18784 | v8::V8::Initialize(); |
| 18785 | v8::Isolate* isolate = CcTest::isolate(); |
| 18786 | i::FLAG_retain_maps_for_n_gc = 0; |
| 18787 | v8::HandleScope scope(isolate); |
| 18788 | v8::Local<Context> other_context; |
| 18789 | int gc_count; |
| 18790 | |
| 18791 | // Create a context used to keep the code from aging in the compilation |
| 18792 | // cache. |
| 18793 | other_context = Context::New(isolate); |
| 18794 | |
| 18795 | // Context-dependent context data creates reference from the compilation |
| 18796 | // cache to the global object. |
| 18797 | const char* source_simple = "1" ; |
| 18798 | { |
| 18799 | v8::HandleScope scope(isolate); |
| 18800 | v8::Local<Context> context = Context::New(isolate); |
| 18801 | |
| 18802 | context->Enter(); |
| 18803 | Local<v8::String> obj = v8_str("" ); |
| 18804 | context->SetEmbedderData(0, obj); |
| 18805 | CompileRun(source_simple); |
| 18806 | context->Exit(); |
| 18807 | } |
| 18808 | isolate->ContextDisposedNotification(); |
| 18809 | for (gc_count = 1; gc_count < 10; gc_count++) { |
| 18810 | other_context->Enter(); |
| 18811 | CompileRun(source_simple); |
| 18812 | other_context->Exit(); |
| 18813 | CcTest::CollectAllGarbage(); |
| 18814 | if (GetGlobalObjectsCount() == 1) break; |
| 18815 | } |
| 18816 | CHECK_GE(2, gc_count); |
| 18817 | CHECK_EQ(1, GetGlobalObjectsCount()); |
| 18818 | |
| 18819 | // Eval in a function creates reference from the compilation cache to the |
| 18820 | // global object. |
| 18821 | const char* source_eval = "function f(){eval('1')}; f()" ; |
| 18822 | { |
| 18823 | v8::HandleScope scope(isolate); |
| 18824 | v8::Local<Context> context = Context::New(isolate); |
| 18825 | |
| 18826 | context->Enter(); |
| 18827 | CompileRun(source_eval); |
| 18828 | context->Exit(); |
| 18829 | } |
| 18830 | isolate->ContextDisposedNotification(); |
| 18831 | for (gc_count = 1; gc_count < 10; gc_count++) { |
| 18832 | other_context->Enter(); |
| 18833 | CompileRun(source_eval); |
| 18834 | other_context->Exit(); |
| 18835 | CcTest::CollectAllGarbage(); |
| 18836 | if (GetGlobalObjectsCount() == 1) break; |
| 18837 | } |
| 18838 | CHECK_GE(2, gc_count); |
| 18839 | CHECK_EQ(1, GetGlobalObjectsCount()); |
| 18840 | |
| 18841 | // Looking up the line number for an exception creates reference from the |
| 18842 | // compilation cache to the global object. |
| 18843 | const char* source_exception = "function f(){throw 1;} f()" ; |
| 18844 | { |
| 18845 | v8::HandleScope scope(isolate); |
| 18846 | v8::Local<Context> context = Context::New(isolate); |
| 18847 | |
| 18848 | context->Enter(); |
| 18849 | v8::TryCatch try_catch(isolate); |
| 18850 | CompileRun(source_exception); |
| 18851 | CHECK(try_catch.HasCaught()); |
| 18852 | v8::Local<v8::Message> message = try_catch.Message(); |
| 18853 | CHECK(!message.IsEmpty()); |
| 18854 | CHECK_EQ(1, message->GetLineNumber(context).FromJust()); |
| 18855 | context->Exit(); |
| 18856 | } |
| 18857 | isolate->ContextDisposedNotification(); |
| 18858 | for (gc_count = 1; gc_count < 10; gc_count++) { |
| 18859 | other_context->Enter(); |
| 18860 | CompileRun(source_exception); |
| 18861 | other_context->Exit(); |
| 18862 | CcTest::CollectAllGarbage(); |
| 18863 | if (GetGlobalObjectsCount() == 1) break; |
| 18864 | } |
| 18865 | CHECK_GE(2, gc_count); |
| 18866 | CHECK_EQ(1, GetGlobalObjectsCount()); |
| 18867 | |
| 18868 | isolate->ContextDisposedNotification(); |
| 18869 | } |
| 18870 | |
| 18871 | |
| 18872 | THREADED_TEST(ScriptOrigin) { |
| 18873 | LocalContext env; |
| 18874 | v8::Isolate* isolate = env->GetIsolate(); |
| 18875 | v8::HandleScope scope(isolate); |
| 18876 | Local<v8::PrimitiveArray> array(v8::PrimitiveArray::New(isolate, 1)); |
| 18877 | Local<v8::Symbol> symbol(v8::Symbol::New(isolate)); |
| 18878 | array->Set(isolate, 0, symbol); |
| 18879 | |
| 18880 | v8::ScriptOrigin origin = v8::ScriptOrigin( |
| 18881 | v8_str("test" ), v8::Integer::New(env->GetIsolate(), 1), |
| 18882 | v8::Integer::New(env->GetIsolate(), 1), v8::True(env->GetIsolate()), |
| 18883 | v8::Local<v8::Integer>(), v8_str("http://sourceMapUrl" ), |
| 18884 | v8::True(env->GetIsolate()), v8::False(env->GetIsolate()), |
| 18885 | v8::False(env->GetIsolate()), array); |
| 18886 | v8::Local<v8::String> script = v8_str("function f() {}\n\nfunction g() {}" ); |
| 18887 | v8::Script::Compile(env.local(), script, &origin) |
| 18888 | .ToLocalChecked() |
| 18889 | ->Run(env.local()) |
| 18890 | .ToLocalChecked(); |
| 18891 | v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( |
| 18892 | env->Global()->Get(env.local(), v8_str("f" )).ToLocalChecked()); |
| 18893 | v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( |
| 18894 | env->Global()->Get(env.local(), v8_str("g" )).ToLocalChecked()); |
| 18895 | |
| 18896 | v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); |
| 18897 | CHECK_EQ(0, strcmp("test" , |
| 18898 | *v8::String::Utf8Value(env->GetIsolate(), |
| 18899 | script_origin_f.ResourceName()))); |
| 18900 | CHECK_EQ( |
| 18901 | 1, |
| 18902 | script_origin_f.ResourceLineOffset()->Int32Value(env.local()).FromJust()); |
| 18903 | CHECK(script_origin_f.Options().IsSharedCrossOrigin()); |
| 18904 | CHECK(script_origin_f.Options().IsOpaque()); |
| 18905 | printf("is name = %d\n" , script_origin_f.SourceMapUrl()->IsUndefined()); |
| 18906 | CHECK(script_origin_f.HostDefinedOptions()->Get(isolate, 0)->IsSymbol()); |
| 18907 | |
| 18908 | CHECK_EQ(0, strcmp("http://sourceMapUrl" , |
| 18909 | *v8::String::Utf8Value(env->GetIsolate(), |
| 18910 | script_origin_f.SourceMapUrl()))); |
| 18911 | |
| 18912 | v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); |
| 18913 | CHECK_EQ(0, strcmp("test" , |
| 18914 | *v8::String::Utf8Value(env->GetIsolate(), |
| 18915 | script_origin_g.ResourceName()))); |
| 18916 | CHECK_EQ( |
| 18917 | 1, |
| 18918 | script_origin_g.ResourceLineOffset()->Int32Value(env.local()).FromJust()); |
| 18919 | CHECK(script_origin_g.Options().IsSharedCrossOrigin()); |
| 18920 | CHECK(script_origin_g.Options().IsOpaque()); |
| 18921 | CHECK_EQ(0, strcmp("http://sourceMapUrl" , |
| 18922 | *v8::String::Utf8Value(env->GetIsolate(), |
| 18923 | script_origin_g.SourceMapUrl()))); |
| 18924 | CHECK(script_origin_g.HostDefinedOptions()->Get(isolate, 0)->IsSymbol()); |
| 18925 | } |
| 18926 | |
| 18927 | |
| 18928 | THREADED_TEST(FunctionGetInferredName) { |
| 18929 | LocalContext env; |
| 18930 | v8::HandleScope scope(env->GetIsolate()); |
| 18931 | v8::ScriptOrigin origin = v8::ScriptOrigin(v8_str("test" )); |
| 18932 | v8::Local<v8::String> script = |
| 18933 | v8_str("var foo = { bar : { baz : function() {}}}; var f = foo.bar.baz;" ); |
| 18934 | v8::Script::Compile(env.local(), script, &origin) |
| 18935 | .ToLocalChecked() |
| 18936 | ->Run(env.local()) |
| 18937 | .ToLocalChecked(); |
| 18938 | v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( |
| 18939 | env->Global()->Get(env.local(), v8_str("f" )).ToLocalChecked()); |
| 18940 | CHECK_EQ(0, |
| 18941 | strcmp("foo.bar.baz" , *v8::String::Utf8Value(env->GetIsolate(), |
| 18942 | f->GetInferredName()))); |
| 18943 | } |
| 18944 | |
| 18945 | |
| 18946 | THREADED_TEST(FunctionGetDebugName) { |
| 18947 | LocalContext env; |
| 18948 | v8::Isolate* isolate = env->GetIsolate(); |
| 18949 | v8::HandleScope scope(isolate); |
| 18950 | const char* code = |
| 18951 | "var error = false;" |
| 18952 | "function a() { this.x = 1; };" |
| 18953 | "a.displayName = 'display_a';" |
| 18954 | "var b = (function() {" |
| 18955 | " var f = function() { this.x = 2; };" |
| 18956 | " f.displayName = 'display_b';" |
| 18957 | " return f;" |
| 18958 | "})();" |
| 18959 | "var c = function() {};" |
| 18960 | "c.__defineGetter__('displayName', function() {" |
| 18961 | " error = true;" |
| 18962 | " throw new Error();" |
| 18963 | "});" |
| 18964 | "function d() {};" |
| 18965 | "d.__defineGetter__('displayName', function() {" |
| 18966 | " error = true;" |
| 18967 | " return 'wrong_display_name';" |
| 18968 | "});" |
| 18969 | "function e() {};" |
| 18970 | "e.displayName = 'wrong_display_name';" |
| 18971 | "e.__defineSetter__('displayName', function() {" |
| 18972 | " error = true;" |
| 18973 | " throw new Error();" |
| 18974 | "});" |
| 18975 | "function f() {};" |
| 18976 | "f.displayName = { 'foo': 6, toString: function() {" |
| 18977 | " error = true;" |
| 18978 | " return 'wrong_display_name';" |
| 18979 | "}};" |
| 18980 | "var g = function() {" |
| 18981 | " arguments.callee.displayName = 'set_in_runtime';" |
| 18982 | "}; g();" |
| 18983 | "var h = function() {};" |
| 18984 | "h.displayName = 'displayName';" |
| 18985 | "Object.defineProperty(h, 'name', { value: 'function.name' });" |
| 18986 | "var i = function() {};" |
| 18987 | "i.displayName = 239;" |
| 18988 | "Object.defineProperty(i, 'name', { value: 'function.name' });" |
| 18989 | "var j = function() {};" |
| 18990 | "Object.defineProperty(j, 'name', { value: 'function.name' });" |
| 18991 | "var foo = { bar : { baz : (0, function() {})}}; var k = foo.bar.baz;" |
| 18992 | "var foo = { bar : { baz : function() {} }}; var l = foo.bar.baz;" ; |
| 18993 | v8::ScriptOrigin origin = v8::ScriptOrigin(v8_str("test" )); |
| 18994 | v8::Script::Compile(env.local(), v8_str(code), &origin) |
| 18995 | .ToLocalChecked() |
| 18996 | ->Run(env.local()) |
| 18997 | .ToLocalChecked(); |
| 18998 | v8::Local<v8::Value> error = |
| 18999 | env->Global()->Get(env.local(), v8_str("error" )).ToLocalChecked(); |
| 19000 | CHECK(!error->BooleanValue(isolate)); |
| 19001 | const char* functions[] = {"a" , "display_a" , |
| 19002 | "b" , "display_b" , |
| 19003 | "c" , "c" , |
| 19004 | "d" , "d" , |
| 19005 | "e" , "e" , |
| 19006 | "f" , "f" , |
| 19007 | "g" , "set_in_runtime" , |
| 19008 | "h" , "displayName" , |
| 19009 | "i" , "function.name" , |
| 19010 | "j" , "function.name" , |
| 19011 | "k" , "foo.bar.baz" , |
| 19012 | "l" , "baz" }; |
| 19013 | for (size_t i = 0; i < sizeof(functions) / sizeof(functions[0]) / 2; ++i) { |
| 19014 | v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( |
| 19015 | env->Global() |
| 19016 | ->Get(env.local(), |
| 19017 | v8::String::NewFromUtf8(isolate, functions[i * 2], |
| 19018 | v8::NewStringType::kNormal) |
| 19019 | .ToLocalChecked()) |
| 19020 | .ToLocalChecked()); |
| 19021 | CHECK_EQ(0, strcmp(functions[i * 2 + 1], |
| 19022 | *v8::String::Utf8Value(isolate, f->GetDebugName()))); |
| 19023 | } |
| 19024 | } |
| 19025 | |
| 19026 | |
| 19027 | THREADED_TEST(FunctionGetDisplayName) { |
| 19028 | LocalContext env; |
| 19029 | v8::Isolate* isolate = env->GetIsolate(); |
| 19030 | v8::HandleScope scope(isolate); |
| 19031 | const char* code = "var error = false;" |
| 19032 | "function a() { this.x = 1; };" |
| 19033 | "a.displayName = 'display_a';" |
| 19034 | "var b = (function() {" |
| 19035 | " var f = function() { this.x = 2; };" |
| 19036 | " f.displayName = 'display_b';" |
| 19037 | " return f;" |
| 19038 | "})();" |
| 19039 | "var c = function() {};" |
| 19040 | "c.__defineGetter__('displayName', function() {" |
| 19041 | " error = true;" |
| 19042 | " throw new Error();" |
| 19043 | "});" |
| 19044 | "function d() {};" |
| 19045 | "d.__defineGetter__('displayName', function() {" |
| 19046 | " error = true;" |
| 19047 | " return 'wrong_display_name';" |
| 19048 | "});" |
| 19049 | "function e() {};" |
| 19050 | "e.displayName = 'wrong_display_name';" |
| 19051 | "e.__defineSetter__('displayName', function() {" |
| 19052 | " error = true;" |
| 19053 | " throw new Error();" |
| 19054 | "});" |
| 19055 | "function f() {};" |
| 19056 | "f.displayName = { 'foo': 6, toString: function() {" |
| 19057 | " error = true;" |
| 19058 | " return 'wrong_display_name';" |
| 19059 | "}};" |
| 19060 | "var g = function() {" |
| 19061 | " arguments.callee.displayName = 'set_in_runtime';" |
| 19062 | "}; g();" ; |
| 19063 | v8::ScriptOrigin origin = v8::ScriptOrigin(v8_str("test" )); |
| 19064 | v8::Script::Compile(env.local(), v8_str(code), &origin) |
| 19065 | .ToLocalChecked() |
| 19066 | ->Run(env.local()) |
| 19067 | .ToLocalChecked(); |
| 19068 | v8::Local<v8::Value> error = |
| 19069 | env->Global()->Get(env.local(), v8_str("error" )).ToLocalChecked(); |
| 19070 | v8::Local<v8::Function> a = v8::Local<v8::Function>::Cast( |
| 19071 | env->Global()->Get(env.local(), v8_str("a" )).ToLocalChecked()); |
| 19072 | v8::Local<v8::Function> b = v8::Local<v8::Function>::Cast( |
| 19073 | env->Global()->Get(env.local(), v8_str("b" )).ToLocalChecked()); |
| 19074 | v8::Local<v8::Function> c = v8::Local<v8::Function>::Cast( |
| 19075 | env->Global()->Get(env.local(), v8_str("c" )).ToLocalChecked()); |
| 19076 | v8::Local<v8::Function> d = v8::Local<v8::Function>::Cast( |
| 19077 | env->Global()->Get(env.local(), v8_str("d" )).ToLocalChecked()); |
| 19078 | v8::Local<v8::Function> e = v8::Local<v8::Function>::Cast( |
| 19079 | env->Global()->Get(env.local(), v8_str("e" )).ToLocalChecked()); |
| 19080 | v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( |
| 19081 | env->Global()->Get(env.local(), v8_str("f" )).ToLocalChecked()); |
| 19082 | v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( |
| 19083 | env->Global()->Get(env.local(), v8_str("g" )).ToLocalChecked()); |
| 19084 | CHECK(!error->BooleanValue(isolate)); |
| 19085 | CHECK_EQ(0, strcmp("display_a" , |
| 19086 | *v8::String::Utf8Value(isolate, a->GetDisplayName()))); |
| 19087 | CHECK_EQ(0, strcmp("display_b" , |
| 19088 | *v8::String::Utf8Value(isolate, b->GetDisplayName()))); |
| 19089 | CHECK(c->GetDisplayName()->IsUndefined()); |
| 19090 | CHECK(d->GetDisplayName()->IsUndefined()); |
| 19091 | CHECK(e->GetDisplayName()->IsUndefined()); |
| 19092 | CHECK(f->GetDisplayName()->IsUndefined()); |
| 19093 | CHECK_EQ(0, strcmp("set_in_runtime" , |
| 19094 | *v8::String::Utf8Value(isolate, g->GetDisplayName()))); |
| 19095 | } |
| 19096 | |
| 19097 | |
| 19098 | THREADED_TEST(ScriptLineNumber) { |
| 19099 | LocalContext env; |
| 19100 | v8::HandleScope scope(env->GetIsolate()); |
| 19101 | v8::ScriptOrigin origin = v8::ScriptOrigin(v8_str("test" )); |
| 19102 | v8::Local<v8::String> script = v8_str("function f() {}\n\nfunction g() {}" ); |
| 19103 | v8::Script::Compile(env.local(), script, &origin) |
| 19104 | .ToLocalChecked() |
| 19105 | ->Run(env.local()) |
| 19106 | .ToLocalChecked(); |
| 19107 | v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( |
| 19108 | env->Global()->Get(env.local(), v8_str("f" )).ToLocalChecked()); |
| 19109 | v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( |
| 19110 | env->Global()->Get(env.local(), v8_str("g" )).ToLocalChecked()); |
| 19111 | CHECK_EQ(0, f->GetScriptLineNumber()); |
| 19112 | CHECK_EQ(2, g->GetScriptLineNumber()); |
| 19113 | } |
| 19114 | |
| 19115 | |
| 19116 | THREADED_TEST(ScriptColumnNumber) { |
| 19117 | LocalContext env; |
| 19118 | v8::Isolate* isolate = env->GetIsolate(); |
| 19119 | v8::HandleScope scope(isolate); |
| 19120 | v8::ScriptOrigin origin = |
| 19121 | v8::ScriptOrigin(v8_str("test" ), v8::Integer::New(isolate, 3), |
| 19122 | v8::Integer::New(isolate, 2)); |
| 19123 | v8::Local<v8::String> script = |
| 19124 | v8_str("function foo() {}\n\n function bar() {}" ); |
| 19125 | v8::Script::Compile(env.local(), script, &origin) |
| 19126 | .ToLocalChecked() |
| 19127 | ->Run(env.local()) |
| 19128 | .ToLocalChecked(); |
| 19129 | v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( |
| 19130 | env->Global()->Get(env.local(), v8_str("foo" )).ToLocalChecked()); |
| 19131 | v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( |
| 19132 | env->Global()->Get(env.local(), v8_str("bar" )).ToLocalChecked()); |
| 19133 | CHECK_EQ(14, foo->GetScriptColumnNumber()); |
| 19134 | CHECK_EQ(17, bar->GetScriptColumnNumber()); |
| 19135 | } |
| 19136 | |
| 19137 | |
| 19138 | THREADED_TEST(FunctionGetScriptId) { |
| 19139 | LocalContext env; |
| 19140 | v8::Isolate* isolate = env->GetIsolate(); |
| 19141 | v8::HandleScope scope(isolate); |
| 19142 | v8::ScriptOrigin origin = |
| 19143 | v8::ScriptOrigin(v8_str("test" ), v8::Integer::New(isolate, 3), |
| 19144 | v8::Integer::New(isolate, 2)); |
| 19145 | v8::Local<v8::String> scriptSource = |
| 19146 | v8_str("function foo() {}\n\n function bar() {}" ); |
| 19147 | v8::Local<v8::Script> script( |
| 19148 | v8::Script::Compile(env.local(), scriptSource, &origin).ToLocalChecked()); |
| 19149 | script->Run(env.local()).ToLocalChecked(); |
| 19150 | v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( |
| 19151 | env->Global()->Get(env.local(), v8_str("foo" )).ToLocalChecked()); |
| 19152 | v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( |
| 19153 | env->Global()->Get(env.local(), v8_str("bar" )).ToLocalChecked()); |
| 19154 | CHECK_EQ(script->GetUnboundScript()->GetId(), foo->ScriptId()); |
| 19155 | CHECK_EQ(script->GetUnboundScript()->GetId(), bar->ScriptId()); |
| 19156 | } |
| 19157 | |
| 19158 | |
| 19159 | THREADED_TEST(FunctionGetBoundFunction) { |
| 19160 | LocalContext env; |
| 19161 | v8::HandleScope scope(env->GetIsolate()); |
| 19162 | v8::ScriptOrigin origin = v8::ScriptOrigin(v8_str("test" )); |
| 19163 | v8::Local<v8::String> script = v8_str( |
| 19164 | "var a = new Object();\n" |
| 19165 | "a.x = 1;\n" |
| 19166 | "function f () { return this.x };\n" |
| 19167 | "var g = f.bind(a);\n" |
| 19168 | "var b = g();" ); |
| 19169 | v8::Script::Compile(env.local(), script, &origin) |
| 19170 | .ToLocalChecked() |
| 19171 | ->Run(env.local()) |
| 19172 | .ToLocalChecked(); |
| 19173 | v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( |
| 19174 | env->Global()->Get(env.local(), v8_str("f" )).ToLocalChecked()); |
| 19175 | v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( |
| 19176 | env->Global()->Get(env.local(), v8_str("g" )).ToLocalChecked()); |
| 19177 | CHECK(g->GetBoundFunction()->IsFunction()); |
| 19178 | Local<v8::Function> original_function = Local<v8::Function>::Cast( |
| 19179 | g->GetBoundFunction()); |
| 19180 | CHECK(f->GetName() |
| 19181 | ->Equals(env.local(), original_function->GetName()) |
| 19182 | .FromJust()); |
| 19183 | CHECK_EQ(f->GetScriptLineNumber(), original_function->GetScriptLineNumber()); |
| 19184 | CHECK_EQ(f->GetScriptColumnNumber(), |
| 19185 | original_function->GetScriptColumnNumber()); |
| 19186 | } |
| 19187 | |
| 19188 | |
| 19189 | static void GetterWhichReturns42( |
| 19190 | Local<String> name, |
| 19191 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 19192 | CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); |
| 19193 | CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); |
| 19194 | info.GetReturnValue().Set(v8_num(42)); |
| 19195 | } |
| 19196 | |
| 19197 | |
| 19198 | static void SetterWhichSetsYOnThisTo23( |
| 19199 | Local<String> name, |
| 19200 | Local<Value> value, |
| 19201 | const v8::PropertyCallbackInfo<void>& info) { |
| 19202 | CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); |
| 19203 | CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); |
| 19204 | Local<Object>::Cast(info.This()) |
| 19205 | ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("y" ), v8_num(23)) |
| 19206 | .FromJust(); |
| 19207 | } |
| 19208 | |
| 19209 | |
| 19210 | void FooGetInterceptor(Local<Name> name, |
| 19211 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 19212 | CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); |
| 19213 | CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); |
| 19214 | if (!name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo" )) |
| 19215 | .FromJust()) { |
| 19216 | return; |
| 19217 | } |
| 19218 | info.GetReturnValue().Set(v8_num(42)); |
| 19219 | } |
| 19220 | |
| 19221 | |
| 19222 | void FooSetInterceptor(Local<Name> name, Local<Value> value, |
| 19223 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 19224 | CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject()); |
| 19225 | CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject()); |
| 19226 | if (!name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo" )) |
| 19227 | .FromJust()) { |
| 19228 | return; |
| 19229 | } |
| 19230 | Local<Object>::Cast(info.This()) |
| 19231 | ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("y" ), v8_num(23)) |
| 19232 | .FromJust(); |
| 19233 | info.GetReturnValue().Set(v8_num(23)); |
| 19234 | } |
| 19235 | |
| 19236 | |
| 19237 | TEST(SetterOnConstructorPrototype) { |
| 19238 | v8::Isolate* isolate = CcTest::isolate(); |
| 19239 | v8::HandleScope scope(isolate); |
| 19240 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 19241 | templ->SetAccessor(v8_str("x" ), GetterWhichReturns42, |
| 19242 | SetterWhichSetsYOnThisTo23); |
| 19243 | LocalContext context; |
| 19244 | CHECK(context->Global() |
| 19245 | ->Set(context.local(), v8_str("P" ), |
| 19246 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 19247 | .FromJust()); |
| 19248 | CompileRun("function C1() {" |
| 19249 | " this.x = 23;" |
| 19250 | "};" |
| 19251 | "C1.prototype = P;" |
| 19252 | "function C2() {" |
| 19253 | " this.x = 23" |
| 19254 | "};" |
| 19255 | "C2.prototype = { };" |
| 19256 | "C2.prototype.__proto__ = P;" ); |
| 19257 | |
| 19258 | v8::Local<v8::Script> script; |
| 19259 | script = v8_compile("new C1();" ); |
| 19260 | for (int i = 0; i < 10; i++) { |
| 19261 | v8::Local<v8::Object> c1 = v8::Local<v8::Object>::Cast( |
| 19262 | script->Run(context.local()).ToLocalChecked()); |
| 19263 | CHECK_EQ(42, c1->Get(context.local(), v8_str("x" )) |
| 19264 | .ToLocalChecked() |
| 19265 | ->Int32Value(context.local()) |
| 19266 | .FromJust()); |
| 19267 | CHECK_EQ(23, c1->Get(context.local(), v8_str("y" )) |
| 19268 | .ToLocalChecked() |
| 19269 | ->Int32Value(context.local()) |
| 19270 | .FromJust()); |
| 19271 | } |
| 19272 | |
| 19273 | script = v8_compile("new C2();" ); |
| 19274 | for (int i = 0; i < 10; i++) { |
| 19275 | v8::Local<v8::Object> c2 = v8::Local<v8::Object>::Cast( |
| 19276 | script->Run(context.local()).ToLocalChecked()); |
| 19277 | CHECK_EQ(42, c2->Get(context.local(), v8_str("x" )) |
| 19278 | .ToLocalChecked() |
| 19279 | ->Int32Value(context.local()) |
| 19280 | .FromJust()); |
| 19281 | CHECK_EQ(23, c2->Get(context.local(), v8_str("y" )) |
| 19282 | .ToLocalChecked() |
| 19283 | ->Int32Value(context.local()) |
| 19284 | .FromJust()); |
| 19285 | } |
| 19286 | } |
| 19287 | |
| 19288 | |
| 19289 | static void NamedPropertySetterWhichSetsYOnThisTo23( |
| 19290 | Local<Name> name, Local<Value> value, |
| 19291 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 19292 | if (name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("x" )) |
| 19293 | .FromJust()) { |
| 19294 | Local<Object>::Cast(info.This()) |
| 19295 | ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("y" ), v8_num(23)) |
| 19296 | .FromJust(); |
| 19297 | } |
| 19298 | } |
| 19299 | |
| 19300 | |
| 19301 | THREADED_TEST(InterceptorOnConstructorPrototype) { |
| 19302 | v8::Isolate* isolate = CcTest::isolate(); |
| 19303 | v8::HandleScope scope(isolate); |
| 19304 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 19305 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 19306 | NamedPropertyGetterWhichReturns42, |
| 19307 | NamedPropertySetterWhichSetsYOnThisTo23)); |
| 19308 | LocalContext context; |
| 19309 | CHECK(context->Global() |
| 19310 | ->Set(context.local(), v8_str("P" ), |
| 19311 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 19312 | .FromJust()); |
| 19313 | CompileRun("function C1() {" |
| 19314 | " this.x = 23;" |
| 19315 | "};" |
| 19316 | "C1.prototype = P;" |
| 19317 | "function C2() {" |
| 19318 | " this.x = 23" |
| 19319 | "};" |
| 19320 | "C2.prototype = { };" |
| 19321 | "C2.prototype.__proto__ = P;" ); |
| 19322 | |
| 19323 | v8::Local<v8::Script> script; |
| 19324 | script = v8_compile("new C1();" ); |
| 19325 | for (int i = 0; i < 10; i++) { |
| 19326 | v8::Local<v8::Object> c1 = v8::Local<v8::Object>::Cast( |
| 19327 | script->Run(context.local()).ToLocalChecked()); |
| 19328 | CHECK_EQ(23, c1->Get(context.local(), v8_str("x" )) |
| 19329 | .ToLocalChecked() |
| 19330 | ->Int32Value(context.local()) |
| 19331 | .FromJust()); |
| 19332 | CHECK_EQ(42, c1->Get(context.local(), v8_str("y" )) |
| 19333 | .ToLocalChecked() |
| 19334 | ->Int32Value(context.local()) |
| 19335 | .FromJust()); |
| 19336 | } |
| 19337 | |
| 19338 | script = v8_compile("new C2();" ); |
| 19339 | for (int i = 0; i < 10; i++) { |
| 19340 | v8::Local<v8::Object> c2 = v8::Local<v8::Object>::Cast( |
| 19341 | script->Run(context.local()).ToLocalChecked()); |
| 19342 | CHECK_EQ(23, c2->Get(context.local(), v8_str("x" )) |
| 19343 | .ToLocalChecked() |
| 19344 | ->Int32Value(context.local()) |
| 19345 | .FromJust()); |
| 19346 | CHECK_EQ(42, c2->Get(context.local(), v8_str("y" )) |
| 19347 | .ToLocalChecked() |
| 19348 | ->Int32Value(context.local()) |
| 19349 | .FromJust()); |
| 19350 | } |
| 19351 | } |
| 19352 | |
| 19353 | |
| 19354 | TEST(Regress618) { |
| 19355 | const char* source = "function C1() {" |
| 19356 | " this.x = 23;" |
| 19357 | "};" |
| 19358 | "C1.prototype = P;" ; |
| 19359 | |
| 19360 | LocalContext context; |
| 19361 | v8::Isolate* isolate = context->GetIsolate(); |
| 19362 | v8::HandleScope scope(isolate); |
| 19363 | v8::Local<v8::Script> script; |
| 19364 | |
| 19365 | // Use a simple object as prototype. |
| 19366 | v8::Local<v8::Object> prototype = v8::Object::New(isolate); |
| 19367 | prototype->Set(context.local(), v8_str("y" ), v8_num(42)).FromJust(); |
| 19368 | CHECK(context->Global() |
| 19369 | ->Set(context.local(), v8_str("P" ), prototype) |
| 19370 | .FromJust()); |
| 19371 | |
| 19372 | // This compile will add the code to the compilation cache. |
| 19373 | CompileRun(source); |
| 19374 | |
| 19375 | script = v8_compile("new C1();" ); |
| 19376 | // Allow enough iterations for the inobject slack tracking logic |
| 19377 | // to finalize instance size and install the fast construct stub. |
| 19378 | for (int i = 0; i < 256; i++) { |
| 19379 | v8::Local<v8::Object> c1 = v8::Local<v8::Object>::Cast( |
| 19380 | script->Run(context.local()).ToLocalChecked()); |
| 19381 | CHECK_EQ(23, c1->Get(context.local(), v8_str("x" )) |
| 19382 | .ToLocalChecked() |
| 19383 | ->Int32Value(context.local()) |
| 19384 | .FromJust()); |
| 19385 | CHECK_EQ(42, c1->Get(context.local(), v8_str("y" )) |
| 19386 | .ToLocalChecked() |
| 19387 | ->Int32Value(context.local()) |
| 19388 | .FromJust()); |
| 19389 | } |
| 19390 | |
| 19391 | // Use an API object with accessors as prototype. |
| 19392 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 19393 | templ->SetAccessor(v8_str("x" ), GetterWhichReturns42, |
| 19394 | SetterWhichSetsYOnThisTo23); |
| 19395 | CHECK(context->Global() |
| 19396 | ->Set(context.local(), v8_str("P" ), |
| 19397 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 19398 | .FromJust()); |
| 19399 | |
| 19400 | // This compile will get the code from the compilation cache. |
| 19401 | CompileRun(source); |
| 19402 | |
| 19403 | script = v8_compile("new C1();" ); |
| 19404 | for (int i = 0; i < 10; i++) { |
| 19405 | v8::Local<v8::Object> c1 = v8::Local<v8::Object>::Cast( |
| 19406 | script->Run(context.local()).ToLocalChecked()); |
| 19407 | CHECK_EQ(42, c1->Get(context.local(), v8_str("x" )) |
| 19408 | .ToLocalChecked() |
| 19409 | ->Int32Value(context.local()) |
| 19410 | .FromJust()); |
| 19411 | CHECK_EQ(23, c1->Get(context.local(), v8_str("y" )) |
| 19412 | .ToLocalChecked() |
| 19413 | ->Int32Value(context.local()) |
| 19414 | .FromJust()); |
| 19415 | } |
| 19416 | } |
| 19417 | |
| 19418 | v8::Isolate* gc_callbacks_isolate = nullptr; |
| 19419 | int prologue_call_count = 0; |
| 19420 | int epilogue_call_count = 0; |
| 19421 | int prologue_call_count_second = 0; |
| 19422 | int epilogue_call_count_second = 0; |
| 19423 | int prologue_call_count_alloc = 0; |
| 19424 | int epilogue_call_count_alloc = 0; |
| 19425 | |
| 19426 | void PrologueCallback(v8::Isolate* isolate, |
| 19427 | v8::GCType, |
| 19428 | v8::GCCallbackFlags flags) { |
| 19429 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19430 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19431 | ++prologue_call_count; |
| 19432 | } |
| 19433 | |
| 19434 | void EpilogueCallback(v8::Isolate* isolate, |
| 19435 | v8::GCType, |
| 19436 | v8::GCCallbackFlags flags) { |
| 19437 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19438 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19439 | ++epilogue_call_count; |
| 19440 | } |
| 19441 | |
| 19442 | |
| 19443 | void PrologueCallbackSecond(v8::Isolate* isolate, |
| 19444 | v8::GCType, |
| 19445 | v8::GCCallbackFlags flags) { |
| 19446 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19447 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19448 | ++prologue_call_count_second; |
| 19449 | } |
| 19450 | |
| 19451 | |
| 19452 | void EpilogueCallbackSecond(v8::Isolate* isolate, |
| 19453 | v8::GCType, |
| 19454 | v8::GCCallbackFlags flags) { |
| 19455 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19456 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19457 | ++epilogue_call_count_second; |
| 19458 | } |
| 19459 | |
| 19460 | void PrologueCallbackNew(v8::Isolate* isolate, v8::GCType, |
| 19461 | v8::GCCallbackFlags flags, void* data) { |
| 19462 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19463 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19464 | ++*static_cast<int*>(data); |
| 19465 | } |
| 19466 | |
| 19467 | void EpilogueCallbackNew(v8::Isolate* isolate, v8::GCType, |
| 19468 | v8::GCCallbackFlags flags, void* data) { |
| 19469 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19470 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19471 | ++*static_cast<int*>(data); |
| 19472 | } |
| 19473 | |
| 19474 | void PrologueCallbackAlloc(v8::Isolate* isolate, |
| 19475 | v8::GCType, |
| 19476 | v8::GCCallbackFlags flags) { |
| 19477 | v8::HandleScope scope(isolate); |
| 19478 | |
| 19479 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19480 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19481 | ++prologue_call_count_alloc; |
| 19482 | |
| 19483 | // Simulate full heap to see if we will reenter this callback |
| 19484 | i::heap::SimulateFullSpace(CcTest::heap()->new_space()); |
| 19485 | |
| 19486 | Local<Object> obj = Object::New(isolate); |
| 19487 | CHECK(!obj.IsEmpty()); |
| 19488 | |
| 19489 | CcTest::PreciseCollectAllGarbage(); |
| 19490 | } |
| 19491 | |
| 19492 | |
| 19493 | void EpilogueCallbackAlloc(v8::Isolate* isolate, |
| 19494 | v8::GCType, |
| 19495 | v8::GCCallbackFlags flags) { |
| 19496 | v8::HandleScope scope(isolate); |
| 19497 | |
| 19498 | CHECK_EQ(flags, v8::kNoGCCallbackFlags); |
| 19499 | CHECK_EQ(gc_callbacks_isolate, isolate); |
| 19500 | ++epilogue_call_count_alloc; |
| 19501 | |
| 19502 | // Simulate full heap to see if we will reenter this callback |
| 19503 | i::heap::SimulateFullSpace(CcTest::heap()->new_space()); |
| 19504 | |
| 19505 | Local<Object> obj = Object::New(isolate); |
| 19506 | CHECK(!obj.IsEmpty()); |
| 19507 | |
| 19508 | CcTest::PreciseCollectAllGarbage(); |
| 19509 | } |
| 19510 | |
| 19511 | |
| 19512 | TEST(GCCallbacksOld) { |
| 19513 | LocalContext context; |
| 19514 | |
| 19515 | gc_callbacks_isolate = context->GetIsolate(); |
| 19516 | |
| 19517 | context->GetIsolate()->AddGCPrologueCallback(PrologueCallback); |
| 19518 | context->GetIsolate()->AddGCEpilogueCallback(EpilogueCallback); |
| 19519 | CHECK_EQ(0, prologue_call_count); |
| 19520 | CHECK_EQ(0, epilogue_call_count); |
| 19521 | CcTest::CollectAllGarbage(); |
| 19522 | CHECK_EQ(1, prologue_call_count); |
| 19523 | CHECK_EQ(1, epilogue_call_count); |
| 19524 | context->GetIsolate()->AddGCPrologueCallback(PrologueCallbackSecond); |
| 19525 | context->GetIsolate()->AddGCEpilogueCallback(EpilogueCallbackSecond); |
| 19526 | CcTest::CollectAllGarbage(); |
| 19527 | CHECK_EQ(2, prologue_call_count); |
| 19528 | CHECK_EQ(2, epilogue_call_count); |
| 19529 | CHECK_EQ(1, prologue_call_count_second); |
| 19530 | CHECK_EQ(1, epilogue_call_count_second); |
| 19531 | context->GetIsolate()->RemoveGCPrologueCallback(PrologueCallback); |
| 19532 | context->GetIsolate()->RemoveGCEpilogueCallback(EpilogueCallback); |
| 19533 | CcTest::CollectAllGarbage(); |
| 19534 | CHECK_EQ(2, prologue_call_count); |
| 19535 | CHECK_EQ(2, epilogue_call_count); |
| 19536 | CHECK_EQ(2, prologue_call_count_second); |
| 19537 | CHECK_EQ(2, epilogue_call_count_second); |
| 19538 | context->GetIsolate()->RemoveGCPrologueCallback(PrologueCallbackSecond); |
| 19539 | context->GetIsolate()->RemoveGCEpilogueCallback(EpilogueCallbackSecond); |
| 19540 | CcTest::CollectAllGarbage(); |
| 19541 | CHECK_EQ(2, prologue_call_count); |
| 19542 | CHECK_EQ(2, epilogue_call_count); |
| 19543 | CHECK_EQ(2, prologue_call_count_second); |
| 19544 | CHECK_EQ(2, epilogue_call_count_second); |
| 19545 | } |
| 19546 | |
| 19547 | TEST(GCCallbacksWithData) { |
| 19548 | LocalContext context; |
| 19549 | |
| 19550 | gc_callbacks_isolate = context->GetIsolate(); |
| 19551 | int prologue1 = 0; |
| 19552 | int epilogue1 = 0; |
| 19553 | int prologue2 = 0; |
| 19554 | int epilogue2 = 0; |
| 19555 | |
| 19556 | context->GetIsolate()->AddGCPrologueCallback(PrologueCallbackNew, &prologue1); |
| 19557 | context->GetIsolate()->AddGCEpilogueCallback(EpilogueCallbackNew, &epilogue1); |
| 19558 | CHECK_EQ(0, prologue1); |
| 19559 | CHECK_EQ(0, epilogue1); |
| 19560 | CHECK_EQ(0, prologue2); |
| 19561 | CHECK_EQ(0, epilogue2); |
| 19562 | CcTest::CollectAllGarbage(); |
| 19563 | CHECK_EQ(1, prologue1); |
| 19564 | CHECK_EQ(1, epilogue1); |
| 19565 | CHECK_EQ(0, prologue2); |
| 19566 | CHECK_EQ(0, epilogue2); |
| 19567 | context->GetIsolate()->AddGCPrologueCallback(PrologueCallbackNew, &prologue2); |
| 19568 | context->GetIsolate()->AddGCEpilogueCallback(EpilogueCallbackNew, &epilogue2); |
| 19569 | CcTest::CollectAllGarbage(); |
| 19570 | CHECK_EQ(2, prologue1); |
| 19571 | CHECK_EQ(2, epilogue1); |
| 19572 | CHECK_EQ(1, prologue2); |
| 19573 | CHECK_EQ(1, epilogue2); |
| 19574 | context->GetIsolate()->RemoveGCPrologueCallback(PrologueCallbackNew, |
| 19575 | &prologue1); |
| 19576 | context->GetIsolate()->RemoveGCEpilogueCallback(EpilogueCallbackNew, |
| 19577 | &epilogue1); |
| 19578 | CcTest::CollectAllGarbage(); |
| 19579 | CHECK_EQ(2, prologue1); |
| 19580 | CHECK_EQ(2, epilogue1); |
| 19581 | CHECK_EQ(2, prologue2); |
| 19582 | CHECK_EQ(2, epilogue2); |
| 19583 | context->GetIsolate()->RemoveGCPrologueCallback(PrologueCallbackNew, |
| 19584 | &prologue2); |
| 19585 | context->GetIsolate()->RemoveGCEpilogueCallback(EpilogueCallbackNew, |
| 19586 | &epilogue2); |
| 19587 | CcTest::CollectAllGarbage(); |
| 19588 | CHECK_EQ(2, prologue1); |
| 19589 | CHECK_EQ(2, epilogue1); |
| 19590 | CHECK_EQ(2, prologue2); |
| 19591 | CHECK_EQ(2, epilogue2); |
| 19592 | } |
| 19593 | |
| 19594 | TEST(GCCallbacks) { |
| 19595 | LocalContext context; |
| 19596 | v8::Isolate* isolate = context->GetIsolate(); |
| 19597 | gc_callbacks_isolate = isolate; |
| 19598 | isolate->AddGCPrologueCallback(PrologueCallback); |
| 19599 | isolate->AddGCEpilogueCallback(EpilogueCallback); |
| 19600 | CHECK_EQ(0, prologue_call_count); |
| 19601 | CHECK_EQ(0, epilogue_call_count); |
| 19602 | CcTest::CollectAllGarbage(); |
| 19603 | CHECK_EQ(1, prologue_call_count); |
| 19604 | CHECK_EQ(1, epilogue_call_count); |
| 19605 | isolate->AddGCPrologueCallback(PrologueCallbackSecond); |
| 19606 | isolate->AddGCEpilogueCallback(EpilogueCallbackSecond); |
| 19607 | CcTest::CollectAllGarbage(); |
| 19608 | CHECK_EQ(2, prologue_call_count); |
| 19609 | CHECK_EQ(2, epilogue_call_count); |
| 19610 | CHECK_EQ(1, prologue_call_count_second); |
| 19611 | CHECK_EQ(1, epilogue_call_count_second); |
| 19612 | isolate->RemoveGCPrologueCallback(PrologueCallback); |
| 19613 | isolate->RemoveGCEpilogueCallback(EpilogueCallback); |
| 19614 | CcTest::CollectAllGarbage(); |
| 19615 | CHECK_EQ(2, prologue_call_count); |
| 19616 | CHECK_EQ(2, epilogue_call_count); |
| 19617 | CHECK_EQ(2, prologue_call_count_second); |
| 19618 | CHECK_EQ(2, epilogue_call_count_second); |
| 19619 | isolate->RemoveGCPrologueCallback(PrologueCallbackSecond); |
| 19620 | isolate->RemoveGCEpilogueCallback(EpilogueCallbackSecond); |
| 19621 | CcTest::CollectAllGarbage(); |
| 19622 | CHECK_EQ(2, prologue_call_count); |
| 19623 | CHECK_EQ(2, epilogue_call_count); |
| 19624 | CHECK_EQ(2, prologue_call_count_second); |
| 19625 | CHECK_EQ(2, epilogue_call_count_second); |
| 19626 | |
| 19627 | CHECK_EQ(0, prologue_call_count_alloc); |
| 19628 | CHECK_EQ(0, epilogue_call_count_alloc); |
| 19629 | isolate->AddGCPrologueCallback(PrologueCallbackAlloc); |
| 19630 | isolate->AddGCEpilogueCallback(EpilogueCallbackAlloc); |
| 19631 | CcTest::PreciseCollectAllGarbage(); |
| 19632 | CHECK_EQ(1, prologue_call_count_alloc); |
| 19633 | CHECK_EQ(1, epilogue_call_count_alloc); |
| 19634 | isolate->RemoveGCPrologueCallback(PrologueCallbackAlloc); |
| 19635 | isolate->RemoveGCEpilogueCallback(EpilogueCallbackAlloc); |
| 19636 | } |
| 19637 | |
| 19638 | |
| 19639 | THREADED_TEST(TwoByteStringInOneByteCons) { |
| 19640 | // See Chromium issue 47824. |
| 19641 | LocalContext context; |
| 19642 | v8::HandleScope scope(context->GetIsolate()); |
| 19643 | |
| 19644 | const char* init_code = |
| 19645 | "var str1 = 'abelspendabel';" |
| 19646 | "var str2 = str1 + str1 + str1;" |
| 19647 | "str2;" ; |
| 19648 | Local<Value> result = CompileRun(init_code); |
| 19649 | |
| 19650 | Local<Value> indexof = CompileRun("str2.indexOf('els')" ); |
| 19651 | Local<Value> lastindexof = CompileRun("str2.lastIndexOf('dab')" ); |
| 19652 | |
| 19653 | CHECK(result->IsString()); |
| 19654 | i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); |
| 19655 | int length = string->length(); |
| 19656 | CHECK(string->IsOneByteRepresentation()); |
| 19657 | |
| 19658 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate()); |
| 19659 | i::Handle<i::String> flat_string = i::String::Flatten(i_isolate, string); |
| 19660 | |
| 19661 | CHECK(string->IsOneByteRepresentation()); |
| 19662 | CHECK(flat_string->IsOneByteRepresentation()); |
| 19663 | |
| 19664 | // Create external resource. |
| 19665 | uint16_t* uc16_buffer = new uint16_t[length + 1]; |
| 19666 | |
| 19667 | i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); |
| 19668 | uc16_buffer[length] = 0; |
| 19669 | |
| 19670 | TestResource resource(uc16_buffer); |
| 19671 | |
| 19672 | flat_string->MakeExternal(&resource); |
| 19673 | |
| 19674 | CHECK(flat_string->IsTwoByteRepresentation()); |
| 19675 | |
| 19676 | // If the cons string has been short-circuited, skip the following checks. |
| 19677 | if (!string.is_identical_to(flat_string)) { |
| 19678 | // At this point, we should have a Cons string which is flat and one-byte, |
| 19679 | // with a first half that is a two-byte string (although it only contains |
| 19680 | // one-byte characters). This is a valid sequence of steps, and it can |
| 19681 | // happen in real pages. |
| 19682 | CHECK(string->IsOneByteRepresentation()); |
| 19683 | i::ConsString cons = i::ConsString::cast(*string); |
| 19684 | CHECK_EQ(0, cons->second()->length()); |
| 19685 | CHECK(cons->first()->IsTwoByteRepresentation()); |
| 19686 | } |
| 19687 | |
| 19688 | // Check that some string operations work. |
| 19689 | |
| 19690 | // Atom RegExp. |
| 19691 | Local<Value> reresult = CompileRun("str2.match(/abel/g).length;" ); |
| 19692 | CHECK_EQ(6, reresult->Int32Value(context.local()).FromJust()); |
| 19693 | |
| 19694 | // Nonatom RegExp. |
| 19695 | reresult = CompileRun("str2.match(/abe./g).length;" ); |
| 19696 | CHECK_EQ(6, reresult->Int32Value(context.local()).FromJust()); |
| 19697 | |
| 19698 | reresult = CompileRun("str2.search(/bel/g);" ); |
| 19699 | CHECK_EQ(1, reresult->Int32Value(context.local()).FromJust()); |
| 19700 | |
| 19701 | reresult = CompileRun("str2.search(/be./g);" ); |
| 19702 | CHECK_EQ(1, reresult->Int32Value(context.local()).FromJust()); |
| 19703 | |
| 19704 | ExpectTrue("/bel/g.test(str2);" ); |
| 19705 | |
| 19706 | ExpectTrue("/be./g.test(str2);" ); |
| 19707 | |
| 19708 | reresult = CompileRun("/bel/g.exec(str2);" ); |
| 19709 | CHECK(!reresult->IsNull()); |
| 19710 | |
| 19711 | reresult = CompileRun("/be./g.exec(str2);" ); |
| 19712 | CHECK(!reresult->IsNull()); |
| 19713 | |
| 19714 | ExpectString("str2.substring(2, 10);" , "elspenda" ); |
| 19715 | |
| 19716 | ExpectString("str2.substring(2, 20);" , "elspendabelabelspe" ); |
| 19717 | |
| 19718 | ExpectString("str2.charAt(2);" , "e" ); |
| 19719 | |
| 19720 | ExpectObject("str2.indexOf('els');" , indexof); |
| 19721 | |
| 19722 | ExpectObject("str2.lastIndexOf('dab');" , lastindexof); |
| 19723 | |
| 19724 | reresult = CompileRun("str2.charCodeAt(2);" ); |
| 19725 | CHECK_EQ(static_cast<int32_t>('e'), |
| 19726 | reresult->Int32Value(context.local()).FromJust()); |
| 19727 | // This avoids the GC from trying to free stack allocated resources. |
| 19728 | i::Handle<i::ExternalTwoByteString>::cast(flat_string) |
| 19729 | ->SetResource(i_isolate, nullptr); |
| 19730 | } |
| 19731 | |
| 19732 | |
| 19733 | TEST(ContainsOnlyOneByte) { |
| 19734 | v8::V8::Initialize(); |
| 19735 | v8::Isolate* isolate = CcTest::isolate(); |
| 19736 | v8::HandleScope scope(isolate); |
| 19737 | // Make a buffer long enough that it won't automatically be converted. |
| 19738 | const int length = 512; |
| 19739 | // Ensure word aligned assignment. |
| 19740 | const int aligned_length = length*sizeof(uintptr_t)/sizeof(uint16_t); |
| 19741 | std::unique_ptr<uintptr_t[]> aligned_contents(new uintptr_t[aligned_length]); |
| 19742 | uint16_t* string_contents = |
| 19743 | reinterpret_cast<uint16_t*>(aligned_contents.get()); |
| 19744 | // Set to contain only one byte. |
| 19745 | for (int i = 0; i < length-1; i++) { |
| 19746 | string_contents[i] = 0x41; |
| 19747 | } |
| 19748 | string_contents[length-1] = 0; |
| 19749 | // Simple case. |
| 19750 | Local<String> string = |
| 19751 | String::NewExternalTwoByte( |
| 19752 | isolate, new TestResource(string_contents, nullptr, false)) |
| 19753 | .ToLocalChecked(); |
| 19754 | CHECK(!string->IsOneByte() && string->ContainsOnlyOneByte()); |
| 19755 | // Counter example. |
| 19756 | string = String::NewFromTwoByte(isolate, string_contents, |
| 19757 | v8::NewStringType::kNormal) |
| 19758 | .ToLocalChecked(); |
| 19759 | CHECK(string->IsOneByte() && string->ContainsOnlyOneByte()); |
| 19760 | // Test left right and balanced cons strings. |
| 19761 | Local<String> base = v8_str("a" ); |
| 19762 | Local<String> left = base; |
| 19763 | Local<String> right = base; |
| 19764 | for (int i = 0; i < 1000; i++) { |
| 19765 | left = String::Concat(isolate, base, left); |
| 19766 | right = String::Concat(isolate, right, base); |
| 19767 | } |
| 19768 | Local<String> balanced = String::Concat(isolate, left, base); |
| 19769 | balanced = String::Concat(isolate, balanced, right); |
| 19770 | Local<String> cons_strings[] = {left, balanced, right}; |
| 19771 | Local<String> two_byte = |
| 19772 | String::NewExternalTwoByte( |
| 19773 | isolate, new TestResource(string_contents, nullptr, false)) |
| 19774 | .ToLocalChecked(); |
| 19775 | USE(two_byte); USE(cons_strings); |
| 19776 | for (size_t i = 0; i < arraysize(cons_strings); i++) { |
| 19777 | // Base assumptions. |
| 19778 | string = cons_strings[i]; |
| 19779 | CHECK(string->IsOneByte() && string->ContainsOnlyOneByte()); |
| 19780 | // Test left and right concatentation. |
| 19781 | string = String::Concat(isolate, two_byte, cons_strings[i]); |
| 19782 | CHECK(!string->IsOneByte() && string->ContainsOnlyOneByte()); |
| 19783 | string = String::Concat(isolate, cons_strings[i], two_byte); |
| 19784 | CHECK(!string->IsOneByte() && string->ContainsOnlyOneByte()); |
| 19785 | } |
| 19786 | // Set bits in different positions |
| 19787 | // for strings of different lengths and alignments. |
| 19788 | for (int alignment = 0; alignment < 7; alignment++) { |
| 19789 | for (int size = 2; alignment + size < length; size *= 2) { |
| 19790 | int zero_offset = size + alignment; |
| 19791 | string_contents[zero_offset] = 0; |
| 19792 | for (int i = 0; i < size; i++) { |
| 19793 | int shift = 8 + (i % 7); |
| 19794 | string_contents[alignment + i] = 1 << shift; |
| 19795 | string = String::NewExternalTwoByte( |
| 19796 | isolate, new TestResource(string_contents + alignment, |
| 19797 | nullptr, false)) |
| 19798 | .ToLocalChecked(); |
| 19799 | CHECK_EQ(size, string->Length()); |
| 19800 | CHECK(!string->ContainsOnlyOneByte()); |
| 19801 | string_contents[alignment + i] = 0x41; |
| 19802 | } |
| 19803 | string_contents[zero_offset] = 0x41; |
| 19804 | } |
| 19805 | } |
| 19806 | } |
| 19807 | |
| 19808 | |
| 19809 | // Failed access check callback that performs a GC on each invocation. |
| 19810 | void FailedAccessCheckCallbackGC(Local<v8::Object> target, |
| 19811 | v8::AccessType type, |
| 19812 | Local<v8::Value> data) { |
| 19813 | CcTest::CollectAllGarbage(); |
| 19814 | CcTest::isolate()->ThrowException( |
| 19815 | v8::Exception::Error(v8_str("cross context" ))); |
| 19816 | } |
| 19817 | |
| 19818 | |
| 19819 | TEST(GCInFailedAccessCheckCallback) { |
| 19820 | // Install a failed access check callback that performs a GC on each |
| 19821 | // invocation. Then force the callback to be called from va |
| 19822 | |
| 19823 | v8::V8::Initialize(); |
| 19824 | v8::Isolate* isolate = CcTest::isolate(); |
| 19825 | |
| 19826 | isolate->SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); |
| 19827 | |
| 19828 | v8::HandleScope scope(isolate); |
| 19829 | |
| 19830 | // Create an ObjectTemplate for global objects and install access |
| 19831 | // check callbacks that will block access. |
| 19832 | v8::Local<v8::ObjectTemplate> global_template = |
| 19833 | v8::ObjectTemplate::New(isolate); |
| 19834 | global_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 19835 | |
| 19836 | // Create a context and set an x property on it's global object. |
| 19837 | LocalContext context0(nullptr, global_template); |
| 19838 | CHECK(context0->Global() |
| 19839 | ->Set(context0.local(), v8_str("x" ), v8_num(42)) |
| 19840 | .FromJust()); |
| 19841 | v8::Local<v8::Object> global0 = context0->Global(); |
| 19842 | |
| 19843 | // Create a context with a different security token so that the |
| 19844 | // failed access check callback will be called on each access. |
| 19845 | LocalContext context1(nullptr, global_template); |
| 19846 | CHECK(context1->Global() |
| 19847 | ->Set(context1.local(), v8_str("other" ), global0) |
| 19848 | .FromJust()); |
| 19849 | |
| 19850 | v8::TryCatch try_catch(isolate); |
| 19851 | |
| 19852 | // Get property with failed access check. |
| 19853 | CHECK(CompileRun("other.x" ).IsEmpty()); |
| 19854 | CHECK(try_catch.HasCaught()); |
| 19855 | try_catch.Reset(); |
| 19856 | |
| 19857 | // Get element with failed access check. |
| 19858 | CHECK(CompileRun("other[0]" ).IsEmpty()); |
| 19859 | CHECK(try_catch.HasCaught()); |
| 19860 | try_catch.Reset(); |
| 19861 | |
| 19862 | // Set property with failed access check. |
| 19863 | CHECK(CompileRun("other.x = new Object()" ).IsEmpty()); |
| 19864 | CHECK(try_catch.HasCaught()); |
| 19865 | try_catch.Reset(); |
| 19866 | |
| 19867 | // Set element with failed access check. |
| 19868 | CHECK(CompileRun("other[0] = new Object()" ).IsEmpty()); |
| 19869 | CHECK(try_catch.HasCaught()); |
| 19870 | try_catch.Reset(); |
| 19871 | |
| 19872 | // Get property attribute with failed access check. |
| 19873 | CHECK(CompileRun("\'x\' in other" ).IsEmpty()); |
| 19874 | CHECK(try_catch.HasCaught()); |
| 19875 | try_catch.Reset(); |
| 19876 | |
| 19877 | // Get property attribute for element with failed access check. |
| 19878 | CHECK(CompileRun("0 in other" ).IsEmpty()); |
| 19879 | CHECK(try_catch.HasCaught()); |
| 19880 | try_catch.Reset(); |
| 19881 | |
| 19882 | // Delete property. |
| 19883 | CHECK(CompileRun("delete other.x" ).IsEmpty()); |
| 19884 | CHECK(try_catch.HasCaught()); |
| 19885 | try_catch.Reset(); |
| 19886 | |
| 19887 | // Delete element. |
| 19888 | CHECK(global0->Delete(context1.local(), 0).IsNothing()); |
| 19889 | CHECK(try_catch.HasCaught()); |
| 19890 | try_catch.Reset(); |
| 19891 | |
| 19892 | // DefineAccessor. |
| 19893 | CHECK(global0 |
| 19894 | ->SetAccessor(context1.local(), v8_str("x" ), GetXValue, nullptr, |
| 19895 | v8_str("x" )) |
| 19896 | .IsNothing()); |
| 19897 | CHECK(try_catch.HasCaught()); |
| 19898 | try_catch.Reset(); |
| 19899 | |
| 19900 | // Define JavaScript accessor. |
| 19901 | CHECK(CompileRun( |
| 19902 | "Object.prototype.__defineGetter__.call(" |
| 19903 | " other, \'x\', function() { return 42; })" ).IsEmpty()); |
| 19904 | CHECK(try_catch.HasCaught()); |
| 19905 | try_catch.Reset(); |
| 19906 | |
| 19907 | // LookupAccessor. |
| 19908 | CHECK(CompileRun( |
| 19909 | "Object.prototype.__lookupGetter__.call(" |
| 19910 | " other, \'x\')" ).IsEmpty()); |
| 19911 | CHECK(try_catch.HasCaught()); |
| 19912 | try_catch.Reset(); |
| 19913 | |
| 19914 | // HasOwnElement. |
| 19915 | CHECK(CompileRun( |
| 19916 | "Object.prototype.hasOwnProperty.call(" |
| 19917 | "other, \'0\')" ).IsEmpty()); |
| 19918 | CHECK(try_catch.HasCaught()); |
| 19919 | try_catch.Reset(); |
| 19920 | |
| 19921 | CHECK(global0->HasRealIndexedProperty(context1.local(), 0).IsNothing()); |
| 19922 | CHECK(try_catch.HasCaught()); |
| 19923 | try_catch.Reset(); |
| 19924 | |
| 19925 | CHECK( |
| 19926 | global0->HasRealNamedProperty(context1.local(), v8_str("x" )).IsNothing()); |
| 19927 | CHECK(try_catch.HasCaught()); |
| 19928 | try_catch.Reset(); |
| 19929 | |
| 19930 | CHECK(global0->HasRealNamedCallbackProperty(context1.local(), v8_str("x" )) |
| 19931 | .IsNothing()); |
| 19932 | CHECK(try_catch.HasCaught()); |
| 19933 | try_catch.Reset(); |
| 19934 | |
| 19935 | // Reset the failed access check callback so it does not influence |
| 19936 | // the other tests. |
| 19937 | isolate->SetFailedAccessCheckCallbackFunction(nullptr); |
| 19938 | } |
| 19939 | |
| 19940 | |
| 19941 | TEST(IsolateNewDispose) { |
| 19942 | v8::Isolate* current_isolate = CcTest::isolate(); |
| 19943 | v8::Isolate::CreateParams create_params; |
| 19944 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 19945 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 19946 | CHECK_NOT_NULL(isolate); |
| 19947 | CHECK(current_isolate != isolate); |
| 19948 | CHECK(current_isolate == CcTest::isolate()); |
| 19949 | CHECK(isolate->GetArrayBufferAllocator() == CcTest::array_buffer_allocator()); |
| 19950 | |
| 19951 | isolate->SetFatalErrorHandler(StoringErrorCallback); |
| 19952 | last_location = last_message = nullptr; |
| 19953 | isolate->Dispose(); |
| 19954 | CHECK(!last_location); |
| 19955 | CHECK(!last_message); |
| 19956 | } |
| 19957 | |
| 19958 | |
| 19959 | UNINITIALIZED_TEST(DisposeIsolateWhenInUse) { |
| 19960 | v8::Isolate::CreateParams create_params; |
| 19961 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 19962 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 19963 | { |
| 19964 | v8::Isolate::Scope i_scope(isolate); |
| 19965 | v8::HandleScope scope(isolate); |
| 19966 | LocalContext context(isolate); |
| 19967 | // Run something in this isolate. |
| 19968 | ExpectTrue("true" ); |
| 19969 | isolate->SetFatalErrorHandler(StoringErrorCallback); |
| 19970 | last_location = last_message = nullptr; |
| 19971 | // Still entered, should fail. |
| 19972 | isolate->Dispose(); |
| 19973 | CHECK(last_location); |
| 19974 | CHECK(last_message); |
| 19975 | } |
| 19976 | isolate->Dispose(); |
| 19977 | } |
| 19978 | |
| 19979 | |
| 19980 | static void BreakArrayGuarantees(const char* script) { |
| 19981 | v8::Isolate::CreateParams create_params; |
| 19982 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 19983 | v8::Isolate* isolate1 = v8::Isolate::New(create_params); |
| 19984 | isolate1->Enter(); |
| 19985 | v8::Persistent<v8::Context> context1; |
| 19986 | { |
| 19987 | v8::HandleScope scope(isolate1); |
| 19988 | context1.Reset(isolate1, Context::New(isolate1)); |
| 19989 | } |
| 19990 | |
| 19991 | { |
| 19992 | v8::HandleScope scope(isolate1); |
| 19993 | v8::Local<v8::Context> context = |
| 19994 | v8::Local<v8::Context>::New(isolate1, context1); |
| 19995 | v8::Context::Scope context_scope(context); |
| 19996 | v8::internal::Isolate* i_isolate = |
| 19997 | reinterpret_cast<v8::internal::Isolate*>(isolate1); |
| 19998 | CHECK(i_isolate->IsNoElementsProtectorIntact()); |
| 19999 | // Run something in new isolate. |
| 20000 | CompileRun(script); |
| 20001 | CHECK(!i_isolate->IsNoElementsProtectorIntact()); |
| 20002 | } |
| 20003 | isolate1->Exit(); |
| 20004 | isolate1->Dispose(); |
| 20005 | } |
| 20006 | |
| 20007 | |
| 20008 | TEST(VerifyArrayPrototypeGuarantees) { |
| 20009 | // Break fast array hole handling by element changes. |
| 20010 | BreakArrayGuarantees("[].__proto__[1] = 3;" ); |
| 20011 | BreakArrayGuarantees("Object.prototype[3] = 'three';" ); |
| 20012 | BreakArrayGuarantees("Array.prototype.push(1);" ); |
| 20013 | BreakArrayGuarantees("Array.prototype.unshift(1);" ); |
| 20014 | // Break fast array hole handling by changing length. |
| 20015 | BreakArrayGuarantees("Array.prototype.length = 30;" ); |
| 20016 | // Break fast array hole handling by prototype structure changes. |
| 20017 | BreakArrayGuarantees("[].__proto__.__proto__ = { funny: true };" ); |
| 20018 | // By sending elements to dictionary mode. |
| 20019 | BreakArrayGuarantees( |
| 20020 | "Object.defineProperty(Array.prototype, 0, {" |
| 20021 | " get: function() { return 3; }});" ); |
| 20022 | BreakArrayGuarantees( |
| 20023 | "Object.defineProperty(Object.prototype, 0, {" |
| 20024 | " get: function() { return 3; }});" ); |
| 20025 | } |
| 20026 | |
| 20027 | |
| 20028 | TEST(RunTwoIsolatesOnSingleThread) { |
| 20029 | // Run isolate 1. |
| 20030 | v8::Isolate::CreateParams create_params; |
| 20031 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 20032 | v8::Isolate* isolate1 = v8::Isolate::New(create_params); |
| 20033 | isolate1->Enter(); |
| 20034 | v8::Persistent<v8::Context> context1; |
| 20035 | { |
| 20036 | v8::HandleScope scope(isolate1); |
| 20037 | context1.Reset(isolate1, Context::New(isolate1)); |
| 20038 | } |
| 20039 | |
| 20040 | { |
| 20041 | v8::HandleScope scope(isolate1); |
| 20042 | v8::Local<v8::Context> context = |
| 20043 | v8::Local<v8::Context>::New(isolate1, context1); |
| 20044 | v8::Context::Scope context_scope(context); |
| 20045 | // Run something in new isolate. |
| 20046 | CompileRun("var foo = 'isolate 1';" ); |
| 20047 | ExpectString("function f() { return foo; }; f()" , "isolate 1" ); |
| 20048 | } |
| 20049 | |
| 20050 | // Run isolate 2. |
| 20051 | v8::Isolate* isolate2 = v8::Isolate::New(create_params); |
| 20052 | v8::Persistent<v8::Context> context2; |
| 20053 | |
| 20054 | { |
| 20055 | v8::Isolate::Scope iscope(isolate2); |
| 20056 | v8::HandleScope scope(isolate2); |
| 20057 | context2.Reset(isolate2, Context::New(isolate2)); |
| 20058 | v8::Local<v8::Context> context = |
| 20059 | v8::Local<v8::Context>::New(isolate2, context2); |
| 20060 | v8::Context::Scope context_scope(context); |
| 20061 | |
| 20062 | // Run something in new isolate. |
| 20063 | CompileRun("var foo = 'isolate 2';" ); |
| 20064 | ExpectString("function f() { return foo; }; f()" , "isolate 2" ); |
| 20065 | } |
| 20066 | |
| 20067 | { |
| 20068 | v8::HandleScope scope(isolate1); |
| 20069 | v8::Local<v8::Context> context = |
| 20070 | v8::Local<v8::Context>::New(isolate1, context1); |
| 20071 | v8::Context::Scope context_scope(context); |
| 20072 | // Now again in isolate 1 |
| 20073 | ExpectString("function f() { return foo; }; f()" , "isolate 1" ); |
| 20074 | } |
| 20075 | |
| 20076 | isolate1->Exit(); |
| 20077 | |
| 20078 | // Run some stuff in default isolate. |
| 20079 | v8::Persistent<v8::Context> context_default; |
| 20080 | { |
| 20081 | v8::Isolate* isolate = CcTest::isolate(); |
| 20082 | v8::Isolate::Scope iscope(isolate); |
| 20083 | v8::HandleScope scope(isolate); |
| 20084 | context_default.Reset(isolate, Context::New(isolate)); |
| 20085 | } |
| 20086 | |
| 20087 | { |
| 20088 | v8::HandleScope scope(CcTest::isolate()); |
| 20089 | v8::Local<v8::Context> context = |
| 20090 | v8::Local<v8::Context>::New(CcTest::isolate(), context_default); |
| 20091 | v8::Context::Scope context_scope(context); |
| 20092 | // Variables in other isolates should be not available, verify there |
| 20093 | // is an exception. |
| 20094 | ExpectTrue("function f() {" |
| 20095 | " try {" |
| 20096 | " foo;" |
| 20097 | " return false;" |
| 20098 | " } catch(e) {" |
| 20099 | " return true;" |
| 20100 | " }" |
| 20101 | "};" |
| 20102 | "var isDefaultIsolate = true;" |
| 20103 | "f()" ); |
| 20104 | } |
| 20105 | |
| 20106 | isolate1->Enter(); |
| 20107 | |
| 20108 | { |
| 20109 | v8::Isolate::Scope iscope(isolate2); |
| 20110 | v8::HandleScope scope(isolate2); |
| 20111 | v8::Local<v8::Context> context = |
| 20112 | v8::Local<v8::Context>::New(isolate2, context2); |
| 20113 | v8::Context::Scope context_scope(context); |
| 20114 | ExpectString("function f() { return foo; }; f()" , "isolate 2" ); |
| 20115 | } |
| 20116 | |
| 20117 | { |
| 20118 | v8::HandleScope scope(v8::Isolate::GetCurrent()); |
| 20119 | v8::Local<v8::Context> context = |
| 20120 | v8::Local<v8::Context>::New(v8::Isolate::GetCurrent(), context1); |
| 20121 | v8::Context::Scope context_scope(context); |
| 20122 | ExpectString("function f() { return foo; }; f()" , "isolate 1" ); |
| 20123 | } |
| 20124 | |
| 20125 | { |
| 20126 | v8::Isolate::Scope iscope(isolate2); |
| 20127 | context2.Reset(); |
| 20128 | } |
| 20129 | |
| 20130 | context1.Reset(); |
| 20131 | isolate1->Exit(); |
| 20132 | |
| 20133 | isolate2->SetFatalErrorHandler(StoringErrorCallback); |
| 20134 | last_location = last_message = nullptr; |
| 20135 | |
| 20136 | isolate1->Dispose(); |
| 20137 | CHECK(!last_location); |
| 20138 | CHECK(!last_message); |
| 20139 | |
| 20140 | isolate2->Dispose(); |
| 20141 | CHECK(!last_location); |
| 20142 | CHECK(!last_message); |
| 20143 | |
| 20144 | // Check that default isolate still runs. |
| 20145 | { |
| 20146 | v8::HandleScope scope(CcTest::isolate()); |
| 20147 | v8::Local<v8::Context> context = |
| 20148 | v8::Local<v8::Context>::New(CcTest::isolate(), context_default); |
| 20149 | v8::Context::Scope context_scope(context); |
| 20150 | ExpectTrue("function f() { return isDefaultIsolate; }; f()" ); |
| 20151 | } |
| 20152 | } |
| 20153 | |
| 20154 | |
| 20155 | static int CalcFibonacci(v8::Isolate* isolate, int limit) { |
| 20156 | v8::Isolate::Scope isolate_scope(isolate); |
| 20157 | v8::HandleScope scope(isolate); |
| 20158 | LocalContext context(isolate); |
| 20159 | i::ScopedVector<char> code(1024); |
| 20160 | i::SNPrintF(code, "function fib(n) {" |
| 20161 | " if (n <= 2) return 1;" |
| 20162 | " return fib(n-1) + fib(n-2);" |
| 20163 | "}" |
| 20164 | "fib(%d)" , limit); |
| 20165 | Local<Value> value = CompileRun(code.start()); |
| 20166 | CHECK(value->IsNumber()); |
| 20167 | return static_cast<int>(value->NumberValue(context.local()).FromJust()); |
| 20168 | } |
| 20169 | |
| 20170 | class IsolateThread : public v8::base::Thread { |
| 20171 | public: |
| 20172 | explicit IsolateThread(int fib_limit) |
| 20173 | : Thread(Options("IsolateThread" )), fib_limit_(fib_limit), result_(0) {} |
| 20174 | |
| 20175 | void Run() override { |
| 20176 | v8::Isolate::CreateParams create_params; |
| 20177 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 20178 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 20179 | result_ = CalcFibonacci(isolate, fib_limit_); |
| 20180 | isolate->Dispose(); |
| 20181 | } |
| 20182 | |
| 20183 | int result() { return result_; } |
| 20184 | |
| 20185 | private: |
| 20186 | int fib_limit_; |
| 20187 | int result_; |
| 20188 | }; |
| 20189 | |
| 20190 | |
| 20191 | TEST(MultipleIsolatesOnIndividualThreads) { |
| 20192 | IsolateThread thread1(21); |
| 20193 | IsolateThread thread2(12); |
| 20194 | |
| 20195 | // Compute some fibonacci numbers on 3 threads in 3 isolates. |
| 20196 | thread1.Start(); |
| 20197 | thread2.Start(); |
| 20198 | |
| 20199 | int result1 = CalcFibonacci(CcTest::isolate(), 21); |
| 20200 | int result2 = CalcFibonacci(CcTest::isolate(), 12); |
| 20201 | |
| 20202 | thread1.Join(); |
| 20203 | thread2.Join(); |
| 20204 | |
| 20205 | // Compare results. The actual fibonacci numbers for 12 and 21 are taken |
| 20206 | // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number |
| 20207 | CHECK_EQ(result1, 10946); |
| 20208 | CHECK_EQ(result2, 144); |
| 20209 | CHECK_EQ(result1, thread1.result()); |
| 20210 | CHECK_EQ(result2, thread2.result()); |
| 20211 | } |
| 20212 | |
| 20213 | |
| 20214 | TEST(IsolateDifferentContexts) { |
| 20215 | v8::Isolate::CreateParams create_params; |
| 20216 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 20217 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 20218 | Local<v8::Context> context; |
| 20219 | { |
| 20220 | v8::Isolate::Scope isolate_scope(isolate); |
| 20221 | v8::HandleScope handle_scope(isolate); |
| 20222 | context = v8::Context::New(isolate); |
| 20223 | v8::Context::Scope context_scope(context); |
| 20224 | Local<Value> v = CompileRun("2" ); |
| 20225 | CHECK(v->IsNumber()); |
| 20226 | CHECK_EQ(2, static_cast<int>(v->NumberValue(context).FromJust())); |
| 20227 | } |
| 20228 | { |
| 20229 | v8::Isolate::Scope isolate_scope(isolate); |
| 20230 | v8::HandleScope handle_scope(isolate); |
| 20231 | context = v8::Context::New(isolate); |
| 20232 | v8::Context::Scope context_scope(context); |
| 20233 | Local<Value> v = CompileRun("22" ); |
| 20234 | CHECK(v->IsNumber()); |
| 20235 | CHECK_EQ(22, static_cast<int>(v->NumberValue(context).FromJust())); |
| 20236 | } |
| 20237 | isolate->Dispose(); |
| 20238 | } |
| 20239 | |
| 20240 | class InitDefaultIsolateThread : public v8::base::Thread { |
| 20241 | public: |
| 20242 | enum TestCase { |
| 20243 | SetFatalHandler, |
| 20244 | SetCounterFunction, |
| 20245 | SetCreateHistogramFunction, |
| 20246 | SetAddHistogramSampleFunction |
| 20247 | }; |
| 20248 | |
| 20249 | explicit InitDefaultIsolateThread(TestCase testCase) |
| 20250 | : Thread(Options("InitDefaultIsolateThread" )), |
| 20251 | testCase_(testCase), |
| 20252 | result_(false) {} |
| 20253 | |
| 20254 | void Run() override { |
| 20255 | v8::Isolate::CreateParams create_params; |
| 20256 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 20257 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 20258 | isolate->Enter(); |
| 20259 | switch (testCase_) { |
| 20260 | case SetFatalHandler: |
| 20261 | isolate->SetFatalErrorHandler(nullptr); |
| 20262 | break; |
| 20263 | |
| 20264 | case SetCounterFunction: |
| 20265 | CcTest::isolate()->SetCounterFunction(nullptr); |
| 20266 | break; |
| 20267 | |
| 20268 | case SetCreateHistogramFunction: |
| 20269 | CcTest::isolate()->SetCreateHistogramFunction(nullptr); |
| 20270 | break; |
| 20271 | |
| 20272 | case SetAddHistogramSampleFunction: |
| 20273 | CcTest::isolate()->SetAddHistogramSampleFunction(nullptr); |
| 20274 | break; |
| 20275 | } |
| 20276 | isolate->Exit(); |
| 20277 | isolate->Dispose(); |
| 20278 | result_ = true; |
| 20279 | } |
| 20280 | |
| 20281 | bool result() { return result_; } |
| 20282 | |
| 20283 | private: |
| 20284 | TestCase testCase_; |
| 20285 | bool result_; |
| 20286 | }; |
| 20287 | |
| 20288 | |
| 20289 | static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) { |
| 20290 | InitDefaultIsolateThread thread(testCase); |
| 20291 | thread.Start(); |
| 20292 | thread.Join(); |
| 20293 | CHECK(thread.result()); |
| 20294 | } |
| 20295 | |
| 20296 | TEST(InitializeDefaultIsolateOnSecondaryThread_FatalHandler) { |
| 20297 | InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler); |
| 20298 | } |
| 20299 | |
| 20300 | TEST(InitializeDefaultIsolateOnSecondaryThread_CounterFunction) { |
| 20301 | InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction); |
| 20302 | } |
| 20303 | |
| 20304 | TEST(InitializeDefaultIsolateOnSecondaryThread_CreateHistogramFunction) { |
| 20305 | InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction); |
| 20306 | } |
| 20307 | |
| 20308 | TEST(InitializeDefaultIsolateOnSecondaryThread_AddHistogramSampleFunction) { |
| 20309 | InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction); |
| 20310 | } |
| 20311 | |
| 20312 | |
| 20313 | TEST(StringCheckMultipleContexts) { |
| 20314 | const char* code = |
| 20315 | "(function() { return \"a\".charAt(0); })()" ; |
| 20316 | |
| 20317 | { |
| 20318 | // Run the code twice in the first context to initialize the call IC. |
| 20319 | LocalContext context1; |
| 20320 | v8::HandleScope scope(context1->GetIsolate()); |
| 20321 | ExpectString(code, "a" ); |
| 20322 | ExpectString(code, "a" ); |
| 20323 | } |
| 20324 | |
| 20325 | { |
| 20326 | // Change the String.prototype in the second context and check |
| 20327 | // that the right function gets called. |
| 20328 | LocalContext context2; |
| 20329 | v8::HandleScope scope(context2->GetIsolate()); |
| 20330 | CompileRun("String.prototype.charAt = function() { return \"not a\"; }" ); |
| 20331 | ExpectString(code, "not a" ); |
| 20332 | } |
| 20333 | } |
| 20334 | |
| 20335 | |
| 20336 | TEST(NumberCheckMultipleContexts) { |
| 20337 | const char* code = |
| 20338 | "(function() { return (42).toString(); })()" ; |
| 20339 | |
| 20340 | { |
| 20341 | // Run the code twice in the first context to initialize the call IC. |
| 20342 | LocalContext context1; |
| 20343 | v8::HandleScope scope(context1->GetIsolate()); |
| 20344 | ExpectString(code, "42" ); |
| 20345 | ExpectString(code, "42" ); |
| 20346 | } |
| 20347 | |
| 20348 | { |
| 20349 | // Change the Number.prototype in the second context and check |
| 20350 | // that the right function gets called. |
| 20351 | LocalContext context2; |
| 20352 | v8::HandleScope scope(context2->GetIsolate()); |
| 20353 | CompileRun("Number.prototype.toString = function() { return \"not 42\"; }" ); |
| 20354 | ExpectString(code, "not 42" ); |
| 20355 | } |
| 20356 | } |
| 20357 | |
| 20358 | |
| 20359 | TEST(BooleanCheckMultipleContexts) { |
| 20360 | const char* code = |
| 20361 | "(function() { return true.toString(); })()" ; |
| 20362 | |
| 20363 | { |
| 20364 | // Run the code twice in the first context to initialize the call IC. |
| 20365 | LocalContext context1; |
| 20366 | v8::HandleScope scope(context1->GetIsolate()); |
| 20367 | ExpectString(code, "true" ); |
| 20368 | ExpectString(code, "true" ); |
| 20369 | } |
| 20370 | |
| 20371 | { |
| 20372 | // Change the Boolean.prototype in the second context and check |
| 20373 | // that the right function gets called. |
| 20374 | LocalContext context2; |
| 20375 | v8::HandleScope scope(context2->GetIsolate()); |
| 20376 | CompileRun("Boolean.prototype.toString = function() { return \"\"; }" ); |
| 20377 | ExpectString(code, "" ); |
| 20378 | } |
| 20379 | } |
| 20380 | |
| 20381 | |
| 20382 | TEST(DontDeleteCellLoadIC) { |
| 20383 | const char* function_code = |
| 20384 | "function readCell() { while (true) { return cell; } }" ; |
| 20385 | |
| 20386 | { |
| 20387 | // Run the code twice in the first context to initialize the load |
| 20388 | // IC for a don't delete cell. |
| 20389 | LocalContext context1; |
| 20390 | v8::HandleScope scope(context1->GetIsolate()); |
| 20391 | CompileRun("var cell = \"first\";" ); |
| 20392 | ExpectBoolean("delete cell" , false); |
| 20393 | CompileRun(function_code); |
| 20394 | ExpectString("readCell()" , "first" ); |
| 20395 | ExpectString("readCell()" , "first" ); |
| 20396 | } |
| 20397 | |
| 20398 | { |
| 20399 | // Use a deletable cell in the second context. |
| 20400 | LocalContext context2; |
| 20401 | v8::HandleScope scope(context2->GetIsolate()); |
| 20402 | CompileRun("cell = \"second\";" ); |
| 20403 | CompileRun(function_code); |
| 20404 | ExpectString("readCell()" , "second" ); |
| 20405 | ExpectBoolean("delete cell" , true); |
| 20406 | ExpectString("(function() {" |
| 20407 | " try {" |
| 20408 | " return readCell();" |
| 20409 | " } catch(e) {" |
| 20410 | " return e.toString();" |
| 20411 | " }" |
| 20412 | "})()" , |
| 20413 | "ReferenceError: cell is not defined" ); |
| 20414 | CompileRun("cell = \"new_second\";" ); |
| 20415 | CcTest::CollectAllGarbage(); |
| 20416 | ExpectString("readCell()" , "new_second" ); |
| 20417 | ExpectString("readCell()" , "new_second" ); |
| 20418 | } |
| 20419 | } |
| 20420 | |
| 20421 | |
| 20422 | class Visitor42 : public v8::PersistentHandleVisitor { |
| 20423 | public: |
| 20424 | explicit Visitor42(v8::Persistent<v8::Object>* object) |
| 20425 | : counter_(0), object_(object) { } |
| 20426 | |
| 20427 | void VisitPersistentHandle(Persistent<Value>* value, |
| 20428 | uint16_t class_id) override { |
| 20429 | if (class_id != 42) return; |
| 20430 | CHECK_EQ(42, value->WrapperClassId()); |
| 20431 | v8::Isolate* isolate = CcTest::isolate(); |
| 20432 | v8::HandleScope handle_scope(isolate); |
| 20433 | v8::Local<v8::Value> handle = v8::Local<v8::Value>::New(isolate, *value); |
| 20434 | v8::Local<v8::Value> object = v8::Local<v8::Object>::New(isolate, *object_); |
| 20435 | CHECK(handle->IsObject()); |
| 20436 | CHECK(Local<Object>::Cast(handle) |
| 20437 | ->Equals(isolate->GetCurrentContext(), object) |
| 20438 | .FromJust()); |
| 20439 | ++counter_; |
| 20440 | } |
| 20441 | |
| 20442 | int counter_; |
| 20443 | v8::Persistent<v8::Object>* object_; |
| 20444 | }; |
| 20445 | |
| 20446 | |
| 20447 | TEST(PersistentHandleVisitor) { |
| 20448 | LocalContext context; |
| 20449 | v8::Isolate* isolate = context->GetIsolate(); |
| 20450 | v8::HandleScope scope(isolate); |
| 20451 | v8::Persistent<v8::Object> object(isolate, v8::Object::New(isolate)); |
| 20452 | CHECK_EQ(0, object.WrapperClassId()); |
| 20453 | object.SetWrapperClassId(42); |
| 20454 | CHECK_EQ(42, object.WrapperClassId()); |
| 20455 | |
| 20456 | Visitor42 visitor(&object); |
| 20457 | isolate->VisitHandlesWithClassIds(&visitor); |
| 20458 | CHECK_EQ(1, visitor.counter_); |
| 20459 | |
| 20460 | object.Reset(); |
| 20461 | } |
| 20462 | |
| 20463 | |
| 20464 | TEST(WrapperClassId) { |
| 20465 | LocalContext context; |
| 20466 | v8::Isolate* isolate = context->GetIsolate(); |
| 20467 | v8::HandleScope scope(isolate); |
| 20468 | v8::Persistent<v8::Object> object(isolate, v8::Object::New(isolate)); |
| 20469 | CHECK_EQ(0, object.WrapperClassId()); |
| 20470 | object.SetWrapperClassId(65535); |
| 20471 | CHECK_EQ(65535, object.WrapperClassId()); |
| 20472 | object.Reset(); |
| 20473 | } |
| 20474 | |
| 20475 | TEST(RegExp) { |
| 20476 | LocalContext context; |
| 20477 | v8::HandleScope scope(context->GetIsolate()); |
| 20478 | |
| 20479 | v8::Local<v8::RegExp> re = |
| 20480 | v8::RegExp::New(context.local(), v8_str("foo" ), v8::RegExp::kNone) |
| 20481 | .ToLocalChecked(); |
| 20482 | CHECK(re->IsRegExp()); |
| 20483 | CHECK(re->GetSource()->Equals(context.local(), v8_str("foo" )).FromJust()); |
| 20484 | CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); |
| 20485 | |
| 20486 | re = v8::RegExp::New(context.local(), v8_str("bar" ), |
| 20487 | static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | |
| 20488 | v8::RegExp::kGlobal)) |
| 20489 | .ToLocalChecked(); |
| 20490 | CHECK(re->IsRegExp()); |
| 20491 | CHECK(re->GetSource()->Equals(context.local(), v8_str("bar" )).FromJust()); |
| 20492 | CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal, |
| 20493 | static_cast<int>(re->GetFlags())); |
| 20494 | |
| 20495 | re = v8::RegExp::New(context.local(), v8_str("baz" ), |
| 20496 | static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | |
| 20497 | v8::RegExp::kMultiline)) |
| 20498 | .ToLocalChecked(); |
| 20499 | CHECK(re->IsRegExp()); |
| 20500 | CHECK(re->GetSource()->Equals(context.local(), v8_str("baz" )).FromJust()); |
| 20501 | CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, |
| 20502 | static_cast<int>(re->GetFlags())); |
| 20503 | |
| 20504 | re = v8::RegExp::New(context.local(), v8_str("baz" ), |
| 20505 | static_cast<v8::RegExp::Flags>(v8::RegExp::kUnicode | |
| 20506 | v8::RegExp::kSticky)) |
| 20507 | .ToLocalChecked(); |
| 20508 | CHECK(re->IsRegExp()); |
| 20509 | CHECK(re->GetSource()->Equals(context.local(), v8_str("baz" )).FromJust()); |
| 20510 | CHECK_EQ(v8::RegExp::kUnicode | v8::RegExp::kSticky, |
| 20511 | static_cast<int>(re->GetFlags())); |
| 20512 | |
| 20513 | re = CompileRun("/quux/" ).As<v8::RegExp>(); |
| 20514 | CHECK(re->IsRegExp()); |
| 20515 | CHECK(re->GetSource()->Equals(context.local(), v8_str("quux" )).FromJust()); |
| 20516 | CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); |
| 20517 | |
| 20518 | re = CompileRun("/quux/gm" ).As<v8::RegExp>(); |
| 20519 | CHECK(re->IsRegExp()); |
| 20520 | CHECK(re->GetSource()->Equals(context.local(), v8_str("quux" )).FromJust()); |
| 20521 | CHECK_EQ(v8::RegExp::kGlobal | v8::RegExp::kMultiline, |
| 20522 | static_cast<int>(re->GetFlags())); |
| 20523 | |
| 20524 | // Override the RegExp constructor and check the API constructor |
| 20525 | // still works. |
| 20526 | CompileRun("RegExp = function() {}" ); |
| 20527 | |
| 20528 | re = v8::RegExp::New(context.local(), v8_str("foobar" ), v8::RegExp::kNone) |
| 20529 | .ToLocalChecked(); |
| 20530 | CHECK(re->IsRegExp()); |
| 20531 | CHECK(re->GetSource()->Equals(context.local(), v8_str("foobar" )).FromJust()); |
| 20532 | CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); |
| 20533 | |
| 20534 | re = v8::RegExp::New(context.local(), v8_str("foobarbaz" ), |
| 20535 | static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | |
| 20536 | v8::RegExp::kMultiline)) |
| 20537 | .ToLocalChecked(); |
| 20538 | CHECK(re->IsRegExp()); |
| 20539 | CHECK( |
| 20540 | re->GetSource()->Equals(context.local(), v8_str("foobarbaz" )).FromJust()); |
| 20541 | CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, |
| 20542 | static_cast<int>(re->GetFlags())); |
| 20543 | |
| 20544 | CHECK(context->Global()->Set(context.local(), v8_str("re" ), re).FromJust()); |
| 20545 | ExpectTrue("re.test('FoobarbaZ')" ); |
| 20546 | |
| 20547 | // RegExps are objects on which you can set properties. |
| 20548 | re->Set(context.local(), v8_str("property" ), |
| 20549 | v8::Integer::New(context->GetIsolate(), 32)) |
| 20550 | .FromJust(); |
| 20551 | v8::Local<v8::Value> value(CompileRun("re.property" )); |
| 20552 | CHECK_EQ(32, value->Int32Value(context.local()).FromJust()); |
| 20553 | |
| 20554 | v8::TryCatch try_catch(context->GetIsolate()); |
| 20555 | CHECK(v8::RegExp::New(context.local(), v8_str("foo[" ), v8::RegExp::kNone) |
| 20556 | .IsEmpty()); |
| 20557 | CHECK(try_catch.HasCaught()); |
| 20558 | CHECK(context->Global() |
| 20559 | ->Set(context.local(), v8_str("ex" ), try_catch.Exception()) |
| 20560 | .FromJust()); |
| 20561 | ExpectTrue("ex instanceof SyntaxError" ); |
| 20562 | } |
| 20563 | |
| 20564 | |
| 20565 | THREADED_TEST(Equals) { |
| 20566 | LocalContext localContext; |
| 20567 | v8::HandleScope handleScope(localContext->GetIsolate()); |
| 20568 | |
| 20569 | v8::Local<v8::Object> globalProxy = localContext->Global(); |
| 20570 | v8::Local<Value> global = globalProxy->GetPrototype(); |
| 20571 | |
| 20572 | CHECK(global->StrictEquals(global)); |
| 20573 | CHECK(!global->StrictEquals(globalProxy)); |
| 20574 | CHECK(!globalProxy->StrictEquals(global)); |
| 20575 | CHECK(globalProxy->StrictEquals(globalProxy)); |
| 20576 | |
| 20577 | CHECK(global->Equals(localContext.local(), global).FromJust()); |
| 20578 | CHECK(!global->Equals(localContext.local(), globalProxy).FromJust()); |
| 20579 | CHECK(!globalProxy->Equals(localContext.local(), global).FromJust()); |
| 20580 | CHECK(globalProxy->Equals(localContext.local(), globalProxy).FromJust()); |
| 20581 | } |
| 20582 | |
| 20583 | |
| 20584 | static void Getter(v8::Local<v8::Name> property, |
| 20585 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 20586 | info.GetReturnValue().Set(v8_str("42!" )); |
| 20587 | } |
| 20588 | |
| 20589 | |
| 20590 | static void Enumerator(const v8::PropertyCallbackInfo<v8::Array>& info) { |
| 20591 | v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate()); |
| 20592 | result->Set(info.GetIsolate()->GetCurrentContext(), 0, |
| 20593 | v8_str("universalAnswer" )) |
| 20594 | .FromJust(); |
| 20595 | info.GetReturnValue().Set(result); |
| 20596 | } |
| 20597 | |
| 20598 | |
| 20599 | TEST(NamedEnumeratorAndForIn) { |
| 20600 | LocalContext context; |
| 20601 | v8::Isolate* isolate = context->GetIsolate(); |
| 20602 | v8::HandleScope handle_scope(isolate); |
| 20603 | v8::Context::Scope context_scope(context.local()); |
| 20604 | |
| 20605 | v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate); |
| 20606 | tmpl->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 20607 | Getter, nullptr, nullptr, nullptr, Enumerator)); |
| 20608 | CHECK(context->Global() |
| 20609 | ->Set(context.local(), v8_str("o" ), |
| 20610 | tmpl->NewInstance(context.local()).ToLocalChecked()) |
| 20611 | .FromJust()); |
| 20612 | v8::Local<v8::Array> result = v8::Local<v8::Array>::Cast( |
| 20613 | CompileRun("var result = []; for (var k in o) result.push(k); result" )); |
| 20614 | CHECK_EQ(1u, result->Length()); |
| 20615 | CHECK(v8_str("universalAnswer" ) |
| 20616 | ->Equals(context.local(), |
| 20617 | result->Get(context.local(), 0).ToLocalChecked()) |
| 20618 | .FromJust()); |
| 20619 | } |
| 20620 | |
| 20621 | |
| 20622 | TEST(DefinePropertyPostDetach) { |
| 20623 | LocalContext context; |
| 20624 | v8::HandleScope scope(context->GetIsolate()); |
| 20625 | v8::Local<v8::Object> proxy = context->Global(); |
| 20626 | v8::Local<v8::Function> define_property = |
| 20627 | CompileRun( |
| 20628 | "(function() {" |
| 20629 | " Object.defineProperty(" |
| 20630 | " this," |
| 20631 | " 1," |
| 20632 | " { configurable: true, enumerable: true, value: 3 });" |
| 20633 | "})" ) |
| 20634 | .As<Function>(); |
| 20635 | context->DetachGlobal(); |
| 20636 | CHECK(define_property->Call(context.local(), proxy, 0, nullptr).IsEmpty()); |
| 20637 | } |
| 20638 | |
| 20639 | |
| 20640 | static void InstallContextId(v8::Local<Context> context, int id) { |
| 20641 | Context::Scope scope(context); |
| 20642 | CHECK(CompileRun("Object.prototype" ) |
| 20643 | .As<Object>() |
| 20644 | ->Set(context, v8_str("context_id" ), |
| 20645 | v8::Integer::New(context->GetIsolate(), id)) |
| 20646 | .FromJust()); |
| 20647 | } |
| 20648 | |
| 20649 | |
| 20650 | static void CheckContextId(v8::Local<Object> object, int expected) { |
| 20651 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 20652 | CHECK_EQ(expected, object->Get(context, v8_str("context_id" )) |
| 20653 | .ToLocalChecked() |
| 20654 | ->Int32Value(context) |
| 20655 | .FromJust()); |
| 20656 | } |
| 20657 | |
| 20658 | |
| 20659 | THREADED_TEST(CreationContext) { |
| 20660 | v8::Isolate* isolate = CcTest::isolate(); |
| 20661 | HandleScope handle_scope(isolate); |
| 20662 | Local<Context> context1 = Context::New(isolate); |
| 20663 | InstallContextId(context1, 1); |
| 20664 | Local<Context> context2 = Context::New(isolate); |
| 20665 | InstallContextId(context2, 2); |
| 20666 | Local<Context> context3 = Context::New(isolate); |
| 20667 | InstallContextId(context3, 3); |
| 20668 | |
| 20669 | Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(isolate); |
| 20670 | |
| 20671 | Local<Object> object1; |
| 20672 | Local<Function> func1; |
| 20673 | { |
| 20674 | Context::Scope scope(context1); |
| 20675 | object1 = Object::New(isolate); |
| 20676 | func1 = tmpl->GetFunction(context1).ToLocalChecked(); |
| 20677 | } |
| 20678 | |
| 20679 | Local<Object> object2; |
| 20680 | Local<Function> func2; |
| 20681 | { |
| 20682 | Context::Scope scope(context2); |
| 20683 | object2 = Object::New(isolate); |
| 20684 | func2 = tmpl->GetFunction(context2).ToLocalChecked(); |
| 20685 | } |
| 20686 | |
| 20687 | Local<Object> instance1; |
| 20688 | Local<Object> instance2; |
| 20689 | |
| 20690 | { |
| 20691 | Context::Scope scope(context3); |
| 20692 | instance1 = func1->NewInstance(context3).ToLocalChecked(); |
| 20693 | instance2 = func2->NewInstance(context3).ToLocalChecked(); |
| 20694 | } |
| 20695 | |
| 20696 | { |
| 20697 | Local<Context> other_context = Context::New(isolate); |
| 20698 | Context::Scope scope(other_context); |
| 20699 | CHECK(object1->CreationContext() == context1); |
| 20700 | CheckContextId(object1, 1); |
| 20701 | CHECK(func1->CreationContext() == context1); |
| 20702 | CheckContextId(func1, 1); |
| 20703 | CHECK(instance1->CreationContext() == context1); |
| 20704 | CheckContextId(instance1, 1); |
| 20705 | CHECK(object2->CreationContext() == context2); |
| 20706 | CheckContextId(object2, 2); |
| 20707 | CHECK(func2->CreationContext() == context2); |
| 20708 | CheckContextId(func2, 2); |
| 20709 | CHECK(instance2->CreationContext() == context2); |
| 20710 | CheckContextId(instance2, 2); |
| 20711 | } |
| 20712 | |
| 20713 | { |
| 20714 | Context::Scope scope(context1); |
| 20715 | CHECK(object1->CreationContext() == context1); |
| 20716 | CheckContextId(object1, 1); |
| 20717 | CHECK(func1->CreationContext() == context1); |
| 20718 | CheckContextId(func1, 1); |
| 20719 | CHECK(instance1->CreationContext() == context1); |
| 20720 | CheckContextId(instance1, 1); |
| 20721 | CHECK(object2->CreationContext() == context2); |
| 20722 | CheckContextId(object2, 2); |
| 20723 | CHECK(func2->CreationContext() == context2); |
| 20724 | CheckContextId(func2, 2); |
| 20725 | CHECK(instance2->CreationContext() == context2); |
| 20726 | CheckContextId(instance2, 2); |
| 20727 | } |
| 20728 | |
| 20729 | { |
| 20730 | Context::Scope scope(context2); |
| 20731 | CHECK(object1->CreationContext() == context1); |
| 20732 | CheckContextId(object1, 1); |
| 20733 | CHECK(func1->CreationContext() == context1); |
| 20734 | CheckContextId(func1, 1); |
| 20735 | CHECK(instance1->CreationContext() == context1); |
| 20736 | CheckContextId(instance1, 1); |
| 20737 | CHECK(object2->CreationContext() == context2); |
| 20738 | CheckContextId(object2, 2); |
| 20739 | CHECK(func2->CreationContext() == context2); |
| 20740 | CheckContextId(func2, 2); |
| 20741 | CHECK(instance2->CreationContext() == context2); |
| 20742 | CheckContextId(instance2, 2); |
| 20743 | } |
| 20744 | } |
| 20745 | |
| 20746 | |
| 20747 | THREADED_TEST(CreationContextOfJsFunction) { |
| 20748 | HandleScope handle_scope(CcTest::isolate()); |
| 20749 | Local<Context> context = Context::New(CcTest::isolate()); |
| 20750 | InstallContextId(context, 1); |
| 20751 | |
| 20752 | Local<Object> function; |
| 20753 | { |
| 20754 | Context::Scope scope(context); |
| 20755 | function = CompileRun("function foo() {}; foo" ).As<Object>(); |
| 20756 | } |
| 20757 | |
| 20758 | Local<Context> other_context = Context::New(CcTest::isolate()); |
| 20759 | Context::Scope scope(other_context); |
| 20760 | CHECK(function->CreationContext() == context); |
| 20761 | CheckContextId(function, 1); |
| 20762 | } |
| 20763 | |
| 20764 | |
| 20765 | THREADED_TEST(CreationContextOfJsBoundFunction) { |
| 20766 | HandleScope handle_scope(CcTest::isolate()); |
| 20767 | Local<Context> context1 = Context::New(CcTest::isolate()); |
| 20768 | InstallContextId(context1, 1); |
| 20769 | Local<Context> context2 = Context::New(CcTest::isolate()); |
| 20770 | InstallContextId(context2, 2); |
| 20771 | |
| 20772 | Local<Function> target_function; |
| 20773 | { |
| 20774 | Context::Scope scope(context1); |
| 20775 | target_function = CompileRun("function foo() {}; foo" ).As<Function>(); |
| 20776 | } |
| 20777 | |
| 20778 | Local<Function> bound_function1, bound_function2; |
| 20779 | { |
| 20780 | Context::Scope scope(context2); |
| 20781 | CHECK(context2->Global() |
| 20782 | ->Set(context2, v8_str("foo" ), target_function) |
| 20783 | .FromJust()); |
| 20784 | bound_function1 = CompileRun("foo.bind(1)" ).As<Function>(); |
| 20785 | bound_function2 = |
| 20786 | CompileRun("Function.prototype.bind.call(foo, 2)" ).As<Function>(); |
| 20787 | } |
| 20788 | |
| 20789 | Local<Context> other_context = Context::New(CcTest::isolate()); |
| 20790 | Context::Scope scope(other_context); |
| 20791 | CHECK(bound_function1->CreationContext() == context1); |
| 20792 | CheckContextId(bound_function1, 1); |
| 20793 | CHECK(bound_function2->CreationContext() == context1); |
| 20794 | CheckContextId(bound_function2, 1); |
| 20795 | } |
| 20796 | |
| 20797 | |
| 20798 | void HasOwnPropertyIndexedPropertyGetter( |
| 20799 | uint32_t index, |
| 20800 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 20801 | if (index == 42) info.GetReturnValue().Set(v8_str("yes" )); |
| 20802 | } |
| 20803 | |
| 20804 | |
| 20805 | void HasOwnPropertyNamedPropertyGetter( |
| 20806 | Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 20807 | if (property->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo" )) |
| 20808 | .FromJust()) { |
| 20809 | info.GetReturnValue().Set(v8_str("yes" )); |
| 20810 | } |
| 20811 | } |
| 20812 | |
| 20813 | |
| 20814 | void HasOwnPropertyIndexedPropertyQuery( |
| 20815 | uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 20816 | if (index == 42) info.GetReturnValue().Set(1); |
| 20817 | } |
| 20818 | |
| 20819 | |
| 20820 | void HasOwnPropertyNamedPropertyQuery( |
| 20821 | Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 20822 | if (property->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo" )) |
| 20823 | .FromJust()) { |
| 20824 | info.GetReturnValue().Set(1); |
| 20825 | } |
| 20826 | } |
| 20827 | |
| 20828 | |
| 20829 | void HasOwnPropertyNamedPropertyQuery2( |
| 20830 | Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| 20831 | if (property->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("bar" )) |
| 20832 | .FromJust()) { |
| 20833 | info.GetReturnValue().Set(1); |
| 20834 | } |
| 20835 | } |
| 20836 | |
| 20837 | void HasOwnPropertyAccessorGetter( |
| 20838 | Local<String> property, |
| 20839 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 20840 | info.GetReturnValue().Set(v8_str("yes" )); |
| 20841 | } |
| 20842 | |
| 20843 | void HasOwnPropertyAccessorNameGetter( |
| 20844 | Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 20845 | info.GetReturnValue().Set(v8_str("yes" )); |
| 20846 | } |
| 20847 | |
| 20848 | TEST(HasOwnProperty) { |
| 20849 | LocalContext env; |
| 20850 | v8::Isolate* isolate = env->GetIsolate(); |
| 20851 | v8::HandleScope scope(isolate); |
| 20852 | { // Check normal properties and defined getters. |
| 20853 | Local<Value> value = CompileRun( |
| 20854 | "function Foo() {" |
| 20855 | " this.foo = 11;" |
| 20856 | " this.__defineGetter__('baz', function() { return 1; });" |
| 20857 | "};" |
| 20858 | "function Bar() { " |
| 20859 | " this.bar = 13;" |
| 20860 | " this.__defineGetter__('bla', function() { return 2; });" |
| 20861 | "};" |
| 20862 | "Bar.prototype = new Foo();" |
| 20863 | "new Bar();" ); |
| 20864 | CHECK(value->IsObject()); |
| 20865 | Local<Object> object = value->ToObject(env.local()).ToLocalChecked(); |
| 20866 | CHECK(object->Has(env.local(), v8_str("foo" )).FromJust()); |
| 20867 | CHECK(!object->HasOwnProperty(env.local(), v8_str("foo" )).FromJust()); |
| 20868 | CHECK(object->HasOwnProperty(env.local(), v8_str("bar" )).FromJust()); |
| 20869 | CHECK(object->Has(env.local(), v8_str("baz" )).FromJust()); |
| 20870 | CHECK(!object->HasOwnProperty(env.local(), v8_str("baz" )).FromJust()); |
| 20871 | CHECK(object->HasOwnProperty(env.local(), v8_str("bla" )).FromJust()); |
| 20872 | } |
| 20873 | { // Check named getter interceptors. |
| 20874 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20875 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 20876 | HasOwnPropertyNamedPropertyGetter)); |
| 20877 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20878 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("42" )).FromJust()); |
| 20879 | CHECK(!instance->HasOwnProperty(env.local(), 42).FromJust()); |
| 20880 | CHECK(instance->HasOwnProperty(env.local(), v8_str("foo" )).FromJust()); |
| 20881 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("bar" )).FromJust()); |
| 20882 | } |
| 20883 | { // Check indexed getter interceptors. |
| 20884 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20885 | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 20886 | HasOwnPropertyIndexedPropertyGetter)); |
| 20887 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20888 | CHECK(instance->HasOwnProperty(env.local(), v8_str("42" )).FromJust()); |
| 20889 | CHECK(instance->HasOwnProperty(env.local(), 42).FromJust()); |
| 20890 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("43" )).FromJust()); |
| 20891 | CHECK(!instance->HasOwnProperty(env.local(), 43).FromJust()); |
| 20892 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("foo" )).FromJust()); |
| 20893 | } |
| 20894 | { // Check named query interceptors. |
| 20895 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20896 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 20897 | nullptr, nullptr, HasOwnPropertyNamedPropertyQuery)); |
| 20898 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20899 | CHECK(instance->HasOwnProperty(env.local(), v8_str("foo" )).FromJust()); |
| 20900 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("bar" )).FromJust()); |
| 20901 | } |
| 20902 | { // Check indexed query interceptors. |
| 20903 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20904 | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 20905 | nullptr, nullptr, HasOwnPropertyIndexedPropertyQuery)); |
| 20906 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20907 | CHECK(instance->HasOwnProperty(env.local(), v8_str("42" )).FromJust()); |
| 20908 | CHECK(instance->HasOwnProperty(env.local(), 42).FromJust()); |
| 20909 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("41" )).FromJust()); |
| 20910 | CHECK(!instance->HasOwnProperty(env.local(), 41).FromJust()); |
| 20911 | } |
| 20912 | { // Check callbacks. |
| 20913 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20914 | templ->SetAccessor(v8_str("foo" ), HasOwnPropertyAccessorGetter); |
| 20915 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20916 | CHECK(instance->HasOwnProperty(env.local(), v8_str("foo" )).FromJust()); |
| 20917 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("bar" )).FromJust()); |
| 20918 | } |
| 20919 | { // Check that query wins on disagreement. |
| 20920 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20921 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 20922 | HasOwnPropertyNamedPropertyGetter, nullptr, |
| 20923 | HasOwnPropertyNamedPropertyQuery2)); |
| 20924 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20925 | CHECK(!instance->HasOwnProperty(env.local(), v8_str("foo" )).FromJust()); |
| 20926 | CHECK(instance->HasOwnProperty(env.local(), v8_str("bar" )).FromJust()); |
| 20927 | } |
| 20928 | { // Check that non-internalized keys are handled correctly. |
| 20929 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20930 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 20931 | HasOwnPropertyAccessorNameGetter)); |
| 20932 | Local<Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 20933 | env->Global()->Set(env.local(), v8_str("obj" ), instance).FromJust(); |
| 20934 | const char* src = |
| 20935 | "var dyn_string = 'this string ';" |
| 20936 | "dyn_string += 'does not exist elsewhere';" |
| 20937 | "({}).hasOwnProperty.call(obj, dyn_string)" ; |
| 20938 | CHECK(CompileRun(src)->BooleanValue(isolate)); |
| 20939 | } |
| 20940 | } |
| 20941 | |
| 20942 | |
| 20943 | TEST(IndexedInterceptorWithStringProto) { |
| 20944 | v8::Isolate* isolate = CcTest::isolate(); |
| 20945 | v8::HandleScope scope(isolate); |
| 20946 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 20947 | templ->SetHandler(v8::IndexedPropertyHandlerConfiguration( |
| 20948 | nullptr, nullptr, HasOwnPropertyIndexedPropertyQuery)); |
| 20949 | LocalContext context; |
| 20950 | CHECK(context->Global() |
| 20951 | ->Set(context.local(), v8_str("obj" ), |
| 20952 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 20953 | .FromJust()); |
| 20954 | CompileRun("var s = new String('foobar'); obj.__proto__ = s;" ); |
| 20955 | // These should be intercepted. |
| 20956 | CHECK(CompileRun("42 in obj" )->BooleanValue(isolate)); |
| 20957 | CHECK(CompileRun("'42' in obj" )->BooleanValue(isolate)); |
| 20958 | // These should fall through to the String prototype. |
| 20959 | CHECK(CompileRun("0 in obj" )->BooleanValue(isolate)); |
| 20960 | CHECK(CompileRun("'0' in obj" )->BooleanValue(isolate)); |
| 20961 | // And these should both fail. |
| 20962 | CHECK(!CompileRun("32 in obj" )->BooleanValue(isolate)); |
| 20963 | CHECK(!CompileRun("'32' in obj" )->BooleanValue(isolate)); |
| 20964 | } |
| 20965 | |
| 20966 | |
| 20967 | void CheckCodeGenerationAllowed() { |
| 20968 | Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 20969 | Local<Value> result = CompileRun("eval('42')" ); |
| 20970 | CHECK_EQ(42, result->Int32Value(context).FromJust()); |
| 20971 | result = CompileRun("(function(e) { return e('42'); })(eval)" ); |
| 20972 | CHECK_EQ(42, result->Int32Value(context).FromJust()); |
| 20973 | result = CompileRun("var f = new Function('return 42'); f()" ); |
| 20974 | CHECK_EQ(42, result->Int32Value(context).FromJust()); |
| 20975 | } |
| 20976 | |
| 20977 | |
| 20978 | void CheckCodeGenerationDisallowed() { |
| 20979 | TryCatch try_catch(CcTest::isolate()); |
| 20980 | |
| 20981 | Local<Value> result = CompileRun("eval('42')" ); |
| 20982 | CHECK(result.IsEmpty()); |
| 20983 | CHECK(try_catch.HasCaught()); |
| 20984 | try_catch.Reset(); |
| 20985 | |
| 20986 | result = CompileRun("(function(e) { return e('42'); })(eval)" ); |
| 20987 | CHECK(result.IsEmpty()); |
| 20988 | CHECK(try_catch.HasCaught()); |
| 20989 | try_catch.Reset(); |
| 20990 | |
| 20991 | result = CompileRun("var f = new Function('return 42'); f()" ); |
| 20992 | CHECK(result.IsEmpty()); |
| 20993 | CHECK(try_catch.HasCaught()); |
| 20994 | } |
| 20995 | |
| 20996 | char first_fourty_bytes[41]; |
| 20997 | |
| 20998 | bool CodeGenerationAllowed(Local<Context> context, Local<String> source) { |
| 20999 | String::Utf8Value str(CcTest::isolate(), source); |
| 21000 | size_t len = std::min(sizeof(first_fourty_bytes) - 1, |
| 21001 | static_cast<size_t>(str.length())); |
| 21002 | strncpy(first_fourty_bytes, *str, len); |
| 21003 | first_fourty_bytes[len] = 0; |
| 21004 | ApiTestFuzzer::Fuzz(); |
| 21005 | return true; |
| 21006 | } |
| 21007 | |
| 21008 | bool CodeGenerationDisallowed(Local<Context> context, Local<String> source) { |
| 21009 | ApiTestFuzzer::Fuzz(); |
| 21010 | return false; |
| 21011 | } |
| 21012 | |
| 21013 | |
| 21014 | THREADED_TEST(AllowCodeGenFromStrings) { |
| 21015 | LocalContext context; |
| 21016 | v8::HandleScope scope(context->GetIsolate()); |
| 21017 | |
| 21018 | // eval and the Function constructor allowed by default. |
| 21019 | CHECK(context->IsCodeGenerationFromStringsAllowed()); |
| 21020 | CheckCodeGenerationAllowed(); |
| 21021 | |
| 21022 | // Disallow eval and the Function constructor. |
| 21023 | context->AllowCodeGenerationFromStrings(false); |
| 21024 | CHECK(!context->IsCodeGenerationFromStringsAllowed()); |
| 21025 | CheckCodeGenerationDisallowed(); |
| 21026 | |
| 21027 | // Allow again. |
| 21028 | context->AllowCodeGenerationFromStrings(true); |
| 21029 | CheckCodeGenerationAllowed(); |
| 21030 | |
| 21031 | // Disallow but setting a global callback that will allow the calls. |
| 21032 | context->AllowCodeGenerationFromStrings(false); |
| 21033 | context->GetIsolate()->SetAllowCodeGenerationFromStringsCallback( |
| 21034 | &CodeGenerationAllowed); |
| 21035 | CHECK(!context->IsCodeGenerationFromStringsAllowed()); |
| 21036 | CheckCodeGenerationAllowed(); |
| 21037 | |
| 21038 | // Set a callback that disallows the code generation. |
| 21039 | context->GetIsolate()->SetAllowCodeGenerationFromStringsCallback( |
| 21040 | &CodeGenerationDisallowed); |
| 21041 | CHECK(!context->IsCodeGenerationFromStringsAllowed()); |
| 21042 | CheckCodeGenerationDisallowed(); |
| 21043 | } |
| 21044 | |
| 21045 | |
| 21046 | TEST(SetErrorMessageForCodeGenFromStrings) { |
| 21047 | LocalContext context; |
| 21048 | v8::HandleScope scope(context->GetIsolate()); |
| 21049 | TryCatch try_catch(context->GetIsolate()); |
| 21050 | |
| 21051 | Local<String> message = v8_str("Message" ); |
| 21052 | Local<String> expected_message = v8_str("Uncaught EvalError: Message" ); |
| 21053 | context->GetIsolate()->SetAllowCodeGenerationFromStringsCallback( |
| 21054 | &CodeGenerationDisallowed); |
| 21055 | context->AllowCodeGenerationFromStrings(false); |
| 21056 | context->SetErrorMessageForCodeGenerationFromStrings(message); |
| 21057 | Local<Value> result = CompileRun("eval('42')" ); |
| 21058 | CHECK(result.IsEmpty()); |
| 21059 | CHECK(try_catch.HasCaught()); |
| 21060 | Local<String> actual_message = try_catch.Message()->Get(); |
| 21061 | CHECK(expected_message->Equals(context.local(), actual_message).FromJust()); |
| 21062 | } |
| 21063 | |
| 21064 | TEST(CaptureSourceForCodeGenFromStrings) { |
| 21065 | LocalContext context; |
| 21066 | v8::HandleScope scope(context->GetIsolate()); |
| 21067 | TryCatch try_catch(context->GetIsolate()); |
| 21068 | |
| 21069 | context->GetIsolate()->SetAllowCodeGenerationFromStringsCallback( |
| 21070 | &CodeGenerationAllowed); |
| 21071 | context->AllowCodeGenerationFromStrings(false); |
| 21072 | CompileRun("eval('42')" ); |
| 21073 | CHECK(!strcmp(first_fourty_bytes, "42" )); |
| 21074 | } |
| 21075 | |
| 21076 | static void NonObjectThis(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 21077 | } |
| 21078 | |
| 21079 | |
| 21080 | THREADED_TEST(CallAPIFunctionOnNonObject) { |
| 21081 | LocalContext context; |
| 21082 | v8::Isolate* isolate = context->GetIsolate(); |
| 21083 | v8::HandleScope scope(isolate); |
| 21084 | Local<FunctionTemplate> templ = |
| 21085 | v8::FunctionTemplate::New(isolate, NonObjectThis); |
| 21086 | Local<Function> function = |
| 21087 | templ->GetFunction(context.local()).ToLocalChecked(); |
| 21088 | CHECK(context->Global() |
| 21089 | ->Set(context.local(), v8_str("f" ), function) |
| 21090 | .FromJust()); |
| 21091 | TryCatch try_catch(isolate); |
| 21092 | CompileRun("f.call(2)" ); |
| 21093 | } |
| 21094 | |
| 21095 | |
| 21096 | // Regression test for issue 1470. |
| 21097 | THREADED_TEST(ReadOnlyIndexedProperties) { |
| 21098 | v8::Isolate* isolate = CcTest::isolate(); |
| 21099 | v8::HandleScope scope(isolate); |
| 21100 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 21101 | |
| 21102 | LocalContext context; |
| 21103 | Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); |
| 21104 | CHECK(context->Global()->Set(context.local(), v8_str("obj" ), obj).FromJust()); |
| 21105 | obj->DefineOwnProperty(context.local(), v8_str("1" ), v8_str("DONT_CHANGE" ), |
| 21106 | v8::ReadOnly) |
| 21107 | .FromJust(); |
| 21108 | obj->Set(context.local(), v8_str("1" ), v8_str("foobar" )).FromJust(); |
| 21109 | CHECK(v8_str("DONT_CHANGE" ) |
| 21110 | ->Equals(context.local(), |
| 21111 | obj->Get(context.local(), v8_str("1" )).ToLocalChecked()) |
| 21112 | .FromJust()); |
| 21113 | obj->DefineOwnProperty(context.local(), v8_str("2" ), v8_str("DONT_CHANGE" ), |
| 21114 | v8::ReadOnly) |
| 21115 | .FromJust(); |
| 21116 | obj->Set(context.local(), v8_num(2), v8_str("foobar" )).FromJust(); |
| 21117 | CHECK(v8_str("DONT_CHANGE" ) |
| 21118 | ->Equals(context.local(), |
| 21119 | obj->Get(context.local(), v8_num(2)).ToLocalChecked()) |
| 21120 | .FromJust()); |
| 21121 | |
| 21122 | // Test non-smi case. |
| 21123 | obj->DefineOwnProperty(context.local(), v8_str("2000000000" ), |
| 21124 | v8_str("DONT_CHANGE" ), v8::ReadOnly) |
| 21125 | .FromJust(); |
| 21126 | obj->Set(context.local(), v8_str("2000000000" ), v8_str("foobar" )).FromJust(); |
| 21127 | CHECK(v8_str("DONT_CHANGE" ) |
| 21128 | ->Equals(context.local(), |
| 21129 | obj->Get(context.local(), v8_str("2000000000" )) |
| 21130 | .ToLocalChecked()) |
| 21131 | .FromJust()); |
| 21132 | } |
| 21133 | |
| 21134 | static int CountLiveMapsInMapCache(i::Context context) { |
| 21135 | i::WeakFixedArray map_cache = i::WeakFixedArray::cast(context->map_cache()); |
| 21136 | int length = map_cache->length(); |
| 21137 | int count = 0; |
| 21138 | for (int i = 0; i < length; i++) { |
| 21139 | if (map_cache->Get(i)->IsWeak()) count++; |
| 21140 | } |
| 21141 | return count; |
| 21142 | } |
| 21143 | |
| 21144 | |
| 21145 | THREADED_TEST(Regress1516) { |
| 21146 | LocalContext context; |
| 21147 | v8::HandleScope scope(context->GetIsolate()); |
| 21148 | |
| 21149 | // Object with 20 properties is not a common case, so it should be removed |
| 21150 | // from the cache after GC. |
| 21151 | { v8::HandleScope temp_scope(context->GetIsolate()); |
| 21152 | CompileRun( |
| 21153 | "({" |
| 21154 | "'a00': 0, 'a01': 0, 'a02': 0, 'a03': 0, 'a04': 0, " |
| 21155 | "'a05': 0, 'a06': 0, 'a07': 0, 'a08': 0, 'a09': 0, " |
| 21156 | "'a10': 0, 'a11': 0, 'a12': 0, 'a13': 0, 'a14': 0, " |
| 21157 | "'a15': 0, 'a16': 0, 'a17': 0, 'a18': 0, 'a19': 0, " |
| 21158 | "})" ); |
| 21159 | } |
| 21160 | |
| 21161 | int elements = CountLiveMapsInMapCache(CcTest::i_isolate()->context()); |
| 21162 | CHECK_LE(1, elements); |
| 21163 | |
| 21164 | // We have to abort incremental marking here to abandon black pages. |
| 21165 | CcTest::PreciseCollectAllGarbage(); |
| 21166 | |
| 21167 | CHECK_GT(elements, CountLiveMapsInMapCache(CcTest::i_isolate()->context())); |
| 21168 | } |
| 21169 | |
| 21170 | |
| 21171 | static void TestReceiver(Local<Value> expected_result, |
| 21172 | Local<Value> expected_receiver, |
| 21173 | const char* code) { |
| 21174 | Local<Value> result = CompileRun(code); |
| 21175 | Local<Context> context = CcTest::isolate()->GetCurrentContext(); |
| 21176 | CHECK(result->IsObject()); |
| 21177 | CHECK(expected_receiver |
| 21178 | ->Equals(context, |
| 21179 | result.As<v8::Object>()->Get(context, 1).ToLocalChecked()) |
| 21180 | .FromJust()); |
| 21181 | CHECK(expected_result |
| 21182 | ->Equals(context, |
| 21183 | result.As<v8::Object>()->Get(context, 0).ToLocalChecked()) |
| 21184 | .FromJust()); |
| 21185 | } |
| 21186 | |
| 21187 | |
| 21188 | THREADED_TEST(ForeignFunctionReceiver) { |
| 21189 | v8::Isolate* isolate = CcTest::isolate(); |
| 21190 | HandleScope scope(isolate); |
| 21191 | |
| 21192 | // Create two contexts with different "id" properties ('i' and 'o'). |
| 21193 | // Call a function both from its own context and from a the foreign |
| 21194 | // context, and see what "this" is bound to (returning both "this" |
| 21195 | // and "this.id" for comparison). |
| 21196 | |
| 21197 | Local<Context> foreign_context = v8::Context::New(isolate); |
| 21198 | foreign_context->Enter(); |
| 21199 | Local<Value> foreign_function = |
| 21200 | CompileRun("function func() { return { 0: this.id, " |
| 21201 | " 1: this, " |
| 21202 | " toString: function() { " |
| 21203 | " return this[0];" |
| 21204 | " }" |
| 21205 | " };" |
| 21206 | "}" |
| 21207 | "var id = 'i';" |
| 21208 | "func;" ); |
| 21209 | CHECK(foreign_function->IsFunction()); |
| 21210 | foreign_context->Exit(); |
| 21211 | |
| 21212 | LocalContext context; |
| 21213 | |
| 21214 | Local<String> password = v8_str("Password" ); |
| 21215 | // Don't get hit by security checks when accessing foreign_context's |
| 21216 | // global receiver (aka. global proxy). |
| 21217 | context->SetSecurityToken(password); |
| 21218 | foreign_context->SetSecurityToken(password); |
| 21219 | |
| 21220 | Local<String> i = v8_str("i" ); |
| 21221 | Local<String> o = v8_str("o" ); |
| 21222 | Local<String> id = v8_str("id" ); |
| 21223 | |
| 21224 | CompileRun("function ownfunc() { return { 0: this.id, " |
| 21225 | " 1: this, " |
| 21226 | " toString: function() { " |
| 21227 | " return this[0];" |
| 21228 | " }" |
| 21229 | " };" |
| 21230 | "}" |
| 21231 | "var id = 'o';" |
| 21232 | "ownfunc" ); |
| 21233 | CHECK(context->Global() |
| 21234 | ->Set(context.local(), v8_str("func" ), foreign_function) |
| 21235 | .FromJust()); |
| 21236 | |
| 21237 | // Sanity check the contexts. |
| 21238 | CHECK( |
| 21239 | i->Equals( |
| 21240 | context.local(), |
| 21241 | foreign_context->Global()->Get(context.local(), id).ToLocalChecked()) |
| 21242 | .FromJust()); |
| 21243 | CHECK(o->Equals(context.local(), |
| 21244 | context->Global()->Get(context.local(), id).ToLocalChecked()) |
| 21245 | .FromJust()); |
| 21246 | |
| 21247 | // Checking local function's receiver. |
| 21248 | // Calling function using its call/apply methods. |
| 21249 | TestReceiver(o, context->Global(), "ownfunc.call()" ); |
| 21250 | TestReceiver(o, context->Global(), "ownfunc.apply()" ); |
| 21251 | // Making calls through built-in functions. |
| 21252 | TestReceiver(o, context->Global(), "[1].map(ownfunc)[0]" ); |
| 21253 | CHECK( |
| 21254 | o->Equals(context.local(), CompileRun("'abcbd'.replace(/b/,ownfunc)[1]" )) |
| 21255 | .FromJust()); |
| 21256 | CHECK( |
| 21257 | o->Equals(context.local(), CompileRun("'abcbd'.replace(/b/g,ownfunc)[1]" )) |
| 21258 | .FromJust()); |
| 21259 | CHECK( |
| 21260 | o->Equals(context.local(), CompileRun("'abcbd'.replace(/b/g,ownfunc)[3]" )) |
| 21261 | .FromJust()); |
| 21262 | // Calling with environment record as base. |
| 21263 | TestReceiver(o, context->Global(), "ownfunc()" ); |
| 21264 | // Calling with no base. |
| 21265 | TestReceiver(o, context->Global(), "(1,ownfunc)()" ); |
| 21266 | |
| 21267 | // Checking foreign function return value. |
| 21268 | // Calling function using its call/apply methods. |
| 21269 | TestReceiver(i, foreign_context->Global(), "func.call()" ); |
| 21270 | TestReceiver(i, foreign_context->Global(), "func.apply()" ); |
| 21271 | // Calling function using another context's call/apply methods. |
| 21272 | TestReceiver(i, foreign_context->Global(), |
| 21273 | "Function.prototype.call.call(func)" ); |
| 21274 | TestReceiver(i, foreign_context->Global(), |
| 21275 | "Function.prototype.call.apply(func)" ); |
| 21276 | TestReceiver(i, foreign_context->Global(), |
| 21277 | "Function.prototype.apply.call(func)" ); |
| 21278 | TestReceiver(i, foreign_context->Global(), |
| 21279 | "Function.prototype.apply.apply(func)" ); |
| 21280 | // Making calls through built-in functions. |
| 21281 | TestReceiver(i, foreign_context->Global(), "[1].map(func)[0]" ); |
| 21282 | // ToString(func()) is func()[0], i.e., the returned this.id. |
| 21283 | CHECK(i->Equals(context.local(), CompileRun("'abcbd'.replace(/b/,func)[1]" )) |
| 21284 | .FromJust()); |
| 21285 | CHECK(i->Equals(context.local(), CompileRun("'abcbd'.replace(/b/g,func)[1]" )) |
| 21286 | .FromJust()); |
| 21287 | CHECK(i->Equals(context.local(), CompileRun("'abcbd'.replace(/b/g,func)[3]" )) |
| 21288 | .FromJust()); |
| 21289 | |
| 21290 | // Calling with environment record as base. |
| 21291 | TestReceiver(i, foreign_context->Global(), "func()" ); |
| 21292 | // Calling with no base. |
| 21293 | TestReceiver(i, foreign_context->Global(), "(1,func)()" ); |
| 21294 | } |
| 21295 | |
| 21296 | |
| 21297 | uint8_t callback_fired = 0; |
| 21298 | uint8_t before_call_entered_callback_count1 = 0; |
| 21299 | uint8_t before_call_entered_callback_count2 = 0; |
| 21300 | |
| 21301 | |
| 21302 | void CallCompletedCallback1(v8::Isolate*) { |
| 21303 | v8::base::OS::Print("Firing callback 1.\n" ); |
| 21304 | callback_fired ^= 1; // Toggle first bit. |
| 21305 | } |
| 21306 | |
| 21307 | |
| 21308 | void CallCompletedCallback2(v8::Isolate*) { |
| 21309 | v8::base::OS::Print("Firing callback 2.\n" ); |
| 21310 | callback_fired ^= 2; // Toggle second bit. |
| 21311 | } |
| 21312 | |
| 21313 | |
| 21314 | void BeforeCallEnteredCallback1(v8::Isolate*) { |
| 21315 | v8::base::OS::Print("Firing before call entered callback 1.\n" ); |
| 21316 | before_call_entered_callback_count1++; |
| 21317 | } |
| 21318 | |
| 21319 | |
| 21320 | void BeforeCallEnteredCallback2(v8::Isolate*) { |
| 21321 | v8::base::OS::Print("Firing before call entered callback 2.\n" ); |
| 21322 | before_call_entered_callback_count2++; |
| 21323 | } |
| 21324 | |
| 21325 | |
| 21326 | void RecursiveCall(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 21327 | int32_t level = |
| 21328 | args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust(); |
| 21329 | if (level < 3) { |
| 21330 | level++; |
| 21331 | v8::base::OS::Print("Entering recursion level %d.\n" , level); |
| 21332 | char script[64]; |
| 21333 | i::Vector<char> script_vector(script, sizeof(script)); |
| 21334 | i::SNPrintF(script_vector, "recursion(%d)" , level); |
| 21335 | CompileRun(script_vector.start()); |
| 21336 | v8::base::OS::Print("Leaving recursion level %d.\n" , level); |
| 21337 | CHECK_EQ(0, callback_fired); |
| 21338 | } else { |
| 21339 | v8::base::OS::Print("Recursion ends.\n" ); |
| 21340 | CHECK_EQ(0, callback_fired); |
| 21341 | } |
| 21342 | } |
| 21343 | |
| 21344 | |
| 21345 | TEST(CallCompletedCallback) { |
| 21346 | LocalContext env; |
| 21347 | v8::HandleScope scope(env->GetIsolate()); |
| 21348 | v8::Local<v8::FunctionTemplate> recursive_runtime = |
| 21349 | v8::FunctionTemplate::New(env->GetIsolate(), RecursiveCall); |
| 21350 | env->Global() |
| 21351 | ->Set(env.local(), v8_str("recursion" ), |
| 21352 | recursive_runtime->GetFunction(env.local()).ToLocalChecked()) |
| 21353 | .FromJust(); |
| 21354 | // Adding the same callback a second time has no effect. |
| 21355 | env->GetIsolate()->AddCallCompletedCallback(CallCompletedCallback1); |
| 21356 | env->GetIsolate()->AddCallCompletedCallback(CallCompletedCallback1); |
| 21357 | env->GetIsolate()->AddCallCompletedCallback(CallCompletedCallback2); |
| 21358 | env->GetIsolate()->AddBeforeCallEnteredCallback(BeforeCallEnteredCallback1); |
| 21359 | env->GetIsolate()->AddBeforeCallEnteredCallback(BeforeCallEnteredCallback2); |
| 21360 | env->GetIsolate()->AddBeforeCallEnteredCallback(BeforeCallEnteredCallback1); |
| 21361 | v8::base::OS::Print("--- Script (1) ---\n" ); |
| 21362 | callback_fired = 0; |
| 21363 | before_call_entered_callback_count1 = 0; |
| 21364 | before_call_entered_callback_count2 = 0; |
| 21365 | Local<Script> script = |
| 21366 | v8::Script::Compile(env.local(), v8_str("recursion(0)" )).ToLocalChecked(); |
| 21367 | script->Run(env.local()).ToLocalChecked(); |
| 21368 | CHECK_EQ(3, callback_fired); |
| 21369 | CHECK_EQ(4, before_call_entered_callback_count1); |
| 21370 | CHECK_EQ(4, before_call_entered_callback_count2); |
| 21371 | |
| 21372 | v8::base::OS::Print("\n--- Script (2) ---\n" ); |
| 21373 | callback_fired = 0; |
| 21374 | before_call_entered_callback_count1 = 0; |
| 21375 | before_call_entered_callback_count2 = 0; |
| 21376 | env->GetIsolate()->RemoveCallCompletedCallback(CallCompletedCallback1); |
| 21377 | env->GetIsolate()->RemoveBeforeCallEnteredCallback( |
| 21378 | BeforeCallEnteredCallback1); |
| 21379 | script->Run(env.local()).ToLocalChecked(); |
| 21380 | CHECK_EQ(2, callback_fired); |
| 21381 | CHECK_EQ(0, before_call_entered_callback_count1); |
| 21382 | CHECK_EQ(4, before_call_entered_callback_count2); |
| 21383 | |
| 21384 | v8::base::OS::Print("\n--- Function ---\n" ); |
| 21385 | callback_fired = 0; |
| 21386 | before_call_entered_callback_count1 = 0; |
| 21387 | before_call_entered_callback_count2 = 0; |
| 21388 | Local<Function> recursive_function = Local<Function>::Cast( |
| 21389 | env->Global()->Get(env.local(), v8_str("recursion" )).ToLocalChecked()); |
| 21390 | v8::Local<Value> args[] = {v8_num(0)}; |
| 21391 | recursive_function->Call(env.local(), env->Global(), 1, args) |
| 21392 | .ToLocalChecked(); |
| 21393 | CHECK_EQ(2, callback_fired); |
| 21394 | CHECK_EQ(0, before_call_entered_callback_count1); |
| 21395 | CHECK_EQ(4, before_call_entered_callback_count2); |
| 21396 | } |
| 21397 | |
| 21398 | |
| 21399 | void CallCompletedCallbackNoException(v8::Isolate*) { |
| 21400 | v8::HandleScope scope(CcTest::isolate()); |
| 21401 | CompileRun("1+1;" ); |
| 21402 | } |
| 21403 | |
| 21404 | |
| 21405 | void CallCompletedCallbackException(v8::Isolate*) { |
| 21406 | v8::HandleScope scope(CcTest::isolate()); |
| 21407 | CompileRun("throw 'second exception';" ); |
| 21408 | } |
| 21409 | |
| 21410 | |
| 21411 | TEST(CallCompletedCallbackOneException) { |
| 21412 | LocalContext env; |
| 21413 | v8::HandleScope scope(env->GetIsolate()); |
| 21414 | env->GetIsolate()->AddCallCompletedCallback(CallCompletedCallbackNoException); |
| 21415 | CompileRun("throw 'exception';" ); |
| 21416 | } |
| 21417 | |
| 21418 | |
| 21419 | TEST(CallCompletedCallbackTwoExceptions) { |
| 21420 | LocalContext env; |
| 21421 | v8::HandleScope scope(env->GetIsolate()); |
| 21422 | env->GetIsolate()->AddCallCompletedCallback(CallCompletedCallbackException); |
| 21423 | CompileRun("throw 'first exception';" ); |
| 21424 | } |
| 21425 | |
| 21426 | |
| 21427 | static void MicrotaskOne(const v8::FunctionCallbackInfo<Value>& info) { |
| 21428 | CHECK(v8::MicrotasksScope::IsRunningMicrotasks(info.GetIsolate())); |
| 21429 | v8::HandleScope scope(info.GetIsolate()); |
| 21430 | v8::MicrotasksScope microtasks(info.GetIsolate(), |
| 21431 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21432 | CompileRun("ext1Calls++;" ); |
| 21433 | } |
| 21434 | |
| 21435 | |
| 21436 | static void MicrotaskTwo(const v8::FunctionCallbackInfo<Value>& info) { |
| 21437 | CHECK(v8::MicrotasksScope::IsRunningMicrotasks(info.GetIsolate())); |
| 21438 | v8::HandleScope scope(info.GetIsolate()); |
| 21439 | v8::MicrotasksScope microtasks(info.GetIsolate(), |
| 21440 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21441 | CompileRun("ext2Calls++;" ); |
| 21442 | } |
| 21443 | |
| 21444 | void* g_passed_to_three = nullptr; |
| 21445 | |
| 21446 | static void MicrotaskThree(void* data) { |
| 21447 | g_passed_to_three = data; |
| 21448 | } |
| 21449 | |
| 21450 | |
| 21451 | TEST(EnqueueMicrotask) { |
| 21452 | LocalContext env; |
| 21453 | v8::HandleScope scope(env->GetIsolate()); |
| 21454 | CHECK(!v8::MicrotasksScope::IsRunningMicrotasks(env->GetIsolate())); |
| 21455 | CompileRun( |
| 21456 | "var ext1Calls = 0;" |
| 21457 | "var ext2Calls = 0;" ); |
| 21458 | CompileRun("1+1;" ); |
| 21459 | CHECK_EQ(0, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21460 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21461 | |
| 21462 | env->GetIsolate()->EnqueueMicrotask( |
| 21463 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21464 | CompileRun("1+1;" ); |
| 21465 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21466 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21467 | |
| 21468 | env->GetIsolate()->EnqueueMicrotask( |
| 21469 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21470 | env->GetIsolate()->EnqueueMicrotask( |
| 21471 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21472 | CompileRun("1+1;" ); |
| 21473 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21474 | CHECK_EQ(1, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21475 | |
| 21476 | env->GetIsolate()->EnqueueMicrotask( |
| 21477 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21478 | CompileRun("1+1;" ); |
| 21479 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21480 | CHECK_EQ(2, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21481 | |
| 21482 | CompileRun("1+1;" ); |
| 21483 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21484 | CHECK_EQ(2, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21485 | |
| 21486 | g_passed_to_three = nullptr; |
| 21487 | env->GetIsolate()->EnqueueMicrotask(MicrotaskThree); |
| 21488 | CompileRun("1+1;" ); |
| 21489 | CHECK(!g_passed_to_three); |
| 21490 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21491 | CHECK_EQ(2, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21492 | |
| 21493 | int dummy; |
| 21494 | env->GetIsolate()->EnqueueMicrotask( |
| 21495 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21496 | env->GetIsolate()->EnqueueMicrotask(MicrotaskThree, &dummy); |
| 21497 | env->GetIsolate()->EnqueueMicrotask( |
| 21498 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21499 | CompileRun("1+1;" ); |
| 21500 | CHECK_EQ(&dummy, g_passed_to_three); |
| 21501 | CHECK_EQ(3, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21502 | CHECK_EQ(3, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21503 | g_passed_to_three = nullptr; |
| 21504 | } |
| 21505 | |
| 21506 | |
| 21507 | static void MicrotaskExceptionOne( |
| 21508 | const v8::FunctionCallbackInfo<Value>& info) { |
| 21509 | v8::HandleScope scope(info.GetIsolate()); |
| 21510 | CompileRun("exception1Calls++;" ); |
| 21511 | info.GetIsolate()->ThrowException( |
| 21512 | v8::Exception::Error(v8_str("first" ))); |
| 21513 | } |
| 21514 | |
| 21515 | |
| 21516 | static void MicrotaskExceptionTwo( |
| 21517 | const v8::FunctionCallbackInfo<Value>& info) { |
| 21518 | v8::HandleScope scope(info.GetIsolate()); |
| 21519 | CompileRun("exception2Calls++;" ); |
| 21520 | info.GetIsolate()->ThrowException( |
| 21521 | v8::Exception::Error(v8_str("second" ))); |
| 21522 | } |
| 21523 | |
| 21524 | |
| 21525 | TEST(RunMicrotasksIgnoresThrownExceptions) { |
| 21526 | LocalContext env; |
| 21527 | v8::Isolate* isolate = env->GetIsolate(); |
| 21528 | v8::HandleScope scope(isolate); |
| 21529 | CompileRun( |
| 21530 | "var exception1Calls = 0;" |
| 21531 | "var exception2Calls = 0;" ); |
| 21532 | isolate->EnqueueMicrotask( |
| 21533 | Function::New(env.local(), MicrotaskExceptionOne).ToLocalChecked()); |
| 21534 | isolate->EnqueueMicrotask( |
| 21535 | Function::New(env.local(), MicrotaskExceptionTwo).ToLocalChecked()); |
| 21536 | TryCatch try_catch(isolate); |
| 21537 | CompileRun("1+1;" ); |
| 21538 | CHECK(!try_catch.HasCaught()); |
| 21539 | CHECK_EQ(1, |
| 21540 | CompileRun("exception1Calls" )->Int32Value(env.local()).FromJust()); |
| 21541 | CHECK_EQ(1, |
| 21542 | CompileRun("exception2Calls" )->Int32Value(env.local()).FromJust()); |
| 21543 | } |
| 21544 | |
| 21545 | static void ThrowExceptionMicrotask(void* data) { |
| 21546 | CcTest::isolate()->ThrowException(v8_str("exception" )); |
| 21547 | } |
| 21548 | |
| 21549 | int microtask_callback_count = 0; |
| 21550 | |
| 21551 | static void IncrementCounterMicrotask(void* data) { |
| 21552 | microtask_callback_count++; |
| 21553 | } |
| 21554 | |
| 21555 | TEST(RunMicrotasksIgnoresThrownExceptionsFromApi) { |
| 21556 | LocalContext env; |
| 21557 | v8::Isolate* isolate = CcTest::isolate(); |
| 21558 | isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); |
| 21559 | v8::HandleScope scope(isolate); |
| 21560 | v8::TryCatch try_catch(isolate); |
| 21561 | { |
| 21562 | CHECK(!isolate->IsExecutionTerminating()); |
| 21563 | isolate->EnqueueMicrotask(ThrowExceptionMicrotask); |
| 21564 | isolate->EnqueueMicrotask(IncrementCounterMicrotask); |
| 21565 | isolate->RunMicrotasks(); |
| 21566 | CHECK_EQ(1, microtask_callback_count); |
| 21567 | CHECK(!try_catch.HasCaught()); |
| 21568 | } |
| 21569 | } |
| 21570 | |
| 21571 | uint8_t microtasks_completed_callback_count = 0; |
| 21572 | |
| 21573 | static void MicrotasksCompletedCallback(v8::Isolate* isolate, void*) { |
| 21574 | ++microtasks_completed_callback_count; |
| 21575 | } |
| 21576 | |
| 21577 | TEST(SetAutorunMicrotasks) { |
| 21578 | LocalContext env; |
| 21579 | v8::HandleScope scope(env->GetIsolate()); |
| 21580 | env->GetIsolate()->AddMicrotasksCompletedCallback( |
| 21581 | &MicrotasksCompletedCallback); |
| 21582 | CompileRun( |
| 21583 | "var ext1Calls = 0;" |
| 21584 | "var ext2Calls = 0;" ); |
| 21585 | CompileRun("1+1;" ); |
| 21586 | CHECK_EQ(0, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21587 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21588 | CHECK_EQ(0u, microtasks_completed_callback_count); |
| 21589 | |
| 21590 | env->GetIsolate()->EnqueueMicrotask( |
| 21591 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21592 | CompileRun("1+1;" ); |
| 21593 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21594 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21595 | CHECK_EQ(1u, microtasks_completed_callback_count); |
| 21596 | |
| 21597 | env->GetIsolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); |
| 21598 | env->GetIsolate()->EnqueueMicrotask( |
| 21599 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21600 | env->GetIsolate()->EnqueueMicrotask( |
| 21601 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21602 | CompileRun("1+1;" ); |
| 21603 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21604 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21605 | CHECK_EQ(1u, microtasks_completed_callback_count); |
| 21606 | |
| 21607 | env->GetIsolate()->RunMicrotasks(); |
| 21608 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21609 | CHECK_EQ(1, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21610 | CHECK_EQ(2u, microtasks_completed_callback_count); |
| 21611 | |
| 21612 | env->GetIsolate()->EnqueueMicrotask( |
| 21613 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21614 | CompileRun("1+1;" ); |
| 21615 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21616 | CHECK_EQ(1, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21617 | CHECK_EQ(2u, microtasks_completed_callback_count); |
| 21618 | |
| 21619 | env->GetIsolate()->RunMicrotasks(); |
| 21620 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21621 | CHECK_EQ(2, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21622 | CHECK_EQ(3u, microtasks_completed_callback_count); |
| 21623 | |
| 21624 | env->GetIsolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kAuto); |
| 21625 | env->GetIsolate()->EnqueueMicrotask( |
| 21626 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21627 | CompileRun("1+1;" ); |
| 21628 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21629 | CHECK_EQ(3, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21630 | CHECK_EQ(4u, microtasks_completed_callback_count); |
| 21631 | |
| 21632 | env->GetIsolate()->EnqueueMicrotask( |
| 21633 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21634 | { |
| 21635 | v8::Isolate::SuppressMicrotaskExecutionScope scope(env->GetIsolate()); |
| 21636 | CompileRun("1+1;" ); |
| 21637 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21638 | CHECK_EQ(3, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21639 | CHECK_EQ(4u, microtasks_completed_callback_count); |
| 21640 | } |
| 21641 | |
| 21642 | CompileRun("1+1;" ); |
| 21643 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21644 | CHECK_EQ(4, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21645 | CHECK_EQ(5u, microtasks_completed_callback_count); |
| 21646 | |
| 21647 | env->GetIsolate()->RemoveMicrotasksCompletedCallback( |
| 21648 | &MicrotasksCompletedCallback); |
| 21649 | env->GetIsolate()->EnqueueMicrotask( |
| 21650 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21651 | CompileRun("1+1;" ); |
| 21652 | CHECK_EQ(3, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21653 | CHECK_EQ(4, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21654 | CHECK_EQ(5u, microtasks_completed_callback_count); |
| 21655 | } |
| 21656 | |
| 21657 | |
| 21658 | TEST(RunMicrotasksWithoutEnteringContext) { |
| 21659 | v8::Isolate* isolate = CcTest::isolate(); |
| 21660 | HandleScope handle_scope(isolate); |
| 21661 | isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); |
| 21662 | Local<Context> context = Context::New(isolate); |
| 21663 | { |
| 21664 | Context::Scope context_scope(context); |
| 21665 | CompileRun("var ext1Calls = 0;" ); |
| 21666 | isolate->EnqueueMicrotask( |
| 21667 | Function::New(context, MicrotaskOne).ToLocalChecked()); |
| 21668 | } |
| 21669 | isolate->RunMicrotasks(); |
| 21670 | { |
| 21671 | Context::Scope context_scope(context); |
| 21672 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(context).FromJust()); |
| 21673 | } |
| 21674 | isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kAuto); |
| 21675 | } |
| 21676 | |
| 21677 | static void Regress808911_MicrotaskCallback(void* data) { |
| 21678 | // So here we expect "current context" to be context1 and |
| 21679 | // "entered or microtask context" to be context2. |
| 21680 | v8::Isolate* isolate = static_cast<v8::Isolate*>(data); |
| 21681 | CHECK(isolate->GetCurrentContext() != |
| 21682 | isolate->GetEnteredOrMicrotaskContext()); |
| 21683 | } |
| 21684 | |
| 21685 | static void Regress808911_CurrentContextWrapper( |
| 21686 | const v8::FunctionCallbackInfo<Value>& info) { |
| 21687 | // So here we expect "current context" to be context1 and |
| 21688 | // "entered or microtask context" to be context2. |
| 21689 | v8::Isolate* isolate = info.GetIsolate(); |
| 21690 | CHECK(isolate->GetCurrentContext() != |
| 21691 | isolate->GetEnteredOrMicrotaskContext()); |
| 21692 | isolate->EnqueueMicrotask(Regress808911_MicrotaskCallback, isolate); |
| 21693 | isolate->RunMicrotasks(); |
| 21694 | } |
| 21695 | |
| 21696 | THREADED_TEST(Regress808911) { |
| 21697 | v8::Isolate* isolate = CcTest::isolate(); |
| 21698 | HandleScope handle_scope(isolate); |
| 21699 | Local<Context> context1 = Context::New(isolate); |
| 21700 | Local<Function> function; |
| 21701 | { |
| 21702 | Context::Scope context_scope(context1); |
| 21703 | function = Function::New(context1, Regress808911_CurrentContextWrapper) |
| 21704 | .ToLocalChecked(); |
| 21705 | } |
| 21706 | Local<Context> context2 = Context::New(isolate); |
| 21707 | Context::Scope context_scope(context2); |
| 21708 | function->CallAsFunction(context2, v8::Undefined(isolate), 0, nullptr) |
| 21709 | .ToLocalChecked(); |
| 21710 | } |
| 21711 | |
| 21712 | TEST(ScopedMicrotasks) { |
| 21713 | LocalContext env; |
| 21714 | v8::HandleScope handles(env->GetIsolate()); |
| 21715 | env->GetIsolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped); |
| 21716 | { |
| 21717 | v8::MicrotasksScope scope1(env->GetIsolate(), |
| 21718 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21719 | env->GetIsolate()->EnqueueMicrotask( |
| 21720 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21721 | CompileRun( |
| 21722 | "var ext1Calls = 0;" |
| 21723 | "var ext2Calls = 0;" ); |
| 21724 | CompileRun("1+1;" ); |
| 21725 | CHECK_EQ(0, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21726 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21727 | { |
| 21728 | v8::MicrotasksScope scope2(env->GetIsolate(), |
| 21729 | v8::MicrotasksScope::kRunMicrotasks); |
| 21730 | CompileRun("1+1;" ); |
| 21731 | CHECK_EQ(0, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21732 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21733 | { |
| 21734 | v8::MicrotasksScope scope3(env->GetIsolate(), |
| 21735 | v8::MicrotasksScope::kRunMicrotasks); |
| 21736 | CompileRun("1+1;" ); |
| 21737 | CHECK_EQ(0, |
| 21738 | CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21739 | CHECK_EQ(0, |
| 21740 | CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21741 | } |
| 21742 | CHECK_EQ(0, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21743 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21744 | } |
| 21745 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21746 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21747 | env->GetIsolate()->EnqueueMicrotask( |
| 21748 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21749 | } |
| 21750 | |
| 21751 | { |
| 21752 | v8::MicrotasksScope scope(env->GetIsolate(), |
| 21753 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21754 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21755 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21756 | } |
| 21757 | |
| 21758 | { |
| 21759 | v8::MicrotasksScope scope1(env->GetIsolate(), |
| 21760 | v8::MicrotasksScope::kRunMicrotasks); |
| 21761 | CompileRun("1+1;" ); |
| 21762 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21763 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21764 | { |
| 21765 | v8::MicrotasksScope scope2(env->GetIsolate(), |
| 21766 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21767 | } |
| 21768 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21769 | CHECK_EQ(0, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21770 | } |
| 21771 | |
| 21772 | { |
| 21773 | v8::MicrotasksScope scope(env->GetIsolate(), |
| 21774 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21775 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21776 | CHECK_EQ(1, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21777 | env->GetIsolate()->EnqueueMicrotask( |
| 21778 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21779 | } |
| 21780 | |
| 21781 | { |
| 21782 | v8::Isolate::SuppressMicrotaskExecutionScope scope1(env->GetIsolate()); |
| 21783 | { |
| 21784 | v8::MicrotasksScope scope2(env->GetIsolate(), |
| 21785 | v8::MicrotasksScope::kRunMicrotasks); |
| 21786 | } |
| 21787 | v8::MicrotasksScope scope3(env->GetIsolate(), |
| 21788 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21789 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21790 | CHECK_EQ(1, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21791 | } |
| 21792 | |
| 21793 | { |
| 21794 | v8::MicrotasksScope scope1(env->GetIsolate(), |
| 21795 | v8::MicrotasksScope::kRunMicrotasks); |
| 21796 | v8::MicrotasksScope::PerformCheckpoint(env->GetIsolate()); |
| 21797 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21798 | CHECK_EQ(1, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21799 | } |
| 21800 | |
| 21801 | { |
| 21802 | v8::MicrotasksScope scope(env->GetIsolate(), |
| 21803 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21804 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21805 | CHECK_EQ(2, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21806 | } |
| 21807 | |
| 21808 | v8::MicrotasksScope::PerformCheckpoint(env->GetIsolate()); |
| 21809 | |
| 21810 | { |
| 21811 | v8::MicrotasksScope scope(env->GetIsolate(), |
| 21812 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21813 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21814 | CHECK_EQ(2, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21815 | env->GetIsolate()->EnqueueMicrotask( |
| 21816 | Function::New(env.local(), MicrotaskTwo).ToLocalChecked()); |
| 21817 | } |
| 21818 | |
| 21819 | v8::MicrotasksScope::PerformCheckpoint(env->GetIsolate()); |
| 21820 | |
| 21821 | { |
| 21822 | v8::MicrotasksScope scope(env->GetIsolate(), |
| 21823 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21824 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21825 | CHECK_EQ(3, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21826 | } |
| 21827 | |
| 21828 | env->GetIsolate()->EnqueueMicrotask( |
| 21829 | Function::New(env.local(), MicrotaskOne).ToLocalChecked()); |
| 21830 | { |
| 21831 | v8::Isolate::SuppressMicrotaskExecutionScope scope1(env->GetIsolate()); |
| 21832 | v8::MicrotasksScope::PerformCheckpoint(env->GetIsolate()); |
| 21833 | v8::MicrotasksScope scope2(env->GetIsolate(), |
| 21834 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21835 | CHECK_EQ(1, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21836 | CHECK_EQ(3, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21837 | } |
| 21838 | |
| 21839 | v8::MicrotasksScope::PerformCheckpoint(env->GetIsolate()); |
| 21840 | |
| 21841 | { |
| 21842 | v8::MicrotasksScope scope(env->GetIsolate(), |
| 21843 | v8::MicrotasksScope::kDoNotRunMicrotasks); |
| 21844 | CHECK_EQ(2, CompileRun("ext1Calls" )->Int32Value(env.local()).FromJust()); |
| 21845 | CHECK_EQ(3, CompileRun("ext2Calls" )->Int32Value(env.local()).FromJust()); |
| 21846 | } |
| 21847 | |
| 21848 | env->GetIsolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kAuto); |
| 21849 | } |
| 21850 | |
| 21851 | namespace { |
| 21852 | |
| 21853 | void AssertCowElements(bool expected, const char* source) { |
| 21854 | Local<Value> object = CompileRun(source); |
| 21855 | i::Handle<i::JSObject> array = |
| 21856 | i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*object.As<Object>())); |
| 21857 | CHECK_EQ(expected, array->elements()->IsCowArray()); |
| 21858 | } |
| 21859 | |
| 21860 | } // namespace |
| 21861 | |
| 21862 | TEST(CheckCOWArraysCreatedRuntimeCounter) { |
| 21863 | LocalContext env; |
| 21864 | v8::HandleScope scope(env->GetIsolate()); |
| 21865 | AssertCowElements(true, "[1, 2, 3]" ); |
| 21866 | AssertCowElements(false, "[[1], 2, 3]" ); |
| 21867 | AssertCowElements(true, "[[1], 2, 3][0]" ); |
| 21868 | AssertCowElements(true, "({foo: [4, 5, 6], bar: [3, 0]}.foo)" ); |
| 21869 | AssertCowElements(true, "({foo: [4, 5, 6], bar: [3, 0]}.bar)" ); |
| 21870 | AssertCowElements(false, "({foo: [1, 2, 3, [4, 5, 6]], bar: 'hi'}.foo)" ); |
| 21871 | AssertCowElements(true, "({foo: [1, 2, 3, [4, 5, 6]], bar: 'hi'}.foo[3])" ); |
| 21872 | } |
| 21873 | |
| 21874 | |
| 21875 | TEST(StaticGetters) { |
| 21876 | LocalContext context; |
| 21877 | i::Factory* factory = CcTest::i_isolate()->factory(); |
| 21878 | v8::Isolate* isolate = CcTest::isolate(); |
| 21879 | v8::HandleScope scope(isolate); |
| 21880 | i::Handle<i::Object> undefined_value = factory->undefined_value(); |
| 21881 | CHECK(*v8::Utils::OpenHandle(*v8::Undefined(isolate)) == *undefined_value); |
| 21882 | i::Handle<i::Object> null_value = factory->null_value(); |
| 21883 | CHECK(*v8::Utils::OpenHandle(*v8::Null(isolate)) == *null_value); |
| 21884 | i::Handle<i::Object> true_value = factory->true_value(); |
| 21885 | CHECK(*v8::Utils::OpenHandle(*v8::True(isolate)) == *true_value); |
| 21886 | i::Handle<i::Object> false_value = factory->false_value(); |
| 21887 | CHECK(*v8::Utils::OpenHandle(*v8::False(isolate)) == *false_value); |
| 21888 | } |
| 21889 | |
| 21890 | |
| 21891 | UNINITIALIZED_TEST(IsolateEmbedderData) { |
| 21892 | CcTest::DisableAutomaticDispose(); |
| 21893 | v8::Isolate::CreateParams create_params; |
| 21894 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 21895 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 21896 | isolate->Enter(); |
| 21897 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 21898 | for (uint32_t slot = 0; slot < v8::Isolate::GetNumberOfDataSlots(); ++slot) { |
| 21899 | CHECK(!isolate->GetData(slot)); |
| 21900 | CHECK(!i_isolate->GetData(slot)); |
| 21901 | } |
| 21902 | for (uint32_t slot = 0; slot < v8::Isolate::GetNumberOfDataSlots(); ++slot) { |
| 21903 | void* data = reinterpret_cast<void*>(0xACCE55ED + slot); |
| 21904 | isolate->SetData(slot, data); |
| 21905 | } |
| 21906 | for (uint32_t slot = 0; slot < v8::Isolate::GetNumberOfDataSlots(); ++slot) { |
| 21907 | void* data = reinterpret_cast<void*>(0xACCE55ED + slot); |
| 21908 | CHECK_EQ(data, isolate->GetData(slot)); |
| 21909 | CHECK_EQ(data, i_isolate->GetData(slot)); |
| 21910 | } |
| 21911 | for (uint32_t slot = 0; slot < v8::Isolate::GetNumberOfDataSlots(); ++slot) { |
| 21912 | void* data = reinterpret_cast<void*>(0xDECEA5ED + slot); |
| 21913 | isolate->SetData(slot, data); |
| 21914 | } |
| 21915 | for (uint32_t slot = 0; slot < v8::Isolate::GetNumberOfDataSlots(); ++slot) { |
| 21916 | void* data = reinterpret_cast<void*>(0xDECEA5ED + slot); |
| 21917 | CHECK_EQ(data, isolate->GetData(slot)); |
| 21918 | CHECK_EQ(data, i_isolate->GetData(slot)); |
| 21919 | } |
| 21920 | isolate->Exit(); |
| 21921 | isolate->Dispose(); |
| 21922 | } |
| 21923 | |
| 21924 | |
| 21925 | TEST(StringEmpty) { |
| 21926 | LocalContext context; |
| 21927 | i::Factory* factory = CcTest::i_isolate()->factory(); |
| 21928 | v8::Isolate* isolate = CcTest::isolate(); |
| 21929 | v8::HandleScope scope(isolate); |
| 21930 | i::Handle<i::Object> empty_string = factory->empty_string(); |
| 21931 | CHECK(*v8::Utils::OpenHandle(*v8::String::Empty(isolate)) == *empty_string); |
| 21932 | } |
| 21933 | |
| 21934 | |
| 21935 | static int instance_checked_getter_count = 0; |
| 21936 | static void InstanceCheckedGetter( |
| 21937 | Local<String> name, |
| 21938 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 21939 | CHECK(name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo" )) |
| 21940 | .FromJust()); |
| 21941 | instance_checked_getter_count++; |
| 21942 | info.GetReturnValue().Set(v8_num(11)); |
| 21943 | } |
| 21944 | |
| 21945 | |
| 21946 | static int instance_checked_setter_count = 0; |
| 21947 | static void InstanceCheckedSetter(Local<String> name, |
| 21948 | Local<Value> value, |
| 21949 | const v8::PropertyCallbackInfo<void>& info) { |
| 21950 | CHECK(name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo" )) |
| 21951 | .FromJust()); |
| 21952 | CHECK(value->Equals(info.GetIsolate()->GetCurrentContext(), v8_num(23)) |
| 21953 | .FromJust()); |
| 21954 | instance_checked_setter_count++; |
| 21955 | } |
| 21956 | |
| 21957 | |
| 21958 | static void CheckInstanceCheckedResult(int getters, int setters, |
| 21959 | bool expects_callbacks, |
| 21960 | TryCatch* try_catch) { |
| 21961 | if (expects_callbacks) { |
| 21962 | CHECK(!try_catch->HasCaught()); |
| 21963 | CHECK_EQ(getters, instance_checked_getter_count); |
| 21964 | CHECK_EQ(setters, instance_checked_setter_count); |
| 21965 | } else { |
| 21966 | CHECK(try_catch->HasCaught()); |
| 21967 | CHECK_EQ(0, instance_checked_getter_count); |
| 21968 | CHECK_EQ(0, instance_checked_setter_count); |
| 21969 | } |
| 21970 | try_catch->Reset(); |
| 21971 | } |
| 21972 | |
| 21973 | |
| 21974 | static void CheckInstanceCheckedAccessors(bool expects_callbacks) { |
| 21975 | instance_checked_getter_count = 0; |
| 21976 | instance_checked_setter_count = 0; |
| 21977 | TryCatch try_catch(CcTest::isolate()); |
| 21978 | |
| 21979 | // Test path through generic runtime code. |
| 21980 | CompileRun("obj.foo" ); |
| 21981 | CheckInstanceCheckedResult(1, 0, expects_callbacks, &try_catch); |
| 21982 | CompileRun("obj.foo = 23" ); |
| 21983 | CheckInstanceCheckedResult(1, 1, expects_callbacks, &try_catch); |
| 21984 | |
| 21985 | // Test path through generated LoadIC and StoredIC. |
| 21986 | CompileRun("function test_get(o) { o.foo; }" |
| 21987 | "test_get(obj);" ); |
| 21988 | CheckInstanceCheckedResult(2, 1, expects_callbacks, &try_catch); |
| 21989 | CompileRun("test_get(obj);" ); |
| 21990 | CheckInstanceCheckedResult(3, 1, expects_callbacks, &try_catch); |
| 21991 | CompileRun("test_get(obj);" ); |
| 21992 | CheckInstanceCheckedResult(4, 1, expects_callbacks, &try_catch); |
| 21993 | CompileRun("function test_set(o) { o.foo = 23; }" |
| 21994 | "test_set(obj);" ); |
| 21995 | CheckInstanceCheckedResult(4, 2, expects_callbacks, &try_catch); |
| 21996 | CompileRun("test_set(obj);" ); |
| 21997 | CheckInstanceCheckedResult(4, 3, expects_callbacks, &try_catch); |
| 21998 | CompileRun("test_set(obj);" ); |
| 21999 | CheckInstanceCheckedResult(4, 4, expects_callbacks, &try_catch); |
| 22000 | |
| 22001 | // Test path through optimized code. |
| 22002 | CompileRun("%OptimizeFunctionOnNextCall(test_get);" |
| 22003 | "test_get(obj);" ); |
| 22004 | CheckInstanceCheckedResult(5, 4, expects_callbacks, &try_catch); |
| 22005 | CompileRun("%OptimizeFunctionOnNextCall(test_set);" |
| 22006 | "test_set(obj);" ); |
| 22007 | CheckInstanceCheckedResult(5, 5, expects_callbacks, &try_catch); |
| 22008 | |
| 22009 | // Cleanup so that closures start out fresh in next check. |
| 22010 | CompileRun( |
| 22011 | "%DeoptimizeFunction(test_get);" |
| 22012 | "%ClearFunctionFeedback(test_get);" |
| 22013 | "%DeoptimizeFunction(test_set);" |
| 22014 | "%ClearFunctionFeedback(test_set);" ); |
| 22015 | } |
| 22016 | |
| 22017 | |
| 22018 | THREADED_TEST(InstanceCheckOnInstanceAccessor) { |
| 22019 | v8::internal::FLAG_allow_natives_syntax = true; |
| 22020 | LocalContext context; |
| 22021 | v8::HandleScope scope(context->GetIsolate()); |
| 22022 | |
| 22023 | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| 22024 | Local<ObjectTemplate> inst = templ->InstanceTemplate(); |
| 22025 | inst->SetAccessor(v8_str("foo" ), InstanceCheckedGetter, InstanceCheckedSetter, |
| 22026 | Local<Value>(), v8::DEFAULT, v8::None, |
| 22027 | v8::AccessorSignature::New(context->GetIsolate(), templ)); |
| 22028 | CHECK(context->Global() |
| 22029 | ->Set(context.local(), v8_str("f" ), |
| 22030 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 22031 | .FromJust()); |
| 22032 | |
| 22033 | printf("Testing positive ...\n" ); |
| 22034 | CompileRun("var obj = new f();" ); |
| 22035 | CHECK(templ->HasInstance( |
| 22036 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22037 | CheckInstanceCheckedAccessors(true); |
| 22038 | |
| 22039 | printf("Testing negative ...\n" ); |
| 22040 | CompileRun("var obj = {};" |
| 22041 | "obj.__proto__ = new f();" ); |
| 22042 | CHECK(!templ->HasInstance( |
| 22043 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22044 | CheckInstanceCheckedAccessors(false); |
| 22045 | } |
| 22046 | |
| 22047 | static void EmptyInterceptorGetter( |
| 22048 | Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {} |
| 22049 | |
| 22050 | static void EmptyInterceptorSetter( |
| 22051 | Local<Name> name, Local<Value> value, |
| 22052 | const v8::PropertyCallbackInfo<v8::Value>& info) {} |
| 22053 | |
| 22054 | THREADED_TEST(InstanceCheckOnInstanceAccessorWithInterceptor) { |
| 22055 | v8::internal::FLAG_allow_natives_syntax = true; |
| 22056 | LocalContext context; |
| 22057 | v8::HandleScope scope(context->GetIsolate()); |
| 22058 | |
| 22059 | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| 22060 | Local<ObjectTemplate> inst = templ->InstanceTemplate(); |
| 22061 | templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| 22062 | EmptyInterceptorGetter, EmptyInterceptorSetter)); |
| 22063 | inst->SetAccessor(v8_str("foo" ), InstanceCheckedGetter, InstanceCheckedSetter, |
| 22064 | Local<Value>(), v8::DEFAULT, v8::None, |
| 22065 | v8::AccessorSignature::New(context->GetIsolate(), templ)); |
| 22066 | CHECK(context->Global() |
| 22067 | ->Set(context.local(), v8_str("f" ), |
| 22068 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 22069 | .FromJust()); |
| 22070 | |
| 22071 | printf("Testing positive ...\n" ); |
| 22072 | CompileRun("var obj = new f();" ); |
| 22073 | CHECK(templ->HasInstance( |
| 22074 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22075 | CheckInstanceCheckedAccessors(true); |
| 22076 | |
| 22077 | printf("Testing negative ...\n" ); |
| 22078 | CompileRun("var obj = {};" |
| 22079 | "obj.__proto__ = new f();" ); |
| 22080 | CHECK(!templ->HasInstance( |
| 22081 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22082 | CheckInstanceCheckedAccessors(false); |
| 22083 | } |
| 22084 | |
| 22085 | |
| 22086 | THREADED_TEST(InstanceCheckOnPrototypeAccessor) { |
| 22087 | v8::internal::FLAG_allow_natives_syntax = true; |
| 22088 | LocalContext context; |
| 22089 | v8::HandleScope scope(context->GetIsolate()); |
| 22090 | |
| 22091 | Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate()); |
| 22092 | Local<ObjectTemplate> proto = templ->PrototypeTemplate(); |
| 22093 | proto->SetAccessor(v8_str("foo" ), InstanceCheckedGetter, |
| 22094 | InstanceCheckedSetter, Local<Value>(), v8::DEFAULT, |
| 22095 | v8::None, |
| 22096 | v8::AccessorSignature::New(context->GetIsolate(), templ)); |
| 22097 | CHECK(context->Global() |
| 22098 | ->Set(context.local(), v8_str("f" ), |
| 22099 | templ->GetFunction(context.local()).ToLocalChecked()) |
| 22100 | .FromJust()); |
| 22101 | |
| 22102 | printf("Testing positive ...\n" ); |
| 22103 | CompileRun("var obj = new f();" ); |
| 22104 | CHECK(templ->HasInstance( |
| 22105 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22106 | CheckInstanceCheckedAccessors(true); |
| 22107 | |
| 22108 | printf("Testing negative ...\n" ); |
| 22109 | CompileRun("var obj = {};" |
| 22110 | "obj.__proto__ = new f();" ); |
| 22111 | CHECK(!templ->HasInstance( |
| 22112 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22113 | CheckInstanceCheckedAccessors(false); |
| 22114 | |
| 22115 | printf("Testing positive with modified prototype chain ...\n" ); |
| 22116 | CompileRun("var obj = new f();" |
| 22117 | "var pro = {};" |
| 22118 | "pro.__proto__ = obj.__proto__;" |
| 22119 | "obj.__proto__ = pro;" ); |
| 22120 | CHECK(templ->HasInstance( |
| 22121 | context->Global()->Get(context.local(), v8_str("obj" )).ToLocalChecked())); |
| 22122 | CheckInstanceCheckedAccessors(true); |
| 22123 | } |
| 22124 | |
| 22125 | |
| 22126 | TEST(TryFinallyMessage) { |
| 22127 | LocalContext context; |
| 22128 | v8::HandleScope scope(context->GetIsolate()); |
| 22129 | { |
| 22130 | // Test that the original error message is not lost if there is a |
| 22131 | // recursive call into Javascript is done in the finally block, e.g. to |
| 22132 | // initialize an IC. (crbug.com/129171) |
| 22133 | TryCatch try_catch(context->GetIsolate()); |
| 22134 | const char* trigger_ic = |
| 22135 | "try { \n" |
| 22136 | " throw new Error('test'); \n" |
| 22137 | "} finally { \n" |
| 22138 | " var x = 0; \n" |
| 22139 | " x++; \n" // Trigger an IC initialization here. |
| 22140 | "} \n" ; |
| 22141 | CompileRun(trigger_ic); |
| 22142 | CHECK(try_catch.HasCaught()); |
| 22143 | Local<Message> message = try_catch.Message(); |
| 22144 | CHECK(!message.IsEmpty()); |
| 22145 | CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust()); |
| 22146 | } |
| 22147 | |
| 22148 | { |
| 22149 | // Test that the original exception message is indeed overwritten if |
| 22150 | // a new error is thrown in the finally block. |
| 22151 | TryCatch try_catch(context->GetIsolate()); |
| 22152 | const char* throw_again = |
| 22153 | "try { \n" |
| 22154 | " throw new Error('test'); \n" |
| 22155 | "} finally { \n" |
| 22156 | " var x = 0; \n" |
| 22157 | " x++; \n" |
| 22158 | " throw new Error('again'); \n" // This is the new uncaught error. |
| 22159 | "} \n" ; |
| 22160 | CompileRun(throw_again); |
| 22161 | CHECK(try_catch.HasCaught()); |
| 22162 | Local<Message> message = try_catch.Message(); |
| 22163 | CHECK(!message.IsEmpty()); |
| 22164 | CHECK_EQ(6, message->GetLineNumber(context.local()).FromJust()); |
| 22165 | } |
| 22166 | } |
| 22167 | |
| 22168 | |
| 22169 | static void Helper137002(bool do_store, |
| 22170 | bool polymorphic, |
| 22171 | bool remove_accessor, |
| 22172 | bool interceptor) { |
| 22173 | LocalContext context; |
| 22174 | Local<ObjectTemplate> templ = ObjectTemplate::New(context->GetIsolate()); |
| 22175 | if (interceptor) { |
| 22176 | templ->SetHandler(v8::NamedPropertyHandlerConfiguration(FooGetInterceptor, |
| 22177 | FooSetInterceptor)); |
| 22178 | } else { |
| 22179 | templ->SetAccessor(v8_str("foo" ), |
| 22180 | GetterWhichReturns42, |
| 22181 | SetterWhichSetsYOnThisTo23); |
| 22182 | } |
| 22183 | CHECK(context->Global() |
| 22184 | ->Set(context.local(), v8_str("obj" ), |
| 22185 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 22186 | .FromJust()); |
| 22187 | |
| 22188 | // Turn monomorphic on slow object with native accessor, then turn |
| 22189 | // polymorphic, finally optimize to create negative lookup and fail. |
| 22190 | CompileRun(do_store ? |
| 22191 | "function f(x) { x.foo = void 0; }" : |
| 22192 | "function f(x) { return x.foo; }" ); |
| 22193 | CompileRun("obj.y = void 0;" ); |
| 22194 | if (!interceptor) { |
| 22195 | CompileRun("%OptimizeObjectForAddingMultipleProperties(obj, 1);" ); |
| 22196 | } |
| 22197 | CompileRun("obj.__proto__ = null;" |
| 22198 | "f(obj); f(obj); f(obj);" ); |
| 22199 | if (polymorphic) { |
| 22200 | CompileRun("f({});" ); |
| 22201 | } |
| 22202 | CompileRun("obj.y = void 0;" |
| 22203 | "%OptimizeFunctionOnNextCall(f);" ); |
| 22204 | if (remove_accessor) { |
| 22205 | CompileRun("delete obj.foo;" ); |
| 22206 | } |
| 22207 | CompileRun("var result = f(obj);" ); |
| 22208 | if (do_store) { |
| 22209 | CompileRun("result = obj.y;" ); |
| 22210 | } |
| 22211 | if (remove_accessor && !interceptor) { |
| 22212 | CHECK(context->Global() |
| 22213 | ->Get(context.local(), v8_str("result" )) |
| 22214 | .ToLocalChecked() |
| 22215 | ->IsUndefined()); |
| 22216 | } else { |
| 22217 | CHECK_EQ(do_store ? 23 : 42, context->Global() |
| 22218 | ->Get(context.local(), v8_str("result" )) |
| 22219 | .ToLocalChecked() |
| 22220 | ->Int32Value(context.local()) |
| 22221 | .FromJust()); |
| 22222 | } |
| 22223 | } |
| 22224 | |
| 22225 | |
| 22226 | THREADED_TEST(Regress137002a) { |
| 22227 | i::FLAG_allow_natives_syntax = true; |
| 22228 | i::FLAG_compilation_cache = false; |
| 22229 | v8::HandleScope scope(CcTest::isolate()); |
| 22230 | for (int i = 0; i < 16; i++) { |
| 22231 | Helper137002(i & 8, i & 4, i & 2, i & 1); |
| 22232 | } |
| 22233 | } |
| 22234 | |
| 22235 | |
| 22236 | THREADED_TEST(Regress137002b) { |
| 22237 | i::FLAG_allow_natives_syntax = true; |
| 22238 | LocalContext context; |
| 22239 | v8::Isolate* isolate = context->GetIsolate(); |
| 22240 | v8::HandleScope scope(isolate); |
| 22241 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 22242 | templ->SetAccessor(v8_str("foo" ), |
| 22243 | GetterWhichReturns42, |
| 22244 | SetterWhichSetsYOnThisTo23); |
| 22245 | CHECK(context->Global() |
| 22246 | ->Set(context.local(), v8_str("obj" ), |
| 22247 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 22248 | .FromJust()); |
| 22249 | |
| 22250 | // Turn monomorphic on slow object with native accessor, then just |
| 22251 | // delete the property and fail. |
| 22252 | CompileRun("function load(x) { return x.foo; }" |
| 22253 | "function store(x) { x.foo = void 0; }" |
| 22254 | "function keyed_load(x, key) { return x[key]; }" |
| 22255 | // Second version of function has a different source (add void 0) |
| 22256 | // so that it does not share code with the first version. This |
| 22257 | // ensures that the ICs are monomorphic. |
| 22258 | "function load2(x) { void 0; return x.foo; }" |
| 22259 | "function store2(x) { void 0; x.foo = void 0; }" |
| 22260 | "function keyed_load2(x, key) { void 0; return x[key]; }" |
| 22261 | |
| 22262 | "obj.y = void 0;" |
| 22263 | "obj.__proto__ = null;" |
| 22264 | "var subobj = {};" |
| 22265 | "subobj.y = void 0;" |
| 22266 | "subobj.__proto__ = obj;" |
| 22267 | "%OptimizeObjectForAddingMultipleProperties(obj, 1);" |
| 22268 | |
| 22269 | // Make the ICs monomorphic. |
| 22270 | "load(obj); load(obj);" |
| 22271 | "load2(subobj); load2(subobj);" |
| 22272 | "store(obj); store(obj);" |
| 22273 | "store2(subobj); store2(subobj);" |
| 22274 | "keyed_load(obj, 'foo'); keyed_load(obj, 'foo');" |
| 22275 | "keyed_load2(subobj, 'foo'); keyed_load2(subobj, 'foo');" |
| 22276 | |
| 22277 | // Actually test the shiny new ICs and better not crash. This |
| 22278 | // serves as a regression test for issue 142088 as well. |
| 22279 | "load(obj);" |
| 22280 | "load2(subobj);" |
| 22281 | "store(obj);" |
| 22282 | "store2(subobj);" |
| 22283 | "keyed_load(obj, 'foo');" |
| 22284 | "keyed_load2(subobj, 'foo');" |
| 22285 | |
| 22286 | // Delete the accessor. It better not be called any more now. |
| 22287 | "delete obj.foo;" |
| 22288 | "obj.y = void 0;" |
| 22289 | "subobj.y = void 0;" |
| 22290 | |
| 22291 | "var load_result = load(obj);" |
| 22292 | "var load_result2 = load2(subobj);" |
| 22293 | "var keyed_load_result = keyed_load(obj, 'foo');" |
| 22294 | "var keyed_load_result2 = keyed_load2(subobj, 'foo');" |
| 22295 | "store(obj);" |
| 22296 | "store2(subobj);" |
| 22297 | "var y_from_obj = obj.y;" |
| 22298 | "var y_from_subobj = subobj.y;" ); |
| 22299 | CHECK(context->Global() |
| 22300 | ->Get(context.local(), v8_str("load_result" )) |
| 22301 | .ToLocalChecked() |
| 22302 | ->IsUndefined()); |
| 22303 | CHECK(context->Global() |
| 22304 | ->Get(context.local(), v8_str("load_result2" )) |
| 22305 | .ToLocalChecked() |
| 22306 | ->IsUndefined()); |
| 22307 | CHECK(context->Global() |
| 22308 | ->Get(context.local(), v8_str("keyed_load_result" )) |
| 22309 | .ToLocalChecked() |
| 22310 | ->IsUndefined()); |
| 22311 | CHECK(context->Global() |
| 22312 | ->Get(context.local(), v8_str("keyed_load_result2" )) |
| 22313 | .ToLocalChecked() |
| 22314 | ->IsUndefined()); |
| 22315 | CHECK(context->Global() |
| 22316 | ->Get(context.local(), v8_str("y_from_obj" )) |
| 22317 | .ToLocalChecked() |
| 22318 | ->IsUndefined()); |
| 22319 | CHECK(context->Global() |
| 22320 | ->Get(context.local(), v8_str("y_from_subobj" )) |
| 22321 | .ToLocalChecked() |
| 22322 | ->IsUndefined()); |
| 22323 | } |
| 22324 | |
| 22325 | |
| 22326 | THREADED_TEST(Regress142088) { |
| 22327 | i::FLAG_allow_natives_syntax = true; |
| 22328 | LocalContext context; |
| 22329 | v8::Isolate* isolate = context->GetIsolate(); |
| 22330 | v8::HandleScope scope(isolate); |
| 22331 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 22332 | templ->SetAccessor(v8_str("foo" ), |
| 22333 | GetterWhichReturns42, |
| 22334 | SetterWhichSetsYOnThisTo23); |
| 22335 | CHECK(context->Global() |
| 22336 | ->Set(context.local(), v8_str("obj" ), |
| 22337 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 22338 | .FromJust()); |
| 22339 | |
| 22340 | CompileRun("function load(x) { return x.foo; }" |
| 22341 | "var o = Object.create(obj);" |
| 22342 | "%OptimizeObjectForAddingMultipleProperties(obj, 1);" |
| 22343 | "load(o); load(o); load(o); load(o);" ); |
| 22344 | } |
| 22345 | |
| 22346 | |
| 22347 | THREADED_TEST(Regress137496) { |
| 22348 | i::FLAG_expose_gc = true; |
| 22349 | LocalContext context; |
| 22350 | v8::HandleScope scope(context->GetIsolate()); |
| 22351 | |
| 22352 | // Compile a try-finally clause where the finally block causes a GC |
| 22353 | // while there still is a message pending for external reporting. |
| 22354 | TryCatch try_catch(context->GetIsolate()); |
| 22355 | try_catch.SetVerbose(true); |
| 22356 | CompileRun("try { throw new Error(); } finally { gc(); }" ); |
| 22357 | CHECK(try_catch.HasCaught()); |
| 22358 | } |
| 22359 | |
| 22360 | |
| 22361 | THREADED_TEST(Regress157124) { |
| 22362 | LocalContext context; |
| 22363 | v8::Isolate* isolate = context->GetIsolate(); |
| 22364 | v8::HandleScope scope(isolate); |
| 22365 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 22366 | Local<Object> obj = templ->NewInstance(context.local()).ToLocalChecked(); |
| 22367 | obj->GetIdentityHash(); |
| 22368 | obj->DeletePrivate(context.local(), |
| 22369 | v8::Private::ForApi(isolate, v8_str("Bug" ))) |
| 22370 | .FromJust(); |
| 22371 | } |
| 22372 | |
| 22373 | |
| 22374 | THREADED_TEST(Regress2535) { |
| 22375 | LocalContext context; |
| 22376 | v8::HandleScope scope(context->GetIsolate()); |
| 22377 | Local<Value> set_value = CompileRun("new Set();" ); |
| 22378 | Local<Object> set_object(Local<Object>::Cast(set_value)); |
| 22379 | CHECK_EQ(0, set_object->InternalFieldCount()); |
| 22380 | Local<Value> map_value = CompileRun("new Map();" ); |
| 22381 | Local<Object> map_object(Local<Object>::Cast(map_value)); |
| 22382 | CHECK_EQ(0, map_object->InternalFieldCount()); |
| 22383 | } |
| 22384 | |
| 22385 | |
| 22386 | THREADED_TEST(Regress2746) { |
| 22387 | LocalContext context; |
| 22388 | v8::Isolate* isolate = context->GetIsolate(); |
| 22389 | v8::HandleScope scope(isolate); |
| 22390 | Local<Object> obj = Object::New(isolate); |
| 22391 | Local<v8::Private> key = v8::Private::New(isolate, v8_str("key" )); |
| 22392 | CHECK( |
| 22393 | obj->SetPrivate(context.local(), key, v8::Undefined(isolate)).FromJust()); |
| 22394 | Local<Value> value = obj->GetPrivate(context.local(), key).ToLocalChecked(); |
| 22395 | CHECK(!value.IsEmpty()); |
| 22396 | CHECK(value->IsUndefined()); |
| 22397 | } |
| 22398 | |
| 22399 | |
| 22400 | THREADED_TEST(Regress260106) { |
| 22401 | LocalContext context; |
| 22402 | v8::Isolate* isolate = context->GetIsolate(); |
| 22403 | v8::HandleScope scope(isolate); |
| 22404 | Local<FunctionTemplate> templ = FunctionTemplate::New(isolate, |
| 22405 | DummyCallHandler); |
| 22406 | CompileRun("for (var i = 0; i < 128; i++) Object.prototype[i] = 0;" ); |
| 22407 | Local<Function> function = |
| 22408 | templ->GetFunction(context.local()).ToLocalChecked(); |
| 22409 | CHECK(!function.IsEmpty()); |
| 22410 | CHECK(function->IsFunction()); |
| 22411 | } |
| 22412 | |
| 22413 | THREADED_TEST(JSONParseObject) { |
| 22414 | LocalContext context; |
| 22415 | HandleScope scope(context->GetIsolate()); |
| 22416 | Local<Value> obj = |
| 22417 | v8::JSON::Parse(context.local(), v8_str("{\"x\":42}" )).ToLocalChecked(); |
| 22418 | Local<Object> global = context->Global(); |
| 22419 | global->Set(context.local(), v8_str("obj" ), obj).FromJust(); |
| 22420 | ExpectString("JSON.stringify(obj)" , "{\"x\":42}" ); |
| 22421 | } |
| 22422 | |
| 22423 | THREADED_TEST(JSONParseNumber) { |
| 22424 | LocalContext context; |
| 22425 | HandleScope scope(context->GetIsolate()); |
| 22426 | Local<Value> obj = |
| 22427 | v8::JSON::Parse(context.local(), v8_str("42" )).ToLocalChecked(); |
| 22428 | Local<Object> global = context->Global(); |
| 22429 | global->Set(context.local(), v8_str("obj" ), obj).FromJust(); |
| 22430 | ExpectString("JSON.stringify(obj)" , "42" ); |
| 22431 | } |
| 22432 | |
| 22433 | namespace { |
| 22434 | void TestJSONParseArray(Local<Context> context, const char* input_str, |
| 22435 | const char* expected_output_str, |
| 22436 | i::ElementsKind expected_elements_kind) { |
| 22437 | Local<Value> obj = |
| 22438 | v8::JSON::Parse(context, v8_str(input_str)).ToLocalChecked(); |
| 22439 | |
| 22440 | i::Handle<i::JSArray> a = |
| 22441 | i::Handle<i::JSArray>::cast(v8::Utils::OpenHandle(*obj)); |
| 22442 | CHECK_EQ(expected_elements_kind, a->GetElementsKind()); |
| 22443 | |
| 22444 | Local<Object> global = context->Global(); |
| 22445 | global->Set(context, v8_str("obj" ), obj).FromJust(); |
| 22446 | ExpectString("JSON.stringify(obj)" , expected_output_str); |
| 22447 | } |
| 22448 | } // namespace |
| 22449 | |
| 22450 | THREADED_TEST(JSONParseArray) { |
| 22451 | LocalContext context; |
| 22452 | HandleScope scope(context->GetIsolate()); |
| 22453 | |
| 22454 | TestJSONParseArray(context.local(), "[0, 1, 2]" , "[0,1,2]" , |
| 22455 | i::PACKED_SMI_ELEMENTS); |
| 22456 | TestJSONParseArray(context.local(), "[0, 1.2, 2]" , "[0,1.2,2]" , |
| 22457 | i::PACKED_DOUBLE_ELEMENTS); |
| 22458 | TestJSONParseArray(context.local(), "[0.2, 1, 2]" , "[0.2,1,2]" , |
| 22459 | i::PACKED_DOUBLE_ELEMENTS); |
| 22460 | TestJSONParseArray(context.local(), "[0, \"a\", 2]" , "[0,\"a\",2]" , |
| 22461 | i::PACKED_ELEMENTS); |
| 22462 | TestJSONParseArray(context.local(), "[\"a\", 1, 2]" , "[\"a\",1,2]" , |
| 22463 | i::PACKED_ELEMENTS); |
| 22464 | TestJSONParseArray(context.local(), "[\"a\", 1.2, 2]" , "[\"a\",1.2,2]" , |
| 22465 | i::PACKED_ELEMENTS); |
| 22466 | TestJSONParseArray(context.local(), "[0, 1.2, \"a\"]" , "[0,1.2,\"a\"]" , |
| 22467 | i::PACKED_ELEMENTS); |
| 22468 | } |
| 22469 | |
| 22470 | THREADED_TEST(JSONStringifyObject) { |
| 22471 | LocalContext context; |
| 22472 | HandleScope scope(context->GetIsolate()); |
| 22473 | Local<Value> value = |
| 22474 | v8::JSON::Parse(context.local(), v8_str("{\"x\":42}" )).ToLocalChecked(); |
| 22475 | Local<Object> obj = value->ToObject(context.local()).ToLocalChecked(); |
| 22476 | Local<Object> global = context->Global(); |
| 22477 | global->Set(context.local(), v8_str("obj" ), obj).FromJust(); |
| 22478 | Local<String> json = |
| 22479 | v8::JSON::Stringify(context.local(), obj).ToLocalChecked(); |
| 22480 | v8::String::Utf8Value utf8(context->GetIsolate(), json); |
| 22481 | ExpectString("JSON.stringify(obj)" , *utf8); |
| 22482 | } |
| 22483 | |
| 22484 | THREADED_TEST(JSONStringifyObjectWithGap) { |
| 22485 | LocalContext context; |
| 22486 | HandleScope scope(context->GetIsolate()); |
| 22487 | Local<Value> value = |
| 22488 | v8::JSON::Parse(context.local(), v8_str("{\"x\":42}" )).ToLocalChecked(); |
| 22489 | Local<Object> obj = value->ToObject(context.local()).ToLocalChecked(); |
| 22490 | Local<Object> global = context->Global(); |
| 22491 | global->Set(context.local(), v8_str("obj" ), obj).FromJust(); |
| 22492 | Local<String> json = |
| 22493 | v8::JSON::Stringify(context.local(), obj, v8_str("*" )).ToLocalChecked(); |
| 22494 | v8::String::Utf8Value utf8(context->GetIsolate(), json); |
| 22495 | ExpectString("JSON.stringify(obj, null, '*')" , *utf8); |
| 22496 | } |
| 22497 | |
| 22498 | #if V8_OS_POSIX |
| 22499 | class ThreadInterruptTest { |
| 22500 | public: |
| 22501 | ThreadInterruptTest() : sem_(0), sem_value_(0) { } |
| 22502 | ~ThreadInterruptTest() = default; |
| 22503 | |
| 22504 | void RunTest() { |
| 22505 | InterruptThread i_thread(this); |
| 22506 | i_thread.Start(); |
| 22507 | |
| 22508 | sem_.Wait(); |
| 22509 | CHECK_EQ(kExpectedValue, sem_value_); |
| 22510 | } |
| 22511 | |
| 22512 | private: |
| 22513 | static const int kExpectedValue = 1; |
| 22514 | |
| 22515 | class InterruptThread : public v8::base::Thread { |
| 22516 | public: |
| 22517 | explicit InterruptThread(ThreadInterruptTest* test) |
| 22518 | : Thread(Options("InterruptThread" )), test_(test) {} |
| 22519 | |
| 22520 | void Run() override { |
| 22521 | struct sigaction action; |
| 22522 | |
| 22523 | // Ensure that we'll enter waiting condition |
| 22524 | v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(100)); |
| 22525 | |
| 22526 | // Setup signal handler |
| 22527 | memset(&action, 0, sizeof(action)); |
| 22528 | action.sa_handler = SignalHandler; |
| 22529 | sigaction(SIGCHLD, &action, nullptr); |
| 22530 | |
| 22531 | // Send signal |
| 22532 | kill(getpid(), SIGCHLD); |
| 22533 | |
| 22534 | // Ensure that if wait has returned because of error |
| 22535 | v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(100)); |
| 22536 | |
| 22537 | // Set value and signal semaphore |
| 22538 | test_->sem_value_ = 1; |
| 22539 | test_->sem_.Signal(); |
| 22540 | } |
| 22541 | |
| 22542 | static void SignalHandler(int signal) { |
| 22543 | } |
| 22544 | |
| 22545 | private: |
| 22546 | ThreadInterruptTest* test_; |
| 22547 | }; |
| 22548 | |
| 22549 | v8::base::Semaphore sem_; |
| 22550 | volatile int sem_value_; |
| 22551 | }; |
| 22552 | |
| 22553 | |
| 22554 | THREADED_TEST(SemaphoreInterruption) { |
| 22555 | ThreadInterruptTest().RunTest(); |
| 22556 | } |
| 22557 | |
| 22558 | |
| 22559 | #endif // V8_OS_POSIX |
| 22560 | |
| 22561 | |
| 22562 | void UnreachableCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 22563 | UNREACHABLE(); |
| 22564 | } |
| 22565 | |
| 22566 | |
| 22567 | TEST(JSONStringifyAccessCheck) { |
| 22568 | v8::V8::Initialize(); |
| 22569 | v8::Isolate* isolate = CcTest::isolate(); |
| 22570 | v8::HandleScope scope(isolate); |
| 22571 | |
| 22572 | // Create an ObjectTemplate for global objects and install access |
| 22573 | // check callbacks that will block access. |
| 22574 | v8::Local<v8::ObjectTemplate> global_template = |
| 22575 | v8::ObjectTemplate::New(isolate); |
| 22576 | global_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 22577 | |
| 22578 | // Create a context and set an x property on it's global object. |
| 22579 | LocalContext context0(nullptr, global_template); |
| 22580 | v8::Local<v8::Object> global0 = context0->Global(); |
| 22581 | global0->Set(context0.local(), v8_str("x" ), v8_num(42)).FromJust(); |
| 22582 | ExpectString("JSON.stringify(this)" , "{\"x\":42}" ); |
| 22583 | |
| 22584 | for (int i = 0; i < 2; i++) { |
| 22585 | if (i == 1) { |
| 22586 | // Install a toJSON function on the second run. |
| 22587 | v8::Local<v8::FunctionTemplate> toJSON = |
| 22588 | v8::FunctionTemplate::New(isolate, UnreachableCallback); |
| 22589 | |
| 22590 | global0->Set(context0.local(), v8_str("toJSON" ), |
| 22591 | toJSON->GetFunction(context0.local()).ToLocalChecked()) |
| 22592 | .FromJust(); |
| 22593 | } |
| 22594 | // Create a context with a different security token so that the |
| 22595 | // failed access check callback will be called on each access. |
| 22596 | LocalContext context1(nullptr, global_template); |
| 22597 | CHECK(context1->Global() |
| 22598 | ->Set(context1.local(), v8_str("other" ), global0) |
| 22599 | .FromJust()); |
| 22600 | |
| 22601 | CHECK(CompileRun("JSON.stringify(other)" ).IsEmpty()); |
| 22602 | CHECK(CompileRun("JSON.stringify({ 'a' : other, 'b' : ['c'] })" ).IsEmpty()); |
| 22603 | CHECK(CompileRun("JSON.stringify([other, 'b', 'c'])" ).IsEmpty()); |
| 22604 | } |
| 22605 | } |
| 22606 | |
| 22607 | |
| 22608 | bool access_check_fail_thrown = false; |
| 22609 | bool catch_callback_called = false; |
| 22610 | |
| 22611 | |
| 22612 | // Failed access check callback that performs a GC on each invocation. |
| 22613 | void FailedAccessCheckThrows(Local<v8::Object> target, |
| 22614 | v8::AccessType type, |
| 22615 | Local<v8::Value> data) { |
| 22616 | access_check_fail_thrown = true; |
| 22617 | i::PrintF("Access check failed. Error thrown.\n" ); |
| 22618 | CcTest::isolate()->ThrowException( |
| 22619 | v8::Exception::Error(v8_str("cross context" ))); |
| 22620 | } |
| 22621 | |
| 22622 | |
| 22623 | void CatcherCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 22624 | for (int i = 0; i < args.Length(); i++) { |
| 22625 | i::PrintF("%s\n" , *String::Utf8Value(args.GetIsolate(), args[i])); |
| 22626 | } |
| 22627 | catch_callback_called = true; |
| 22628 | } |
| 22629 | |
| 22630 | |
| 22631 | void HasOwnPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 22632 | v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext(); |
| 22633 | CHECK( |
| 22634 | args[0] |
| 22635 | ->ToObject(context) |
| 22636 | .ToLocalChecked() |
| 22637 | ->HasOwnProperty(context, args[1]->ToString(context).ToLocalChecked()) |
| 22638 | .IsNothing()); |
| 22639 | } |
| 22640 | |
| 22641 | |
| 22642 | void CheckCorrectThrow(const char* script) { |
| 22643 | // Test that the script, when wrapped into a try-catch, triggers the catch |
| 22644 | // clause due to failed access check throwing an exception. |
| 22645 | // The subsequent try-catch should run without any exception. |
| 22646 | access_check_fail_thrown = false; |
| 22647 | catch_callback_called = false; |
| 22648 | i::ScopedVector<char> source(1024); |
| 22649 | i::SNPrintF(source, "try { %s; } catch (e) { catcher(e); }" , script); |
| 22650 | CompileRun(source.start()); |
| 22651 | CHECK(access_check_fail_thrown); |
| 22652 | CHECK(catch_callback_called); |
| 22653 | |
| 22654 | access_check_fail_thrown = false; |
| 22655 | catch_callback_called = false; |
| 22656 | CompileRun("try { [1, 2, 3].sort(); } catch (e) { catcher(e) };" ); |
| 22657 | CHECK(!access_check_fail_thrown); |
| 22658 | CHECK(!catch_callback_called); |
| 22659 | } |
| 22660 | |
| 22661 | |
| 22662 | TEST(AccessCheckThrows) { |
| 22663 | i::FLAG_allow_natives_syntax = true; |
| 22664 | v8::V8::Initialize(); |
| 22665 | v8::Isolate* isolate = CcTest::isolate(); |
| 22666 | isolate->SetFailedAccessCheckCallbackFunction(&FailedAccessCheckThrows); |
| 22667 | v8::HandleScope scope(isolate); |
| 22668 | |
| 22669 | // Create an ObjectTemplate for global objects and install access |
| 22670 | // check callbacks that will block access. |
| 22671 | v8::Local<v8::ObjectTemplate> global_template = |
| 22672 | v8::ObjectTemplate::New(isolate); |
| 22673 | global_template->SetAccessCheckCallback(AccessAlwaysBlocked); |
| 22674 | |
| 22675 | // Create a context and set an x property on it's global object. |
| 22676 | LocalContext context0(nullptr, global_template); |
| 22677 | v8::Local<v8::Object> global0 = context0->Global(); |
| 22678 | CHECK(global0->Set(context0.local(), v8_str("x" ), global0).FromJust()); |
| 22679 | |
| 22680 | // Create a context with a different security token so that the |
| 22681 | // failed access check callback will be called on each access. |
| 22682 | LocalContext context1(nullptr, global_template); |
| 22683 | CHECK(context1->Global() |
| 22684 | ->Set(context1.local(), v8_str("other" ), global0) |
| 22685 | .FromJust()); |
| 22686 | |
| 22687 | v8::Local<v8::FunctionTemplate> catcher_fun = |
| 22688 | v8::FunctionTemplate::New(isolate, CatcherCallback); |
| 22689 | CHECK(context1->Global() |
| 22690 | ->Set(context1.local(), v8_str("catcher" ), |
| 22691 | catcher_fun->GetFunction(context1.local()).ToLocalChecked()) |
| 22692 | .FromJust()); |
| 22693 | |
| 22694 | v8::Local<v8::FunctionTemplate> has_own_property_fun = |
| 22695 | v8::FunctionTemplate::New(isolate, HasOwnPropertyCallback); |
| 22696 | CHECK(context1->Global() |
| 22697 | ->Set(context1.local(), v8_str("has_own_property" ), |
| 22698 | has_own_property_fun->GetFunction(context1.local()) |
| 22699 | .ToLocalChecked()) |
| 22700 | .FromJust()); |
| 22701 | |
| 22702 | { |
| 22703 | v8::TryCatch try_catch(isolate); |
| 22704 | access_check_fail_thrown = false; |
| 22705 | CompileRun("other.x;" ); |
| 22706 | CHECK(access_check_fail_thrown); |
| 22707 | CHECK(try_catch.HasCaught()); |
| 22708 | } |
| 22709 | |
| 22710 | CheckCorrectThrow("other.x" ); |
| 22711 | CheckCorrectThrow("other[1]" ); |
| 22712 | CheckCorrectThrow("JSON.stringify(other)" ); |
| 22713 | CheckCorrectThrow("has_own_property(other, 'x')" ); |
| 22714 | CheckCorrectThrow("%GetProperty(other, 'x')" ); |
| 22715 | CheckCorrectThrow("%SetKeyedProperty(other, 'x', 'foo')" ); |
| 22716 | CheckCorrectThrow("%SetNamedProperty(other, 'y', 'foo')" ); |
| 22717 | STATIC_ASSERT(static_cast<int>(i::LanguageMode::kSloppy) == 0); |
| 22718 | STATIC_ASSERT(static_cast<int>(i::LanguageMode::kStrict) == 1); |
| 22719 | CheckCorrectThrow("%DeleteProperty(other, 'x', 0)" ); // 0 == SLOPPY |
| 22720 | CheckCorrectThrow("%DeleteProperty(other, 'x', 1)" ); // 1 == STRICT |
| 22721 | CheckCorrectThrow("%DeleteProperty(other, '1', 0)" ); |
| 22722 | CheckCorrectThrow("%DeleteProperty(other, '1', 1)" ); |
| 22723 | CheckCorrectThrow("Object.prototype.hasOwnProperty.call(other, 'x')" ); |
| 22724 | CheckCorrectThrow("%HasProperty(other, 'x')" ); |
| 22725 | CheckCorrectThrow("Object.prototype.propertyIsEnumerable(other, 'x')" ); |
| 22726 | // PROPERTY_ATTRIBUTES_NONE = 0 |
| 22727 | CheckCorrectThrow("%DefineAccessorPropertyUnchecked(" |
| 22728 | "other, 'x', null, null, 1)" ); |
| 22729 | |
| 22730 | // Reset the failed access check callback so it does not influence |
| 22731 | // the other tests. |
| 22732 | isolate->SetFailedAccessCheckCallbackFunction(nullptr); |
| 22733 | } |
| 22734 | |
| 22735 | namespace { |
| 22736 | |
| 22737 | const char kOneByteSubjectString[] = { |
| 22738 | 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', |
| 22739 | 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', |
| 22740 | 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', '\0'}; |
| 22741 | const uint16_t kTwoByteSubjectString[] = { |
| 22742 | 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', |
| 22743 | 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', |
| 22744 | 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', '\0'}; |
| 22745 | |
| 22746 | const int kSubjectStringLength = arraysize(kOneByteSubjectString) - 1; |
| 22747 | STATIC_ASSERT(arraysize(kOneByteSubjectString) == |
| 22748 | arraysize(kTwoByteSubjectString)); |
| 22749 | |
| 22750 | OneByteVectorResource one_byte_string_resource( |
| 22751 | i::Vector<const char>(&kOneByteSubjectString[0], kSubjectStringLength)); |
| 22752 | UC16VectorResource two_byte_string_resource( |
| 22753 | i::Vector<const i::uc16>(&kTwoByteSubjectString[0], kSubjectStringLength)); |
| 22754 | |
| 22755 | class RegExpInterruptTest { |
| 22756 | public: |
| 22757 | RegExpInterruptTest() |
| 22758 | : i_thread(this), |
| 22759 | env_(), |
| 22760 | isolate_(env_->GetIsolate()), |
| 22761 | sem_(0), |
| 22762 | ran_test_body_(false), |
| 22763 | ran_to_completion_(false) {} |
| 22764 | |
| 22765 | void RunTest(v8::InterruptCallback test_body_fn) { |
| 22766 | v8::HandleScope handle_scope(isolate_); |
| 22767 | |
| 22768 | i_thread.SetTestBody(test_body_fn); |
| 22769 | i_thread.Start(); |
| 22770 | |
| 22771 | TestBody(); |
| 22772 | |
| 22773 | i_thread.Join(); |
| 22774 | } |
| 22775 | |
| 22776 | static void CollectAllGarbage(v8::Isolate* isolate, void* data) { |
| 22777 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 22778 | i_isolate->heap()->PreciseCollectAllGarbage( |
| 22779 | i::Heap::kNoGCFlags, i::GarbageCollectionReason::kRuntime); |
| 22780 | } |
| 22781 | |
| 22782 | static void MakeSubjectOneByteExternal(v8::Isolate* isolate, void* data) { |
| 22783 | auto instance = reinterpret_cast<RegExpInterruptTest*>(data); |
| 22784 | |
| 22785 | v8::HandleScope scope(isolate); |
| 22786 | v8::Local<v8::String> string = |
| 22787 | v8::Local<v8::String>::New(isolate, instance->string_handle_); |
| 22788 | CHECK(string->CanMakeExternal()); |
| 22789 | string->MakeExternal(&one_byte_string_resource); |
| 22790 | } |
| 22791 | |
| 22792 | static void MakeSubjectTwoByteExternal(v8::Isolate* isolate, void* data) { |
| 22793 | auto instance = reinterpret_cast<RegExpInterruptTest*>(data); |
| 22794 | |
| 22795 | v8::HandleScope scope(isolate); |
| 22796 | v8::Local<v8::String> string = |
| 22797 | v8::Local<v8::String>::New(isolate, instance->string_handle_); |
| 22798 | CHECK(string->CanMakeExternal()); |
| 22799 | string->MakeExternal(&two_byte_string_resource); |
| 22800 | } |
| 22801 | |
| 22802 | private: |
| 22803 | static void SignalSemaphore(v8::Isolate* isolate, void* data) { |
| 22804 | reinterpret_cast<RegExpInterruptTest*>(data)->sem_.Signal(); |
| 22805 | } |
| 22806 | |
| 22807 | void CreateTestStrings() { |
| 22808 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_); |
| 22809 | |
| 22810 | // The string must be in old space to support externalization. |
| 22811 | i::Handle<i::String> i_string = |
| 22812 | i_isolate->factory()->NewStringFromAsciiChecked( |
| 22813 | &kOneByteSubjectString[0], i::AllocationType::kOld); |
| 22814 | v8::Local<v8::String> string = v8::Utils::ToLocal(i_string); |
| 22815 | |
| 22816 | env_->Global()->Set(env_.local(), v8_str("a" ), string).FromJust(); |
| 22817 | |
| 22818 | string_handle_.Reset(env_->GetIsolate(), string); |
| 22819 | } |
| 22820 | |
| 22821 | void TestBody() { |
| 22822 | CHECK(!ran_test_body_.load()); |
| 22823 | CHECK(!ran_to_completion_.load()); |
| 22824 | |
| 22825 | CreateTestStrings(); |
| 22826 | |
| 22827 | v8::TryCatch try_catch(env_->GetIsolate()); |
| 22828 | |
| 22829 | isolate_->RequestInterrupt(&SignalSemaphore, this); |
| 22830 | CompileRun("/((a*)*)*b/.exec(a)" ); |
| 22831 | |
| 22832 | CHECK(try_catch.HasTerminated()); |
| 22833 | CHECK(ran_test_body_.load()); |
| 22834 | CHECK(ran_to_completion_.load()); |
| 22835 | } |
| 22836 | |
| 22837 | class InterruptThread : public v8::base::Thread { |
| 22838 | public: |
| 22839 | explicit InterruptThread(RegExpInterruptTest* test) |
| 22840 | : Thread(Options("RegExpInterruptTest" )), test_(test) {} |
| 22841 | |
| 22842 | void Run() override { |
| 22843 | CHECK_NOT_NULL(test_body_fn_); |
| 22844 | |
| 22845 | // Wait for JS execution to start. |
| 22846 | test_->sem_.Wait(); |
| 22847 | |
| 22848 | // Sleep for a bit to allow irregexp execution to start up, then run the |
| 22849 | // test body. |
| 22850 | v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(50)); |
| 22851 | test_->isolate_->RequestInterrupt(&RunTestBody, test_); |
| 22852 | test_->isolate_->RequestInterrupt(&SignalSemaphore, test_); |
| 22853 | |
| 22854 | // Wait for the scheduled interrupt to signal. |
| 22855 | test_->sem_.Wait(); |
| 22856 | |
| 22857 | // Sleep again to resume irregexp execution, then terminate. |
| 22858 | v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(50)); |
| 22859 | test_->ran_to_completion_.store(true); |
| 22860 | test_->isolate_->TerminateExecution(); |
| 22861 | } |
| 22862 | |
| 22863 | static void RunTestBody(v8::Isolate* isolate, void* data) { |
| 22864 | auto instance = reinterpret_cast<RegExpInterruptTest*>(data); |
| 22865 | instance->i_thread.test_body_fn_(isolate, data); |
| 22866 | instance->ran_test_body_.store(true); |
| 22867 | } |
| 22868 | |
| 22869 | void SetTestBody(v8::InterruptCallback callback) { |
| 22870 | test_body_fn_ = callback; |
| 22871 | } |
| 22872 | |
| 22873 | private: |
| 22874 | v8::InterruptCallback test_body_fn_; |
| 22875 | RegExpInterruptTest* test_; |
| 22876 | }; |
| 22877 | |
| 22878 | InterruptThread i_thread; |
| 22879 | |
| 22880 | LocalContext env_; |
| 22881 | v8::Isolate* isolate_; |
| 22882 | v8::base::Semaphore sem_; // Coordinates between main and interrupt threads. |
| 22883 | |
| 22884 | v8::Persistent<v8::String> string_handle_; |
| 22885 | |
| 22886 | std::atomic<bool> ran_test_body_; |
| 22887 | std::atomic<bool> ran_to_completion_; |
| 22888 | }; |
| 22889 | |
| 22890 | } // namespace |
| 22891 | |
| 22892 | TEST(RegExpInterruptAndCollectAllGarbage) { |
| 22893 | i::FLAG_always_compact = true; // Move all movable objects on GC. |
| 22894 | RegExpInterruptTest test; |
| 22895 | test.RunTest(RegExpInterruptTest::CollectAllGarbage); |
| 22896 | } |
| 22897 | |
| 22898 | TEST(RegExpInterruptAndMakeSubjectOneByteExternal) { |
| 22899 | RegExpInterruptTest test; |
| 22900 | test.RunTest(RegExpInterruptTest::MakeSubjectOneByteExternal); |
| 22901 | } |
| 22902 | |
| 22903 | TEST(RegExpInterruptAndMakeSubjectTwoByteExternal) { |
| 22904 | RegExpInterruptTest test; |
| 22905 | test.RunTest(RegExpInterruptTest::MakeSubjectTwoByteExternal); |
| 22906 | } |
| 22907 | |
| 22908 | class RequestInterruptTestBase { |
| 22909 | public: |
| 22910 | RequestInterruptTestBase() |
| 22911 | : env_(), |
| 22912 | isolate_(env_->GetIsolate()), |
| 22913 | sem_(0), |
| 22914 | warmup_(20000), |
| 22915 | should_continue_(true) { |
| 22916 | } |
| 22917 | |
| 22918 | virtual ~RequestInterruptTestBase() = default; |
| 22919 | |
| 22920 | virtual void StartInterruptThread() = 0; |
| 22921 | |
| 22922 | virtual void TestBody() = 0; |
| 22923 | |
| 22924 | void RunTest() { |
| 22925 | StartInterruptThread(); |
| 22926 | |
| 22927 | v8::HandleScope handle_scope(isolate_); |
| 22928 | |
| 22929 | TestBody(); |
| 22930 | |
| 22931 | // Verify we arrived here because interruptor was called |
| 22932 | // not due to a bug causing us to exit the loop too early. |
| 22933 | CHECK(!should_continue()); |
| 22934 | } |
| 22935 | |
| 22936 | void WakeUpInterruptor() { |
| 22937 | sem_.Signal(); |
| 22938 | } |
| 22939 | |
| 22940 | bool should_continue() const { return should_continue_; } |
| 22941 | |
| 22942 | bool ShouldContinue() { |
| 22943 | if (warmup_ > 0) { |
| 22944 | if (--warmup_ == 0) { |
| 22945 | WakeUpInterruptor(); |
| 22946 | } |
| 22947 | } |
| 22948 | |
| 22949 | return should_continue_; |
| 22950 | } |
| 22951 | |
| 22952 | static void ShouldContinueCallback( |
| 22953 | const v8::FunctionCallbackInfo<Value>& info) { |
| 22954 | RequestInterruptTestBase* test = |
| 22955 | reinterpret_cast<RequestInterruptTestBase*>( |
| 22956 | info.Data().As<v8::External>()->Value()); |
| 22957 | info.GetReturnValue().Set(test->ShouldContinue()); |
| 22958 | } |
| 22959 | |
| 22960 | LocalContext env_; |
| 22961 | v8::Isolate* isolate_; |
| 22962 | v8::base::Semaphore sem_; |
| 22963 | int warmup_; |
| 22964 | bool should_continue_; |
| 22965 | }; |
| 22966 | |
| 22967 | |
| 22968 | class RequestInterruptTestBaseWithSimpleInterrupt |
| 22969 | : public RequestInterruptTestBase { |
| 22970 | public: |
| 22971 | RequestInterruptTestBaseWithSimpleInterrupt() : i_thread(this) { } |
| 22972 | |
| 22973 | void StartInterruptThread() override { i_thread.Start(); } |
| 22974 | |
| 22975 | private: |
| 22976 | class InterruptThread : public v8::base::Thread { |
| 22977 | public: |
| 22978 | explicit InterruptThread(RequestInterruptTestBase* test) |
| 22979 | : Thread(Options("RequestInterruptTest" )), test_(test) {} |
| 22980 | |
| 22981 | void Run() override { |
| 22982 | test_->sem_.Wait(); |
| 22983 | test_->isolate_->RequestInterrupt(&OnInterrupt, test_); |
| 22984 | } |
| 22985 | |
| 22986 | static void OnInterrupt(v8::Isolate* isolate, void* data) { |
| 22987 | reinterpret_cast<RequestInterruptTestBase*>(data)-> |
| 22988 | should_continue_ = false; |
| 22989 | } |
| 22990 | |
| 22991 | private: |
| 22992 | RequestInterruptTestBase* test_; |
| 22993 | }; |
| 22994 | |
| 22995 | InterruptThread i_thread; |
| 22996 | }; |
| 22997 | |
| 22998 | |
| 22999 | class RequestInterruptTestWithFunctionCall |
| 23000 | : public RequestInterruptTestBaseWithSimpleInterrupt { |
| 23001 | public: |
| 23002 | void TestBody() override { |
| 23003 | Local<Function> func = Function::New(env_.local(), ShouldContinueCallback, |
| 23004 | v8::External::New(isolate_, this)) |
| 23005 | .ToLocalChecked(); |
| 23006 | CHECK(env_->Global() |
| 23007 | ->Set(env_.local(), v8_str("ShouldContinue" ), func) |
| 23008 | .FromJust()); |
| 23009 | |
| 23010 | CompileRun("while (ShouldContinue()) { }" ); |
| 23011 | } |
| 23012 | }; |
| 23013 | |
| 23014 | |
| 23015 | class RequestInterruptTestWithMethodCall |
| 23016 | : public RequestInterruptTestBaseWithSimpleInterrupt { |
| 23017 | public: |
| 23018 | void TestBody() override { |
| 23019 | v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_); |
| 23020 | v8::Local<v8::Template> proto = t->PrototypeTemplate(); |
| 23021 | proto->Set(v8_str("shouldContinue" ), |
| 23022 | FunctionTemplate::New(isolate_, ShouldContinueCallback, |
| 23023 | v8::External::New(isolate_, this))); |
| 23024 | CHECK(env_->Global() |
| 23025 | ->Set(env_.local(), v8_str("Klass" ), |
| 23026 | t->GetFunction(env_.local()).ToLocalChecked()) |
| 23027 | .FromJust()); |
| 23028 | |
| 23029 | CompileRun("var obj = new Klass; while (obj.shouldContinue()) { }" ); |
| 23030 | } |
| 23031 | }; |
| 23032 | |
| 23033 | |
| 23034 | class RequestInterruptTestWithAccessor |
| 23035 | : public RequestInterruptTestBaseWithSimpleInterrupt { |
| 23036 | public: |
| 23037 | void TestBody() override { |
| 23038 | v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_); |
| 23039 | v8::Local<v8::Template> proto = t->PrototypeTemplate(); |
| 23040 | proto->SetAccessorProperty(v8_str("shouldContinue" ), FunctionTemplate::New( |
| 23041 | isolate_, ShouldContinueCallback, v8::External::New(isolate_, this))); |
| 23042 | CHECK(env_->Global() |
| 23043 | ->Set(env_.local(), v8_str("Klass" ), |
| 23044 | t->GetFunction(env_.local()).ToLocalChecked()) |
| 23045 | .FromJust()); |
| 23046 | |
| 23047 | CompileRun("var obj = new Klass; while (obj.shouldContinue) { }" ); |
| 23048 | } |
| 23049 | }; |
| 23050 | |
| 23051 | |
| 23052 | class RequestInterruptTestWithNativeAccessor |
| 23053 | : public RequestInterruptTestBaseWithSimpleInterrupt { |
| 23054 | public: |
| 23055 | void TestBody() override { |
| 23056 | v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_); |
| 23057 | t->InstanceTemplate()->SetNativeDataProperty( |
| 23058 | v8_str("shouldContinue" ), &ShouldContinueNativeGetter, nullptr, |
| 23059 | v8::External::New(isolate_, this)); |
| 23060 | CHECK(env_->Global() |
| 23061 | ->Set(env_.local(), v8_str("Klass" ), |
| 23062 | t->GetFunction(env_.local()).ToLocalChecked()) |
| 23063 | .FromJust()); |
| 23064 | |
| 23065 | CompileRun("var obj = new Klass; while (obj.shouldContinue) { }" ); |
| 23066 | } |
| 23067 | |
| 23068 | private: |
| 23069 | static void ShouldContinueNativeGetter( |
| 23070 | Local<String> property, |
| 23071 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 23072 | RequestInterruptTestBase* test = |
| 23073 | reinterpret_cast<RequestInterruptTestBase*>( |
| 23074 | info.Data().As<v8::External>()->Value()); |
| 23075 | info.GetReturnValue().Set(test->ShouldContinue()); |
| 23076 | } |
| 23077 | }; |
| 23078 | |
| 23079 | |
| 23080 | class RequestInterruptTestWithMethodCallAndInterceptor |
| 23081 | : public RequestInterruptTestBaseWithSimpleInterrupt { |
| 23082 | public: |
| 23083 | void TestBody() override { |
| 23084 | v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_); |
| 23085 | v8::Local<v8::Template> proto = t->PrototypeTemplate(); |
| 23086 | proto->Set(v8_str("shouldContinue" ), |
| 23087 | FunctionTemplate::New(isolate_, ShouldContinueCallback, |
| 23088 | v8::External::New(isolate_, this))); |
| 23089 | v8::Local<v8::ObjectTemplate> instance_template = t->InstanceTemplate(); |
| 23090 | instance_template->SetHandler( |
| 23091 | v8::NamedPropertyHandlerConfiguration(EmptyInterceptor)); |
| 23092 | |
| 23093 | CHECK(env_->Global() |
| 23094 | ->Set(env_.local(), v8_str("Klass" ), |
| 23095 | t->GetFunction(env_.local()).ToLocalChecked()) |
| 23096 | .FromJust()); |
| 23097 | |
| 23098 | CompileRun("var obj = new Klass; while (obj.shouldContinue()) { }" ); |
| 23099 | } |
| 23100 | |
| 23101 | private: |
| 23102 | static void EmptyInterceptor( |
| 23103 | Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {} |
| 23104 | }; |
| 23105 | |
| 23106 | |
| 23107 | class RequestInterruptTestWithMathAbs |
| 23108 | : public RequestInterruptTestBaseWithSimpleInterrupt { |
| 23109 | public: |
| 23110 | void TestBody() override { |
| 23111 | env_->Global() |
| 23112 | ->Set(env_.local(), v8_str("WakeUpInterruptor" ), |
| 23113 | Function::New(env_.local(), WakeUpInterruptorCallback, |
| 23114 | v8::External::New(isolate_, this)) |
| 23115 | .ToLocalChecked()) |
| 23116 | .FromJust(); |
| 23117 | |
| 23118 | env_->Global() |
| 23119 | ->Set(env_.local(), v8_str("ShouldContinue" ), |
| 23120 | Function::New(env_.local(), ShouldContinueCallback, |
| 23121 | v8::External::New(isolate_, this)) |
| 23122 | .ToLocalChecked()) |
| 23123 | .FromJust(); |
| 23124 | |
| 23125 | i::FLAG_allow_natives_syntax = true; |
| 23126 | CompileRun("function loopish(o) {" |
| 23127 | " var pre = 10;" |
| 23128 | " while (o.abs(1) > 0) {" |
| 23129 | " if (o.abs(1) >= 0 && !ShouldContinue()) break;" |
| 23130 | " if (pre > 0) {" |
| 23131 | " if (--pre === 0) WakeUpInterruptor(o === Math);" |
| 23132 | " }" |
| 23133 | " }" |
| 23134 | "}" |
| 23135 | "var i = 50;" |
| 23136 | "var obj = {abs: function () { return i-- }, x: null};" |
| 23137 | "delete obj.x;" |
| 23138 | "loopish(obj);" |
| 23139 | "%OptimizeFunctionOnNextCall(loopish);" |
| 23140 | "loopish(Math);" ); |
| 23141 | |
| 23142 | i::FLAG_allow_natives_syntax = false; |
| 23143 | } |
| 23144 | |
| 23145 | private: |
| 23146 | static void WakeUpInterruptorCallback( |
| 23147 | const v8::FunctionCallbackInfo<Value>& info) { |
| 23148 | if (!info[0]->BooleanValue(info.GetIsolate())) { |
| 23149 | return; |
| 23150 | } |
| 23151 | |
| 23152 | RequestInterruptTestBase* test = |
| 23153 | reinterpret_cast<RequestInterruptTestBase*>( |
| 23154 | info.Data().As<v8::External>()->Value()); |
| 23155 | test->WakeUpInterruptor(); |
| 23156 | } |
| 23157 | |
| 23158 | static void ShouldContinueCallback( |
| 23159 | const v8::FunctionCallbackInfo<Value>& info) { |
| 23160 | RequestInterruptTestBase* test = |
| 23161 | reinterpret_cast<RequestInterruptTestBase*>( |
| 23162 | info.Data().As<v8::External>()->Value()); |
| 23163 | info.GetReturnValue().Set(test->should_continue()); |
| 23164 | } |
| 23165 | }; |
| 23166 | |
| 23167 | |
| 23168 | TEST(RequestInterruptTestWithFunctionCall) { |
| 23169 | RequestInterruptTestWithFunctionCall().RunTest(); |
| 23170 | } |
| 23171 | |
| 23172 | |
| 23173 | TEST(RequestInterruptTestWithMethodCall) { |
| 23174 | RequestInterruptTestWithMethodCall().RunTest(); |
| 23175 | } |
| 23176 | |
| 23177 | |
| 23178 | TEST(RequestInterruptTestWithAccessor) { |
| 23179 | RequestInterruptTestWithAccessor().RunTest(); |
| 23180 | } |
| 23181 | |
| 23182 | |
| 23183 | TEST(RequestInterruptTestWithNativeAccessor) { |
| 23184 | RequestInterruptTestWithNativeAccessor().RunTest(); |
| 23185 | } |
| 23186 | |
| 23187 | |
| 23188 | TEST(RequestInterruptTestWithMethodCallAndInterceptor) { |
| 23189 | RequestInterruptTestWithMethodCallAndInterceptor().RunTest(); |
| 23190 | } |
| 23191 | |
| 23192 | |
| 23193 | TEST(RequestInterruptTestWithMathAbs) { |
| 23194 | RequestInterruptTestWithMathAbs().RunTest(); |
| 23195 | } |
| 23196 | |
| 23197 | |
| 23198 | class RequestMultipleInterrupts : public RequestInterruptTestBase { |
| 23199 | public: |
| 23200 | RequestMultipleInterrupts() : i_thread(this), counter_(0) {} |
| 23201 | |
| 23202 | void StartInterruptThread() override { i_thread.Start(); } |
| 23203 | |
| 23204 | void TestBody() override { |
| 23205 | Local<Function> func = Function::New(env_.local(), ShouldContinueCallback, |
| 23206 | v8::External::New(isolate_, this)) |
| 23207 | .ToLocalChecked(); |
| 23208 | CHECK(env_->Global() |
| 23209 | ->Set(env_.local(), v8_str("ShouldContinue" ), func) |
| 23210 | .FromJust()); |
| 23211 | |
| 23212 | CompileRun("while (ShouldContinue()) { }" ); |
| 23213 | } |
| 23214 | |
| 23215 | private: |
| 23216 | class InterruptThread : public v8::base::Thread { |
| 23217 | public: |
| 23218 | enum { NUM_INTERRUPTS = 10 }; |
| 23219 | explicit InterruptThread(RequestMultipleInterrupts* test) |
| 23220 | : Thread(Options("RequestInterruptTest" )), test_(test) {} |
| 23221 | |
| 23222 | void Run() override { |
| 23223 | test_->sem_.Wait(); |
| 23224 | for (int i = 0; i < NUM_INTERRUPTS; i++) { |
| 23225 | test_->isolate_->RequestInterrupt(&OnInterrupt, test_); |
| 23226 | } |
| 23227 | } |
| 23228 | |
| 23229 | static void OnInterrupt(v8::Isolate* isolate, void* data) { |
| 23230 | RequestMultipleInterrupts* test = |
| 23231 | reinterpret_cast<RequestMultipleInterrupts*>(data); |
| 23232 | test->should_continue_ = ++test->counter_ < NUM_INTERRUPTS; |
| 23233 | } |
| 23234 | |
| 23235 | private: |
| 23236 | RequestMultipleInterrupts* test_; |
| 23237 | }; |
| 23238 | |
| 23239 | InterruptThread i_thread; |
| 23240 | int counter_; |
| 23241 | }; |
| 23242 | |
| 23243 | |
| 23244 | TEST(RequestMultipleInterrupts) { RequestMultipleInterrupts().RunTest(); } |
| 23245 | |
| 23246 | |
| 23247 | static bool interrupt_was_called = false; |
| 23248 | |
| 23249 | |
| 23250 | void SmallScriptsInterruptCallback(v8::Isolate* isolate, void* data) { |
| 23251 | interrupt_was_called = true; |
| 23252 | } |
| 23253 | |
| 23254 | |
| 23255 | TEST(RequestInterruptSmallScripts) { |
| 23256 | LocalContext env; |
| 23257 | v8::Isolate* isolate = CcTest::isolate(); |
| 23258 | v8::HandleScope scope(isolate); |
| 23259 | |
| 23260 | interrupt_was_called = false; |
| 23261 | isolate->RequestInterrupt(&SmallScriptsInterruptCallback, nullptr); |
| 23262 | CompileRun("(function(x){return x;})(1);" ); |
| 23263 | CHECK(interrupt_was_called); |
| 23264 | } |
| 23265 | |
| 23266 | |
| 23267 | static Local<Value> function_new_expected_env; |
| 23268 | static void FunctionNewCallback(const v8::FunctionCallbackInfo<Value>& info) { |
| 23269 | CHECK( |
| 23270 | function_new_expected_env->Equals(info.GetIsolate()->GetCurrentContext(), |
| 23271 | info.Data()) |
| 23272 | .FromJust()); |
| 23273 | info.GetReturnValue().Set(17); |
| 23274 | } |
| 23275 | |
| 23276 | |
| 23277 | THREADED_TEST(FunctionNew) { |
| 23278 | LocalContext env; |
| 23279 | v8::Isolate* isolate = env->GetIsolate(); |
| 23280 | v8::HandleScope scope(isolate); |
| 23281 | Local<Object> data = v8::Object::New(isolate); |
| 23282 | function_new_expected_env = data; |
| 23283 | Local<Function> func = |
| 23284 | Function::New(env.local(), FunctionNewCallback, data).ToLocalChecked(); |
| 23285 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 23286 | Local<Value> result = CompileRun("func();" ); |
| 23287 | CHECK(v8::Integer::New(isolate, 17)->Equals(env.local(), result).FromJust()); |
| 23288 | // Serial number should be invalid => should not be cached. |
| 23289 | auto serial_number = |
| 23290 | i::Smi::cast(i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*func)) |
| 23291 | ->shared() |
| 23292 | ->get_api_func_data() |
| 23293 | ->serial_number()) |
| 23294 | ->value(); |
| 23295 | CHECK_EQ(i::FunctionTemplateInfo::kInvalidSerialNumber, serial_number); |
| 23296 | |
| 23297 | // Verify that each Function::New creates a new function instance |
| 23298 | Local<Object> data2 = v8::Object::New(isolate); |
| 23299 | function_new_expected_env = data2; |
| 23300 | Local<Function> func2 = |
| 23301 | Function::New(env.local(), FunctionNewCallback, data2).ToLocalChecked(); |
| 23302 | CHECK(!func2->IsNull()); |
| 23303 | CHECK(!func->Equals(env.local(), func2).FromJust()); |
| 23304 | CHECK(env->Global()->Set(env.local(), v8_str("func2" ), func2).FromJust()); |
| 23305 | Local<Value> result2 = CompileRun("func2();" ); |
| 23306 | CHECK(v8::Integer::New(isolate, 17)->Equals(env.local(), result2).FromJust()); |
| 23307 | } |
| 23308 | |
| 23309 | namespace { |
| 23310 | |
| 23311 | void Verify(v8::Isolate* isolate, Local<v8::Object> obj) { |
| 23312 | #if VERIFY_HEAP |
| 23313 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 23314 | i::Handle<i::JSReceiver> i_obj = v8::Utils::OpenHandle(*obj); |
| 23315 | i_obj->ObjectVerify(i_isolate); |
| 23316 | #endif |
| 23317 | } |
| 23318 | |
| 23319 | } // namespace |
| 23320 | |
| 23321 | THREADED_TEST(ObjectNew) { |
| 23322 | LocalContext env; |
| 23323 | v8::Isolate* isolate = env->GetIsolate(); |
| 23324 | v8::HandleScope scope(isolate); |
| 23325 | { |
| 23326 | // Verify that Object::New(null) produces an object with a null |
| 23327 | // [[Prototype]]. |
| 23328 | Local<v8::Object> obj = |
| 23329 | v8::Object::New(isolate, v8::Null(isolate), nullptr, nullptr, 0); |
| 23330 | CHECK(obj->GetPrototype()->IsNull()); |
| 23331 | Verify(isolate, obj); |
| 23332 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23333 | CHECK_EQ(0, keys->Length()); |
| 23334 | } |
| 23335 | { |
| 23336 | // Verify that Object::New(proto) produces an object with |
| 23337 | // proto as it's [[Prototype]]. |
| 23338 | Local<v8::Object> proto = v8::Object::New(isolate); |
| 23339 | Local<v8::Object> obj = |
| 23340 | v8::Object::New(isolate, proto, nullptr, nullptr, 0); |
| 23341 | Verify(isolate, obj); |
| 23342 | CHECK(obj->GetPrototype()->SameValue(proto)); |
| 23343 | } |
| 23344 | { |
| 23345 | // Verify that the properties are installed correctly. |
| 23346 | Local<v8::Name> names[3] = {v8_str("a" ), v8_str("b" ), v8_str("c" )}; |
| 23347 | Local<v8::Value> values[3] = {v8_num(1), v8_num(2), v8_num(3)}; |
| 23348 | Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names, |
| 23349 | values, arraysize(values)); |
| 23350 | Verify(isolate, obj); |
| 23351 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23352 | CHECK_EQ(arraysize(names), keys->Length()); |
| 23353 | for (uint32_t i = 0; i < arraysize(names); ++i) { |
| 23354 | CHECK(names[i]->SameValue(keys->Get(env.local(), i).ToLocalChecked())); |
| 23355 | CHECK(values[i]->SameValue( |
| 23356 | obj->Get(env.local(), names[i]).ToLocalChecked())); |
| 23357 | } |
| 23358 | } |
| 23359 | { |
| 23360 | // Same as above, but with non-null prototype. |
| 23361 | Local<v8::Object> proto = v8::Object::New(isolate); |
| 23362 | Local<v8::Name> names[3] = {v8_str("x" ), v8_str("y" ), v8_str("z" )}; |
| 23363 | Local<v8::Value> values[3] = {v8_num(1), v8_num(2), v8_num(3)}; |
| 23364 | Local<v8::Object> obj = |
| 23365 | v8::Object::New(isolate, proto, names, values, arraysize(values)); |
| 23366 | CHECK(obj->GetPrototype()->SameValue(proto)); |
| 23367 | Verify(isolate, obj); |
| 23368 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23369 | CHECK_EQ(arraysize(names), keys->Length()); |
| 23370 | for (uint32_t i = 0; i < arraysize(names); ++i) { |
| 23371 | CHECK(names[i]->SameValue(keys->Get(env.local(), i).ToLocalChecked())); |
| 23372 | CHECK(values[i]->SameValue( |
| 23373 | obj->Get(env.local(), names[i]).ToLocalChecked())); |
| 23374 | } |
| 23375 | } |
| 23376 | { |
| 23377 | // This has to work with duplicate names too. |
| 23378 | Local<v8::Name> names[3] = {v8_str("a" ), v8_str("a" ), v8_str("a" )}; |
| 23379 | Local<v8::Value> values[3] = {v8_num(1), v8_num(2), v8_num(3)}; |
| 23380 | Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names, |
| 23381 | values, arraysize(values)); |
| 23382 | Verify(isolate, obj); |
| 23383 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23384 | CHECK_EQ(1, keys->Length()); |
| 23385 | CHECK(v8_str("a" )->SameValue(keys->Get(env.local(), 0).ToLocalChecked())); |
| 23386 | CHECK(v8_num(3)->SameValue( |
| 23387 | obj->Get(env.local(), v8_str("a" )).ToLocalChecked())); |
| 23388 | } |
| 23389 | { |
| 23390 | // This has to work with array indices too. |
| 23391 | Local<v8::Name> names[2] = {v8_str("0" ), v8_str("1" )}; |
| 23392 | Local<v8::Value> values[2] = {v8_num(0), v8_num(1)}; |
| 23393 | Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names, |
| 23394 | values, arraysize(values)); |
| 23395 | Verify(isolate, obj); |
| 23396 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23397 | CHECK_EQ(arraysize(names), keys->Length()); |
| 23398 | for (uint32_t i = 0; i < arraysize(names); ++i) { |
| 23399 | CHECK(v8::Number::New(isolate, i) |
| 23400 | ->SameValue(keys->Get(env.local(), i).ToLocalChecked())); |
| 23401 | CHECK(values[i]->SameValue(obj->Get(env.local(), i).ToLocalChecked())); |
| 23402 | } |
| 23403 | } |
| 23404 | { |
| 23405 | // This has to work with mixed array indices / property names too. |
| 23406 | Local<v8::Name> names[2] = {v8_str("0" ), v8_str("x" )}; |
| 23407 | Local<v8::Value> values[2] = {v8_num(42), v8_num(24)}; |
| 23408 | Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names, |
| 23409 | values, arraysize(values)); |
| 23410 | Verify(isolate, obj); |
| 23411 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23412 | CHECK_EQ(arraysize(names), keys->Length()); |
| 23413 | // 0 -> 42 |
| 23414 | CHECK(v8_num(0)->SameValue(keys->Get(env.local(), 0).ToLocalChecked())); |
| 23415 | CHECK( |
| 23416 | values[0]->SameValue(obj->Get(env.local(), names[0]).ToLocalChecked())); |
| 23417 | // "x" -> 24 |
| 23418 | CHECK(v8_str("x" )->SameValue(keys->Get(env.local(), 1).ToLocalChecked())); |
| 23419 | CHECK( |
| 23420 | values[1]->SameValue(obj->Get(env.local(), names[1]).ToLocalChecked())); |
| 23421 | } |
| 23422 | { |
| 23423 | // Verify that this also works for a couple thousand properties. |
| 23424 | size_t const kLength = 10 * 1024; |
| 23425 | Local<v8::Name> names[kLength]; |
| 23426 | Local<v8::Value> values[kLength]; |
| 23427 | for (size_t i = 0; i < arraysize(names); ++i) { |
| 23428 | std::ostringstream ost; |
| 23429 | ost << "a" << i; |
| 23430 | names[i] = v8_str(ost.str().c_str()); |
| 23431 | values[i] = v8_num(static_cast<double>(i)); |
| 23432 | } |
| 23433 | Local<v8::Object> obj = v8::Object::New(isolate, v8::Null(isolate), names, |
| 23434 | values, arraysize(names)); |
| 23435 | Verify(isolate, obj); |
| 23436 | Local<Array> keys = obj->GetOwnPropertyNames(env.local()).ToLocalChecked(); |
| 23437 | CHECK_EQ(arraysize(names), keys->Length()); |
| 23438 | for (uint32_t i = 0; i < arraysize(names); ++i) { |
| 23439 | CHECK(names[i]->SameValue(keys->Get(env.local(), i).ToLocalChecked())); |
| 23440 | CHECK(values[i]->SameValue( |
| 23441 | obj->Get(env.local(), names[i]).ToLocalChecked())); |
| 23442 | } |
| 23443 | } |
| 23444 | } |
| 23445 | |
| 23446 | TEST(EscapableHandleScope) { |
| 23447 | HandleScope outer_scope(CcTest::isolate()); |
| 23448 | LocalContext context; |
| 23449 | const int runs = 10; |
| 23450 | Local<String> values[runs]; |
| 23451 | for (int i = 0; i < runs; i++) { |
| 23452 | v8::EscapableHandleScope inner_scope(CcTest::isolate()); |
| 23453 | Local<String> value; |
| 23454 | if (i != 0) value = v8_str("escape value" ); |
| 23455 | if (i < runs / 2) { |
| 23456 | values[i] = inner_scope.Escape(value); |
| 23457 | } else { |
| 23458 | values[i] = inner_scope.EscapeMaybe(v8::MaybeLocal<String>(value)) |
| 23459 | .ToLocalChecked(); |
| 23460 | } |
| 23461 | } |
| 23462 | for (int i = 0; i < runs; i++) { |
| 23463 | Local<String> expected; |
| 23464 | if (i != 0) { |
| 23465 | CHECK(v8_str("escape value" ) |
| 23466 | ->Equals(context.local(), values[i]) |
| 23467 | .FromJust()); |
| 23468 | } else { |
| 23469 | CHECK(values[i].IsEmpty()); |
| 23470 | } |
| 23471 | } |
| 23472 | } |
| 23473 | |
| 23474 | |
| 23475 | static void SetterWhichExpectsThisAndHolderToDiffer( |
| 23476 | Local<String>, Local<Value>, const v8::PropertyCallbackInfo<void>& info) { |
| 23477 | CHECK(info.Holder() != info.This()); |
| 23478 | } |
| 23479 | |
| 23480 | |
| 23481 | TEST(Regress239669) { |
| 23482 | LocalContext context; |
| 23483 | v8::Isolate* isolate = context->GetIsolate(); |
| 23484 | v8::HandleScope scope(isolate); |
| 23485 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 23486 | templ->SetAccessor(v8_str("x" ), nullptr, |
| 23487 | SetterWhichExpectsThisAndHolderToDiffer); |
| 23488 | CHECK(context->Global() |
| 23489 | ->Set(context.local(), v8_str("P" ), |
| 23490 | templ->NewInstance(context.local()).ToLocalChecked()) |
| 23491 | .FromJust()); |
| 23492 | CompileRun( |
| 23493 | "function C1() {" |
| 23494 | " this.x = 23;" |
| 23495 | "};" |
| 23496 | "C1.prototype = P;" |
| 23497 | "for (var i = 0; i < 4; i++ ) {" |
| 23498 | " new C1();" |
| 23499 | "}" ); |
| 23500 | } |
| 23501 | |
| 23502 | |
| 23503 | class ApiCallOptimizationChecker { |
| 23504 | private: |
| 23505 | static Local<Object> data; |
| 23506 | static Local<Object> receiver; |
| 23507 | static Local<Object> holder; |
| 23508 | static Local<Object> callee; |
| 23509 | static int count; |
| 23510 | |
| 23511 | static void OptimizationCallback( |
| 23512 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 23513 | CHECK(data == info.Data()); |
| 23514 | CHECK(receiver == info.This()); |
| 23515 | if (info.Length() == 1) { |
| 23516 | CHECK(v8_num(1) |
| 23517 | ->Equals(info.GetIsolate()->GetCurrentContext(), info[0]) |
| 23518 | .FromJust()); |
| 23519 | } |
| 23520 | CHECK(holder == info.Holder()); |
| 23521 | count++; |
| 23522 | info.GetReturnValue().Set(v8_str("returned" )); |
| 23523 | } |
| 23524 | |
| 23525 | public: |
| 23526 | enum SignatureType { |
| 23527 | kNoSignature, |
| 23528 | kSignatureOnReceiver, |
| 23529 | kSignatureOnPrototype |
| 23530 | }; |
| 23531 | |
| 23532 | void RunAll() { |
| 23533 | SignatureType signature_types[] = |
| 23534 | {kNoSignature, kSignatureOnReceiver, kSignatureOnPrototype}; |
| 23535 | for (unsigned i = 0; i < arraysize(signature_types); i++) { |
| 23536 | SignatureType signature_type = signature_types[i]; |
| 23537 | for (int j = 0; j < 2; j++) { |
| 23538 | bool global = j == 0; |
| 23539 | int key = signature_type + |
| 23540 | arraysize(signature_types) * (global ? 1 : 0); |
| 23541 | Run(signature_type, global, key); |
| 23542 | } |
| 23543 | } |
| 23544 | } |
| 23545 | |
| 23546 | void Run(SignatureType signature_type, bool global, int key) { |
| 23547 | v8::Isolate* isolate = CcTest::isolate(); |
| 23548 | v8::HandleScope scope(isolate); |
| 23549 | // Build a template for signature checks. |
| 23550 | Local<v8::ObjectTemplate> signature_template; |
| 23551 | Local<v8::Signature> signature; |
| 23552 | { |
| 23553 | Local<v8::FunctionTemplate> parent_template = |
| 23554 | FunctionTemplate::New(isolate); |
| 23555 | Local<v8::FunctionTemplate> function_template |
| 23556 | = FunctionTemplate::New(isolate); |
| 23557 | function_template->Inherit(parent_template); |
| 23558 | switch (signature_type) { |
| 23559 | case kNoSignature: |
| 23560 | break; |
| 23561 | case kSignatureOnReceiver: |
| 23562 | signature = v8::Signature::New(isolate, function_template); |
| 23563 | break; |
| 23564 | case kSignatureOnPrototype: |
| 23565 | signature = v8::Signature::New(isolate, parent_template); |
| 23566 | break; |
| 23567 | } |
| 23568 | signature_template = function_template->InstanceTemplate(); |
| 23569 | } |
| 23570 | // Global object must pass checks. |
| 23571 | Local<v8::Context> context = |
| 23572 | v8::Context::New(isolate, nullptr, signature_template); |
| 23573 | v8::Context::Scope context_scope(context); |
| 23574 | // Install regular object that can pass signature checks. |
| 23575 | Local<Object> function_receiver = |
| 23576 | signature_template->NewInstance(context).ToLocalChecked(); |
| 23577 | CHECK(context->Global() |
| 23578 | ->Set(context, v8_str("function_receiver" ), function_receiver) |
| 23579 | .FromJust()); |
| 23580 | // Get the holder objects. |
| 23581 | Local<Object> inner_global = |
| 23582 | Local<Object>::Cast(context->Global()->GetPrototype()); |
| 23583 | data = Object::New(isolate); |
| 23584 | Local<FunctionTemplate> function_template = FunctionTemplate::New( |
| 23585 | isolate, OptimizationCallback, data, signature); |
| 23586 | Local<Function> function = |
| 23587 | function_template->GetFunction(context).ToLocalChecked(); |
| 23588 | Local<Object> global_holder = inner_global; |
| 23589 | Local<Object> function_holder = function_receiver; |
| 23590 | if (signature_type == kSignatureOnPrototype) { |
| 23591 | function_holder = Local<Object>::Cast(function_holder->GetPrototype()); |
| 23592 | global_holder = Local<Object>::Cast(global_holder->GetPrototype()); |
| 23593 | } |
| 23594 | global_holder->Set(context, v8_str("g_f" ), function).FromJust(); |
| 23595 | global_holder->SetAccessorProperty(v8_str("g_acc" ), function, function); |
| 23596 | function_holder->Set(context, v8_str("f" ), function).FromJust(); |
| 23597 | function_holder->SetAccessorProperty(v8_str("acc" ), function, function); |
| 23598 | // Initialize expected values. |
| 23599 | callee = function; |
| 23600 | count = 0; |
| 23601 | if (global) { |
| 23602 | receiver = context->Global(); |
| 23603 | holder = inner_global; |
| 23604 | } else { |
| 23605 | holder = function_receiver; |
| 23606 | // If not using a signature, add something else to the prototype chain |
| 23607 | // to test the case that holder != receiver |
| 23608 | if (signature_type == kNoSignature) { |
| 23609 | receiver = Local<Object>::Cast(CompileRun( |
| 23610 | "var receiver_subclass = {};\n" |
| 23611 | "receiver_subclass.__proto__ = function_receiver;\n" |
| 23612 | "receiver_subclass" )); |
| 23613 | } else { |
| 23614 | receiver = Local<Object>::Cast(CompileRun( |
| 23615 | "var receiver_subclass = function_receiver;\n" |
| 23616 | "receiver_subclass" )); |
| 23617 | } |
| 23618 | } |
| 23619 | // With no signature, the holder is not set. |
| 23620 | if (signature_type == kNoSignature) holder = receiver; |
| 23621 | // build wrap_function |
| 23622 | i::ScopedVector<char> wrap_function(200); |
| 23623 | if (global) { |
| 23624 | i::SNPrintF( |
| 23625 | wrap_function, |
| 23626 | "function wrap_f_%d() { var f = g_f; return f(); }\n" |
| 23627 | "function wrap_get_%d() { return this.g_acc; }\n" |
| 23628 | "function wrap_set_%d() { return this.g_acc = 1; }\n" , |
| 23629 | key, key, key); |
| 23630 | } else { |
| 23631 | i::SNPrintF( |
| 23632 | wrap_function, |
| 23633 | "function wrap_f_%d() { return receiver_subclass.f(); }\n" |
| 23634 | "function wrap_get_%d() { return receiver_subclass.acc; }\n" |
| 23635 | "function wrap_set_%d() { return receiver_subclass.acc = 1; }\n" , |
| 23636 | key, key, key); |
| 23637 | } |
| 23638 | // build source string |
| 23639 | i::ScopedVector<char> source(1000); |
| 23640 | i::SNPrintF( |
| 23641 | source, |
| 23642 | "%s\n" // wrap functions |
| 23643 | "function wrap_f() { return wrap_f_%d(); }\n" |
| 23644 | "function wrap_get() { return wrap_get_%d(); }\n" |
| 23645 | "function wrap_set() { return wrap_set_%d(); }\n" |
| 23646 | "check = function(returned) {\n" |
| 23647 | " if (returned !== 'returned') { throw returned; }\n" |
| 23648 | "}\n" |
| 23649 | "\n" |
| 23650 | "check(wrap_f());\n" |
| 23651 | "check(wrap_f());\n" |
| 23652 | "%%OptimizeFunctionOnNextCall(wrap_f_%d);\n" |
| 23653 | "check(wrap_f());\n" |
| 23654 | "\n" |
| 23655 | "check(wrap_get());\n" |
| 23656 | "check(wrap_get());\n" |
| 23657 | "%%OptimizeFunctionOnNextCall(wrap_get_%d);\n" |
| 23658 | "check(wrap_get());\n" |
| 23659 | "\n" |
| 23660 | "check = function(returned) {\n" |
| 23661 | " if (returned !== 1) { throw returned; }\n" |
| 23662 | "}\n" |
| 23663 | "check(wrap_set());\n" |
| 23664 | "check(wrap_set());\n" |
| 23665 | "%%OptimizeFunctionOnNextCall(wrap_set_%d);\n" |
| 23666 | "check(wrap_set());\n" , |
| 23667 | wrap_function.start(), key, key, key, key, key, key); |
| 23668 | v8::TryCatch try_catch(isolate); |
| 23669 | CompileRun(source.start()); |
| 23670 | CHECK(!try_catch.HasCaught()); |
| 23671 | CHECK_EQ(9, count); |
| 23672 | } |
| 23673 | }; |
| 23674 | |
| 23675 | |
| 23676 | Local<Object> ApiCallOptimizationChecker::data; |
| 23677 | Local<Object> ApiCallOptimizationChecker::receiver; |
| 23678 | Local<Object> ApiCallOptimizationChecker::holder; |
| 23679 | Local<Object> ApiCallOptimizationChecker::callee; |
| 23680 | int ApiCallOptimizationChecker::count = 0; |
| 23681 | |
| 23682 | |
| 23683 | TEST(FunctionCallOptimization) { |
| 23684 | i::FLAG_allow_natives_syntax = true; |
| 23685 | ApiCallOptimizationChecker checker; |
| 23686 | checker.RunAll(); |
| 23687 | } |
| 23688 | |
| 23689 | |
| 23690 | TEST(FunctionCallOptimizationMultipleArgs) { |
| 23691 | i::FLAG_allow_natives_syntax = true; |
| 23692 | LocalContext context; |
| 23693 | v8::Isolate* isolate = context->GetIsolate(); |
| 23694 | v8::HandleScope scope(isolate); |
| 23695 | Local<Object> global = context->Global(); |
| 23696 | Local<v8::Function> function = |
| 23697 | Function::New(context.local(), Returns42).ToLocalChecked(); |
| 23698 | global->Set(context.local(), v8_str("x" ), function).FromJust(); |
| 23699 | CompileRun( |
| 23700 | "function x_wrap() {\n" |
| 23701 | " for (var i = 0; i < 5; i++) {\n" |
| 23702 | " x(1,2,3);\n" |
| 23703 | " }\n" |
| 23704 | "}\n" |
| 23705 | "x_wrap();\n" |
| 23706 | "%OptimizeFunctionOnNextCall(x_wrap);" |
| 23707 | "x_wrap();\n" ); |
| 23708 | } |
| 23709 | |
| 23710 | |
| 23711 | static void ReturnsSymbolCallback( |
| 23712 | const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 23713 | info.GetReturnValue().Set(v8::Symbol::New(info.GetIsolate())); |
| 23714 | } |
| 23715 | |
| 23716 | |
| 23717 | TEST(ApiCallbackCanReturnSymbols) { |
| 23718 | i::FLAG_allow_natives_syntax = true; |
| 23719 | LocalContext context; |
| 23720 | v8::Isolate* isolate = context->GetIsolate(); |
| 23721 | v8::HandleScope scope(isolate); |
| 23722 | Local<Object> global = context->Global(); |
| 23723 | Local<v8::Function> function = |
| 23724 | Function::New(context.local(), ReturnsSymbolCallback).ToLocalChecked(); |
| 23725 | global->Set(context.local(), v8_str("x" ), function).FromJust(); |
| 23726 | CompileRun( |
| 23727 | "function x_wrap() {\n" |
| 23728 | " for (var i = 0; i < 5; i++) {\n" |
| 23729 | " x();\n" |
| 23730 | " }\n" |
| 23731 | "}\n" |
| 23732 | "x_wrap();\n" |
| 23733 | "%OptimizeFunctionOnNextCall(x_wrap);" |
| 23734 | "x_wrap();\n" ); |
| 23735 | } |
| 23736 | |
| 23737 | |
| 23738 | TEST(EmptyApiCallback) { |
| 23739 | LocalContext context; |
| 23740 | auto isolate = context->GetIsolate(); |
| 23741 | v8::HandleScope scope(isolate); |
| 23742 | auto global = context->Global(); |
| 23743 | auto function = FunctionTemplate::New(isolate) |
| 23744 | ->GetFunction(context.local()) |
| 23745 | .ToLocalChecked(); |
| 23746 | global->Set(context.local(), v8_str("x" ), function).FromJust(); |
| 23747 | |
| 23748 | auto result = CompileRun("x()" ); |
| 23749 | CHECK(v8::Utils::OpenHandle(*result)->IsJSGlobalProxy()); |
| 23750 | |
| 23751 | result = CompileRun("x(1,2,3)" ); |
| 23752 | CHECK(v8::Utils::OpenHandle(*result)->IsJSGlobalProxy()); |
| 23753 | |
| 23754 | result = CompileRun("x.call(undefined)" ); |
| 23755 | CHECK(v8::Utils::OpenHandle(*result)->IsJSGlobalProxy()); |
| 23756 | |
| 23757 | result = CompileRun("x.call(null)" ); |
| 23758 | CHECK(v8::Utils::OpenHandle(*result)->IsJSGlobalProxy()); |
| 23759 | |
| 23760 | result = CompileRun("7 + x.call(3) + 11" ); |
| 23761 | CHECK(result->IsInt32()); |
| 23762 | CHECK_EQ(21, result->Int32Value(context.local()).FromJust()); |
| 23763 | |
| 23764 | result = CompileRun("7 + x.call(3, 101, 102, 103, 104) + 11" ); |
| 23765 | CHECK(result->IsInt32()); |
| 23766 | CHECK_EQ(21, result->Int32Value(context.local()).FromJust()); |
| 23767 | |
| 23768 | result = CompileRun("var y = []; x.call(y)" ); |
| 23769 | CHECK(result->IsArray()); |
| 23770 | |
| 23771 | result = CompileRun("x.call(y, 1, 2, 3, 4)" ); |
| 23772 | CHECK(result->IsArray()); |
| 23773 | } |
| 23774 | |
| 23775 | |
| 23776 | TEST(SimpleSignatureCheck) { |
| 23777 | LocalContext context; |
| 23778 | auto isolate = context->GetIsolate(); |
| 23779 | v8::HandleScope scope(isolate); |
| 23780 | auto global = context->Global(); |
| 23781 | auto sig_obj = FunctionTemplate::New(isolate); |
| 23782 | auto sig = v8::Signature::New(isolate, sig_obj); |
| 23783 | auto x = FunctionTemplate::New(isolate, Returns42, Local<Value>(), sig); |
| 23784 | global->Set(context.local(), v8_str("sig_obj" ), |
| 23785 | sig_obj->GetFunction(context.local()).ToLocalChecked()) |
| 23786 | .FromJust(); |
| 23787 | global->Set(context.local(), v8_str("x" ), |
| 23788 | x->GetFunction(context.local()).ToLocalChecked()) |
| 23789 | .FromJust(); |
| 23790 | CompileRun("var s = new sig_obj();" ); |
| 23791 | { |
| 23792 | TryCatch try_catch(isolate); |
| 23793 | CompileRun("x()" ); |
| 23794 | CHECK(try_catch.HasCaught()); |
| 23795 | } |
| 23796 | { |
| 23797 | TryCatch try_catch(isolate); |
| 23798 | CompileRun("x.call(1)" ); |
| 23799 | CHECK(try_catch.HasCaught()); |
| 23800 | } |
| 23801 | { |
| 23802 | TryCatch try_catch(isolate); |
| 23803 | auto result = CompileRun("s.x = x; s.x()" ); |
| 23804 | CHECK(!try_catch.HasCaught()); |
| 23805 | CHECK_EQ(42, result->Int32Value(context.local()).FromJust()); |
| 23806 | } |
| 23807 | { |
| 23808 | TryCatch try_catch(isolate); |
| 23809 | auto result = CompileRun("x.call(s)" ); |
| 23810 | CHECK(!try_catch.HasCaught()); |
| 23811 | CHECK_EQ(42, result->Int32Value(context.local()).FromJust()); |
| 23812 | } |
| 23813 | } |
| 23814 | |
| 23815 | |
| 23816 | TEST(ChainSignatureCheck) { |
| 23817 | LocalContext context; |
| 23818 | auto isolate = context->GetIsolate(); |
| 23819 | v8::HandleScope scope(isolate); |
| 23820 | auto global = context->Global(); |
| 23821 | auto sig_obj = FunctionTemplate::New(isolate); |
| 23822 | auto sig = v8::Signature::New(isolate, sig_obj); |
| 23823 | for (int i = 0; i < 4; ++i) { |
| 23824 | auto temp = FunctionTemplate::New(isolate); |
| 23825 | temp->Inherit(sig_obj); |
| 23826 | sig_obj = temp; |
| 23827 | } |
| 23828 | auto x = FunctionTemplate::New(isolate, Returns42, Local<Value>(), sig); |
| 23829 | global->Set(context.local(), v8_str("sig_obj" ), |
| 23830 | sig_obj->GetFunction(context.local()).ToLocalChecked()) |
| 23831 | .FromJust(); |
| 23832 | global->Set(context.local(), v8_str("x" ), |
| 23833 | x->GetFunction(context.local()).ToLocalChecked()) |
| 23834 | .FromJust(); |
| 23835 | CompileRun("var s = new sig_obj();" ); |
| 23836 | { |
| 23837 | TryCatch try_catch(isolate); |
| 23838 | CompileRun("x()" ); |
| 23839 | CHECK(try_catch.HasCaught()); |
| 23840 | } |
| 23841 | { |
| 23842 | TryCatch try_catch(isolate); |
| 23843 | CompileRun("x.call(1)" ); |
| 23844 | CHECK(try_catch.HasCaught()); |
| 23845 | } |
| 23846 | { |
| 23847 | TryCatch try_catch(isolate); |
| 23848 | auto result = CompileRun("s.x = x; s.x()" ); |
| 23849 | CHECK(!try_catch.HasCaught()); |
| 23850 | CHECK_EQ(42, result->Int32Value(context.local()).FromJust()); |
| 23851 | } |
| 23852 | { |
| 23853 | TryCatch try_catch(isolate); |
| 23854 | auto result = CompileRun("x.call(s)" ); |
| 23855 | CHECK(!try_catch.HasCaught()); |
| 23856 | CHECK_EQ(42, result->Int32Value(context.local()).FromJust()); |
| 23857 | } |
| 23858 | } |
| 23859 | |
| 23860 | |
| 23861 | static const char* last_event_message; |
| 23862 | static int last_event_status; |
| 23863 | void StoringEventLoggerCallback(const char* message, int status) { |
| 23864 | last_event_message = message; |
| 23865 | last_event_status = status; |
| 23866 | } |
| 23867 | |
| 23868 | |
| 23869 | TEST(EventLogging) { |
| 23870 | v8::Isolate* isolate = CcTest::isolate(); |
| 23871 | isolate->SetEventLogger(StoringEventLoggerCallback); |
| 23872 | v8::internal::HistogramTimer histogramTimer( |
| 23873 | "V8.Test" , 0, 10000, v8::internal::HistogramTimerResolution::MILLISECOND, |
| 23874 | 50, reinterpret_cast<v8::internal::Isolate*>(isolate)->counters()); |
| 23875 | histogramTimer.Start(); |
| 23876 | CHECK_EQ(0, strcmp("V8.Test" , last_event_message)); |
| 23877 | CHECK_EQ(0, last_event_status); |
| 23878 | histogramTimer.Stop(); |
| 23879 | CHECK_EQ(0, strcmp("V8.Test" , last_event_message)); |
| 23880 | CHECK_EQ(1, last_event_status); |
| 23881 | } |
| 23882 | |
| 23883 | TEST(PropertyDescriptor) { |
| 23884 | LocalContext context; |
| 23885 | v8::Isolate* isolate = context->GetIsolate(); |
| 23886 | v8::HandleScope scope(isolate); |
| 23887 | |
| 23888 | { // empty descriptor |
| 23889 | v8::PropertyDescriptor desc; |
| 23890 | CHECK(!desc.has_value()); |
| 23891 | CHECK(!desc.has_set()); |
| 23892 | CHECK(!desc.has_get()); |
| 23893 | CHECK(!desc.has_enumerable()); |
| 23894 | CHECK(!desc.has_configurable()); |
| 23895 | CHECK(!desc.has_writable()); |
| 23896 | } |
| 23897 | { |
| 23898 | // data descriptor |
| 23899 | v8::PropertyDescriptor desc(v8_num(42)); |
| 23900 | desc.set_enumerable(false); |
| 23901 | CHECK(desc.value() == v8_num(42)); |
| 23902 | CHECK(desc.has_value()); |
| 23903 | CHECK(!desc.has_set()); |
| 23904 | CHECK(!desc.has_get()); |
| 23905 | CHECK(desc.has_enumerable()); |
| 23906 | CHECK(!desc.enumerable()); |
| 23907 | CHECK(!desc.has_configurable()); |
| 23908 | CHECK(!desc.has_writable()); |
| 23909 | } |
| 23910 | { |
| 23911 | // data descriptor |
| 23912 | v8::PropertyDescriptor desc(v8_num(42)); |
| 23913 | desc.set_configurable(true); |
| 23914 | CHECK(desc.value() == v8_num(42)); |
| 23915 | CHECK(desc.has_value()); |
| 23916 | CHECK(!desc.has_set()); |
| 23917 | CHECK(!desc.has_get()); |
| 23918 | CHECK(desc.has_configurable()); |
| 23919 | CHECK(desc.configurable()); |
| 23920 | CHECK(!desc.has_enumerable()); |
| 23921 | CHECK(!desc.has_writable()); |
| 23922 | } |
| 23923 | { |
| 23924 | // data descriptor |
| 23925 | v8::PropertyDescriptor desc(v8_num(42)); |
| 23926 | desc.set_configurable(false); |
| 23927 | CHECK(desc.value() == v8_num(42)); |
| 23928 | CHECK(desc.has_value()); |
| 23929 | CHECK(!desc.has_set()); |
| 23930 | CHECK(!desc.has_get()); |
| 23931 | CHECK(desc.has_configurable()); |
| 23932 | CHECK(!desc.configurable()); |
| 23933 | CHECK(!desc.has_enumerable()); |
| 23934 | CHECK(!desc.has_writable()); |
| 23935 | } |
| 23936 | { |
| 23937 | // data descriptor |
| 23938 | v8::PropertyDescriptor desc(v8_num(42), false); |
| 23939 | CHECK(desc.value() == v8_num(42)); |
| 23940 | CHECK(desc.has_value()); |
| 23941 | CHECK(!desc.has_set()); |
| 23942 | CHECK(!desc.has_get()); |
| 23943 | CHECK(!desc.has_enumerable()); |
| 23944 | CHECK(!desc.has_configurable()); |
| 23945 | CHECK(desc.has_writable()); |
| 23946 | CHECK(!desc.writable()); |
| 23947 | } |
| 23948 | { |
| 23949 | // data descriptor |
| 23950 | v8::PropertyDescriptor desc(v8::Local<v8::Value>(), true); |
| 23951 | CHECK(!desc.has_value()); |
| 23952 | CHECK(!desc.has_set()); |
| 23953 | CHECK(!desc.has_get()); |
| 23954 | CHECK(!desc.has_enumerable()); |
| 23955 | CHECK(!desc.has_configurable()); |
| 23956 | CHECK(desc.has_writable()); |
| 23957 | CHECK(desc.writable()); |
| 23958 | } |
| 23959 | { |
| 23960 | // accessor descriptor |
| 23961 | CompileRun("var set = function() {return 43;};" ); |
| 23962 | |
| 23963 | v8::Local<v8::Function> set = |
| 23964 | v8::Local<v8::Function>::Cast(context->Global() |
| 23965 | ->Get(context.local(), v8_str("set" )) |
| 23966 | .ToLocalChecked()); |
| 23967 | v8::PropertyDescriptor desc(v8::Undefined(isolate), set); |
| 23968 | desc.set_configurable(false); |
| 23969 | CHECK(!desc.has_value()); |
| 23970 | CHECK(desc.has_get()); |
| 23971 | CHECK(desc.get() == v8::Undefined(isolate)); |
| 23972 | CHECK(desc.has_set()); |
| 23973 | CHECK(desc.set() == set); |
| 23974 | CHECK(!desc.has_enumerable()); |
| 23975 | CHECK(desc.has_configurable()); |
| 23976 | CHECK(!desc.configurable()); |
| 23977 | CHECK(!desc.has_writable()); |
| 23978 | } |
| 23979 | { |
| 23980 | // accessor descriptor with Proxy |
| 23981 | CompileRun( |
| 23982 | "var set = new Proxy(function() {}, {});" |
| 23983 | "var get = undefined;" ); |
| 23984 | |
| 23985 | v8::Local<v8::Value> get = |
| 23986 | v8::Local<v8::Value>::Cast(context->Global() |
| 23987 | ->Get(context.local(), v8_str("get" )) |
| 23988 | .ToLocalChecked()); |
| 23989 | v8::Local<v8::Function> set = |
| 23990 | v8::Local<v8::Function>::Cast(context->Global() |
| 23991 | ->Get(context.local(), v8_str("set" )) |
| 23992 | .ToLocalChecked()); |
| 23993 | v8::PropertyDescriptor desc(get, set); |
| 23994 | desc.set_configurable(false); |
| 23995 | CHECK(!desc.has_value()); |
| 23996 | CHECK(desc.get() == v8::Undefined(isolate)); |
| 23997 | CHECK(desc.has_get()); |
| 23998 | CHECK(desc.set() == set); |
| 23999 | CHECK(desc.has_set()); |
| 24000 | CHECK(!desc.has_enumerable()); |
| 24001 | CHECK(desc.has_configurable()); |
| 24002 | CHECK(!desc.configurable()); |
| 24003 | CHECK(!desc.has_writable()); |
| 24004 | } |
| 24005 | { |
| 24006 | // accessor descriptor with empty function handle |
| 24007 | v8::Local<v8::Function> get = v8::Local<v8::Function>(); |
| 24008 | v8::PropertyDescriptor desc(get, get); |
| 24009 | CHECK(!desc.has_value()); |
| 24010 | CHECK(!desc.has_get()); |
| 24011 | CHECK(!desc.has_set()); |
| 24012 | CHECK(!desc.has_enumerable()); |
| 24013 | CHECK(!desc.has_configurable()); |
| 24014 | CHECK(!desc.has_writable()); |
| 24015 | } |
| 24016 | } |
| 24017 | |
| 24018 | TEST(Promises) { |
| 24019 | LocalContext context; |
| 24020 | v8::Isolate* isolate = context->GetIsolate(); |
| 24021 | v8::HandleScope scope(isolate); |
| 24022 | |
| 24023 | // Creation. |
| 24024 | Local<v8::Promise::Resolver> pr = |
| 24025 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24026 | Local<v8::Promise::Resolver> rr = |
| 24027 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24028 | Local<v8::Promise> p = pr->GetPromise(); |
| 24029 | Local<v8::Promise> r = rr->GetPromise(); |
| 24030 | |
| 24031 | // IsPromise predicate. |
| 24032 | CHECK(p->IsPromise()); |
| 24033 | CHECK(r->IsPromise()); |
| 24034 | Local<Value> o = v8::Object::New(isolate); |
| 24035 | CHECK(!o->IsPromise()); |
| 24036 | |
| 24037 | // Resolution and rejection. |
| 24038 | pr->Resolve(context.local(), v8::Integer::New(isolate, 1)).FromJust(); |
| 24039 | CHECK(p->IsPromise()); |
| 24040 | rr->Reject(context.local(), v8::Integer::New(isolate, 2)).FromJust(); |
| 24041 | CHECK(r->IsPromise()); |
| 24042 | } |
| 24043 | |
| 24044 | // Promise.Then(on_fulfilled) |
| 24045 | TEST(PromiseThen) { |
| 24046 | LocalContext context; |
| 24047 | v8::Isolate* isolate = context->GetIsolate(); |
| 24048 | isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); |
| 24049 | v8::HandleScope scope(isolate); |
| 24050 | Local<Object> global = context->Global(); |
| 24051 | |
| 24052 | // Creation. |
| 24053 | Local<v8::Promise::Resolver> pr = |
| 24054 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24055 | Local<v8::Promise::Resolver> qr = |
| 24056 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24057 | Local<v8::Promise> p = pr->GetPromise(); |
| 24058 | Local<v8::Promise> q = qr->GetPromise(); |
| 24059 | |
| 24060 | CHECK(p->IsPromise()); |
| 24061 | CHECK(q->IsPromise()); |
| 24062 | |
| 24063 | pr->Resolve(context.local(), v8::Integer::New(isolate, 1)).FromJust(); |
| 24064 | qr->Resolve(context.local(), p).FromJust(); |
| 24065 | |
| 24066 | // Chaining non-pending promises. |
| 24067 | CompileRun( |
| 24068 | "var x1 = 0;\n" |
| 24069 | "var x2 = 0;\n" |
| 24070 | "function f1(x) { x1 = x; return x+1 };\n" |
| 24071 | "function f2(x) { x2 = x; return x+1 };\n" ); |
| 24072 | Local<Function> f1 = Local<Function>::Cast( |
| 24073 | global->Get(context.local(), v8_str("f1" )).ToLocalChecked()); |
| 24074 | Local<Function> f2 = Local<Function>::Cast( |
| 24075 | global->Get(context.local(), v8_str("f2" )).ToLocalChecked()); |
| 24076 | |
| 24077 | // Then |
| 24078 | CompileRun("x1 = x2 = 0;" ); |
| 24079 | q->Then(context.local(), f1).ToLocalChecked(); |
| 24080 | CHECK_EQ(0, global->Get(context.local(), v8_str("x1" )) |
| 24081 | .ToLocalChecked() |
| 24082 | ->Int32Value(context.local()) |
| 24083 | .FromJust()); |
| 24084 | isolate->RunMicrotasks(); |
| 24085 | CHECK_EQ(1, global->Get(context.local(), v8_str("x1" )) |
| 24086 | .ToLocalChecked() |
| 24087 | ->Int32Value(context.local()) |
| 24088 | .FromJust()); |
| 24089 | |
| 24090 | // Then |
| 24091 | CompileRun("x1 = x2 = 0;" ); |
| 24092 | pr = v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24093 | qr = v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24094 | |
| 24095 | qr->Resolve(context.local(), pr).FromJust(); |
| 24096 | qr->GetPromise() |
| 24097 | ->Then(context.local(), f1) |
| 24098 | .ToLocalChecked() |
| 24099 | ->Then(context.local(), f2) |
| 24100 | .ToLocalChecked(); |
| 24101 | |
| 24102 | CHECK_EQ(0, global->Get(context.local(), v8_str("x1" )) |
| 24103 | .ToLocalChecked() |
| 24104 | ->Int32Value(context.local()) |
| 24105 | .FromJust()); |
| 24106 | CHECK_EQ(0, global->Get(context.local(), v8_str("x2" )) |
| 24107 | .ToLocalChecked() |
| 24108 | ->Int32Value(context.local()) |
| 24109 | .FromJust()); |
| 24110 | isolate->RunMicrotasks(); |
| 24111 | CHECK_EQ(0, global->Get(context.local(), v8_str("x1" )) |
| 24112 | .ToLocalChecked() |
| 24113 | ->Int32Value(context.local()) |
| 24114 | .FromJust()); |
| 24115 | CHECK_EQ(0, global->Get(context.local(), v8_str("x2" )) |
| 24116 | .ToLocalChecked() |
| 24117 | ->Int32Value(context.local()) |
| 24118 | .FromJust()); |
| 24119 | |
| 24120 | pr->Resolve(context.local(), v8::Integer::New(isolate, 3)).FromJust(); |
| 24121 | |
| 24122 | CHECK_EQ(0, global->Get(context.local(), v8_str("x1" )) |
| 24123 | .ToLocalChecked() |
| 24124 | ->Int32Value(context.local()) |
| 24125 | .FromJust()); |
| 24126 | CHECK_EQ(0, global->Get(context.local(), v8_str("x2" )) |
| 24127 | .ToLocalChecked() |
| 24128 | ->Int32Value(context.local()) |
| 24129 | .FromJust()); |
| 24130 | isolate->RunMicrotasks(); |
| 24131 | CHECK_EQ(3, global->Get(context.local(), v8_str("x1" )) |
| 24132 | .ToLocalChecked() |
| 24133 | ->Int32Value(context.local()) |
| 24134 | .FromJust()); |
| 24135 | CHECK_EQ(4, global->Get(context.local(), v8_str("x2" )) |
| 24136 | .ToLocalChecked() |
| 24137 | ->Int32Value(context.local()) |
| 24138 | .FromJust()); |
| 24139 | } |
| 24140 | |
| 24141 | // Promise.Then(on_fulfilled, on_rejected) |
| 24142 | TEST(PromiseThen2) { |
| 24143 | LocalContext context; |
| 24144 | v8::Isolate* isolate = context->GetIsolate(); |
| 24145 | isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); |
| 24146 | v8::HandleScope scope(isolate); |
| 24147 | Local<Object> global = context->Global(); |
| 24148 | |
| 24149 | // Creation. |
| 24150 | Local<v8::Promise::Resolver> pr = |
| 24151 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24152 | Local<v8::Promise> p = pr->GetPromise(); |
| 24153 | |
| 24154 | CHECK(p->IsPromise()); |
| 24155 | |
| 24156 | pr->Resolve(context.local(), v8::Integer::New(isolate, 1)).FromJust(); |
| 24157 | |
| 24158 | // Chaining non-pending promises. |
| 24159 | CompileRun( |
| 24160 | "var x1 = 0;\n" |
| 24161 | "var x2 = 0;\n" |
| 24162 | "function f1(x) { x1 = x; return x+1 };\n" |
| 24163 | "function f2(x) { x2 = x; return x+1 };\n" |
| 24164 | "function f3(x) { throw x + 100 };\n" ); |
| 24165 | Local<Function> f1 = Local<Function>::Cast( |
| 24166 | global->Get(context.local(), v8_str("f1" )).ToLocalChecked()); |
| 24167 | Local<Function> f2 = Local<Function>::Cast( |
| 24168 | global->Get(context.local(), v8_str("f2" )).ToLocalChecked()); |
| 24169 | Local<Function> f3 = Local<Function>::Cast( |
| 24170 | global->Get(context.local(), v8_str("f3" )).ToLocalChecked()); |
| 24171 | |
| 24172 | // Then |
| 24173 | CompileRun("x1 = x2 = 0;" ); |
| 24174 | Local<v8::Promise> a = p->Then(context.local(), f1, f2).ToLocalChecked(); |
| 24175 | CHECK_EQ(0, global->Get(context.local(), v8_str("x1" )) |
| 24176 | .ToLocalChecked() |
| 24177 | ->Int32Value(context.local()) |
| 24178 | .FromJust()); |
| 24179 | isolate->RunMicrotasks(); |
| 24180 | CHECK_EQ(1, global->Get(context.local(), v8_str("x1" )) |
| 24181 | .ToLocalChecked() |
| 24182 | ->Int32Value(context.local()) |
| 24183 | .FromJust()); |
| 24184 | CHECK_EQ(0, global->Get(context.local(), v8_str("x2" )) |
| 24185 | .ToLocalChecked() |
| 24186 | ->Int32Value(context.local()) |
| 24187 | .FromJust()); |
| 24188 | |
| 24189 | Local<v8::Promise> b = a->Then(context.local(), f3, f2).ToLocalChecked(); |
| 24190 | isolate->RunMicrotasks(); |
| 24191 | CHECK_EQ(1, global->Get(context.local(), v8_str("x1" )) |
| 24192 | .ToLocalChecked() |
| 24193 | ->Int32Value(context.local()) |
| 24194 | .FromJust()); |
| 24195 | CHECK_EQ(0, global->Get(context.local(), v8_str("x2" )) |
| 24196 | .ToLocalChecked() |
| 24197 | ->Int32Value(context.local()) |
| 24198 | .FromJust()); |
| 24199 | |
| 24200 | Local<v8::Promise> c = b->Then(context.local(), f1, f2).ToLocalChecked(); |
| 24201 | isolate->RunMicrotasks(); |
| 24202 | CHECK_EQ(1, global->Get(context.local(), v8_str("x1" )) |
| 24203 | .ToLocalChecked() |
| 24204 | ->Int32Value(context.local()) |
| 24205 | .FromJust()); |
| 24206 | CHECK_EQ(102, global->Get(context.local(), v8_str("x2" )) |
| 24207 | .ToLocalChecked() |
| 24208 | ->Int32Value(context.local()) |
| 24209 | .FromJust()); |
| 24210 | |
| 24211 | v8::Local<v8::Promise> d = c->Then(context.local(), f1, f2).ToLocalChecked(); |
| 24212 | isolate->RunMicrotasks(); |
| 24213 | CHECK_EQ(103, global->Get(context.local(), v8_str("x1" )) |
| 24214 | .ToLocalChecked() |
| 24215 | ->Int32Value(context.local()) |
| 24216 | .FromJust()); |
| 24217 | CHECK_EQ(102, global->Get(context.local(), v8_str("x2" )) |
| 24218 | .ToLocalChecked() |
| 24219 | ->Int32Value(context.local()) |
| 24220 | .FromJust()); |
| 24221 | |
| 24222 | v8::Local<v8::Promise> e = d->Then(context.local(), f3, f2).ToLocalChecked(); |
| 24223 | isolate->RunMicrotasks(); |
| 24224 | CHECK_EQ(103, global->Get(context.local(), v8_str("x1" )) |
| 24225 | .ToLocalChecked() |
| 24226 | ->Int32Value(context.local()) |
| 24227 | .FromJust()); |
| 24228 | CHECK_EQ(102, global->Get(context.local(), v8_str("x2" )) |
| 24229 | .ToLocalChecked() |
| 24230 | ->Int32Value(context.local()) |
| 24231 | .FromJust()); |
| 24232 | |
| 24233 | v8::Local<v8::Promise> f = e->Then(context.local(), f1, f3).ToLocalChecked(); |
| 24234 | isolate->RunMicrotasks(); |
| 24235 | CHECK_EQ(103, global->Get(context.local(), v8_str("x1" )) |
| 24236 | .ToLocalChecked() |
| 24237 | ->Int32Value(context.local()) |
| 24238 | .FromJust()); |
| 24239 | CHECK_EQ(102, global->Get(context.local(), v8_str("x2" )) |
| 24240 | .ToLocalChecked() |
| 24241 | ->Int32Value(context.local()) |
| 24242 | .FromJust()); |
| 24243 | |
| 24244 | f->Then(context.local(), f1, f2).ToLocalChecked(); |
| 24245 | isolate->RunMicrotasks(); |
| 24246 | CHECK_EQ(103, global->Get(context.local(), v8_str("x1" )) |
| 24247 | .ToLocalChecked() |
| 24248 | ->Int32Value(context.local()) |
| 24249 | .FromJust()); |
| 24250 | CHECK_EQ(304, global->Get(context.local(), v8_str("x2" )) |
| 24251 | .ToLocalChecked() |
| 24252 | ->Int32Value(context.local()) |
| 24253 | .FromJust()); |
| 24254 | } |
| 24255 | |
| 24256 | TEST(PromiseStateAndValue) { |
| 24257 | LocalContext context; |
| 24258 | v8::Isolate* isolate = context->GetIsolate(); |
| 24259 | v8::HandleScope scope(isolate); |
| 24260 | v8::Local<v8::Value> result = CompileRun( |
| 24261 | "var resolver;" |
| 24262 | "new Promise((res, rej) => { resolver = res; })" ); |
| 24263 | v8::Local<v8::Promise> promise = v8::Local<v8::Promise>::Cast(result); |
| 24264 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kPending); |
| 24265 | |
| 24266 | CompileRun("resolver('fulfilled')" ); |
| 24267 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kFulfilled); |
| 24268 | CHECK(v8_str("fulfilled" )->SameValue(promise->Result())); |
| 24269 | |
| 24270 | result = CompileRun("Promise.reject('rejected')" ); |
| 24271 | promise = v8::Local<v8::Promise>::Cast(result); |
| 24272 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kRejected); |
| 24273 | CHECK(v8_str("rejected" )->SameValue(promise->Result())); |
| 24274 | } |
| 24275 | |
| 24276 | TEST(ResolvedPromiseReFulfill) { |
| 24277 | LocalContext context; |
| 24278 | v8::Isolate* isolate = context->GetIsolate(); |
| 24279 | v8::HandleScope scope(isolate); |
| 24280 | v8::Local<v8::String> value1 = |
| 24281 | v8::String::NewFromUtf8(isolate, "foo" , v8::NewStringType::kNormal) |
| 24282 | .ToLocalChecked(); |
| 24283 | v8::Local<v8::String> value2 = |
| 24284 | v8::String::NewFromUtf8(isolate, "bar" , v8::NewStringType::kNormal) |
| 24285 | .ToLocalChecked(); |
| 24286 | |
| 24287 | v8::Local<v8::Promise::Resolver> resolver = |
| 24288 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24289 | v8::Local<v8::Promise> promise = resolver->GetPromise(); |
| 24290 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kPending); |
| 24291 | |
| 24292 | resolver->Resolve(context.local(), value1).ToChecked(); |
| 24293 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kFulfilled); |
| 24294 | CHECK_EQ(promise->Result(), value1); |
| 24295 | |
| 24296 | // This should be a no-op. |
| 24297 | resolver->Resolve(context.local(), value2).ToChecked(); |
| 24298 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kFulfilled); |
| 24299 | CHECK_EQ(promise->Result(), value1); |
| 24300 | |
| 24301 | // This should be a no-op. |
| 24302 | resolver->Reject(context.local(), value2).ToChecked(); |
| 24303 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kFulfilled); |
| 24304 | CHECK_EQ(promise->Result(), value1); |
| 24305 | } |
| 24306 | |
| 24307 | TEST(RejectedPromiseReFulfill) { |
| 24308 | LocalContext context; |
| 24309 | v8::Isolate* isolate = context->GetIsolate(); |
| 24310 | v8::HandleScope scope(isolate); |
| 24311 | v8::Local<v8::String> value1 = |
| 24312 | v8::String::NewFromUtf8(isolate, "foo" , v8::NewStringType::kNormal) |
| 24313 | .ToLocalChecked(); |
| 24314 | v8::Local<v8::String> value2 = |
| 24315 | v8::String::NewFromUtf8(isolate, "bar" , v8::NewStringType::kNormal) |
| 24316 | .ToLocalChecked(); |
| 24317 | |
| 24318 | v8::Local<v8::Promise::Resolver> resolver = |
| 24319 | v8::Promise::Resolver::New(context.local()).ToLocalChecked(); |
| 24320 | v8::Local<v8::Promise> promise = resolver->GetPromise(); |
| 24321 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kPending); |
| 24322 | |
| 24323 | resolver->Reject(context.local(), value1).ToChecked(); |
| 24324 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kRejected); |
| 24325 | CHECK_EQ(promise->Result(), value1); |
| 24326 | |
| 24327 | // This should be a no-op. |
| 24328 | resolver->Reject(context.local(), value2).ToChecked(); |
| 24329 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kRejected); |
| 24330 | CHECK_EQ(promise->Result(), value1); |
| 24331 | |
| 24332 | // This should be a no-op. |
| 24333 | resolver->Resolve(context.local(), value2).ToChecked(); |
| 24334 | CHECK_EQ(promise->State(), v8::Promise::PromiseState::kRejected); |
| 24335 | CHECK_EQ(promise->Result(), value1); |
| 24336 | } |
| 24337 | |
| 24338 | TEST(DisallowJavascriptExecutionScope) { |
| 24339 | LocalContext context; |
| 24340 | v8::Isolate* isolate = context->GetIsolate(); |
| 24341 | v8::HandleScope scope(isolate); |
| 24342 | v8::Isolate::DisallowJavascriptExecutionScope no_js( |
| 24343 | isolate, v8::Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE); |
| 24344 | CompileRun("2+2" ); |
| 24345 | } |
| 24346 | |
| 24347 | TEST(AllowJavascriptExecutionScope) { |
| 24348 | LocalContext context; |
| 24349 | v8::Isolate* isolate = context->GetIsolate(); |
| 24350 | v8::HandleScope scope(isolate); |
| 24351 | v8::Isolate::DisallowJavascriptExecutionScope no_js( |
| 24352 | isolate, v8::Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE); |
| 24353 | v8::Isolate::DisallowJavascriptExecutionScope throw_js( |
| 24354 | isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); |
| 24355 | { v8::Isolate::AllowJavascriptExecutionScope yes_js(isolate); |
| 24356 | CompileRun("1+1" ); |
| 24357 | } |
| 24358 | } |
| 24359 | |
| 24360 | TEST(ThrowOnJavascriptExecution) { |
| 24361 | LocalContext context; |
| 24362 | v8::Isolate* isolate = context->GetIsolate(); |
| 24363 | v8::HandleScope scope(isolate); |
| 24364 | v8::TryCatch try_catch(isolate); |
| 24365 | v8::Isolate::DisallowJavascriptExecutionScope throw_js( |
| 24366 | isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); |
| 24367 | CompileRun("1+1" ); |
| 24368 | CHECK(try_catch.HasCaught()); |
| 24369 | } |
| 24370 | |
| 24371 | namespace { |
| 24372 | |
| 24373 | class MockPlatform : public TestPlatform { |
| 24374 | public: |
| 24375 | MockPlatform() : old_platform_(i::V8::GetCurrentPlatform()) { |
| 24376 | // Now that it's completely constructed, make this the current platform. |
| 24377 | i::V8::SetPlatformForTesting(this); |
| 24378 | } |
| 24379 | ~MockPlatform() override { i::V8::SetPlatformForTesting(old_platform_); } |
| 24380 | |
| 24381 | bool dump_without_crashing_called() const { |
| 24382 | return dump_without_crashing_called_; |
| 24383 | } |
| 24384 | |
| 24385 | void DumpWithoutCrashing() override { dump_without_crashing_called_ = true; } |
| 24386 | |
| 24387 | private: |
| 24388 | v8::Platform* old_platform_; |
| 24389 | bool dump_without_crashing_called_ = false; |
| 24390 | }; |
| 24391 | |
| 24392 | } // namespace |
| 24393 | |
| 24394 | TEST(DumpOnJavascriptExecution) { |
| 24395 | MockPlatform platform; |
| 24396 | |
| 24397 | LocalContext context; |
| 24398 | v8::Isolate* isolate = context->GetIsolate(); |
| 24399 | v8::HandleScope scope(isolate); |
| 24400 | v8::Isolate::DisallowJavascriptExecutionScope throw_js( |
| 24401 | isolate, v8::Isolate::DisallowJavascriptExecutionScope::DUMP_ON_FAILURE); |
| 24402 | CHECK(!platform.dump_without_crashing_called()); |
| 24403 | CompileRun("1+1" ); |
| 24404 | CHECK(platform.dump_without_crashing_called()); |
| 24405 | } |
| 24406 | |
| 24407 | TEST(Regress354123) { |
| 24408 | LocalContext current; |
| 24409 | v8::Isolate* isolate = current->GetIsolate(); |
| 24410 | v8::HandleScope scope(isolate); |
| 24411 | |
| 24412 | v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); |
| 24413 | templ->SetAccessCheckCallback(AccessCounter); |
| 24414 | CHECK(current->Global() |
| 24415 | ->Set(current.local(), v8_str("friend" ), |
| 24416 | templ->NewInstance(current.local()).ToLocalChecked()) |
| 24417 | .FromJust()); |
| 24418 | |
| 24419 | // Test access using __proto__ from the prototype chain. |
| 24420 | access_count = 0; |
| 24421 | CompileRun("friend.__proto__ = {};" ); |
| 24422 | CHECK_EQ(2, access_count); |
| 24423 | CompileRun("friend.__proto__;" ); |
| 24424 | CHECK_EQ(4, access_count); |
| 24425 | |
| 24426 | // Test access using __proto__ as a hijacked function (A). |
| 24427 | access_count = 0; |
| 24428 | CompileRun("var p = Object.prototype;" |
| 24429 | "var f = Object.getOwnPropertyDescriptor(p, '__proto__').set;" |
| 24430 | "f.call(friend, {});" ); |
| 24431 | CHECK_EQ(1, access_count); |
| 24432 | CompileRun("var p = Object.prototype;" |
| 24433 | "var f = Object.getOwnPropertyDescriptor(p, '__proto__').get;" |
| 24434 | "f.call(friend);" ); |
| 24435 | CHECK_EQ(2, access_count); |
| 24436 | |
| 24437 | // Test access using __proto__ as a hijacked function (B). |
| 24438 | access_count = 0; |
| 24439 | CompileRun("var f = Object.prototype.__lookupSetter__('__proto__');" |
| 24440 | "f.call(friend, {});" ); |
| 24441 | CHECK_EQ(1, access_count); |
| 24442 | CompileRun("var f = Object.prototype.__lookupGetter__('__proto__');" |
| 24443 | "f.call(friend);" ); |
| 24444 | CHECK_EQ(2, access_count); |
| 24445 | |
| 24446 | // Test access using Object.setPrototypeOf reflective method. |
| 24447 | access_count = 0; |
| 24448 | CompileRun("Object.setPrototypeOf(friend, {});" ); |
| 24449 | CHECK_EQ(1, access_count); |
| 24450 | CompileRun("Object.getPrototypeOf(friend);" ); |
| 24451 | CHECK_EQ(2, access_count); |
| 24452 | } |
| 24453 | |
| 24454 | |
| 24455 | TEST(CaptureStackTraceForStackOverflow) { |
| 24456 | v8::internal::FLAG_stack_size = 150; |
| 24457 | LocalContext current; |
| 24458 | v8::Isolate* isolate = current->GetIsolate(); |
| 24459 | v8::HandleScope scope(isolate); |
| 24460 | isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10, |
| 24461 | v8::StackTrace::kDetailed); |
| 24462 | v8::TryCatch try_catch(isolate); |
| 24463 | CompileRun("(function f(x) { f(x+1); })(0)" ); |
| 24464 | CHECK(try_catch.HasCaught()); |
| 24465 | } |
| 24466 | |
| 24467 | namespace { |
| 24468 | bool ValueEqualsString(v8::Isolate* isolate, Local<Value> lhs, |
| 24469 | const char* rhs) { |
| 24470 | CHECK(!lhs.IsEmpty()); |
| 24471 | CHECK(lhs->IsString()); |
| 24472 | String::Utf8Value utf8_lhs(isolate, lhs); |
| 24473 | return strcmp(rhs, *utf8_lhs) == 0; |
| 24474 | } |
| 24475 | } // namespace |
| 24476 | |
| 24477 | TEST(ScriptNameAndLineNumber) { |
| 24478 | LocalContext env; |
| 24479 | v8::Isolate* isolate = env->GetIsolate(); |
| 24480 | v8::HandleScope scope(isolate); |
| 24481 | const char* url = "http://www.foo.com/foo.js" ; |
| 24482 | v8::ScriptOrigin origin(v8_str(url), v8::Integer::New(isolate, 13)); |
| 24483 | v8::ScriptCompiler::Source script_source(v8_str("var foo;" ), origin); |
| 24484 | |
| 24485 | Local<Script> script = |
| 24486 | v8::ScriptCompiler::Compile(env.local(), &script_source).ToLocalChecked(); |
| 24487 | CHECK(ValueEqualsString(isolate, script->GetUnboundScript()->GetScriptName(), |
| 24488 | url)); |
| 24489 | |
| 24490 | int line_number = script->GetUnboundScript()->GetLineNumber(0); |
| 24491 | CHECK_EQ(13, line_number); |
| 24492 | } |
| 24493 | |
| 24494 | TEST(ScriptPositionInfo) { |
| 24495 | LocalContext env; |
| 24496 | v8::Isolate* isolate = env->GetIsolate(); |
| 24497 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 24498 | v8::HandleScope scope(isolate); |
| 24499 | const char* url = "http://www.foo.com/foo.js" ; |
| 24500 | v8::ScriptOrigin origin(v8_str(url), v8::Integer::New(isolate, 13)); |
| 24501 | v8::ScriptCompiler::Source script_source(v8_str("var foo;\n" |
| 24502 | "var bar;\n" |
| 24503 | "var fisk = foo + bar;\n" ), |
| 24504 | origin); |
| 24505 | Local<Script> script = |
| 24506 | v8::ScriptCompiler::Compile(env.local(), &script_source).ToLocalChecked(); |
| 24507 | |
| 24508 | i::Handle<i::SharedFunctionInfo> obj = i::Handle<i::SharedFunctionInfo>::cast( |
| 24509 | v8::Utils::OpenHandle(*script->GetUnboundScript())); |
| 24510 | CHECK(obj->script()->IsScript()); |
| 24511 | |
| 24512 | i::Handle<i::Script> script1(i::Script::cast(obj->script()), i_isolate); |
| 24513 | |
| 24514 | v8::internal::Script::PositionInfo info; |
| 24515 | |
| 24516 | for (int i = 0; i < 2; ++i) { |
| 24517 | // With offset. |
| 24518 | |
| 24519 | // Behave as if 0 was passed if position is negative. |
| 24520 | CHECK(script1->GetPositionInfo(-1, &info, script1->WITH_OFFSET)); |
| 24521 | CHECK_EQ(13, info.line); |
| 24522 | CHECK_EQ(0, info.column); |
| 24523 | CHECK_EQ(0, info.line_start); |
| 24524 | CHECK_EQ(8, info.line_end); |
| 24525 | |
| 24526 | CHECK(script1->GetPositionInfo(0, &info, script1->WITH_OFFSET)); |
| 24527 | CHECK_EQ(13, info.line); |
| 24528 | CHECK_EQ(0, info.column); |
| 24529 | CHECK_EQ(0, info.line_start); |
| 24530 | CHECK_EQ(8, info.line_end); |
| 24531 | |
| 24532 | CHECK(script1->GetPositionInfo(8, &info, script1->WITH_OFFSET)); |
| 24533 | CHECK_EQ(13, info.line); |
| 24534 | CHECK_EQ(8, info.column); |
| 24535 | CHECK_EQ(0, info.line_start); |
| 24536 | CHECK_EQ(8, info.line_end); |
| 24537 | |
| 24538 | CHECK(script1->GetPositionInfo(9, &info, script1->WITH_OFFSET)); |
| 24539 | CHECK_EQ(14, info.line); |
| 24540 | CHECK_EQ(0, info.column); |
| 24541 | CHECK_EQ(9, info.line_start); |
| 24542 | CHECK_EQ(17, info.line_end); |
| 24543 | |
| 24544 | // Fail when position is larger than script size. |
| 24545 | CHECK(!script1->GetPositionInfo(220384, &info, script1->WITH_OFFSET)); |
| 24546 | |
| 24547 | // Without offset. |
| 24548 | |
| 24549 | // Behave as if 0 was passed if position is negative. |
| 24550 | CHECK(script1->GetPositionInfo(-1, &info, script1->NO_OFFSET)); |
| 24551 | CHECK_EQ(0, info.line); |
| 24552 | CHECK_EQ(0, info.column); |
| 24553 | CHECK_EQ(0, info.line_start); |
| 24554 | CHECK_EQ(8, info.line_end); |
| 24555 | |
| 24556 | CHECK(script1->GetPositionInfo(0, &info, script1->NO_OFFSET)); |
| 24557 | CHECK_EQ(0, info.line); |
| 24558 | CHECK_EQ(0, info.column); |
| 24559 | CHECK_EQ(0, info.line_start); |
| 24560 | CHECK_EQ(8, info.line_end); |
| 24561 | |
| 24562 | CHECK(script1->GetPositionInfo(8, &info, script1->NO_OFFSET)); |
| 24563 | CHECK_EQ(0, info.line); |
| 24564 | CHECK_EQ(8, info.column); |
| 24565 | CHECK_EQ(0, info.line_start); |
| 24566 | CHECK_EQ(8, info.line_end); |
| 24567 | |
| 24568 | CHECK(script1->GetPositionInfo(9, &info, script1->NO_OFFSET)); |
| 24569 | CHECK_EQ(1, info.line); |
| 24570 | CHECK_EQ(0, info.column); |
| 24571 | CHECK_EQ(9, info.line_start); |
| 24572 | CHECK_EQ(17, info.line_end); |
| 24573 | |
| 24574 | // Fail when position is larger than script size. |
| 24575 | CHECK(!script1->GetPositionInfo(220384, &info, script1->NO_OFFSET)); |
| 24576 | |
| 24577 | i::Script::InitLineEnds(script1); |
| 24578 | } |
| 24579 | } |
| 24580 | |
| 24581 | void (v8::Isolate* isolate, Local<Script> script, |
| 24582 | const char* expected_source_url, |
| 24583 | const char* expected_source_mapping_url) { |
| 24584 | if (expected_source_url != nullptr) { |
| 24585 | v8::String::Utf8Value url(isolate, |
| 24586 | script->GetUnboundScript()->GetSourceURL()); |
| 24587 | CHECK_EQ(0, strcmp(expected_source_url, *url)); |
| 24588 | } else { |
| 24589 | CHECK(script->GetUnboundScript()->GetSourceURL()->IsUndefined()); |
| 24590 | } |
| 24591 | if (expected_source_mapping_url != nullptr) { |
| 24592 | v8::String::Utf8Value url( |
| 24593 | isolate, script->GetUnboundScript()->GetSourceMappingURL()); |
| 24594 | CHECK_EQ(0, strcmp(expected_source_mapping_url, *url)); |
| 24595 | } else { |
| 24596 | CHECK(script->GetUnboundScript()->GetSourceMappingURL()->IsUndefined()); |
| 24597 | } |
| 24598 | } |
| 24599 | |
| 24600 | void SourceURLHelper(v8::Isolate* isolate, const char* source, |
| 24601 | const char* expected_source_url, |
| 24602 | const char* expected_source_mapping_url) { |
| 24603 | Local<Script> script = v8_compile(source); |
| 24604 | CheckMagicComments(isolate, script, expected_source_url, |
| 24605 | expected_source_mapping_url); |
| 24606 | } |
| 24607 | |
| 24608 | |
| 24609 | TEST(ScriptSourceURLAndSourceMappingURL) { |
| 24610 | LocalContext env; |
| 24611 | v8::Isolate* isolate = env->GetIsolate(); |
| 24612 | v8::HandleScope scope(isolate); |
| 24613 | SourceURLHelper(isolate, |
| 24614 | "function foo() {}\n" |
| 24615 | "//# sourceURL=bar1.js\n" , |
| 24616 | "bar1.js" , nullptr); |
| 24617 | SourceURLHelper(isolate, |
| 24618 | "function foo() {}\n" |
| 24619 | "//# sourceMappingURL=bar2.js\n" , |
| 24620 | nullptr, "bar2.js" ); |
| 24621 | |
| 24622 | // Both sourceURL and sourceMappingURL. |
| 24623 | SourceURLHelper(isolate, |
| 24624 | "function foo() {}\n" |
| 24625 | "//# sourceURL=bar3.js\n" |
| 24626 | "//# sourceMappingURL=bar4.js\n" , |
| 24627 | "bar3.js" , "bar4.js" ); |
| 24628 | |
| 24629 | // Two source URLs; the first one is ignored. |
| 24630 | SourceURLHelper(isolate, |
| 24631 | "function foo() {}\n" |
| 24632 | "//# sourceURL=ignoreme.js\n" |
| 24633 | "//# sourceURL=bar5.js\n" , |
| 24634 | "bar5.js" , nullptr); |
| 24635 | SourceURLHelper(isolate, |
| 24636 | "function foo() {}\n" |
| 24637 | "//# sourceMappingURL=ignoreme.js\n" |
| 24638 | "//# sourceMappingURL=bar6.js\n" , |
| 24639 | nullptr, "bar6.js" ); |
| 24640 | |
| 24641 | // SourceURL or sourceMappingURL in the middle of the script. |
| 24642 | SourceURLHelper(isolate, |
| 24643 | "function foo() {}\n" |
| 24644 | "//# sourceURL=bar7.js\n" |
| 24645 | "function baz() {}\n" , |
| 24646 | "bar7.js" , nullptr); |
| 24647 | SourceURLHelper(isolate, |
| 24648 | "function foo() {}\n" |
| 24649 | "//# sourceMappingURL=bar8.js\n" |
| 24650 | "function baz() {}\n" , |
| 24651 | nullptr, "bar8.js" ); |
| 24652 | |
| 24653 | // Too much whitespace. |
| 24654 | SourceURLHelper(isolate, |
| 24655 | "function foo() {}\n" |
| 24656 | "//# sourceURL=bar9.js\n" |
| 24657 | "//# sourceMappingURL=bar10.js\n" , |
| 24658 | nullptr, nullptr); |
| 24659 | SourceURLHelper(isolate, |
| 24660 | "function foo() {}\n" |
| 24661 | "//# sourceURL =bar11.js\n" |
| 24662 | "//# sourceMappingURL =bar12.js\n" , |
| 24663 | nullptr, nullptr); |
| 24664 | |
| 24665 | // Disallowed characters in value. |
| 24666 | SourceURLHelper(isolate, |
| 24667 | "function foo() {}\n" |
| 24668 | "//# sourceURL=bar13 .js \n" |
| 24669 | "//# sourceMappingURL=bar14 .js \n" , |
| 24670 | nullptr, nullptr); |
| 24671 | SourceURLHelper(isolate, |
| 24672 | "function foo() {}\n" |
| 24673 | "//# sourceURL=bar15\t.js \n" |
| 24674 | "//# sourceMappingURL=bar16\t.js \n" , |
| 24675 | nullptr, nullptr); |
| 24676 | SourceURLHelper(isolate, |
| 24677 | "function foo() {}\n" |
| 24678 | "//# sourceURL=bar17'.js \n" |
| 24679 | "//# sourceMappingURL=bar18'.js \n" , |
| 24680 | nullptr, nullptr); |
| 24681 | SourceURLHelper(isolate, |
| 24682 | "function foo() {}\n" |
| 24683 | "//# sourceURL=bar19\".js \n" |
| 24684 | "//# sourceMappingURL=bar20\".js \n" , |
| 24685 | nullptr, nullptr); |
| 24686 | |
| 24687 | // Not too much whitespace. |
| 24688 | SourceURLHelper(isolate, |
| 24689 | "function foo() {}\n" |
| 24690 | "//# sourceURL= bar21.js \n" |
| 24691 | "//# sourceMappingURL= bar22.js \n" , |
| 24692 | "bar21.js" , "bar22.js" ); |
| 24693 | } |
| 24694 | |
| 24695 | |
| 24696 | TEST(GetOwnPropertyDescriptor) { |
| 24697 | LocalContext env; |
| 24698 | v8::Isolate* isolate = env->GetIsolate(); |
| 24699 | v8::HandleScope scope(isolate); |
| 24700 | CompileRun( |
| 24701 | "var x = { value : 13};" |
| 24702 | "Object.defineProperty(x, 'p0', {value : 12});" |
| 24703 | "Object.defineProperty(x, Symbol.toStringTag, {value: 'foo'});" |
| 24704 | "Object.defineProperty(x, 'p1', {" |
| 24705 | " set : function(value) { this.value = value; }," |
| 24706 | " get : function() { return this.value; }," |
| 24707 | "});" ); |
| 24708 | Local<Object> x = Local<Object>::Cast( |
| 24709 | env->Global()->Get(env.local(), v8_str("x" )).ToLocalChecked()); |
| 24710 | Local<Value> desc = |
| 24711 | x->GetOwnPropertyDescriptor(env.local(), v8_str("no_prop" )) |
| 24712 | .ToLocalChecked(); |
| 24713 | CHECK(desc->IsUndefined()); |
| 24714 | desc = |
| 24715 | x->GetOwnPropertyDescriptor(env.local(), v8_str("p0" )).ToLocalChecked(); |
| 24716 | CHECK(v8_num(12) |
| 24717 | ->Equals(env.local(), Local<Object>::Cast(desc) |
| 24718 | ->Get(env.local(), v8_str("value" )) |
| 24719 | .ToLocalChecked()) |
| 24720 | .FromJust()); |
| 24721 | desc = |
| 24722 | x->GetOwnPropertyDescriptor(env.local(), v8_str("p1" )).ToLocalChecked(); |
| 24723 | Local<Function> set = |
| 24724 | Local<Function>::Cast(Local<Object>::Cast(desc) |
| 24725 | ->Get(env.local(), v8_str("set" )) |
| 24726 | .ToLocalChecked()); |
| 24727 | Local<Function> get = |
| 24728 | Local<Function>::Cast(Local<Object>::Cast(desc) |
| 24729 | ->Get(env.local(), v8_str("get" )) |
| 24730 | .ToLocalChecked()); |
| 24731 | CHECK(v8_num(13) |
| 24732 | ->Equals(env.local(), |
| 24733 | get->Call(env.local(), x, 0, nullptr).ToLocalChecked()) |
| 24734 | .FromJust()); |
| 24735 | Local<Value> args[] = {v8_num(14)}; |
| 24736 | set->Call(env.local(), x, 1, args).ToLocalChecked(); |
| 24737 | CHECK(v8_num(14) |
| 24738 | ->Equals(env.local(), |
| 24739 | get->Call(env.local(), x, 0, nullptr).ToLocalChecked()) |
| 24740 | .FromJust()); |
| 24741 | desc = |
| 24742 | x->GetOwnPropertyDescriptor(env.local(), Symbol::GetToStringTag(isolate)) |
| 24743 | .ToLocalChecked(); |
| 24744 | CHECK(v8_str("foo" ) |
| 24745 | ->Equals(env.local(), Local<Object>::Cast(desc) |
| 24746 | ->Get(env.local(), v8_str("value" )) |
| 24747 | .ToLocalChecked()) |
| 24748 | .FromJust()); |
| 24749 | } |
| 24750 | |
| 24751 | |
| 24752 | TEST(Regress411877) { |
| 24753 | v8::Isolate* isolate = CcTest::isolate(); |
| 24754 | v8::HandleScope handle_scope(isolate); |
| 24755 | v8::Local<v8::ObjectTemplate> object_template = |
| 24756 | v8::ObjectTemplate::New(isolate); |
| 24757 | object_template->SetAccessCheckCallback(AccessCounter); |
| 24758 | |
| 24759 | v8::Local<Context> context = Context::New(isolate); |
| 24760 | v8::Context::Scope context_scope(context); |
| 24761 | |
| 24762 | CHECK(context->Global() |
| 24763 | ->Set(context, v8_str("o" ), |
| 24764 | object_template->NewInstance(context).ToLocalChecked()) |
| 24765 | .FromJust()); |
| 24766 | CompileRun("Object.getOwnPropertyNames(o)" ); |
| 24767 | } |
| 24768 | |
| 24769 | |
| 24770 | TEST(GetHiddenPropertyTableAfterAccessCheck) { |
| 24771 | v8::Isolate* isolate = CcTest::isolate(); |
| 24772 | v8::HandleScope handle_scope(isolate); |
| 24773 | v8::Local<v8::ObjectTemplate> object_template = |
| 24774 | v8::ObjectTemplate::New(isolate); |
| 24775 | object_template->SetAccessCheckCallback(AccessCounter); |
| 24776 | |
| 24777 | v8::Local<Context> context = Context::New(isolate); |
| 24778 | v8::Context::Scope context_scope(context); |
| 24779 | |
| 24780 | v8::Local<v8::Object> obj = |
| 24781 | object_template->NewInstance(context).ToLocalChecked(); |
| 24782 | obj->Set(context, v8_str("key" ), v8_str("value" )).FromJust(); |
| 24783 | obj->Delete(context, v8_str("key" )).FromJust(); |
| 24784 | |
| 24785 | obj->SetPrivate(context, v8::Private::New(isolate, v8_str("hidden key 2" )), |
| 24786 | v8_str("hidden value 2" )) |
| 24787 | .FromJust(); |
| 24788 | } |
| 24789 | |
| 24790 | |
| 24791 | TEST(Regress411793) { |
| 24792 | v8::Isolate* isolate = CcTest::isolate(); |
| 24793 | v8::HandleScope handle_scope(isolate); |
| 24794 | v8::Local<v8::ObjectTemplate> object_template = |
| 24795 | v8::ObjectTemplate::New(isolate); |
| 24796 | object_template->SetAccessCheckCallback(AccessCounter); |
| 24797 | |
| 24798 | v8::Local<Context> context = Context::New(isolate); |
| 24799 | v8::Context::Scope context_scope(context); |
| 24800 | |
| 24801 | CHECK(context->Global() |
| 24802 | ->Set(context, v8_str("o" ), |
| 24803 | object_template->NewInstance(context).ToLocalChecked()) |
| 24804 | .FromJust()); |
| 24805 | CompileRun( |
| 24806 | "Object.defineProperty(o, 'key', " |
| 24807 | " { get: function() {}, set: function() {} });" ); |
| 24808 | } |
| 24809 | |
| 24810 | class TestSourceStream : public v8::ScriptCompiler::ExternalSourceStream { |
| 24811 | public: |
| 24812 | explicit TestSourceStream(const char** chunks) : chunks_(chunks), index_(0) {} |
| 24813 | |
| 24814 | size_t GetMoreData(const uint8_t** src) override { |
| 24815 | // Unlike in real use cases, this function will never block. |
| 24816 | if (chunks_[index_] == nullptr) { |
| 24817 | return 0; |
| 24818 | } |
| 24819 | // Copy the data, since the caller takes ownership of it. |
| 24820 | size_t len = strlen(chunks_[index_]); |
| 24821 | // We don't need to zero-terminate since we return the length. |
| 24822 | uint8_t* copy = new uint8_t[len]; |
| 24823 | memcpy(copy, chunks_[index_], len); |
| 24824 | *src = copy; |
| 24825 | ++index_; |
| 24826 | return len; |
| 24827 | } |
| 24828 | |
| 24829 | // Helper for constructing a string from chunks (the compilation needs it |
| 24830 | // too). |
| 24831 | static char* FullSourceString(const char** chunks) { |
| 24832 | size_t total_len = 0; |
| 24833 | for (size_t i = 0; chunks[i] != nullptr; ++i) { |
| 24834 | total_len += strlen(chunks[i]); |
| 24835 | } |
| 24836 | char* full_string = new char[total_len + 1]; |
| 24837 | size_t offset = 0; |
| 24838 | for (size_t i = 0; chunks[i] != nullptr; ++i) { |
| 24839 | size_t len = strlen(chunks[i]); |
| 24840 | memcpy(full_string + offset, chunks[i], len); |
| 24841 | offset += len; |
| 24842 | } |
| 24843 | full_string[total_len] = 0; |
| 24844 | return full_string; |
| 24845 | } |
| 24846 | |
| 24847 | private: |
| 24848 | const char** chunks_; |
| 24849 | unsigned index_; |
| 24850 | }; |
| 24851 | |
| 24852 | |
| 24853 | // Helper function for running streaming tests. |
| 24854 | void RunStreamingTest(const char** chunks, |
| 24855 | v8::ScriptCompiler::StreamedSource::Encoding encoding = |
| 24856 | v8::ScriptCompiler::StreamedSource::ONE_BYTE, |
| 24857 | bool expected_success = true, |
| 24858 | const char* expected_source_url = nullptr, |
| 24859 | const char* expected_source_mapping_url = nullptr) { |
| 24860 | LocalContext env; |
| 24861 | v8::Isolate* isolate = env->GetIsolate(); |
| 24862 | v8::HandleScope scope(isolate); |
| 24863 | v8::TryCatch try_catch(isolate); |
| 24864 | |
| 24865 | v8::ScriptCompiler::StreamedSource source( |
| 24866 | v8::base::make_unique<TestSourceStream>(chunks), encoding); |
| 24867 | v8::ScriptCompiler::ScriptStreamingTask* task = |
| 24868 | v8::ScriptCompiler::StartStreamingScript(isolate, &source); |
| 24869 | |
| 24870 | // TestSourceStream::GetMoreData won't block, so it's OK to just run the |
| 24871 | // task here in the main thread. |
| 24872 | task->Run(); |
| 24873 | delete task; |
| 24874 | |
| 24875 | // Possible errors are only produced while compiling. |
| 24876 | CHECK(!try_catch.HasCaught()); |
| 24877 | |
| 24878 | v8::ScriptOrigin origin(v8_str("http://foo.com" )); |
| 24879 | char* full_source = TestSourceStream::FullSourceString(chunks); |
| 24880 | v8::MaybeLocal<Script> script = v8::ScriptCompiler::Compile( |
| 24881 | env.local(), &source, v8_str(full_source), origin); |
| 24882 | if (expected_success) { |
| 24883 | CHECK(!script.IsEmpty()); |
| 24884 | v8::Local<Value> result( |
| 24885 | script.ToLocalChecked()->Run(env.local()).ToLocalChecked()); |
| 24886 | // All scripts are supposed to return the fixed value 13 when ran. |
| 24887 | CHECK_EQ(13, result->Int32Value(env.local()).FromJust()); |
| 24888 | CheckMagicComments(isolate, script.ToLocalChecked(), expected_source_url, |
| 24889 | expected_source_mapping_url); |
| 24890 | } else { |
| 24891 | CHECK(script.IsEmpty()); |
| 24892 | CHECK(try_catch.HasCaught()); |
| 24893 | } |
| 24894 | delete[] full_source; |
| 24895 | } |
| 24896 | |
| 24897 | TEST(StreamingSimpleScript) { |
| 24898 | // This script is unrealistically small, since no one chunk is enough to fill |
| 24899 | // the backing buffer of Scanner, let alone overflow it. |
| 24900 | const char* chunks[] = {"function foo() { ret" , "urn 13; } f" , "oo(); " , |
| 24901 | nullptr}; |
| 24902 | RunStreamingTest(chunks); |
| 24903 | } |
| 24904 | |
| 24905 | TEST(StreamingScriptConstantArray) { |
| 24906 | // When run with Ignition, tests that the streaming parser canonicalizes |
| 24907 | // handles so that they are only added to the constant pool array once. |
| 24908 | const char* chunks[] = { |
| 24909 | "var a = {};" , "var b = {};" , "var c = 'testing';" , |
| 24910 | "var d = 'testing';" , "13;" , nullptr}; |
| 24911 | RunStreamingTest(chunks); |
| 24912 | } |
| 24913 | |
| 24914 | TEST(StreamingScriptEvalShadowing) { |
| 24915 | // When run with Ignition, tests that the streaming parser canonicalizes |
| 24916 | // handles so the Variable::is_possibly_eval() is correct. |
| 24917 | const char* chunk1 = |
| 24918 | "(function() {\n" |
| 24919 | " var y = 2;\n" |
| 24920 | " return (function() {\n" |
| 24921 | " eval('var y = 13;');\n" |
| 24922 | " function g() {\n" |
| 24923 | " return y\n" |
| 24924 | " }\n" |
| 24925 | " return g();\n" |
| 24926 | " })()\n" |
| 24927 | "})()\n" ; |
| 24928 | const char* chunks[] = {chunk1, nullptr}; |
| 24929 | RunStreamingTest(chunks); |
| 24930 | } |
| 24931 | |
| 24932 | TEST(StreamingBiggerScript) { |
| 24933 | const char* chunk1 = |
| 24934 | "function foo() {\n" |
| 24935 | " // Make this chunk sufficiently long so that it will overflow the\n" |
| 24936 | " // backing buffer of the Scanner.\n" |
| 24937 | " var i = 0;\n" |
| 24938 | " var result = 0;\n" |
| 24939 | " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 24940 | " result = 0;\n" |
| 24941 | " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 24942 | " result = 0;\n" |
| 24943 | " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 24944 | " result = 0;\n" |
| 24945 | " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 24946 | " return result;\n" |
| 24947 | "}\n" ; |
| 24948 | const char* chunks[] = {chunk1, "foo(); " , nullptr}; |
| 24949 | RunStreamingTest(chunks); |
| 24950 | } |
| 24951 | |
| 24952 | |
| 24953 | TEST(StreamingScriptWithParseError) { |
| 24954 | // Test that parse errors from streamed scripts are propagated correctly. |
| 24955 | { |
| 24956 | char chunk1[] = |
| 24957 | " // This will result in a parse error.\n" |
| 24958 | " var if else then foo" ; |
| 24959 | char chunk2[] = " 13\n" ; |
| 24960 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 24961 | |
| 24962 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::ONE_BYTE, |
| 24963 | false); |
| 24964 | } |
| 24965 | // Test that the next script succeeds normally. |
| 24966 | { |
| 24967 | char chunk1[] = |
| 24968 | " // This will be parsed successfully.\n" |
| 24969 | " function foo() { return " ; |
| 24970 | char chunk2[] = " 13; }\n" ; |
| 24971 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 24972 | |
| 24973 | RunStreamingTest(chunks); |
| 24974 | } |
| 24975 | } |
| 24976 | |
| 24977 | |
| 24978 | TEST(StreamingUtf8Script) { |
| 24979 | // We'd want to write \uc481 instead of \xec\x92\x81, but Windows compilers |
| 24980 | // don't like it. |
| 24981 | const char* chunk1 = |
| 24982 | "function foo() {\n" |
| 24983 | " // This function will contain an UTF-8 character which is not in\n" |
| 24984 | " // ASCII.\n" |
| 24985 | " var foob\xec\x92\x81r = 13;\n" |
| 24986 | " return foob\xec\x92\x81r;\n" |
| 24987 | "}\n" ; |
| 24988 | const char* chunks[] = {chunk1, "foo(); " , nullptr}; |
| 24989 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 24990 | } |
| 24991 | |
| 24992 | |
| 24993 | TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) { |
| 24994 | // A sanity check to prove that the approach of splitting UTF-8 |
| 24995 | // characters is correct. Here is an UTF-8 character which will take three |
| 24996 | // bytes. |
| 24997 | const char* reference = "\xec\x92\x81" ; |
| 24998 | CHECK_EQ(3, strlen(reference)); |
| 24999 | |
| 25000 | char chunk1[] = |
| 25001 | "function foo() {\n" |
| 25002 | " // This function will contain an UTF-8 character which is not in\n" |
| 25003 | " // ASCII.\n" |
| 25004 | " var foob" ; |
| 25005 | char chunk2[] = |
| 25006 | "XXXr = 13;\n" |
| 25007 | " return foob\xec\x92\x81r;\n" |
| 25008 | "}\n" ; |
| 25009 | for (int i = 0; i < 3; ++i) { |
| 25010 | chunk2[i] = reference[i]; |
| 25011 | } |
| 25012 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 25013 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25014 | } |
| 25015 | |
| 25016 | |
| 25017 | TEST(StreamingUtf8ScriptWithSplitCharacters) { |
| 25018 | // Stream data where a multi-byte UTF-8 character is split between two data |
| 25019 | // chunks. |
| 25020 | const char* reference = "\xec\x92\x81" ; |
| 25021 | char chunk1[] = |
| 25022 | "function foo() {\n" |
| 25023 | " // This function will contain an UTF-8 character which is not in\n" |
| 25024 | " // ASCII.\n" |
| 25025 | " var foobX" ; |
| 25026 | char chunk2[] = |
| 25027 | "XXr = 13;\n" |
| 25028 | " return foob\xec\x92\x81r;\n" |
| 25029 | "}\n" ; |
| 25030 | chunk1[strlen(chunk1) - 1] = reference[0]; |
| 25031 | chunk2[0] = reference[1]; |
| 25032 | chunk2[1] = reference[2]; |
| 25033 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 25034 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25035 | } |
| 25036 | |
| 25037 | |
| 25038 | TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) { |
| 25039 | // Tests edge cases which should still be decoded correctly. |
| 25040 | |
| 25041 | // Case 1: a chunk contains only bytes for a split character (and no other |
| 25042 | // data). This kind of a chunk would be exceptionally small, but we should |
| 25043 | // still decode it correctly. |
| 25044 | const char* reference = "\xec\x92\x81" ; |
| 25045 | // The small chunk is at the beginning of the split character |
| 25046 | { |
| 25047 | char chunk1[] = |
| 25048 | "function foo() {\n" |
| 25049 | " // This function will contain an UTF-8 character which is not in\n" |
| 25050 | " // ASCII.\n" |
| 25051 | " var foob" ; |
| 25052 | char chunk2[] = "XX" ; |
| 25053 | char chunk3[] = |
| 25054 | "Xr = 13;\n" |
| 25055 | " return foob\xec\x92\x81r;\n" |
| 25056 | "}\n" ; |
| 25057 | chunk2[0] = reference[0]; |
| 25058 | chunk2[1] = reference[1]; |
| 25059 | chunk3[0] = reference[2]; |
| 25060 | const char* chunks[] = {chunk1, chunk2, chunk3, "foo();" , nullptr}; |
| 25061 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25062 | } |
| 25063 | // The small chunk is at the end of a character |
| 25064 | { |
| 25065 | char chunk1[] = |
| 25066 | "function foo() {\n" |
| 25067 | " // This function will contain an UTF-8 character which is not in\n" |
| 25068 | " // ASCII.\n" |
| 25069 | " var foobX" ; |
| 25070 | char chunk2[] = "XX" ; |
| 25071 | char chunk3[] = |
| 25072 | "r = 13;\n" |
| 25073 | " return foob\xec\x92\x81r;\n" |
| 25074 | "}\n" ; |
| 25075 | chunk1[strlen(chunk1) - 1] = reference[0]; |
| 25076 | chunk2[0] = reference[1]; |
| 25077 | chunk2[1] = reference[2]; |
| 25078 | const char* chunks[] = {chunk1, chunk2, chunk3, "foo();" , nullptr}; |
| 25079 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25080 | } |
| 25081 | // Case 2: the script ends with a multi-byte character. Make sure that it's |
| 25082 | // decoded correctly and not just ignored. |
| 25083 | { |
| 25084 | char chunk1[] = |
| 25085 | "var foob\xec\x92\x81 = 13;\n" |
| 25086 | "foob\xec\x92\x81" ; |
| 25087 | const char* chunks[] = {chunk1, nullptr}; |
| 25088 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25089 | } |
| 25090 | } |
| 25091 | |
| 25092 | |
| 25093 | TEST(StreamingUtf8ScriptWithSplitCharactersInvalidEdgeCases) { |
| 25094 | // Test cases where a UTF-8 character is split over several chunks. Those |
| 25095 | // cases are not supported (the embedder should give the data in big enough |
| 25096 | // chunks), but we shouldn't crash and parse this just fine. |
| 25097 | const char* reference = "\xec\x92\x81" ; |
| 25098 | char chunk1[] = |
| 25099 | "function foo() {\n" |
| 25100 | " // This function will contain an UTF-8 character which is not in\n" |
| 25101 | " // ASCII.\n" |
| 25102 | " var foobX" ; |
| 25103 | char chunk2[] = "X" ; |
| 25104 | char chunk3[] = |
| 25105 | "Xr = 13;\n" |
| 25106 | " return foob\xec\x92\x81r;\n" |
| 25107 | "}\n" ; |
| 25108 | chunk1[strlen(chunk1) - 1] = reference[0]; |
| 25109 | chunk2[0] = reference[1]; |
| 25110 | chunk3[0] = reference[2]; |
| 25111 | const char* chunks[] = {chunk1, chunk2, chunk3, "foo();" , nullptr}; |
| 25112 | |
| 25113 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25114 | } |
| 25115 | |
| 25116 | |
| 25117 | |
| 25118 | TEST(StreamingWithDebuggingEnabledLate) { |
| 25119 | // The streaming parser can only parse lazily, i.e. inner functions are not |
| 25120 | // fully parsed. However, we may compile inner functions eagerly when |
| 25121 | // debugging. Make sure that we can deal with this when turning on debugging |
| 25122 | // after streaming parser has already finished parsing. |
| 25123 | const char* chunks[] = {"with({x:1}) {" , |
| 25124 | " var foo = function foo(y) {" , |
| 25125 | " return x + y;" , |
| 25126 | " };" , |
| 25127 | " foo(2);" , |
| 25128 | "}" , |
| 25129 | nullptr}; |
| 25130 | |
| 25131 | LocalContext env; |
| 25132 | v8::Isolate* isolate = env->GetIsolate(); |
| 25133 | v8::HandleScope scope(isolate); |
| 25134 | v8::TryCatch try_catch(isolate); |
| 25135 | |
| 25136 | v8::ScriptCompiler::StreamedSource source( |
| 25137 | v8::base::make_unique<TestSourceStream>(chunks), |
| 25138 | v8::ScriptCompiler::StreamedSource::ONE_BYTE); |
| 25139 | v8::ScriptCompiler::ScriptStreamingTask* task = |
| 25140 | v8::ScriptCompiler::StartStreamingScript(isolate, &source); |
| 25141 | |
| 25142 | task->Run(); |
| 25143 | delete task; |
| 25144 | |
| 25145 | CHECK(!try_catch.HasCaught()); |
| 25146 | |
| 25147 | v8::ScriptOrigin origin(v8_str("http://foo.com" )); |
| 25148 | char* full_source = TestSourceStream::FullSourceString(chunks); |
| 25149 | |
| 25150 | EnableDebugger(isolate); |
| 25151 | |
| 25152 | v8::Local<Script> script = |
| 25153 | v8::ScriptCompiler::Compile(env.local(), &source, v8_str(full_source), |
| 25154 | origin) |
| 25155 | .ToLocalChecked(); |
| 25156 | |
| 25157 | Maybe<uint32_t> result = |
| 25158 | script->Run(env.local()).ToLocalChecked()->Uint32Value(env.local()); |
| 25159 | CHECK_EQ(3U, result.FromMaybe(0)); |
| 25160 | |
| 25161 | delete[] full_source; |
| 25162 | |
| 25163 | DisableDebugger(isolate); |
| 25164 | } |
| 25165 | |
| 25166 | |
| 25167 | TEST(StreamingScriptWithInvalidUtf8) { |
| 25168 | // Regression test for a crash: test that invalid UTF-8 bytes in the end of a |
| 25169 | // chunk don't produce a crash. |
| 25170 | const char* reference = "\xec\x92\x81\x80\x80" ; |
| 25171 | char chunk1[] = |
| 25172 | "function foo() {\n" |
| 25173 | " // This function will contain an UTF-8 character which is not in\n" |
| 25174 | " // ASCII.\n" |
| 25175 | " var foobXXXXX" ; // Too many bytes which look like incomplete chars! |
| 25176 | char chunk2[] = |
| 25177 | "r = 13;\n" |
| 25178 | " return foob\xec\x92\x81\x80\x80r;\n" |
| 25179 | "}\n" ; |
| 25180 | for (int i = 0; i < 5; ++i) chunk1[strlen(chunk1) - 5 + i] = reference[i]; |
| 25181 | |
| 25182 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 25183 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, false); |
| 25184 | } |
| 25185 | |
| 25186 | |
| 25187 | TEST(StreamingUtf8ScriptWithMultipleMultibyteCharactersSomeSplit) { |
| 25188 | // Regression test: Stream data where there are several multi-byte UTF-8 |
| 25189 | // characters in a sequence and one of them is split between two data chunks. |
| 25190 | const char* reference = "\xec\x92\x81" ; |
| 25191 | char chunk1[] = |
| 25192 | "function foo() {\n" |
| 25193 | " // This function will contain an UTF-8 character which is not in\n" |
| 25194 | " // ASCII.\n" |
| 25195 | " var foob\xec\x92\x81X" ; |
| 25196 | char chunk2[] = |
| 25197 | "XXr = 13;\n" |
| 25198 | " return foob\xec\x92\x81\xec\x92\x81r;\n" |
| 25199 | "}\n" ; |
| 25200 | chunk1[strlen(chunk1) - 1] = reference[0]; |
| 25201 | chunk2[0] = reference[1]; |
| 25202 | chunk2[1] = reference[2]; |
| 25203 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 25204 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25205 | } |
| 25206 | |
| 25207 | |
| 25208 | TEST(StreamingUtf8ScriptWithMultipleMultibyteCharactersSomeSplit2) { |
| 25209 | // Another regression test, similar to the previous one. The difference is |
| 25210 | // that the split character is not the last one in the sequence. |
| 25211 | const char* reference = "\xec\x92\x81" ; |
| 25212 | char chunk1[] = |
| 25213 | "function foo() {\n" |
| 25214 | " // This function will contain an UTF-8 character which is not in\n" |
| 25215 | " // ASCII.\n" |
| 25216 | " var foobX" ; |
| 25217 | char chunk2[] = |
| 25218 | "XX\xec\x92\x81r = 13;\n" |
| 25219 | " return foob\xec\x92\x81\xec\x92\x81r;\n" |
| 25220 | "}\n" ; |
| 25221 | chunk1[strlen(chunk1) - 1] = reference[0]; |
| 25222 | chunk2[0] = reference[1]; |
| 25223 | chunk2[1] = reference[2]; |
| 25224 | const char* chunks[] = {chunk1, chunk2, "foo();" , nullptr}; |
| 25225 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 25226 | } |
| 25227 | |
| 25228 | |
| 25229 | TEST(StreamingWithHarmonyScopes) { |
| 25230 | // Don't use RunStreamingTest here so that both scripts get to use the same |
| 25231 | // LocalContext and HandleScope. |
| 25232 | LocalContext env; |
| 25233 | v8::Isolate* isolate = env->GetIsolate(); |
| 25234 | v8::HandleScope scope(isolate); |
| 25235 | |
| 25236 | // First, run a script with a let variable. |
| 25237 | CompileRun("\"use strict\"; let x = 1;" ); |
| 25238 | |
| 25239 | // Then stream a script which (erroneously) tries to introduce the same |
| 25240 | // variable again. |
| 25241 | const char* chunks[] = {"\"use strict\"; let x = 2;" , nullptr}; |
| 25242 | |
| 25243 | v8::TryCatch try_catch(isolate); |
| 25244 | v8::ScriptCompiler::StreamedSource source( |
| 25245 | v8::base::make_unique<TestSourceStream>(chunks), |
| 25246 | v8::ScriptCompiler::StreamedSource::ONE_BYTE); |
| 25247 | v8::ScriptCompiler::ScriptStreamingTask* task = |
| 25248 | v8::ScriptCompiler::StartStreamingScript(isolate, &source); |
| 25249 | task->Run(); |
| 25250 | delete task; |
| 25251 | |
| 25252 | // Parsing should succeed (the script will be parsed and compiled in a context |
| 25253 | // independent way, so the error is not detected). |
| 25254 | CHECK(!try_catch.HasCaught()); |
| 25255 | |
| 25256 | v8::ScriptOrigin origin(v8_str("http://foo.com" )); |
| 25257 | char* full_source = TestSourceStream::FullSourceString(chunks); |
| 25258 | v8::Local<Script> script = |
| 25259 | v8::ScriptCompiler::Compile(env.local(), &source, v8_str(full_source), |
| 25260 | origin) |
| 25261 | .ToLocalChecked(); |
| 25262 | CHECK(!script.IsEmpty()); |
| 25263 | CHECK(!try_catch.HasCaught()); |
| 25264 | |
| 25265 | // Running the script exposes the error. |
| 25266 | CHECK(script->Run(env.local()).IsEmpty()); |
| 25267 | CHECK(try_catch.HasCaught()); |
| 25268 | delete[] full_source; |
| 25269 | } |
| 25270 | |
| 25271 | |
| 25272 | TEST(CodeCache) { |
| 25273 | v8::Isolate::CreateParams create_params; |
| 25274 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 25275 | |
| 25276 | const char* source = "Math.sqrt(4)" ; |
| 25277 | const char* origin = "code cache test" ; |
| 25278 | v8::ScriptCompiler::CachedData* cache; |
| 25279 | |
| 25280 | v8::Isolate* isolate1 = v8::Isolate::New(create_params); |
| 25281 | { |
| 25282 | v8::Isolate::Scope iscope(isolate1); |
| 25283 | v8::HandleScope scope(isolate1); |
| 25284 | v8::Local<v8::Context> context = v8::Context::New(isolate1); |
| 25285 | v8::Context::Scope cscope(context); |
| 25286 | v8::Local<v8::String> source_string = v8_str(source); |
| 25287 | v8::ScriptOrigin script_origin(v8_str(origin)); |
| 25288 | v8::ScriptCompiler::Source source(source_string, script_origin); |
| 25289 | v8::ScriptCompiler::CompileOptions option = |
| 25290 | v8::ScriptCompiler::kNoCompileOptions; |
| 25291 | v8::Local<v8::Script> script = |
| 25292 | v8::ScriptCompiler::Compile(context, &source, option).ToLocalChecked(); |
| 25293 | cache = v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript()); |
| 25294 | } |
| 25295 | isolate1->Dispose(); |
| 25296 | |
| 25297 | v8::Isolate* isolate2 = v8::Isolate::New(create_params); |
| 25298 | { |
| 25299 | v8::Isolate::Scope iscope(isolate2); |
| 25300 | v8::HandleScope scope(isolate2); |
| 25301 | v8::Local<v8::Context> context = v8::Context::New(isolate2); |
| 25302 | v8::Context::Scope cscope(context); |
| 25303 | v8::Local<v8::String> source_string = v8_str(source); |
| 25304 | v8::ScriptOrigin script_origin(v8_str(origin)); |
| 25305 | v8::ScriptCompiler::Source source(source_string, script_origin, cache); |
| 25306 | v8::ScriptCompiler::CompileOptions option = |
| 25307 | v8::ScriptCompiler::kConsumeCodeCache; |
| 25308 | v8::Local<v8::Script> script; |
| 25309 | { |
| 25310 | i::DisallowCompilation no_compile( |
| 25311 | reinterpret_cast<i::Isolate*>(isolate2)); |
| 25312 | script = v8::ScriptCompiler::Compile(context, &source, option) |
| 25313 | .ToLocalChecked(); |
| 25314 | } |
| 25315 | CHECK_EQ(2, script->Run(context) |
| 25316 | .ToLocalChecked() |
| 25317 | ->ToInt32(context) |
| 25318 | .ToLocalChecked() |
| 25319 | ->Int32Value(context) |
| 25320 | .FromJust()); |
| 25321 | } |
| 25322 | isolate2->Dispose(); |
| 25323 | } |
| 25324 | |
| 25325 | v8::MaybeLocal<Module> UnexpectedModuleResolveCallback(Local<Context> context, |
| 25326 | Local<String> specifier, |
| 25327 | Local<Module> referrer) { |
| 25328 | CHECK_WITH_MSG(false, "Unexpected call to resolve callback" ); |
| 25329 | } |
| 25330 | |
| 25331 | namespace { |
| 25332 | |
| 25333 | Local<Module> CompileAndInstantiateModule(v8::Isolate* isolate, |
| 25334 | Local<Context> context, |
| 25335 | const char* resource_name, |
| 25336 | const char* source) { |
| 25337 | Local<String> source_string = v8_str(source); |
| 25338 | v8::ScriptOrigin script_origin( |
| 25339 | v8_str(resource_name), Local<v8::Integer>(), Local<v8::Integer>(), |
| 25340 | Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(), |
| 25341 | Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate)); |
| 25342 | v8::ScriptCompiler::Source script_compiler_source(source_string, |
| 25343 | script_origin); |
| 25344 | Local<Module> module = |
| 25345 | v8::ScriptCompiler::CompileModule(isolate, &script_compiler_source) |
| 25346 | .ToLocalChecked(); |
| 25347 | module->InstantiateModule(context, UnexpectedModuleResolveCallback) |
| 25348 | .ToChecked(); |
| 25349 | |
| 25350 | return module; |
| 25351 | } |
| 25352 | |
| 25353 | Local<Module> CompileAndInstantiateModuleFromCache( |
| 25354 | v8::Isolate* isolate, Local<Context> context, const char* resource_name, |
| 25355 | const char* source, v8::ScriptCompiler::CachedData* cache) { |
| 25356 | Local<String> source_string = v8_str(source); |
| 25357 | v8::ScriptOrigin script_origin( |
| 25358 | v8_str(resource_name), Local<v8::Integer>(), Local<v8::Integer>(), |
| 25359 | Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(), |
| 25360 | Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate)); |
| 25361 | v8::ScriptCompiler::Source script_compiler_source(source_string, |
| 25362 | script_origin, cache); |
| 25363 | |
| 25364 | Local<Module> module = |
| 25365 | v8::ScriptCompiler::CompileModule(isolate, &script_compiler_source, |
| 25366 | v8::ScriptCompiler::kConsumeCodeCache) |
| 25367 | .ToLocalChecked(); |
| 25368 | module->InstantiateModule(context, UnexpectedModuleResolveCallback) |
| 25369 | .ToChecked(); |
| 25370 | |
| 25371 | return module; |
| 25372 | } |
| 25373 | |
| 25374 | } // namespace |
| 25375 | |
| 25376 | TEST(ModuleCodeCache) { |
| 25377 | v8::Isolate::CreateParams create_params; |
| 25378 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 25379 | |
| 25380 | const char* origin = "code cache test" ; |
| 25381 | const char* source = |
| 25382 | "export default 5; export const a = 10; function f() { return 42; } " |
| 25383 | "(function() { return f(); })();" ; |
| 25384 | |
| 25385 | v8::ScriptCompiler::CachedData* cache; |
| 25386 | { |
| 25387 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 25388 | { |
| 25389 | v8::Isolate::Scope iscope(isolate); |
| 25390 | v8::HandleScope scope(isolate); |
| 25391 | v8::Local<v8::Context> context = v8::Context::New(isolate); |
| 25392 | v8::Context::Scope cscope(context); |
| 25393 | |
| 25394 | Local<Module> module = |
| 25395 | CompileAndInstantiateModule(isolate, context, origin, source); |
| 25396 | |
| 25397 | // Fetch the shared function info before evaluation. |
| 25398 | Local<v8::UnboundModuleScript> unbound_module_script = |
| 25399 | module->GetUnboundModuleScript(); |
| 25400 | |
| 25401 | // Evaluate for possible lazy compilation. |
| 25402 | Local<Value> completion_value = |
| 25403 | module->Evaluate(context).ToLocalChecked(); |
| 25404 | CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); |
| 25405 | |
| 25406 | // Now create the cache. Note that it is freed, obscurely, when |
| 25407 | // ScriptCompiler::Source goes out of scope below. |
| 25408 | cache = v8::ScriptCompiler::CreateCodeCache(unbound_module_script); |
| 25409 | } |
| 25410 | isolate->Dispose(); |
| 25411 | } |
| 25412 | |
| 25413 | // Test that the cache is consumed and execution still works. |
| 25414 | { |
| 25415 | // Disable --always_opt, otherwise we try to optimize during module |
| 25416 | // instantiation, violating the DisallowCompilation scope. |
| 25417 | i::FLAG_always_opt = false; |
| 25418 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 25419 | { |
| 25420 | v8::Isolate::Scope iscope(isolate); |
| 25421 | v8::HandleScope scope(isolate); |
| 25422 | v8::Local<v8::Context> context = v8::Context::New(isolate); |
| 25423 | v8::Context::Scope cscope(context); |
| 25424 | |
| 25425 | Local<Module> module; |
| 25426 | { |
| 25427 | i::DisallowCompilation no_compile( |
| 25428 | reinterpret_cast<i::Isolate*>(isolate)); |
| 25429 | module = CompileAndInstantiateModuleFromCache(isolate, context, origin, |
| 25430 | source, cache); |
| 25431 | } |
| 25432 | |
| 25433 | Local<Value> completion_value = |
| 25434 | module->Evaluate(context).ToLocalChecked(); |
| 25435 | CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); |
| 25436 | } |
| 25437 | isolate->Dispose(); |
| 25438 | } |
| 25439 | } |
| 25440 | |
| 25441 | // Tests that the code cache does not confuse the same source code compiled as a |
| 25442 | // script and as a module. |
| 25443 | TEST(CodeCacheModuleScriptMismatch) { |
| 25444 | v8::Isolate::CreateParams create_params; |
| 25445 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 25446 | |
| 25447 | const char* origin = "code cache test" ; |
| 25448 | const char* source = "42" ; |
| 25449 | |
| 25450 | v8::ScriptCompiler::CachedData* cache; |
| 25451 | { |
| 25452 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 25453 | { |
| 25454 | v8::Isolate::Scope iscope(isolate); |
| 25455 | v8::HandleScope scope(isolate); |
| 25456 | v8::Local<v8::Context> context = v8::Context::New(isolate); |
| 25457 | v8::Context::Scope cscope(context); |
| 25458 | |
| 25459 | Local<Module> module = |
| 25460 | CompileAndInstantiateModule(isolate, context, origin, source); |
| 25461 | |
| 25462 | // Fetch the shared function info before evaluation. |
| 25463 | Local<v8::UnboundModuleScript> unbound_module_script = |
| 25464 | module->GetUnboundModuleScript(); |
| 25465 | |
| 25466 | // Evaluate for possible lazy compilation. |
| 25467 | Local<Value> completion_value = |
| 25468 | module->Evaluate(context).ToLocalChecked(); |
| 25469 | CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); |
| 25470 | |
| 25471 | // Now create the cache. Note that it is freed, obscurely, when |
| 25472 | // ScriptCompiler::Source goes out of scope below. |
| 25473 | cache = v8::ScriptCompiler::CreateCodeCache(unbound_module_script); |
| 25474 | } |
| 25475 | isolate->Dispose(); |
| 25476 | } |
| 25477 | |
| 25478 | // Test that the cache is not consumed when source is compiled as a script. |
| 25479 | { |
| 25480 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 25481 | { |
| 25482 | v8::Isolate::Scope iscope(isolate); |
| 25483 | v8::HandleScope scope(isolate); |
| 25484 | v8::Local<v8::Context> context = v8::Context::New(isolate); |
| 25485 | v8::Context::Scope cscope(context); |
| 25486 | |
| 25487 | v8::ScriptOrigin script_origin(v8_str(origin)); |
| 25488 | v8::ScriptCompiler::Source script_compiler_source(v8_str(source), |
| 25489 | script_origin, cache); |
| 25490 | |
| 25491 | v8::Local<v8::Script> script = |
| 25492 | v8::ScriptCompiler::Compile(context, &script_compiler_source, |
| 25493 | v8::ScriptCompiler::kConsumeCodeCache) |
| 25494 | .ToLocalChecked(); |
| 25495 | |
| 25496 | CHECK(cache->rejected); |
| 25497 | |
| 25498 | CHECK_EQ(42, script->Run(context) |
| 25499 | .ToLocalChecked() |
| 25500 | ->ToInt32(context) |
| 25501 | .ToLocalChecked() |
| 25502 | ->Int32Value(context) |
| 25503 | .FromJust()); |
| 25504 | } |
| 25505 | isolate->Dispose(); |
| 25506 | } |
| 25507 | } |
| 25508 | |
| 25509 | // Same as above but other way around. |
| 25510 | TEST(CodeCacheScriptModuleMismatch) { |
| 25511 | v8::Isolate::CreateParams create_params; |
| 25512 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 25513 | |
| 25514 | const char* origin = "code cache test" ; |
| 25515 | const char* source = "42" ; |
| 25516 | |
| 25517 | v8::ScriptCompiler::CachedData* cache; |
| 25518 | { |
| 25519 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 25520 | { |
| 25521 | v8::Isolate::Scope iscope(isolate); |
| 25522 | v8::HandleScope scope(isolate); |
| 25523 | v8::Local<v8::Context> context = v8::Context::New(isolate); |
| 25524 | v8::Context::Scope cscope(context); |
| 25525 | v8::Local<v8::String> source_string = v8_str(source); |
| 25526 | v8::ScriptOrigin script_origin(v8_str(origin)); |
| 25527 | v8::ScriptCompiler::Source source(source_string, script_origin); |
| 25528 | v8::ScriptCompiler::CompileOptions option = |
| 25529 | v8::ScriptCompiler::kNoCompileOptions; |
| 25530 | v8::Local<v8::Script> script = |
| 25531 | v8::ScriptCompiler::Compile(context, &source, option) |
| 25532 | .ToLocalChecked(); |
| 25533 | cache = v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript()); |
| 25534 | } |
| 25535 | isolate->Dispose(); |
| 25536 | } |
| 25537 | |
| 25538 | // Test that the cache is not consumed when source is compiled as a module. |
| 25539 | { |
| 25540 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 25541 | { |
| 25542 | v8::Isolate::Scope iscope(isolate); |
| 25543 | v8::HandleScope scope(isolate); |
| 25544 | v8::Local<v8::Context> context = v8::Context::New(isolate); |
| 25545 | v8::Context::Scope cscope(context); |
| 25546 | |
| 25547 | v8::ScriptOrigin script_origin( |
| 25548 | v8_str(origin), Local<v8::Integer>(), Local<v8::Integer>(), |
| 25549 | Local<v8::Boolean>(), Local<v8::Integer>(), Local<v8::Value>(), |
| 25550 | Local<v8::Boolean>(), Local<v8::Boolean>(), True(isolate)); |
| 25551 | v8::ScriptCompiler::Source script_compiler_source(v8_str(source), |
| 25552 | script_origin, cache); |
| 25553 | |
| 25554 | Local<Module> module = v8::ScriptCompiler::CompileModule( |
| 25555 | isolate, &script_compiler_source, |
| 25556 | v8::ScriptCompiler::kConsumeCodeCache) |
| 25557 | .ToLocalChecked(); |
| 25558 | module->InstantiateModule(context, UnexpectedModuleResolveCallback) |
| 25559 | .ToChecked(); |
| 25560 | |
| 25561 | CHECK(cache->rejected); |
| 25562 | |
| 25563 | Local<Value> completion_value = |
| 25564 | module->Evaluate(context).ToLocalChecked(); |
| 25565 | CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); |
| 25566 | } |
| 25567 | isolate->Dispose(); |
| 25568 | } |
| 25569 | } |
| 25570 | |
| 25571 | // Tests that compilation can handle a garbled cache. |
| 25572 | TEST(InvalidCodeCacheDataInCompileModule) { |
| 25573 | v8::V8::Initialize(); |
| 25574 | v8::Isolate* isolate = CcTest::isolate(); |
| 25575 | v8::HandleScope scope(isolate); |
| 25576 | LocalContext local_context; |
| 25577 | |
| 25578 | const char* garbage = "garbage garbage garbage garbage garbage garbage" ; |
| 25579 | const uint8_t* data = reinterpret_cast<const uint8_t*>(garbage); |
| 25580 | Local<String> origin = v8_str("origin" ); |
| 25581 | int length = 16; |
| 25582 | v8::ScriptCompiler::CachedData* cached_data = |
| 25583 | new v8::ScriptCompiler::CachedData(data, length); |
| 25584 | CHECK(!cached_data->rejected); |
| 25585 | |
| 25586 | v8::ScriptOrigin script_origin( |
| 25587 | origin, Local<v8::Integer>(), Local<v8::Integer>(), Local<v8::Boolean>(), |
| 25588 | Local<v8::Integer>(), Local<v8::Value>(), Local<v8::Boolean>(), |
| 25589 | Local<v8::Boolean>(), True(isolate)); |
| 25590 | v8::ScriptCompiler::Source source(v8_str("42" ), script_origin, cached_data); |
| 25591 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 25592 | |
| 25593 | Local<Module> module = |
| 25594 | v8::ScriptCompiler::CompileModule(isolate, &source, |
| 25595 | v8::ScriptCompiler::kConsumeCodeCache) |
| 25596 | .ToLocalChecked(); |
| 25597 | module->InstantiateModule(context, UnexpectedModuleResolveCallback) |
| 25598 | .ToChecked(); |
| 25599 | |
| 25600 | CHECK(cached_data->rejected); |
| 25601 | CHECK_EQ(42, module->Evaluate(context) |
| 25602 | .ToLocalChecked() |
| 25603 | ->Int32Value(context) |
| 25604 | .FromJust()); |
| 25605 | } |
| 25606 | |
| 25607 | void TestInvalidCacheData(v8::ScriptCompiler::CompileOptions option) { |
| 25608 | const char* garbage = "garbage garbage garbage garbage garbage garbage" ; |
| 25609 | const uint8_t* data = reinterpret_cast<const uint8_t*>(garbage); |
| 25610 | int length = 16; |
| 25611 | v8::ScriptCompiler::CachedData* cached_data = |
| 25612 | new v8::ScriptCompiler::CachedData(data, length); |
| 25613 | CHECK(!cached_data->rejected); |
| 25614 | v8::ScriptOrigin origin(v8_str("origin" )); |
| 25615 | v8::ScriptCompiler::Source source(v8_str("42" ), origin, cached_data); |
| 25616 | v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
| 25617 | v8::Local<v8::Script> script = |
| 25618 | v8::ScriptCompiler::Compile(context, &source, option).ToLocalChecked(); |
| 25619 | CHECK(cached_data->rejected); |
| 25620 | CHECK_EQ( |
| 25621 | 42, |
| 25622 | script->Run(context).ToLocalChecked()->Int32Value(context).FromJust()); |
| 25623 | } |
| 25624 | |
| 25625 | |
| 25626 | TEST(InvalidCodeCacheData) { |
| 25627 | v8::V8::Initialize(); |
| 25628 | v8::HandleScope scope(CcTest::isolate()); |
| 25629 | LocalContext context; |
| 25630 | TestInvalidCacheData(v8::ScriptCompiler::kConsumeCodeCache); |
| 25631 | } |
| 25632 | |
| 25633 | |
| 25634 | TEST(StringConcatOverflow) { |
| 25635 | v8::V8::Initialize(); |
| 25636 | v8::Isolate* isolate = CcTest::isolate(); |
| 25637 | v8::HandleScope scope(isolate); |
| 25638 | RandomLengthOneByteResource* r = |
| 25639 | new RandomLengthOneByteResource(i::String::kMaxLength); |
| 25640 | v8::Local<v8::String> str = |
| 25641 | v8::String::NewExternalOneByte(isolate, r).ToLocalChecked(); |
| 25642 | CHECK(!str.IsEmpty()); |
| 25643 | v8::TryCatch try_catch(isolate); |
| 25644 | v8::Local<v8::String> result = v8::String::Concat(isolate, str, str); |
| 25645 | v8::String::Concat(CcTest::isolate(), str, str); |
| 25646 | CHECK(result.IsEmpty()); |
| 25647 | CHECK(!try_catch.HasCaught()); |
| 25648 | } |
| 25649 | |
| 25650 | TEST(TurboAsmDisablesDetach) { |
| 25651 | #ifndef V8_LITE_MODE |
| 25652 | i::FLAG_opt = true; |
| 25653 | i::FLAG_allow_natives_syntax = true; |
| 25654 | v8::V8::Initialize(); |
| 25655 | v8::HandleScope scope(CcTest::isolate()); |
| 25656 | LocalContext context; |
| 25657 | const char* load = |
| 25658 | "function Module(stdlib, foreign, heap) {" |
| 25659 | " 'use asm';" |
| 25660 | " var MEM32 = new stdlib.Int32Array(heap);" |
| 25661 | " function load() { return MEM32[0] | 0; }" |
| 25662 | " return { load: load };" |
| 25663 | "}" |
| 25664 | "var buffer = new ArrayBuffer(4096);" |
| 25665 | "var module = Module(this, {}, buffer);" |
| 25666 | "%OptimizeFunctionOnNextCall(module.load);" |
| 25667 | "module.load();" |
| 25668 | "buffer" ; |
| 25669 | |
| 25670 | v8::Local<v8::ArrayBuffer> result = CompileRun(load).As<v8::ArrayBuffer>(); |
| 25671 | CHECK(!result->IsDetachable()); |
| 25672 | |
| 25673 | const char* store = |
| 25674 | "function Module(stdlib, foreign, heap) {" |
| 25675 | " 'use asm';" |
| 25676 | " var MEM32 = new stdlib.Int32Array(heap);" |
| 25677 | " function store() { MEM32[0] = 0; }" |
| 25678 | " return { store: store };" |
| 25679 | "}" |
| 25680 | "var buffer = new ArrayBuffer(4096);" |
| 25681 | "var module = Module(this, {}, buffer);" |
| 25682 | "%OptimizeFunctionOnNextCall(module.store);" |
| 25683 | "module.store();" |
| 25684 | "buffer" ; |
| 25685 | |
| 25686 | result = CompileRun(store).As<v8::ArrayBuffer>(); |
| 25687 | CHECK(!result->IsDetachable()); |
| 25688 | #endif // V8_LITE_MODE |
| 25689 | } |
| 25690 | |
| 25691 | TEST(ClassPrototypeCreationContext) { |
| 25692 | v8::Isolate* isolate = CcTest::isolate(); |
| 25693 | v8::HandleScope handle_scope(isolate); |
| 25694 | LocalContext env; |
| 25695 | |
| 25696 | Local<Object> result = Local<Object>::Cast( |
| 25697 | CompileRun("'use strict'; class Example { }; Example.prototype" )); |
| 25698 | CHECK(env.local() == result->CreationContext()); |
| 25699 | } |
| 25700 | |
| 25701 | |
| 25702 | TEST(SimpleStreamingScriptWithSourceURL) { |
| 25703 | const char* chunks[] = {"function foo() { ret" , "urn 13; } f" , "oo();\n" , |
| 25704 | "//# sourceURL=bar2.js\n" , nullptr}; |
| 25705 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, true, |
| 25706 | "bar2.js" ); |
| 25707 | } |
| 25708 | |
| 25709 | |
| 25710 | TEST(StreamingScriptWithSplitSourceURL) { |
| 25711 | const char* chunks[] = {"function foo() { ret" , "urn 13; } f" , |
| 25712 | "oo();\n//# sourceURL=b" , "ar2.js\n" , nullptr}; |
| 25713 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, true, |
| 25714 | "bar2.js" ); |
| 25715 | } |
| 25716 | |
| 25717 | |
| 25718 | TEST(StreamingScriptWithSourceMappingURLInTheMiddle) { |
| 25719 | const char* chunks[] = {"function foo() { ret" , "urn 13; }\n//#" , |
| 25720 | " sourceMappingURL=bar2.js\n" , "foo();" , nullptr}; |
| 25721 | RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, true, |
| 25722 | nullptr, "bar2.js" ); |
| 25723 | } |
| 25724 | |
| 25725 | |
| 25726 | TEST(NewStringRangeError) { |
| 25727 | // This test uses a lot of memory and fails with flaky OOM when run |
| 25728 | // with --stress-incremental-marking on TSAN. |
| 25729 | i::FLAG_stress_incremental_marking = false; |
| 25730 | v8::Isolate* isolate = CcTest::isolate(); |
| 25731 | v8::HandleScope handle_scope(isolate); |
| 25732 | const int length = i::String::kMaxLength + 1; |
| 25733 | const int buffer_size = length * sizeof(uint16_t); |
| 25734 | void* buffer = malloc(buffer_size); |
| 25735 | if (buffer == nullptr) return; |
| 25736 | memset(buffer, 'A', buffer_size); |
| 25737 | { |
| 25738 | v8::TryCatch try_catch(isolate); |
| 25739 | char* data = reinterpret_cast<char*>(buffer); |
| 25740 | CHECK(v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kNormal, |
| 25741 | length) |
| 25742 | .IsEmpty()); |
| 25743 | CHECK(!try_catch.HasCaught()); |
| 25744 | } |
| 25745 | { |
| 25746 | v8::TryCatch try_catch(isolate); |
| 25747 | uint8_t* data = reinterpret_cast<uint8_t*>(buffer); |
| 25748 | CHECK(v8::String::NewFromOneByte(isolate, data, v8::NewStringType::kNormal, |
| 25749 | length) |
| 25750 | .IsEmpty()); |
| 25751 | CHECK(!try_catch.HasCaught()); |
| 25752 | } |
| 25753 | { |
| 25754 | v8::TryCatch try_catch(isolate); |
| 25755 | uint16_t* data = reinterpret_cast<uint16_t*>(buffer); |
| 25756 | CHECK(v8::String::NewFromTwoByte(isolate, data, v8::NewStringType::kNormal, |
| 25757 | length) |
| 25758 | .IsEmpty()); |
| 25759 | CHECK(!try_catch.HasCaught()); |
| 25760 | } |
| 25761 | free(buffer); |
| 25762 | } |
| 25763 | |
| 25764 | |
| 25765 | TEST(SealHandleScope) { |
| 25766 | v8::Isolate* isolate = CcTest::isolate(); |
| 25767 | v8::HandleScope handle_scope(isolate); |
| 25768 | LocalContext env; |
| 25769 | |
| 25770 | v8::SealHandleScope seal(isolate); |
| 25771 | |
| 25772 | // Should fail |
| 25773 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 25774 | |
| 25775 | USE(obj); |
| 25776 | } |
| 25777 | |
| 25778 | |
| 25779 | TEST(SealHandleScopeNested) { |
| 25780 | v8::Isolate* isolate = CcTest::isolate(); |
| 25781 | v8::HandleScope handle_scope(isolate); |
| 25782 | LocalContext env; |
| 25783 | |
| 25784 | v8::SealHandleScope seal(isolate); |
| 25785 | |
| 25786 | { |
| 25787 | v8::HandleScope handle_scope(isolate); |
| 25788 | |
| 25789 | // Should work |
| 25790 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 25791 | |
| 25792 | USE(obj); |
| 25793 | } |
| 25794 | } |
| 25795 | |
| 25796 | |
| 25797 | static void ( |
| 25798 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 25799 | CHECK_EQ( |
| 25800 | 3, |
| 25801 | args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust()); |
| 25802 | args.GetReturnValue().Set(v8_num(7)); |
| 25803 | } |
| 25804 | |
| 25805 | TEST(ExtrasFunctionSource) { |
| 25806 | v8::Isolate* isolate = CcTest::isolate(); |
| 25807 | v8::HandleScope handle_scope(isolate); |
| 25808 | LocalContext env; |
| 25809 | |
| 25810 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25811 | |
| 25812 | // Functions defined in extras do not expose source code. |
| 25813 | auto func = binding->Get(env.local(), v8_str("testFunctionToString" )) |
| 25814 | .ToLocalChecked() |
| 25815 | .As<v8::Function>(); |
| 25816 | auto undefined = v8::Undefined(isolate); |
| 25817 | auto result = func->Call(env.local(), undefined, 0, {}) |
| 25818 | .ToLocalChecked() |
| 25819 | .As<v8::String>(); |
| 25820 | CHECK(result->StrictEquals(v8_str("function foo() { [native code] }" ))); |
| 25821 | |
| 25822 | // Functions defined in extras do not show up in the stack trace. |
| 25823 | auto wrapper = binding->Get(env.local(), v8_str("testStackTrace" )) |
| 25824 | .ToLocalChecked() |
| 25825 | .As<v8::Function>(); |
| 25826 | CHECK(env->Global()->Set(env.local(), v8_str("wrapper" ), wrapper).FromJust()); |
| 25827 | ExpectString( |
| 25828 | "function f(x) { return wrapper(x) }" |
| 25829 | "function g() { return new Error().stack; }" |
| 25830 | "f(g)" , |
| 25831 | "Error\n" |
| 25832 | " at g (<anonymous>:1:58)\n" |
| 25833 | " at f (<anonymous>:1:24)\n" |
| 25834 | " at <anonymous>:1:78" ); |
| 25835 | } |
| 25836 | |
| 25837 | TEST(ExtrasBindingObject) { |
| 25838 | v8::Isolate* isolate = CcTest::isolate(); |
| 25839 | v8::HandleScope handle_scope(isolate); |
| 25840 | LocalContext env; |
| 25841 | |
| 25842 | // standalone.gypi ensures we include the test-extra.js file, which should |
| 25843 | // export the tested functions. |
| 25844 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25845 | |
| 25846 | auto func = binding->Get(env.local(), v8_str("testExtraShouldReturnFive" )) |
| 25847 | .ToLocalChecked() |
| 25848 | .As<v8::Function>(); |
| 25849 | auto undefined = v8::Undefined(isolate); |
| 25850 | auto result = func->Call(env.local(), undefined, 0, {}) |
| 25851 | .ToLocalChecked() |
| 25852 | .As<v8::Number>(); |
| 25853 | CHECK_EQ(5, result->Int32Value(env.local()).FromJust()); |
| 25854 | |
| 25855 | v8::Local<v8::FunctionTemplate> runtimeFunction = |
| 25856 | v8::FunctionTemplate::New(isolate, ExtrasBindingTestRuntimeFunction); |
| 25857 | binding->Set(env.local(), v8_str("runtime" ), |
| 25858 | runtimeFunction->GetFunction(env.local()).ToLocalChecked()) |
| 25859 | .FromJust(); |
| 25860 | func = binding->Get(env.local(), v8_str("testExtraShouldCallToRuntime" )) |
| 25861 | .ToLocalChecked() |
| 25862 | .As<v8::Function>(); |
| 25863 | result = func->Call(env.local(), undefined, 0, {}) |
| 25864 | .ToLocalChecked() |
| 25865 | .As<v8::Number>(); |
| 25866 | CHECK_EQ(7, result->Int32Value(env.local()).FromJust()); |
| 25867 | } |
| 25868 | |
| 25869 | |
| 25870 | TEST(ExtrasCreatePromise) { |
| 25871 | i::FLAG_allow_natives_syntax = true; |
| 25872 | LocalContext context; |
| 25873 | v8::Isolate* isolate = context->GetIsolate(); |
| 25874 | v8::HandleScope handle_scope(isolate); |
| 25875 | |
| 25876 | LocalContext env; |
| 25877 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25878 | |
| 25879 | auto func = binding->Get(env.local(), v8_str("testCreatePromise" )) |
| 25880 | .ToLocalChecked() |
| 25881 | .As<v8::Function>(); |
| 25882 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 25883 | |
| 25884 | auto promise = CompileRun( |
| 25885 | "func();\n" |
| 25886 | "func();\n" |
| 25887 | "%OptimizeFunctionOnNextCall(func);\n" |
| 25888 | "func()\n" ) |
| 25889 | .As<v8::Promise>(); |
| 25890 | CHECK_EQ(v8::Promise::kPending, promise->State()); |
| 25891 | } |
| 25892 | |
| 25893 | TEST(ExtrasCreatePromiseWithParent) { |
| 25894 | i::FLAG_allow_natives_syntax = true; |
| 25895 | LocalContext context; |
| 25896 | v8::Isolate* isolate = context->GetIsolate(); |
| 25897 | v8::HandleScope handle_scope(isolate); |
| 25898 | |
| 25899 | LocalContext env; |
| 25900 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25901 | |
| 25902 | auto func = binding->Get(env.local(), v8_str("testCreatePromiseWithParent" )) |
| 25903 | .ToLocalChecked() |
| 25904 | .As<v8::Function>(); |
| 25905 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 25906 | |
| 25907 | auto promise = CompileRun( |
| 25908 | "var parent = new Promise((a, b) => {});\n" |
| 25909 | "func(parent);\n" |
| 25910 | "func(parent);\n" |
| 25911 | "%OptimizeFunctionOnNextCall(func);\n" |
| 25912 | "func(parent)\n" ) |
| 25913 | .As<v8::Promise>(); |
| 25914 | CHECK_EQ(v8::Promise::kPending, promise->State()); |
| 25915 | } |
| 25916 | |
| 25917 | TEST(ExtrasRejectPromise) { |
| 25918 | i::FLAG_allow_natives_syntax = true; |
| 25919 | LocalContext context; |
| 25920 | v8::Isolate* isolate = context->GetIsolate(); |
| 25921 | v8::HandleScope handle_scope(isolate); |
| 25922 | |
| 25923 | LocalContext env; |
| 25924 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25925 | |
| 25926 | auto func = binding->Get(env.local(), v8_str("testRejectPromise" )) |
| 25927 | .ToLocalChecked() |
| 25928 | .As<v8::Function>(); |
| 25929 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 25930 | |
| 25931 | auto rejected_promise = CompileRun( |
| 25932 | "function newPromise() {\n" |
| 25933 | " return new Promise((a, b) => {});\n" |
| 25934 | "}\n" |
| 25935 | "func(newPromise(), 1);\n" |
| 25936 | "func(newPromise(), 1);\n" |
| 25937 | "%OptimizeFunctionOnNextCall(func);\n" |
| 25938 | "var promise = newPromise();\n" |
| 25939 | "func(promise, 1);\n" |
| 25940 | "promise;\n" ) |
| 25941 | .As<v8::Promise>(); |
| 25942 | CHECK_EQ(v8::Promise::kRejected, rejected_promise->State()); |
| 25943 | CHECK_EQ(1, rejected_promise->Result()->Int32Value(env.local()).FromJust()); |
| 25944 | } |
| 25945 | |
| 25946 | TEST(ExtrasResolvePromise) { |
| 25947 | i::FLAG_allow_natives_syntax = true; |
| 25948 | LocalContext context; |
| 25949 | v8::Isolate* isolate = context->GetIsolate(); |
| 25950 | v8::HandleScope handle_scope(isolate); |
| 25951 | |
| 25952 | LocalContext env; |
| 25953 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25954 | |
| 25955 | auto func = binding->Get(env.local(), v8_str("testResolvePromise" )) |
| 25956 | .ToLocalChecked() |
| 25957 | .As<v8::Function>(); |
| 25958 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 25959 | |
| 25960 | auto pending_promise = CompileRun( |
| 25961 | "function newPromise() {\n" |
| 25962 | " return new Promise((a, b) => {});\n" |
| 25963 | "}\n" |
| 25964 | "func(newPromise(), newPromise());\n" |
| 25965 | "func(newPromise(), newPromise());\n" |
| 25966 | "%OptimizeFunctionOnNextCall(func);\n" |
| 25967 | "var promise = newPromise();\n" |
| 25968 | "func(promise, newPromise());\n" |
| 25969 | "promise;\n" ) |
| 25970 | .As<v8::Promise>(); |
| 25971 | CHECK_EQ(v8::Promise::kPending, pending_promise->State()); |
| 25972 | |
| 25973 | auto fulfilled_promise = CompileRun( |
| 25974 | "function newPromise() {\n" |
| 25975 | " return new Promise((a, b) => {});\n" |
| 25976 | "}\n" |
| 25977 | "func(newPromise(), 1);\n" |
| 25978 | "func(newPromise(), 1);\n" |
| 25979 | "%OptimizeFunctionOnNextCall(func);\n" |
| 25980 | "var promise = newPromise();\n" |
| 25981 | "func(promise, 1);\n" |
| 25982 | "promise;\n" ) |
| 25983 | .As<v8::Promise>(); |
| 25984 | CHECK_EQ(v8::Promise::kFulfilled, fulfilled_promise->State()); |
| 25985 | CHECK_EQ(1, fulfilled_promise->Result()->Int32Value(env.local()).FromJust()); |
| 25986 | } |
| 25987 | |
| 25988 | TEST(ExtrasUtilsObject) { |
| 25989 | LocalContext context; |
| 25990 | v8::Isolate* isolate = context->GetIsolate(); |
| 25991 | v8::HandleScope handle_scope(isolate); |
| 25992 | |
| 25993 | LocalContext env; |
| 25994 | v8::Local<v8::Object> binding = env->GetExtrasBindingObject(); |
| 25995 | |
| 25996 | auto func = binding->Get(env.local(), v8_str("testExtraCanUseUtils" )) |
| 25997 | .ToLocalChecked() |
| 25998 | .As<v8::Function>(); |
| 25999 | auto undefined = v8::Undefined(isolate); |
| 26000 | auto result = func->Call(env.local(), undefined, 0, {}) |
| 26001 | .ToLocalChecked() |
| 26002 | .As<v8::Object>(); |
| 26003 | |
| 26004 | auto private_symbol = result->Get(env.local(), v8_str("privateSymbol" )) |
| 26005 | .ToLocalChecked() |
| 26006 | .As<v8::Symbol>(); |
| 26007 | i::Handle<i::Symbol> ips = v8::Utils::OpenHandle(*private_symbol); |
| 26008 | CHECK(ips->IsPrivate()); |
| 26009 | |
| 26010 | CompileRun("var result = 0; function store(x) { result = x; }" ); |
| 26011 | auto store = CompileRun("store" ).As<v8::Function>(); |
| 26012 | |
| 26013 | auto fulfilled_promise = result->Get(env.local(), v8_str("fulfilledPromise" )) |
| 26014 | .ToLocalChecked() |
| 26015 | .As<v8::Promise>(); |
| 26016 | fulfilled_promise->Then(env.local(), store).ToLocalChecked(); |
| 26017 | isolate->RunMicrotasks(); |
| 26018 | CHECK_EQ(1, CompileRun("result" )->Int32Value(env.local()).FromJust()); |
| 26019 | |
| 26020 | auto fulfilled_promise_2 = |
| 26021 | result->Get(env.local(), v8_str("fulfilledPromise2" )) |
| 26022 | .ToLocalChecked() |
| 26023 | .As<v8::Promise>(); |
| 26024 | fulfilled_promise_2->Then(env.local(), store).ToLocalChecked(); |
| 26025 | isolate->RunMicrotasks(); |
| 26026 | CHECK_EQ(2, CompileRun("result" )->Int32Value(env.local()).FromJust()); |
| 26027 | |
| 26028 | auto rejected_promise = result->Get(env.local(), v8_str("rejectedPromise" )) |
| 26029 | .ToLocalChecked() |
| 26030 | .As<v8::Promise>(); |
| 26031 | rejected_promise->Catch(env.local(), store).ToLocalChecked(); |
| 26032 | isolate->RunMicrotasks(); |
| 26033 | CHECK_EQ(3, CompileRun("result" )->Int32Value(env.local()).FromJust()); |
| 26034 | |
| 26035 | auto rejected_but_handled_promise = |
| 26036 | result->Get(env.local(), v8_str("rejectedButHandledPromise" )) |
| 26037 | .ToLocalChecked() |
| 26038 | .As<v8::Promise>(); |
| 26039 | CHECK(rejected_but_handled_promise->HasHandler()); |
| 26040 | |
| 26041 | auto promise_states = result->Get(env.local(), v8_str("promiseStates" )) |
| 26042 | .ToLocalChecked() |
| 26043 | .As<v8::String>(); |
| 26044 | String::Utf8Value promise_states_string(isolate, promise_states); |
| 26045 | CHECK_EQ(0, strcmp(*promise_states_string, "pending fulfilled rejected" )); |
| 26046 | |
| 26047 | auto promise_is_promise = result->Get(env.local(), v8_str("promiseIsPromise" )) |
| 26048 | .ToLocalChecked() |
| 26049 | .As<v8::Boolean>(); |
| 26050 | CHECK_EQ(true, promise_is_promise->Value()); |
| 26051 | |
| 26052 | auto thenable_is_promise = |
| 26053 | result->Get(env.local(), v8_str("thenableIsPromise" )) |
| 26054 | .ToLocalChecked() |
| 26055 | .As<v8::Boolean>(); |
| 26056 | CHECK_EQ(false, thenable_is_promise->Value()); |
| 26057 | |
| 26058 | auto uncurry_this = result->Get(env.local(), v8_str("uncurryThis" )) |
| 26059 | .ToLocalChecked() |
| 26060 | .As<v8::Boolean>(); |
| 26061 | CHECK_EQ(true, uncurry_this->Value()); |
| 26062 | } |
| 26063 | |
| 26064 | |
| 26065 | TEST(Map) { |
| 26066 | v8::Isolate* isolate = CcTest::isolate(); |
| 26067 | v8::HandleScope handle_scope(isolate); |
| 26068 | LocalContext env; |
| 26069 | |
| 26070 | v8::Local<v8::Map> map = v8::Map::New(isolate); |
| 26071 | CHECK(map->IsObject()); |
| 26072 | CHECK(map->IsMap()); |
| 26073 | CHECK(map->GetPrototype()->StrictEquals(CompileRun("Map.prototype" ))); |
| 26074 | CHECK_EQ(0U, map->Size()); |
| 26075 | |
| 26076 | v8::Local<v8::Value> val = CompileRun("new Map([[1, 2], [3, 4]])" ); |
| 26077 | CHECK(val->IsMap()); |
| 26078 | map = v8::Local<v8::Map>::Cast(val); |
| 26079 | CHECK_EQ(2U, map->Size()); |
| 26080 | |
| 26081 | v8::Local<v8::Array> contents = map->AsArray(); |
| 26082 | CHECK_EQ(4U, contents->Length()); |
| 26083 | CHECK_EQ( |
| 26084 | 1, |
| 26085 | contents->Get(env.local(), 0).ToLocalChecked().As<v8::Int32>()->Value()); |
| 26086 | CHECK_EQ( |
| 26087 | 2, |
| 26088 | contents->Get(env.local(), 1).ToLocalChecked().As<v8::Int32>()->Value()); |
| 26089 | CHECK_EQ( |
| 26090 | 3, |
| 26091 | contents->Get(env.local(), 2).ToLocalChecked().As<v8::Int32>()->Value()); |
| 26092 | CHECK_EQ( |
| 26093 | 4, |
| 26094 | contents->Get(env.local(), 3).ToLocalChecked().As<v8::Int32>()->Value()); |
| 26095 | |
| 26096 | CHECK_EQ(2U, map->Size()); |
| 26097 | |
| 26098 | CHECK(map->Has(env.local(), v8::Integer::New(isolate, 1)).FromJust()); |
| 26099 | CHECK(map->Has(env.local(), v8::Integer::New(isolate, 3)).FromJust()); |
| 26100 | |
| 26101 | CHECK(!map->Has(env.local(), v8::Integer::New(isolate, 2)).FromJust()); |
| 26102 | CHECK(!map->Has(env.local(), map).FromJust()); |
| 26103 | |
| 26104 | CHECK_EQ(2, map->Get(env.local(), v8::Integer::New(isolate, 1)) |
| 26105 | .ToLocalChecked() |
| 26106 | ->Int32Value(env.local()) |
| 26107 | .FromJust()); |
| 26108 | CHECK_EQ(4, map->Get(env.local(), v8::Integer::New(isolate, 3)) |
| 26109 | .ToLocalChecked() |
| 26110 | ->Int32Value(env.local()) |
| 26111 | .FromJust()); |
| 26112 | |
| 26113 | CHECK(map->Get(env.local(), v8::Integer::New(isolate, 42)) |
| 26114 | .ToLocalChecked() |
| 26115 | ->IsUndefined()); |
| 26116 | |
| 26117 | CHECK(!map->Set(env.local(), map, map).IsEmpty()); |
| 26118 | CHECK_EQ(3U, map->Size()); |
| 26119 | CHECK(map->Has(env.local(), map).FromJust()); |
| 26120 | |
| 26121 | CHECK(map->Delete(env.local(), map).FromJust()); |
| 26122 | CHECK_EQ(2U, map->Size()); |
| 26123 | CHECK(!map->Has(env.local(), map).FromJust()); |
| 26124 | CHECK(!map->Delete(env.local(), map).FromJust()); |
| 26125 | |
| 26126 | map->Clear(); |
| 26127 | CHECK_EQ(0U, map->Size()); |
| 26128 | } |
| 26129 | |
| 26130 | |
| 26131 | TEST(Set) { |
| 26132 | v8::Isolate* isolate = CcTest::isolate(); |
| 26133 | v8::HandleScope handle_scope(isolate); |
| 26134 | LocalContext env; |
| 26135 | |
| 26136 | v8::Local<v8::Set> set = v8::Set::New(isolate); |
| 26137 | CHECK(set->IsObject()); |
| 26138 | CHECK(set->IsSet()); |
| 26139 | CHECK(set->GetPrototype()->StrictEquals(CompileRun("Set.prototype" ))); |
| 26140 | CHECK_EQ(0U, set->Size()); |
| 26141 | |
| 26142 | v8::Local<v8::Value> val = CompileRun("new Set([1, 2])" ); |
| 26143 | CHECK(val->IsSet()); |
| 26144 | set = v8::Local<v8::Set>::Cast(val); |
| 26145 | CHECK_EQ(2U, set->Size()); |
| 26146 | |
| 26147 | v8::Local<v8::Array> keys = set->AsArray(); |
| 26148 | CHECK_EQ(2U, keys->Length()); |
| 26149 | CHECK_EQ(1, |
| 26150 | keys->Get(env.local(), 0).ToLocalChecked().As<v8::Int32>()->Value()); |
| 26151 | CHECK_EQ(2, |
| 26152 | keys->Get(env.local(), 1).ToLocalChecked().As<v8::Int32>()->Value()); |
| 26153 | |
| 26154 | CHECK_EQ(2U, set->Size()); |
| 26155 | |
| 26156 | CHECK(set->Has(env.local(), v8::Integer::New(isolate, 1)).FromJust()); |
| 26157 | CHECK(set->Has(env.local(), v8::Integer::New(isolate, 2)).FromJust()); |
| 26158 | |
| 26159 | CHECK(!set->Has(env.local(), v8::Integer::New(isolate, 3)).FromJust()); |
| 26160 | CHECK(!set->Has(env.local(), set).FromJust()); |
| 26161 | |
| 26162 | CHECK(!set->Add(env.local(), set).IsEmpty()); |
| 26163 | CHECK_EQ(3U, set->Size()); |
| 26164 | CHECK(set->Has(env.local(), set).FromJust()); |
| 26165 | |
| 26166 | CHECK(set->Delete(env.local(), set).FromJust()); |
| 26167 | CHECK_EQ(2U, set->Size()); |
| 26168 | CHECK(!set->Has(env.local(), set).FromJust()); |
| 26169 | CHECK(!set->Delete(env.local(), set).FromJust()); |
| 26170 | |
| 26171 | set->Clear(); |
| 26172 | CHECK_EQ(0U, set->Size()); |
| 26173 | } |
| 26174 | |
| 26175 | TEST(SetDeleteThenAsArray) { |
| 26176 | // https://bugs.chromium.org/p/v8/issues/detail?id=4946 |
| 26177 | v8::Isolate* isolate = CcTest::isolate(); |
| 26178 | v8::HandleScope handle_scope(isolate); |
| 26179 | LocalContext env; |
| 26180 | |
| 26181 | // make a Set |
| 26182 | v8::Local<v8::Value> val = CompileRun("new Set([1, 2, 3])" ); |
| 26183 | v8::Local<v8::Set> set = v8::Local<v8::Set>::Cast(val); |
| 26184 | CHECK_EQ(3U, set->Size()); |
| 26185 | |
| 26186 | // delete the "middle" element (using AsArray to |
| 26187 | // determine which element is the "middle" element) |
| 26188 | v8::Local<v8::Array> array1 = set->AsArray(); |
| 26189 | CHECK_EQ(3U, array1->Length()); |
| 26190 | CHECK(set->Delete(env.local(), array1->Get(env.local(), 1).ToLocalChecked()) |
| 26191 | .FromJust()); |
| 26192 | |
| 26193 | // make sure there are no undefined values when we convert to an array again. |
| 26194 | v8::Local<v8::Array> array2 = set->AsArray(); |
| 26195 | uint32_t length = array2->Length(); |
| 26196 | CHECK_EQ(2U, length); |
| 26197 | for (uint32_t i = 0; i < length; i++) { |
| 26198 | CHECK(!array2->Get(env.local(), i).ToLocalChecked()->IsUndefined()); |
| 26199 | } |
| 26200 | } |
| 26201 | |
| 26202 | TEST(MapDeleteThenAsArray) { |
| 26203 | // https://bugs.chromium.org/p/v8/issues/detail?id=4946 |
| 26204 | v8::Isolate* isolate = CcTest::isolate(); |
| 26205 | v8::HandleScope handle_scope(isolate); |
| 26206 | LocalContext env; |
| 26207 | |
| 26208 | // make a Map |
| 26209 | v8::Local<v8::Value> val = CompileRun("new Map([[1, 2], [3, 4], [5, 6]])" ); |
| 26210 | v8::Local<v8::Map> map = v8::Local<v8::Map>::Cast(val); |
| 26211 | CHECK_EQ(3U, map->Size()); |
| 26212 | |
| 26213 | // delete the "middle" element (using AsArray to |
| 26214 | // determine which element is the "middle" element) |
| 26215 | v8::Local<v8::Array> array1 = map->AsArray(); |
| 26216 | CHECK_EQ(6U, array1->Length()); |
| 26217 | // Map::AsArray returns a flat array, so the second key is at index 2. |
| 26218 | v8::Local<v8::Value> key = array1->Get(env.local(), 2).ToLocalChecked(); |
| 26219 | CHECK(map->Delete(env.local(), key).FromJust()); |
| 26220 | |
| 26221 | // make sure there are no undefined values when we convert to an array again. |
| 26222 | v8::Local<v8::Array> array2 = map->AsArray(); |
| 26223 | uint32_t length = array2->Length(); |
| 26224 | CHECK_EQ(4U, length); |
| 26225 | for (uint32_t i = 0; i < length; i++) { |
| 26226 | CHECK(!array2->Get(env.local(), i).ToLocalChecked()->IsUndefined()); |
| 26227 | } |
| 26228 | } |
| 26229 | |
| 26230 | TEST(CompatibleReceiverCheckOnCachedICHandler) { |
| 26231 | v8::Isolate* isolate = CcTest::isolate(); |
| 26232 | v8::HandleScope scope(isolate); |
| 26233 | v8::Local<v8::FunctionTemplate> parent = FunctionTemplate::New(isolate); |
| 26234 | v8::Local<v8::Signature> signature = v8::Signature::New(isolate, parent); |
| 26235 | auto returns_42 = |
| 26236 | v8::FunctionTemplate::New(isolate, Returns42, Local<Value>(), signature); |
| 26237 | parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age" ), returns_42); |
| 26238 | v8::Local<v8::FunctionTemplate> child = v8::FunctionTemplate::New(isolate); |
| 26239 | child->Inherit(parent); |
| 26240 | LocalContext env; |
| 26241 | CHECK(env->Global() |
| 26242 | ->Set(env.local(), v8_str("Child" ), |
| 26243 | child->GetFunction(env.local()).ToLocalChecked()) |
| 26244 | .FromJust()); |
| 26245 | |
| 26246 | // Make sure there's a compiled stub for "Child.prototype.age" in the cache. |
| 26247 | CompileRun( |
| 26248 | "var real = new Child();\n" |
| 26249 | "for (var i = 0; i < 3; ++i) {\n" |
| 26250 | " real.age;\n" |
| 26251 | "}\n" ); |
| 26252 | |
| 26253 | // Check that the cached stub is never used. |
| 26254 | ExpectInt32( |
| 26255 | "var fake = Object.create(Child.prototype);\n" |
| 26256 | "var result = 0;\n" |
| 26257 | "function test(d) {\n" |
| 26258 | " if (d == 3) return;\n" |
| 26259 | " try {\n" |
| 26260 | " fake.age;\n" |
| 26261 | " result = 1;\n" |
| 26262 | " } catch (e) {\n" |
| 26263 | " }\n" |
| 26264 | " test(d+1);\n" |
| 26265 | "}\n" |
| 26266 | "test(0);\n" |
| 26267 | "result;\n" , |
| 26268 | 0); |
| 26269 | } |
| 26270 | |
| 26271 | THREADED_TEST(ReceiverConversionForAccessors) { |
| 26272 | LocalContext env; |
| 26273 | v8::Isolate* isolate = CcTest::isolate(); |
| 26274 | v8::HandleScope scope(isolate); |
| 26275 | Local<v8::FunctionTemplate> acc = |
| 26276 | v8::FunctionTemplate::New(isolate, Returns42); |
| 26277 | CHECK(env->Global() |
| 26278 | ->Set(env.local(), v8_str("acc" ), |
| 26279 | acc->GetFunction(env.local()).ToLocalChecked()) |
| 26280 | .FromJust()); |
| 26281 | |
| 26282 | Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); |
| 26283 | templ->SetAccessorProperty(v8_str("acc" ), acc, acc); |
| 26284 | Local<v8::Object> instance = templ->NewInstance(env.local()).ToLocalChecked(); |
| 26285 | |
| 26286 | CHECK(env->Global()->Set(env.local(), v8_str("p" ), instance).FromJust()); |
| 26287 | CHECK(CompileRun("(p.acc == 42)" )->BooleanValue(isolate)); |
| 26288 | CHECK(CompileRun("(p.acc = 7) == 7" )->BooleanValue(isolate)); |
| 26289 | |
| 26290 | CHECK(!CompileRun("Number.prototype.__proto__ = p;" |
| 26291 | "var a = 1;" ) |
| 26292 | .IsEmpty()); |
| 26293 | CHECK(CompileRun("(a.acc == 42)" )->BooleanValue(isolate)); |
| 26294 | CHECK(CompileRun("(a.acc = 7) == 7" )->BooleanValue(isolate)); |
| 26295 | |
| 26296 | CHECK(!CompileRun("Boolean.prototype.__proto__ = p;" |
| 26297 | "var a = true;" ) |
| 26298 | .IsEmpty()); |
| 26299 | CHECK(CompileRun("(a.acc == 42)" )->BooleanValue(isolate)); |
| 26300 | CHECK(CompileRun("(a.acc = 7) == 7" )->BooleanValue(isolate)); |
| 26301 | |
| 26302 | CHECK(!CompileRun("String.prototype.__proto__ = p;" |
| 26303 | "var a = 'foo';" ) |
| 26304 | .IsEmpty()); |
| 26305 | CHECK(CompileRun("(a.acc == 42)" )->BooleanValue(isolate)); |
| 26306 | CHECK(CompileRun("(a.acc = 7) == 7" )->BooleanValue(isolate)); |
| 26307 | |
| 26308 | CHECK(CompileRun("acc.call(1) == 42" )->BooleanValue(isolate)); |
| 26309 | CHECK(CompileRun("acc.call(true)==42" )->BooleanValue(isolate)); |
| 26310 | CHECK(CompileRun("acc.call('aa')==42" )->BooleanValue(isolate)); |
| 26311 | CHECK(CompileRun("acc.call(null) == 42" )->BooleanValue(isolate)); |
| 26312 | CHECK(CompileRun("acc.call(undefined) == 42" )->BooleanValue(isolate)); |
| 26313 | } |
| 26314 | |
| 26315 | class FutexInterruptionThread : public v8::base::Thread { |
| 26316 | public: |
| 26317 | explicit FutexInterruptionThread(v8::Isolate* isolate) |
| 26318 | : Thread(Options("FutexInterruptionThread" )), isolate_(isolate) {} |
| 26319 | |
| 26320 | void Run() override { |
| 26321 | // Wait a bit before terminating. |
| 26322 | v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(100)); |
| 26323 | isolate_->TerminateExecution(); |
| 26324 | } |
| 26325 | |
| 26326 | private: |
| 26327 | v8::Isolate* isolate_; |
| 26328 | }; |
| 26329 | |
| 26330 | |
| 26331 | TEST(FutexInterruption) { |
| 26332 | i::FLAG_harmony_sharedarraybuffer = true; |
| 26333 | v8::Isolate* isolate = CcTest::isolate(); |
| 26334 | v8::HandleScope scope(isolate); |
| 26335 | LocalContext env; |
| 26336 | |
| 26337 | FutexInterruptionThread timeout_thread(isolate); |
| 26338 | |
| 26339 | v8::TryCatch try_catch(CcTest::isolate()); |
| 26340 | timeout_thread.Start(); |
| 26341 | |
| 26342 | CompileRun( |
| 26343 | "var ab = new SharedArrayBuffer(4);" |
| 26344 | "var i32a = new Int32Array(ab);" |
| 26345 | "Atomics.wait(i32a, 0, 0);" ); |
| 26346 | CHECK(try_catch.HasTerminated()); |
| 26347 | timeout_thread.Join(); |
| 26348 | } |
| 26349 | |
| 26350 | THREADED_TEST(SharedArrayBuffer_AllocationInformation) { |
| 26351 | i::FLAG_harmony_sharedarraybuffer = true; |
| 26352 | LocalContext env; |
| 26353 | v8::Isolate* isolate = env->GetIsolate(); |
| 26354 | v8::HandleScope handle_scope(isolate); |
| 26355 | |
| 26356 | const size_t ab_size = 1024; |
| 26357 | Local<v8::SharedArrayBuffer> ab = |
| 26358 | v8::SharedArrayBuffer::New(isolate, ab_size); |
| 26359 | ScopedSharedArrayBufferContents contents(ab->Externalize()); |
| 26360 | |
| 26361 | // Array buffers should have normal allocation mode. |
| 26362 | CHECK_EQ(contents.AllocationMode(), |
| 26363 | v8::ArrayBuffer::Allocator::AllocationMode::kNormal); |
| 26364 | // The allocation must contain the buffer (normally they will be equal, but |
| 26365 | // this is not required by the contract). |
| 26366 | CHECK_NOT_NULL(contents.AllocationBase()); |
| 26367 | const uintptr_t alloc = |
| 26368 | reinterpret_cast<uintptr_t>(contents.AllocationBase()); |
| 26369 | const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data()); |
| 26370 | CHECK_LE(alloc, data); |
| 26371 | CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength()); |
| 26372 | } |
| 26373 | |
| 26374 | static int nb_uncaught_exception_callback_calls = 0; |
| 26375 | |
| 26376 | |
| 26377 | bool NoAbortOnUncaughtException(v8::Isolate* isolate) { |
| 26378 | ++nb_uncaught_exception_callback_calls; |
| 26379 | return false; |
| 26380 | } |
| 26381 | |
| 26382 | |
| 26383 | TEST(AbortOnUncaughtExceptionNoAbort) { |
| 26384 | v8::Isolate* isolate = CcTest::isolate(); |
| 26385 | v8::HandleScope handle_scope(isolate); |
| 26386 | v8::Local<v8::ObjectTemplate> global_template = |
| 26387 | v8::ObjectTemplate::New(isolate); |
| 26388 | LocalContext env(nullptr, global_template); |
| 26389 | |
| 26390 | i::FLAG_abort_on_uncaught_exception = true; |
| 26391 | isolate->SetAbortOnUncaughtExceptionCallback(NoAbortOnUncaughtException); |
| 26392 | |
| 26393 | CompileRun("function boom() { throw new Error(\"boom\") }" ); |
| 26394 | |
| 26395 | v8::Local<v8::Object> global_object = env->Global(); |
| 26396 | v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( |
| 26397 | global_object->Get(env.local(), v8_str("boom" )).ToLocalChecked()); |
| 26398 | |
| 26399 | CHECK(foo->Call(env.local(), global_object, 0, nullptr).IsEmpty()); |
| 26400 | |
| 26401 | CHECK_EQ(1, nb_uncaught_exception_callback_calls); |
| 26402 | } |
| 26403 | |
| 26404 | |
| 26405 | TEST(AccessCheckedIsConcatSpreadable) { |
| 26406 | v8::Isolate* isolate = CcTest::isolate(); |
| 26407 | HandleScope scope(isolate); |
| 26408 | LocalContext env; |
| 26409 | |
| 26410 | // Object with access check |
| 26411 | Local<ObjectTemplate> spreadable_template = v8::ObjectTemplate::New(isolate); |
| 26412 | spreadable_template->SetAccessCheckCallback(AccessBlocker); |
| 26413 | spreadable_template->Set(v8::Symbol::GetIsConcatSpreadable(isolate), |
| 26414 | v8::Boolean::New(isolate, true)); |
| 26415 | Local<Object> object = |
| 26416 | spreadable_template->NewInstance(env.local()).ToLocalChecked(); |
| 26417 | |
| 26418 | allowed_access = true; |
| 26419 | CHECK(env->Global()->Set(env.local(), v8_str("object" ), object).FromJust()); |
| 26420 | object->Set(env.local(), v8_str("length" ), v8_num(2)).FromJust(); |
| 26421 | object->Set(env.local(), 0U, v8_str("a" )).FromJust(); |
| 26422 | object->Set(env.local(), 1U, v8_str("b" )).FromJust(); |
| 26423 | |
| 26424 | // Access check is allowed, and the object is spread |
| 26425 | CompileRun("var result = [].concat(object)" ); |
| 26426 | ExpectTrue("Array.isArray(result)" ); |
| 26427 | ExpectString("result[0]" , "a" ); |
| 26428 | ExpectString("result[1]" , "b" ); |
| 26429 | ExpectTrue("result.length === 2" ); |
| 26430 | ExpectTrue("object[Symbol.isConcatSpreadable]" ); |
| 26431 | |
| 26432 | // If access check fails, the value of @@isConcatSpreadable is ignored |
| 26433 | allowed_access = false; |
| 26434 | CompileRun("var result = [].concat(object)" ); |
| 26435 | ExpectTrue("Array.isArray(result)" ); |
| 26436 | ExpectTrue("result[0] === object" ); |
| 26437 | ExpectTrue("result.length === 1" ); |
| 26438 | ExpectTrue("object[Symbol.isConcatSpreadable] === undefined" ); |
| 26439 | } |
| 26440 | |
| 26441 | |
| 26442 | TEST(AccessCheckedToStringTag) { |
| 26443 | v8::Isolate* isolate = CcTest::isolate(); |
| 26444 | HandleScope scope(isolate); |
| 26445 | LocalContext env; |
| 26446 | |
| 26447 | // Object with access check |
| 26448 | Local<ObjectTemplate> object_template = v8::ObjectTemplate::New(isolate); |
| 26449 | object_template->SetAccessCheckCallback(AccessBlocker); |
| 26450 | Local<Object> object = |
| 26451 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 26452 | |
| 26453 | allowed_access = true; |
| 26454 | env->Global()->Set(env.local(), v8_str("object" ), object).FromJust(); |
| 26455 | object->Set(env.local(), v8::Symbol::GetToStringTag(isolate), v8_str("hello" )) |
| 26456 | .FromJust(); |
| 26457 | |
| 26458 | // Access check is allowed, and the toStringTag is read |
| 26459 | CompileRun("var result = Object.prototype.toString.call(object)" ); |
| 26460 | ExpectString("result" , "[object hello]" ); |
| 26461 | ExpectString("object[Symbol.toStringTag]" , "hello" ); |
| 26462 | |
| 26463 | // ToString through the API should succeed too. |
| 26464 | String::Utf8Value result_allowed( |
| 26465 | isolate, object->ObjectProtoToString(env.local()).ToLocalChecked()); |
| 26466 | CHECK_EQ(0, strcmp(*result_allowed, "[object hello]" )); |
| 26467 | |
| 26468 | // If access check fails, the value of @@toStringTag is ignored |
| 26469 | allowed_access = false; |
| 26470 | CompileRun("var result = Object.prototype.toString.call(object)" ); |
| 26471 | ExpectString("result" , "[object Object]" ); |
| 26472 | ExpectTrue("object[Symbol.toStringTag] === undefined" ); |
| 26473 | |
| 26474 | // ToString through the API should also fail. |
| 26475 | String::Utf8Value result_denied( |
| 26476 | isolate, object->ObjectProtoToString(env.local()).ToLocalChecked()); |
| 26477 | CHECK_EQ(0, strcmp(*result_denied, "[object Object]" )); |
| 26478 | } |
| 26479 | |
| 26480 | TEST(TemplateIteratorPrototypeIntrinsics) { |
| 26481 | v8::Isolate* isolate = CcTest::isolate(); |
| 26482 | v8::HandleScope scope(isolate); |
| 26483 | LocalContext env; |
| 26484 | |
| 26485 | // Object templates. |
| 26486 | { |
| 26487 | Local<ObjectTemplate> object_template = v8::ObjectTemplate::New(isolate); |
| 26488 | object_template->SetIntrinsicDataProperty(v8_str("iter_proto" ), |
| 26489 | v8::kIteratorPrototype); |
| 26490 | Local<Object> object = |
| 26491 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 26492 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), object).FromJust()); |
| 26493 | ExpectTrue("obj.iter_proto === [][Symbol.iterator]().__proto__.__proto__" ); |
| 26494 | } |
| 26495 | // Setting %IteratorProto% on the function object's prototype template. |
| 26496 | { |
| 26497 | Local<FunctionTemplate> func_template = v8::FunctionTemplate::New(isolate); |
| 26498 | func_template->PrototypeTemplate()->SetIntrinsicDataProperty( |
| 26499 | v8_str("iter_proto" ), v8::kIteratorPrototype); |
| 26500 | Local<Function> func1 = |
| 26501 | func_template->GetFunction(env.local()).ToLocalChecked(); |
| 26502 | CHECK(env->Global()->Set(env.local(), v8_str("func1" ), func1).FromJust()); |
| 26503 | Local<Function> func2 = |
| 26504 | func_template->GetFunction(env.local()).ToLocalChecked(); |
| 26505 | CHECK(env->Global()->Set(env.local(), v8_str("func2" ), func2).FromJust()); |
| 26506 | ExpectTrue( |
| 26507 | "func1.prototype.iter_proto === " |
| 26508 | "[][Symbol.iterator]().__proto__.__proto__" ); |
| 26509 | ExpectTrue( |
| 26510 | "func2.prototype.iter_proto === " |
| 26511 | "[][Symbol.iterator]().__proto__.__proto__" ); |
| 26512 | ExpectTrue("func1.prototype.iter_proto === func2.prototype.iter_proto" ); |
| 26513 | |
| 26514 | Local<Object> instance1 = func1->NewInstance(env.local()).ToLocalChecked(); |
| 26515 | CHECK(env->Global() |
| 26516 | ->Set(env.local(), v8_str("instance1" ), instance1) |
| 26517 | .FromJust()); |
| 26518 | ExpectFalse("instance1.hasOwnProperty('iter_proto')" ); |
| 26519 | ExpectTrue("'iter_proto' in instance1.__proto__" ); |
| 26520 | ExpectTrue( |
| 26521 | "instance1.iter_proto === [][Symbol.iterator]().__proto__.__proto__" ); |
| 26522 | } |
| 26523 | // Put %IteratorProto% in a function object's inheritance chain. |
| 26524 | { |
| 26525 | Local<FunctionTemplate> parent_template = |
| 26526 | v8::FunctionTemplate::New(isolate); |
| 26527 | parent_template->RemovePrototype(); // Remove so there is no name clash. |
| 26528 | parent_template->SetIntrinsicDataProperty(v8_str("prototype" ), |
| 26529 | v8::kIteratorPrototype); |
| 26530 | Local<FunctionTemplate> func_template = v8::FunctionTemplate::New(isolate); |
| 26531 | func_template->Inherit(parent_template); |
| 26532 | |
| 26533 | Local<Function> func = |
| 26534 | func_template->GetFunction(env.local()).ToLocalChecked(); |
| 26535 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 26536 | ExpectTrue( |
| 26537 | "func.prototype.__proto__ === " |
| 26538 | "[][Symbol.iterator]().__proto__.__proto__" ); |
| 26539 | |
| 26540 | Local<Object> func_instance = |
| 26541 | func->NewInstance(env.local()).ToLocalChecked(); |
| 26542 | CHECK(env->Global() |
| 26543 | ->Set(env.local(), v8_str("instance" ), func_instance) |
| 26544 | .FromJust()); |
| 26545 | ExpectTrue( |
| 26546 | "instance.__proto__.__proto__ === " |
| 26547 | "[][Symbol.iterator]().__proto__.__proto__" ); |
| 26548 | ExpectTrue("instance.__proto__.__proto__.__proto__ === Object.prototype" ); |
| 26549 | } |
| 26550 | } |
| 26551 | |
| 26552 | TEST(TemplateErrorPrototypeIntrinsics) { |
| 26553 | v8::Isolate* isolate = CcTest::isolate(); |
| 26554 | v8::HandleScope scope(isolate); |
| 26555 | LocalContext env; |
| 26556 | |
| 26557 | // Object templates. |
| 26558 | { |
| 26559 | Local<ObjectTemplate> object_template = v8::ObjectTemplate::New(isolate); |
| 26560 | object_template->SetIntrinsicDataProperty(v8_str("error_proto" ), |
| 26561 | v8::kErrorPrototype); |
| 26562 | Local<Object> object = |
| 26563 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 26564 | CHECK(env->Global()->Set(env.local(), v8_str("obj" ), object).FromJust()); |
| 26565 | ExpectTrue("obj.error_proto === Error.prototype" ); |
| 26566 | Local<Value> error = v8::Exception::Error(v8_str("error message" )); |
| 26567 | CHECK(env->Global()->Set(env.local(), v8_str("err" ), error).FromJust()); |
| 26568 | ExpectTrue("obj.error_proto === Object.getPrototypeOf(err)" ); |
| 26569 | } |
| 26570 | // Setting %ErrorPrototype% on the function object's prototype template. |
| 26571 | { |
| 26572 | Local<FunctionTemplate> func_template = v8::FunctionTemplate::New(isolate); |
| 26573 | func_template->PrototypeTemplate()->SetIntrinsicDataProperty( |
| 26574 | v8_str("error_proto" ), v8::kErrorPrototype); |
| 26575 | Local<Function> func1 = |
| 26576 | func_template->GetFunction(env.local()).ToLocalChecked(); |
| 26577 | CHECK(env->Global()->Set(env.local(), v8_str("func1" ), func1).FromJust()); |
| 26578 | Local<Function> func2 = |
| 26579 | func_template->GetFunction(env.local()).ToLocalChecked(); |
| 26580 | CHECK(env->Global()->Set(env.local(), v8_str("func2" ), func2).FromJust()); |
| 26581 | ExpectTrue("func1.prototype.error_proto === Error.prototype" ); |
| 26582 | ExpectTrue("func2.prototype.error_proto === Error.prototype" ); |
| 26583 | ExpectTrue("func1.prototype.error_proto === func2.prototype.error_proto" ); |
| 26584 | |
| 26585 | Local<Object> instance1 = func1->NewInstance(env.local()).ToLocalChecked(); |
| 26586 | CHECK(env->Global() |
| 26587 | ->Set(env.local(), v8_str("instance1" ), instance1) |
| 26588 | .FromJust()); |
| 26589 | ExpectFalse("instance1.hasOwnProperty('error_proto')" ); |
| 26590 | ExpectTrue("'error_proto' in instance1.__proto__" ); |
| 26591 | ExpectTrue("instance1.error_proto === Error.prototype" ); |
| 26592 | } |
| 26593 | // Put %ErrorPrototype% in a function object's inheritance chain. |
| 26594 | { |
| 26595 | Local<FunctionTemplate> parent_template = |
| 26596 | v8::FunctionTemplate::New(isolate); |
| 26597 | parent_template->RemovePrototype(); // Remove so there is no name clash. |
| 26598 | parent_template->SetIntrinsicDataProperty(v8_str("prototype" ), |
| 26599 | v8::kErrorPrototype); |
| 26600 | Local<FunctionTemplate> func_template = v8::FunctionTemplate::New(isolate); |
| 26601 | func_template->Inherit(parent_template); |
| 26602 | |
| 26603 | Local<Function> func = |
| 26604 | func_template->GetFunction(env.local()).ToLocalChecked(); |
| 26605 | CHECK(env->Global()->Set(env.local(), v8_str("func" ), func).FromJust()); |
| 26606 | ExpectTrue("func.prototype.__proto__ === Error.prototype" ); |
| 26607 | |
| 26608 | Local<Object> func_instance = |
| 26609 | func->NewInstance(env.local()).ToLocalChecked(); |
| 26610 | CHECK(env->Global() |
| 26611 | ->Set(env.local(), v8_str("instance" ), func_instance) |
| 26612 | .FromJust()); |
| 26613 | ExpectTrue("instance.__proto__.__proto__.__proto__ === Object.prototype" ); |
| 26614 | // Now let's check if %ErrorPrototype% properties are in the instance. |
| 26615 | ExpectTrue("'constructor' in instance" ); |
| 26616 | ExpectTrue("'message' in instance" ); |
| 26617 | ExpectTrue("'name' in instance" ); |
| 26618 | ExpectTrue("'toString' in instance" ); |
| 26619 | } |
| 26620 | } |
| 26621 | |
| 26622 | TEST(ObjectTemplateArrayProtoIntrinsics) { |
| 26623 | v8::Isolate* isolate = CcTest::isolate(); |
| 26624 | v8::HandleScope scope(isolate); |
| 26625 | LocalContext env; |
| 26626 | |
| 26627 | Local<ObjectTemplate> object_template = v8::ObjectTemplate::New(isolate); |
| 26628 | object_template->SetIntrinsicDataProperty(v8_str("prop_entries" ), |
| 26629 | v8::kArrayProto_entries); |
| 26630 | object_template->SetIntrinsicDataProperty(v8_str("prop_forEach" ), |
| 26631 | v8::kArrayProto_forEach); |
| 26632 | object_template->SetIntrinsicDataProperty(v8_str("prop_keys" ), |
| 26633 | v8::kArrayProto_keys); |
| 26634 | object_template->SetIntrinsicDataProperty(v8_str("prop_values" ), |
| 26635 | v8::kArrayProto_values); |
| 26636 | Local<Object> object = |
| 26637 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 26638 | CHECK(env->Global()->Set(env.local(), v8_str("obj1" ), object).FromJust()); |
| 26639 | |
| 26640 | const struct { |
| 26641 | const char* const object_property_name; |
| 26642 | const char* const array_property_name; |
| 26643 | } intrinsics_comparisons[] = { |
| 26644 | {"prop_entries" , "Array.prototype.entries" }, |
| 26645 | {"prop_forEach" , "Array.prototype.forEach" }, |
| 26646 | {"prop_keys" , "Array.prototype.keys" }, |
| 26647 | {"prop_values" , "Array.prototype[Symbol.iterator]" }, |
| 26648 | }; |
| 26649 | |
| 26650 | for (unsigned i = 0; i < arraysize(intrinsics_comparisons); i++) { |
| 26651 | i::ScopedVector<char> test_string(64); |
| 26652 | |
| 26653 | i::SNPrintF(test_string, "typeof obj1.%s" , |
| 26654 | intrinsics_comparisons[i].object_property_name); |
| 26655 | ExpectString(test_string.start(), "function" ); |
| 26656 | |
| 26657 | i::SNPrintF(test_string, "obj1.%s === %s" , |
| 26658 | intrinsics_comparisons[i].object_property_name, |
| 26659 | intrinsics_comparisons[i].array_property_name); |
| 26660 | ExpectTrue(test_string.start()); |
| 26661 | |
| 26662 | i::SNPrintF(test_string, "obj1.%s = 42" , |
| 26663 | intrinsics_comparisons[i].object_property_name); |
| 26664 | CompileRun(test_string.start()); |
| 26665 | |
| 26666 | i::SNPrintF(test_string, "obj1.%s === %s" , |
| 26667 | intrinsics_comparisons[i].object_property_name, |
| 26668 | intrinsics_comparisons[i].array_property_name); |
| 26669 | ExpectFalse(test_string.start()); |
| 26670 | |
| 26671 | i::SNPrintF(test_string, "typeof obj1.%s" , |
| 26672 | intrinsics_comparisons[i].object_property_name); |
| 26673 | ExpectString(test_string.start(), "number" ); |
| 26674 | } |
| 26675 | } |
| 26676 | |
| 26677 | TEST(ObjectTemplatePerContextIntrinsics) { |
| 26678 | v8::Isolate* isolate = CcTest::isolate(); |
| 26679 | v8::HandleScope scope(isolate); |
| 26680 | LocalContext env; |
| 26681 | |
| 26682 | Local<ObjectTemplate> object_template = v8::ObjectTemplate::New(isolate); |
| 26683 | object_template->SetIntrinsicDataProperty(v8_str("values" ), |
| 26684 | v8::kArrayProto_values); |
| 26685 | Local<Object> object = |
| 26686 | object_template->NewInstance(env.local()).ToLocalChecked(); |
| 26687 | |
| 26688 | CHECK(env->Global()->Set(env.local(), v8_str("obj1" ), object).FromJust()); |
| 26689 | ExpectString("typeof obj1.values" , "function" ); |
| 26690 | |
| 26691 | auto values = Local<Function>::Cast( |
| 26692 | object->Get(env.local(), v8_str("values" )).ToLocalChecked()); |
| 26693 | auto fn = i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*values)); |
| 26694 | auto ctx = v8::Utils::OpenHandle(*env.local()); |
| 26695 | CHECK_EQ(*fn->GetCreationContext(), *ctx); |
| 26696 | |
| 26697 | { |
| 26698 | LocalContext env2; |
| 26699 | Local<Object> object2 = |
| 26700 | object_template->NewInstance(env2.local()).ToLocalChecked(); |
| 26701 | CHECK( |
| 26702 | env2->Global()->Set(env2.local(), v8_str("obj2" ), object2).FromJust()); |
| 26703 | ExpectString("typeof obj2.values" , "function" ); |
| 26704 | CHECK_NE(*object->Get(env2.local(), v8_str("values" )).ToLocalChecked(), |
| 26705 | *object2->Get(env2.local(), v8_str("values" )).ToLocalChecked()); |
| 26706 | |
| 26707 | auto values2 = Local<Function>::Cast( |
| 26708 | object2->Get(env2.local(), v8_str("values" )).ToLocalChecked()); |
| 26709 | auto fn2 = i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*values2)); |
| 26710 | auto ctx2 = v8::Utils::OpenHandle(*env2.local()); |
| 26711 | CHECK_EQ(*fn2->GetCreationContext(), *ctx2); |
| 26712 | } |
| 26713 | } |
| 26714 | |
| 26715 | |
| 26716 | TEST(Proxy) { |
| 26717 | LocalContext context; |
| 26718 | v8::Isolate* isolate = CcTest::isolate(); |
| 26719 | v8::HandleScope scope(isolate); |
| 26720 | v8::Local<v8::Object> target = CompileRun("({})" ).As<v8::Object>(); |
| 26721 | v8::Local<v8::Object> handler = CompileRun("({})" ).As<v8::Object>(); |
| 26722 | |
| 26723 | v8::Local<v8::Proxy> proxy = |
| 26724 | v8::Proxy::New(context.local(), target, handler).ToLocalChecked(); |
| 26725 | CHECK(proxy->IsProxy()); |
| 26726 | CHECK(!target->IsProxy()); |
| 26727 | CHECK(!proxy->IsRevoked()); |
| 26728 | CHECK(proxy->GetTarget()->SameValue(target)); |
| 26729 | CHECK(proxy->GetHandler()->SameValue(handler)); |
| 26730 | |
| 26731 | proxy->Revoke(); |
| 26732 | CHECK(proxy->IsProxy()); |
| 26733 | CHECK(!target->IsProxy()); |
| 26734 | CHECK(proxy->IsRevoked()); |
| 26735 | CHECK(proxy->GetTarget()->IsNull()); |
| 26736 | CHECK(proxy->GetHandler()->IsNull()); |
| 26737 | } |
| 26738 | |
| 26739 | WeakCallCounterAndPersistent<Value>* CreateGarbageWithWeakCallCounter( |
| 26740 | v8::Isolate* isolate, WeakCallCounter* counter) { |
| 26741 | v8::Locker locker(isolate); |
| 26742 | LocalContext env; |
| 26743 | HandleScope scope(isolate); |
| 26744 | WeakCallCounterAndPersistent<Value>* val = |
| 26745 | new WeakCallCounterAndPersistent<Value>(counter); |
| 26746 | val->handle.Reset(isolate, Object::New(isolate)); |
| 26747 | val->handle.SetWeak(val, &WeakPointerCallback, |
| 26748 | v8::WeakCallbackType::kParameter); |
| 26749 | return val; |
| 26750 | } |
| 26751 | |
| 26752 | class MemoryPressureThread : public v8::base::Thread { |
| 26753 | public: |
| 26754 | explicit MemoryPressureThread(v8::Isolate* isolate, |
| 26755 | v8::MemoryPressureLevel level) |
| 26756 | : Thread(Options("MemoryPressureThread" )), |
| 26757 | isolate_(isolate), |
| 26758 | level_(level) {} |
| 26759 | |
| 26760 | void Run() override { isolate_->MemoryPressureNotification(level_); } |
| 26761 | |
| 26762 | private: |
| 26763 | v8::Isolate* isolate_; |
| 26764 | v8::MemoryPressureLevel level_; |
| 26765 | }; |
| 26766 | |
| 26767 | TEST(MemoryPressure) { |
| 26768 | if (v8::internal::FLAG_optimize_for_size) return; |
| 26769 | v8::Isolate* isolate = CcTest::isolate(); |
| 26770 | WeakCallCounter counter(1234); |
| 26771 | |
| 26772 | // Check that critical memory pressure notification sets GC interrupt. |
| 26773 | auto garbage = CreateGarbageWithWeakCallCounter(isolate, &counter); |
| 26774 | CHECK(!v8::Locker::IsLocked(isolate)); |
| 26775 | { |
| 26776 | v8::Locker locker(isolate); |
| 26777 | v8::HandleScope scope(isolate); |
| 26778 | LocalContext env; |
| 26779 | MemoryPressureThread memory_pressure_thread( |
| 26780 | isolate, v8::MemoryPressureLevel::kCritical); |
| 26781 | memory_pressure_thread.Start(); |
| 26782 | memory_pressure_thread.Join(); |
| 26783 | // This should trigger GC. |
| 26784 | CHECK_EQ(0, counter.NumberOfWeakCalls()); |
| 26785 | CompileRun("(function noop() { return 0; })()" ); |
| 26786 | CHECK_EQ(1, counter.NumberOfWeakCalls()); |
| 26787 | } |
| 26788 | delete garbage; |
| 26789 | // Check that critical memory pressure notification triggers GC. |
| 26790 | garbage = CreateGarbageWithWeakCallCounter(isolate, &counter); |
| 26791 | { |
| 26792 | v8::Locker locker(isolate); |
| 26793 | // If isolate is locked, memory pressure notification should trigger GC. |
| 26794 | CHECK_EQ(1, counter.NumberOfWeakCalls()); |
| 26795 | isolate->MemoryPressureNotification(v8::MemoryPressureLevel::kCritical); |
| 26796 | CHECK_EQ(2, counter.NumberOfWeakCalls()); |
| 26797 | } |
| 26798 | delete garbage; |
| 26799 | // Check that moderate memory pressure notification sets GC into memory |
| 26800 | // optimizing mode. |
| 26801 | isolate->MemoryPressureNotification(v8::MemoryPressureLevel::kModerate); |
| 26802 | CHECK(CcTest::i_isolate()->heap()->ShouldOptimizeForMemoryUsage()); |
| 26803 | // Check that disabling memory pressure returns GC into normal mode. |
| 26804 | isolate->MemoryPressureNotification(v8::MemoryPressureLevel::kNone); |
| 26805 | CHECK(!CcTest::i_isolate()->heap()->ShouldOptimizeForMemoryUsage()); |
| 26806 | } |
| 26807 | |
| 26808 | TEST(SetIntegrityLevel) { |
| 26809 | LocalContext context; |
| 26810 | v8::Isolate* isolate = CcTest::isolate(); |
| 26811 | v8::HandleScope scope(isolate); |
| 26812 | |
| 26813 | v8::Local<v8::Object> obj = v8::Object::New(isolate); |
| 26814 | CHECK(context->Global()->Set(context.local(), v8_str("o" ), obj).FromJust()); |
| 26815 | |
| 26816 | v8::Local<v8::Value> is_frozen = CompileRun("Object.isFrozen(o)" ); |
| 26817 | CHECK(!is_frozen->BooleanValue(isolate)); |
| 26818 | |
| 26819 | CHECK(obj->SetIntegrityLevel(context.local(), v8::IntegrityLevel::kFrozen) |
| 26820 | .FromJust()); |
| 26821 | |
| 26822 | is_frozen = CompileRun("Object.isFrozen(o)" ); |
| 26823 | CHECK(is_frozen->BooleanValue(isolate)); |
| 26824 | } |
| 26825 | |
| 26826 | TEST(PrivateForApiIsNumber) { |
| 26827 | LocalContext context; |
| 26828 | v8::Isolate* isolate = CcTest::isolate(); |
| 26829 | v8::HandleScope scope(isolate); |
| 26830 | |
| 26831 | // Shouldn't crash. |
| 26832 | v8::Private::ForApi(isolate, v8_str("42" )); |
| 26833 | } |
| 26834 | |
| 26835 | THREADED_TEST(ImmutableProto) { |
| 26836 | LocalContext context; |
| 26837 | v8::Isolate* isolate = context->GetIsolate(); |
| 26838 | v8::HandleScope handle_scope(isolate); |
| 26839 | |
| 26840 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 26841 | templ->InstanceTemplate()->SetImmutableProto(); |
| 26842 | |
| 26843 | Local<v8::Object> object = templ->GetFunction(context.local()) |
| 26844 | .ToLocalChecked() |
| 26845 | ->NewInstance(context.local()) |
| 26846 | .ToLocalChecked(); |
| 26847 | |
| 26848 | // Look up the prototype |
| 26849 | Local<v8::Value> original_proto = |
| 26850 | object->Get(context.local(), v8_str("__proto__" )).ToLocalChecked(); |
| 26851 | |
| 26852 | // Setting the prototype (e.g., to null) throws |
| 26853 | CHECK(object->SetPrototype(context.local(), v8::Null(isolate)).IsNothing()); |
| 26854 | |
| 26855 | // The original prototype is still there |
| 26856 | Local<Value> new_proto = |
| 26857 | object->Get(context.local(), v8_str("__proto__" )).ToLocalChecked(); |
| 26858 | CHECK(new_proto->IsObject()); |
| 26859 | CHECK(new_proto.As<v8::Object>() |
| 26860 | ->Equals(context.local(), original_proto) |
| 26861 | .FromJust()); |
| 26862 | } |
| 26863 | |
| 26864 | Local<v8::Context> call_eval_context; |
| 26865 | Local<v8::Function> call_eval_bound_function; |
| 26866 | |
| 26867 | static void CallEval(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 26868 | v8::Context::Scope scope(call_eval_context); |
| 26869 | args.GetReturnValue().Set( |
| 26870 | call_eval_bound_function |
| 26871 | ->Call(call_eval_context, call_eval_context->Global(), 0, nullptr) |
| 26872 | .ToLocalChecked()); |
| 26873 | } |
| 26874 | |
| 26875 | TEST(CrossActivationEval) { |
| 26876 | LocalContext env; |
| 26877 | v8::Isolate* isolate = env->GetIsolate(); |
| 26878 | v8::HandleScope scope(isolate); |
| 26879 | { |
| 26880 | call_eval_context = v8::Context::New(isolate); |
| 26881 | v8::Context::Scope scope(call_eval_context); |
| 26882 | call_eval_bound_function = |
| 26883 | Local<Function>::Cast(CompileRun("eval.bind(this, '1')" )); |
| 26884 | } |
| 26885 | env->Global() |
| 26886 | ->Set(env.local(), v8_str("CallEval" ), |
| 26887 | v8::FunctionTemplate::New(isolate, CallEval) |
| 26888 | ->GetFunction(env.local()) |
| 26889 | .ToLocalChecked()) |
| 26890 | .FromJust(); |
| 26891 | Local<Value> result = CompileRun("CallEval();" ); |
| 26892 | CHECK(result->IsInt32()); |
| 26893 | CHECK_EQ(1, result->Int32Value(env.local()).FromJust()); |
| 26894 | } |
| 26895 | |
| 26896 | TEST(EvalInAccessCheckedContext) { |
| 26897 | v8::Isolate* isolate = CcTest::isolate(); |
| 26898 | v8::HandleScope scope(isolate); |
| 26899 | |
| 26900 | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); |
| 26901 | |
| 26902 | obj_template->SetAccessCheckCallback(AccessAlwaysAllowed); |
| 26903 | |
| 26904 | v8::Local<Context> context0 = Context::New(isolate, nullptr, obj_template); |
| 26905 | v8::Local<Context> context1 = Context::New(isolate, nullptr, obj_template); |
| 26906 | |
| 26907 | Local<Value> foo = v8_str("foo" ); |
| 26908 | Local<Value> bar = v8_str("bar" ); |
| 26909 | |
| 26910 | // Set to different domains. |
| 26911 | context0->SetSecurityToken(foo); |
| 26912 | context1->SetSecurityToken(bar); |
| 26913 | |
| 26914 | // Set up function in context0 that uses eval from context0. |
| 26915 | context0->Enter(); |
| 26916 | v8::Local<v8::Value> fun = CompileRun( |
| 26917 | "var x = 42;" |
| 26918 | "(function() {" |
| 26919 | " var e = eval;" |
| 26920 | " return function(s) { return e(s); }" |
| 26921 | "})()" ); |
| 26922 | context0->Exit(); |
| 26923 | |
| 26924 | // Put the function into context1 and call it. Since the access check |
| 26925 | // callback always returns true, the call succeeds even though the tokens |
| 26926 | // are different. |
| 26927 | context1->Enter(); |
| 26928 | context1->Global()->Set(context1, v8_str("fun" ), fun).FromJust(); |
| 26929 | v8::Local<v8::Value> x_value = CompileRun("fun('x')" ); |
| 26930 | CHECK_EQ(42, x_value->Int32Value(context1).FromJust()); |
| 26931 | context1->Exit(); |
| 26932 | } |
| 26933 | |
| 26934 | THREADED_TEST(ImmutableProtoWithParent) { |
| 26935 | LocalContext context; |
| 26936 | v8::Isolate* isolate = context->GetIsolate(); |
| 26937 | v8::HandleScope handle_scope(isolate); |
| 26938 | |
| 26939 | Local<v8::FunctionTemplate> parent = v8::FunctionTemplate::New(isolate); |
| 26940 | |
| 26941 | Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| 26942 | templ->Inherit(parent); |
| 26943 | templ->PrototypeTemplate()->SetImmutableProto(); |
| 26944 | |
| 26945 | Local<v8::Function> function = |
| 26946 | templ->GetFunction(context.local()).ToLocalChecked(); |
| 26947 | Local<v8::Object> instance = |
| 26948 | function->NewInstance(context.local()).ToLocalChecked(); |
| 26949 | Local<v8::Object> prototype = |
| 26950 | instance->Get(context.local(), v8_str("__proto__" )) |
| 26951 | .ToLocalChecked() |
| 26952 | ->ToObject(context.local()) |
| 26953 | .ToLocalChecked(); |
| 26954 | |
| 26955 | // Look up the prototype |
| 26956 | Local<v8::Value> original_proto = |
| 26957 | prototype->Get(context.local(), v8_str("__proto__" )).ToLocalChecked(); |
| 26958 | |
| 26959 | // Setting the prototype (e.g., to null) throws |
| 26960 | CHECK( |
| 26961 | prototype->SetPrototype(context.local(), v8::Null(isolate)).IsNothing()); |
| 26962 | |
| 26963 | // The original prototype is still there |
| 26964 | Local<Value> new_proto = |
| 26965 | prototype->Get(context.local(), v8_str("__proto__" )).ToLocalChecked(); |
| 26966 | CHECK(new_proto->IsObject()); |
| 26967 | CHECK(new_proto.As<v8::Object>() |
| 26968 | ->Equals(context.local(), original_proto) |
| 26969 | .FromJust()); |
| 26970 | } |
| 26971 | |
| 26972 | TEST(InternalFieldsOnGlobalProxy) { |
| 26973 | v8::Isolate* isolate = CcTest::isolate(); |
| 26974 | v8::HandleScope scope(isolate); |
| 26975 | |
| 26976 | v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate); |
| 26977 | obj_template->SetInternalFieldCount(1); |
| 26978 | |
| 26979 | v8::Local<v8::Context> context = Context::New(isolate, nullptr, obj_template); |
| 26980 | v8::Local<v8::Object> global = context->Global(); |
| 26981 | CHECK_EQ(1, global->InternalFieldCount()); |
| 26982 | } |
| 26983 | |
| 26984 | THREADED_TEST(ImmutableProtoGlobal) { |
| 26985 | v8::Isolate* isolate = CcTest::isolate(); |
| 26986 | v8::HandleScope handle_scope(isolate); |
| 26987 | Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); |
| 26988 | global_template->SetImmutableProto(); |
| 26989 | v8::Local<Context> context = Context::New(isolate, nullptr, global_template); |
| 26990 | Context::Scope context_scope(context); |
| 26991 | v8::Local<Value> result = CompileRun( |
| 26992 | "global = this;" |
| 26993 | "(function() {" |
| 26994 | " try {" |
| 26995 | " global.__proto__ = {};" |
| 26996 | " return 0;" |
| 26997 | " } catch (e) {" |
| 26998 | " return 1;" |
| 26999 | " }" |
| 27000 | "})()" ); |
| 27001 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 1)) |
| 27002 | .FromJust()); |
| 27003 | } |
| 27004 | |
| 27005 | THREADED_TEST(MutableProtoGlobal) { |
| 27006 | v8::Isolate* isolate = CcTest::isolate(); |
| 27007 | v8::HandleScope handle_scope(isolate); |
| 27008 | Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); |
| 27009 | v8::Local<Context> context = Context::New(isolate, nullptr, global_template); |
| 27010 | Context::Scope context_scope(context); |
| 27011 | v8::Local<Value> result = CompileRun( |
| 27012 | "global = this;" |
| 27013 | "(function() {" |
| 27014 | " try {" |
| 27015 | " global.__proto__ = {};" |
| 27016 | " return 0;" |
| 27017 | " } catch (e) {" |
| 27018 | " return 1;" |
| 27019 | " }" |
| 27020 | "})()" ); |
| 27021 | CHECK(result->Equals(context, v8::Integer::New(CcTest::isolate(), 0)) |
| 27022 | .FromJust()); |
| 27023 | } |
| 27024 | |
| 27025 | TEST(InternalFieldsOnTypedArray) { |
| 27026 | LocalContext env; |
| 27027 | v8::Isolate* isolate = env->GetIsolate(); |
| 27028 | v8::HandleScope scope(isolate); |
| 27029 | v8::Local<v8::Context> context = env.local(); |
| 27030 | Context::Scope context_scope(context); |
| 27031 | v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1); |
| 27032 | v8::Local<v8::Uint8Array> array = v8::Uint8Array::New(buffer, 0, 1); |
| 27033 | for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| 27034 | CHECK_EQ(static_cast<void*>(nullptr), |
| 27035 | array->GetAlignedPointerFromInternalField(i)); |
| 27036 | } |
| 27037 | } |
| 27038 | |
| 27039 | TEST(InternalFieldsOnDataView) { |
| 27040 | LocalContext env; |
| 27041 | v8::Isolate* isolate = env->GetIsolate(); |
| 27042 | v8::HandleScope scope(isolate); |
| 27043 | v8::Local<v8::Context> context = env.local(); |
| 27044 | Context::Scope context_scope(context); |
| 27045 | v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1); |
| 27046 | v8::Local<v8::DataView> array = v8::DataView::New(buffer, 0, 1); |
| 27047 | for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| 27048 | CHECK_EQ(static_cast<void*>(nullptr), |
| 27049 | array->GetAlignedPointerFromInternalField(i)); |
| 27050 | } |
| 27051 | } |
| 27052 | |
| 27053 | TEST(SetPrototypeTemplate) { |
| 27054 | LocalContext env; |
| 27055 | v8::Isolate* isolate = env->GetIsolate(); |
| 27056 | v8::HandleScope scope(isolate); |
| 27057 | |
| 27058 | Local<FunctionTemplate> HTMLElementTemplate = FunctionTemplate::New(isolate); |
| 27059 | Local<FunctionTemplate> HTMLImageElementTemplate = |
| 27060 | FunctionTemplate::New(isolate); |
| 27061 | HTMLImageElementTemplate->Inherit(HTMLElementTemplate); |
| 27062 | |
| 27063 | Local<FunctionTemplate> ImageTemplate = FunctionTemplate::New(isolate); |
| 27064 | ImageTemplate->SetPrototypeProviderTemplate(HTMLImageElementTemplate); |
| 27065 | |
| 27066 | Local<Function> HTMLImageElement = |
| 27067 | HTMLImageElementTemplate->GetFunction(env.local()).ToLocalChecked(); |
| 27068 | Local<Function> Image = |
| 27069 | ImageTemplate->GetFunction(env.local()).ToLocalChecked(); |
| 27070 | |
| 27071 | CHECK(env->Global() |
| 27072 | ->Set(env.local(), v8_str("HTMLImageElement" ), HTMLImageElement) |
| 27073 | .FromJust()); |
| 27074 | CHECK(env->Global()->Set(env.local(), v8_str("Image" ), Image).FromJust()); |
| 27075 | |
| 27076 | ExpectTrue("Image.prototype === HTMLImageElement.prototype" ); |
| 27077 | } |
| 27078 | |
| 27079 | void ensure_receiver_is_global_proxy( |
| 27080 | v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 27081 | CHECK(v8::Utils::OpenHandle(*info.This())->IsJSGlobalProxy()); |
| 27082 | } |
| 27083 | |
| 27084 | THREADED_TEST(GlobalAccessorInfo) { |
| 27085 | v8::Isolate* isolate = CcTest::isolate(); |
| 27086 | v8::HandleScope scope(isolate); |
| 27087 | Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); |
| 27088 | global_template->SetAccessor( |
| 27089 | v8::String::NewFromUtf8(isolate, "prop" , v8::NewStringType::kInternalized) |
| 27090 | .ToLocalChecked(), |
| 27091 | &ensure_receiver_is_global_proxy); |
| 27092 | LocalContext env(nullptr, global_template); |
| 27093 | CompileRun("for (var i = 0; i < 10; i++) this.prop" ); |
| 27094 | CompileRun("for (var i = 0; i < 10; i++) prop" ); |
| 27095 | } |
| 27096 | |
| 27097 | TEST(DeterministicRandomNumberGeneration) { |
| 27098 | v8::HandleScope scope(CcTest::isolate()); |
| 27099 | |
| 27100 | int previous_seed = v8::internal::FLAG_random_seed; |
| 27101 | v8::internal::FLAG_random_seed = 1234; |
| 27102 | |
| 27103 | double first_value; |
| 27104 | double second_value; |
| 27105 | { |
| 27106 | v8::Local<Context> context = Context::New(CcTest::isolate()); |
| 27107 | Context::Scope context_scope(context); |
| 27108 | v8::Local<Value> result = CompileRun("Math.random();" ); |
| 27109 | first_value = result->ToNumber(context).ToLocalChecked()->Value(); |
| 27110 | } |
| 27111 | { |
| 27112 | v8::Local<Context> context = Context::New(CcTest::isolate()); |
| 27113 | Context::Scope context_scope(context); |
| 27114 | v8::Local<Value> result = CompileRun("Math.random();" ); |
| 27115 | second_value = result->ToNumber(context).ToLocalChecked()->Value(); |
| 27116 | } |
| 27117 | CHECK_EQ(first_value, second_value); |
| 27118 | |
| 27119 | v8::internal::FLAG_random_seed = previous_seed; |
| 27120 | } |
| 27121 | |
| 27122 | UNINITIALIZED_TEST(AllowAtomicsWait) { |
| 27123 | v8::Isolate::CreateParams create_params; |
| 27124 | create_params.allow_atomics_wait = false; |
| 27125 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 27126 | v8::Isolate* isolate = v8::Isolate::New(create_params); |
| 27127 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 27128 | { |
| 27129 | CHECK_EQ(false, i_isolate->allow_atomics_wait()); |
| 27130 | isolate->SetAllowAtomicsWait(true); |
| 27131 | CHECK_EQ(true, i_isolate->allow_atomics_wait()); |
| 27132 | } |
| 27133 | isolate->Dispose(); |
| 27134 | } |
| 27135 | |
| 27136 | enum ContextId { EnteredContext, CurrentContext }; |
| 27137 | |
| 27138 | void CheckContexts(v8::Isolate* isolate) { |
| 27139 | CHECK_EQ(CurrentContext, isolate->GetCurrentContext() |
| 27140 | ->GetEmbedderData(1) |
| 27141 | .As<v8::Integer>() |
| 27142 | ->Value()); |
| 27143 | CHECK_EQ(EnteredContext, isolate->GetEnteredOrMicrotaskContext() |
| 27144 | ->GetEmbedderData(1) |
| 27145 | .As<v8::Integer>() |
| 27146 | ->Value()); |
| 27147 | } |
| 27148 | |
| 27149 | void ContextCheckGetter(Local<String> name, |
| 27150 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 27151 | CheckContexts(info.GetIsolate()); |
| 27152 | info.GetReturnValue().Set(true); |
| 27153 | } |
| 27154 | |
| 27155 | void ContextCheckSetter(Local<String> name, Local<Value>, |
| 27156 | const v8::PropertyCallbackInfo<void>& info) { |
| 27157 | CheckContexts(info.GetIsolate()); |
| 27158 | } |
| 27159 | |
| 27160 | void ContextCheckToString(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 27161 | CheckContexts(info.GetIsolate()); |
| 27162 | info.GetReturnValue().Set(v8_str("foo" )); |
| 27163 | } |
| 27164 | |
| 27165 | TEST(CorrectEnteredContext) { |
| 27166 | v8::HandleScope scope(CcTest::isolate()); |
| 27167 | |
| 27168 | LocalContext currentContext; |
| 27169 | currentContext->SetEmbedderData( |
| 27170 | 1, v8::Integer::New(currentContext->GetIsolate(), CurrentContext)); |
| 27171 | LocalContext enteredContext; |
| 27172 | enteredContext->SetEmbedderData( |
| 27173 | 1, v8::Integer::New(enteredContext->GetIsolate(), EnteredContext)); |
| 27174 | |
| 27175 | v8::Context::Scope contextScope(enteredContext.local()); |
| 27176 | |
| 27177 | v8::Local<v8::ObjectTemplate> object_template = |
| 27178 | ObjectTemplate::New(currentContext->GetIsolate()); |
| 27179 | object_template->SetAccessor(v8_str("p" ), &ContextCheckGetter, |
| 27180 | &ContextCheckSetter); |
| 27181 | |
| 27182 | v8::Local<v8::Object> object = |
| 27183 | object_template->NewInstance(currentContext.local()).ToLocalChecked(); |
| 27184 | |
| 27185 | object->Get(currentContext.local(), v8_str("p" )).ToLocalChecked(); |
| 27186 | object->Set(currentContext.local(), v8_str("p" ), v8_int(0)).FromJust(); |
| 27187 | |
| 27188 | v8::Local<v8::Function> to_string = |
| 27189 | v8::Function::New(currentContext.local(), ContextCheckToString) |
| 27190 | .ToLocalChecked(); |
| 27191 | |
| 27192 | to_string->Call(currentContext.local(), object, 0, nullptr).ToLocalChecked(); |
| 27193 | |
| 27194 | object |
| 27195 | ->CreateDataProperty(currentContext.local(), v8_str("toString" ), |
| 27196 | to_string) |
| 27197 | .FromJust(); |
| 27198 | |
| 27199 | object->ToString(currentContext.local()).ToLocalChecked(); |
| 27200 | } |
| 27201 | |
| 27202 | v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackResolve( |
| 27203 | Local<Context> context, Local<v8::ScriptOrModule> referrer, |
| 27204 | Local<String> specifier) { |
| 27205 | CHECK(!referrer.IsEmpty()); |
| 27206 | String::Utf8Value referrer_utf8( |
| 27207 | context->GetIsolate(), Local<String>::Cast(referrer->GetResourceName())); |
| 27208 | CHECK_EQ(0, strcmp("www.google.com" , *referrer_utf8)); |
| 27209 | CHECK(referrer->GetHostDefinedOptions() |
| 27210 | ->Get(context->GetIsolate(), 0) |
| 27211 | ->IsSymbol()); |
| 27212 | |
| 27213 | CHECK(!specifier.IsEmpty()); |
| 27214 | String::Utf8Value specifier_utf8(context->GetIsolate(), specifier); |
| 27215 | CHECK_EQ(0, strcmp("index.js" , *specifier_utf8)); |
| 27216 | |
| 27217 | Local<v8::Promise::Resolver> resolver = |
| 27218 | v8::Promise::Resolver::New(context).ToLocalChecked(); |
| 27219 | auto result = v8_str("hello world" ); |
| 27220 | resolver->Resolve(context, result).ToChecked(); |
| 27221 | return resolver->GetPromise(); |
| 27222 | } |
| 27223 | |
| 27224 | TEST(DynamicImport) { |
| 27225 | i::FLAG_harmony_dynamic_import = true; |
| 27226 | LocalContext context; |
| 27227 | v8::Isolate* isolate = context->GetIsolate(); |
| 27228 | v8::HandleScope scope(isolate); |
| 27229 | |
| 27230 | isolate->SetHostImportModuleDynamicallyCallback( |
| 27231 | HostImportModuleDynamicallyCallbackResolve); |
| 27232 | |
| 27233 | i::Handle<i::String> url(v8::Utils::OpenHandle(*v8_str("www.google.com" ))); |
| 27234 | i::Handle<i::Object> specifier(v8::Utils::OpenHandle(*v8_str("index.js" ))); |
| 27235 | i::Handle<i::String> result(v8::Utils::OpenHandle(*v8_str("hello world" ))); |
| 27236 | i::Handle<i::String> source(v8::Utils::OpenHandle(*v8_str("foo" ))); |
| 27237 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 27238 | i::Handle<i::FixedArray> options = i_isolate->factory()->NewFixedArray(1); |
| 27239 | i::Handle<i::Symbol> symbol = i_isolate->factory()->NewSymbol(); |
| 27240 | options->set(0, *symbol); |
| 27241 | i::Handle<i::Script> referrer = i_isolate->factory()->NewScript(source); |
| 27242 | referrer->set_name(*url); |
| 27243 | referrer->set_host_defined_options(*options); |
| 27244 | i::MaybeHandle<i::JSPromise> maybe_promise = |
| 27245 | i_isolate->RunHostImportModuleDynamicallyCallback(referrer, specifier); |
| 27246 | i::Handle<i::JSPromise> promise = maybe_promise.ToHandleChecked(); |
| 27247 | isolate->RunMicrotasks(); |
| 27248 | CHECK(result->Equals(i::String::cast(promise->result()))); |
| 27249 | } |
| 27250 | |
| 27251 | void HostInitializeImportMetaObjectCallbackStatic(Local<Context> context, |
| 27252 | Local<Module> module, |
| 27253 | Local<Object> meta) { |
| 27254 | CHECK(!module.IsEmpty()); |
| 27255 | |
| 27256 | meta->CreateDataProperty(context, v8_str("foo" ), v8_str("bar" )).ToChecked(); |
| 27257 | } |
| 27258 | |
| 27259 | TEST(ImportMeta) { |
| 27260 | i::FLAG_harmony_dynamic_import = true; |
| 27261 | i::FLAG_harmony_import_meta = true; |
| 27262 | LocalContext context; |
| 27263 | v8::Isolate* isolate = context->GetIsolate(); |
| 27264 | v8::HandleScope scope(isolate); |
| 27265 | |
| 27266 | isolate->SetHostInitializeImportMetaObjectCallback( |
| 27267 | HostInitializeImportMetaObjectCallbackStatic); |
| 27268 | |
| 27269 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 27270 | Local<String> url = v8_str("www.google.com" ); |
| 27271 | Local<String> source_text = v8_str("import.meta;" ); |
| 27272 | v8::ScriptOrigin origin(url, Local<v8::Integer>(), Local<v8::Integer>(), |
| 27273 | Local<v8::Boolean>(), Local<v8::Integer>(), |
| 27274 | Local<v8::Value>(), Local<v8::Boolean>(), |
| 27275 | Local<v8::Boolean>(), True(isolate)); |
| 27276 | v8::ScriptCompiler::Source source(source_text, origin); |
| 27277 | Local<Module> module = |
| 27278 | v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| 27279 | i::Handle<i::Object> meta = |
| 27280 | i_isolate->RunHostInitializeImportMetaObjectCallback( |
| 27281 | v8::Utils::OpenHandle(*module)); |
| 27282 | CHECK(meta->IsJSObject()); |
| 27283 | Local<Object> meta_obj = Local<Object>::Cast(v8::Utils::ToLocal(meta)); |
| 27284 | CHECK(meta_obj->Get(context.local(), v8_str("foo" )) |
| 27285 | .ToLocalChecked() |
| 27286 | ->IsString()); |
| 27287 | CHECK(meta_obj->Get(context.local(), v8_str("zapp" )) |
| 27288 | .ToLocalChecked() |
| 27289 | ->IsUndefined()); |
| 27290 | |
| 27291 | module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback) |
| 27292 | .ToChecked(); |
| 27293 | Local<Value> result = module->Evaluate(context.local()).ToLocalChecked(); |
| 27294 | CHECK(result->StrictEquals(Local<v8::Value>::Cast(v8::Utils::ToLocal(meta)))); |
| 27295 | } |
| 27296 | |
| 27297 | TEST(GetModuleNamespace) { |
| 27298 | LocalContext context; |
| 27299 | v8::Isolate* isolate = context->GetIsolate(); |
| 27300 | v8::HandleScope scope(isolate); |
| 27301 | |
| 27302 | Local<String> url = v8_str("www.google.com" ); |
| 27303 | Local<String> source_text = v8_str("export default 5; export const a = 10;" ); |
| 27304 | v8::ScriptOrigin origin(url, Local<v8::Integer>(), Local<v8::Integer>(), |
| 27305 | Local<v8::Boolean>(), Local<v8::Integer>(), |
| 27306 | Local<v8::Value>(), Local<v8::Boolean>(), |
| 27307 | Local<v8::Boolean>(), True(isolate)); |
| 27308 | v8::ScriptCompiler::Source source(source_text, origin); |
| 27309 | Local<Module> module = |
| 27310 | v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| 27311 | module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback) |
| 27312 | .ToChecked(); |
| 27313 | module->Evaluate(context.local()).ToLocalChecked(); |
| 27314 | |
| 27315 | Local<Value> ns_val = module->GetModuleNamespace(); |
| 27316 | CHECK(ns_val->IsModuleNamespaceObject()); |
| 27317 | Local<Object> ns = ns_val.As<Object>(); |
| 27318 | CHECK(ns->Get(context.local(), v8_str("default" )) |
| 27319 | .ToLocalChecked() |
| 27320 | ->StrictEquals(v8::Number::New(isolate, 5))); |
| 27321 | CHECK(ns->Get(context.local(), v8_str("a" )) |
| 27322 | .ToLocalChecked() |
| 27323 | ->StrictEquals(v8::Number::New(isolate, 10))); |
| 27324 | } |
| 27325 | |
| 27326 | TEST(ModuleGetUnboundModuleScript) { |
| 27327 | LocalContext context; |
| 27328 | v8::Isolate* isolate = context->GetIsolate(); |
| 27329 | v8::HandleScope scope(isolate); |
| 27330 | |
| 27331 | Local<String> url = v8_str("www.google.com" ); |
| 27332 | Local<String> source_text = v8_str("export default 5; export const a = 10;" ); |
| 27333 | v8::ScriptOrigin origin(url, Local<v8::Integer>(), Local<v8::Integer>(), |
| 27334 | Local<v8::Boolean>(), Local<v8::Integer>(), |
| 27335 | Local<v8::Value>(), Local<v8::Boolean>(), |
| 27336 | Local<v8::Boolean>(), True(isolate)); |
| 27337 | v8::ScriptCompiler::Source source(source_text, origin); |
| 27338 | Local<Module> module = |
| 27339 | v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| 27340 | Local<v8::UnboundModuleScript> sfi_before_instantiation = |
| 27341 | module->GetUnboundModuleScript(); |
| 27342 | module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback) |
| 27343 | .ToChecked(); |
| 27344 | Local<v8::UnboundModuleScript> sfi_after_instantiation = |
| 27345 | module->GetUnboundModuleScript(); |
| 27346 | |
| 27347 | // Check object identity. |
| 27348 | { |
| 27349 | i::Handle<i::Object> s1 = v8::Utils::OpenHandle(*sfi_before_instantiation); |
| 27350 | i::Handle<i::Object> s2 = v8::Utils::OpenHandle(*sfi_after_instantiation); |
| 27351 | CHECK_EQ(*s1, *s2); |
| 27352 | } |
| 27353 | } |
| 27354 | |
| 27355 | TEST(GlobalTemplateWithDoubleProperty) { |
| 27356 | v8::Isolate* isolate = CcTest::isolate(); |
| 27357 | v8::HandleScope handle_scope(isolate); |
| 27358 | |
| 27359 | v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); |
| 27360 | global->Set(v8_str("double" ), v8_num(3.14)); |
| 27361 | |
| 27362 | v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global); |
| 27363 | |
| 27364 | v8::Context::Scope context_scope(context); |
| 27365 | |
| 27366 | Local<Value> result = CompileRun("double" ); |
| 27367 | CHECK(result->IsNumber()); |
| 27368 | CheckDoubleEquals(3.14, result->NumberValue(context).ToChecked()); |
| 27369 | } |
| 27370 | |
| 27371 | TEST(PrimitiveArray) { |
| 27372 | v8::Isolate* isolate = CcTest::isolate(); |
| 27373 | v8::HandleScope scope(isolate); |
| 27374 | LocalContext env; |
| 27375 | |
| 27376 | int length = 5; |
| 27377 | Local<v8::PrimitiveArray> array(v8::PrimitiveArray::New(isolate, 5)); |
| 27378 | CHECK_EQ(length, array->Length()); |
| 27379 | |
| 27380 | for (int i = 0; i < length; i++) { |
| 27381 | Local<v8::Primitive> item = array->Get(isolate, i); |
| 27382 | CHECK(item->IsUndefined()); |
| 27383 | } |
| 27384 | |
| 27385 | Local<v8::Symbol> symbol(v8::Symbol::New(isolate)); |
| 27386 | array->Set(isolate, 0, symbol); |
| 27387 | CHECK(array->Get(isolate, 0)->IsSymbol()); |
| 27388 | |
| 27389 | Local<v8::String> string = |
| 27390 | v8::String::NewFromUtf8(isolate, "test" , v8::NewStringType::kInternalized) |
| 27391 | .ToLocalChecked(); |
| 27392 | array->Set(isolate, 1, string); |
| 27393 | CHECK(array->Get(isolate, 0)->IsSymbol()); |
| 27394 | CHECK(array->Get(isolate, 1)->IsString()); |
| 27395 | |
| 27396 | Local<v8::Number> num = v8::Number::New(env->GetIsolate(), 3.1415926); |
| 27397 | array->Set(isolate, 2, num); |
| 27398 | CHECK(array->Get(isolate, 0)->IsSymbol()); |
| 27399 | CHECK(array->Get(isolate, 1)->IsString()); |
| 27400 | CHECK(array->Get(isolate, 2)->IsNumber()); |
| 27401 | |
| 27402 | v8::Local<v8::Boolean> f = v8::False(isolate); |
| 27403 | array->Set(isolate, 3, f); |
| 27404 | CHECK(array->Get(isolate, 0)->IsSymbol()); |
| 27405 | CHECK(array->Get(isolate, 1)->IsString()); |
| 27406 | CHECK(array->Get(isolate, 2)->IsNumber()); |
| 27407 | CHECK(array->Get(isolate, 3)->IsBoolean()); |
| 27408 | |
| 27409 | v8::Local<v8::Primitive> n = v8::Null(isolate); |
| 27410 | array->Set(isolate, 4, n); |
| 27411 | CHECK(array->Get(isolate, 0)->IsSymbol()); |
| 27412 | CHECK(array->Get(isolate, 1)->IsString()); |
| 27413 | CHECK(array->Get(isolate, 2)->IsNumber()); |
| 27414 | CHECK(array->Get(isolate, 3)->IsBoolean()); |
| 27415 | CHECK(array->Get(isolate, 4)->IsNull()); |
| 27416 | } |
| 27417 | |
| 27418 | TEST(PersistentValueMap) { |
| 27419 | v8::Isolate* isolate = CcTest::isolate(); |
| 27420 | v8::HandleScope scope(isolate); |
| 27421 | LocalContext env; |
| 27422 | |
| 27423 | v8::PersistentValueMap< |
| 27424 | std::string, v8::Value, |
| 27425 | v8::DefaultPersistentValueMapTraits<std::string, v8::Value>> |
| 27426 | map(isolate); |
| 27427 | v8::Local<v8::Value> value = |
| 27428 | v8::String::NewFromUtf8(isolate, "value" , |
| 27429 | v8::NewStringType::kInternalized) |
| 27430 | .ToLocalChecked(); |
| 27431 | map.Set("key" , value); |
| 27432 | } |
| 27433 | |
| 27434 | namespace { |
| 27435 | |
| 27436 | bool wasm_streaming_callback_got_called = false; |
| 27437 | bool wasm_streaming_data_got_collected = false; |
| 27438 | |
| 27439 | void WasmStreamingTestFinalizer(const v8::WeakCallbackInfo<void>& data) { |
| 27440 | CHECK(!wasm_streaming_data_got_collected); |
| 27441 | wasm_streaming_data_got_collected = true; |
| 27442 | i::GlobalHandles::Destroy(reinterpret_cast<i::Address*>(data.GetParameter())); |
| 27443 | } |
| 27444 | |
| 27445 | void WasmStreamingCallbackTestCallbackIsCalled( |
| 27446 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 27447 | CHECK(!wasm_streaming_callback_got_called); |
| 27448 | wasm_streaming_callback_got_called = true; |
| 27449 | |
| 27450 | i::Handle<i::Object> global_handle = |
| 27451 | reinterpret_cast<i::Isolate*>(args.GetIsolate()) |
| 27452 | ->global_handles() |
| 27453 | ->Create(*v8::Utils::OpenHandle(*args.Data())); |
| 27454 | i::GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), |
| 27455 | WasmStreamingTestFinalizer, |
| 27456 | v8::WeakCallbackType::kParameter); |
| 27457 | } |
| 27458 | |
| 27459 | void WasmStreamingCallbackTestOnBytesReceived( |
| 27460 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 27461 | std::shared_ptr<v8::WasmStreaming> streaming = |
| 27462 | v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); |
| 27463 | |
| 27464 | // The first bytes of the WebAssembly magic word. |
| 27465 | const uint8_t bytes[]{0x00, 0x61, 0x73}; |
| 27466 | streaming->OnBytesReceived(bytes, arraysize(bytes)); |
| 27467 | } |
| 27468 | |
| 27469 | void WasmStreamingCallbackTestFinishWithSuccess( |
| 27470 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 27471 | std::shared_ptr<v8::WasmStreaming> streaming = |
| 27472 | v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); |
| 27473 | // The bytes of a minimal WebAssembly module. |
| 27474 | const uint8_t bytes[]{0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00}; |
| 27475 | streaming->OnBytesReceived(bytes, arraysize(bytes)); |
| 27476 | streaming->Finish(); |
| 27477 | } |
| 27478 | |
| 27479 | void WasmStreamingCallbackTestFinishWithFailure( |
| 27480 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 27481 | std::shared_ptr<v8::WasmStreaming> streaming = |
| 27482 | v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); |
| 27483 | streaming->Finish(); |
| 27484 | } |
| 27485 | |
| 27486 | void WasmStreamingCallbackTestAbortWithReject( |
| 27487 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 27488 | std::shared_ptr<v8::WasmStreaming> streaming = |
| 27489 | v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); |
| 27490 | streaming->Abort(v8::Object::New(args.GetIsolate())); |
| 27491 | } |
| 27492 | |
| 27493 | void WasmStreamingCallbackTestAbortNoReject( |
| 27494 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 27495 | std::shared_ptr<v8::WasmStreaming> streaming = |
| 27496 | v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data()); |
| 27497 | streaming->Abort({}); |
| 27498 | } |
| 27499 | |
| 27500 | void TestWasmStreaming(v8::WasmStreamingCallback callback, |
| 27501 | v8::Promise::PromiseState expected_state) { |
| 27502 | CcTest::isolate()->SetWasmStreamingCallback(callback); |
| 27503 | LocalContext env; |
| 27504 | v8::Isolate* isolate = env->GetIsolate(); |
| 27505 | v8::HandleScope scope(isolate); |
| 27506 | |
| 27507 | // Call {WebAssembly.compileStreaming} with {null} as parameter. The parameter |
| 27508 | // is only really processed by the embedder, so for this test the value is |
| 27509 | // irrelevant. |
| 27510 | v8::Local<v8::Promise> promise = v8::Local<v8::Promise>::Cast( |
| 27511 | CompileRun("WebAssembly.compileStreaming(null)" )); |
| 27512 | |
| 27513 | EmptyMessageQueues(isolate); |
| 27514 | CHECK_EQ(expected_state, promise->State()); |
| 27515 | } |
| 27516 | |
| 27517 | } // namespace |
| 27518 | |
| 27519 | TEST(WasmStreamingCallback) { |
| 27520 | TestWasmStreaming(WasmStreamingCallbackTestCallbackIsCalled, |
| 27521 | v8::Promise::kPending); |
| 27522 | CHECK(wasm_streaming_callback_got_called); |
| 27523 | CcTest::CollectAllAvailableGarbage(); |
| 27524 | CHECK(wasm_streaming_data_got_collected); |
| 27525 | } |
| 27526 | |
| 27527 | TEST(WasmStreamingOnBytesReceived) { |
| 27528 | TestWasmStreaming(WasmStreamingCallbackTestOnBytesReceived, |
| 27529 | v8::Promise::kPending); |
| 27530 | } |
| 27531 | |
| 27532 | TEST(WasmStreamingFinishWithSuccess) { |
| 27533 | TestWasmStreaming(WasmStreamingCallbackTestFinishWithSuccess, |
| 27534 | v8::Promise::kFulfilled); |
| 27535 | } |
| 27536 | |
| 27537 | TEST(WasmStreamingFinishWithFailure) { |
| 27538 | TestWasmStreaming(WasmStreamingCallbackTestFinishWithFailure, |
| 27539 | v8::Promise::kRejected); |
| 27540 | } |
| 27541 | |
| 27542 | TEST(WasmStreamingAbortWithReject) { |
| 27543 | TestWasmStreaming(WasmStreamingCallbackTestAbortWithReject, |
| 27544 | v8::Promise::kRejected); |
| 27545 | } |
| 27546 | |
| 27547 | TEST(WasmStreamingAbortWithoutReject) { |
| 27548 | TestWasmStreaming(WasmStreamingCallbackTestAbortNoReject, |
| 27549 | v8::Promise::kPending); |
| 27550 | } |
| 27551 | |
| 27552 | enum class AtomicsWaitCallbackAction { |
| 27553 | Interrupt, |
| 27554 | StopAndThrowInFirstCall, |
| 27555 | StopAndThrowInSecondCall, |
| 27556 | StopFromThreadAndThrow, |
| 27557 | KeepWaiting |
| 27558 | }; |
| 27559 | |
| 27560 | class StopAtomicsWaitThread; |
| 27561 | |
| 27562 | struct AtomicsWaitCallbackInfo { |
| 27563 | v8::Isolate* isolate; |
| 27564 | v8::Isolate::AtomicsWaitWakeHandle* wake_handle; |
| 27565 | std::unique_ptr<StopAtomicsWaitThread> stop_thread; |
| 27566 | AtomicsWaitCallbackAction action; |
| 27567 | |
| 27568 | Local<v8::SharedArrayBuffer> expected_sab; |
| 27569 | v8::Isolate::AtomicsWaitEvent expected_event; |
| 27570 | double expected_timeout; |
| 27571 | int64_t expected_value; |
| 27572 | size_t expected_offset; |
| 27573 | |
| 27574 | size_t ncalls = 0; |
| 27575 | }; |
| 27576 | |
| 27577 | class StopAtomicsWaitThread : public v8::base::Thread { |
| 27578 | public: |
| 27579 | explicit StopAtomicsWaitThread(AtomicsWaitCallbackInfo* info) |
| 27580 | : Thread(Options("StopAtomicsWaitThread" )), info_(info) {} |
| 27581 | |
| 27582 | void Run() override { |
| 27583 | CHECK_NOT_NULL(info_->wake_handle); |
| 27584 | info_->wake_handle->Wake(); |
| 27585 | } |
| 27586 | |
| 27587 | private: |
| 27588 | AtomicsWaitCallbackInfo* info_; |
| 27589 | }; |
| 27590 | |
| 27591 | void AtomicsWaitCallbackForTesting( |
| 27592 | v8::Isolate::AtomicsWaitEvent event, Local<v8::SharedArrayBuffer> sab, |
| 27593 | size_t offset_in_bytes, int64_t value, double timeout_in_ms, |
| 27594 | v8::Isolate::AtomicsWaitWakeHandle* wake_handle, void* data) { |
| 27595 | AtomicsWaitCallbackInfo* info = static_cast<AtomicsWaitCallbackInfo*>(data); |
| 27596 | info->ncalls++; |
| 27597 | info->wake_handle = wake_handle; |
| 27598 | CHECK(sab->StrictEquals(info->expected_sab)); |
| 27599 | CHECK_EQ(timeout_in_ms, info->expected_timeout); |
| 27600 | CHECK_EQ(value, info->expected_value); |
| 27601 | CHECK_EQ(offset_in_bytes, info->expected_offset); |
| 27602 | |
| 27603 | auto ThrowSomething = [&]() { |
| 27604 | info->isolate->ThrowException(v8::Integer::New(info->isolate, 42)); |
| 27605 | }; |
| 27606 | |
| 27607 | if (event == v8::Isolate::AtomicsWaitEvent::kStartWait) { |
| 27608 | CHECK_NOT_NULL(wake_handle); |
| 27609 | switch (info->action) { |
| 27610 | case AtomicsWaitCallbackAction::Interrupt: |
| 27611 | info->isolate->TerminateExecution(); |
| 27612 | break; |
| 27613 | case AtomicsWaitCallbackAction::StopAndThrowInFirstCall: |
| 27614 | ThrowSomething(); |
| 27615 | V8_FALLTHROUGH; |
| 27616 | case AtomicsWaitCallbackAction::StopAndThrowInSecondCall: |
| 27617 | wake_handle->Wake(); |
| 27618 | break; |
| 27619 | case AtomicsWaitCallbackAction::StopFromThreadAndThrow: |
| 27620 | info->stop_thread = v8::base::make_unique<StopAtomicsWaitThread>(info); |
| 27621 | info->stop_thread->Start(); |
| 27622 | break; |
| 27623 | case AtomicsWaitCallbackAction::KeepWaiting: |
| 27624 | break; |
| 27625 | } |
| 27626 | } else { |
| 27627 | CHECK_EQ(event, info->expected_event); |
| 27628 | CHECK_NULL(wake_handle); |
| 27629 | |
| 27630 | if (info->stop_thread) { |
| 27631 | info->stop_thread->Join(); |
| 27632 | info->stop_thread.reset(); |
| 27633 | } |
| 27634 | |
| 27635 | if (info->action == AtomicsWaitCallbackAction::StopAndThrowInSecondCall || |
| 27636 | info->action == AtomicsWaitCallbackAction::StopFromThreadAndThrow) { |
| 27637 | ThrowSomething(); |
| 27638 | } |
| 27639 | } |
| 27640 | } |
| 27641 | |
| 27642 | // Must be called from within HandleScope |
| 27643 | void AtomicsWaitCallbackCommon(v8::Isolate* isolate, Local<Value> sab, |
| 27644 | size_t initial_offset, |
| 27645 | size_t offset_multiplier) { |
| 27646 | CHECK(sab->IsSharedArrayBuffer()); |
| 27647 | |
| 27648 | AtomicsWaitCallbackInfo info; |
| 27649 | info.isolate = isolate; |
| 27650 | info.expected_sab = sab.As<v8::SharedArrayBuffer>(); |
| 27651 | isolate->SetAtomicsWaitCallback(AtomicsWaitCallbackForTesting, &info); |
| 27652 | |
| 27653 | { |
| 27654 | v8::TryCatch try_catch(isolate); |
| 27655 | info.expected_offset = initial_offset; |
| 27656 | info.expected_timeout = std::numeric_limits<double>::infinity(); |
| 27657 | info.expected_value = 0; |
| 27658 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kTerminatedExecution; |
| 27659 | info.action = AtomicsWaitCallbackAction::Interrupt; |
| 27660 | info.ncalls = 0; |
| 27661 | CompileRun("wait(0, 0);" ); |
| 27662 | CHECK_EQ(info.ncalls, 2); |
| 27663 | CHECK(try_catch.HasTerminated()); |
| 27664 | } |
| 27665 | |
| 27666 | { |
| 27667 | v8::TryCatch try_catch(isolate); |
| 27668 | info.expected_offset = initial_offset + offset_multiplier; |
| 27669 | info.expected_timeout = std::numeric_limits<double>::infinity(); |
| 27670 | info.expected_value = 1; |
| 27671 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kNotEqual; |
| 27672 | info.action = AtomicsWaitCallbackAction::KeepWaiting; |
| 27673 | info.ncalls = 0; |
| 27674 | CompileRun("wait(1, 1);" ); // real value is 0 != 1 |
| 27675 | CHECK_EQ(info.ncalls, 2); |
| 27676 | CHECK(!try_catch.HasCaught()); |
| 27677 | } |
| 27678 | |
| 27679 | { |
| 27680 | v8::TryCatch try_catch(isolate); |
| 27681 | info.expected_offset = initial_offset + offset_multiplier; |
| 27682 | info.expected_timeout = 0.125; |
| 27683 | info.expected_value = 0; |
| 27684 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kTimedOut; |
| 27685 | info.action = AtomicsWaitCallbackAction::KeepWaiting; |
| 27686 | info.ncalls = 0; |
| 27687 | CompileRun("wait(1, 0, 0.125);" ); // timeout |
| 27688 | CHECK_EQ(info.ncalls, 2); |
| 27689 | CHECK(!try_catch.HasCaught()); |
| 27690 | } |
| 27691 | |
| 27692 | { |
| 27693 | v8::TryCatch try_catch(isolate); |
| 27694 | info.expected_offset = initial_offset + offset_multiplier; |
| 27695 | info.expected_timeout = std::numeric_limits<double>::infinity(); |
| 27696 | info.expected_value = 0; |
| 27697 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kAPIStopped; |
| 27698 | info.action = AtomicsWaitCallbackAction::StopAndThrowInFirstCall; |
| 27699 | info.ncalls = 0; |
| 27700 | CompileRun("wait(1, 0);" ); |
| 27701 | CHECK_EQ(info.ncalls, 1); // Only one extra call |
| 27702 | CHECK(try_catch.HasCaught()); |
| 27703 | CHECK(try_catch.Exception()->IsInt32()); |
| 27704 | CHECK_EQ(try_catch.Exception().As<v8::Int32>()->Value(), 42); |
| 27705 | } |
| 27706 | |
| 27707 | { |
| 27708 | v8::TryCatch try_catch(isolate); |
| 27709 | info.expected_offset = initial_offset + offset_multiplier; |
| 27710 | info.expected_timeout = std::numeric_limits<double>::infinity(); |
| 27711 | info.expected_value = 0; |
| 27712 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kAPIStopped; |
| 27713 | info.action = AtomicsWaitCallbackAction::StopAndThrowInSecondCall; |
| 27714 | info.ncalls = 0; |
| 27715 | CompileRun("wait(1, 0);" ); |
| 27716 | CHECK_EQ(info.ncalls, 2); |
| 27717 | CHECK(try_catch.HasCaught()); |
| 27718 | CHECK(try_catch.Exception()->IsInt32()); |
| 27719 | CHECK_EQ(try_catch.Exception().As<v8::Int32>()->Value(), 42); |
| 27720 | } |
| 27721 | |
| 27722 | { |
| 27723 | // Same test as before, but with a different `expected_value`. |
| 27724 | v8::TryCatch try_catch(isolate); |
| 27725 | info.expected_offset = initial_offset + offset_multiplier; |
| 27726 | info.expected_timeout = std::numeric_limits<double>::infinity(); |
| 27727 | info.expected_value = 200; |
| 27728 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kAPIStopped; |
| 27729 | info.action = AtomicsWaitCallbackAction::StopAndThrowInSecondCall; |
| 27730 | info.ncalls = 0; |
| 27731 | CompileRun( |
| 27732 | "setArrayElemAs(1, 200);" |
| 27733 | "wait(1, 200);" ); |
| 27734 | CHECK_EQ(info.ncalls, 2); |
| 27735 | CHECK(try_catch.HasCaught()); |
| 27736 | CHECK(try_catch.Exception()->IsInt32()); |
| 27737 | CHECK_EQ(try_catch.Exception().As<v8::Int32>()->Value(), 42); |
| 27738 | } |
| 27739 | |
| 27740 | { |
| 27741 | // Wake the `Atomics.wait()` call from a thread. |
| 27742 | v8::TryCatch try_catch(isolate); |
| 27743 | info.expected_offset = initial_offset; |
| 27744 | info.expected_timeout = std::numeric_limits<double>::infinity(); |
| 27745 | info.expected_value = 0; |
| 27746 | info.expected_event = v8::Isolate::AtomicsWaitEvent::kAPIStopped; |
| 27747 | info.action = AtomicsWaitCallbackAction::StopFromThreadAndThrow; |
| 27748 | info.ncalls = 0; |
| 27749 | CompileRun( |
| 27750 | "setArrayElemAs(1, 0);" |
| 27751 | "wait(0, 0);" ); |
| 27752 | CHECK_EQ(info.ncalls, 2); |
| 27753 | CHECK(try_catch.HasCaught()); |
| 27754 | CHECK(try_catch.Exception()->IsInt32()); |
| 27755 | CHECK_EQ(try_catch.Exception().As<v8::Int32>()->Value(), 42); |
| 27756 | } |
| 27757 | } |
| 27758 | |
| 27759 | TEST(AtomicsWaitCallback) { |
| 27760 | LocalContext env; |
| 27761 | v8::Isolate* isolate = env->GetIsolate(); |
| 27762 | v8::HandleScope scope(isolate); |
| 27763 | const char* init = R"( |
| 27764 | let sab = new SharedArrayBuffer(16); |
| 27765 | let int32arr = new Int32Array(sab, 4); |
| 27766 | let setArrayElemAs = function(id, val) { |
| 27767 | int32arr[id] = val; |
| 27768 | }; |
| 27769 | let wait = function(id, val, timeout) { |
| 27770 | if(arguments.length == 2) return Atomics.wait(int32arr, id, val); |
| 27771 | return Atomics.wait(int32arr, id, val, timeout); |
| 27772 | }; |
| 27773 | sab;)" ; |
| 27774 | AtomicsWaitCallbackCommon(isolate, CompileRun(init), 4, 4); |
| 27775 | } |
| 27776 | |
| 27777 | namespace v8 { |
| 27778 | namespace internal { |
| 27779 | namespace wasm { |
| 27780 | TEST(WasmI32AtomicWaitCallback) { |
| 27781 | FlagScope<bool> wasm_threads_flag(&i::FLAG_experimental_wasm_threads, true); |
| 27782 | WasmRunner<int32_t, int32_t, int32_t, double> r(ExecutionTier::kTurbofan); |
| 27783 | r.builder().AddMemory(kWasmPageSize, SharedFlag::kShared); |
| 27784 | r.builder().SetHasSharedMemory(); |
| 27785 | BUILD(r, WASM_ATOMICS_WAIT(kExprI32AtomicWait, WASM_GET_LOCAL(0), |
| 27786 | WASM_GET_LOCAL(1), |
| 27787 | WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(2)), 4)); |
| 27788 | LocalContext env; |
| 27789 | v8::Isolate* isolate = env->GetIsolate(); |
| 27790 | v8::HandleScope scope(isolate); |
| 27791 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 27792 | Handle<JSFunction> func = r.builder().WrapCode(0); |
| 27793 | CHECK(env->Global() |
| 27794 | ->Set(env.local(), v8_str("func" ), v8::Utils::ToLocal(func)) |
| 27795 | .FromJust()); |
| 27796 | Handle<JSArrayBuffer> memory( |
| 27797 | r.builder().instance_object()->memory_object()->array_buffer(), |
| 27798 | i_isolate); |
| 27799 | CHECK(env->Global() |
| 27800 | ->Set(env.local(), v8_str("sab" ), v8::Utils::ToLocal(memory)) |
| 27801 | .FromJust()); |
| 27802 | |
| 27803 | const char* init = R"( |
| 27804 | let int32arr = new Int32Array(sab, 4); |
| 27805 | let setArrayElemAs = function(id, val) { |
| 27806 | int32arr[id] = val; |
| 27807 | }; |
| 27808 | let wait = function(id, val, timeout) { |
| 27809 | if(arguments.length === 2) |
| 27810 | return func(id << 2, val, -1); |
| 27811 | return func(id << 2, val, timeout*1000000); |
| 27812 | }; |
| 27813 | sab;)" ; |
| 27814 | AtomicsWaitCallbackCommon(isolate, CompileRun(init), 4, 4); |
| 27815 | } |
| 27816 | |
| 27817 | TEST(WasmI64AtomicWaitCallback) { |
| 27818 | FlagScope<bool> wasm_threads_flag(&i::FLAG_experimental_wasm_threads, true); |
| 27819 | WasmRunner<int32_t, int32_t, double, double> r(ExecutionTier::kTurbofan); |
| 27820 | r.builder().AddMemory(kWasmPageSize, SharedFlag::kShared); |
| 27821 | r.builder().SetHasSharedMemory(); |
| 27822 | BUILD(r, WASM_ATOMICS_WAIT(kExprI64AtomicWait, WASM_GET_LOCAL(0), |
| 27823 | WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(1)), |
| 27824 | WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(2)), 8)); |
| 27825 | LocalContext env; |
| 27826 | v8::Isolate* isolate = env->GetIsolate(); |
| 27827 | v8::HandleScope scope(isolate); |
| 27828 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 27829 | Handle<JSFunction> func = r.builder().WrapCode(0); |
| 27830 | CHECK(env->Global() |
| 27831 | ->Set(env.local(), v8_str("func" ), v8::Utils::ToLocal(func)) |
| 27832 | .FromJust()); |
| 27833 | Handle<JSArrayBuffer> memory( |
| 27834 | r.builder().instance_object()->memory_object()->array_buffer(), |
| 27835 | i_isolate); |
| 27836 | CHECK(env->Global() |
| 27837 | ->Set(env.local(), v8_str("sab" ), v8::Utils::ToLocal(memory)) |
| 27838 | .FromJust()); |
| 27839 | |
| 27840 | const char* init = R"( |
| 27841 | let int64arr = new BigInt64Array(sab, 8); |
| 27842 | let setArrayElemAs = function(id, val) { |
| 27843 | int64arr[id] = BigInt(val); |
| 27844 | }; |
| 27845 | let wait = function(id, val, timeout) { |
| 27846 | if(arguments.length === 2) |
| 27847 | return func(id << 3, val, -1); |
| 27848 | return func(id << 3, val, timeout*1000000); |
| 27849 | }; |
| 27850 | sab;)" ; |
| 27851 | AtomicsWaitCallbackCommon(isolate, CompileRun(init), 8, 8); |
| 27852 | } |
| 27853 | |
| 27854 | } // namespace wasm |
| 27855 | } // namespace internal |
| 27856 | } // namespace v8 |
| 27857 | |
| 27858 | // TODO(mstarzinger): Move this into a test-api-wasm.cc file when this large |
| 27859 | // test file is being split up into chunks. |
| 27860 | TEST(WasmModuleObjectCompileFailure) { |
| 27861 | LocalContext env; |
| 27862 | v8::Isolate* isolate = env->GetIsolate(); |
| 27863 | v8::HandleScope scope(isolate); |
| 27864 | |
| 27865 | { |
| 27866 | v8::TryCatch try_catch(isolate); |
| 27867 | uint8_t buffer[] = {0xDE, 0xAD, 0xBE, 0xEF}; |
| 27868 | v8::MemorySpan<const uint8_t> serialized_module; |
| 27869 | v8::MemorySpan<const uint8_t> wire_bytes = {buffer, arraysize(buffer)}; |
| 27870 | v8::MaybeLocal<v8::WasmModuleObject> maybe_module = |
| 27871 | v8::WasmModuleObject::DeserializeOrCompile(isolate, serialized_module, |
| 27872 | wire_bytes); |
| 27873 | CHECK(maybe_module.IsEmpty()); |
| 27874 | CHECK(try_catch.HasCaught()); |
| 27875 | } |
| 27876 | } |
| 27877 | |
| 27878 | TEST(BigIntAPI) { |
| 27879 | LocalContext env; |
| 27880 | v8::Isolate* isolate = env->GetIsolate(); |
| 27881 | v8::HandleScope scope(isolate); |
| 27882 | bool lossless; |
| 27883 | uint64_t words1[10]; |
| 27884 | uint64_t words2[10]; |
| 27885 | |
| 27886 | { |
| 27887 | Local<Value> bi = CompileRun("12n" ); |
| 27888 | CHECK(bi->IsBigInt()); |
| 27889 | |
| 27890 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), 12); |
| 27891 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless), 12); |
| 27892 | CHECK_EQ(lossless, true); |
| 27893 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), 12); |
| 27894 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), 12); |
| 27895 | CHECK_EQ(lossless, true); |
| 27896 | } |
| 27897 | |
| 27898 | { |
| 27899 | Local<Value> bi = CompileRun("-12n" ); |
| 27900 | CHECK(bi->IsBigInt()); |
| 27901 | |
| 27902 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), static_cast<uint64_t>(-12)); |
| 27903 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless), |
| 27904 | static_cast<uint64_t>(-12)); |
| 27905 | CHECK_EQ(lossless, false); |
| 27906 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), -12); |
| 27907 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), -12); |
| 27908 | CHECK_EQ(lossless, true); |
| 27909 | } |
| 27910 | |
| 27911 | { |
| 27912 | Local<Value> bi = CompileRun("123456789012345678901234567890n" ); |
| 27913 | CHECK(bi->IsBigInt()); |
| 27914 | |
| 27915 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), 14083847773837265618ULL); |
| 27916 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless), |
| 27917 | 14083847773837265618ULL); |
| 27918 | CHECK_EQ(lossless, false); |
| 27919 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), -4362896299872285998LL); |
| 27920 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), |
| 27921 | -4362896299872285998LL); |
| 27922 | CHECK_EQ(lossless, false); |
| 27923 | } |
| 27924 | |
| 27925 | { |
| 27926 | Local<Value> bi = CompileRun("-123456789012345678901234567890n" ); |
| 27927 | CHECK(bi->IsBigInt()); |
| 27928 | |
| 27929 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(), 4362896299872285998LL); |
| 27930 | CHECK_EQ(bi.As<v8::BigInt>()->Uint64Value(&lossless), |
| 27931 | 4362896299872285998LL); |
| 27932 | CHECK_EQ(lossless, false); |
| 27933 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(), 4362896299872285998LL); |
| 27934 | CHECK_EQ(bi.As<v8::BigInt>()->Int64Value(&lossless), 4362896299872285998LL); |
| 27935 | CHECK_EQ(lossless, false); |
| 27936 | } |
| 27937 | |
| 27938 | { |
| 27939 | Local<v8::BigInt> bi = |
| 27940 | v8::BigInt::NewFromWords(env.local(), 0, 0, words1).ToLocalChecked(); |
| 27941 | CHECK_EQ(bi->Uint64Value(), 0); |
| 27942 | CHECK_EQ(bi->WordCount(), 0); |
| 27943 | } |
| 27944 | |
| 27945 | { |
| 27946 | TryCatch try_catch(isolate); |
| 27947 | v8::MaybeLocal<v8::BigInt> bi = v8::BigInt::NewFromWords( |
| 27948 | env.local(), 0, std::numeric_limits<int>::max(), words1); |
| 27949 | CHECK(bi.IsEmpty()); |
| 27950 | CHECK(try_catch.HasCaught()); |
| 27951 | } |
| 27952 | |
| 27953 | { |
| 27954 | TryCatch try_catch(isolate); |
| 27955 | v8::MaybeLocal<v8::BigInt> bi = |
| 27956 | v8::BigInt::NewFromWords(env.local(), 0, -1, words1); |
| 27957 | CHECK(bi.IsEmpty()); |
| 27958 | CHECK(try_catch.HasCaught()); |
| 27959 | } |
| 27960 | |
| 27961 | { |
| 27962 | TryCatch try_catch(isolate); |
| 27963 | v8::MaybeLocal<v8::BigInt> bi = |
| 27964 | v8::BigInt::NewFromWords(env.local(), 0, 1 << 30, words1); |
| 27965 | CHECK(bi.IsEmpty()); |
| 27966 | CHECK(try_catch.HasCaught()); |
| 27967 | } |
| 27968 | |
| 27969 | for (int sign_bit = 0; sign_bit <= 1; sign_bit++) { |
| 27970 | words1[0] = 0xffffffff00000000ULL; |
| 27971 | words1[1] = 0x00000000ffffffffULL; |
| 27972 | v8::Local<v8::BigInt> bi = |
| 27973 | v8::BigInt::NewFromWords(env.local(), sign_bit, 2, words1) |
| 27974 | .ToLocalChecked(); |
| 27975 | CHECK_EQ(bi->Uint64Value(&lossless), |
| 27976 | sign_bit ? static_cast<uint64_t>(-static_cast<int64_t>(words1[0])) |
| 27977 | : words1[0]); |
| 27978 | CHECK_EQ(lossless, false); |
| 27979 | CHECK_EQ(bi->Int64Value(&lossless), sign_bit |
| 27980 | ? -static_cast<int64_t>(words1[0]) |
| 27981 | : static_cast<int64_t>(words1[0])); |
| 27982 | CHECK_EQ(lossless, false); |
| 27983 | CHECK_EQ(bi->WordCount(), 2); |
| 27984 | int real_sign_bit; |
| 27985 | int word_count = arraysize(words2); |
| 27986 | bi->ToWordsArray(&real_sign_bit, &word_count, words2); |
| 27987 | CHECK_EQ(real_sign_bit, sign_bit); |
| 27988 | CHECK_EQ(word_count, 2); |
| 27989 | } |
| 27990 | } |
| 27991 | |
| 27992 | namespace { |
| 27993 | |
| 27994 | bool wasm_threads_enabled_value = false; |
| 27995 | |
| 27996 | bool MockWasmThreadsEnabledCallback(Local<Context>) { |
| 27997 | return wasm_threads_enabled_value; |
| 27998 | } |
| 27999 | |
| 28000 | } // namespace |
| 28001 | |
| 28002 | TEST(TestSetWasmThreadsEnabledCallback) { |
| 28003 | LocalContext env; |
| 28004 | v8::Isolate* isolate = env->GetIsolate(); |
| 28005 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 28006 | v8::HandleScope scope(isolate); |
| 28007 | v8::Local<Context> context = Context::New(CcTest::isolate()); |
| 28008 | i::Handle<i::Context> i_context = v8::Utils::OpenHandle(*context); |
| 28009 | |
| 28010 | // {Isolate::AreWasmThreadsEnabled} calls the callback set by the embedder if |
| 28011 | // such a callback exists. Otherwise it returns |
| 28012 | // {FLAG_experimental_wasm_threads}. First we test that the flag is returned |
| 28013 | // correctly if no callback is set. Then we test that the flag is ignored if |
| 28014 | // the callback is set. |
| 28015 | |
| 28016 | i::FLAG_experimental_wasm_threads = false; |
| 28017 | CHECK(!i_isolate->AreWasmThreadsEnabled(i_context)); |
| 28018 | |
| 28019 | i::FLAG_experimental_wasm_threads = true; |
| 28020 | CHECK(i_isolate->AreWasmThreadsEnabled(i_context)); |
| 28021 | |
| 28022 | isolate->SetWasmThreadsEnabledCallback(MockWasmThreadsEnabledCallback); |
| 28023 | wasm_threads_enabled_value = false; |
| 28024 | CHECK(!i_isolate->AreWasmThreadsEnabled(i_context)); |
| 28025 | |
| 28026 | wasm_threads_enabled_value = true; |
| 28027 | i::FLAG_experimental_wasm_threads = false; |
| 28028 | CHECK(i_isolate->AreWasmThreadsEnabled(i_context)); |
| 28029 | } |
| 28030 | |
| 28031 | TEST(TestGetUnwindState) { |
| 28032 | LocalContext env; |
| 28033 | v8::Isolate* isolate = env->GetIsolate(); |
| 28034 | i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
| 28035 | |
| 28036 | v8::UnwindState unwind_state = isolate->GetUnwindState(); |
| 28037 | v8::MemoryRange builtins_range = unwind_state.embedded_code_range; |
| 28038 | |
| 28039 | // Check that each off-heap builtin is within the builtins code range. |
| 28040 | if (i::FLAG_embedded_builtins) { |
| 28041 | for (int id = 0; id < i::Builtins::builtin_count; id++) { |
| 28042 | if (!i::Builtins::IsIsolateIndependent(id)) continue; |
| 28043 | i::Code builtin = i_isolate->builtins()->builtin(id); |
| 28044 | i::Address start = builtin->InstructionStart(); |
| 28045 | i::Address end = start + builtin->InstructionSize(); |
| 28046 | |
| 28047 | i::Address builtins_start = |
| 28048 | reinterpret_cast<i::Address>(builtins_range.start); |
| 28049 | CHECK(start >= builtins_start && |
| 28050 | end < builtins_start + builtins_range.length_in_bytes); |
| 28051 | } |
| 28052 | } else { |
| 28053 | CHECK_EQ(nullptr, builtins_range.start); |
| 28054 | CHECK_EQ(0, builtins_range.length_in_bytes); |
| 28055 | } |
| 28056 | |
| 28057 | v8::JSEntryStub js_entry_stub = unwind_state.js_entry_stub; |
| 28058 | |
| 28059 | CHECK_EQ( |
| 28060 | i_isolate->heap()->builtin(i::Builtins::kJSEntry)->InstructionStart(), |
| 28061 | reinterpret_cast<i::Address>(js_entry_stub.code.start)); |
| 28062 | } |
| 28063 | |
| 28064 | TEST(MicrotaskContextShouldBeNativeContext) { |
| 28065 | LocalContext env; |
| 28066 | v8::Isolate* isolate = env->GetIsolate(); |
| 28067 | v8::HandleScope scope(isolate); |
| 28068 | |
| 28069 | auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 28070 | v8::Isolate* isolate = info.GetIsolate(); |
| 28071 | v8::HandleScope scope(isolate); |
| 28072 | i::Handle<i::Context> context = |
| 28073 | v8::Utils::OpenHandle(*isolate->GetEnteredOrMicrotaskContext()); |
| 28074 | |
| 28075 | CHECK(context->IsNativeContext()); |
| 28076 | info.GetReturnValue().SetUndefined(); |
| 28077 | }; |
| 28078 | |
| 28079 | Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); |
| 28080 | desc->InstanceTemplate()->SetCallAsFunctionHandler(callback); |
| 28081 | Local<v8::Object> obj = desc->GetFunction(env.local()) |
| 28082 | .ToLocalChecked() |
| 28083 | ->NewInstance(env.local()) |
| 28084 | .ToLocalChecked(); |
| 28085 | |
| 28086 | CHECK(env->Global()->Set(env.local(), v8_str("callback" ), obj).FromJust()); |
| 28087 | CompileRun( |
| 28088 | "with({}){(async ()=>{" |
| 28089 | " await 42;" |
| 28090 | "})().then(callback);}" ); |
| 28091 | |
| 28092 | isolate->RunMicrotasks(); |
| 28093 | } |
| 28094 | |
| 28095 | TEST(PreviewSetKeysIteratorEntriesWithDeleted) { |
| 28096 | LocalContext env; |
| 28097 | v8::HandleScope handle_scope(env->GetIsolate()); |
| 28098 | v8::Local<v8::Context> context = env.local(); |
| 28099 | |
| 28100 | { |
| 28101 | // Create set, delete entry, create iterator, preview. |
| 28102 | v8::Local<v8::Object> iterator = |
| 28103 | CompileRun("var set = new Set([1,2,3]); set.delete(1); set.keys()" ) |
| 28104 | ->ToObject(context) |
| 28105 | .ToLocalChecked(); |
| 28106 | bool is_key; |
| 28107 | v8::Local<v8::Array> entries = |
| 28108 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28109 | CHECK(!is_key); |
| 28110 | CHECK_EQ(2, entries->Length()); |
| 28111 | CHECK_EQ(2, entries->Get(context, 0) |
| 28112 | .ToLocalChecked() |
| 28113 | ->Int32Value(context) |
| 28114 | .FromJust()); |
| 28115 | CHECK_EQ(3, entries->Get(context, 1) |
| 28116 | .ToLocalChecked() |
| 28117 | ->Int32Value(context) |
| 28118 | .FromJust()); |
| 28119 | } |
| 28120 | { |
| 28121 | // Create set, create iterator, delete entry, preview. |
| 28122 | v8::Local<v8::Object> iterator = |
| 28123 | CompileRun("var set = new Set([1,2,3]); set.keys()" ) |
| 28124 | ->ToObject(context) |
| 28125 | .ToLocalChecked(); |
| 28126 | CompileRun("set.delete(1);" ); |
| 28127 | bool is_key; |
| 28128 | v8::Local<v8::Array> entries = |
| 28129 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28130 | CHECK(!is_key); |
| 28131 | CHECK_EQ(2, entries->Length()); |
| 28132 | CHECK_EQ(2, entries->Get(context, 0) |
| 28133 | .ToLocalChecked() |
| 28134 | ->Int32Value(context) |
| 28135 | .FromJust()); |
| 28136 | CHECK_EQ(3, entries->Get(context, 1) |
| 28137 | .ToLocalChecked() |
| 28138 | ->Int32Value(context) |
| 28139 | .FromJust()); |
| 28140 | } |
| 28141 | { |
| 28142 | // Create set, create iterator, delete entry, iterate, preview. |
| 28143 | v8::Local<v8::Object> iterator = |
| 28144 | CompileRun("var set = new Set([1,2,3]); var it = set.keys(); it" ) |
| 28145 | ->ToObject(context) |
| 28146 | .ToLocalChecked(); |
| 28147 | CompileRun("set.delete(1); it.next();" ); |
| 28148 | bool is_key; |
| 28149 | v8::Local<v8::Array> entries = |
| 28150 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28151 | CHECK(!is_key); |
| 28152 | CHECK_EQ(1, entries->Length()); |
| 28153 | CHECK_EQ(3, entries->Get(context, 0) |
| 28154 | .ToLocalChecked() |
| 28155 | ->Int32Value(context) |
| 28156 | .FromJust()); |
| 28157 | } |
| 28158 | { |
| 28159 | // Create set, create iterator, delete entry, iterate until empty, preview. |
| 28160 | v8::Local<v8::Object> iterator = |
| 28161 | CompileRun("var set = new Set([1,2,3]); var it = set.keys(); it" ) |
| 28162 | ->ToObject(context) |
| 28163 | .ToLocalChecked(); |
| 28164 | CompileRun("set.delete(1); it.next(); it.next();" ); |
| 28165 | bool is_key; |
| 28166 | v8::Local<v8::Array> entries = |
| 28167 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28168 | CHECK(!is_key); |
| 28169 | CHECK_EQ(0, entries->Length()); |
| 28170 | } |
| 28171 | { |
| 28172 | // Create set, create iterator, delete entry, iterate, trigger rehash, |
| 28173 | // preview. |
| 28174 | v8::Local<v8::Object> iterator = |
| 28175 | CompileRun("var set = new Set([1,2,3]); var it = set.keys(); it" ) |
| 28176 | ->ToObject(context) |
| 28177 | .ToLocalChecked(); |
| 28178 | CompileRun("set.delete(1); it.next();" ); |
| 28179 | CompileRun("for (var i = 4; i < 20; i++) set.add(i);" ); |
| 28180 | bool is_key; |
| 28181 | v8::Local<v8::Array> entries = |
| 28182 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28183 | CHECK(!is_key); |
| 28184 | CHECK_EQ(17, entries->Length()); |
| 28185 | for (uint32_t i = 0; i < 17; i++) { |
| 28186 | CHECK_EQ(i + 3, entries->Get(context, i) |
| 28187 | .ToLocalChecked() |
| 28188 | ->Int32Value(context) |
| 28189 | .FromJust()); |
| 28190 | } |
| 28191 | } |
| 28192 | } |
| 28193 | |
| 28194 | TEST(PreviewSetValuesIteratorEntriesWithDeleted) { |
| 28195 | LocalContext env; |
| 28196 | v8::HandleScope handle_scope(env->GetIsolate()); |
| 28197 | v8::Local<v8::Context> context = env.local(); |
| 28198 | |
| 28199 | { |
| 28200 | // Create set, delete entry, create iterator, preview. |
| 28201 | v8::Local<v8::Object> iterator = |
| 28202 | CompileRun("var set = new Set([1,2,3]); set.delete(1); set.values()" ) |
| 28203 | ->ToObject(context) |
| 28204 | .ToLocalChecked(); |
| 28205 | bool is_key; |
| 28206 | v8::Local<v8::Array> entries = |
| 28207 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28208 | CHECK(!is_key); |
| 28209 | CHECK_EQ(2, entries->Length()); |
| 28210 | CHECK_EQ(2, entries->Get(context, 0) |
| 28211 | .ToLocalChecked() |
| 28212 | ->Int32Value(context) |
| 28213 | .FromJust()); |
| 28214 | CHECK_EQ(3, entries->Get(context, 1) |
| 28215 | .ToLocalChecked() |
| 28216 | ->Int32Value(context) |
| 28217 | .FromJust()); |
| 28218 | } |
| 28219 | { |
| 28220 | // Create set, create iterator, delete entry, preview. |
| 28221 | v8::Local<v8::Object> iterator = |
| 28222 | CompileRun("var set = new Set([1,2,3]); set.values()" ) |
| 28223 | ->ToObject(context) |
| 28224 | .ToLocalChecked(); |
| 28225 | CompileRun("set.delete(1);" ); |
| 28226 | bool is_key; |
| 28227 | v8::Local<v8::Array> entries = |
| 28228 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28229 | CHECK(!is_key); |
| 28230 | CHECK_EQ(2, entries->Length()); |
| 28231 | CHECK_EQ(2, entries->Get(context, 0) |
| 28232 | .ToLocalChecked() |
| 28233 | ->Int32Value(context) |
| 28234 | .FromJust()); |
| 28235 | CHECK_EQ(3, entries->Get(context, 1) |
| 28236 | .ToLocalChecked() |
| 28237 | ->Int32Value(context) |
| 28238 | .FromJust()); |
| 28239 | } |
| 28240 | { |
| 28241 | // Create set, create iterator, delete entry, iterate, preview. |
| 28242 | v8::Local<v8::Object> iterator = |
| 28243 | CompileRun("var set = new Set([1,2,3]); var it = set.values(); it" ) |
| 28244 | ->ToObject(context) |
| 28245 | .ToLocalChecked(); |
| 28246 | CompileRun("set.delete(1); it.next();" ); |
| 28247 | bool is_key; |
| 28248 | v8::Local<v8::Array> entries = |
| 28249 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28250 | CHECK(!is_key); |
| 28251 | CHECK_EQ(1, entries->Length()); |
| 28252 | CHECK_EQ(3, entries->Get(context, 0) |
| 28253 | .ToLocalChecked() |
| 28254 | ->Int32Value(context) |
| 28255 | .FromJust()); |
| 28256 | } |
| 28257 | { |
| 28258 | // Create set, create iterator, delete entry, iterate until empty, preview. |
| 28259 | v8::Local<v8::Object> iterator = |
| 28260 | CompileRun("var set = new Set([1,2,3]); var it = set.values(); it" ) |
| 28261 | ->ToObject(context) |
| 28262 | .ToLocalChecked(); |
| 28263 | CompileRun("set.delete(1); it.next(); it.next();" ); |
| 28264 | bool is_key; |
| 28265 | v8::Local<v8::Array> entries = |
| 28266 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28267 | CHECK(!is_key); |
| 28268 | CHECK_EQ(0, entries->Length()); |
| 28269 | } |
| 28270 | { |
| 28271 | // Create set, create iterator, delete entry, iterate, trigger rehash, |
| 28272 | // preview. |
| 28273 | v8::Local<v8::Object> iterator = |
| 28274 | CompileRun("var set = new Set([1,2,3]); var it = set.values(); it" ) |
| 28275 | ->ToObject(context) |
| 28276 | .ToLocalChecked(); |
| 28277 | CompileRun("set.delete(1); it.next();" ); |
| 28278 | CompileRun("for (var i = 4; i < 20; i++) set.add(i);" ); |
| 28279 | bool is_key; |
| 28280 | v8::Local<v8::Array> entries = |
| 28281 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28282 | CHECK(!is_key); |
| 28283 | CHECK_EQ(17, entries->Length()); |
| 28284 | for (uint32_t i = 0; i < 17; i++) { |
| 28285 | CHECK_EQ(i + 3, entries->Get(context, i) |
| 28286 | .ToLocalChecked() |
| 28287 | ->Int32Value(context) |
| 28288 | .FromJust()); |
| 28289 | } |
| 28290 | } |
| 28291 | } |
| 28292 | |
| 28293 | TEST(PreviewMapEntriesIteratorEntries) { |
| 28294 | LocalContext env; |
| 28295 | v8::HandleScope handle_scope(env->GetIsolate()); |
| 28296 | v8::Local<v8::Context> context = env.local(); |
| 28297 | { |
| 28298 | // Create set, delete entry, create entries iterator, preview. |
| 28299 | v8::Local<v8::Object> iterator = |
| 28300 | CompileRun("var set = new Set([1,2,3]); set.delete(2); set.entries()" ) |
| 28301 | ->ToObject(context) |
| 28302 | .ToLocalChecked(); |
| 28303 | bool is_key; |
| 28304 | v8::Local<v8::Array> entries = |
| 28305 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28306 | CHECK(is_key); |
| 28307 | CHECK_EQ(4, entries->Length()); |
| 28308 | uint32_t first = entries->Get(context, 0) |
| 28309 | .ToLocalChecked() |
| 28310 | ->Int32Value(context) |
| 28311 | .FromJust(); |
| 28312 | uint32_t second = entries->Get(context, 2) |
| 28313 | .ToLocalChecked() |
| 28314 | ->Int32Value(context) |
| 28315 | .FromJust(); |
| 28316 | CHECK_EQ(1, first); |
| 28317 | CHECK_EQ(3, second); |
| 28318 | CHECK_EQ(first, entries->Get(context, 1) |
| 28319 | .ToLocalChecked() |
| 28320 | ->Int32Value(context) |
| 28321 | .FromJust()); |
| 28322 | CHECK_EQ(second, entries->Get(context, 3) |
| 28323 | .ToLocalChecked() |
| 28324 | ->Int32Value(context) |
| 28325 | .FromJust()); |
| 28326 | } |
| 28327 | } |
| 28328 | |
| 28329 | TEST(PreviewMapValuesIteratorEntriesWithDeleted) { |
| 28330 | LocalContext env; |
| 28331 | v8::HandleScope handle_scope(env->GetIsolate()); |
| 28332 | v8::Local<v8::Context> context = env.local(); |
| 28333 | |
| 28334 | { |
| 28335 | // Create map, delete entry, create iterator, preview. |
| 28336 | v8::Local<v8::Object> iterator = CompileRun( |
| 28337 | "var map = new Map();" |
| 28338 | "var key = {}; map.set(key, 1);" |
| 28339 | "map.set({}, 2); map.set({}, 3);" |
| 28340 | "map.delete(key);" |
| 28341 | "map.values()" ) |
| 28342 | ->ToObject(context) |
| 28343 | .ToLocalChecked(); |
| 28344 | bool is_key; |
| 28345 | v8::Local<v8::Array> entries = |
| 28346 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28347 | CHECK(!is_key); |
| 28348 | CHECK_EQ(2, entries->Length()); |
| 28349 | CHECK_EQ(2, entries->Get(context, 0) |
| 28350 | .ToLocalChecked() |
| 28351 | ->Int32Value(context) |
| 28352 | .FromJust()); |
| 28353 | CHECK_EQ(3, entries->Get(context, 1) |
| 28354 | .ToLocalChecked() |
| 28355 | ->Int32Value(context) |
| 28356 | .FromJust()); |
| 28357 | } |
| 28358 | { |
| 28359 | // Create map, create iterator, delete entry, preview. |
| 28360 | v8::Local<v8::Object> iterator = CompileRun( |
| 28361 | "var map = new Map();" |
| 28362 | "var key = {}; map.set(key, 1);" |
| 28363 | "map.set({}, 2); map.set({}, 3);" |
| 28364 | "map.values()" ) |
| 28365 | ->ToObject(context) |
| 28366 | .ToLocalChecked(); |
| 28367 | CompileRun("map.delete(key);" ); |
| 28368 | bool is_key; |
| 28369 | v8::Local<v8::Array> entries = |
| 28370 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28371 | CHECK(!is_key); |
| 28372 | CHECK_EQ(2, entries->Length()); |
| 28373 | CHECK_EQ(2, entries->Get(context, 0) |
| 28374 | .ToLocalChecked() |
| 28375 | ->Int32Value(context) |
| 28376 | .FromJust()); |
| 28377 | CHECK_EQ(3, entries->Get(context, 1) |
| 28378 | .ToLocalChecked() |
| 28379 | ->Int32Value(context) |
| 28380 | .FromJust()); |
| 28381 | } |
| 28382 | { |
| 28383 | // Create map, create iterator, delete entry, iterate, preview. |
| 28384 | v8::Local<v8::Object> iterator = CompileRun( |
| 28385 | "var map = new Map();" |
| 28386 | "var key = {}; map.set(key, 1);" |
| 28387 | "map.set({}, 2); map.set({}, 3);" |
| 28388 | "var it = map.values(); it" ) |
| 28389 | ->ToObject(context) |
| 28390 | .ToLocalChecked(); |
| 28391 | CompileRun("map.delete(key); it.next();" ); |
| 28392 | bool is_key; |
| 28393 | v8::Local<v8::Array> entries = |
| 28394 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28395 | CHECK(!is_key); |
| 28396 | CHECK_EQ(1, entries->Length()); |
| 28397 | CHECK_EQ(3, entries->Get(context, 0) |
| 28398 | .ToLocalChecked() |
| 28399 | ->Int32Value(context) |
| 28400 | .FromJust()); |
| 28401 | } |
| 28402 | { |
| 28403 | // Create map, create iterator, delete entry, iterate until empty, preview. |
| 28404 | v8::Local<v8::Object> iterator = CompileRun( |
| 28405 | "var map = new Map();" |
| 28406 | "var key = {}; map.set(key, 1);" |
| 28407 | "map.set({}, 2); map.set({}, 3);" |
| 28408 | "var it = map.values(); it" ) |
| 28409 | ->ToObject(context) |
| 28410 | .ToLocalChecked(); |
| 28411 | CompileRun("map.delete(key); it.next(); it.next();" ); |
| 28412 | bool is_key; |
| 28413 | v8::Local<v8::Array> entries = |
| 28414 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28415 | CHECK(!is_key); |
| 28416 | CHECK_EQ(0, entries->Length()); |
| 28417 | } |
| 28418 | { |
| 28419 | // Create map, create iterator, delete entry, iterate, trigger rehash, |
| 28420 | // preview. |
| 28421 | v8::Local<v8::Object> iterator = CompileRun( |
| 28422 | "var map = new Map();" |
| 28423 | "var key = {}; map.set(key, 1);" |
| 28424 | "map.set({}, 2); map.set({}, 3);" |
| 28425 | "var it = map.values(); it" ) |
| 28426 | ->ToObject(context) |
| 28427 | .ToLocalChecked(); |
| 28428 | CompileRun("map.delete(key); it.next();" ); |
| 28429 | CompileRun("for (var i = 4; i < 20; i++) map.set({}, i);" ); |
| 28430 | bool is_key; |
| 28431 | v8::Local<v8::Array> entries = |
| 28432 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28433 | CHECK(!is_key); |
| 28434 | CHECK_EQ(17, entries->Length()); |
| 28435 | for (uint32_t i = 0; i < 17; i++) { |
| 28436 | CHECK_EQ(i + 3, entries->Get(context, i) |
| 28437 | .ToLocalChecked() |
| 28438 | ->Int32Value(context) |
| 28439 | .FromJust()); |
| 28440 | } |
| 28441 | } |
| 28442 | } |
| 28443 | |
| 28444 | TEST(PreviewMapKeysIteratorEntriesWithDeleted) { |
| 28445 | LocalContext env; |
| 28446 | v8::HandleScope handle_scope(env->GetIsolate()); |
| 28447 | v8::Local<v8::Context> context = env.local(); |
| 28448 | |
| 28449 | { |
| 28450 | // Create map, delete entry, create iterator, preview. |
| 28451 | v8::Local<v8::Object> iterator = CompileRun( |
| 28452 | "var map = new Map();" |
| 28453 | "var key = 1; map.set(key, {});" |
| 28454 | "map.set(2, {}); map.set(3, {});" |
| 28455 | "map.delete(key);" |
| 28456 | "map.keys()" ) |
| 28457 | ->ToObject(context) |
| 28458 | .ToLocalChecked(); |
| 28459 | bool is_key; |
| 28460 | v8::Local<v8::Array> entries = |
| 28461 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28462 | CHECK(!is_key); |
| 28463 | CHECK_EQ(2, entries->Length()); |
| 28464 | CHECK_EQ(2, entries->Get(context, 0) |
| 28465 | .ToLocalChecked() |
| 28466 | ->Int32Value(context) |
| 28467 | .FromJust()); |
| 28468 | CHECK_EQ(3, entries->Get(context, 1) |
| 28469 | .ToLocalChecked() |
| 28470 | ->Int32Value(context) |
| 28471 | .FromJust()); |
| 28472 | } |
| 28473 | { |
| 28474 | // Create map, create iterator, delete entry, preview. |
| 28475 | v8::Local<v8::Object> iterator = CompileRun( |
| 28476 | "var map = new Map();" |
| 28477 | "var key = 1; map.set(key, {});" |
| 28478 | "map.set(2, {}); map.set(3, {});" |
| 28479 | "map.keys()" ) |
| 28480 | ->ToObject(context) |
| 28481 | .ToLocalChecked(); |
| 28482 | CompileRun("map.delete(key);" ); |
| 28483 | bool is_key; |
| 28484 | v8::Local<v8::Array> entries = |
| 28485 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28486 | CHECK(!is_key); |
| 28487 | CHECK_EQ(2, entries->Length()); |
| 28488 | CHECK_EQ(2, entries->Get(context, 0) |
| 28489 | .ToLocalChecked() |
| 28490 | ->Int32Value(context) |
| 28491 | .FromJust()); |
| 28492 | CHECK_EQ(3, entries->Get(context, 1) |
| 28493 | .ToLocalChecked() |
| 28494 | ->Int32Value(context) |
| 28495 | .FromJust()); |
| 28496 | } |
| 28497 | { |
| 28498 | // Create map, create iterator, delete entry, iterate, preview. |
| 28499 | v8::Local<v8::Object> iterator = CompileRun( |
| 28500 | "var map = new Map();" |
| 28501 | "var key = 1; map.set(key, {});" |
| 28502 | "map.set(2, {}); map.set(3, {});" |
| 28503 | "var it = map.keys(); it" ) |
| 28504 | ->ToObject(context) |
| 28505 | .ToLocalChecked(); |
| 28506 | CompileRun("map.delete(key); it.next();" ); |
| 28507 | bool is_key; |
| 28508 | v8::Local<v8::Array> entries = |
| 28509 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28510 | CHECK(!is_key); |
| 28511 | CHECK_EQ(1, entries->Length()); |
| 28512 | CHECK_EQ(3, entries->Get(context, 0) |
| 28513 | .ToLocalChecked() |
| 28514 | ->Int32Value(context) |
| 28515 | .FromJust()); |
| 28516 | } |
| 28517 | { |
| 28518 | // Create map, create iterator, delete entry, iterate until empty, preview. |
| 28519 | v8::Local<v8::Object> iterator = CompileRun( |
| 28520 | "var map = new Map();" |
| 28521 | "var key = 1; map.set(key, {});" |
| 28522 | "map.set(2, {}); map.set(3, {});" |
| 28523 | "var it = map.keys(); it" ) |
| 28524 | ->ToObject(context) |
| 28525 | .ToLocalChecked(); |
| 28526 | CompileRun("map.delete(key); it.next(); it.next();" ); |
| 28527 | bool is_key; |
| 28528 | v8::Local<v8::Array> entries = |
| 28529 | iterator->PreviewEntries(&is_key).ToLocalChecked(); |
| 28530 | CHECK(!is_key); |
| 28531 | CHECK_EQ(0, entries->Length()); |
| 28532 | } |
| 28533 | } |
| 28534 | |
| 28535 | namespace { |
| 28536 | static v8::Isolate* isolate_1; |
| 28537 | static v8::Isolate* isolate_2; |
| 28538 | v8::Persistent<v8::Context> context_1; |
| 28539 | v8::Persistent<v8::Context> context_2; |
| 28540 | |
| 28541 | static void CallIsolate1(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 28542 | v8::Isolate::Scope isolate_scope(isolate_1); |
| 28543 | v8::HandleScope handle_scope(isolate_1); |
| 28544 | v8::Local<v8::Context> context = |
| 28545 | v8::Local<v8::Context>::New(isolate_1, context_1); |
| 28546 | v8::Context::Scope context_scope(context); |
| 28547 | CompileRun("f1() //# sourceURL=isolate1b" ); |
| 28548 | } |
| 28549 | |
| 28550 | static void CallIsolate2(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 28551 | v8::Isolate::Scope isolate_scope(isolate_2); |
| 28552 | v8::HandleScope handle_scope(isolate_2); |
| 28553 | v8::Local<v8::Context> context = |
| 28554 | v8::Local<v8::Context>::New(isolate_2, context_2); |
| 28555 | v8::Context::Scope context_scope(context); |
| 28556 | reinterpret_cast<i::Isolate*>(isolate_2)->heap()->CollectAllGarbage( |
| 28557 | i::Heap::kNoGCFlags, i::GarbageCollectionReason::kTesting, |
| 28558 | v8::kGCCallbackFlagForced); |
| 28559 | CompileRun("f2() //# sourceURL=isolate2b" ); |
| 28560 | } |
| 28561 | |
| 28562 | } // anonymous namespace |
| 28563 | |
| 28564 | UNINITIALIZED_TEST(NestedIsolates) { |
| 28565 | #ifdef VERIFY_HEAP |
| 28566 | i::FLAG_verify_heap = true; |
| 28567 | #endif // VERIFY_HEAP |
| 28568 | // Create two isolates and set up C++ functions via function templates that |
| 28569 | // call into the other isolate. Recurse a few times, trigger GC along the way, |
| 28570 | // and finally capture a stack trace. Check that the stack trace only includes |
| 28571 | // frames from its own isolate. |
| 28572 | v8::Isolate::CreateParams create_params; |
| 28573 | create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| 28574 | isolate_1 = v8::Isolate::New(create_params); |
| 28575 | isolate_2 = v8::Isolate::New(create_params); |
| 28576 | |
| 28577 | { |
| 28578 | v8::Isolate::Scope isolate_scope(isolate_1); |
| 28579 | v8::HandleScope handle_scope(isolate_1); |
| 28580 | |
| 28581 | v8::Local<v8::Context> context = v8::Context::New(isolate_1); |
| 28582 | v8::Context::Scope context_scope(context); |
| 28583 | |
| 28584 | v8::Local<v8::FunctionTemplate> fun_templ = |
| 28585 | v8::FunctionTemplate::New(isolate_1, CallIsolate2); |
| 28586 | fun_templ->SetClassName(v8_str(isolate_1, "call_isolate_2" )); |
| 28587 | Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked(); |
| 28588 | CHECK(context->Global() |
| 28589 | ->Set(context, v8_str(isolate_1, "call_isolate_2" ), fun) |
| 28590 | .FromJust()); |
| 28591 | CompileRun( |
| 28592 | "let c = 0;" |
| 28593 | "function f1() {" |
| 28594 | " c++;" |
| 28595 | " return call_isolate_2();" |
| 28596 | "} //# sourceURL=isolate1a" ); |
| 28597 | context_1.Reset(isolate_1, context); |
| 28598 | } |
| 28599 | |
| 28600 | { |
| 28601 | v8::Isolate::Scope isolate_scope(isolate_2); |
| 28602 | v8::HandleScope handle_scope(isolate_2); |
| 28603 | |
| 28604 | v8::Local<v8::Context> context = v8::Context::New(isolate_2); |
| 28605 | v8::Context::Scope context_scope(context); |
| 28606 | |
| 28607 | v8::Local<v8::FunctionTemplate> fun_templ = |
| 28608 | v8::FunctionTemplate::New(isolate_2, CallIsolate1); |
| 28609 | fun_templ->SetClassName(v8_str(isolate_2, "call_isolate_1" )); |
| 28610 | Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked(); |
| 28611 | |
| 28612 | CHECK(context->Global() |
| 28613 | ->Set(context, v8_str(isolate_2, "call_isolate_1" ), fun) |
| 28614 | .FromJust()); |
| 28615 | CompileRun( |
| 28616 | "let c = 4;" |
| 28617 | "let result = undefined;" |
| 28618 | "function f2() {" |
| 28619 | " if (c-- > 0) return call_isolate_1();" |
| 28620 | " else result = new Error().stack;" |
| 28621 | "} //# sourceURL=isolate2a" ); |
| 28622 | context_2.Reset(isolate_2, context); |
| 28623 | |
| 28624 | v8::Local<v8::String> result = |
| 28625 | CompileRun("f2(); result //# sourceURL=isolate2c" ) |
| 28626 | ->ToString(context) |
| 28627 | .ToLocalChecked(); |
| 28628 | v8::Local<v8::String> expectation = v8_str(isolate_2, |
| 28629 | "Error\n" |
| 28630 | " at f2 (isolate2a:1:104)\n" |
| 28631 | " at isolate2b:1:1\n" |
| 28632 | " at f2 (isolate2a:1:71)\n" |
| 28633 | " at isolate2b:1:1\n" |
| 28634 | " at f2 (isolate2a:1:71)\n" |
| 28635 | " at isolate2b:1:1\n" |
| 28636 | " at f2 (isolate2a:1:71)\n" |
| 28637 | " at isolate2b:1:1\n" |
| 28638 | " at f2 (isolate2a:1:71)\n" |
| 28639 | " at isolate2c:1:1" ); |
| 28640 | CHECK(result->StrictEquals(expectation)); |
| 28641 | } |
| 28642 | |
| 28643 | { |
| 28644 | v8::Isolate::Scope isolate_scope(isolate_1); |
| 28645 | v8::HandleScope handle_scope(isolate_1); |
| 28646 | v8::Local<v8::Context> context = |
| 28647 | v8::Local<v8::Context>::New(isolate_1, context_1); |
| 28648 | v8::Context::Scope context_scope(context); |
| 28649 | ExpectInt32("c" , 4); |
| 28650 | } |
| 28651 | |
| 28652 | isolate_1->Dispose(); |
| 28653 | isolate_2->Dispose(); |
| 28654 | } |
| 28655 | |