// Copyright 2007-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <stdlib.h>

#include <map>
#include <string>

#include "v8.h"

#include "api.h"
#include "snapshot.h"
#include "platform.h"
#include "top.h"
#include "cctest.h"

static bool IsNaN(double x) {
#ifdef WIN32
  return _isnan(x);
#else
  return isnan(x);
#endif
}

using ::v8::ObjectTemplate;
using ::v8::Value;
using ::v8::Context;
using ::v8::Local;
using ::v8::String;
using ::v8::Script;
using ::v8::Function;
using ::v8::AccessorInfo;
using ::v8::Extension;

namespace i = ::v8::internal;

static Local<Value> v8_num(double x) {
  return v8::Number::New(x);
}


static Local<String> v8_str(const char* x) {
  return String::New(x);
}


static Local<Script> v8_compile(const char* x) {
  return Script::Compile(v8_str(x));
}


// A LocalContext holds a reference to a v8::Context.
class LocalContext {
 public:
  LocalContext(v8::ExtensionConfiguration* extensions = 0,
               v8::Handle<ObjectTemplate> global_template =
                   v8::Handle<ObjectTemplate>(),
               v8::Handle<Value> global_object = v8::Handle<Value>())
    : context_(Context::New(extensions, global_template, global_object)) {
    context_->Enter();
  }

  virtual ~LocalContext() {
    context_->Exit();
    context_.Dispose();
  }

  Context* operator->() { return *context_; }
  Context* operator*() { return *context_; }
  Local<Context> local() { return Local<Context>::New(context_); }
  bool IsReady() { return !context_.IsEmpty(); }

 private:
  v8::Persistent<Context> context_;
};


// Switches between all the Api tests using the threading support.
// In order to get a surprising but repeatable pattern of thread
// switching it has extra semaphores to control the order in which
// the tests alternate, not relying solely on the big V8 lock.
//
// A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
// callbacks.  This will have no effect when we are not running the
// thread fuzzing test.  In the thread fuzzing test it will
// pseudorandomly select a successor thread and switch execution
// to that thread, suspending the current test.
class ApiTestFuzzer: public v8::internal::Thread {
 public:
  void CallTest();
  explicit ApiTestFuzzer(int num)
      : test_number_(num),
        gate_(v8::internal::OS::CreateSemaphore(0)),
        active_(true) {
  }

  // The ApiTestFuzzer is also a Thread, so it has a Run method.
  virtual void Run();

  enum PartOfTest { FIRST_PART, SECOND_PART };

  static void Setup(PartOfTest part);
  static void RunAllTests();
  static void TearDown();
  // This method switches threads if we are running the Threading test.
  // Otherwise it does nothing.
  static void Fuzz();
 private:
  static bool fuzzing_;
  static int tests_being_run_;
  static int current_;
  static int active_tests_;
  static bool NextThread();
  int test_number_;
  v8::internal::Semaphore* gate_;
  bool active_;
  void ContextSwitch();
  static int GetNextTestNumber();
  static v8::internal::Semaphore* all_tests_done_;
};


#define THREADED_TEST(Name)                                          \
  static void Test##Name();                                          \
  RegisterThreadedTest register_##Name(Test##Name);                  \
  /* */ TEST(Name)


class RegisterThreadedTest {
 public:
  explicit RegisterThreadedTest(CcTest::TestFunction* callback)
      : callback_(callback) {
    prev_ = first_;
    first_ = this;
    count_++;
  }
  static int count() { return count_; }
  static RegisterThreadedTest* nth(int i) {
    ASSERT(i < count());
    RegisterThreadedTest* current = first_;
    while (i > 0) {
      i--;
      current = current->prev_;
    }
    return current;
  }
  CcTest::TestFunction* callback() { return callback_; }
  ApiTestFuzzer* fuzzer_;

 private:
  static RegisterThreadedTest* first_;
  static int count_;
  CcTest::TestFunction* callback_;
  RegisterThreadedTest* prev_;
};


RegisterThreadedTest *RegisterThreadedTest::first_ = NULL;
int RegisterThreadedTest::count_ = 0;


static int signature_callback_count;
static v8::Handle<Value> IncrementingSignatureCallback(
    const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  signature_callback_count++;
  v8::Handle<v8::Array> result = v8::Array::New(args.Length());
  for (int i = 0; i < args.Length(); i++)
    result->Set(v8::Integer::New(i), args[i]);
  return result;
}


static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  v8::Handle<v8::Array> result = v8::Array::New(args.Length());
  for (int i = 0; i < args.Length(); i++) {
    result->Set(v8::Integer::New(i), args[i]);
  }
  return result;
}


THREADED_TEST(Handles) {
  v8::HandleScope scope;
  Local<Context> local_env;
  {
    LocalContext env;
    local_env = env.local();
  }

  // Local context should still be live.
  CHECK(!local_env.IsEmpty());
  local_env->Enter();

  v8::Handle<v8::Primitive> undef = v8::Undefined();
  CHECK(!undef.IsEmpty());
  CHECK(undef->IsUndefined());

  const char* c_source = "1 + 2 + 3";
  Local<String> source = String::New(c_source);
  Local<Script> script = Script::Compile(source);
  CHECK_EQ(6, script->Run()->Int32Value());

  local_env->Exit();
}


// Helper function that compiles and runs the source.
static Local<Value> CompileRun(const char* source) {
  return Script::Compile(String::New(source))->Run();
}

THREADED_TEST(ReceiverSignature) {
  v8::HandleScope scope;
  LocalContext env;
  v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
  v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
  fun->PrototypeTemplate()->Set(
      v8_str("m"),
      v8::FunctionTemplate::New(IncrementingSignatureCallback,
                                v8::Handle<Value>(),
                                sig));
  env->Global()->Set(v8_str("Fun"), fun->GetFunction());
  signature_callback_count = 0;
  CompileRun(
      "var o = new Fun();"
      "o.m();");
  CHECK_EQ(1, signature_callback_count);
  v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
  sub_fun->Inherit(fun);
  env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
  CompileRun(
      "var o = new SubFun();"
      "o.m();");
  CHECK_EQ(2, signature_callback_count);

  v8::TryCatch try_catch;
  CompileRun(
      "var o = { };"
      "o.m = Fun.prototype.m;"
      "o.m();");
  CHECK_EQ(2, signature_callback_count);
  CHECK(try_catch.HasCaught());
  try_catch.Reset();
  v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
  sub_fun->Inherit(fun);
  env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
  CompileRun(
      "var o = new UnrelFun();"
      "o.m = Fun.prototype.m;"
      "o.m();");
  CHECK_EQ(2, signature_callback_count);
  CHECK(try_catch.HasCaught());
}




THREADED_TEST(ArgumentSignature) {
  v8::HandleScope scope;
  LocalContext env;
  v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
  cons->SetClassName(v8_str("Cons"));
  v8::Handle<v8::Signature> sig =
      v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
  v8::Handle<v8::FunctionTemplate> fun =
      v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
  env->Global()->Set(v8_str("Cons"), cons->GetFunction());
  env->Global()->Set(v8_str("Fun1"), fun->GetFunction());

  v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
  ASSERT(value1->IsTrue());

  v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
  ASSERT(value2->IsTrue());

  v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
  ASSERT(value3->IsTrue());

  v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
  cons1->SetClassName(v8_str("Cons1"));
  v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
  cons2->SetClassName(v8_str("Cons2"));
  v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
  cons3->SetClassName(v8_str("Cons3"));

  v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
  v8::Handle<v8::Signature> wsig =
      v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
  v8::Handle<v8::FunctionTemplate> fun2 =
      v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);

  env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
  env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
  env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
  env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
  v8::Handle<Value> value4 = CompileRun(
      "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
      "'[object Cons1],[object Cons2],[object Cons3]'");
  ASSERT(value4->IsTrue());

  v8::Handle<Value> value5 = CompileRun(
      "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
  ASSERT(value5->IsTrue());

  v8::Handle<Value> value6 = CompileRun(
      "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
  ASSERT(value6->IsTrue());

  v8::Handle<Value> value7 = CompileRun(
      "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
      "'[object Cons1],[object Cons2],[object Cons3],d';");
  ASSERT(value7->IsTrue());

  v8::Handle<Value> value8 = CompileRun(
      "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
  ASSERT(value8->IsTrue());
}


THREADED_TEST(HulIgennem) {
  v8::HandleScope scope;
  LocalContext env;
  v8::Handle<v8::Primitive> undef = v8::Undefined();
  Local<String> undef_str = undef->ToString();
  char* value = i::NewArray<char>(undef_str->Length() + 1);
  undef_str->WriteAscii(value);
  CHECK_EQ(0, strcmp(value, "undefined"));
  i::DeleteArray(value);
}


THREADED_TEST(Access) {
  v8::HandleScope scope;
  LocalContext env;
  Local<v8::Object> obj = v8::Object::New();
  Local<Value> foo_before = obj->Get(v8_str("foo"));
  CHECK(foo_before->IsUndefined());
  Local<String> bar_str = v8_str("bar");
  obj->Set(v8_str("foo"), bar_str);
  Local<Value> foo_after = obj->Get(v8_str("foo"));
  CHECK(!foo_after->IsUndefined());
  CHECK(foo_after->IsString());
  CHECK_EQ(bar_str, foo_after);
}


THREADED_TEST(Script) {
  v8::HandleScope scope;
  LocalContext env;
  const char* c_source = "1 + 2 + 3";
  Local<String> source = String::New(c_source);
  Local<Script> script = Script::Compile(source);
  CHECK_EQ(6, script->Run()->Int32Value());
}


static uint16_t* AsciiToTwoByteString(const char* source) {
  size_t array_length = strlen(source) + 1;
  uint16_t* converted = i::NewArray<uint16_t>(array_length);
  for (size_t i = 0; i < array_length; i++) converted[i] = source[i];
  return converted;
}


class TestResource: public String::ExternalStringResource {
 public:
  static int dispose_count;

  explicit TestResource(uint16_t* data)
      : data_(data), length_(0) {
    while (data[length_]) ++length_;
  }

  ~TestResource() {
    i::DeleteArray(data_);
    ++dispose_count;
  }

  const uint16_t* data() const {
    return data_;
  }

  size_t length() const {
    return length_;
  }
 private:
  uint16_t* data_;
  size_t length_;
};


int TestResource::dispose_count = 0;


class TestAsciiResource: public String::ExternalAsciiStringResource {
 public:
  static int dispose_count;

  explicit TestAsciiResource(char* data)
      : data_(data),
        length_(strlen(data)) { }

  ~TestAsciiResource() {
    i::DeleteArray(data_);
    ++dispose_count;
  }

  const char* data() const {
    return data_;
  }

  size_t length() const {
    return length_;
  }
 private:
  char* data_;
  size_t length_;
};


int TestAsciiResource::dispose_count = 0;


THREADED_TEST(ScriptUsingStringResource) {
  TestResource::dispose_count = 0;
  const char* c_source = "1 + 2 * 3";
  uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
  {
    v8::HandleScope scope;
    LocalContext env;
    TestResource* resource = new TestResource(two_byte_source);
    Local<String> source = String::NewExternal(resource);
    Local<Script> script = Script::Compile(source);
    Local<Value> value = script->Run();
    CHECK(value->IsNumber());
    CHECK_EQ(7, value->Int32Value());
    CHECK(source->IsExternal());
    CHECK_EQ(resource,
             static_cast<TestResource*>(source->GetExternalStringResource()));
    v8::internal::Heap::CollectAllGarbage();
    CHECK_EQ(0, TestResource::dispose_count);
  }
  v8::internal::Heap::CollectAllGarbage();
  CHECK_EQ(1, TestResource::dispose_count);
}


THREADED_TEST(ScriptUsingAsciiStringResource) {
  TestAsciiResource::dispose_count = 0;
  const char* c_source = "1 + 2 * 3";
  {
    v8::HandleScope scope;
    LocalContext env;
    Local<String> source =
        String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
    Local<Script> script = Script::Compile(source);
    Local<Value> value = script->Run();
    CHECK(value->IsNumber());
    CHECK_EQ(7, value->Int32Value());
    v8::internal::Heap::CollectAllGarbage();
    CHECK_EQ(0, TestAsciiResource::dispose_count);
  }
  v8::internal::Heap::CollectAllGarbage();
  CHECK_EQ(1, TestAsciiResource::dispose_count);
}


THREADED_TEST(GlobalProperties) {
  v8::HandleScope scope;
  LocalContext env;
  v8::Handle<v8::Object> global = env->Global();
  global->Set(v8_str("pi"), v8_num(3.1415926));
  Local<Value> pi = global->Get(v8_str("pi"));
  CHECK_EQ(3.1415926, pi->NumberValue());
}


static v8::Handle<Value> handle_call(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8_num(102);
}


static v8::Handle<Value> construct_call(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  args.This()->Set(v8_str("x"), v8_num(1));
  args.This()->Set(v8_str("y"), v8_num(2));
  return args.This();
}

THREADED_TEST(FunctionTemplate) {
  v8::HandleScope scope;
  LocalContext env;
  {
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(handle_call);
    Local<Function> fun = fun_templ->GetFunction();
    env->Global()->Set(v8_str("obj"), fun);
    Local<Script> script = v8_compile("obj()");
    CHECK_EQ(102, script->Run()->Int32Value());
  }
  // Use SetCallHandler to initialize a function template, should work like the
  // previous one.
  {
    Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
    fun_templ->SetCallHandler(handle_call);
    Local<Function> fun = fun_templ->GetFunction();
    env->Global()->Set(v8_str("obj"), fun);
    Local<Script> script = v8_compile("obj()");
    CHECK_EQ(102, script->Run()->Int32Value());
  }
  // Test constructor calls.
  {
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(construct_call);
    fun_templ->SetClassName(v8_str("funky"));
    Local<Function> fun = fun_templ->GetFunction();
    env->Global()->Set(v8_str("obj"), fun);
    Local<Script> script = v8_compile("var s = new obj(); s.x");
    CHECK_EQ(1, script->Run()->Int32Value());

    Local<Value> result = v8_compile("(new obj()).toString()")->Run();
    CHECK_EQ(v8_str("[object funky]"), result);
  }
}


static v8::Handle<Value> handle_property(Local<String> name,
                                         const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  return v8_num(900);
}


THREADED_TEST(PropertyHandler) {
  v8::HandleScope scope;
  Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
  fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property);
  LocalContext env;
  Local<Function> fun = fun_templ->GetFunction();
  env->Global()->Set(v8_str("Fun"), fun);
  Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;");
  CHECK_EQ(900, getter->Run()->Int32Value());
  Local<Script> setter = v8_compile("obj.foo = 901;");
  CHECK_EQ(901, setter->Run()->Int32Value());
}


THREADED_TEST(Number) {
  v8::HandleScope scope;
  LocalContext env;
  double PI = 3.1415926;
  Local<v8::Number> pi_obj = v8::Number::New(PI);
  CHECK_EQ(PI, pi_obj->NumberValue());
}


THREADED_TEST(ToNumber) {
  v8::HandleScope scope;
  LocalContext env;
  Local<String> str = v8_str("3.1415926");
  CHECK_EQ(3.1415926, str->NumberValue());
  v8::Handle<v8::Boolean> t = v8::True();
  CHECK_EQ(1.0, t->NumberValue());
  v8::Handle<v8::Boolean> f = v8::False();
  CHECK_EQ(0.0, f->NumberValue());
}


THREADED_TEST(Date) {
  v8::HandleScope scope;
  LocalContext env;
  double PI = 3.1415926;
  Local<Value> date_obj = v8::Date::New(PI);
  CHECK_EQ(3.0, date_obj->NumberValue());
}


THREADED_TEST(Boolean) {
  v8::HandleScope scope;
  LocalContext env;
  v8::Handle<v8::Boolean> t = v8::True();
  CHECK(t->Value());
  v8::Handle<v8::Boolean> f = v8::False();
  CHECK(!f->Value());
  v8::Handle<v8::Primitive> u = v8::Undefined();
  CHECK(!u->BooleanValue());
  v8::Handle<v8::Primitive> n = v8::Null();
  CHECK(!n->BooleanValue());
  v8::Handle<String> str1 = v8_str("");
  CHECK(!str1->BooleanValue());
  v8::Handle<String> str2 = v8_str("x");
  CHECK(str2->BooleanValue());
  CHECK(!v8::Number::New(0)->BooleanValue());
  CHECK(v8::Number::New(-1)->BooleanValue());
  CHECK(v8::Number::New(1)->BooleanValue());
  CHECK(v8::Number::New(42)->BooleanValue());
  CHECK(!v8_compile("NaN")->Run()->BooleanValue());
}


static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8_num(13.4);
}


static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  return v8_num(876);
}


THREADED_TEST(GlobalPrototype) {
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
  func_templ->PrototypeTemplate()->Set(
      "dummy",
      v8::FunctionTemplate::New(DummyCallHandler));
  v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
  templ->Set("x", v8_num(200));
  templ->SetAccessor(v8_str("m"), GetM);
  LocalContext env(0, templ);
  v8::Handle<v8::Object> obj = env->Global();
  v8::Handle<Script> script = v8_compile("dummy()");
  v8::Handle<Value> result = script->Run();
  CHECK_EQ(13.4, result->NumberValue());
  CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
  CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
}


static v8::Handle<Value> GetIntValue(Local<String> property,
                                     const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  int* value =
      static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
  return v8_num(*value);
}

static void SetIntValue(Local<String> property,
                        Local<Value> value,
                        const AccessorInfo& info) {
  int* field =
      static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
  *field = value->Int32Value();
}

int foo, bar, baz;

THREADED_TEST(GlobalVariableAccess) {
  foo = 0;
  bar = -4;
  baz = 10;
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
                                         GetIntValue,
                                         SetIntValue,
                                         v8::External::New(&foo));
  templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
                                         GetIntValue,
                                         SetIntValue,
                                         v8::External::New(&bar));
  templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
                                         GetIntValue,
                                         SetIntValue,
                                         v8::External::New(&baz));
  LocalContext env(0, templ->InstanceTemplate());
  v8_compile("foo = (++bar) + baz")->Run();
  CHECK_EQ(bar, -3);
  CHECK_EQ(foo, 7);
}


THREADED_TEST(ObjectTemplate) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ1 = ObjectTemplate::New();
  templ1->Set("x", v8_num(10));
  templ1->Set("y", v8_num(13));
  LocalContext env;
  Local<v8::Object> instance1 = templ1->NewInstance();
  env->Global()->Set(v8_str("p"), instance1);
  CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
  CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
  Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
  fun->PrototypeTemplate()->Set("nirk", v8_num(123));
  Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
  templ2->Set("a", v8_num(12));
  templ2->Set("b", templ1);
  Local<v8::Object> instance2 = templ2->NewInstance();
  env->Global()->Set(v8_str("q"), instance2);
  CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
  CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
  CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
  CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
}


static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8_num(17.2);
}


static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  return v8_num(15.2);
}


THREADED_TEST(DescriptorInheritance) {
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
  super->PrototypeTemplate()->Set("flabby",
                                  v8::FunctionTemplate::New(GetFlabby));
  super->PrototypeTemplate()->Set("PI", v8_num(3.14));

  super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);

  v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
  base1->Inherit(super);
  base1->PrototypeTemplate()->Set("v1", v8_num(20.1));

  v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
  base2->Inherit(super);
  base2->PrototypeTemplate()->Set("v2", v8_num(10.1));

  LocalContext env;

  env->Global()->Set(v8_str("s"), super->GetFunction());
  env->Global()->Set(v8_str("base1"), base1->GetFunction());
  env->Global()->Set(v8_str("base2"), base2->GetFunction());

  // Checks right __proto__ chain.
  CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
  CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());

  CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());

  // Instance accessor should not be visible on function object or its prototype
  CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
  CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
  CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());

  env->Global()->Set(v8_str("obj"),
                     base1->GetFunction()->NewInstance());
  CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
  CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
  CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
  CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
  CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());

  env->Global()->Set(v8_str("obj2"),
                     base2->GetFunction()->NewInstance());
  CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
  CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
  CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
  CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
  CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());

  // base1 and base2 cannot cross reference to each's prototype
  CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
  CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
}


int echo_named_call_count;


static v8::Handle<Value> EchoNamedProperty(Local<String> name,
                                           const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(v8_str("data"), info.Data());
  echo_named_call_count++;
  return name;
}


THREADED_TEST(NamedPropertyHandlerGetter) {
  echo_named_call_count = 0;
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
                                                     0, 0, 0, 0,
                                                     v8_str("data"));
  LocalContext env;
  env->Global()->Set(v8_str("obj"),
                     templ->GetFunction()->NewInstance());
  CHECK_EQ(echo_named_call_count, 0);
  v8_compile("obj.x")->Run();
  CHECK_EQ(echo_named_call_count, 1);
  const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
  v8::Handle<Value> str = CompileRun(code);
  String::AsciiValue value(str);
  CHECK_EQ(*value, "oddlepoddle");
  // Check default behavior
  CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
  CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
  CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
}


int echo_indexed_call_count = 0;


static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
                                             const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(v8_num(637), info.Data());
  echo_indexed_call_count++;
  return v8_num(index);
}


THREADED_TEST(IndexedPropertyHandlerGetter) {
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
                                                       0, 0, 0, 0,
                                                       v8_num(637));
  LocalContext env;
  env->Global()->Set(v8_str("obj"),
                     templ->GetFunction()->NewInstance());
  Local<Script> script = v8_compile("obj[900]");
  CHECK_EQ(script->Run()->Int32Value(), 900);
}


v8::Handle<v8::Object> bottom;

static v8::Handle<Value> CheckThisIndexedPropertyHandler(
    uint32_t index,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<Value>();
}

static v8::Handle<Value> CheckThisNamedPropertyHandler(
    Local<String> name,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<Value>();
}


v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
                                                 Local<Value> value,
                                                 const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<Value>();
}


v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
                                               Local<Value> value,
                                               const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<Value>();
}

v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
    uint32_t index,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<v8::Boolean>();
}


v8::Handle<v8::Boolean> CheckThisNamedPropertyQuery(Local<String> property,
                                                    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<v8::Boolean>();
}


v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
    uint32_t index,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<v8::Boolean>();
}


v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
    Local<String> property,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<v8::Boolean>();
}


v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<v8::Array>();
}


v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.This()->Equals(bottom));
  return v8::Handle<v8::Array>();
}


THREADED_TEST(PropertyHandlerInPrototype) {
  v8::HandleScope scope;
  LocalContext env;

  // Set up a prototype chain with three interceptors.
  v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->InstanceTemplate()->SetIndexedPropertyHandler(
      CheckThisIndexedPropertyHandler,
      CheckThisIndexedPropertySetter,
      CheckThisIndexedPropertyQuery,
      CheckThisIndexedPropertyDeleter,
      CheckThisIndexedPropertyEnumerator);

  templ->InstanceTemplate()->SetNamedPropertyHandler(
      CheckThisNamedPropertyHandler,
      CheckThisNamedPropertySetter,
      CheckThisNamedPropertyQuery,
      CheckThisNamedPropertyDeleter,
      CheckThisNamedPropertyEnumerator);

  bottom = templ->GetFunction()->NewInstance();
  Local<v8::Object> top = templ->GetFunction()->NewInstance();
  Local<v8::Object> middle = templ->GetFunction()->NewInstance();

  bottom->Set(v8_str("__proto__"), middle);
  middle->Set(v8_str("__proto__"), top);
  env->Global()->Set(v8_str("obj"), bottom);

  // Indexed and named get.
  Script::Compile(v8_str("obj[0]"))->Run();
  Script::Compile(v8_str("obj.x"))->Run();

  // Indexed and named set.
  Script::Compile(v8_str("obj[1] = 42"))->Run();
  Script::Compile(v8_str("obj.y = 42"))->Run();

  // Indexed and named query.
  Script::Compile(v8_str("0 in obj"))->Run();
  Script::Compile(v8_str("'x' in obj"))->Run();

  // Indexed and named deleter.
  Script::Compile(v8_str("delete obj[0]"))->Run();
  Script::Compile(v8_str("delete obj.x"))->Run();

  // Enumerators.
  Script::Compile(v8_str("for (var p in obj) ;"))->Run();
}


static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
                                               const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  if (v8_str("pre")->Equals(key)) {
    return v8_str("PrePropertyHandler: pre");
  }
  return v8::Handle<String>();
}


static v8::Handle<v8::Boolean> PrePropertyHandlerHas(Local<String> key,
                                                     const AccessorInfo&) {
  if (v8_str("pre")->Equals(key)) {
    return v8::True();
  }

  return v8::Handle<v8::Boolean>();  // do not intercept the call
}


THREADED_TEST(PrePropertyHandler) {
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
  desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
                                                    0,
                                                    PrePropertyHandlerHas);
  LocalContext env(NULL, desc->InstanceTemplate());
  Script::Compile(v8_str(
      "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
  v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
  CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
  v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
  CHECK_EQ(v8_str("Object: on"), result_on);
  v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
  CHECK(result_post.IsEmpty());
}


v8::Handle<Script> call_recursively_script;
static const int kTargetRecursionDepth = 300;  // near maximum


static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  int depth = args.This()->Get(v8_str("depth"))->Int32Value();
  if (depth == kTargetRecursionDepth) return v8::Undefined();
  args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
  return call_recursively_script->Run();
}


static v8::Handle<Value> CallFunctionRecursivelyCall(
    const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  int depth = args.This()->Get(v8_str("depth"))->Int32Value();
  if (depth == kTargetRecursionDepth) {
    printf("[depth = %d]\n", depth);
    return v8::Undefined();
  }
  args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
  v8::Handle<Value> function =
      args.This()->Get(v8_str("callFunctionRecursively"));
  return v8::Handle<Function>::Cast(function)->Call(args.This(), 0, NULL);
}


THREADED_TEST(DeepCrossLanguageRecursion) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
  global->Set(v8_str("callScriptRecursively"),
              v8::FunctionTemplate::New(CallScriptRecursivelyCall));
  global->Set(v8_str("callFunctionRecursively"),
              v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
  LocalContext env(NULL, global);

  env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
  call_recursively_script = v8_compile("callScriptRecursively()");
  v8::Handle<Value> result = call_recursively_script->Run();
  call_recursively_script = v8::Handle<Script>();

  env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
  Script::Compile(v8_str("callFunctionRecursively()"))->Run();
}


static v8::Handle<Value>
    ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  return v8::ThrowException(key);
}


static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
                                                    Local<Value>,
                                                    const AccessorInfo&) {
  v8::ThrowException(key);
  return v8::Undefined();  // not the same as v8::Handle<v8::Value>()
}


THREADED_TEST(CallbackExceptionRegression) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
                               ThrowingPropertyHandlerSet);
  LocalContext env;
  env->Global()->Set(v8_str("obj"), obj->NewInstance());
  v8::Handle<Value> otto = Script::Compile(v8_str(
      "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
  CHECK_EQ(v8_str("otto"), otto);
  v8::Handle<Value> netto = Script::Compile(v8_str(
      "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
  CHECK_EQ(v8_str("netto"), netto);
}


static v8::Handle<Value> ThrowingGetAccessor(Local<String> name,
                                             const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  return v8::ThrowException(v8_str("g"));
}


static void ThrowingSetAccessor(Local<String> name,
                                Local<Value> value,
                                const AccessorInfo& info) {
  v8::ThrowException(value);
}


THREADED_TEST(Regress1054726) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetAccessor(v8_str("x"),
                   ThrowingGetAccessor,
                   ThrowingSetAccessor,
                   Local<Value>());

  LocalContext env;
  env->Global()->Set(v8_str("obj"), obj->NewInstance());

  // Use the throwing property setter/getter in a loop to force
  // the accessor ICs to be initialized.
  v8::Handle<Value> result;
  result = Script::Compile(v8_str(
      "var result = '';"
      "for (var i = 0; i < 5; i++) {"
      "  try { obj.x; } catch (e) { result += e; }"
      "}; result"))->Run();
  CHECK_EQ(v8_str("ggggg"), result);

  result = Script::Compile(String::New(
      "var result = '';"
      "for (var i = 0; i < 5; i++) {"
      "  try { obj.x = i; } catch (e) { result += e; }"
      "}; result"))->Run();
  CHECK_EQ(v8_str("01234"), result);
}


THREADED_TEST(FunctionPrototype) {
  v8::HandleScope scope;
  Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
  Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
  LocalContext env;
  env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
  Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
  CHECK_EQ(script->Run()->Int32Value(), 321);
}


THREADED_TEST(InternalFields) {
  v8::HandleScope scope;
  LocalContext env;

  Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
  instance_templ->SetInternalFieldCount(1);
  Local<v8::Object> obj = templ->GetFunction()->NewInstance();
  CHECK_EQ(1, obj->InternalFieldCount());
  CHECK(obj->GetInternalField(0)->IsUndefined());
  obj->SetInternalField(0, v8_num(17));
  CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
}


THREADED_TEST(External) {
  v8::HandleScope scope;
  int x = 3;
  Local<v8::External> ext = v8::External::New(&x);
  LocalContext env;
  env->Global()->Set(v8_str("ext"), ext);
  Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
  v8::Handle<v8::External> reext = v8::Handle<v8::External>::Cast(reext_obj);
  int* ptr = static_cast<int*>(reext->Value());
  CHECK_EQ(x, 3);
  *ptr = 10;
  CHECK_EQ(x, 10);
}


THREADED_TEST(GlobalHandle) {
  v8::Persistent<String> global;
  {
    v8::HandleScope scope;
    Local<String> str = v8_str("str");
    global = v8::Persistent<String>::New(str);
  }
  CHECK_EQ(global->Length(), 3);
  global.Dispose();
}


THREADED_TEST(ScriptException) {
  v8::HandleScope scope;
  LocalContext env;
  Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
  v8::TryCatch try_catch;
  Local<Value> result = script->Run();
  CHECK(result.IsEmpty());
  CHECK(try_catch.HasCaught());
  String::AsciiValue exception_value(try_catch.Exception());
  CHECK_EQ(*exception_value, "panama!");
}


bool message_received;


static void check_message(v8::Handle<v8::Message> message,
                          v8::Handle<Value> data) {
  CHECK_EQ(5.76, data->NumberValue());
  CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
  message_received = true;
}


THREADED_TEST(MessageHandlerData) {
  message_received = false;
  v8::HandleScope scope;
  CHECK(!message_received);
  v8::V8::AddMessageListener(check_message, v8_num(5.76));
  LocalContext context;
  v8::ScriptOrigin origin =
      v8::ScriptOrigin(v8_str("6.75"));
  Script::Compile(v8_str("throw 'error'"), &origin)->Run();
  CHECK(message_received);
  // clear out the message listener
  v8::V8::RemoveMessageListeners(check_message);
}


THREADED_TEST(GetSetProperty) {
  v8::HandleScope scope;
  LocalContext context;
  context->Global()->Set(v8_str("foo"), v8_num(14));
  context->Global()->Set(v8_str("12"), v8_num(92));
  context->Global()->Set(v8::Integer::New(16), v8_num(32));
  context->Global()->Set(v8_num(13), v8_num(56));
  Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
  CHECK_EQ(14, foo->Int32Value());
  Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
  CHECK_EQ(92, twelve->Int32Value());
  Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
  CHECK_EQ(32, sixteen->Int32Value());
  Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
  CHECK_EQ(56, thirteen->Int32Value());
  CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
  CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
  CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
  CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
  CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
  CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
  CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
  CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
  CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
}


THREADED_TEST(PropertyAttributes) {
  v8::HandleScope scope;
  LocalContext context;
  // read-only
  Local<String> prop = v8_str("read_only");
  context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
  CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
  Script::Compile(v8_str("read_only = 9"))->Run();
  CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
  context->Global()->Set(prop, v8_num(10));
  CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
  // dont-delete
  prop = v8_str("dont_delete");
  context->Global()->Set(prop, v8_num(13), v8::DontDelete);
  CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
  Script::Compile(v8_str("delete dont_delete"))->Run();
  CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
}


THREADED_TEST(Array) {
  v8::HandleScope scope;
  LocalContext context;
  Local<v8::Array> array = v8::Array::New();
  CHECK_EQ(0, array->Length());
  CHECK(array->Get(v8::Integer::New(0))->IsUndefined());
  CHECK(!array->Has(0));
  CHECK(array->Get(v8::Integer::New(100))->IsUndefined());
  CHECK(!array->Has(100));
  array->Set(v8::Integer::New(2), v8_num(7));
  CHECK_EQ(3, array->Length());
  CHECK(!array->Has(0));
  CHECK(!array->Has(1));
  CHECK(array->Has(2));
  CHECK_EQ(7, array->Get(v8::Integer::New(2))->Int32Value());
  Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
  Local<v8::Array> arr = Local<v8::Array>::Cast(obj);
  CHECK_EQ(3, arr->Length());
  CHECK_EQ(1, arr->Get(v8::Integer::New(0))->Int32Value());
  CHECK_EQ(2, arr->Get(v8::Integer::New(1))->Int32Value());
  CHECK_EQ(3, arr->Get(v8::Integer::New(2))->Int32Value());
}


v8::Handle<Value> HandleF(const v8::Arguments& args) {
  v8::HandleScope scope;
  ApiTestFuzzer::Fuzz();
  Local<v8::Array> result = v8::Array::New(args.Length());
  for (int i = 0; i < args.Length(); i++)
    result->Set(v8::Integer::New(i), args[i]);
  return scope.Close(result);
}


THREADED_TEST(Vector) {
  v8::HandleScope scope;
  Local<ObjectTemplate> global = ObjectTemplate::New();
  global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
  LocalContext context(0, global);

  const char* fun = "f()";
  Local<v8::Array> a0 =
      Local<v8::Array>::Cast(Script::Compile(String::New(fun))->Run());
  CHECK_EQ(0, a0->Length());

  const char* fun2 = "f(11)";
  Local<v8::Array> a1 =
      Local<v8::Array>::Cast(Script::Compile(String::New(fun2))->Run());
  CHECK_EQ(1, a1->Length());
  CHECK_EQ(11, a1->Get(v8::Integer::New(0))->Int32Value());

  const char* fun3 = "f(12, 13)";
  Local<v8::Array> a2 =
      Local<v8::Array>::Cast(Script::Compile(String::New(fun3))->Run());
  CHECK_EQ(2, a2->Length());
  CHECK_EQ(12, a2->Get(v8::Integer::New(0))->Int32Value());
  CHECK_EQ(13, a2->Get(v8::Integer::New(1))->Int32Value());

  const char* fun4 = "f(14, 15, 16)";
  Local<v8::Array> a3 =
      Local<v8::Array>::Cast(Script::Compile(String::New(fun4))->Run());
  CHECK_EQ(3, a3->Length());
  CHECK_EQ(14, a3->Get(v8::Integer::New(0))->Int32Value());
  CHECK_EQ(15, a3->Get(v8::Integer::New(1))->Int32Value());
  CHECK_EQ(16, a3->Get(v8::Integer::New(2))->Int32Value());

  const char* fun5 = "f(17, 18, 19, 20)";
  Local<v8::Array> a4 =
      Local<v8::Array>::Cast(Script::Compile(String::New(fun5))->Run());
  CHECK_EQ(4, a4->Length());
  CHECK_EQ(17, a4->Get(v8::Integer::New(0))->Int32Value());
  CHECK_EQ(18, a4->Get(v8::Integer::New(1))->Int32Value());
  CHECK_EQ(19, a4->Get(v8::Integer::New(2))->Int32Value());
  CHECK_EQ(20, a4->Get(v8::Integer::New(3))->Int32Value());
}


THREADED_TEST(FunctionCall) {
  v8::HandleScope scope;
  LocalContext context;
  CompileRun(
    "function Foo() {"
    "  var result = [];"
    "  for (var i = 0; i < arguments.length; i++) {"
    "    result.push(arguments[i]);"
    "  }"
    "  return result;"
    "}");
  Local<Function> Foo =
      Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));

  v8::Handle<Value>* args0 = NULL;
  Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
  CHECK_EQ(0, a0->Length());

  v8::Handle<Value> args1[] = { v8_num(1.1) };
  Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
  CHECK_EQ(1, a1->Length());
  CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());

  v8::Handle<Value> args2[] = { v8_num(2.2),
                                v8_num(3.3) };
  Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
  CHECK_EQ(2, a2->Length());
  CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
  CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());

  v8::Handle<Value> args3[] = { v8_num(4.4),
                                v8_num(5.5),
                                v8_num(6.6) };
  Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
  CHECK_EQ(3, a3->Length());
  CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
  CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
  CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());

  v8::Handle<Value> args4[] = { v8_num(7.7),
                                v8_num(8.8),
                                v8_num(9.9),
                                v8_num(10.11) };
  Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
  CHECK_EQ(4, a4->Length());
  CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
  CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
  CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
  CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
}


static const char* js_code_causing_out_of_memory =
    "var a = new Array(); while(true) a.push(a);";


// These tests run for a long time and prevent us from running tests
// that come after them so they cannot run in parallel.
TEST(OutOfMemory) {
  // It's not possible to read a snapshot into a heap with different dimensions.
  if (v8::internal::Snapshot::IsEnabled()) return;
  // Set heap limits.
  static const int K = 1024;
  v8::ResourceConstraints constraints;
  constraints.set_max_young_space_size(256 * K);
  constraints.set_max_old_space_size(4 * K * K);
  v8::SetResourceConstraints(&constraints);

  // Execute a script that causes out of memory.
  v8::HandleScope scope;
  LocalContext context;
  v8::V8::IgnoreOutOfMemoryException();
  Local<Script> script =
      Script::Compile(String::New(js_code_causing_out_of_memory));
  Local<Value> result = script->Run();

  // Check for out of memory state.
  CHECK(result.IsEmpty());
  CHECK(context->HasOutOfMemoryException());
}


v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();

  v8::HandleScope scope;
  LocalContext context;
  Local<Script> script =
      Script::Compile(String::New(js_code_causing_out_of_memory));
  Local<Value> result = script->Run();

  // Check for out of memory state.
  CHECK(result.IsEmpty());
  CHECK(context->HasOutOfMemoryException());

  return result;
}


TEST(OutOfMemoryNested) {
  // It's not possible to read a snapshot into a heap with different dimensions.
  if (v8::internal::Snapshot::IsEnabled()) return;
  // Set heap limits.
  static const int K = 1024;
  v8::ResourceConstraints constraints;
  constraints.set_max_young_space_size(256 * K);
  constraints.set_max_old_space_size(4 * K * K);
  v8::SetResourceConstraints(&constraints);

  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->Set(v8_str("ProvokeOutOfMemory"),
             v8::FunctionTemplate::New(ProvokeOutOfMemory));
  LocalContext context(0, templ);
  v8::V8::IgnoreOutOfMemoryException();
  Local<Value> result = CompileRun(
    "var thrown = false;"
    "try {"
    "  ProvokeOutOfMemory();"
    "} catch (e) {"
    "  thrown = true;"
    "}");
  // Check for out of memory state.
  CHECK(result.IsEmpty());
  CHECK(context->HasOutOfMemoryException());
}


TEST(HugeConsStringOutOfMemory) {
  // It's not possible to read a snapshot into a heap with different dimensions.
  if (v8::internal::Snapshot::IsEnabled()) return;
  v8::HandleScope scope;
  LocalContext context;
  // Set heap limits.
  static const int K = 1024;
  v8::ResourceConstraints constraints;
  constraints.set_max_young_space_size(256 * K);
  constraints.set_max_old_space_size(2 * K * K);
  v8::SetResourceConstraints(&constraints);

  // Execute a script that causes out of memory.
  v8::V8::IgnoreOutOfMemoryException();

  // Build huge string. This should fail with out of memory exception.
  Local<Value> result = CompileRun(
    "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
    "for (var i = 0; i < 21; i++) { str = str + str; }");

  // Check for out of memory state.
  CHECK(result.IsEmpty());
  CHECK(context->HasOutOfMemoryException());
}


THREADED_TEST(ConstructCall) {
  v8::HandleScope scope;
  LocalContext context;
  CompileRun(
    "function Foo() {"
    "  var result = [];"
    "  for (var i = 0; i < arguments.length; i++) {"
    "    result.push(arguments[i]);"
    "  }"
    "  return result;"
    "}");
  Local<Function> Foo =
      Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));

  v8::Handle<Value>* args0 = NULL;
  Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
  CHECK_EQ(0, a0->Length());

  v8::Handle<Value> args1[] = { v8_num(1.1) };
  Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
  CHECK_EQ(1, a1->Length());
  CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());

  v8::Handle<Value> args2[] = { v8_num(2.2),
                                v8_num(3.3) };
  Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
  CHECK_EQ(2, a2->Length());
  CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
  CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());

  v8::Handle<Value> args3[] = { v8_num(4.4),
                                v8_num(5.5),
                                v8_num(6.6) };
  Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
  CHECK_EQ(3, a3->Length());
  CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
  CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
  CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());

  v8::Handle<Value> args4[] = { v8_num(7.7),
                                v8_num(8.8),
                                v8_num(9.9),
                                v8_num(10.11) };
  Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
  CHECK_EQ(4, a4->Length());
  CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
  CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
  CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
  CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
}


static void CheckUncle(v8::TryCatch* try_catch) {
  CHECK(try_catch->HasCaught());
  String::AsciiValue str_value(try_catch->Exception());
  CHECK_EQ(*str_value, "uncle?");
  try_catch->Reset();
}


THREADED_TEST(ConversionException) {
  v8::HandleScope scope;
  LocalContext env;
  CompileRun(
    "function TestClass() { };"
    "TestClass.prototype.toString = function () { throw 'uncle?'; };"
    "var obj = new TestClass();");
  Local<Value> obj = env->Global()->Get(v8_str("obj"));

  v8::TryCatch try_catch;

  Local<Value> to_string_result = obj->ToString();
  CHECK(to_string_result.IsEmpty());
  CheckUncle(&try_catch);

  Local<Value> to_number_result = obj->ToNumber();
  CHECK(to_number_result.IsEmpty());
  CheckUncle(&try_catch);

  Local<Value> to_integer_result = obj->ToInteger();
  CHECK(to_integer_result.IsEmpty());
  CheckUncle(&try_catch);

  Local<Value> to_uint32_result = obj->ToUint32();
  CHECK(to_uint32_result.IsEmpty());
  CheckUncle(&try_catch);

  Local<Value> to_int32_result = obj->ToInt32();
  CHECK(to_int32_result.IsEmpty());
  CheckUncle(&try_catch);

  Local<Value> to_object_result = v8::Undefined()->ToObject();
  CHECK(to_object_result.IsEmpty());
  CHECK(try_catch.HasCaught());
  try_catch.Reset();

  int32_t int32_value = obj->Int32Value();
  CHECK_EQ(0, int32_value);
  CheckUncle(&try_catch);

  uint32_t uint32_value = obj->Uint32Value();
  CHECK_EQ(0, uint32_value);
  CheckUncle(&try_catch);

  double number_value = obj->NumberValue();
  CHECK_NE(0, IsNaN(number_value));
  CheckUncle(&try_catch);

  int64_t integer_value = obj->IntegerValue();
  CHECK_EQ(0.0, static_cast<double>(integer_value));
  CheckUncle(&try_catch);
}


v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8::ThrowException(v8_str("konto"));
}


THREADED_TEST(APICatch) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->Set(v8_str("ThrowFromC"),
             v8::FunctionTemplate::New(ThrowFromC));
  LocalContext context(0, templ);
  CompileRun(
    "var thrown = false;"
    "try {"
    "  ThrowFromC();"
    "} catch (e) {"
    "  thrown = true;"
    "}");
  Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
  CHECK(thrown->BooleanValue());
}


THREADED_TEST(ExternalScriptException) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->Set(v8_str("ThrowFromC"),
             v8::FunctionTemplate::New(ThrowFromC));
  LocalContext context(0, templ);

  v8::TryCatch try_catch;
  Local<Script> script
      = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
  Local<Value> result = script->Run();
  CHECK(result.IsEmpty());
  CHECK(try_catch.HasCaught());
  String::AsciiValue exception_value(try_catch.Exception());
  CHECK_EQ("konto", *exception_value);
}



v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(4, args.Length());
  int count = args[0]->Int32Value();
  int cInterval = args[2]->Int32Value();
  if (count == 0) {
    return v8::ThrowException(v8_str("FromC"));
  } else {
    Local<v8::Object> global = Context::GetCurrent()->Global();
    Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
    v8::Handle<Value> argv[] = { v8_num(count - 1),
                                 args[1],
                                 args[2],
                                 args[3] };
    if (count % cInterval == 0) {
      v8::TryCatch try_catch;
      Local<Value> result =
          v8::Handle<Function>::Cast(fun)->Call(global, 4, argv);
      int expected = args[3]->Int32Value();
      if (try_catch.HasCaught()) {
        CHECK_EQ(expected, count);
        CHECK(!i::Top::has_scheduled_exception());
      } else {
        CHECK_NE(expected, count);
      }
      return result;
    } else {
      return v8::Handle<Function>::Cast(fun)->Call(global, 4, argv);
    }
  }
}


v8::Handle<Value> JSCheck(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(3, args.Length());
  bool equality = args[0]->BooleanValue();
  int count = args[1]->Int32Value();
  int expected = args[2]->Int32Value();
  if (equality) {
    CHECK_EQ(count, expected);
  } else {
    CHECK_NE(count, expected);
  }
  return v8::Undefined();
}


// This test works by making a stack of alternating JavaScript and C
// activations.  These activations set up exception handlers with regular
// intervals, one interval for C activations and another for JavaScript
// activations.  When enough activations have been created an exception is
// thrown and we check that the right activation catches the exception and that
// no other activations do.  The right activation is always the topmost one with
// a handler, regardless of whether it is in JavaScript or C.
//
// The notation used to describe a test case looks like this:
//
//    *JS[4] *C[3] @JS[2] C[1] JS[0]
//
// Each entry is an activation, either JS or C.  The index is the count at that
// level.  Stars identify activations with exception handlers, the @ identifies
// the exception handler that should catch the exception.
THREADED_TEST(ExceptionOrder) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
  templ->Set(v8_str("CThrowCountDown"),
             v8::FunctionTemplate::New(CThrowCountDown));
  LocalContext context(0, templ);
  CompileRun(
    "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
    "  if (count == 0) throw 'FromJS';"
    "  if (count % jsInterval == 0) {"
    "    try {"
    "      var value = CThrowCountDown(count - 1,"
    "                                  jsInterval,"
    "                                  cInterval,"
    "                                  expected);"
    "      check(false, count, expected);"
    "      return value;"
    "    } catch (e) {"
    "      check(true, count, expected);"
    "    }"
    "  } else {"
    "    return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
    "  }"
    "}");
  Local<Function> fun =
      Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));

  const int argc = 4;
  //                             count      jsInterval cInterval  expected

  // *JS[4] *C[3] @JS[2] C[1] JS[0]
  v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
  fun->Call(fun, argc, a0);

  // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
  v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
  fun->Call(fun, argc, a1);

  // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
  v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
  fun->Call(fun, argc, a2);

  // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
  v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
  fun->Call(fun, argc, a3);

  // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
  v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
  fun->Call(fun, argc, a4);

  // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
  v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
  fun->Call(fun, argc, a5);
}


v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(1, args.Length());
  return v8::ThrowException(args[0]);
}


THREADED_TEST(ThrowValues) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
  LocalContext context(0, templ);
  v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
    "function Run(obj) {"
    "  try {"
    "    Throw(obj);"
    "  } catch (e) {"
    "    return e;"
    "  }"
    "  return 'no exception';"
    "}"
    "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
  CHECK_EQ(5, result->Length());
  CHECK(result->Get(v8::Integer::New(0))->IsString());
  CHECK(result->Get(v8::Integer::New(1))->IsNumber());
  CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
  CHECK(result->Get(v8::Integer::New(2))->IsNumber());
  CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
  CHECK(result->Get(v8::Integer::New(3))->IsNull());
  CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
}


THREADED_TEST(CatchZero) {
  v8::HandleScope scope;
  LocalContext context;
  v8::TryCatch try_catch;
  CHECK(!try_catch.HasCaught());
  Script::Compile(v8_str("throw 10"))->Run();
  CHECK(try_catch.HasCaught());
  CHECK_EQ(10, try_catch.Exception()->Int32Value());
  try_catch.Reset();
  CHECK(!try_catch.HasCaught());
  Script::Compile(v8_str("throw 0"))->Run();
  CHECK(try_catch.HasCaught());
  CHECK_EQ(0, try_catch.Exception()->Int32Value());
}


THREADED_TEST(CatchExceptionFromWith) {
  v8::HandleScope scope;
  LocalContext context;
  v8::TryCatch try_catch;
  CHECK(!try_catch.HasCaught());
  Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
  CHECK(try_catch.HasCaught());
}


THREADED_TEST(Equality) {
  v8::HandleScope scope;
  LocalContext context;
  // Check that equality works at all before relying on CHECK_EQ
  CHECK(v8_str("a")->Equals(v8_str("a")));
  CHECK(!v8_str("a")->Equals(v8_str("b")));

  CHECK_EQ(v8_str("a"), v8_str("a"));
  CHECK_NE(v8_str("a"), v8_str("b"));
  CHECK_EQ(v8_num(1), v8_num(1));
  CHECK_EQ(v8_num(1.00), v8_num(1));
  CHECK_NE(v8_num(1), v8_num(2));

  // Assume String is not symbol.
  CHECK(v8_str("a")->StrictEquals(v8_str("a")));
  CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
  CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
  CHECK(v8_num(1)->StrictEquals(v8_num(1)));
  CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
  CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
  Local<Value> not_a_number = v8_num(i::OS::nan_value());
  CHECK(!not_a_number->StrictEquals(not_a_number));
  CHECK(v8::False()->StrictEquals(v8::False()));
  CHECK(!v8::False()->StrictEquals(v8::Undefined()));

  v8::Handle<v8::Object> obj = v8::Object::New();
  v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
  CHECK(alias->StrictEquals(obj));
  alias.Dispose();
}


THREADED_TEST(MultiRun) {
  v8::HandleScope scope;
  LocalContext context;
  Local<Script> script = Script::Compile(v8_str("x"));
  for (int i = 0; i < 10; i++)
    script->Run();
}


static v8::Handle<Value> GetXValue(Local<String> name,
                                   const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(info.Data(), v8_str("donut"));
  CHECK_EQ(name, v8_str("x"));
  return name;
}


THREADED_TEST(SimplePropertyRead) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
  LocalContext context;
  context->Global()->Set(v8_str("obj"), templ->NewInstance());
  Local<Script> script = Script::Compile(v8_str("obj.x"));
  for (int i = 0; i < 10; i++) {
    Local<Value> result = script->Run();
    CHECK_EQ(result, v8_str("x"));
  }
}


v8::Persistent<Value> xValue;


static void SetXValue(Local<String> name,
                      Local<Value> value,
                      const AccessorInfo& info) {
  CHECK_EQ(value, v8_num(4));
  CHECK_EQ(info.Data(), v8_str("donut"));
  CHECK_EQ(name, v8_str("x"));
  CHECK(xValue.IsEmpty());
  xValue = v8::Persistent<Value>::New(value);
}


THREADED_TEST(SimplePropertyWrite) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
  LocalContext context;
  context->Global()->Set(v8_str("obj"), templ->NewInstance());
  Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
  for (int i = 0; i < 10; i++) {
    CHECK(xValue.IsEmpty());
    script->Run();
    CHECK_EQ(v8_num(4), xValue);
    xValue.Dispose();
    xValue = v8::Persistent<Value>();
  }
}


static v8::Handle<Value> XPropertyGetter(Local<String> property,
                                         const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(info.Data()->IsUndefined());
  return property;
}


THREADED_TEST(NamedInterceporPropertyRead) {
  v8::HandleScope scope;
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(XPropertyGetter);
  LocalContext context;
  context->Global()->Set(v8_str("obj"), templ->NewInstance());
  Local<Script> script = Script::Compile(v8_str("obj.x"));
  for (int i = 0; i < 10; i++) {
    Local<Value> result = script->Run();
    CHECK_EQ(result, v8_str("x"));
  }
}

THREADED_TEST(MultiContexts) {
  v8::HandleScope scope;
  v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
  templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));

  Local<String> password = v8_str("Password");

  // Create an environment
  LocalContext context0(0, templ);
  context0->SetSecurityToken(password);
  v8::Handle<v8::Object> global0 = context0->Global();
  global0->Set(v8_str("custom"), v8_num(1234));
  CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());

  // Create an independent environment
  LocalContext context1(0, templ);
  context1->SetSecurityToken(password);
  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("custom"), v8_num(1234));
  CHECK_NE(global0, global1);
  CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
  CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());

  // Now create a new context with the old global
  LocalContext context2(0, templ, global1);
  context2->SetSecurityToken(password);
  v8::Handle<v8::Object> global2 = context2->Global();
  CHECK_EQ(global1, global2);
  CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
  CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
}


THREADED_TEST(FunctionPrototypeAcrossContexts) {
  // Make sure that functions created by cloning boilerplates cannot
  // communicate through their __proto__ field.

  v8::HandleScope scope;

  LocalContext env0;
  v8::Handle<v8::Object> global0 =
      env0->Global();
  v8::Handle<v8::Object> object0 =
      v8::Handle<v8::Object>::Cast(global0->Get(v8_str("Object")));
  v8::Handle<v8::Object> tostring0 =
      v8::Handle<v8::Object>::Cast(object0->Get(v8_str("toString")));
  v8::Handle<v8::Object> proto0 =
      v8::Handle<v8::Object>::Cast(tostring0->Get(v8_str("__proto__")));
  proto0->Set(v8_str("custom"), v8_num(1234));

  LocalContext env1;
  v8::Handle<v8::Object> global1 =
      env1->Global();
  v8::Handle<v8::Object> object1 =
      v8::Handle<v8::Object>::Cast(global1->Get(v8_str("Object")));
  v8::Handle<v8::Object> tostring1 =
      v8::Handle<v8::Object>::Cast(object1->Get(v8_str("toString")));
  v8::Handle<v8::Object> proto1 =
      v8::Handle<v8::Object>::Cast(tostring1->Get(v8_str("__proto__")));
  CHECK(!proto1->Has(v8_str("custom")));
}


THREADED_TEST(Regress892105) {
  // Make sure that object and array literals created by cloning
  // boilerplates cannot communicate through their __proto__
  // field. This is rather difficult to check, but we try to add stuff
  // to Object.prototype and Array.prototype and create a new
  // environment. This should succeed.

  v8::HandleScope scope;

  Local<String> source = v8_str("Object.prototype.obj = 1234;"
                                "Array.prototype.arr = 4567;"
                                "8901");

  LocalContext env0;
  Local<Script> script0 = Script::Compile(source);
  CHECK_EQ(8901.0, script0->Run()->NumberValue());

  LocalContext env1;
  Local<Script> script1 = Script::Compile(source);
  CHECK_EQ(8901.0, script1->Run()->NumberValue());
}


static void ExpectString(const char* code, const char* expected) {
  Local<Value> result = CompileRun(code);
  CHECK(result->IsString());
  String::AsciiValue ascii(result);
  CHECK_EQ(0, strcmp(*ascii, expected));
}


static void ExpectBoolean(const char* code, bool expected) {
  Local<Value> result = CompileRun(code);
  CHECK(result->IsBoolean());
  CHECK_EQ(expected, result->BooleanValue());
}


static void ExpectObject(const char* code, Local<Value> expected) {
  Local<Value> result = CompileRun(code);
  CHECK(result->Equals(expected));
}


THREADED_TEST(UndetectableObject) {
  v8::HandleScope scope;
  LocalContext env;

  Local<v8::FunctionTemplate> desc =
      v8::FunctionTemplate::New(0, v8::Handle<Value>());
  desc->InstanceTemplate()->MarkAsUndetectable();  // undetectable

  Local<v8::Object> obj = desc->GetFunction()->NewInstance();
  env->Global()->Set(v8_str("undetectable"), obj);

  ExpectString("undetectable.toString()", "[object Object]");
  ExpectString("typeof undetectable", "undefined");
  ExpectString("typeof(undetectable)", "undefined");
  ExpectBoolean("typeof undetectable == 'undefined'", true);
  ExpectBoolean("typeof undetectable == 'object'", false);
  ExpectBoolean("if (undetectable) { true; } else { false; }", false);
  ExpectBoolean("!undetectable", true);

  ExpectObject("true&&undetectable", obj);
  ExpectBoolean("false&&undetectable", false);
  ExpectBoolean("true||undetectable", true);
  ExpectObject("false||undetectable", obj);

  ExpectObject("undetectable&&true", obj);
  ExpectObject("undetectable&&false", obj);
  ExpectBoolean("undetectable||true", true);
  ExpectBoolean("undetectable||false", false);

  ExpectBoolean("undetectable==null", true);
  ExpectBoolean("undetectable==undefined", true);
  ExpectBoolean("undetectable==undetectable", true);

  ExpectBoolean("undetectable===null", false);
  ExpectBoolean("undetectable===undefined", true);
  ExpectBoolean("undetectable===undetectable", true);
}


THREADED_TEST(UndetectableString) {
  v8::HandleScope scope;
  LocalContext env;

  Local<String> obj = String::NewUndetectable("foo");
  env->Global()->Set(v8_str("undetectable"), obj);

  ExpectString("undetectable", "foo");
  ExpectString("typeof undetectable", "undefined");
  ExpectString("typeof(undetectable)", "undefined");
  ExpectBoolean("typeof undetectable == 'undefined'", true);
  ExpectBoolean("typeof undetectable == 'string'", false);
  ExpectBoolean("if (undetectable) { true; } else { false; }", false);
  ExpectBoolean("!undetectable", true);

  ExpectObject("true&&undetectable", obj);
  ExpectBoolean("false&&undetectable", false);
  ExpectBoolean("true||undetectable", true);
  ExpectObject("false||undetectable", obj);

  ExpectObject("undetectable&&true", obj);
  ExpectObject("undetectable&&false", obj);
  ExpectBoolean("undetectable||true", true);
  ExpectBoolean("undetectable||false", false);

  ExpectBoolean("undetectable==null", true);
  ExpectBoolean("undetectable==undefined", true);
  ExpectBoolean("undetectable==undetectable", true);

  ExpectBoolean("undetectable===null", false);
  ExpectBoolean("undetectable===undefined", true);
  ExpectBoolean("undetectable===undetectable", true);
}


template <typename T> static void USE(T) { }


// This test is not intended to be run, just type checked.
static void PersistentHandles() {
  USE(PersistentHandles);
  Local<String> str = v8_str("foo");
  v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
  USE(p_str);
  Local<Script> scr = Script::Compile(v8_str(""));
  v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
  USE(p_scr);
  Local<ObjectTemplate> templ = ObjectTemplate::New();
  v8::Persistent<ObjectTemplate> p_templ =
    v8::Persistent<ObjectTemplate>::New(templ);
  USE(p_templ);
}


static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8::Undefined();
}


THREADED_TEST(GlobalObjectTemplate) {
  v8::HandleScope handle_scope;
  Local<ObjectTemplate> global_template = ObjectTemplate::New();
  global_template->Set(v8_str("JSNI_Log"),
                       v8::FunctionTemplate::New(HandleLogDelegator));
  v8::Persistent<Context> context = Context::New(0, global_template);
  Context::Scope context_scope(context);
  Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
  context.Dispose();
}


static const char* kSimpleExtensionSource =
  "function Foo() {"
  "  return 4;"
  "}";


THREADED_TEST(SimpleExtensions) {
  v8::HandleScope handle_scope;
  v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
  const char* extension_names[] = { "simpletest" };
  v8::ExtensionConfiguration extensions(1, extension_names);
  v8::Handle<Context> context = Context::New(&extensions);
  Context::Scope lock(context);
  v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
  CHECK_EQ(result, v8::Integer::New(4));
}


THREADED_TEST(AutoExtensions) {
  v8::HandleScope handle_scope;
  Extension* extension = new Extension("autotest", kSimpleExtensionSource);
  extension->set_auto_enable(true);
  v8::RegisterExtension(extension);
  v8::Handle<Context> context = Context::New();
  Context::Scope lock(context);
  v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
  CHECK_EQ(result, v8::Integer::New(4));
}


static void CheckDependencies(const char* name, const char* expected) {
  v8::HandleScope handle_scope;
  v8::ExtensionConfiguration config(1, &name);
  LocalContext context(&config);
  CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
}


/*
 * Configuration:
 *
 *     /-- B <--\
 * A <-          -- D <-- E
 *     \-- C <--/
 */
THREADED_TEST(ExtensionDependency) {
  static const char* kEDeps[] = { "D" };
  v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
  static const char* kDDeps[] = { "B", "C" };
  v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
  static const char* kBCDeps[] = { "A" };
  v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
  v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
  v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
  CheckDependencies("A", "undefinedA");
  CheckDependencies("B", "undefinedAB");
  CheckDependencies("C", "undefinedAC");
  CheckDependencies("D", "undefinedABCD");
  CheckDependencies("E", "undefinedABCDE");
  v8::HandleScope handle_scope;
  static const char* exts[2] = { "C", "E" };
  v8::ExtensionConfiguration config(2, exts);
  LocalContext context(&config);
  CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
}


static const char* kExtensionTestScript =
  "native function A();"
  "native function B();"
  "native function C();"
  "function Foo(i) {"
  "  if (i == 0) return A();"
  "  if (i == 1) return B();"
  "  if (i == 2) return C();"
  "}";


static v8::Handle<Value> CallFun(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return args.Data();
}


class FunctionExtension : public Extension {
 public:
  FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
      v8::Handle<String> name);
};


static int lookup_count = 0;
v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
      v8::Handle<String> name) {
  lookup_count++;
  if (name->Equals(v8_str("A"))) {
    return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
  } else if (name->Equals(v8_str("B"))) {
    return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
  } else if (name->Equals(v8_str("C"))) {
    return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
  } else {
    return v8::Handle<v8::FunctionTemplate>();
  }
}


THREADED_TEST(FunctionLookup) {
  v8::RegisterExtension(new FunctionExtension());
  v8::HandleScope handle_scope;
  static const char* exts[1] = { "functiontest" };
  v8::ExtensionConfiguration config(1, exts);
  LocalContext context(&config);
  CHECK_EQ(3, lookup_count);
  CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
  CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
  CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
}


static const char* last_location;
static const char* last_message;
void StoringErrorCallback(const char* location, const char* message) {
  if (last_location == NULL) {
    last_location = location;
    last_message = message;
  }
}


// ErrorReporting creates a circular extensions configuration and
// tests that the fatal error handler gets called.  This renders V8
// unusable and therefore this test cannot be run in parallel.
TEST(ErrorReporting) {
  v8::V8::SetFatalErrorHandler(StoringErrorCallback);
  static const char* aDeps[] = { "B" };
  v8::RegisterExtension(new Extension("A", "", 1, aDeps));
  static const char* bDeps[] = { "A" };
  v8::RegisterExtension(new Extension("B", "", 1, bDeps));
  last_location = NULL;
  v8::ExtensionConfiguration config(1, bDeps);
  v8::Handle<Context> context = Context::New(&config);
  CHECK(context.IsEmpty());
  CHECK_NE(last_location, NULL);
}


static const char* js_code_causing_huge_string_flattening =
    "var str = 'X';"
    "for (var i = 0; i < 29; i++) {"
    "  str = str + str;"
    "}"
    "str.match(/X/);";


void OOMCallback(const char* location, const char* message) {
  exit(0);
}


TEST(RegexpOutOfMemory) {
  // Execute a script that causes out of memory when flattening a string.
  v8::HandleScope scope;
  v8::V8::SetFatalErrorHandler(OOMCallback);
  LocalContext context;
  Local<Script> script =
      Script::Compile(String::New(js_code_causing_huge_string_flattening));
  last_location = NULL;
  Local<Value> result = script->Run();

  CHECK(false);  // Should not return.
}


static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
                                             v8::Handle<Value> data) {
  CHECK_EQ(v8::Undefined(), data);
  CHECK(message->GetScriptResourceName()->IsUndefined());
  CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
  message->GetLineNumber();
  message->GetSourceLine();
}


THREADED_TEST(ErrorWithMissingScriptInfo) {
  v8::HandleScope scope;
  LocalContext context;
  v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
  Script::Compile(v8_str("throw Error()"))->Run();
  v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
}


int global_index = 0;

class Snorkel {
 public:
  Snorkel() { index_ = global_index++; }
  int index_;
};

class Whammy {
 public:
  Whammy() {
    cursor_ = 0;
  }
  ~Whammy() {
    script_.Dispose();
  }
  v8::Handle<Script> getScript() {
    if (script_.IsEmpty())
      script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
    return Local<Script>(*script_);
  }

 public:
  static const int kObjectCount = 256;
  int cursor_;
  v8::Persistent<v8::Object> objects_[kObjectCount];
  v8::Persistent<Script> script_;
};

static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
  Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
  delete snorkel;
  obj.ClearWeak();
}

v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
                                       const AccessorInfo& info) {
  Whammy* whammy =
    static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());

  v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];

  v8::Handle<v8::Object> obj = v8::Object::New();
  v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
  if (!prev.IsEmpty()) {
    prev->Set(v8_str("next"), obj);
    prev.MakeWeak(new Snorkel(), &HandleWeakReference);
    whammy->objects_[whammy->cursor_].Clear();
  }
  whammy->objects_[whammy->cursor_] = global;
  whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
  return whammy->getScript()->Run();
}

THREADED_TEST(WeakReference) {
  v8::HandleScope handle_scope;
  v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
  templ->SetNamedPropertyHandler(WhammyPropertyGetter,
                                 0, 0, 0, 0,
                                 v8::External::New(new Whammy()));
  const char* extension_list[] = { "v8/gc" };
  v8::ExtensionConfiguration extensions(1, extension_list);
  v8::Persistent<Context> context = Context::New(&extensions);
  Context::Scope context_scope(context);

  v8::Handle<v8::Object> interceptor = templ->NewInstance();
  context->Global()->Set(v8_str("whammy"), interceptor);
  const char* code =
      "var last;"
      "for (var i = 0; i < 10000; i++) {"
      "  var obj = whammy.length;"
      "  if (last) last.next = obj;"
      "  last = obj;"
      "}"
      "gc();"
      "4";
  v8::Handle<Value> result = CompileRun(code);
  CHECK_EQ(4.0, result->NumberValue());

  context.Dispose();
}


v8::Handle<Function> args_fun;


static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(args_fun, args.Callee());
  CHECK_EQ(3, args.Length());
  CHECK_EQ(v8::Integer::New(1), args[0]);
  CHECK_EQ(v8::Integer::New(2), args[1]);
  CHECK_EQ(v8::Integer::New(3), args[2]);
  CHECK_EQ(v8::Undefined(), args[3]);
  v8::HandleScope scope;
  i::Heap::CollectAllGarbage();
  return v8::Undefined();
}


THREADED_TEST(Arguments) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
  global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
  LocalContext context(NULL, global);
  args_fun = v8::Handle<Function>::Cast(context->Global()->Get(v8_str("f")));
  v8_compile("f(1, 2, 3)")->Run();
}


static int x_register = 0;
static v8::Handle<v8::Object> x_receiver;
static v8::Handle<v8::Object> x_holder;


static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK_EQ(x_receiver, info.This());
  CHECK_EQ(x_holder, info.Holder());
  return v8_num(x_register);
}


static void XSetter(Local<String> name,
                    Local<Value> value,
                    const AccessorInfo& info) {
  CHECK_EQ(x_holder, info.This());
  CHECK_EQ(x_holder, info.Holder());
  x_register = value->Int32Value();
}


THREADED_TEST(AccessorIC) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetAccessor(v8_str("x"), XGetter, XSetter);
  LocalContext context;
  x_holder = obj->NewInstance();
  context->Global()->Set(v8_str("holder"), x_holder);
  x_receiver = v8::Object::New();
  context->Global()->Set(v8_str("obj"), x_receiver);
  v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun(
    "obj.__proto__ = holder;"
    "var result = [];"
    "for (var i = 0; i < 10; i++) {"
    "  holder.x = i;"
    "  result.push(obj.x);"
    "}"
    "result"));
  CHECK_EQ(10, array->Length());
  for (int i = 0; i < 10; i++) {
    v8::Handle<Value> entry = array->Get(v8::Integer::New(i));
    CHECK_EQ(v8::Integer::New(i), entry);
  }
}


static v8::Handle<Value> NoBlockGetterX(Local<String> name,
                                        const AccessorInfo&) {
  return v8::Handle<Value>();
}


static v8::Handle<Value> NoBlockGetterI(uint32_t index,
                                        const AccessorInfo&) {
  return v8::Handle<Value>();
}


static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
                                        const AccessorInfo&) {
  if (!name->Equals(v8_str("foo"))) {
    return v8::Handle<v8::Boolean>();  // not intercepted
  }

  return v8::False();  // intercepted, and don't delete the property
}


static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
  if (index != 2) {
    return v8::Handle<v8::Boolean>();  // not intercepted
  }

  return v8::False();  // intercepted, and don't delete the property
}


THREADED_TEST(Deleter) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
  obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
  LocalContext context;
  context->Global()->Set(v8_str("k"), obj->NewInstance());
  CompileRun(
    "k.foo = 'foo';"
    "k.bar = 'bar';"
    "k[2] = 2;"
    "k[4] = 4;");
  CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
  CHECK(v8_compile("delete k.bar")->Run()->IsTrue());

  CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
  CHECK(v8_compile("k.bar")->Run()->IsUndefined());

  CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
  CHECK(v8_compile("delete k[4]")->Run()->IsTrue());

  CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
  CHECK(v8_compile("k[4]")->Run()->IsUndefined());
}


static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  return v8::Undefined();
}


static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  v8::Handle<v8::Array> result = v8::Array::New(3);
  result->Set(v8::Integer::New(0), v8_str("foo"));
  result->Set(v8::Integer::New(1), v8_str("bar"));
  result->Set(v8::Integer::New(2), v8_str("baz"));
  return result;
}


static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  v8::Handle<v8::Array> result = v8::Array::New(2);
  result->Set(v8::Integer::New(0), v8_str("hat"));
  result->Set(v8::Integer::New(1), v8_str("gyt"));
  return result;
}


THREADED_TEST(Enumerators) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
  obj->SetIndexedPropertyHandler(NULL, NULL, NULL, NULL, IndexedEnum);
  LocalContext context;
  context->Global()->Set(v8_str("k"), obj->NewInstance());
  v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
    "var result = [];"
    "for (var prop in k) {"
    "  result.push(prop);"
    "}"
    "result"));
  CHECK_EQ(5, result->Length());
  CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(0)));
  CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(1)));
  CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(2)));
  CHECK_EQ(v8_str("hat"), result->Get(v8::Integer::New(3)));
  CHECK_EQ(v8_str("gyt"), result->Get(v8::Integer::New(4)));
}


int p_getter_count;
int p_getter_count2;


static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  p_getter_count++;
  v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
  CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
  if (name->Equals(v8_str("p1"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o1")));
  } else if (name->Equals(v8_str("p2"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o2")));
  } else if (name->Equals(v8_str("p3"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o3")));
  } else if (name->Equals(v8_str("p4"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o4")));
  }
  return v8::Undefined();
}


static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
  ApiTestFuzzer::Fuzz();
  LocalContext context;
  context->Global()->Set(v8_str("o1"), obj->NewInstance());
  CompileRun(
    "o1.__proto__ = { };"
    "var o2 = { __proto__: o1 };"
    "var o3 = { __proto__: o2 };"
    "var o4 = { __proto__: o3 };"
    "for (var i = 0; i < 10; i++) o4.p4;"
    "for (var i = 0; i < 10; i++) o3.p3;"
    "for (var i = 0; i < 10; i++) o2.p2;"
    "for (var i = 0; i < 10; i++) o1.p1;");
}


static v8::Handle<Value> PGetter2(Local<String> name,
                                  const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  p_getter_count2++;
  v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
  CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
  if (name->Equals(v8_str("p1"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o1")));
  } else if (name->Equals(v8_str("p2"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o2")));
  } else if (name->Equals(v8_str("p3"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o3")));
  } else if (name->Equals(v8_str("p4"))) {
    CHECK_EQ(info.This(), global->Get(v8_str("o4")));
  }
  return v8::Undefined();
}


THREADED_TEST(GetterHolders) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetAccessor(v8_str("p1"), PGetter);
  obj->SetAccessor(v8_str("p2"), PGetter);
  obj->SetAccessor(v8_str("p3"), PGetter);
  obj->SetAccessor(v8_str("p4"), PGetter);
  p_getter_count = 0;
  RunHolderTest(obj);
  CHECK_EQ(40, p_getter_count);
}


THREADED_TEST(PreInterceptorHolders) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetNamedPropertyHandler(PGetter2);
  p_getter_count2 = 0;
  RunHolderTest(obj);
  CHECK_EQ(40, p_getter_count2);
}


THREADED_TEST(ObjectInstantiation) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetAccessor(v8_str("t"), PGetter2);
  LocalContext context;
  context->Global()->Set(v8_str("o"), templ->NewInstance());
  for (int i = 0; i < 100; i++) {
    v8::HandleScope inner_scope;
    v8::Handle<v8::Object> obj = templ->NewInstance();
    CHECK_NE(obj, context->Global()->Get(v8_str("o")));
    context->Global()->Set(v8_str("o2"), obj);
    v8::Handle<Value> value =
        Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
    CHECK_EQ(v8::True(), value);
    context->Global()->Set(v8_str("o"), obj);
  }
}


THREADED_TEST(StringWrite) {
  v8::HandleScope scope;
  v8::Handle<String> str = v8_str("abcde");

  char buf[100];
  int len;

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf);
  CHECK_EQ(len, 5);
  CHECK_EQ(strncmp("abcde\0", buf, 6), 0);

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf, 0, 4);
  CHECK_EQ(len, 4);
  CHECK_EQ(strncmp("abcd\1", buf, 5), 0);

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf, 0, 5);
  CHECK_EQ(len, 5);
  CHECK_EQ(strncmp("abcde\1", buf, 6), 0);

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf, 0, 6);
  CHECK_EQ(len, 5);
  CHECK_EQ(strncmp("abcde\0", buf, 6), 0);

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf, 4, -1);
  CHECK_EQ(len, 1);
  CHECK_EQ(strncmp("e\0", buf, 2), 0);

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf, 4, 6);
  CHECK_EQ(len, 1);
  CHECK_EQ(strncmp("e\0", buf, 2), 0);

  memset(buf, 0x1, sizeof(buf));
  len = str->WriteAscii(buf, 4, 1);
  CHECK_EQ(len, 1);
  CHECK_EQ(strncmp("e\1", buf, 2), 0);
}


THREADED_TEST(ToArrayIndex) {
  v8::HandleScope scope;
  LocalContext context;

  v8::Handle<String> str = v8_str("42");
  v8::Handle<v8::Uint32> index = str->ToArrayIndex();
  CHECK(!index.IsEmpty());
  CHECK_EQ(42.0, index->Uint32Value());
  str = v8_str("42asdf");
  index = str->ToArrayIndex();
  CHECK(index.IsEmpty());
  str = v8_str("-42");
  index = str->ToArrayIndex();
  CHECK(index.IsEmpty());
  str = v8_str("4294967295");
  index = str->ToArrayIndex();
  CHECK(!index.IsEmpty());
  CHECK_EQ(4294967295.0, index->Uint32Value());
  v8::Handle<v8::Number> num = v8::Number::New(1);
  index = num->ToArrayIndex();
  CHECK(!index.IsEmpty());
  CHECK_EQ(1.0, index->Uint32Value());
  num = v8::Number::New(-1);
  index = num->ToArrayIndex();
  CHECK(index.IsEmpty());
  v8::Handle<v8::Object> obj = v8::Object::New();
  index = obj->ToArrayIndex();
  CHECK(index.IsEmpty());
}


THREADED_TEST(ErrorConstruction) {
  v8::HandleScope scope;
  LocalContext context;

  v8::Handle<String> foo = v8_str("foo");
  v8::Handle<String> message = v8_str("message");
  v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
  CHECK(range_error->IsObject());
  v8::Handle<v8::Object> range_obj(v8::Handle<v8::Object>::Cast(range_error));
  CHECK(v8::Handle<v8::Object>::Cast(range_error)->Get(message)->Equals(foo));
  v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
  CHECK(reference_error->IsObject());
  CHECK(
      v8::Handle<v8::Object>::Cast(reference_error)->Get(message)->Equals(foo));
  v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
  CHECK(syntax_error->IsObject());
  CHECK(v8::Handle<v8::Object>::Cast(syntax_error)->Get(message)->Equals(foo));
  v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
  CHECK(type_error->IsObject());
  CHECK(v8::Handle<v8::Object>::Cast(type_error)->Get(message)->Equals(foo));
  v8::Handle<Value> error = v8::Exception::Error(foo);
  CHECK(error->IsObject());
  CHECK(v8::Handle<v8::Object>::Cast(error)->Get(message)->Equals(foo));
}


static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  return v8_num(10);
}


static void YSetter(Local<String> name,
                    Local<Value> value,
                    const AccessorInfo& info) {
  if (info.This()->Has(name)) {
    info.This()->Delete(name);
  }
  info.This()->Set(name, value);
}


THREADED_TEST(DeleteAccessor) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
  obj->SetAccessor(v8_str("y"), YGetter, YSetter);
  LocalContext context;
  v8::Handle<v8::Object> holder = obj->NewInstance();
  context->Global()->Set(v8_str("holder"), holder);
  v8::Handle<Value> result = CompileRun(
      "holder.y = 11; holder.y = 12; holder.y");
  CHECK_EQ(12, result->Uint32Value());
}


THREADED_TEST(TypeSwitch) {
  v8::HandleScope scope;
  v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
  v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
  v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
  v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
  v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
  LocalContext context;
  v8::Handle<v8::Object> obj0 = v8::Object::New();
  v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
  v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
  v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
  for (int i = 0; i < 10; i++) {
    CHECK_EQ(0, type_switch->match(obj0));
    CHECK_EQ(1, type_switch->match(obj1));
    CHECK_EQ(2, type_switch->match(obj2));
    CHECK_EQ(3, type_switch->match(obj3));
    CHECK_EQ(3, type_switch->match(obj3));
    CHECK_EQ(2, type_switch->match(obj2));
    CHECK_EQ(1, type_switch->match(obj1));
    CHECK_EQ(0, type_switch->match(obj0));
  }
}


// For use within the TestSecurityHandler() test.
static bool g_security_callback_result = false;
static bool NamedSecurityTestCallback(Local<v8::Object> global,
                                      Local<Value> name,
                                      v8::AccessType type,
                                      Local<Value> data) {
  // Always allow read access.
  if (type == v8::ACCESS_GET)
    return true;

  // Sometimes allow other access.
  return g_security_callback_result;
}


static bool IndexedSecurityTestCallback(Local<v8::Object> global,
                                        uint32_t key,
                                        v8::AccessType type,
                                        Local<Value> data) {
  // Always allow read access.
  if (type == v8::ACCESS_GET)
    return true;

  // Sometimes allow other access.
  return g_security_callback_result;
}


static int trouble_nesting = 0;
static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  trouble_nesting++;

  // Call a JS function that throws an uncaught exception.
  Local<v8::Object> arg_this = Context::GetCurrent()->Global();
  Local<Value> trouble_callee = (trouble_nesting == 3) ?
    arg_this->Get(v8_str("trouble_callee")) :
    arg_this->Get(v8_str("trouble_caller"));
  CHECK(trouble_callee->IsFunction());
  return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
}


static int report_count = 0;
static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
                                             v8::Handle<Value>) {
  report_count++;
}


// Counts uncaught exceptions, but other tests running in parallel
// also have uncaught exceptions.
TEST(ApiUncaughtException) {
  v8::HandleScope scope;
  LocalContext env;
  v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);

  Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
  v8::Local<v8::Object> global = env->Global();
  global->Set(v8_str("trouble"), fun->GetFunction());

  Script::Compile(v8_str("function trouble_callee() {"
                         "  var x = null;"
                         "  return x.foo;"
                         "};"
                         "function trouble_caller() {"
                         "  trouble();"
                         "};"))->Run();
  Local<Value> trouble = global->Get(v8_str("trouble"));
  CHECK(trouble->IsFunction());
  Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
  CHECK(trouble_callee->IsFunction());
  Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
  CHECK(trouble_caller->IsFunction());
  Function::Cast(*trouble_caller)->Call(global, 0, NULL);
  CHECK_EQ(1, report_count);
}


// SecurityHandler can't be run twice
TEST(SecurityHandler) {
  v8::HandleScope scope0;
  v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
  global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
                                           IndexedSecurityTestCallback);
  // Create an environment
  v8::Persistent<Context> context0 =
    Context::New(NULL, global_template);
  context0->Enter();

  v8::Handle<v8::Object> global0 = context0->Global();
  v8::Handle<Script> script0 = v8_compile("foo = 111");
  script0->Run();
  global0->Set(v8_str("0"), v8_num(999));
  v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
  CHECK_EQ(111, foo0->Int32Value());
  v8::Handle<Value> z0 = global0->Get(v8_str("0"));
  CHECK_EQ(999, z0->Int32Value());

  // Create another environment, should fail security checks.
  v8::HandleScope scope1;

  v8::Persistent<Context> context1 =
    Context::New(NULL, global_template);
  context1->Enter();

  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("othercontext"), global0);
  // This set will fail the security check.
  v8::Handle<Script> script1 =
    v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
  script1->Run();
  // This read will pass the security check.
  v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
  CHECK_EQ(111, foo1->Int32Value());
  // This read will pass the security check.
  v8::Handle<Value> z1 = global0->Get(v8_str("0"));
  CHECK_EQ(999, z1->Int32Value());

  // Create another environment, should pass security checks.
  { g_security_callback_result = true;  // allow security handler to pass.
    v8::HandleScope scope2;
    LocalContext context2;
    v8::Handle<v8::Object> global2 = context2->Global();
    global2->Set(v8_str("othercontext"), global0);
    v8::Handle<Script> script2 =
        v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
    script2->Run();
    v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
    CHECK_EQ(333, foo2->Int32Value());
    v8::Handle<Value> z2 = global0->Get(v8_str("0"));
    CHECK_EQ(888, z2->Int32Value());
  }

  context1->Exit();
  context1.Dispose();

  context0->Exit();
  context0.Dispose();
}


THREADED_TEST(SecurityChecks) {
  v8::HandleScope handle_scope;
  LocalContext env1;
  v8::Persistent<Context> env2 = Context::New();

  Local<Value> foo = v8_str("foo");
  Local<Value> bar = v8_str("bar");

  // Set to the same domain.
  env1->SetSecurityToken(foo);

  // Create a function in env1.
  Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
  Local<Value> spy = env1->Global()->Get(v8_str("spy"));
  CHECK(spy->IsFunction());

  // Create another function accessing global objects.
  Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
  Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
  CHECK(spy2->IsFunction());

  // Switch to env2 in the same domain and invoke spy on env2.
  {
    env2->SetSecurityToken(foo);
    // Enter env2
    Context::Scope scope_env2(env2);
    Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
    CHECK(result->IsFunction());
  }

  {
    env2->SetSecurityToken(bar);
    Context::Scope scope_env2(env2);

    // Call cross_domain_call, it should throw an exception
    v8::TryCatch try_catch;
    Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
    CHECK(try_catch.HasCaught());
  }

  env2.Dispose();
}


// Regression test case for issue 1183439.
THREADED_TEST(SecurityChecksForPrototypeChain) {
  v8::HandleScope scope;
  LocalContext current;
  v8::Persistent<Context> other = Context::New();

  // Change context to be able to get to the Object function in the
  // other context without hitting the security checks.
  v8::Local<Value> other_object;
  { Context::Scope scope(other);
    other_object = other->Global()->Get(v8_str("Object"));
    other->Global()->Set(v8_num(42), v8_num(87));
  }

  current->Global()->Set(v8_str("other"), other->Global());
  CHECK(v8_compile("other")->Run()->Equals(other->Global()));

  // Make sure the security check fails here and we get an undefined
  // result instead of getting the Object function. Repeat in a loop
  // to make sure to exercise the IC code.
  v8::Local<Script> access_other0 = v8_compile("other.Object");
  v8::Local<Script> access_other1 = v8_compile("other[42]");
  for (int i = 0; i < 5; i++) {
    CHECK(!access_other0->Run()->Equals(other_object));
    CHECK(access_other0->Run()->IsUndefined());
    CHECK(!access_other1->Run()->Equals(v8_num(87)));
    CHECK(access_other1->Run()->IsUndefined());
  }

  // Create an object that has 'other' in its prototype chain and make
  // sure we cannot access the Object function indirectly through
  // that. Repeat in a loop to make sure to exercise the IC code.
  v8_compile("function F() { };"
             "F.prototype = other;"
             "var f = new F();")->Run();
  v8::Local<Script> access_f0 = v8_compile("f.Object");
  v8::Local<Script> access_f1 = v8_compile("f[42]");
  for (int j = 0; j < 5; j++) {
    CHECK(!access_f0->Run()->Equals(other_object));
    CHECK(access_f0->Run()->IsUndefined());
    CHECK(!access_f1->Run()->Equals(v8_num(87)));
    CHECK(access_f1->Run()->IsUndefined());
  }

  // Now it gets hairy: Set the prototype for the other global object
  // to be the current global object. The prototype chain for 'f' now
  // goes through 'other' but ends up in the current global object.
  { Context::Scope scope(other);
    other->Global()->Set(v8_str("__proto__"), current->Global());
  }
  // Set a named and an index property on the current global
  // object. To force the lookup to go through the other global object,
  // the properties must not exist in the other global object.
  current->Global()->Set(v8_str("foo"), v8_num(100));
  current->Global()->Set(v8_num(99), v8_num(101));
  // Try to read the properties from f and make sure that the access
  // gets stopped by the security checks on the other global object.
  Local<Script> access_f2 = v8_compile("f.foo");
  Local<Script> access_f3 = v8_compile("f[99]");
  for (int k = 0; k < 5; k++) {
    CHECK(!access_f2->Run()->Equals(v8_num(100)));
    CHECK(access_f2->Run()->IsUndefined());
    CHECK(!access_f3->Run()->Equals(v8_num(101)));
    CHECK(access_f3->Run()->IsUndefined());
  }
  other.Dispose();
}


THREADED_TEST(CrossDomainDelete) {
  v8::HandleScope handle_scope;
  LocalContext env1;
  v8::Persistent<Context> env2 = Context::New();

  Local<Value> foo = v8_str("foo");
  Local<Value> bar = v8_str("bar");

  // Set to the same domain.
  env1->SetSecurityToken(foo);
  env2->SetSecurityToken(foo);

  env1->Global()->Set(v8_str("prop"), v8_num(3));
  env2->Global()->Set(v8_str("env1"), env1->Global());

  // Change env2 to a different domain and delete env1.prop.
  env2->SetSecurityToken(bar);
  {
    Context::Scope scope_env2(env2);
    Local<Value> result =
        Script::Compile(v8_str("delete env1.prop"))->Run();
    CHECK(result->IsFalse());
  }

  // Check that env1.prop still exists.
  Local<Value> v = env1->Global()->Get(v8_str("prop"));
  CHECK(v->IsNumber());
  CHECK_EQ(3, v->Int32Value());

  env2.Dispose();
}


THREADED_TEST(CrossDomainForIn) {
  v8::HandleScope handle_scope;
  LocalContext env1;
  v8::Persistent<Context> env2 = Context::New();

  Local<Value> foo = v8_str("foo");
  Local<Value> bar = v8_str("bar");

  // Set to the same domain.
  env1->SetSecurityToken(foo);
  env2->SetSecurityToken(foo);

  env1->Global()->Set(v8_str("prop"), v8_num(3));
  env2->Global()->Set(v8_str("env1"), env1->Global());

  // Change env2 to a different domain and set env1's global object
  // as the __proto__ of an object in env2 and enumerate properties
  // in for-in. It shouldn't enumerate properties on env1's global
  // object.
  env2->SetSecurityToken(bar);
  {
    Context::Scope scope_env2(env2);
    Local<Value> result =
        CompileRun("(function(){var obj = {'__proto__':env1};"
                   "for (var p in obj)"
                   "   if (p == 'prop') return false;"
                   "return true;})()");
    CHECK(result->IsTrue());
  }
  env2.Dispose();
}


TEST(ContextDetachGlobal) {
  v8::HandleScope handle_scope;
  LocalContext env1;
  v8::Persistent<Context> env2 = Context::New();

  Local<v8::Object> global1 = env1->Global();

  Local<Value> foo = v8_str("foo");

  // Set to the same domain.
  env1->SetSecurityToken(foo);
  env2->SetSecurityToken(foo);

  // Enter env2
  env2->Enter();

  // Create a function in env1
  Local<v8::Object> global2 = env2->Global();
  global2->Set(v8_str("prop"), v8::Integer::New(1));
  CompileRun("function getProp() {return prop;}");

  env1->Global()->Set(v8_str("getProp"),
                      global2->Get(v8_str("getProp")));

  // Detach env1's global, and reuse the global object of env1
  env2->Exit();
  env2->DetachGlobal();
  // env2 has a new global object.
  CHECK(!env2->Global()->Equals(global2));

  v8::Persistent<Context> env3 =
      Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
  env3->SetSecurityToken(v8_str("bar"));
  env3->Enter();

  Local<v8::Object> global3 = env3->Global();
  CHECK_EQ(global2, global3);
  CHECK(global3->Get(v8_str("prop"))->IsUndefined());
  CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
  global3->Set(v8_str("prop"), v8::Integer::New(-1));
  global3->Set(v8_str("prop2"), v8::Integer::New(2));
  env3->Exit();

  // Call getProp in env1, and it should return the value 1
  {
    Local<Value> get_prop = global1->Get(v8_str("getProp"));
    CHECK(get_prop->IsFunction());
    v8::TryCatch try_catch;
    Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
    CHECK(!try_catch.HasCaught());
    CHECK_EQ(1, r->Int32Value());
  }

  // Check that env3 is not accessible from env1
  {
    Local<Value> r = global3->Get(v8_str("prop2"));
    CHECK(r->IsUndefined());
  }

  env2.Dispose();
  env3.Dispose();
}


static bool NamedAccessBlocker(Local<v8::Object> global,
                               Local<Value> name,
                               v8::AccessType type,
                               Local<Value> data) {
  return Context::GetCurrent()->Global()->Equals(global);
}


static bool IndexedAccessBlocker(Local<v8::Object> global,
                                 uint32_t key,
                                 v8::AccessType type,
                                 Local<Value> data) {
  return Context::GetCurrent()->Global()->Equals(global);
}


static int g_echo_value = -1;
static v8::Handle<Value> EchoGetter(Local<String> name,
                                    const AccessorInfo& info) {
  return v8_num(g_echo_value);
}


static void EchoSetter(Local<String> name,
                       Local<Value> value,
                       const AccessorInfo&) {
  if (value->IsNumber())
    g_echo_value = value->Int32Value();
}


static v8::Handle<Value> UnreachableGetter(Local<String> name,
                                           const AccessorInfo& info) {
  CHECK(false);  // This function should not be called..
  return v8::Undefined();
}


static void UnreachableSetter(Local<String>, Local<Value>,
                              const AccessorInfo&) {
  CHECK(false);  // This function should nto be called.
}


THREADED_TEST(AccessControl) {
  v8::HandleScope handle_scope;
  v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();

  global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
                                           IndexedAccessBlocker);

  // Add an accessor accessible by cross-domain JS code.
  global_template->SetAccessor(
      v8_str("accessible_prop"),
      EchoGetter, EchoSetter,
      v8::Handle<Value>(),
      v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));

  // Add an accessor that is not accessible by cross-domain JS code.
  global_template->SetAccessor(v8_str("blocked_access_prop"),
                               UnreachableGetter, UnreachableSetter,
                               v8::Handle<Value>(),
                               v8::DEFAULT);

  // Create an environment
  v8::Persistent<Context> context0 = Context::New(NULL, global_template);
  context0->Enter();

  v8::Handle<v8::Object> global0 = context0->Global();

  v8::HandleScope scope1;

  v8::Persistent<Context> context1 = Context::New();
  context1->Enter();

  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("other"), global0);

  v8::Handle<Value> value;

  // Access blocked property
  value = v8_compile("other.blocked_prop = 1")->Run();
  value = v8_compile("other.blocked_prop")->Run();
  CHECK(value->IsUndefined());

  // Access accessible property
  value = v8_compile("other.accessible_prop = 3")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(3, value->Int32Value());

  value = v8_compile("other.accessible_prop")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(3, value->Int32Value());

  context1->Exit();
  context0->Exit();
  context1.Dispose();
  context0.Dispose();
}


static v8::Handle<Value> ConstTenGetter(Local<String> name,
                                        const AccessorInfo& info) {
  return v8_num(10);
}


THREADED_TEST(CrossDomainAccessors) {
  v8::HandleScope handle_scope;

  v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();

  v8::Handle<v8::ObjectTemplate> global_template =
      func_template->InstanceTemplate();

  v8::Handle<v8::ObjectTemplate> proto_template =
      func_template->PrototypeTemplate();

  // Add an accessor to proto that's accessible by cross-domain JS code.
  proto_template->SetAccessor(v8_str("accessible"),
                              ConstTenGetter, 0,
                              v8::Handle<Value>(),
                              v8::ALL_CAN_READ);

  // Add an accessor that is not accessible by cross-domain JS code.
  global_template->SetAccessor(v8_str("unreachable"),
                               UnreachableGetter, 0,
                               v8::Handle<Value>(),
                               v8::DEFAULT);

  v8::Persistent<Context> context0 = Context::New(NULL, global_template);
  context0->Enter();

  Local<v8::Object> global = context0->Global();
  // Add a normal property that shadows 'accessible'
  global->Set(v8_str("accessible"), v8_num(11));

  // Enter a new context.
  v8::HandleScope scope1;
  v8::Persistent<Context> context1 = Context::New();
  context1->Enter();

  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("other"), global);

  // Should return 10, instead of 11
  v8::Handle<Value> value = v8_compile("other.accessible")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(10, value->Int32Value());

  value = v8_compile("other.unreachable")->Run();
  CHECK(value->IsUndefined());

  context1->Exit();
  context0->Exit();
  context1.Dispose();
  context0.Dispose();
}


static int named_access_count = 0;
static int indexed_access_count = 0;

static bool NamedAccessCounter(Local<v8::Object> global,
                               Local<Value> name,
                               v8::AccessType type,
                               Local<Value> data) {
  named_access_count++;
  return true;
}


static bool IndexedAccessCounter(Local<v8::Object> global,
                                 uint32_t key,
                                 v8::AccessType type,
                                 Local<Value> data) {
  indexed_access_count++;
  return true;
}


// This one is too easily disturbed by other tests.
TEST(AccessControlIC) {
  named_access_count = 0;
  indexed_access_count = 0;

  v8::HandleScope handle_scope;

  // Create an environment.
  v8::Persistent<Context> context0 = Context::New();
  context0->Enter();

  // Create an object that requires access-check functions to be
  // called for cross-domain access.
  v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
  object_template->SetAccessCheckCallbacks(NamedAccessCounter,
                                           IndexedAccessCounter);
  Local<v8::Object> object = object_template->NewInstance();

  v8::HandleScope scope1;

  // Create another environment.
  v8::Persistent<Context> context1 = Context::New();
  context1->Enter();

  // Make easy access to the object from the other environment.
  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("obj"), object);

  v8::Handle<Value> value;

  // Check that the named access-control function is called every time.
  value = v8_compile("for (var i = 0; i < 10; i++)  obj.prop = 1;")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++)  obj.prop;"
                     "obj.prop")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(1, value->Int32Value());
  CHECK_EQ(21, named_access_count);

  // Check that the named access-control function is called every time.
  value = v8_compile("var p = 'prop';")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
                     "obj[p]")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(1, value->Int32Value());
  CHECK_EQ(42, named_access_count);

  // Check that the indexed access-control function is called every time.
  value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
                     "obj[0]")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(1, value->Int32Value());
  CHECK_EQ(21, indexed_access_count);

  context1->Exit();
  context0->Exit();
  context1.Dispose();
  context0.Dispose();
}


static bool NamedAccessFlatten(Local<v8::Object> global,
                               Local<Value> name,
                               v8::AccessType type,
                               Local<Value> data) {
  char buf[100];
  int len;

  CHECK(name->IsString());

  memset(buf, 0x1, sizeof(buf));
  len = Local<String>::Cast(name)->WriteAscii(buf);
  CHECK_EQ(4, len);

  uint16_t buf2[100];

  memset(buf, 0x1, sizeof(buf));
  len = Local<String>::Cast(name)->Write(buf2);
  CHECK_EQ(4, len);

  return true;
}


static bool IndexedAccessFlatten(Local<v8::Object> global,
                                 uint32_t key,
                                 v8::AccessType type,
                                 Local<Value> data) {
  return true;
}


// Regression test.  In access checks, operations that may cause
// garbage collection are not allowed.  It used to be the case that
// using the Write operation on a string could cause a garbage
// collection due to flattening of the string.  This is no longer the
// case.
THREADED_TEST(AccessControlFlatten) {
  named_access_count = 0;
  indexed_access_count = 0;

  v8::HandleScope handle_scope;

  // Create an environment.
  v8::Persistent<Context> context0 = Context::New();
  context0->Enter();

  // Create an object that requires access-check functions to be
  // called for cross-domain access.
  v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
  object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
                                           IndexedAccessFlatten);
  Local<v8::Object> object = object_template->NewInstance();

  v8::HandleScope scope1;

  // Create another environment.
  v8::Persistent<Context> context1 = Context::New();
  context1->Enter();

  // Make easy access to the object from the other environment.
  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("obj"), object);

  v8::Handle<Value> value;

  value = v8_compile("var p = 'as' + 'df';")->Run();
  value = v8_compile("obj[p];")->Run();

  context1->Exit();
  context0->Exit();
  context1.Dispose();
  context0.Dispose();
}


static v8::Handle<Value> AccessControlNamedGetter(
    Local<String>, const AccessorInfo&) {
  return v8::Integer::New(42);
}


static v8::Handle<Value> AccessControlNamedSetter(
    Local<String>, Local<Value> value, const AccessorInfo&) {
  return value;
}


static v8::Handle<Value> AccessControlIndexedGetter(
      uint32_t index,
      const AccessorInfo& info) {
  return v8_num(42);
}


static v8::Handle<Value> AccessControlIndexedSetter(
    uint32_t, Local<Value> value, const AccessorInfo&) {
  return value;
}


THREADED_TEST(AccessControlInterceptorIC) {
  named_access_count = 0;
  indexed_access_count = 0;

  v8::HandleScope handle_scope;

  // Create an environment.
  v8::Persistent<Context> context0 = Context::New();
  context0->Enter();

  // Create an object that requires access-check functions to be
  // called for cross-domain access.  The object also has interceptors
  // interceptor.
  v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
  object_template->SetAccessCheckCallbacks(NamedAccessCounter,
                                           IndexedAccessCounter);
  object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
                                           AccessControlNamedSetter);
  object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
                                             AccessControlIndexedSetter);
  Local<v8::Object> object = object_template->NewInstance();

  v8::HandleScope scope1;

  // Create another environment.
  v8::Persistent<Context> context1 = Context::New();
  context1->Enter();

  // Make easy access to the object from the other environment.
  v8::Handle<v8::Object> global1 = context1->Global();
  global1->Set(v8_str("obj"), object);

  v8::Handle<Value> value;

  // Check that the named access-control function is called every time
  // eventhough there is an interceptor on the object.
  value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
                     "obj.x")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(42, value->Int32Value());
  CHECK_EQ(21, named_access_count);

  value = v8_compile("var p = 'x';")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
                     "obj[p]")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(42, value->Int32Value());
  CHECK_EQ(42, named_access_count);

  // Check that the indexed access-control function is called every
  // time eventhough there is an interceptor on the object.
  value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
  value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
                     "obj[0]")->Run();
  CHECK(value->IsNumber());
  CHECK_EQ(42, value->Int32Value());
  CHECK_EQ(21, indexed_access_count);

  context1->Exit();
  context0->Exit();
  context1.Dispose();
  context0.Dispose();
}


THREADED_TEST(Version) {
  v8::V8::GetVersion();
}


static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8_num(12);
}


THREADED_TEST(InstanceProperties) {
  v8::HandleScope handle_scope;
  LocalContext context;

  Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
  Local<ObjectTemplate> instance = t->InstanceTemplate();

  instance->Set(v8_str("x"), v8_num(42));
  instance->Set(v8_str("f"),
                v8::FunctionTemplate::New(InstanceFunctionCallback));

  Local<Value> o = t->GetFunction()->NewInstance();

  context->Global()->Set(v8_str("i"), o);
  Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
  CHECK_EQ(42, value->Int32Value());

  value = Script::Compile(v8_str("i.f()"))->Run();
  CHECK_EQ(12, value->Int32Value());
}


static v8::Handle<Value>
GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  return v8::Handle<Value>();
}


THREADED_TEST(GlobalObjectInstanceProperties) {
  v8::HandleScope handle_scope;

  Local<Value> global_object;

  Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
  t->InstanceTemplate()->SetNamedPropertyHandler(
      GlobalObjectInstancePropertiesGet);
  Local<ObjectTemplate> instance_template = t->InstanceTemplate();
  instance_template->Set(v8_str("x"), v8_num(42));
  instance_template->Set(v8_str("f"),
                         v8::FunctionTemplate::New(InstanceFunctionCallback));

  {
    LocalContext env(NULL, instance_template);
    // Hold on to the global object so it can be used again in another
    // environment initialization.
    global_object = env->Global();

    Local<Value> value = Script::Compile(v8_str("x"))->Run();
    CHECK_EQ(42, value->Int32Value());
    value = Script::Compile(v8_str("f()"))->Run();
    CHECK_EQ(12, value->Int32Value());
  }

  {
    // Create new environment reusing the global object.
    LocalContext env(NULL, instance_template, global_object);
    Local<Value> value = Script::Compile(v8_str("x"))->Run();
    CHECK_EQ(42, value->Int32Value());
    value = Script::Compile(v8_str("f()"))->Run();
    CHECK_EQ(12, value->Int32Value());
  }
}


static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8_num(42);
}


static int shadow_y;
static int shadow_y_setter_call_count;
static int shadow_y_getter_call_count;


static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
  shadow_y_setter_call_count++;
  shadow_y = 42;
}


static v8::Handle<Value> ShadowYGetter(Local<String> name,
                                       const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  shadow_y_getter_call_count++;
  return v8_num(shadow_y);
}


static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
                                          const AccessorInfo& info) {
  return v8::Handle<Value>();
}


static v8::Handle<Value> ShadowNamedGet(Local<String> key,
                                        const AccessorInfo&) {
  return v8::Handle<Value>();
}


THREADED_TEST(ShadowObject) {
  shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
  v8::HandleScope handle_scope;

  Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
  LocalContext context(NULL, global_template);

  Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
  t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
  t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
  Local<ObjectTemplate> proto = t->PrototypeTemplate();
  Local<ObjectTemplate> instance = t->InstanceTemplate();

  // Only allow calls of f on instances of t.
  Local<v8::Signature> signature = v8::Signature::New(t);
  proto->Set(v8_str("f"),
             v8::FunctionTemplate::New(ShadowFunctionCallback,
                                       Local<Value>(),
                                       signature));
  proto->Set(v8_str("x"), v8_num(12));

  instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);

  Local<Value> o = t->GetFunction()->NewInstance();
  context->Global()->Set(v8_str("__proto__"), o);

  Local<Value> value =
      Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
  CHECK(value->IsBoolean());
  CHECK(!value->BooleanValue());

  value = Script::Compile(v8_str("x"))->Run();
  CHECK_EQ(12, value->Int32Value());

  value = Script::Compile(v8_str("f()"))->Run();
  CHECK_EQ(42, value->Int32Value());

  Script::Compile(v8_str("y = 42"))->Run();
  CHECK_EQ(1, shadow_y_setter_call_count);
  value = Script::Compile(v8_str("y"))->Run();
  CHECK_EQ(1, shadow_y_getter_call_count);
  CHECK_EQ(42, value->Int32Value());
}


THREADED_TEST(HiddenPrototype) {
  v8::HandleScope handle_scope;
  LocalContext context;

  Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
  t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
  Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
  t1->SetHiddenPrototype(true);
  t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
  Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
  t2->SetHiddenPrototype(true);
  t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
  Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
  t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));

  Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
  Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
  Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
  Local<v8::Object> o3 = t3->GetFunction()->NewInstance();

  // Setting the prototype on an object skips hidden prototypes.
  CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
  o0->Set(v8_str("__proto__"), o1);
  CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
  CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
  o0->Set(v8_str("__proto__"), o2);
  CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
  CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
  CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
  o0->Set(v8_str("__proto__"), o3);
  CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
  CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
  CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
  CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());

  // Getting the prototype of o0 should get the first visible one
  // which is o3.  Therefore, z should not be defined on the prototype
  // object.
  Local<Value> proto = o0->Get(v8_str("__proto__"));
  CHECK(proto->IsObject());
  CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined());
}


THREADED_TEST(GetterSetterExceptions) {
  v8::HandleScope handle_scope;
  LocalContext context;
  CompileRun(
    "function Foo() { };"
    "function Throw() { throw 5; };"
    "var x = { };"
    "x.__defineSetter__('set', Throw);"
    "x.__defineGetter__('get', Throw);");
  Local<v8::Object> x =
      Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
  v8::TryCatch try_catch;
  x->Set(v8_str("set"), v8::Integer::New(8));
  x->Get(v8_str("get"));
  x->Set(v8_str("set"), v8::Integer::New(8));
  x->Get(v8_str("get"));
  x->Set(v8_str("set"), v8::Integer::New(8));
  x->Get(v8_str("get"));
  x->Set(v8_str("set"), v8::Integer::New(8));
  x->Get(v8_str("get"));
}


THREADED_TEST(Constructor) {
  v8::HandleScope handle_scope;
  LocalContext context;
  Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->SetClassName(v8_str("Fun"));
  Local<Function> cons = templ->GetFunction();
  context->Global()->Set(v8_str("Fun"), cons);
  Local<v8::Object> inst = cons->NewInstance();
  i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
  Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
  CHECK(value->BooleanValue());
}

THREADED_TEST(FunctionDescriptorException) {
  v8::HandleScope handle_scope;
  LocalContext context;
  Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->SetClassName(v8_str("Fun"));
  Local<Function> cons = templ->GetFunction();
  context->Global()->Set(v8_str("Fun"), cons);
  Local<Value> value = CompileRun(
    "function test() {"
    "  try {"
    "    (new Fun()).blah()"
    "  } catch (e) {"
    "    var str = String(e);"
    "    if (str.indexOf('TypeError') == -1) return 1;"
    "    if (str.indexOf('[object Fun]') != -1) return 2;"
    "    if (str.indexOf('#<a Fun>') == -1) return 3;"
    "    return 0;"
    "  }"
    "  return 4;"
    "}"
    "test();");
  CHECK_EQ(0, value->Int32Value());
}


THREADED_TEST(CrossEval) {
  v8::HandleScope scope;
  LocalContext other;
  LocalContext current;

  Local<String> token = v8_str("<security token>");
  other->SetSecurityToken(token);
  current->SetSecurityToken(token);

  // Setup reference from current to other.
  current->Global()->Set(v8_str("other"), other->Global());

  // Check that new variables are introduced in other context.
  Local<Script> script =
      Script::Compile(v8_str("other.eval('var foo = 1234')"));
  script->Run();
  Local<Value> foo = other->Global()->Get(v8_str("foo"));
  CHECK_EQ(1234, foo->Int32Value());
  CHECK(!current->Global()->Has(v8_str("foo")));

  // Check that writing to non-existing properties introduces them in
  // the current context.
  script =
      Script::Compile(v8_str("other.eval('na = 1234')"));
  script->Run();
  CHECK_EQ(1234, current->Global()->Get(v8_str("na"))->Int32Value());
  CHECK(!other->Global()->Has(v8_str("na")));

  // Check that variables in current context are visible in other
  // context. This must include local variables.
  script =
      Script::Compile(v8_str("var bar = 42;"
                                      "(function() { "
                                      "  var baz = 87;"
                                      "  return other.eval('bar + baz');"
                                      "})();"));
  Local<Value> result = script->Run();
  CHECK_EQ(42 + 87, result->Int32Value());

  // Check that global variables in the other environment are visible
  // when evaluting code.
  other->Global()->Set(v8_str("bis"), v8_num(1234));
  script = Script::Compile(v8_str("other.eval('bis')"));
  CHECK_EQ(1234, script->Run()->Int32Value());

  // Check that the 'this' pointer isn't touched as a result of
  // calling eval across environments.
  script =
      Script::Compile(v8_str("var t = this; other.eval('this == t')"));
  result = script->Run();
  CHECK(result->IsBoolean());
  CHECK(result->BooleanValue());

  // Check that doing a cross eval works from within a global
  // with-statement.
  script =
      Script::Compile(v8_str("other.y = 1;"
                                      "with({x:2}){other.eval('x+y')}"));
  result = script->Run();
  CHECK_EQ(3, result->Int32Value());
}


THREADED_TEST(CrossLazyLoad) {
  v8::HandleScope scope;
  LocalContext other;
  LocalContext current;

  Local<String> token = v8_str("<security token>");
  other->SetSecurityToken(token);
  current->SetSecurityToken(token);

  // Setup reference from current to other.
  current->Global()->Set(v8_str("other"), other->Global());

  // Trigger lazy loading in other context.
  Local<Script> script =
      Script::Compile(v8_str("other.eval('new Date(42)')"));
  Local<Value> value = script->Run();
  CHECK_EQ(42.0, value->NumberValue());
}


static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return args[0];
}


// Test that a call handler can be set for objects which will allow
// non-function objects created through the API to be called as
// functions.
THREADED_TEST(CallAsFunction) {
  v8::HandleScope scope;
  LocalContext context;

  Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
  Local<ObjectTemplate> instance_template = t->InstanceTemplate();
  instance_template->SetCallAsFunctionHandler(call_as_function);
  Local<v8::Object> instance = t->GetFunction()->NewInstance();
  context->Global()->Set(v8_str("obj"), instance);
  v8::TryCatch try_catch;
  Local<Value> value;
  CHECK(!try_catch.HasCaught());

  value = Script::Compile(v8_str("obj(42)"))->Run();
  CHECK(!try_catch.HasCaught());
  CHECK_EQ(42, value->Int32Value());

  value = Script::Compile(v8_str("(function(o){return o(49)})(obj)"))->Run();
  CHECK(!try_catch.HasCaught());
  CHECK_EQ(49, value->Int32Value());

  // test special case of call as function
  value = Script::Compile(v8_str("[obj]['0'](45)"))->Run();
  CHECK(!try_catch.HasCaught());
  CHECK_EQ(45, value->Int32Value());

  value = Script::Compile(v8_str("obj.call = Function.prototype.call;"
                                 "obj.call(null, 87)"))->Run();
  CHECK(!try_catch.HasCaught());
  CHECK_EQ(87, value->Int32Value());

  // Regression tests for bug #1116356: Calling call through call/apply
  // must work for non-function receivers.
  const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
  value = Script::Compile(v8_str(apply_99))->Run();
  CHECK(!try_catch.HasCaught());
  CHECK_EQ(99, value->Int32Value());

  const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
  value = Script::Compile(v8_str(call_17))->Run();
  CHECK(!try_catch.HasCaught());
  CHECK_EQ(17, value->Int32Value());

  // Try something that will cause an exception: Call the object as a
  // constructor. This should be the last test.
  value = Script::Compile(v8_str("new obj(42)"))->Run();
  CHECK(try_catch.HasCaught());
}


static int CountHandles() {
  return v8::HandleScope::NumberOfHandles();
}


static int Recurse(int depth, int iterations) {
  v8::HandleScope scope;
  if (depth == 0) return CountHandles();
  for (int i = 0; i < iterations; i++) {
    Local<v8::Number> n = v8::Integer::New(42);
  }
  return Recurse(depth - 1, iterations);
}


THREADED_TEST(HandleIteration) {
  static const int kIterations = 500;
  static const int kNesting = 200;
  CHECK_EQ(0, CountHandles());
  {
    v8::HandleScope scope1;
    CHECK_EQ(0, CountHandles());
    for (int i = 0; i < kIterations; i++) {
      Local<v8::Number> n = v8::Integer::New(42);
      CHECK_EQ(i + 1, CountHandles());
    }

    CHECK_EQ(kIterations, CountHandles());
    {
      v8::HandleScope scope2;
      for (int j = 0; j < kIterations; j++) {
        Local<v8::Number> n = v8::Integer::New(42);
        CHECK_EQ(j + 1 + kIterations, CountHandles());
      }
    }
    CHECK_EQ(kIterations, CountHandles());
  }
  CHECK_EQ(0, CountHandles());
  CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
}


static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
    Local<String> name,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  return v8::Handle<Value>();
}


THREADED_TEST(InterceptorHasOwnProperty) {
  v8::HandleScope scope;
  LocalContext context;
  Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
  Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
  instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
  Local<Function> function = fun_templ->GetFunction();
  context->Global()->Set(v8_str("constructor"), function);
  v8::Handle<Value> value = CompileRun(
      "var o = new constructor();"
      "o.hasOwnProperty('ostehaps');");
  CHECK_EQ(false, value->BooleanValue());
  value = CompileRun(
      "o.ostehaps = 42;"
      "o.hasOwnProperty('ostehaps');");
  CHECK_EQ(true, value->BooleanValue());
  value = CompileRun(
      "var p = new constructor();"
      "p.hasOwnProperty('ostehaps');");
  CHECK_EQ(false, value->BooleanValue());
}


static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
                                                 const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(v8_str("x")->Equals(name));
  return v8::Integer::New(42);
}


// This test should hit the load IC for the interceptor case.
THREADED_TEST(InterceptorLoadIC) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(InterceptorLoadICGetter);
  LocalContext context;
  context->Global()->Set(v8_str("o"), templ->NewInstance());
  v8::Handle<Value> value = CompileRun(
    "var result = 0;"
    "for (var i = 0; i < 1000; i++) {"
    "  result = o.x;"
    "}");
  CHECK_EQ(42, value->Int32Value());
}


static v8::Handle<Value> InterceptorStoreICSetter(
    Local<String> key, Local<Value> value, const AccessorInfo&) {
  CHECK(v8_str("x")->Equals(key));
  CHECK_EQ(42, value->Int32Value());
  return value;
}


// This test should hit the store IC for the interceptor case.
THREADED_TEST(InterceptorStoreIC) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
                                 InterceptorStoreICSetter);
  LocalContext context;
  context->Global()->Set(v8_str("o"), templ->NewInstance());
  v8::Handle<Value> value = CompileRun(
    "for (var i = 0; i < 1000; i++) {"
    "  o.x = 42;"
    "}");
}



v8::Handle<Value> call_ic_function;
v8::Handle<Value> call_ic_function2;
v8::Handle<Value> call_ic_function3;

static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
                                                 const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  CHECK(v8_str("x")->Equals(name));
  return call_ic_function;
}


// This test should hit the call IC for the interceptor case.
THREADED_TEST(InterceptorCallIC) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(InterceptorCallICGetter);
  LocalContext context;
  context->Global()->Set(v8_str("o"), templ->NewInstance());
  call_ic_function =
      v8_compile("function f(x) { return x + 1; }; f")->Run();
  v8::Handle<Value> value = CompileRun(
    "var result = 0;"
    "for (var i = 0; i < 1000; i++) {"
    "  result = o.x(41);"
    "}");
  CHECK_EQ(42, value->Int32Value());
}

static int interceptor_call_count = 0;

static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
                                                     const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
    return call_ic_function2;
  }
  return v8::Handle<Value>();
}


// This test should hit load and call ICs for the interceptor case.
// Once in a while, the interceptor will reply that a property was not
// found in which case we should get a reference error.
THREADED_TEST(InterceptorICReferenceErrors) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
  LocalContext context(0, templ, v8::Handle<Value>());
  call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
  v8::Handle<Value> value = CompileRun(
    "function f() {"
    "  for (var i = 0; i < 1000; i++) {"
    "    try { x; } catch(e) { return true; }"
    "  }"
    "  return false;"
    "};"
    "f();");
  CHECK_EQ(true, value->BooleanValue());
  interceptor_call_count = 0;
  value = CompileRun(
    "function g() {"
    "  for (var i = 0; i < 1000; i++) {"
    "    try { x(42); } catch(e) { return true; }"
    "  }"
    "  return false;"
    "};"
    "g();");
  CHECK_EQ(true, value->BooleanValue());
}


static int interceptor_ic_exception_get_count = 0;

static v8::Handle<Value> InterceptorICExceptionGetter(
    Local<String> name,
    const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
    return call_ic_function3;
  }
  if (interceptor_ic_exception_get_count == 20) {
    return v8::ThrowException(v8_num(42));
  }
  // Do not handle get for properties other than x.
  return v8::Handle<Value>();
}

// Test interceptor load/call IC where the interceptor throws an
// exception once in a while.
THREADED_TEST(InterceptorICGetterExceptions) {
  interceptor_ic_exception_get_count = 0;
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
  LocalContext context(0, templ, v8::Handle<Value>());
  call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
  v8::Handle<Value> value = CompileRun(
    "function f() {"
    "  for (var i = 0; i < 100; i++) {"
    "    try { x; } catch(e) { return true; }"
    "  }"
    "  return false;"
    "};"
    "f();");
  CHECK_EQ(true, value->BooleanValue());
  interceptor_ic_exception_get_count = 0;
  value = CompileRun(
    "function f() {"
    "  for (var i = 0; i < 100; i++) {"
    "    try { x(42); } catch(e) { return true; }"
    "  }"
    "  return false;"
    "};"
    "f();");
  CHECK_EQ(true, value->BooleanValue());
}


static int interceptor_ic_exception_set_count = 0;

static v8::Handle<Value> InterceptorICExceptionSetter(
      Local<String> key, Local<Value> value, const AccessorInfo&) {
  ApiTestFuzzer::Fuzz();
  if (++interceptor_ic_exception_set_count > 20) {
    return v8::ThrowException(v8_num(42));
  }
  // Do not actually handle setting.
  return v8::Handle<Value>();
}

// Test interceptor store IC where the interceptor throws an exception
// once in a while.
THREADED_TEST(InterceptorICSetterExceptions) {
  interceptor_ic_exception_set_count = 0;
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
  LocalContext context(0, templ, v8::Handle<Value>());
  v8::Handle<Value> value = CompileRun(
    "function f() {"
    "  for (var i = 0; i < 100; i++) {"
    "    try { x = 42; } catch(e) { return true; }"
    "  }"
    "  return false;"
    "};"
    "f();");
  CHECK_EQ(true, value->BooleanValue());
}


// Test that we ignore null interceptors.
THREADED_TEST(NullNamedInterceptor) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetNamedPropertyHandler(0);
  LocalContext context;
  templ->Set("x", v8_num(42));
  v8::Handle<v8::Object> obj = templ->NewInstance();
  context->Global()->Set(v8_str("obj"), obj);
  v8::Handle<Value> value = CompileRun("obj.x");
  CHECK(value->IsInt32());
  CHECK_EQ(42, value->Int32Value());
}


// Test that we ignore null interceptors.
THREADED_TEST(NullIndexedInterceptor) {
  v8::HandleScope scope;
  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
  templ->SetIndexedPropertyHandler(0);
  LocalContext context;
  templ->Set("42", v8_num(42));
  v8::Handle<v8::Object> obj = templ->NewInstance();
  context->Global()->Set(v8_str("obj"), obj);
  v8::Handle<Value> value = CompileRun("obj[42]");
  CHECK(value->IsInt32());
  CHECK_EQ(42, value->Int32Value());
}


static v8::Handle<Value> ParentGetter(Local<String> name,
                                      const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  return v8_num(1);
}


static v8::Handle<Value> ChildGetter(Local<String> name,
                                     const AccessorInfo& info) {
  ApiTestFuzzer::Fuzz();
  return v8_num(42);
}


THREADED_TEST(Overriding) {
  v8::HandleScope scope;
  LocalContext context;

  // Parent template.
  Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
  Local<ObjectTemplate> parent_instance_templ =
      parent_templ->InstanceTemplate();
  parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);

  // Template that inherits from the parent template.
  Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
  Local<ObjectTemplate> child_instance_templ =
      child_templ->InstanceTemplate();
  child_templ->Inherit(parent_templ);
  // Override 'f'.  The child version of 'f' should get called for child
  // instances.
  child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
  // Add 'g' twice.  The 'g' added last should get called for instances.
  child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
  child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);

  // Add 'h' as an accessor to the proto template with ReadOnly attributes
  // so 'h' can be shadowed on the instance object.
  Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
  child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
      v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);

  // Add 'i' as an accessor to the instance template with ReadOnly attributes
  // but the attribute does not have effect because it is duplicated with
  // NULL setter.
  child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
      v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);



  // Instantiate the child template.
  Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();

  // Check that the child function overrides the parent one.
  context->Global()->Set(v8_str("o"), instance);
  Local<Value> value = v8_compile("o.f")->Run();
  // Check that the 'g' that was added last is hit.
  CHECK_EQ(42, value->Int32Value());
  value = v8_compile("o.g")->Run();
  CHECK_EQ(42, value->Int32Value());

  // Check 'h' can be shadowed.
  value = v8_compile("o.h = 3; o.h")->Run();
  CHECK_EQ(3, value->Int32Value());

  // Check 'i' is cannot be shadowed or changed.
  value = v8_compile("o.i = 3; o.i")->Run();
  CHECK_EQ(42, value->Int32Value());
}


static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  if (args.IsConstructCall()) {
    return v8::Boolean::New(true);
  }
  return v8::Boolean::New(false);
}


THREADED_TEST(IsConstructCall) {
  v8::HandleScope scope;

  // Function template with call handler.
  Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->SetCallHandler(IsConstructHandler);

  LocalContext context;

  context->Global()->Set(v8_str("f"), templ->GetFunction());
  Local<Value> value = v8_compile("f()")->Run();
  CHECK(!value->BooleanValue());
  value = v8_compile("new f()")->Run();
  CHECK(value->BooleanValue());
}


THREADED_TEST(ObjectProtoToString) {
  v8::HandleScope scope;
  Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
  templ->SetClassName(v8_str("MyClass"));

  LocalContext context;

  Local<String> customized_tostring = v8_str("customized toString");

  // Replace Object.prototype.toString
  v8_compile("Object.prototype.toString = function() {"
                  "  return 'customized toString';"
                  "}")->Run();

  // Normal ToString call should call replaced Object.prototype.toString
  Local<v8::Object> instance = templ->GetFunction()->NewInstance();
  Local<String> value = instance->ToString();
  CHECK(value->IsString() && value->Equals(customized_tostring));

  // ObjectProtoToString should not call replace toString function.
  value = instance->ObjectProtoToString();
  CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));

  // Check global
  value = context->Global()->ObjectProtoToString();
  CHECK(value->IsString() && value->Equals(v8_str("[object global]")));

  // Check ordinary object
  Local<Value> object = v8_compile("new Object()")->Run();
  value = Local<v8::Object>::Cast(object)->ObjectProtoToString();
  CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
}


bool ApiTestFuzzer::fuzzing_ = false;
v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
  v8::internal::OS::CreateSemaphore(0);
int ApiTestFuzzer::active_tests_;
int ApiTestFuzzer::tests_being_run_;
int ApiTestFuzzer::current_;


// We are in a callback and want to switch to another thread (if we
// are currently running the thread fuzzing test).
void ApiTestFuzzer::Fuzz() {
  if (!fuzzing_) return;
  ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
  test->ContextSwitch();
}


// Let the next thread go.  Since it is also waiting on the V8 lock it may
// not start immediately.
bool ApiTestFuzzer::NextThread() {
  int test_position = GetNextTestNumber();
  int test_number = RegisterThreadedTest::nth(current_)->fuzzer_->test_number_;
  if (test_position == current_) {
    printf("Stay with %d\n", test_number);
    return false;
  }
  printf("Switch from %d to %d\n",
         current_ < 0 ? 0 : test_number, test_position < 0 ? 0 : test_number);
  current_ = test_position;
  RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
  return true;
}


void ApiTestFuzzer::Run() {
  // When it is our turn...
  gate_->Wait();
  {
    // ... get the V8 lock and start running the test.
    v8::Locker locker;
    CallTest();
  }
  // This test finished.
  active_ = false;
  active_tests_--;
  // If it was the last then signal that fact.
  if (active_tests_ == 0) {
    all_tests_done_->Signal();
  } else {
    // Otherwise select a new test and start that.
    NextThread();
  }
}


static unsigned linear_congruential_generator;


void ApiTestFuzzer::Setup(PartOfTest part) {
  linear_congruential_generator = i::FLAG_testing_prng_seed;
  fuzzing_ = true;
  int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
  int end = (part == FIRST_PART)
      ? (RegisterThreadedTest::count() >> 1)
      : RegisterThreadedTest::count();
  active_tests_ = tests_being_run_ = end - start;
  for (int i = 0; i < tests_being_run_; i++) {
    RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
  }
  for (int i = 0; i < active_tests_; i++) {
    RegisterThreadedTest::nth(i)->fuzzer_->Start();
  }
}


static void CallTestNumber(int test_number) {
  (RegisterThreadedTest::nth(test_number)->callback())();
}


void ApiTestFuzzer::RunAllTests() {
  // Set off the first test.
  current_ = -1;
  NextThread();
  // Wait till they are all done.
  all_tests_done_->Wait();
}


int ApiTestFuzzer::GetNextTestNumber() {
  int next_test;
  do {
    next_test = (linear_congruential_generator >> 16) % tests_being_run_;
    linear_congruential_generator *= 1664525u;
    linear_congruential_generator += 1013904223u;
  } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
  return next_test;
}


void ApiTestFuzzer::ContextSwitch() {
  // If the new thread is the same as the current thread there is nothing to do.
  if (NextThread()) {
    // Now it can start.
    v8::Unlocker unlocker;
    // Wait till someone starts us again.
    gate_->Wait();
    // And we're off.
  }
}


void ApiTestFuzzer::TearDown() {
  fuzzing_ = false;
}


// Lets not be needlessly self-referential.
TEST(Threading) {
  ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
  ApiTestFuzzer::RunAllTests();
  ApiTestFuzzer::TearDown();
}

TEST(Threading2) {
  ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
  ApiTestFuzzer::RunAllTests();
  ApiTestFuzzer::TearDown();
}


void ApiTestFuzzer::CallTest() {
  printf("Start test %d\n", test_number_);
  CallTestNumber(test_number_);
  printf("End test %d\n", test_number_);
}


static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
  CHECK(v8::Locker::IsLocked());
  ApiTestFuzzer::Fuzz();
  v8::Unlocker unlocker;
  const char* code = "throw 7;";
  {
    v8::Locker nested_locker;
    v8::HandleScope scope;
    v8::Handle<Value> exception;
    { v8::TryCatch try_catch;
      v8::Handle<Value> value = CompileRun(code);
      CHECK(value.IsEmpty());
      CHECK(try_catch.HasCaught());
      // Make sure to wrap the exception in a new handle because
      // the handle returned from the TryCatch is destroyed
      // when the TryCatch is destroyed.
      exception = Local<Value>::New(try_catch.Exception());
    }
    return v8::ThrowException(exception);
  }
}


static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
  CHECK(v8::Locker::IsLocked());
  ApiTestFuzzer::Fuzz();
  v8::Unlocker unlocker;
  const char* code = "throw 7;";
  {
    v8::Locker nested_locker;
    v8::HandleScope scope;
    v8::Handle<Value> value = CompileRun(code);
    CHECK(value.IsEmpty());
    return v8_str("foo");
  }
}


// These are locking tests that don't need to be run again
// as part of the locking aggregation tests.
TEST(NestedLockers) {
  v8::Locker locker;
  CHECK(v8::Locker::IsLocked());
  v8::HandleScope scope;
  LocalContext env;
  Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
  Local<Function> fun = fun_templ->GetFunction();
  env->Global()->Set(v8_str("throw_in_js"), fun);
  Local<Script> script = v8_compile("(function () {"
                                    "  try {"
                                    "    throw_in_js();"
                                    "    return 42;"
                                    "  } catch (e) {"
                                    "    return e * 13;"
                                    "  }"
                                    "})();");
  CHECK_EQ(91, script->Run()->Int32Value());
}


// These are locking tests that don't need to be run again
// as part of the locking aggregation tests.
TEST(NestedLockersNoTryCatch) {
  v8::Locker locker;
  v8::HandleScope scope;
  LocalContext env;
  Local<v8::FunctionTemplate> fun_templ =
      v8::FunctionTemplate::New(ThrowInJSNoCatch);
  Local<Function> fun = fun_templ->GetFunction();
  env->Global()->Set(v8_str("throw_in_js"), fun);
  Local<Script> script = v8_compile("(function () {"
                                    "  try {"
                                    "    throw_in_js();"
                                    "    return 42;"
                                    "  } catch (e) {"
                                    "    return e * 13;"
                                    "  }"
                                    "})();");
  CHECK_EQ(91, script->Run()->Int32Value());
}


THREADED_TEST(RecursiveLocking) {
  v8::Locker locker;
  {
    v8::Locker locker2;
    CHECK(v8::Locker::IsLocked());
  }
}


static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  v8::Unlocker unlocker;
  return v8::Undefined();
}


THREADED_TEST(LockUnlockLock) {
  {
    v8::Locker locker;
    v8::HandleScope scope;
    LocalContext env;
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(UnlockForAMoment);
    Local<Function> fun = fun_templ->GetFunction();
    env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
    Local<Script> script = v8_compile("(function () {"
                                      "  unlock_for_a_moment();"
                                      "  return 42;"
                                      "})();");
    CHECK_EQ(42, script->Run()->Int32Value());
  }
  {
    v8::Locker locker;
    v8::HandleScope scope;
    LocalContext env;
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(UnlockForAMoment);
    Local<Function> fun = fun_templ->GetFunction();
    env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
    Local<Script> script = v8_compile("(function () {"
                                      "  unlock_for_a_moment();"
                                      "  return 42;"
                                      "})();");
    CHECK_EQ(42, script->Run()->Int32Value());
  }
}


static int GetSurvivingGlobalObjectsCount() {
  int count = 0;
  v8::internal::Heap::CollectAllGarbage();
  v8::internal::HeapIterator it;
  while (it.has_next()) {
    v8::internal::HeapObject* object = it.next();
    if (object->IsJSGlobalObject()) {
      count++;
    }
  }
#ifdef DEBUG
  if (count > 0) v8::internal::Heap::TracePathToGlobal();
#endif
  return count;
}


TEST(DontLeakGlobalObjects) {
  // Regression test for issues 1139850 and 1174891.

  v8::V8::Initialize();

  // TODO(121): when running "cctest test-api", the initial count is 2,
  // after second GC, the counter drops to 1. Needs to figure out why
  // one GC is not enough to collect all garbage.
  GetSurvivingGlobalObjectsCount();
  int count = GetSurvivingGlobalObjectsCount();

  for (int i = 0; i < 5; i++) {
    { v8::HandleScope scope;
      LocalContext context;
    }
    CHECK_EQ(count, GetSurvivingGlobalObjectsCount());

    { v8::HandleScope scope;
      LocalContext context;
      v8_compile("Date")->Run();
    }
    CHECK_EQ(count, GetSurvivingGlobalObjectsCount());

    { v8::HandleScope scope;
      LocalContext context;
      v8_compile("/aaa/")->Run();
    }
    CHECK_EQ(count, GetSurvivingGlobalObjectsCount());

    { v8::HandleScope scope;
      const char* extension_list[] = { "v8/gc" };
      v8::ExtensionConfiguration extensions(1, extension_list);
      LocalContext context(&extensions);
      v8_compile("gc();")->Run();
    }
    CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
  }
}


THREADED_TEST(CheckForCrossContextObjectLiterals) {
  v8::V8::Initialize();

  const int nof = 2;
  const char* sources[nof] = {
    "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
    "Object()"
  };

  for (int i = 0; i < nof; i++) {
    const char* source = sources[i];
    { v8::HandleScope scope;
      LocalContext context;
      CompileRun(source);
    }
    { v8::HandleScope scope;
      LocalContext context;
      CompileRun(source);
    }
  }
}


static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
  v8::HandleScope inner;
  env->Enter();
  v8::Handle<Value> three = v8_num(3);
  v8::Handle<Value> value = inner.Close(three);
  env->Exit();
  return value;
}


THREADED_TEST(NestedHandleScopeAndContexts) {
  v8::HandleScope outer;
  v8::Persistent<Context> env = Context::New();
  env->Enter();
  v8::Handle<Value> value = NestedScope(env);
  v8::Handle<String> str = value->ToString();
  env->Exit();
  env.Dispose();
}


THREADED_TEST(ExternalAllocatedMemory) {
  v8::HandleScope outer;
  const int kSize = 1024*1024;
  CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
  CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
}


THREADED_TEST(DisposeEnteredContext) {
  v8::HandleScope scope;
  LocalContext outer;
  { v8::Persistent<v8::Context> inner = v8::Context::New();
    inner->Enter();
    inner.Dispose();
    inner.Clear();
    inner->Exit();
  }
}


// Regression test for issue 54, object templates with internal fields
// but no accessors or interceptors did not get their internal field
// count set on instances.
THREADED_TEST(Regress54) {
  v8::HandleScope outer;
  LocalContext context;
  static v8::Persistent<v8::ObjectTemplate> templ;
  if (templ.IsEmpty()) {
    v8::HandleScope inner;
    v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
    local->SetInternalFieldCount(1);
    templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
  }
  v8::Handle<v8::Object> result = templ->NewInstance();
  CHECK_EQ(1, result->InternalFieldCount());
}


// If part of the threaded tests, this test makes ThreadingTest fail
// on mac.
TEST(CatchStackOverflow) {
  v8::HandleScope scope;
  LocalContext context;
  v8::TryCatch try_catch;
  v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
    "function f() {"
    "  return f();"
    "}"
    ""
    "f();"));
  v8::Handle<v8::Value> result = script->Run();
  CHECK(result.IsEmpty());
}


THREADED_TEST(TryCatchSourceInfo) {
  v8::HandleScope scope;
  LocalContext context;
  v8::Handle<v8::String> source = v8::String::New(
      "function Foo() {\n"
      "  return Bar();\n"
      "}\n"
      "\n"
      "function Bar() {\n"
      "  return Baz();\n"
      "}\n"
      "\n"
      "function Baz() {\n"
      "  throw 'nirk';\n"
      "}\n"
      "\n"
      "Foo();\n");
  v8::Handle<v8::Script> script =
      v8::Script::Compile(source, v8::String::New("test.js"));
  v8::TryCatch try_catch;
  v8::Handle<v8::Value> result = script->Run();
  CHECK(result.IsEmpty());
  CHECK(try_catch.HasCaught());
  v8::Handle<v8::Message> message = try_catch.Message();
  CHECK(!message.IsEmpty());
  CHECK_EQ(10, message->GetLineNumber());
  CHECK_EQ(91, message->GetStartPosition());
  CHECK_EQ(92, message->GetEndPosition());
  CHECK_EQ(2, message->GetStartColumn());
  CHECK_EQ(3, message->GetEndColumn());
  v8::String::AsciiValue line(message->GetSourceLine());
  CHECK_EQ("  throw 'nirk';", *line);
  v8::String::AsciiValue name(message->GetScriptResourceName());
  CHECK_EQ("test.js", *name);
}


THREADED_TEST(CompilationCache) {
  v8::HandleScope scope;
  LocalContext context;
  v8::Handle<v8::String> source0 = v8::String::New("1234");
  v8::Handle<v8::String> source1 = v8::String::New("1234");
  v8::Handle<v8::Script> script0 =
      v8::Script::Compile(source0, v8::String::New("test.js"));
  v8::Handle<v8::Script> script1 =
      v8::Script::Compile(source1, v8::String::New("test.js"));
  v8::Handle<v8::Script> script2 =
      v8::Script::Compile(source0);  // different origin
  CHECK_EQ(1234, script0->Run()->Int32Value());
  CHECK_EQ(1234, script1->Run()->Int32Value());
  CHECK_EQ(1234, script2->Run()->Int32Value());
}


static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
  ApiTestFuzzer::Fuzz();
  return v8_num(42);
}


THREADED_TEST(CallbackFunctionName) {
  v8::HandleScope scope;
  LocalContext context;
  Local<ObjectTemplate> t = ObjectTemplate::New();
  t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
  context->Global()->Set(v8_str("obj"), t->NewInstance());
  v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
  CHECK(value->IsString());
  v8::String::AsciiValue name(value);
  CHECK_EQ("asdf", *name);
}


THREADED_TEST(DateAccess) {
  v8::HandleScope scope;
  LocalContext context;
  v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
  CHECK(date->IsDate());
  CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue());
}


void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
  v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
  v8::Handle<v8::Array> props = obj->GetPropertyNames();
  CHECK_EQ(elmc, props->Length());
  for (int i = 0; i < elmc; i++) {
    v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
    CHECK_EQ(elmv[i], *elm);
  }
}


THREADED_TEST(PropertyEnumeration) {
  v8::HandleScope scope;
  LocalContext context;
  v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
      "var result = [];"
      "result[0] = {};"
      "result[1] = {a: 1, b: 2};"
      "result[2] = [1, 2, 3];"
      "var proto = {x: 1, y: 2, z: 3};"
      "var x = { __proto__: proto, w: 0, z: 1 };"
      "result[3] = x;"
      "result;"))->Run();
  v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj);
  CHECK_EQ(4, elms->Length());
  int elmc0 = 0;
  const char** elmv0 = NULL;
  CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
  int elmc1 = 2;
  const char* elmv1[] = {"a", "b"};
  CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
  int elmc2 = 3;
  const char* elmv2[] = {"0", "1", "2"};
  CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
  int elmc3 = 4;
  const char* elmv3[] = {"w", "z", "x", "y"};
  CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
}