summaryrefslogtreecommitdiffstats
path: root/v8/test/cctest
diff options
context:
space:
mode:
authorFeng Qian <fqian@google.com>2009-04-16 10:13:32 -0700
committerFeng Qian <fqian@google.com>2009-04-16 10:13:32 -0700
commite62a68bd8f6c9cb81ff8757c55d64ae13a277eab (patch)
tree93d1837c5c326c12d859500aa63c0db55fc7e985 /v8/test/cctest
parent38b27eb8bd9bb83aa3b8eb1d48efcab50bff6a5a (diff)
downloadexternal_webkit-e62a68bd8f6c9cb81ff8757c55d64ae13a277eab.zip
external_webkit-e62a68bd8f6c9cb81ff8757c55d64ae13a277eab.tar.gz
external_webkit-e62a68bd8f6c9cb81ff8757c55d64ae13a277eab.tar.bz2
Import V8 r1682 from trunk.
v8/REVISION has svn URL and revision number: http://v8.googlecode.com/svn/trunk@1682
Diffstat (limited to 'v8/test/cctest')
-rw-r--r--v8/test/cctest/SConscript84
-rw-r--r--v8/test/cctest/cctest.cc126
-rw-r--r--v8/test/cctest/cctest.h75
-rw-r--r--v8/test/cctest/cctest.status50
-rw-r--r--v8/test/cctest/test-alloc.cc146
-rw-r--r--v8/test/cctest/test-api.cc6148
-rw-r--r--v8/test/cctest/test-assembler-arm.cc227
-rw-r--r--v8/test/cctest/test-assembler-ia32.cc395
-rw-r--r--v8/test/cctest/test-ast.cc97
-rw-r--r--v8/test/cctest/test-compiler.cc318
-rw-r--r--v8/test/cctest/test-conversions.cc130
-rw-r--r--v8/test/cctest/test-debug.cc4087
-rw-r--r--v8/test/cctest/test-decls.cc594
-rw-r--r--v8/test/cctest/test-disasm-arm.cc281
-rw-r--r--v8/test/cctest/test-disasm-ia32.cc384
-rw-r--r--v8/test/cctest/test-flags.cc234
-rw-r--r--v8/test/cctest/test-hashmap.cc123
-rw-r--r--v8/test/cctest/test-heap.cc785
-rw-r--r--v8/test/cctest/test-list.cc67
-rw-r--r--v8/test/cctest/test-lock.cc63
-rw-r--r--v8/test/cctest/test-log-ia32.cc314
-rw-r--r--v8/test/cctest/test-mark-compact.cc314
-rw-r--r--v8/test/cctest/test-platform-linux.cc80
-rw-r--r--v8/test/cctest/test-platform-macos.cc10
-rw-r--r--v8/test/cctest/test-platform-nullos.cc80
-rw-r--r--v8/test/cctest/test-platform-win32.cc26
-rw-r--r--v8/test/cctest/test-regexp.cc1536
-rw-r--r--v8/test/cctest/test-serialize.cc268
-rw-r--r--v8/test/cctest/test-sockets.cc161
-rw-r--r--v8/test/cctest/test-spaces.cc248
-rw-r--r--v8/test/cctest/test-strings.cc389
-rw-r--r--v8/test/cctest/test-threads.cc54
-rw-r--r--v8/test/cctest/test-utils.cc179
-rw-r--r--v8/test/cctest/testcfg.py108
34 files changed, 18181 insertions, 0 deletions
diff --git a/v8/test/cctest/SConscript b/v8/test/cctest/SConscript
new file mode 100644
index 0000000..091768f
--- /dev/null
+++ b/v8/test/cctest/SConscript
@@ -0,0 +1,84 @@
+# Copyright 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.
+
+import sys
+from os.path import join, dirname, abspath
+root_dir = dirname(File('SConstruct').rfile().abspath)
+sys.path.append(join(root_dir, 'tools'))
+Import('context object_files')
+
+
+SOURCES = {
+ 'all': [
+ 'test-alloc.cc',
+ 'test-api.cc',
+ 'test-ast.cc',
+ 'test-compiler.cc',
+ 'test-conversions.cc',
+ 'test-debug.cc',
+ 'test-decls.cc',
+ 'test-flags.cc',
+ 'test-hashmap.cc',
+ 'test-heap.cc',
+ 'test-list.cc',
+ 'test-lock.cc',
+ 'test-mark-compact.cc',
+ 'test-regexp.cc',
+ 'test-serialize.cc',
+ 'test-sockets.cc',
+ 'test-spaces.cc',
+ 'test-strings.cc',
+ 'test-threads.cc',
+ 'test-utils.cc'
+ ],
+ 'arch:arm': ['test-assembler-arm.cc', 'test-disasm-arm.cc'],
+ 'arch:ia32': [
+ 'test-assembler-ia32.cc',
+ 'test-disasm-ia32.cc',
+ 'test-log-ia32.cc'
+ ],
+ 'os:linux': ['test-platform-linux.cc'],
+ 'os:macos': ['test-platform-macos.cc'],
+ 'os:nullos': ['test-platform-nullos.cc'],
+ 'os:win32': ['test-platform-win32.cc']
+}
+
+
+def Build():
+ cctest_files = context.GetRelevantSources(SOURCES)
+ env = Environment()
+ env.Replace(**context.flags['cctest'])
+ context.ApplyEnvOverrides(env)
+ # There seems to be a glitch in the way scons decides where to put
+ # PDB files when compiling using MSVC so we specify it manually.
+ # This should not affect any other platforms.
+ return env.Program('cctest', ['cctest.cc', cctest_files, object_files],
+ PDB='cctest.exe.pdb')
+
+
+program = Build()
+Return('program')
diff --git a/v8/test/cctest/cctest.cc b/v8/test/cctest/cctest.cc
new file mode 100644
index 0000000..2807c8b
--- /dev/null
+++ b/v8/test/cctest/cctest.cc
@@ -0,0 +1,126 @@
+// Copyright 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 <v8.h>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+#include "cctest.h"
+#include "debug.h"
+
+
+CcTest* CcTest::last_ = NULL;
+
+
+CcTest::CcTest(TestFunction* callback, const char* file, const char* name,
+ const char* dependency, bool enabled)
+ : callback_(callback), name_(name), dependency_(dependency), prev_(last_) {
+ // Find the base name of this test (const_cast required on Windows).
+ char *basename = strrchr(const_cast<char *>(file), '/');
+ if (!basename) {
+ basename = strrchr(const_cast<char *>(file), '\\');
+ }
+ if (!basename) {
+ basename = v8::internal::StrDup(file);
+ } else {
+ basename = v8::internal::StrDup(basename + 1);
+ }
+ // Drop the extension, if there is one.
+ char *extension = strrchr(basename, '.');
+ if (extension) *extension = 0;
+ // Install this test in the list of tests
+ file_ = basename;
+ enabled_ = enabled;
+ prev_ = last_;
+ last_ = this;
+}
+
+
+static void PrintTestList(CcTest* current) {
+ if (current == NULL) return;
+ PrintTestList(current->prev());
+ if (current->dependency() != NULL) {
+ printf("%s/%s<%s\n",
+ current->file(), current->name(), current->dependency());
+ } else {
+ printf("%s/%s<\n", current->file(), current->name());
+ }
+}
+
+
+int main(int argc, char* argv[]) {
+ v8::internal::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
+ int tests_run = 0;
+ bool print_run_count = true;
+ for (int i = 1; i < argc; i++) {
+ char* arg = argv[i];
+ if (strcmp(arg, "--list") == 0) {
+ PrintTestList(CcTest::last());
+ print_run_count = false;
+
+ } else {
+ char* arg_copy = v8::internal::StrDup(arg);
+ char* testname = strchr(arg_copy, '/');
+ if (testname) {
+ // Split the string in two by nulling the slash and then run
+ // exact matches.
+ *testname = 0;
+ char* file = arg_copy;
+ char* name = testname + 1;
+ CcTest* test = CcTest::last();
+ while (test != NULL) {
+ if (test->enabled()
+ && strcmp(test->file(), file) == 0
+ && strcmp(test->name(), name) == 0) {
+ test->Run();
+ tests_run++;
+ }
+ test = test->prev();
+ }
+
+ } else {
+ // Run all tests with the specified file or test name.
+ char* file_or_name = arg_copy;
+ CcTest* test = CcTest::last();
+ while (test != NULL) {
+ if (test->enabled()
+ && (strcmp(test->file(), file_or_name) == 0
+ || strcmp(test->name(), file_or_name) == 0)) {
+ test->Run();
+ tests_run++;
+ }
+ test = test->prev();
+ }
+ }
+ v8::internal::DeleteArray<char>(arg_copy);
+ }
+ }
+ if (print_run_count && tests_run != 1)
+ printf("Ran %i tests.\n", tests_run);
+ v8::V8::Dispose();
+ return 0;
+}
diff --git a/v8/test/cctest/cctest.h b/v8/test/cctest/cctest.h
new file mode 100644
index 0000000..a95645e
--- /dev/null
+++ b/v8/test/cctest/cctest.h
@@ -0,0 +1,75 @@
+// Copyright 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.
+
+#ifndef CCTEST_H_
+#define CCTEST_H_
+
+#ifndef TEST
+#define TEST(Name) \
+ static void Test##Name(); \
+ CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, true); \
+ static void Test##Name()
+#endif
+
+#ifndef DEPENDENT_TEST
+#define DEPENDENT_TEST(Name, Dep) \
+ static void Test##Name(); \
+ CcTest register_test_##Name(Test##Name, __FILE__, #Name, #Dep, true); \
+ static void Test##Name()
+#endif
+
+#ifndef DISABLED_TEST
+#define DISABLED_TEST(Name) \
+ static void Test##Name(); \
+ CcTest register_test_##Name(Test##Name, __FILE__, #Name, NULL, false); \
+ static void Test##Name()
+#endif
+
+class CcTest {
+ public:
+ typedef void (TestFunction)();
+ CcTest(TestFunction* callback, const char* file, const char* name,
+ const char* dependency, bool enabled);
+ void Run() { callback_(); }
+ static int test_count();
+ static CcTest* last() { return last_; }
+ CcTest* prev() { return prev_; }
+ const char* file() { return file_; }
+ const char* name() { return name_; }
+ const char* dependency() { return dependency_; }
+ bool enabled() { return enabled_; }
+ private:
+ TestFunction* callback_;
+ const char* file_;
+ const char* name_;
+ const char* dependency_;
+ bool enabled_;
+ static CcTest* last_;
+ CcTest* prev_;
+};
+
+#endif // ifndef CCTEST_H_
diff --git a/v8/test/cctest/cctest.status b/v8/test/cctest/cctest.status
new file mode 100644
index 0000000..9a3f819
--- /dev/null
+++ b/v8/test/cctest/cctest.status
@@ -0,0 +1,50 @@
+# Copyright 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.
+
+prefix cctest
+
+# BUG(96): Fix this flaky test.
+test-debug/ThreadedDebugging: PASS || FAIL
+
+# BUG(281): This test fails on some Linuxes.
+test-debug/DebuggerAgent: PASS, (PASS || FAIL) if $system == linux
+
+[ $arch == arm ]
+
+test-debug: SKIP
+
+# BUG(113): Test seems flaky on ARM.
+test-spaces/LargeObjectSpace: PASS || FAIL
+
+# BUG(240): Test seems flaky on ARM.
+test-api/RegExpInterruption: SKIP
+
+# BUG(271): During exception propagation, we compare pointers into the
+# stack. These tests fail on the ARM simulator because the C++ and
+# the JavaScript stacks are separate.
+test-api/ExceptionOrder: PASS || FAIL
+test-api/TryCatchInTryFinally: PASS || FAIL
diff --git a/v8/test/cctest/test-alloc.cc b/v8/test/cctest/test-alloc.cc
new file mode 100644
index 0000000..9996eeb
--- /dev/null
+++ b/v8/test/cctest/test-alloc.cc
@@ -0,0 +1,146 @@
+// 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 "v8.h"
+#include "accessors.h"
+#include "top.h"
+
+#include "cctest.h"
+
+
+using namespace v8::internal;
+
+
+static Object* AllocateAfterFailures() {
+ static int attempts = 0;
+ if (++attempts < 3) return Failure::RetryAfterGC(0);
+
+ // New space.
+ NewSpace* new_space = Heap::new_space();
+ static const int kNewSpaceFillerSize = ByteArray::SizeFor(0);
+ while (new_space->Available() > kNewSpaceFillerSize) {
+ CHECK(!Heap::AllocateByteArray(0)->IsFailure());
+ }
+ CHECK(!Heap::AllocateByteArray(100)->IsFailure());
+ CHECK(!Heap::AllocateFixedArray(100, NOT_TENURED)->IsFailure());
+
+ // Make sure we can allocate through optimized allocation functions
+ // for specific kinds.
+ CHECK(!Heap::AllocateFixedArray(100)->IsFailure());
+ CHECK(!Heap::AllocateHeapNumber(0.42)->IsFailure());
+ CHECK(!Heap::AllocateArgumentsObject(Smi::FromInt(87), 10)->IsFailure());
+ Object* object = Heap::AllocateJSObject(*Top::object_function());
+ CHECK(!Heap::CopyJSObject(JSObject::cast(object))->IsFailure());
+
+ // Old data space.
+ OldSpace* old_data_space = Heap::old_data_space();
+ static const int kOldDataSpaceFillerSize = SeqAsciiString::SizeFor(0);
+ while (old_data_space->Available() > kOldDataSpaceFillerSize) {
+ CHECK(!Heap::AllocateRawAsciiString(0, TENURED)->IsFailure());
+ }
+ CHECK(!Heap::AllocateRawAsciiString(100, TENURED)->IsFailure());
+
+ // Large object space.
+ while (!Heap::OldGenerationAllocationLimitReached()) {
+ CHECK(!Heap::AllocateFixedArray(10000, TENURED)->IsFailure());
+ }
+ CHECK(!Heap::AllocateFixedArray(10000, TENURED)->IsFailure());
+
+ // Map space.
+ MapSpace* map_space = Heap::map_space();
+ static const int kMapSpaceFillerSize = Map::kSize;
+ InstanceType instance_type = JS_OBJECT_TYPE;
+ int instance_size = JSObject::kHeaderSize;
+ while (map_space->Available() > kMapSpaceFillerSize) {
+ CHECK(!Heap::AllocateMap(instance_type, instance_size)->IsFailure());
+ }
+ CHECK(!Heap::AllocateMap(instance_type, instance_size)->IsFailure());
+
+ // Test that we can allocate in old pointer space and code space.
+ CHECK(!Heap::AllocateFixedArray(100, TENURED)->IsFailure());
+ CHECK(!Heap::CopyCode(Builtins::builtin(Builtins::Illegal))->IsFailure());
+
+ // Return success.
+ return Smi::FromInt(42);
+}
+
+
+static Handle<Object> Test() {
+ CALL_HEAP_FUNCTION(AllocateAfterFailures(), Object);
+}
+
+
+TEST(StressHandles) {
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ v8::HandleScope scope;
+ env->Enter();
+ Handle<Object> o = Test();
+ CHECK(o->IsSmi() && Smi::cast(*o)->value() == 42);
+ env->Exit();
+}
+
+
+static Object* TestAccessorGet(Object* object, void*) {
+ return AllocateAfterFailures();
+}
+
+
+const AccessorDescriptor kDescriptor = {
+ TestAccessorGet,
+ 0,
+ 0
+};
+
+
+TEST(StressJS) {
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ v8::HandleScope scope;
+ env->Enter();
+ Handle<JSFunction> function =
+ Factory::NewFunction(Factory::function_symbol(), Factory::null_value());
+ // Force the creation of an initial map and set the code to
+ // something empty.
+ Factory::NewJSObject(function);
+ function->set_code(Builtins::builtin(Builtins::EmptyFunction));
+ // Patch the map to have an accessor for "get".
+ Handle<Map> map(function->initial_map());
+ Handle<DescriptorArray> instance_descriptors(map->instance_descriptors());
+ Handle<Proxy> proxy = Factory::NewProxy(&kDescriptor);
+ instance_descriptors = Factory::CopyAppendProxyDescriptor(
+ instance_descriptors,
+ Factory::NewStringFromAscii(Vector<const char>("get", 3)),
+ proxy,
+ static_cast<PropertyAttributes>(0));
+ map->set_instance_descriptors(*instance_descriptors);
+ // Add the Foo constructor the global object.
+ env->Global()->Set(v8::String::New("Foo"), v8::Utils::ToLocal(function));
+ // Call the accessor through JavaScript.
+ v8::Handle<v8::Value> result =
+ v8::Script::Compile(v8::String::New("(new Foo).get"))->Run();
+ CHECK_EQ(42, result->Int32Value());
+ env->Exit();
+}
diff --git a/v8/test/cctest/test-api.cc b/v8/test/cctest/test-api.cc
new file mode 100644
index 0000000..dd705d2
--- /dev/null
+++ b/v8/test/cctest/test-api.cc
@@ -0,0 +1,6148 @@
+// 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) {
+ }
+ ~ApiTestFuzzer() { delete gate_; }
+
+ // 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)
+ : fuzzer_(NULL), 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(ScriptMakingExternalString) {
+ TestResource::dispose_count = 0;
+ uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ Local<String> source = String::New(two_byte_source);
+ bool success = source->MakeExternal(new TestResource(two_byte_source));
+ CHECK(success);
+ 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, TestResource::dispose_count);
+ }
+ v8::internal::Heap::CollectAllGarbage();
+ CHECK_EQ(1, TestResource::dispose_count);
+}
+
+
+THREADED_TEST(ScriptMakingExternalAsciiString) {
+ TestAsciiResource::dispose_count = 0;
+ const char* c_source = "1 + 2 * 3";
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ Local<String> source = v8_str(c_source);
+ bool success = source->MakeExternal(
+ new TestAsciiResource(i::StrDup(c_source)));
+ CHECK(success);
+ 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(UsingExternalString) {
+ v8::HandleScope scope;
+ uint16_t* two_byte_string = AsciiToTwoByteString("test string");
+ Local<String> string = String::NewExternal(new TestResource(two_byte_string));
+ i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
+ i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
+ CHECK(isymbol->IsSymbol());
+}
+
+
+THREADED_TEST(UsingExternalAsciiString) {
+ v8::HandleScope scope;
+ const char* one_byte_string = "test string";
+ Local<String> string = String::NewExternal(
+ new TestAsciiResource(i::StrDup(one_byte_string)));
+ i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
+ i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
+ CHECK(isymbol->IsSymbol());
+}
+
+
+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());
+}
+
+
+THREADED_TEST(UndefinedIsNotEnumerable) {
+ v8::HandleScope scope;
+ LocalContext env;
+ v8::Handle<Value> result = Script::Compile(v8_str(
+ "this.propertyIsEnumerable(undefined)"))->Run();
+ CHECK(result->IsFalse());
+}
+
+
+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(IdentityHash) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ // Ensure that the test starts with an fresh heap to test whether the hash
+ // code is based on the address.
+ i::Heap::CollectAllGarbage();
+ Local<v8::Object> obj = v8::Object::New();
+ int hash = obj->GetIdentityHash();
+ int hash1 = obj->GetIdentityHash();
+ CHECK_EQ(hash, hash1);
+ int hash2 = v8::Object::New()->GetIdentityHash();
+ // Since the identity hash is essentially a random number two consecutive
+ // objects should not be assigned the same hash code. If the test below fails
+ // the random number generator should be evaluated.
+ CHECK_NE(hash, hash2);
+ i::Heap::CollectAllGarbage();
+ int hash3 = v8::Object::New()->GetIdentityHash();
+ // Make sure that the identity hash is not based on the initial address of
+ // the object alone. If the test below fails the random number generator
+ // should be evaluated.
+ CHECK_NE(hash, hash3);
+ int hash4 = obj->GetIdentityHash();
+ CHECK_EQ(hash, hash4);
+}
+
+
+THREADED_TEST(HiddenProperties) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ v8::Local<v8::Object> obj = v8::Object::New();
+ v8::Local<v8::String> key = v8_str("api-test::hidden-key");
+ v8::Local<v8::String> empty = v8_str("");
+ v8::Local<v8::String> prop_name = v8_str("prop_name");
+
+ i::Heap::CollectAllGarbage();
+
+ CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
+ CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+
+ i::Heap::CollectAllGarbage();
+
+ // Make sure we do not find the hidden property.
+ CHECK(!obj->Has(empty));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->Get(empty)->IsUndefined());
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->Set(empty, v8::Integer::New(2003)));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK_EQ(2003, obj->Get(empty)->Int32Value());
+
+ i::Heap::CollectAllGarbage();
+
+ // Add another property and delete it afterwards to force the object in
+ // slow case.
+ CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+ CHECK(obj->Delete(prop_name));
+ CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
+
+ i::Heap::CollectAllGarbage();
+
+ CHECK(obj->DeleteHiddenValue(key));
+ CHECK(obj->GetHiddenValue(key).IsEmpty());
+}
+
+
+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);
+
+ // Make sure unaligned pointers are wrapped properly.
+ char* data = i::StrDup("0123456789");
+ Local<v8::Value> zero = v8::External::Wrap(&data[0]);
+ Local<v8::Value> one = v8::External::Wrap(&data[1]);
+ Local<v8::Value> two = v8::External::Wrap(&data[2]);
+ Local<v8::Value> three = v8::External::Wrap(&data[3]);
+
+ char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
+ CHECK_EQ('0', *char_ptr);
+ char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
+ CHECK_EQ('1', *char_ptr);
+ char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
+ CHECK_EQ('2', *char_ptr);
+ char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
+ CHECK_EQ('3', *char_ptr);
+ i::DeleteArray(data);
+}
+
+
+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"));
+}
+
+
+v8::Handle<Value> CCatcher(const v8::Arguments& args) {
+ if (args.Length() < 1) return v8::Boolean::New(false);
+ v8::HandleScope scope;
+ v8::TryCatch try_catch;
+ Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
+ CHECK(!try_catch.HasCaught() || result.IsEmpty());
+ return v8::Boolean::New(try_catch.HasCaught());
+}
+
+
+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(APIThrowTryCatch) {
+ 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;
+ CompileRun("ThrowFromC();");
+ CHECK(try_catch.HasCaught());
+}
+
+
+// Test that a try-finally block doesn't shadow a try-catch block
+// when setting up an external handler.
+//
+// BUG(271): Some of the exception propagation does not work on the
+// ARM simulator because the simulator separates the C++ stack and the
+// JS stack. This test therefore fails on the simulator. The test is
+// not threaded to allow the threading tests to run on the simulator.
+TEST(TryCatchInTryFinally) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->Set(v8_str("CCatcher"),
+ v8::FunctionTemplate::New(CCatcher));
+ LocalContext context(0, templ);
+ Local<Value> result = CompileRun("try {"
+ " try {"
+ " CCatcher('throw 7;');"
+ " } finally {"
+ " }"
+ "} catch (e) {"
+ "}");
+ CHECK(result->IsTrue());
+}
+
+
+static void receive_message(v8::Handle<v8::Message> message,
+ v8::Handle<v8::Value> data) {
+ message->Get();
+ message_received = true;
+}
+
+
+TEST(APIThrowMessage) {
+ message_received = false;
+ v8::HandleScope scope;
+ v8::V8::AddMessageListener(receive_message);
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->Set(v8_str("ThrowFromC"),
+ v8::FunctionTemplate::New(ThrowFromC));
+ LocalContext context(0, templ);
+ CompileRun("ThrowFromC();");
+ CHECK(message_received);
+ v8::V8::RemoveMessageListeners(check_message);
+}
+
+
+TEST(APIThrowMessageAndVerboseTryCatch) {
+ message_received = false;
+ v8::HandleScope scope;
+ v8::V8::AddMessageListener(receive_message);
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->Set(v8_str("ThrowFromC"),
+ v8::FunctionTemplate::New(ThrowFromC));
+ LocalContext context(0, templ);
+ v8::TryCatch try_catch;
+ try_catch.SetVerbose(true);
+ Local<Value> result = CompileRun("ThrowFromC();");
+ CHECK(try_catch.HasCaught());
+ CHECK(result.IsEmpty());
+ CHECK(message_received);
+ v8::V8::RemoveMessageListeners(check_message);
+}
+
+
+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(result.IsEmpty());
+ 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();
+}
+
+
+THREADED_TEST(EvalInTryFinally) {
+ v8::HandleScope scope;
+ LocalContext context;
+ v8::TryCatch try_catch;
+ CompileRun("(function() {"
+ " try {"
+ " eval('asldkf (*&^&*^');"
+ " } finally {"
+ " return;"
+ " }"
+ "})()");
+ CHECK(!try_catch.HasCaught());
+}
+
+
+// This test works by making a stack of alternating JavaScript and C
+// activations. These activations set up exception handlers with regular
+// intervals, one interval for C activations and another for JavaScript
+// activations. When enough activations have been created an exception is
+// thrown and we check that the right activation catches the exception and that
+// no other activations do. The right activation is always the topmost one with
+// a handler, regardless of whether it is in JavaScript or C.
+//
+// The notation used to describe a test case looks like this:
+//
+// *JS[4] *C[3] @JS[2] C[1] JS[0]
+//
+// Each entry is an activation, either JS or C. The index is the count at that
+// level. Stars identify activations with exception handlers, the @ identifies
+// the exception handler that should catch the exception.
+//
+// BUG(271): Some of the exception propagation does not work on the
+// ARM simulator because the simulator separates the C++ stack and the
+// JS stack. This test therefore fails on the simulator. The test is
+// not threaded to allow the threading tests to run on the simulator.
+TEST(ExceptionOrder) {
+ v8::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("null==undetectable", true);
+ ExpectBoolean("undetectable==undefined", true);
+ ExpectBoolean("undefined==undetectable", true);
+ ExpectBoolean("undetectable==undetectable", true);
+
+
+ ExpectBoolean("undetectable===null", false);
+ ExpectBoolean("null===undetectable", false);
+ ExpectBoolean("undetectable===undefined", false);
+ ExpectBoolean("undefined===undetectable", false);
+ ExpectBoolean("undetectable===undetectable", true);
+}
+
+
+THREADED_TEST(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("null==undetectable", true);
+ ExpectBoolean("undetectable==undefined", true);
+ ExpectBoolean("undefined==undetectable", true);
+ ExpectBoolean("undetectable==undetectable", true);
+
+
+ ExpectBoolean("undetectable===null", false);
+ ExpectBoolean("null===undetectable", false);
+ ExpectBoolean("undetectable===undefined", false);
+ ExpectBoolean("undefined===undetectable", false);
+ ExpectBoolean("undetectable===undetectable", true);
+}
+
+
+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) {
+ report_count = 0;
+ 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);
+ v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
+}
+
+
+TEST(CompilationErrorUsingTryCatchHandler) {
+ v8::HandleScope scope;
+ LocalContext env;
+ v8::TryCatch try_catch;
+ Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
+ CHECK_NE(NULL, *try_catch.Exception());
+ CHECK(try_catch.HasCaught());
+}
+
+
+TEST(TryCatchFinallyUsingTryCatchHandler) {
+ v8::HandleScope scope;
+ LocalContext env;
+ v8::TryCatch try_catch;
+ Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
+ CHECK(!try_catch.HasCaught());
+ Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ Script::Compile(v8_str("(function() {"
+ "try { throw ''; } finally { return; }"
+ "})()"))->Run();
+ CHECK(!try_catch.HasCaught());
+ Script::Compile(v8_str("(function()"
+ " { try { throw ''; } finally { throw 0; }"
+ "})()"))->Run();
+ CHECK(try_catch.HasCaught());
+}
+
+
+// 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(CrossDomainIsPropertyEnumerable) {
+ 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());
+
+ // env1.prop is enumerable in env2.
+ Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
+ {
+ Context::Scope scope_env2(env2);
+ Local<Value> result = Script::Compile(test)->Run();
+ CHECK(result->IsTrue());
+ }
+
+ // Change env2 to a different domain and test again.
+ env2->SetSecurityToken(bar);
+ {
+ Context::Scope scope_env2(env2);
+ Local<Value> result = Script::Compile(test)->Run();
+ CHECK(result->IsFalse());
+ }
+
+ 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_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());
+
+ value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
+ CHECK(value->IsFalse());
+
+ // 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());
+
+ value =
+ v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
+ CHECK(value->IsTrue());
+
+ // Enumeration doesn't enumerate accessors from inaccessible objects in
+ // the prototype chain even if the accessors are in themselves accessible.
+ Local<Value> result =
+ CompileRun("(function(){var obj = {'__proto__':other};"
+ "for (var p in obj)"
+ " if (p == 'accessible_prop' || p == 'blocked_prop') {"
+ " return false;"
+ " }"
+ "return true;})()");
+ CHECK(result->IsTrue());
+
+ 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.
+ CompileRun("function testProp(obj) {"
+ " for (var i = 0; i < 10; i++) obj.prop = 1;"
+ " for (var j = 0; j < 10; j++) obj.prop;"
+ " return obj.prop"
+ "}");
+ value = CompileRun("testProp(obj)");
+ CHECK(value->IsNumber());
+ CHECK_EQ(1, value->Int32Value());
+ CHECK_EQ(21, named_access_count);
+
+ // Check that the named access-control function is called every time.
+ CompileRun("var p = 'prop';"
+ "function testKeyed(obj) {"
+ " for (var i = 0; i < 10; i++) obj[p] = 1;"
+ " for (var j = 0; j < 10; j++) obj[p];"
+ " return obj[p];"
+ "}");
+ // Use obj which requires access checks. No inline caching is used
+ // in that case.
+ value = CompileRun("testKeyed(obj)");
+ CHECK(value->IsNumber());
+ CHECK_EQ(1, value->Int32Value());
+ CHECK_EQ(42, named_access_count);
+ // Force the inline caches into generic state and try again.
+ CompileRun("testKeyed({ a: 0 })");
+ CompileRun("testKeyed({ b: 0 })");
+ value = CompileRun("testKeyed(obj)");
+ CHECK(value->IsNumber());
+ CHECK_EQ(1, value->Int32Value());
+ CHECK_EQ(63, named_access_count);
+
+ // Check that the indexed access-control function is called every time.
+ CompileRun("function testIndexed(obj) {"
+ " for (var i = 0; i < 10; i++) obj[0] = 1;"
+ " for (var j = 0; j < 10; j++) obj[0];"
+ " return obj[0]"
+ "}");
+ value = CompileRun("testIndexed(obj)");
+ CHECK(value->IsNumber());
+ CHECK_EQ(1, value->Int32Value());
+ CHECK_EQ(21, indexed_access_count);
+ // Force the inline caches into generic state.
+ CompileRun("testIndexed(new Array(1))");
+ // Test that the indexed access check is called.
+ value = CompileRun("testIndexed(obj)");
+ CHECK(value->IsNumber());
+ CHECK_EQ(1, value->Int32Value());
+ CHECK_EQ(42, indexed_access_count);
+
+ // Check that the named access check is called when invoking
+ // functions on an object that requires access checks.
+ CompileRun("obj.f = function() {}");
+ CompileRun("function testCallNormal(obj) {"
+ " for (var i = 0; i < 10; i++) obj.f();"
+ "}");
+ CompileRun("testCallNormal(obj)");
+ CHECK_EQ(74, named_access_count);
+
+ // Force obj into slow case.
+ value = CompileRun("delete obj.prop");
+ CHECK(value->BooleanValue());
+ // Force inline caches into dictionary probing mode.
+ CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
+ // Test that the named access check is called.
+ value = CompileRun("testProp(obj);");
+ CHECK(value->IsNumber());
+ CHECK_EQ(1, value->Int32Value());
+ CHECK_EQ(96, named_access_count);
+
+ // Force the call inline cache into dictionary probing mode.
+ CompileRun("o.f = function() {}; testCallNormal(o)");
+ // Test that the named access check is still called for each
+ // invocation of the function.
+ value = CompileRun("testCallNormal(obj)");
+ CHECK_EQ(106, named_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(EvalAliasedDynamic) {
+ v8::HandleScope scope;
+ LocalContext current;
+
+ // This sets 'global' to the real global object (as opposed to the
+ // proxy). It is highly implementation dependent, so take care.
+ current->Global()->Set(v8_str("global"), current->Global()->GetPrototype());
+
+ // Tests where aliased eval can only be resolved dynamically.
+ Local<Script> script =
+ Script::Compile(v8_str("function f(x) { "
+ " var foo = 2;"
+ " with (x) { return eval('foo'); }"
+ "}"
+ "foo = 0;"
+ "result1 = f(new Object());"
+ "result2 = f(global);"
+ "var x = new Object();"
+ "x.eval = function(x) { return 1; };"
+ "result3 = f(x);"));
+ script->Run();
+ CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
+ CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
+ CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
+
+ v8::TryCatch try_catch;
+ script =
+ Script::Compile(v8_str("function f(x) { "
+ " var bar = 2;"
+ " with (x) { return eval('bar'); }"
+ "}"
+ "f(global)"));
+ script->Run();
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+}
+
+
+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 other context.
+ script =
+ Script::Compile(v8_str("other.eval('na = 1234')"));
+ script->Run();
+ CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
+ CHECK(!current->Global()->Has(v8_str("na")));
+
+ // Check that global variables in current context are not visible in other
+ // context.
+ v8::TryCatch try_catch;
+ script =
+ Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
+ Local<Value> result = script->Run();
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+
+ // Check that local variables in current context are not visible in other
+ // context.
+ script =
+ Script::Compile(v8_str("(function() { "
+ " var baz = 87;"
+ " return other.eval('baz');"
+ "})();"));
+ result = script->Run();
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+
+ // Check that global variables in the other environment are visible
+ // when evaluting code.
+ other->Global()->Set(v8_str("bis"), v8_num(1234));
+ script = Script::Compile(v8_str("other.eval('bis')"));
+ CHECK_EQ(1234, script->Run()->Int32Value());
+ CHECK(!try_catch.HasCaught());
+
+ // Check that the 'this' pointer points to the global object evaluating
+ // code.
+ other->Global()->Set(v8_str("t"), other->Global());
+ script = Script::Compile(v8_str("other.eval('this == t')"));
+ result = script->Run();
+ CHECK(result->IsTrue());
+ CHECK(!try_catch.HasCaught());
+
+ // Check that variables introduced in with-statement are not visible in
+ // other context.
+ script =
+ Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
+ result = script->Run();
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+
+ // Check that you cannot use 'eval.call' with another object than the
+ // current global object.
+ script =
+ Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
+ result = script->Run();
+ CHECK(try_catch.HasCaught());
+}
+
+
+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;
+ for (int i = 0; i < RegisterThreadedTest::count(); i++) {
+ ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
+ if (fuzzer != NULL) fuzzer->Join();
+ }
+}
+
+
+// Lets not be needlessly self-referential.
+TEST(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;
+ // We need to collect all garbage twice to be sure that everything
+ // has been collected. This is because inline caches are cleared in
+ // the first garbage collection but some of the maps have already
+ // been marked at that point. Therefore some of the maps are not
+ // collected until the second garbage collection.
+ v8::internal::Heap::CollectAllGarbage();
+ 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();
+
+ 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());
+}
+
+
+static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
+ const char* resource_name,
+ int line_offset) {
+ v8::HandleScope scope;
+ 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 + line_offset, message->GetLineNumber());
+ CHECK_EQ(91, message->GetStartPosition());
+ CHECK_EQ(92, message->GetEndPosition());
+ CHECK_EQ(2, message->GetStartColumn());
+ CHECK_EQ(3, message->GetEndColumn());
+ v8::String::AsciiValue line(message->GetSourceLine());
+ CHECK_EQ(" throw 'nirk';", *line);
+ v8::String::AsciiValue name(message->GetScriptResourceName());
+ CHECK_EQ(resource_name, *name);
+}
+
+
+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");
+
+ const char* resource_name;
+ v8::Handle<v8::Script> script;
+ resource_name = "test.js";
+ script = v8::Script::Compile(source, v8::String::New(resource_name));
+ CheckTryCatchSourceInfo(script, resource_name, 0);
+
+ resource_name = "test1.js";
+ v8::ScriptOrigin origin1(v8::String::New(resource_name));
+ script = v8::Script::Compile(source, &origin1);
+ CheckTryCatchSourceInfo(script, resource_name, 0);
+
+ resource_name = "test2.js";
+ v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
+ script = v8::Script::Compile(source, &origin2);
+ CheckTryCatchSourceInfo(script, resource_name, 7);
+}
+
+
+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);
+}
+
+
+static v8::Handle<Value> AccessorProhibitsOverwritingGetter(
+ Local<String> name,
+ const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ return v8::True();
+}
+
+
+THREADED_TEST(AccessorProhibitsOverwriting) {
+ v8::HandleScope scope;
+ LocalContext context;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetAccessor(v8_str("x"),
+ AccessorProhibitsOverwritingGetter,
+ 0,
+ v8::Handle<Value>(),
+ v8::PROHIBITS_OVERWRITING,
+ v8::ReadOnly);
+ Local<v8::Object> instance = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), instance);
+ Local<Value> value = CompileRun(
+ "obj.__defineGetter__('x', function() { return false; });"
+ "obj.x");
+ CHECK(value->BooleanValue());
+ value = CompileRun(
+ "var setter_called = false;"
+ "obj.__defineSetter__('x', function() { setter_called = true; });"
+ "obj.x = 42;"
+ "setter_called");
+ CHECK(!value->BooleanValue());
+ value = CompileRun(
+ "obj2 = {};"
+ "obj2.__proto__ = obj;"
+ "obj2.__defineGetter__('x', function() { return false; });"
+ "obj2.x");
+ CHECK(value->BooleanValue());
+ value = CompileRun(
+ "var setter_called = false;"
+ "obj2 = {};"
+ "obj2.__proto__ = obj;"
+ "obj2.__defineSetter__('x', function() { setter_called = true; });"
+ "obj2.x = 42;"
+ "setter_called");
+ CHECK(!value->BooleanValue());
+}
+
+
+static bool NamedSetAccessBlocker(Local<v8::Object> obj,
+ Local<Value> name,
+ v8::AccessType type,
+ Local<Value> data) {
+ return type != v8::ACCESS_SET;
+}
+
+
+static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
+ uint32_t key,
+ v8::AccessType type,
+ Local<Value> data) {
+ return type != v8::ACCESS_SET;
+}
+
+
+THREADED_TEST(DisableAccessChecksWhileConfiguring) {
+ v8::HandleScope scope;
+ LocalContext context;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
+ IndexedSetAccessBlocker);
+ templ->Set(v8_str("x"), v8::True());
+ Local<v8::Object> instance = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), instance);
+ Local<Value> value = CompileRun("obj.x");
+ CHECK(value->BooleanValue());
+}
+
+static bool NamedGetAccessBlocker(Local<v8::Object> obj,
+ Local<Value> name,
+ v8::AccessType type,
+ Local<Value> data) {
+ return false;
+}
+
+
+static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
+ uint32_t key,
+ v8::AccessType type,
+ Local<Value> data) {
+ return false;
+}
+
+
+
+THREADED_TEST(AccessChecksReenabledCorrectly) {
+ v8::HandleScope scope;
+ LocalContext context;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
+ IndexedGetAccessBlocker);
+ templ->Set(v8_str("a"), v8_str("a"));
+ // Add more than 8 (see kMaxFastProperties) properties
+ // so that the constructor will force copying map.
+ // Cannot sprintf, gcc complains unsafety.
+ char buf[4];
+ for (char i = '0'; i <= '9' ; i++) {
+ buf[0] = i;
+ for (char j = '0'; j <= '9'; j++) {
+ buf[1] = j;
+ for (char k = '0'; k <= '9'; k++) {
+ buf[2] = k;
+ buf[3] = 0;
+ templ->Set(v8_str(buf), v8::Number::New(k));
+ }
+ }
+ }
+
+ Local<v8::Object> instance_1 = templ->NewInstance();
+ context->Global()->Set(v8_str("obj_1"), instance_1);
+
+ Local<Value> value_1 = CompileRun("obj_1.a");
+ CHECK(value_1->IsUndefined());
+
+ Local<v8::Object> instance_2 = templ->NewInstance();
+ context->Global()->Set(v8_str("obj_2"), instance_2);
+
+ Local<Value> value_2 = CompileRun("obj_2.a");
+ CHECK(value_2->IsUndefined());
+}
+
+// This tests that access check information remains on the global
+// object template when creating contexts.
+THREADED_TEST(AccessControlRepeatedContextCreation) {
+ v8::HandleScope handle_scope;
+ v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+ global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
+ IndexedSetAccessBlocker);
+ i::Handle<i::ObjectTemplateInfo> internal_template =
+ v8::Utils::OpenHandle(*global_template);
+ CHECK(!internal_template->constructor()->IsUndefined());
+ i::Handle<i::FunctionTemplateInfo> constructor(
+ i::FunctionTemplateInfo::cast(internal_template->constructor()));
+ CHECK(!constructor->access_check_info()->IsUndefined());
+ v8::Persistent<Context> context0 = Context::New(NULL, global_template);
+ CHECK(!constructor->access_check_info()->IsUndefined());
+}
+
+
+// This test verifies that pre-compilation (aka preparsing) can be called
+// without initializing the whole VM. Thus we cannot run this test in a
+// multi-threaded setup.
+TEST(PreCompile) {
+ // TODO(155): This test would break without the initialization of V8. This is
+ // a workaround for now to make this test not fail.
+ v8::V8::Initialize();
+ const char *script = "function foo(a) { return a+1; }";
+ v8::ScriptData *sd = v8::ScriptData::PreCompile(script, strlen(script));
+ CHECK_NE(sd->Length(), 0);
+ CHECK_NE(sd->Data(), NULL);
+ delete sd;
+}
+
+
+// This tests that we do not allow dictionary load/call inline caches
+// to use functions that have not yet been compiled. The potential
+// problem of loading a function that has not yet been compiled can
+// arise because we share code between contexts via the compilation
+// cache.
+THREADED_TEST(DictionaryICLoadedFunction) {
+ v8::HandleScope scope;
+ // Test LoadIC.
+ for (int i = 0; i < 2; i++) {
+ LocalContext context;
+ context->Global()->Set(v8_str("tmp"), v8::True());
+ context->Global()->Delete(v8_str("tmp"));
+ CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
+ }
+ // Test CallIC.
+ for (int i = 0; i < 2; i++) {
+ LocalContext context;
+ context->Global()->Set(v8_str("tmp"), v8::True());
+ context->Global()->Delete(v8_str("tmp"));
+ CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
+ }
+}
+
+
+// Test that cross-context new calls use the context of the callee to
+// create the new JavaScript object.
+THREADED_TEST(CrossContextNew) {
+ v8::HandleScope scope;
+ v8::Persistent<Context> context0 = Context::New();
+ v8::Persistent<Context> context1 = Context::New();
+
+ // Allow cross-domain access.
+ Local<String> token = v8_str("<security token>");
+ context0->SetSecurityToken(token);
+ context1->SetSecurityToken(token);
+
+ // Set an 'x' property on the Object prototype and define a
+ // constructor function in context0.
+ context0->Enter();
+ CompileRun("Object.prototype.x = 42; function C() {};");
+ context0->Exit();
+
+ // Call the constructor function from context0 and check that the
+ // result has the 'x' property.
+ context1->Enter();
+ context1->Global()->Set(v8_str("other"), context0->Global());
+ Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
+ CHECK(value->IsInt32());
+ CHECK_EQ(42, value->Int32Value());
+ context1->Exit();
+
+ // Dispose the contexts to allow them to be garbage collected.
+ context0.Dispose();
+ context1.Dispose();
+}
+
+
+class RegExpInterruptTest {
+ public:
+ RegExpInterruptTest() : block_(NULL) {}
+ ~RegExpInterruptTest() { delete block_; }
+ void RunTest() {
+ block_ = i::OS::CreateSemaphore(0);
+ gc_count_ = 0;
+ gc_during_regexp_ = 0;
+ regexp_success_ = false;
+ gc_success_ = false;
+ GCThread gc_thread(this);
+ gc_thread.Start();
+ v8::Locker::StartPreemption(1);
+
+ LongRunningRegExp();
+ {
+ v8::Unlocker unlock;
+ gc_thread.Join();
+ }
+ v8::Locker::StopPreemption();
+ CHECK(regexp_success_);
+ CHECK(gc_success_);
+ }
+ private:
+ // Number of garbage collections required.
+ static const int kRequiredGCs = 5;
+
+ class GCThread : public i::Thread {
+ public:
+ explicit GCThread(RegExpInterruptTest* test)
+ : test_(test) {}
+ virtual void Run() {
+ test_->CollectGarbage();
+ }
+ private:
+ RegExpInterruptTest* test_;
+ };
+
+ void CollectGarbage() {
+ block_->Wait();
+ while (gc_during_regexp_ < kRequiredGCs) {
+ {
+ v8::Locker lock;
+ // TODO(lrn): Perhaps create some garbage before collecting.
+ i::Heap::CollectAllGarbage();
+ gc_count_++;
+ }
+ i::OS::Sleep(1);
+ }
+ gc_success_ = true;
+ }
+
+ void LongRunningRegExp() {
+ block_->Signal(); // Enable garbage collection thread on next preemption.
+ int rounds = 0;
+ while (gc_during_regexp_ < kRequiredGCs) {
+ int gc_before = gc_count_;
+ {
+ // Match 15-30 "a"'s against 14 and a "b".
+ const char* c_source =
+ "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
+ ".exec('aaaaaaaaaaaaaaab') === null";
+ Local<String> source = String::New(c_source);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> result = script->Run();
+ if (!result->BooleanValue()) {
+ gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
+ return;
+ }
+ }
+ {
+ // Match 15-30 "a"'s against 15 and a "b".
+ const char* c_source =
+ "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
+ ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
+ Local<String> source = String::New(c_source);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> result = script->Run();
+ if (!result->BooleanValue()) {
+ gc_during_regexp_ = kRequiredGCs;
+ return;
+ }
+ }
+ int gc_after = gc_count_;
+ gc_during_regexp_ += gc_after - gc_before;
+ rounds++;
+ i::OS::Sleep(1);
+ }
+ regexp_success_ = true;
+ }
+
+ i::Semaphore* block_;
+ int gc_count_;
+ int gc_during_regexp_;
+ bool regexp_success_;
+ bool gc_success_;
+};
+
+
+// Test that a regular expression execution can be interrupted and
+// survive a garbage collection.
+TEST(RegExpInterruption) {
+ v8::Locker lock;
+ v8::V8::Initialize();
+ 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();
+
+ // Should complete without problems.
+ RegExpInterruptTest().RunTest();
+
+ local_env->Exit();
+}
+
+
+// Verify that we can clone an object
+TEST(ObjectClone) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ const char* sample =
+ "var rv = {};" \
+ "rv.alpha = 'hello';" \
+ "rv.beta = 123;" \
+ "rv;";
+
+ // Create an object, verify basics.
+ Local<Value> val = CompileRun(sample);
+ CHECK(val->IsObject());
+ Local<v8::Object> obj = Local<v8::Object>::Cast(val);
+ obj->Set(v8_str("gamma"), v8_str("cloneme"));
+
+ CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
+ CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
+ CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
+
+ // Clone it.
+ Local<v8::Object> clone = obj->Clone();
+ CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
+ CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
+ CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
+
+ // Set a property on the clone, verify each object.
+ clone->Set(v8_str("beta"), v8::Integer::New(456));
+ CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
+ CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
+}
+
+
+class RegExpStringModificationTest {
+ public:
+ RegExpStringModificationTest()
+ : block_(i::OS::CreateSemaphore(0)),
+ morphs_(0),
+ morphs_during_regexp_(0),
+ ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
+ uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
+ ~RegExpStringModificationTest() { delete block_; }
+ void RunTest() {
+ regexp_success_ = false;
+ morph_success_ = false;
+
+ // Initialize the contents of two_byte_content_ to be a uc16 representation
+ // of "aaaaaaaaaaaaaab".
+ for (int i = 0; i < 14; i++) {
+ two_byte_content_[i] = 'a';
+ }
+ two_byte_content_[14] = 'b';
+
+ // Create the input string for the regexp - the one we are going to change
+ // properties of.
+ input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
+
+ // Inject the input as a global variable.
+ i::Handle<i::String> input_name =
+ i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
+ i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
+
+
+ MorphThread morph_thread(this);
+ morph_thread.Start();
+ v8::Locker::StartPreemption(1);
+ LongRunningRegExp();
+ {
+ v8::Unlocker unlock;
+ morph_thread.Join();
+ }
+ v8::Locker::StopPreemption();
+ CHECK(regexp_success_);
+ CHECK(morph_success_);
+ }
+ private:
+
+ class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
+ public:
+ explicit AsciiVectorResource(i::Vector<const char> vector)
+ : data_(vector) {}
+ virtual ~AsciiVectorResource() {}
+ virtual size_t length() const { return data_.length(); }
+ virtual const char* data() const { return data_.start(); }
+ private:
+ i::Vector<const char> data_;
+ };
+ class UC16VectorResource : public v8::String::ExternalStringResource {
+ public:
+ explicit UC16VectorResource(i::Vector<const i::uc16> vector)
+ : data_(vector) {}
+ virtual ~UC16VectorResource() {}
+ virtual size_t length() const { return data_.length(); }
+ virtual const i::uc16* data() const { return data_.start(); }
+ private:
+ i::Vector<const i::uc16> data_;
+ };
+ // Number of string modifications required.
+ static const int kRequiredModifications = 5;
+ static const int kMaxModifications = 100;
+
+ class MorphThread : public i::Thread {
+ public:
+ explicit MorphThread(RegExpStringModificationTest* test)
+ : test_(test) {}
+ virtual void Run() {
+ test_->MorphString();
+ }
+ private:
+ RegExpStringModificationTest* test_;
+ };
+
+ void MorphString() {
+ block_->Wait();
+ while (morphs_during_regexp_ < kRequiredModifications &&
+ morphs_ < kMaxModifications) {
+ {
+ v8::Locker lock;
+ // Swap string between ascii and two-byte representation.
+ i::String* string = *input_;
+ CHECK(i::StringShape(string).IsExternal());
+ if (i::StringShape(string).IsAsciiRepresentation()) {
+ // Morph external string to be TwoByte string.
+ i::ExternalAsciiString* ext_string =
+ i::ExternalAsciiString::cast(string);
+ i::ExternalTwoByteString* morphed =
+ reinterpret_cast<i::ExternalTwoByteString*>(ext_string);
+ morphed->map()->set_instance_type(i::SHORT_EXTERNAL_STRING_TYPE);
+ morphed->set_resource(&uc16_resource_);
+ } else {
+ // Morph external string to be ASCII string.
+ i::ExternalTwoByteString* ext_string =
+ i::ExternalTwoByteString::cast(string);
+ i::ExternalAsciiString* morphed =
+ reinterpret_cast<i::ExternalAsciiString*>(ext_string);
+ morphed->map()->set_instance_type(
+ i::SHORT_EXTERNAL_ASCII_STRING_TYPE);
+ morphed->set_resource(&ascii_resource_);
+ }
+ morphs_++;
+ }
+ i::OS::Sleep(1);
+ }
+ morph_success_ = true;
+ }
+
+ void LongRunningRegExp() {
+ block_->Signal(); // Enable morphing thread on next preemption.
+ while (morphs_during_regexp_ < kRequiredModifications &&
+ morphs_ < kMaxModifications) {
+ int morphs_before = morphs_;
+ {
+ // Match 15-30 "a"'s against 14 and a "b".
+ const char* c_source =
+ "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
+ ".exec(input) === null";
+ Local<String> source = String::New(c_source);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> result = script->Run();
+ CHECK(result->IsTrue());
+ }
+ int morphs_after = morphs_;
+ morphs_during_regexp_ += morphs_after - morphs_before;
+ }
+ regexp_success_ = true;
+ }
+
+ i::uc16 two_byte_content_[15];
+ i::Semaphore* block_;
+ int morphs_;
+ int morphs_during_regexp_;
+ bool regexp_success_;
+ bool morph_success_;
+ i::Handle<i::String> input_;
+ AsciiVectorResource ascii_resource_;
+ UC16VectorResource uc16_resource_;
+};
+
+
+// Test that a regular expression execution can be interrupted and
+// the string changed without failing.
+TEST(RegExpStringModification) {
+ v8::Locker lock;
+ v8::V8::Initialize();
+ 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();
+
+ // Should complete without problems.
+ RegExpStringModificationTest().RunTest();
+
+ local_env->Exit();
+}
+
+
+// Test that we can set a property on the global object even if there
+// is a read-only property in the prototype chain.
+TEST(ReadOnlyPropertyInGlobalProto) {
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+ LocalContext context(0, templ);
+ v8::Handle<v8::Object> global = context->Global();
+ v8::Handle<v8::Object> global_proto =
+ v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
+ global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
+ global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
+ // Check without 'eval' or 'with'.
+ v8::Handle<v8::Value> res =
+ CompileRun("function f() { x = 42; return x; }; f()");
+ // Check with 'eval'.
+ res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
+ CHECK_EQ(v8::Integer::New(42), res);
+ // Check with 'with'.
+ res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
+ CHECK_EQ(v8::Integer::New(42), res);
+}
diff --git a/v8/test/cctest/test-assembler-arm.cc b/v8/test/cctest/test-assembler-arm.cc
new file mode 100644
index 0000000..bd75a04
--- /dev/null
+++ b/v8/test/cctest/test-assembler-arm.cc
@@ -0,0 +1,227 @@
+// Copyright 2006-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 "v8.h"
+
+#include "disassembler.h"
+#include "factory.h"
+#include "simulator-arm.h"
+#include "assembler-arm-inl.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+// Define these function prototypes to match JSEntryFunction in execution.cc.
+typedef int (*F1)(int x, int p1, int p2, int p3, int p4);
+typedef int (*F2)(int x, int y, int p2, int p3, int p4);
+typedef int (*F3)(void* p, int p1, int p2, int p3, int p4);
+
+
+static v8::Persistent<v8::Context> env;
+
+
+// The test framework does not accept flags on the command line, so we set them
+static void InitializeVM() {
+ // disable compilation of natives by specifying an empty natives file
+ FLAG_natives_file = "";
+
+ // enable generation of comments
+ FLAG_debug_code = true;
+
+ if (env.IsEmpty()) {
+ env = v8::Context::New();
+ }
+}
+
+
+#define __ assm.
+
+TEST(0) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ Assembler assm(NULL, 0);
+
+ __ add(r0, r0, Operand(r1));
+ __ mov(pc, Operand(lr));
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry());
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 3, 4, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(7, res);
+}
+
+
+TEST(1) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ Assembler assm(NULL, 0);
+ Label L, C;
+
+ __ mov(r1, Operand(r0));
+ __ mov(r0, Operand(0));
+ __ b(&C);
+
+ __ bind(&L);
+ __ add(r0, r0, Operand(r1));
+ __ sub(r1, r1, Operand(1));
+
+ __ bind(&C);
+ __ teq(r1, Operand(0));
+ __ b(ne, &L);
+ __ mov(pc, Operand(lr));
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 100, 0, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(5050, res);
+}
+
+
+TEST(2) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ Assembler assm(NULL, 0);
+ Label L, C;
+
+ __ mov(r1, Operand(r0));
+ __ mov(r0, Operand(1));
+ __ b(&C);
+
+ __ bind(&L);
+ __ mul(r0, r1, r0);
+ __ sub(r1, r1, Operand(1));
+
+ __ bind(&C);
+ __ teq(r1, Operand(0));
+ __ b(ne, &L);
+ __ mov(pc, Operand(lr));
+
+ // some relocated stuff here, not executed
+ __ RecordComment("dead code, just testing relocations");
+ __ mov(r0, Operand(Factory::true_value()));
+ __ RecordComment("dead code, just testing immediate operands");
+ __ mov(r0, Operand(-1));
+ __ mov(r0, Operand(0xFF000000));
+ __ mov(r0, Operand(0xF0F0F0F0));
+ __ mov(r0, Operand(0xFFF0FFFF));
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 10, 0, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(3628800, res);
+}
+
+
+TEST(3) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ typedef struct {
+ int i;
+ char c;
+ int16_t s;
+ } T;
+ T t;
+
+ Assembler assm(NULL, 0);
+ Label L, C;
+
+ __ mov(ip, Operand(sp));
+ __ stm(db_w, sp, r4.bit() | fp.bit() | sp.bit() | lr.bit());
+ __ sub(fp, ip, Operand(4));
+ __ mov(r4, Operand(r0));
+ __ ldr(r0, MemOperand(r4, OFFSET_OF(T, i)));
+ __ mov(r2, Operand(r0, ASR, 1));
+ __ str(r2, MemOperand(r4, OFFSET_OF(T, i)));
+ __ ldrsb(r2, MemOperand(r4, OFFSET_OF(T, c)));
+ __ add(r0, r2, Operand(r0));
+ __ mov(r2, Operand(r2, LSL, 2));
+ __ strb(r2, MemOperand(r4, OFFSET_OF(T, c)));
+ __ ldrsh(r2, MemOperand(r4, OFFSET_OF(T, s)));
+ __ add(r0, r2, Operand(r0));
+ __ mov(r2, Operand(r2, ASR, 3));
+ __ strh(r2, MemOperand(r4, OFFSET_OF(T, s)));
+ __ ldm(ia, sp, r4.bit() | fp.bit() | sp.bit() | pc.bit());
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F3 f = FUNCTION_CAST<F3>(Code::cast(code)->entry());
+ t.i = 100000;
+ t.c = 10;
+ t.s = 1000;
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(101010, res);
+ CHECK_EQ(100000/2, t.i);
+ CHECK_EQ(10*4, t.c);
+ CHECK_EQ(1000/8, t.s);
+}
+
+
+#undef __
diff --git a/v8/test/cctest/test-assembler-ia32.cc b/v8/test/cctest/test-assembler-ia32.cc
new file mode 100644
index 0000000..9ad7c76
--- /dev/null
+++ b/v8/test/cctest/test-assembler-ia32.cc
@@ -0,0 +1,395 @@
+// Copyright 2006-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 "v8.h"
+
+#include "disassembler.h"
+#include "factory.h"
+#include "macro-assembler.h"
+#include "platform.h"
+#include "serialize.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+typedef int (*F0)();
+typedef int (*F1)(int x);
+typedef int (*F2)(int x, int y);
+
+
+static v8::Persistent<v8::Context> env;
+
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ env = v8::Context::New();
+ }
+}
+
+
+#define __ assm.
+
+TEST(AssemblerIa320) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+
+ __ mov(eax, Operand(esp, 4));
+ __ add(eax, Operand(esp, 8));
+ __ ret(0);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry());
+ int res = f(3, 4);
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(7, res);
+}
+
+
+TEST(AssemblerIa321) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+ Label L, C;
+
+ __ mov(edx, Operand(esp, 4));
+ __ xor_(eax, Operand(eax)); // clear eax
+ __ jmp(&C);
+
+ __ bind(&L);
+ __ add(eax, Operand(edx));
+ __ sub(Operand(edx), Immediate(1));
+
+ __ bind(&C);
+ __ test(edx, Operand(edx));
+ __ j(not_zero, &L, taken);
+ __ ret(0);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
+ int res = f(100);
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(5050, res);
+}
+
+
+TEST(AssemblerIa322) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+ Label L, C;
+
+ __ mov(edx, Operand(esp, 4));
+ __ mov(eax, 1);
+ __ jmp(&C);
+
+ __ bind(&L);
+ __ imul(eax, Operand(edx));
+ __ sub(Operand(edx), Immediate(1));
+
+ __ bind(&C);
+ __ test(edx, Operand(edx));
+ __ j(not_zero, &L, taken);
+ __ ret(0);
+
+ // some relocated stuff here, not executed
+ __ mov(eax, Factory::true_value());
+ __ jmp(NULL, RelocInfo::RUNTIME_ENTRY);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
+ int res = f(10);
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(3628800, res);
+}
+
+
+typedef int (*F3)(float x);
+
+TEST(AssemblerIa323) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
+ { CpuFeatures::Scope fscope(CpuFeatures::SSE2);
+ __ cvttss2si(eax, Operand(esp, 4));
+ __ ret(0);
+ }
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Code* code =
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
+ // don't print the code - our disassembler can't handle cvttss2si
+ // instead print bytes
+ Disassembler::Dump(stdout,
+ code->instruction_start(),
+ code->instruction_start() + code->instruction_size());
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ int res = f(static_cast<float>(-3.1415));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(-3, res);
+}
+
+
+typedef int (*F4)(double x);
+
+TEST(AssemblerIa324) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
+ CpuFeatures::Scope fscope(CpuFeatures::SSE2);
+ __ cvttsd2si(eax, Operand(esp, 4));
+ __ ret(0);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Code* code =
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
+ // don't print the code - our disassembler can't handle cvttsd2si
+ // instead print bytes
+ Disassembler::Dump(stdout,
+ code->instruction_start(),
+ code->instruction_start() + code->instruction_size());
+ F4 f = FUNCTION_CAST<F4>(code->entry());
+ int res = f(2.718281828);
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(2, res);
+}
+
+
+static int baz = 42;
+TEST(AssemblerIa325) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+
+ __ mov(eax, Operand(reinterpret_cast<intptr_t>(&baz), RelocInfo::NONE));
+ __ ret(0);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Code* code =
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
+ F0 f = FUNCTION_CAST<F0>(code->entry());
+ int res = f();
+ CHECK_EQ(42, res);
+}
+
+
+typedef double (*F5)(double x, double y);
+
+TEST(AssemblerIa326) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
+ CpuFeatures::Scope fscope(CpuFeatures::SSE2);
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+
+ __ movdbl(xmm0, Operand(esp, 1 * kPointerSize));
+ __ movdbl(xmm1, Operand(esp, 3 * kPointerSize));
+ __ addsd(xmm0, xmm1);
+ __ mulsd(xmm0, xmm1);
+ __ subsd(xmm0, xmm1);
+ __ divsd(xmm0, xmm1);
+ // Copy xmm0 to st(0) using eight bytes of stack.
+ __ sub(Operand(esp), Immediate(8));
+ __ movdbl(Operand(esp, 0), xmm0);
+ __ fld_d(Operand(esp, 0));
+ __ add(Operand(esp), Immediate(8));
+ __ ret(0);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Code* code =
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
+#ifdef DEBUG
+ ::printf("\n---\n");
+ // don't print the code - our disassembler can't handle SSE instructions
+ // instead print bytes
+ Disassembler::Dump(stdout,
+ code->instruction_start(),
+ code->instruction_start() + code->instruction_size());
+#endif
+ F5 f = FUNCTION_CAST<F5>(code->entry());
+ double res = f(2.2, 1.1);
+ ::printf("f() = %f\n", res);
+ CHECK(2.29 < res && res < 2.31);
+}
+
+
+typedef double (*F6)(int x);
+
+TEST(AssemblerIa328) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
+ CpuFeatures::Scope fscope(CpuFeatures::SSE2);
+ v8::internal::byte buffer[256];
+ Assembler assm(buffer, sizeof buffer);
+ __ mov(eax, Operand(esp, 4));
+ __ cvtsi2sd(xmm0, Operand(eax));
+ // Copy xmm0 to st(0) using eight bytes of stack.
+ __ sub(Operand(esp), Immediate(8));
+ __ movdbl(Operand(esp, 0), xmm0);
+ __ fld_d(Operand(esp, 0));
+ __ add(Operand(esp), Immediate(8));
+ __ ret(0);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Code* code =
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F6 f = FUNCTION_CAST<F6>(Code::cast(code)->entry());
+ double res = f(12);
+
+ ::printf("f() = %f\n", res);
+ CHECK(11.99 < res && res < 12.001);
+}
+
+
+typedef int (*F7)(double x, double y);
+
+TEST(AssemblerIa329) {
+ InitializeVM();
+ v8::HandleScope scope;
+ v8::internal::byte buffer[256];
+ MacroAssembler assm(buffer, sizeof buffer);
+ enum { kEqual = 0, kGreater = 1, kLess = 2, kNaN = 3, kUndefined = 4 };
+ Label equal_l, less_l, greater_l, nan_l;
+ __ fld_d(Operand(esp, 3 * kPointerSize));
+ __ fld_d(Operand(esp, 1 * kPointerSize));
+ __ FCmp();
+ __ j(parity_even, &nan_l, taken);
+ __ j(equal, &equal_l, taken);
+ __ j(below, &less_l, taken);
+ __ j(above, &greater_l, taken);
+
+ __ mov(eax, kUndefined);
+ __ ret(0);
+
+ __ bind(&equal_l);
+ __ mov(eax, kEqual);
+ __ ret(0);
+
+ __ bind(&greater_l);
+ __ mov(eax, kGreater);
+ __ ret(0);
+
+ __ bind(&less_l);
+ __ mov(eax, kLess);
+ __ ret(0);
+
+ __ bind(&nan_l);
+ __ mov(eax, kNaN);
+ __ ret(0);
+
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Code* code =
+ Code::cast(Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value())));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+
+ F7 f = FUNCTION_CAST<F7>(Code::cast(code)->entry());
+ CHECK_EQ(kLess, f(1.1, 2.2));
+ CHECK_EQ(kEqual, f(2.2, 2.2));
+ CHECK_EQ(kGreater, f(3.3, 2.2));
+ CHECK_EQ(kNaN, f(OS::nan_value(), 1.1));
+}
+
+#undef __
diff --git a/v8/test/cctest/test-ast.cc b/v8/test/cctest/test-ast.cc
new file mode 100644
index 0000000..2054348
--- /dev/null
+++ b/v8/test/cctest/test-ast.cc
@@ -0,0 +1,97 @@
+// Copyright 2006-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 "v8.h"
+
+#include "ast.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+TEST(List) {
+ List<Node*>* list = new List<Node*>(0);
+ CHECK_EQ(0, list->length());
+
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ Node* node = new EmptyStatement();
+ list->Add(node);
+ CHECK_EQ(1, list->length());
+ CHECK_EQ(node, list->at(0));
+ CHECK_EQ(node, list->last());
+
+ const int kElements = 100;
+ for (int i = 0; i < kElements; i++) {
+ list->Add(node);
+ }
+ CHECK_EQ(1 + kElements, list->length());
+
+ list->Clear();
+ CHECK_EQ(0, list->length());
+ delete list;
+}
+
+
+TEST(RemoveLast) {
+ List<int> list(4);
+ CHECK_EQ(0, list.length());
+ list.Add(1);
+ CHECK_EQ(1, list.length());
+ CHECK_EQ(1, list.last());
+ list.RemoveLast();
+ CHECK_EQ(0, list.length());
+ list.Add(2);
+ list.Add(3);
+ CHECK_EQ(2, list.length());
+ CHECK_EQ(3, list.last());
+ list.RemoveLast();
+ CHECK_EQ(1, list.length());
+ CHECK_EQ(2, list.last());
+ list.RemoveLast();
+ CHECK_EQ(0, list.length());
+
+ const int kElements = 100;
+ for (int i = 0; i < kElements; i++) list.Add(i);
+ for (int j = kElements - 1; j >= 0; j--) {
+ CHECK_EQ(j + 1, list.length());
+ CHECK_EQ(j, list.last());
+ list.RemoveLast();
+ CHECK_EQ(j, list.length());
+ }
+}
+
+
+TEST(DeleteEmpty) {
+ {
+ List<int>* list = new List<int>(0);
+ delete list;
+ }
+ {
+ List<int> list(0);
+ }
+}
diff --git a/v8/test/cctest/test-compiler.cc b/v8/test/cctest/test-compiler.cc
new file mode 100644
index 0000000..08037b3
--- /dev/null
+++ b/v8/test/cctest/test-compiler.cc
@@ -0,0 +1,318 @@
+// Copyright 2006-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 "v8.h"
+
+#include "compiler.h"
+#include "execution.h"
+#include "factory.h"
+#include "platform.h"
+#include "top.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static v8::Persistent<v8::Context> env;
+
+// --- P r i n t E x t e n s i o n ---
+
+class PrintExtension : public v8::Extension {
+ public:
+ PrintExtension() : v8::Extension("v8/print", kSource) { }
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+ static v8::Handle<v8::Value> Print(const v8::Arguments& args);
+ private:
+ static const char* kSource;
+};
+
+
+const char* PrintExtension::kSource = "native function print();";
+
+
+v8::Handle<v8::FunctionTemplate> PrintExtension::GetNativeFunction(
+ v8::Handle<v8::String> str) {
+ return v8::FunctionTemplate::New(PrintExtension::Print);
+}
+
+
+v8::Handle<v8::Value> PrintExtension::Print(const v8::Arguments& args) {
+ for (int i = 0; i < args.Length(); i++) {
+ if (i != 0) printf(" ");
+ v8::HandleScope scope;
+ v8::Handle<v8::Value> arg = args[i];
+ v8::Handle<v8::String> string_obj = arg->ToString();
+ if (string_obj.IsEmpty()) return string_obj;
+ int length = string_obj->Length();
+ uint16_t* string = NewArray<uint16_t>(length + 1);
+ string_obj->Write(string);
+ for (int j = 0; j < length; j++)
+ printf("%lc", string[j]);
+ DeleteArray(string);
+ }
+ printf("\n");
+ return v8::Undefined();
+}
+
+
+static PrintExtension kPrintExtension;
+v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension);
+
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ v8::HandleScope scope;
+ const char* extensions[] = { "v8/print", "v8/gc" };
+ v8::ExtensionConfiguration config(2, extensions);
+ env = v8::Context::New(&config);
+ }
+ v8::HandleScope scope;
+ env->Enter();
+}
+
+
+static Object* GetGlobalProperty(const char* name) {
+ Handle<String> symbol = Factory::LookupAsciiSymbol(name);
+ return Top::context()->global()->GetProperty(*symbol);
+}
+
+
+static void SetGlobalProperty(const char* name, Object* value) {
+ Handle<Object> object(value);
+ Handle<String> symbol = Factory::LookupAsciiSymbol(name);
+ Handle<JSObject> global(Top::context()->global());
+ SetProperty(global, symbol, object, NONE);
+}
+
+
+static Handle<JSFunction> Compile(const char* source) {
+ Handle<String> source_code(Factory::NewStringFromUtf8(CStrVector(source)));
+ Handle<JSFunction> boilerplate =
+ Compiler::Compile(source_code, Handle<String>(), 0, 0, NULL, NULL);
+ return Factory::NewFunctionFromBoilerplate(boilerplate,
+ Top::global_context());
+}
+
+
+static double Inc(int x) {
+ const char* source = "result = %d + 1;";
+ EmbeddedVector<char, 512> buffer;
+ OS::SNPrintF(buffer, source, x);
+
+ Handle<JSFunction> fun = Compile(buffer.start());
+ if (fun.is_null()) return -1;
+
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+ return GetGlobalProperty("result")->Number();
+}
+
+
+TEST(Inc) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK_EQ(4.0, Inc(3));
+}
+
+
+static double Add(int x, int y) {
+ Handle<JSFunction> fun = Compile("result = x + y;");
+ if (fun.is_null()) return -1;
+
+ SetGlobalProperty("x", Smi::FromInt(x));
+ SetGlobalProperty("y", Smi::FromInt(y));
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+ return GetGlobalProperty("result")->Number();
+}
+
+
+TEST(Add) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK_EQ(5.0, Add(2, 3));
+}
+
+
+static double Abs(int x) {
+ Handle<JSFunction> fun = Compile("if (x < 0) result = -x; else result = x;");
+ if (fun.is_null()) return -1;
+
+ SetGlobalProperty("x", Smi::FromInt(x));
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+ return GetGlobalProperty("result")->Number();
+}
+
+
+TEST(Abs) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK_EQ(3.0, Abs(-3));
+}
+
+
+static double Sum(int n) {
+ Handle<JSFunction> fun =
+ Compile("s = 0; while (n > 0) { s += n; n -= 1; }; result = s;");
+ if (fun.is_null()) return -1;
+
+ SetGlobalProperty("n", Smi::FromInt(n));
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+ return GetGlobalProperty("result")->Number();
+}
+
+
+TEST(Sum) {
+ InitializeVM();
+ v8::HandleScope scope;
+ CHECK_EQ(5050.0, Sum(100));
+}
+
+
+TEST(Print) {
+ InitializeVM();
+ v8::HandleScope scope;
+ const char* source = "for (n = 0; n < 100; ++n) print(n, 1, 2);";
+ Handle<JSFunction> fun = Compile(source);
+ if (fun.is_null()) return;
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+}
+
+
+// The following test method stems from my coding efforts today. It
+// tests all the functionality I have added to the compiler today
+TEST(Stuff) {
+ InitializeVM();
+ v8::HandleScope scope;
+ const char* source =
+ "r = 0;\n"
+ "a = new Object;\n"
+ "if (a == a) r+=1;\n" // 1
+ "if (a != new Object()) r+=2;\n" // 2
+ "a.x = 42;\n"
+ "if (a.x == 42) r+=4;\n" // 4
+ "function foo() { var x = 87; return x; }\n"
+ "if (foo() == 87) r+=8;\n" // 8
+ "function bar() { var x; x = 99; return x; }\n"
+ "if (bar() == 99) r+=16;\n" // 16
+ "function baz() { var x = 1, y, z = 2; y = 3; return x + y + z; }\n"
+ "if (baz() == 6) r+=32;\n" // 32
+ "function Cons0() { this.x = 42; this.y = 87; }\n"
+ "if (new Cons0().x == 42) r+=64;\n" // 64
+ "if (new Cons0().y == 87) r+=128;\n" // 128
+ "function Cons2(x, y) { this.sum = x + y; }\n"
+ "if (new Cons2(3,4).sum == 7) r+=256;"; // 256
+
+ Handle<JSFunction> fun = Compile(source);
+ CHECK(!fun.is_null());
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+ CHECK_EQ(511.0, GetGlobalProperty("r")->Number());
+}
+
+
+TEST(UncaughtThrow) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ const char* source = "throw 42;";
+ Handle<JSFunction> fun = Compile(source);
+ CHECK(!fun.is_null());
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Handle<Object> result =
+ Execution::Call(fun, global, 0, NULL, &has_pending_exception);
+ CHECK(has_pending_exception);
+ CHECK_EQ(42.0, Top::pending_exception()->Number());
+}
+
+
+// Tests calling a builtin function from C/C++ code, and the builtin function
+// performs GC. It creates a stack frame looks like following:
+// | C (PerformGC) |
+// | JS-to-C |
+// | JS |
+// | C-to-JS |
+TEST(C2JSFrames) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ const char* source = "function foo(a) { gc(), print(a); }";
+
+ Handle<JSFunction> fun0 = Compile(source);
+ CHECK(!fun0.is_null());
+
+ // Run the generated code to populate the global object with 'foo'.
+ bool has_pending_exception;
+ Handle<JSObject> global(Top::context()->global());
+ Execution::Call(fun0, global, 0, NULL, &has_pending_exception);
+ CHECK(!has_pending_exception);
+
+ Handle<Object> fun1 =
+ Handle<Object>(
+ Top::context()->global()->GetProperty(
+ *Factory::LookupAsciiSymbol("foo")));
+ CHECK(fun1->IsJSFunction());
+
+ Object** argv[1] = {
+ Handle<Object>::cast(Factory::LookupAsciiSymbol("hello")).location()
+ };
+ Execution::Call(Handle<JSFunction>::cast(fun1), global, 1, argv,
+ &has_pending_exception);
+ CHECK(!has_pending_exception);
+}
+
+
+// Regression 236. Calling InitLineEnds on a Script with undefined
+// source resulted in crash.
+TEST(Regression236) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ Handle<Script> script = Factory::NewScript(Factory::empty_string());
+ script->set_source(Heap::undefined_value());
+ CHECK_EQ(-1, GetScriptLineNumber(script, 0));
+ CHECK_EQ(-1, GetScriptLineNumber(script, 100));
+ CHECK_EQ(-1, GetScriptLineNumber(script, -1));
+}
diff --git a/v8/test/cctest/test-conversions.cc b/v8/test/cctest/test-conversions.cc
new file mode 100644
index 0000000..6c0b9a6
--- /dev/null
+++ b/v8/test/cctest/test-conversions.cc
@@ -0,0 +1,130 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "platform.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+TEST(Hex) {
+ CHECK_EQ(0.0, StringToDouble("0x0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble("0X0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(1.0, StringToDouble("0x1", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(16.0, StringToDouble("0x10", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(255.0, StringToDouble("0xff", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(175.0, StringToDouble("0xAF", ALLOW_HEX | ALLOW_OCTALS));
+
+ CHECK_EQ(0.0, StringToDouble("0x0", ALLOW_HEX));
+ CHECK_EQ(0.0, StringToDouble("0X0", ALLOW_HEX));
+ CHECK_EQ(1.0, StringToDouble("0x1", ALLOW_HEX));
+ CHECK_EQ(16.0, StringToDouble("0x10", ALLOW_HEX));
+ CHECK_EQ(255.0, StringToDouble("0xff", ALLOW_HEX));
+ CHECK_EQ(175.0, StringToDouble("0xAF", ALLOW_HEX));
+}
+
+
+TEST(Octal) {
+ CHECK_EQ(0.0, StringToDouble("0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble("00", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(1.0, StringToDouble("01", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(7.0, StringToDouble("07", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(8.0, StringToDouble("010", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(63.0, StringToDouble("077", ALLOW_HEX | ALLOW_OCTALS));
+
+ CHECK_EQ(0.0, StringToDouble("0", ALLOW_HEX));
+ CHECK_EQ(0.0, StringToDouble("00", ALLOW_HEX));
+ CHECK_EQ(1.0, StringToDouble("01", ALLOW_HEX));
+ CHECK_EQ(7.0, StringToDouble("07", ALLOW_HEX));
+ CHECK_EQ(10.0, StringToDouble("010", ALLOW_HEX));
+ CHECK_EQ(77.0, StringToDouble("077", ALLOW_HEX));
+}
+
+
+TEST(MalformedOctal) {
+ CHECK_EQ(8.0, StringToDouble("08", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(81.0, StringToDouble("081", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(78.0, StringToDouble("078", ALLOW_HEX | ALLOW_OCTALS));
+
+ CHECK(isnan(StringToDouble("07.7", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble("07.8", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble("07e8", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble("07e7", ALLOW_HEX | ALLOW_OCTALS)));
+
+ CHECK_EQ(8.7, StringToDouble("08.7", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(8e7, StringToDouble("08e7", ALLOW_HEX | ALLOW_OCTALS));
+
+ CHECK_EQ(0.001, StringToDouble("0.001", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.713, StringToDouble("0.713", ALLOW_HEX | ALLOW_OCTALS));
+
+ CHECK_EQ(8.0, StringToDouble("08", ALLOW_HEX));
+ CHECK_EQ(81.0, StringToDouble("081", ALLOW_HEX));
+ CHECK_EQ(78.0, StringToDouble("078", ALLOW_HEX));
+
+ CHECK_EQ(7.7, StringToDouble("07.7", ALLOW_HEX));
+ CHECK_EQ(7.8, StringToDouble("07.8", ALLOW_HEX));
+ CHECK_EQ(7e8, StringToDouble("07e8", ALLOW_HEX));
+ CHECK_EQ(7e7, StringToDouble("07e7", ALLOW_HEX));
+
+ CHECK_EQ(8.7, StringToDouble("08.7", ALLOW_HEX));
+ CHECK_EQ(8e7, StringToDouble("08e7", ALLOW_HEX));
+
+ CHECK_EQ(0.001, StringToDouble("0.001", ALLOW_HEX));
+ CHECK_EQ(0.713, StringToDouble("0.713", ALLOW_HEX));
+}
+
+
+TEST(TrailingJunk) {
+ CHECK_EQ(8.0, StringToDouble("8q", ALLOW_TRAILING_JUNK));
+ CHECK_EQ(63.0, StringToDouble("077qqq", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
+}
+
+
+TEST(NonStrDecimalLiteral) {
+ CHECK(isnan(StringToDouble(" ", NO_FLAGS, OS::nan_value())));
+ CHECK(isnan(StringToDouble("", NO_FLAGS, OS::nan_value())));
+ CHECK(isnan(StringToDouble(" ", NO_FLAGS, OS::nan_value())));
+ CHECK_EQ(0.0, StringToDouble("", NO_FLAGS));
+ CHECK_EQ(0.0, StringToDouble(" ", NO_FLAGS));
+}
+
+
+TEST(BitField) {
+ uint32_t x;
+
+ // One bit bit field can hold values 0 and 1.
+ class OneBit1: public BitField<uint32_t, 0, 1> {};
+ class OneBit2: public BitField<uint32_t, 7, 1> {};
+ CHECK(!OneBit1::is_valid(static_cast<uint32_t>(-1)));
+ CHECK(!OneBit2::is_valid(static_cast<uint32_t>(-1)));
+ for (int i = 0; i < 2; i++) {
+ CHECK(OneBit1::is_valid(i));
+ x = OneBit1::encode(i);
+ CHECK_EQ(i, OneBit1::decode(x));
+
+ CHECK(OneBit2::is_valid(i));
+ x = OneBit2::encode(i);
+ CHECK_EQ(i, OneBit2::decode(x));
+ }
+ CHECK(!OneBit1::is_valid(2));
+ CHECK(!OneBit2::is_valid(2));
+
+ // Eight bit bit field can hold values from 0 tp 255.
+ class EightBit1: public BitField<uint32_t, 0, 8> {};
+ class EightBit2: public BitField<uint32_t, 13, 8> {};
+ CHECK(!EightBit1::is_valid(static_cast<uint32_t>(-1)));
+ CHECK(!EightBit2::is_valid(static_cast<uint32_t>(-1)));
+ for (int i = 0; i < 256; i++) {
+ CHECK(EightBit1::is_valid(i));
+ x = EightBit1::encode(i);
+ CHECK_EQ(i, EightBit1::decode(x));
+ CHECK(EightBit2::is_valid(i));
+ x = EightBit2::encode(i);
+ CHECK_EQ(i, EightBit2::decode(x));
+ }
+ CHECK(!EightBit1::is_valid(256));
+ CHECK(!EightBit2::is_valid(256));
+}
diff --git a/v8/test/cctest/test-debug.cc b/v8/test/cctest/test-debug.cc
new file mode 100644
index 0000000..74d590e
--- /dev/null
+++ b/v8/test/cctest/test-debug.cc
@@ -0,0 +1,4087 @@
+// 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 "v8.h"
+
+#include "api.h"
+#include "debug.h"
+#include "platform.h"
+#include "stub-cache.h"
+#include "cctest.h"
+
+
+using ::v8::internal::EmbeddedVector;
+using ::v8::internal::Object;
+using ::v8::internal::OS;
+using ::v8::internal::Handle;
+using ::v8::internal::Heap;
+using ::v8::internal::JSGlobalProxy;
+using ::v8::internal::Code;
+using ::v8::internal::Debug;
+using ::v8::internal::Debugger;
+using ::v8::internal::StepAction;
+using ::v8::internal::StepIn; // From StepAction enum
+using ::v8::internal::StepNext; // From StepAction enum
+using ::v8::internal::StepOut; // From StepAction enum
+
+
+// Size of temp buffer for formatting small strings.
+#define SMALL_STRING_BUFFER_SIZE 80
+
+// --- A d d i t i o n a l C h e c k H e l p e r s
+
+
+// Helper function used by the CHECK_EQ function when given Address
+// arguments. Should not be called directly.
+static inline void CheckEqualsHelper(const char* file, int line,
+ const char* expected_source,
+ ::v8::internal::Address expected,
+ const char* value_source,
+ ::v8::internal::Address value) {
+ if (expected != value) {
+ V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n# "
+ "Expected: %i\n# Found: %i",
+ expected_source, value_source, expected, value);
+ }
+}
+
+
+// Helper function used by the CHECK_NE function when given Address
+// arguments. Should not be called directly.
+static inline void CheckNonEqualsHelper(const char* file, int line,
+ const char* unexpected_source,
+ ::v8::internal::Address unexpected,
+ const char* value_source,
+ ::v8::internal::Address value) {
+ if (unexpected == value) {
+ V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %i",
+ unexpected_source, value_source, value);
+ }
+}
+
+
+// Helper function used by the CHECK function when given code
+// arguments. Should not be called directly.
+static inline void CheckEqualsHelper(const char* file, int line,
+ const char* expected_source,
+ const Code* expected,
+ const char* value_source,
+ const Code* value) {
+ if (expected != value) {
+ V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n# "
+ "Expected: %p\n# Found: %p",
+ expected_source, value_source, expected, value);
+ }
+}
+
+
+static inline void CheckNonEqualsHelper(const char* file, int line,
+ const char* expected_source,
+ const Code* expected,
+ const char* value_source,
+ const Code* value) {
+ if (expected == value) {
+ V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p",
+ expected_source, value_source, value);
+ }
+}
+
+
+// --- H e l p e r C l a s s e s
+
+
+// Helper class for creating a V8 enviromnent for running tests
+class DebugLocalContext {
+ public:
+ inline DebugLocalContext(
+ v8::ExtensionConfiguration* extensions = 0,
+ v8::Handle<v8::ObjectTemplate> global_template =
+ v8::Handle<v8::ObjectTemplate>(),
+ v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>())
+ : context_(v8::Context::New(extensions, global_template, global_object)) {
+ context_->Enter();
+ }
+ inline ~DebugLocalContext() {
+ context_->Exit();
+ context_.Dispose();
+ }
+ inline v8::Context* operator->() { return *context_; }
+ inline v8::Context* operator*() { return *context_; }
+ inline bool IsReady() { return !context_.IsEmpty(); }
+ void ExposeDebug() {
+ // Expose the debug context global object in the global object for testing.
+ Debug::Load();
+ Debug::debug_context()->set_security_token(
+ v8::Utils::OpenHandle(*context_)->security_token());
+
+ Handle<JSGlobalProxy> global(Handle<JSGlobalProxy>::cast(
+ v8::Utils::OpenHandle(*context_->Global())));
+ Handle<v8::internal::String> debug_string =
+ v8::internal::Factory::LookupAsciiSymbol("debug");
+ SetProperty(global, debug_string,
+ Handle<Object>(Debug::debug_context()->global_proxy()), DONT_ENUM);
+ }
+ private:
+ v8::Persistent<v8::Context> context_;
+};
+
+
+// --- H e l p e r F u n c t i o n s
+
+
+// Compile and run the supplied source and return the fequested function.
+static v8::Local<v8::Function> CompileFunction(DebugLocalContext* env,
+ const char* source,
+ const char* function_name) {
+ v8::Script::Compile(v8::String::New(source))->Run();
+ return v8::Local<v8::Function>::Cast(
+ (*env)->Global()->Get(v8::String::New(function_name)));
+}
+
+// Helper function that compiles and runs the source.
+static v8::Local<v8::Value> CompileRun(const char* source) {
+ return v8::Script::Compile(v8::String::New(source))->Run();
+}
+
+// Is there any debug info for the function?
+static bool HasDebugInfo(v8::Handle<v8::Function> fun) {
+ Handle<v8::internal::JSFunction> f = v8::Utils::OpenHandle(*fun);
+ Handle<v8::internal::SharedFunctionInfo> shared(f->shared());
+ return Debug::HasDebugInfo(shared);
+}
+
+
+// Set a break point in a function and return the associated break point
+// number.
+static int SetBreakPoint(Handle<v8::internal::JSFunction> fun, int position) {
+ static int break_point = 0;
+ Handle<v8::internal::SharedFunctionInfo> shared(fun->shared());
+ Debug::SetBreakPoint(
+ shared, position,
+ Handle<Object>(v8::internal::Smi::FromInt(++break_point)));
+ return break_point;
+}
+
+
+// Set a break point in a function and return the associated break point
+// number.
+static int SetBreakPoint(v8::Handle<v8::Function> fun, int position) {
+ return SetBreakPoint(v8::Utils::OpenHandle(*fun), position);
+}
+
+
+// Set a break point in a function using the Debug object and return the
+// associated break point number.
+static int SetBreakPointFromJS(const char* function_name,
+ int line, int position) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "debug.Debug.setBreakPoint(%s,%d,%d)",
+ function_name, line, position);
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ v8::Handle<v8::String> str = v8::String::New(buffer.start());
+ return v8::Script::Compile(str)->Run()->Int32Value();
+}
+
+
+// Set a break point in a script identified by id using the global Debug object.
+static int SetScriptBreakPointByIdFromJS(int script_id, int line, int column) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ if (column >= 0) {
+ // Column specified set script break point on precise location.
+ OS::SNPrintF(buffer,
+ "debug.Debug.setScriptBreakPointById(%d,%d,%d)",
+ script_id, line, column);
+ } else {
+ // Column not specified set script break point on line.
+ OS::SNPrintF(buffer,
+ "debug.Debug.setScriptBreakPointById(%d,%d)",
+ script_id, line);
+ }
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ {
+ v8::TryCatch try_catch;
+ v8::Handle<v8::String> str = v8::String::New(buffer.start());
+ v8::Handle<v8::Value> value = v8::Script::Compile(str)->Run();
+ ASSERT(!try_catch.HasCaught());
+ return value->Int32Value();
+ }
+}
+
+
+// Set a break point in a script identified by name using the global Debug
+// object.
+static int SetScriptBreakPointByNameFromJS(const char* script_name,
+ int line, int column) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ if (column >= 0) {
+ // Column specified set script break point on precise location.
+ OS::SNPrintF(buffer,
+ "debug.Debug.setScriptBreakPointByName(\"%s\",%d,%d)",
+ script_name, line, column);
+ } else {
+ // Column not specified set script break point on line.
+ OS::SNPrintF(buffer,
+ "debug.Debug.setScriptBreakPointByName(\"%s\",%d)",
+ script_name, line);
+ }
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ {
+ v8::TryCatch try_catch;
+ v8::Handle<v8::String> str = v8::String::New(buffer.start());
+ v8::Handle<v8::Value> value = v8::Script::Compile(str)->Run();
+ ASSERT(!try_catch.HasCaught());
+ return value->Int32Value();
+ }
+}
+
+
+// Clear a break point.
+static void ClearBreakPoint(int break_point) {
+ Debug::ClearBreakPoint(
+ Handle<Object>(v8::internal::Smi::FromInt(break_point)));
+}
+
+
+// Clear a break point using the global Debug object.
+static void ClearBreakPointFromJS(int break_point_number) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "debug.Debug.clearBreakPoint(%d)",
+ break_point_number);
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ v8::Script::Compile(v8::String::New(buffer.start()))->Run();
+}
+
+
+static void EnableScriptBreakPointFromJS(int break_point_number) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "debug.Debug.enableScriptBreakPoint(%d)",
+ break_point_number);
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ v8::Script::Compile(v8::String::New(buffer.start()))->Run();
+}
+
+
+static void DisableScriptBreakPointFromJS(int break_point_number) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "debug.Debug.disableScriptBreakPoint(%d)",
+ break_point_number);
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ v8::Script::Compile(v8::String::New(buffer.start()))->Run();
+}
+
+
+static void ChangeScriptBreakPointConditionFromJS(int break_point_number,
+ const char* condition) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "debug.Debug.changeScriptBreakPointCondition(%d, \"%s\")",
+ break_point_number, condition);
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ v8::Script::Compile(v8::String::New(buffer.start()))->Run();
+}
+
+
+static void ChangeScriptBreakPointIgnoreCountFromJS(int break_point_number,
+ int ignoreCount) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "debug.Debug.changeScriptBreakPointIgnoreCount(%d, %d)",
+ break_point_number, ignoreCount);
+ buffer[SMALL_STRING_BUFFER_SIZE - 1] = '\0';
+ v8::Script::Compile(v8::String::New(buffer.start()))->Run();
+}
+
+
+// Change break on exception.
+static void ChangeBreakOnException(bool caught, bool uncaught) {
+ Debug::ChangeBreakOnException(v8::internal::BreakException, caught);
+ Debug::ChangeBreakOnException(v8::internal::BreakUncaughtException, uncaught);
+}
+
+
+// Change break on exception using the global Debug object.
+static void ChangeBreakOnExceptionFromJS(bool caught, bool uncaught) {
+ if (caught) {
+ v8::Script::Compile(
+ v8::String::New("debug.Debug.setBreakOnException()"))->Run();
+ } else {
+ v8::Script::Compile(
+ v8::String::New("debug.Debug.clearBreakOnException()"))->Run();
+ }
+ if (uncaught) {
+ v8::Script::Compile(
+ v8::String::New("debug.Debug.setBreakOnUncaughtException()"))->Run();
+ } else {
+ v8::Script::Compile(
+ v8::String::New("debug.Debug.clearBreakOnUncaughtException()"))->Run();
+ }
+}
+
+
+// Prepare to step to next break location.
+static void PrepareStep(StepAction step_action) {
+ Debug::PrepareStep(step_action, 1);
+}
+
+
+// This function is in namespace v8::internal to be friend with class
+// v8::internal::Debug.
+namespace v8 { namespace internal { // NOLINT
+
+// Collect the currently debugged functions.
+Handle<FixedArray> GetDebuggedFunctions() {
+ v8::internal::DebugInfoListNode* node = Debug::debug_info_list_;
+
+ // Find the number of debugged functions.
+ int count = 0;
+ while (node) {
+ count++;
+ node = node->next();
+ }
+
+ // Allocate array for the debugged functions
+ Handle<FixedArray> debugged_functions =
+ v8::internal::Factory::NewFixedArray(count);
+
+ // Run through the debug info objects and collect all functions.
+ count = 0;
+ while (node) {
+ debugged_functions->set(count++, *node->debug_info());
+ node = node->next();
+ }
+
+ return debugged_functions;
+}
+
+
+static Handle<Code> ComputeCallDebugBreak(int argc) {
+ CALL_HEAP_FUNCTION(v8::internal::StubCache::ComputeCallDebugBreak(argc),
+ Code);
+}
+
+
+// Check that the debugger has been fully unloaded.
+void CheckDebuggerUnloaded(bool check_functions) {
+ // Check that the debugger context is cleared and that there is no debug
+ // information stored for the debugger.
+ CHECK(Debug::debug_context().is_null());
+ CHECK_EQ(NULL, Debug::debug_info_list_);
+
+ // Collect garbage to ensure weak handles are cleared.
+ Heap::CollectAllGarbage();
+ Heap::CollectAllGarbage();
+
+ // Iterate the head and check that there are no debugger related objects left.
+ HeapIterator iterator;
+ while (iterator.has_next()) {
+ HeapObject* obj = iterator.next();
+ CHECK(obj != NULL);
+ CHECK(!obj->IsDebugInfo());
+ CHECK(!obj->IsBreakPointInfo());
+
+ // If deep check of functions is requested check that no debug break code
+ // is left in all functions.
+ if (check_functions) {
+ if (obj->IsJSFunction()) {
+ JSFunction* fun = JSFunction::cast(obj);
+ for (RelocIterator it(fun->shared()->code()); !it.done(); it.next()) {
+ RelocInfo::Mode rmode = it.rinfo()->rmode();
+ if (RelocInfo::IsCodeTarget(rmode)) {
+ CHECK(!Debug::IsDebugBreak(it.rinfo()->target_address()));
+ } else if (RelocInfo::IsJSReturn(rmode)) {
+ CHECK(!Debug::IsDebugBreakAtReturn(it.rinfo()));
+ }
+ }
+ }
+ }
+ }
+}
+
+
+} } // namespace v8::internal
+
+
+// Check that the debugger has been fully unloaded.
+static void CheckDebuggerUnloaded(bool check_functions = false) {
+ v8::internal::CheckDebuggerUnloaded(check_functions);
+}
+
+
+// Inherit from BreakLocationIterator to get access to protected parts for
+// testing.
+class TestBreakLocationIterator: public v8::internal::BreakLocationIterator {
+ public:
+ explicit TestBreakLocationIterator(Handle<v8::internal::DebugInfo> debug_info)
+ : BreakLocationIterator(debug_info, v8::internal::SOURCE_BREAK_LOCATIONS) {}
+ v8::internal::RelocIterator* it() { return reloc_iterator_; }
+ v8::internal::RelocIterator* it_original() {
+ return reloc_iterator_original_;
+ }
+};
+
+
+// Compile a function, set a break point and check that the call at the break
+// location in the code is the expected debug_break function.
+void CheckDebugBreakFunction(DebugLocalContext* env,
+ const char* source, const char* name,
+ int position, v8::internal::RelocInfo::Mode mode,
+ Code* debug_break) {
+ // Create function and set the break point.
+ Handle<v8::internal::JSFunction> fun = v8::Utils::OpenHandle(
+ *CompileFunction(env, source, name));
+ int bp = SetBreakPoint(fun, position);
+
+ // Check that the debug break function is as expected.
+ Handle<v8::internal::SharedFunctionInfo> shared(fun->shared());
+ CHECK(Debug::HasDebugInfo(shared));
+ TestBreakLocationIterator it1(Debug::GetDebugInfo(shared));
+ it1.FindBreakLocationFromPosition(position);
+ CHECK_EQ(mode, it1.it()->rinfo()->rmode());
+ if (mode != v8::internal::RelocInfo::JS_RETURN) {
+ CHECK_EQ(debug_break,
+ Code::GetCodeFromTargetAddress(it1.it()->rinfo()->target_address()));
+ } else {
+ // TODO(1240753): Make the test architecture independent or split
+ // parts of the debugger into architecture dependent files.
+ CHECK_EQ(0xE8, *(it1.rinfo()->pc()));
+ }
+
+ // Clear the break point and check that the debug break function is no longer
+ // there
+ ClearBreakPoint(bp);
+ CHECK(!Debug::HasDebugInfo(shared));
+ CHECK(Debug::EnsureDebugInfo(shared));
+ TestBreakLocationIterator it2(Debug::GetDebugInfo(shared));
+ it2.FindBreakLocationFromPosition(position);
+ CHECK_EQ(mode, it2.it()->rinfo()->rmode());
+ if (mode == v8::internal::RelocInfo::JS_RETURN) {
+ // TODO(1240753): Make the test architecture independent or split
+ // parts of the debugger into architecture dependent files.
+ CHECK_NE(0xE8, *(it2.rinfo()->pc()));
+ }
+}
+
+
+// --- D e b u g E v e n t H a n d l e r s
+// ---
+// --- The different tests uses a number of debug event handlers.
+// ---
+
+
+// Source for The JavaScript function which picks out the function name on the
+// top frame.
+const char* frame_function_name_source =
+ "function frame_function_name(exec_state) {"
+ " return exec_state.frame(0).func().name();"
+ "}";
+v8::Local<v8::Function> frame_function_name;
+
+
+// Source for The JavaScript function which returns the number of frames.
+static const char* frame_count_source =
+ "function frame_count(exec_state) {"
+ " return exec_state.frameCount();"
+ "}";
+v8::Handle<v8::Function> frame_count;
+
+
+// Global variable to store the last function hit - used by some tests.
+char last_function_hit[80];
+
+// Debug event handler which counts the break points which have been hit.
+int break_point_hit_count = 0;
+static void DebugEventBreakPointHitCount(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ // Count the number of breaks.
+ if (event == v8::Break) {
+ break_point_hit_count++;
+ if (!frame_function_name.IsEmpty()) {
+ // Get the name of the function.
+ const int argc = 1;
+ v8::Handle<v8::Value> argv[argc] = { exec_state };
+ v8::Handle<v8::Value> result = frame_function_name->Call(exec_state,
+ argc, argv);
+ if (result->IsUndefined()) {
+ last_function_hit[0] = '\0';
+ } else {
+ CHECK(result->IsString());
+ v8::Handle<v8::String> function_name(result->ToString());
+ function_name->WriteAscii(last_function_hit);
+ }
+ }
+ }
+}
+
+
+// Debug event handler which counts a number of events and collects the stack
+// height if there is a function compiled for that.
+int exception_hit_count = 0;
+int uncaught_exception_hit_count = 0;
+int last_js_stack_height = -1;
+
+static void DebugEventCounterClear() {
+ break_point_hit_count = 0;
+ exception_hit_count = 0;
+ uncaught_exception_hit_count = 0;
+}
+
+static void DebugEventCounter(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ // Count the number of breaks.
+ if (event == v8::Break) {
+ break_point_hit_count++;
+ } else if (event == v8::Exception) {
+ exception_hit_count++;
+
+ // Check whether the exception was uncaught.
+ v8::Local<v8::String> fun_name = v8::String::New("uncaught");
+ v8::Local<v8::Function> fun =
+ v8::Function::Cast(*event_data->Get(fun_name));
+ v8::Local<v8::Value> result = *fun->Call(event_data, 0, NULL);
+ if (result->IsTrue()) {
+ uncaught_exception_hit_count++;
+ }
+ }
+
+ // Collect the JavsScript stack height if the function frame_count is
+ // compiled.
+ if (!frame_count.IsEmpty()) {
+ static const int kArgc = 1;
+ v8::Handle<v8::Value> argv[kArgc] = { exec_state };
+ // Using exec_state as receiver is just to have a receiver.
+ v8::Handle<v8::Value> result = frame_count->Call(exec_state, kArgc, argv);
+ last_js_stack_height = result->Int32Value();
+ }
+}
+
+
+// Debug event handler which evaluates a number of expressions when a break
+// point is hit. Each evaluated expression is compared with an expected value.
+// For this debug event handler to work the following two global varaibles
+// must be initialized.
+// checks: An array of expressions and expected results
+// evaluate_check_function: A JavaScript function (see below)
+
+// Structure for holding checks to do.
+struct EvaluateCheck {
+ const char* expr; // An expression to evaluate when a break point is hit.
+ v8::Handle<v8::Value> expected; // The expected result.
+};
+// Array of checks to do.
+struct EvaluateCheck* checks = NULL;
+// Source for The JavaScript function which can do the evaluation when a break
+// point is hit.
+const char* evaluate_check_source =
+ "function evaluate_check(exec_state, expr, expected) {"
+ " return exec_state.frame(0).evaluate(expr).value() === expected;"
+ "}";
+v8::Local<v8::Function> evaluate_check_function;
+
+// The actual debug event described by the longer comment above.
+static void DebugEventEvaluate(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ if (event == v8::Break) {
+ for (int i = 0; checks[i].expr != NULL; i++) {
+ const int argc = 3;
+ v8::Handle<v8::Value> argv[argc] = { exec_state,
+ v8::String::New(checks[i].expr),
+ checks[i].expected };
+ v8::Handle<v8::Value> result =
+ evaluate_check_function->Call(exec_state, argc, argv);
+ if (!result->IsTrue()) {
+ v8::String::AsciiValue ascii(checks[i].expected->ToString());
+ V8_Fatal(__FILE__, __LINE__, "%s != %s", checks[i].expr, *ascii);
+ }
+ }
+ }
+}
+
+
+// This debug event listener removes a breakpoint in a function
+int debug_event_remove_break_point = 0;
+static void DebugEventRemoveBreakPoint(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ if (event == v8::Break) {
+ break_point_hit_count++;
+ v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(data);
+ ClearBreakPoint(debug_event_remove_break_point);
+ }
+}
+
+
+// Debug event handler which counts break points hit and performs a step
+// afterwards.
+StepAction step_action = StepIn; // Step action to perform when stepping.
+static void DebugEventStep(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ if (event == v8::Break) {
+ break_point_hit_count++;
+ PrepareStep(step_action);
+ }
+}
+
+
+// Debug event handler which counts break points hit and performs a step
+// afterwards. For each call the expected function is checked.
+// For this debug event handler to work the following two global varaibles
+// must be initialized.
+// expected_step_sequence: An array of the expected function call sequence.
+// frame_function_name: A JavaScript function (see below).
+
+// String containing the expected function call sequence. Note: this only works
+// if functions have name length of one.
+const char* expected_step_sequence = NULL;
+
+// The actual debug event described by the longer comment above.
+static void DebugEventStepSequence(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ if (event == v8::Break || event == v8::Exception) {
+ // Check that the current function is the expected.
+ CHECK(break_point_hit_count <
+ static_cast<int>(strlen(expected_step_sequence)));
+ const int argc = 1;
+ v8::Handle<v8::Value> argv[argc] = { exec_state };
+ v8::Handle<v8::Value> result = frame_function_name->Call(exec_state,
+ argc, argv);
+ CHECK(result->IsString());
+ v8::String::AsciiValue function_name(result->ToString());
+ CHECK_EQ(1, strlen(*function_name));
+ CHECK_EQ((*function_name)[0],
+ expected_step_sequence[break_point_hit_count]);
+
+ // Perform step.
+ break_point_hit_count++;
+ PrepareStep(step_action);
+ }
+}
+
+
+// Debug event handler which performs a garbage collection.
+static void DebugEventBreakPointCollectGarbage(
+ v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ // Perform a garbage collection when break point is hit and continue. Based
+ // on the number of break points hit either scavenge or mark compact
+ // collector is used.
+ if (event == v8::Break) {
+ break_point_hit_count++;
+ if (break_point_hit_count % 2 == 0) {
+ // Scavenge.
+ Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
+ } else {
+ // Mark sweep (and perhaps compact).
+ Heap::CollectAllGarbage();
+ }
+ }
+}
+
+
+// Debug event handler which re-issues a debug break and calls the garbage
+// collector to have the heap verified.
+static void DebugEventBreak(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+ // When hitting a debug event listener there must be a break set.
+ CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+ if (event == v8::Break) {
+ // Count the number of breaks.
+ break_point_hit_count++;
+
+ // Run the garbage collector to enforce heap verification if option
+ // --verify-heap is set.
+ Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
+
+ // Set the break flag again to come back here as soon as possible.
+ v8::Debug::DebugBreak();
+ }
+}
+
+
+// --- M e s s a g e C a l l b a c k
+
+
+// Message callback which counts the number of messages.
+int message_callback_count = 0;
+
+static void MessageCallbackCountClear() {
+ message_callback_count = 0;
+}
+
+static void MessageCallbackCount(v8::Handle<v8::Message> message,
+ v8::Handle<v8::Value> data) {
+ message_callback_count++;
+}
+
+
+// --- T h e A c t u a l T e s t s
+
+
+// Test that the debug break function is the expected one for different kinds
+// of break locations.
+TEST(DebugStub) {
+ using ::v8::internal::Builtins;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ CheckDebugBreakFunction(&env,
+ "function f1(){}", "f1",
+ 0,
+ v8::internal::RelocInfo::JS_RETURN,
+ NULL);
+ CheckDebugBreakFunction(&env,
+ "function f2(){x=1;}", "f2",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET,
+ Builtins::builtin(Builtins::StoreIC_DebugBreak));
+ CheckDebugBreakFunction(&env,
+ "function f3(){var a=x;}", "f3",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET_CONTEXT,
+ Builtins::builtin(Builtins::LoadIC_DebugBreak));
+
+// TODO(1240753): Make the test architecture independent or split
+// parts of the debugger into architecture dependent files. This
+// part currently disabled as it is not portable between IA32/ARM.
+// Currently on ICs for keyed store/load on ARM.
+#if !defined (__arm__) && !defined(__thumb__)
+ CheckDebugBreakFunction(
+ &env,
+ "function f4(){var index='propertyName'; var a={}; a[index] = 'x';}",
+ "f4",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET,
+ Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
+ CheckDebugBreakFunction(
+ &env,
+ "function f5(){var index='propertyName'; var a={}; return a[index];}",
+ "f5",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET,
+ Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
+#endif
+
+ // Check the debug break code stubs for call ICs with different number of
+ // parameters.
+ Handle<Code> debug_break_0 = v8::internal::ComputeCallDebugBreak(0);
+ Handle<Code> debug_break_1 = v8::internal::ComputeCallDebugBreak(1);
+ Handle<Code> debug_break_4 = v8::internal::ComputeCallDebugBreak(4);
+
+ CheckDebugBreakFunction(&env,
+ "function f4_0(){x();}", "f4_0",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET_CONTEXT,
+ *debug_break_0);
+
+ CheckDebugBreakFunction(&env,
+ "function f4_1(){x(1);}", "f4_1",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET_CONTEXT,
+ *debug_break_1);
+
+ CheckDebugBreakFunction(&env,
+ "function f4_4(){x(1,2,3,4);}", "f4_4",
+ 0,
+ v8::internal::RelocInfo::CODE_TARGET_CONTEXT,
+ *debug_break_4);
+}
+
+
+// Test that the debug info in the VM is in sync with the functions being
+// debugged.
+TEST(DebugInfo) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ // Create a couple of functions for the test.
+ v8::Local<v8::Function> foo =
+ CompileFunction(&env, "function foo(){}", "foo");
+ v8::Local<v8::Function> bar =
+ CompileFunction(&env, "function bar(){}", "bar");
+ // Initially no functions are debugged.
+ CHECK_EQ(0, v8::internal::GetDebuggedFunctions()->length());
+ CHECK(!HasDebugInfo(foo));
+ CHECK(!HasDebugInfo(bar));
+ // One function (foo) is debugged.
+ int bp1 = SetBreakPoint(foo, 0);
+ CHECK_EQ(1, v8::internal::GetDebuggedFunctions()->length());
+ CHECK(HasDebugInfo(foo));
+ CHECK(!HasDebugInfo(bar));
+ // Two functions are debugged.
+ int bp2 = SetBreakPoint(bar, 0);
+ CHECK_EQ(2, v8::internal::GetDebuggedFunctions()->length());
+ CHECK(HasDebugInfo(foo));
+ CHECK(HasDebugInfo(bar));
+ // One function (bar) is debugged.
+ ClearBreakPoint(bp1);
+ CHECK_EQ(1, v8::internal::GetDebuggedFunctions()->length());
+ CHECK(!HasDebugInfo(foo));
+ CHECK(HasDebugInfo(bar));
+ // No functions are debugged.
+ ClearBreakPoint(bp2);
+ CHECK_EQ(0, v8::internal::GetDebuggedFunctions()->length());
+ CHECK(!HasDebugInfo(foo));
+ CHECK(!HasDebugInfo(bar));
+}
+
+
+// Test that a break point can be set at an IC store location.
+TEST(BreakPointICStore) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("function foo(){bar=0;}"))->Run();
+ v8::Local<v8::Function> foo =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
+
+ // Run without breakpoints.
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Run with breakpoint
+ int bp = SetBreakPoint(foo, 0);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Run without breakpoints.
+ ClearBreakPoint(bp);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that a break point can be set at an IC load location.
+TEST(BreakPointICLoad) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("bar=1"))->Run();
+ v8::Script::Compile(v8::String::New("function foo(){var x=bar;}"))->Run();
+ v8::Local<v8::Function> foo =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
+
+ // Run without breakpoints.
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Run with breakpoint
+ int bp = SetBreakPoint(foo, 0);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Run without breakpoints.
+ ClearBreakPoint(bp);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that a break point can be set at an IC call location.
+TEST(BreakPointICCall) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("function bar(){}"))->Run();
+ v8::Script::Compile(v8::String::New("function foo(){bar();}"))->Run();
+ v8::Local<v8::Function> foo =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
+
+ // Run without breakpoints.
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Run with breakpoint
+ int bp = SetBreakPoint(foo, 0);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Run without breakpoints.
+ ClearBreakPoint(bp);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that a break point can be set at a return store location.
+TEST(BreakPointReturn) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("function foo(){}"))->Run();
+ v8::Local<v8::Function> foo =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
+
+ // Run without breakpoints.
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Run with breakpoint
+ int bp = SetBreakPoint(foo, 0);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Run without breakpoints.
+ ClearBreakPoint(bp);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+static void CallWithBreakPoints(v8::Local<v8::Object> recv,
+ v8::Local<v8::Function> f,
+ int break_point_count,
+ int call_count) {
+ break_point_hit_count = 0;
+ for (int i = 0; i < call_count; i++) {
+ f->Call(recv, 0, NULL);
+ CHECK_EQ((i + 1) * break_point_count, break_point_hit_count);
+ }
+}
+
+// Test GC during break point processing.
+TEST(GCDuringBreakPointProcessing) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointCollectGarbage,
+ v8::Undefined());
+ v8::Local<v8::Function> foo;
+
+ // Test IC store break point with garbage collection.
+ foo = CompileFunction(&env, "function foo(){bar=0;}", "foo");
+ SetBreakPoint(foo, 0);
+ CallWithBreakPoints(env->Global(), foo, 1, 10);
+
+ // Test IC load break point with garbage collection.
+ foo = CompileFunction(&env, "bar=1;function foo(){var x=bar;}", "foo");
+ SetBreakPoint(foo, 0);
+ CallWithBreakPoints(env->Global(), foo, 1, 10);
+
+ // Test IC call break point with garbage collection.
+ foo = CompileFunction(&env, "function bar(){};function foo(){bar();}", "foo");
+ SetBreakPoint(foo, 0);
+ CallWithBreakPoints(env->Global(), foo, 1, 10);
+
+ // Test return break point with garbage collection.
+ foo = CompileFunction(&env, "function foo(){}", "foo");
+ SetBreakPoint(foo, 0);
+ CallWithBreakPoints(env->Global(), foo, 1, 25);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Call the function three times with different garbage collections in between
+// and make sure that the break point survives.
+static void CallAndGC(v8::Local<v8::Object> recv, v8::Local<v8::Function> f) {
+ break_point_hit_count = 0;
+
+ for (int i = 0; i < 3; i++) {
+ // Call function.
+ f->Call(recv, 0, NULL);
+ CHECK_EQ(1 + i * 3, break_point_hit_count);
+
+ // Scavenge and call function.
+ Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
+ f->Call(recv, 0, NULL);
+ CHECK_EQ(2 + i * 3, break_point_hit_count);
+
+ // Mark sweep (and perhaps compact) and call function.
+ Heap::CollectAllGarbage();
+ f->Call(recv, 0, NULL);
+ CHECK_EQ(3 + i * 3, break_point_hit_count);
+ }
+}
+
+
+// Test that a break point can be set at a return store location.
+TEST(BreakPointSurviveGC) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Local<v8::Function> foo;
+
+ // Test IC store break point with garbage collection.
+ foo = CompileFunction(&env, "function foo(){bar=0;}", "foo");
+ SetBreakPoint(foo, 0);
+ CallAndGC(env->Global(), foo);
+
+ // Test IC load break point with garbage collection.
+ foo = CompileFunction(&env, "bar=1;function foo(){var x=bar;}", "foo");
+ SetBreakPoint(foo, 0);
+ CallAndGC(env->Global(), foo);
+
+ // Test IC call break point with garbage collection.
+ foo = CompileFunction(&env, "function bar(){};function foo(){bar();}", "foo");
+ SetBreakPoint(foo, 0);
+ CallAndGC(env->Global(), foo);
+
+ // Test return break point with garbage collection.
+ foo = CompileFunction(&env, "function foo(){}", "foo");
+ SetBreakPoint(foo, 0);
+ CallAndGC(env->Global(), foo);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that break points can be set using the global Debug object.
+TEST(BreakPointThroughJavaScript) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("function bar(){}"))->Run();
+ v8::Script::Compile(v8::String::New("function foo(){bar();bar();}"))->Run();
+ // 012345678901234567890
+ // 1 2
+ // Break points are set at position 3 and 9
+ v8::Local<v8::Script> foo = v8::Script::Compile(v8::String::New("foo()"));
+
+ // Run without breakpoints.
+ foo->Run();
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Run with one breakpoint
+ int bp1 = SetBreakPointFromJS("foo", 0, 3);
+ foo->Run();
+ CHECK_EQ(1, break_point_hit_count);
+ foo->Run();
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Run with two breakpoints
+ int bp2 = SetBreakPointFromJS("foo", 0, 9);
+ foo->Run();
+ CHECK_EQ(4, break_point_hit_count);
+ foo->Run();
+ CHECK_EQ(6, break_point_hit_count);
+
+ // Run with one breakpoint
+ ClearBreakPointFromJS(bp2);
+ foo->Run();
+ CHECK_EQ(7, break_point_hit_count);
+ foo->Run();
+ CHECK_EQ(8, break_point_hit_count);
+
+ // Run without breakpoints.
+ ClearBreakPointFromJS(bp1);
+ foo->Run();
+ CHECK_EQ(8, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+
+ // Make sure that the break point numbers are consecutive.
+ CHECK_EQ(1, bp1);
+ CHECK_EQ(2, bp2);
+}
+
+
+// Test that break points on scripts identified by name can be set using the
+// global Debug object.
+TEST(ScriptBreakPointByNameThroughJavaScript) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::String> script = v8::String::New(
+ "function f() {\n"
+ " function h() {\n"
+ " a = 0; // line 2\n"
+ " }\n"
+ " b = 1; // line 4\n"
+ " return h();\n"
+ "}\n"
+ "\n"
+ "function g() {\n"
+ " function h() {\n"
+ " a = 0;\n"
+ " }\n"
+ " b = 2; // line 12\n"
+ " h();\n"
+ " b = 3; // line 14\n"
+ " f(); // line 15\n"
+ "}");
+
+ // Compile the script and get the two functions.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(v8::String::New("test"));
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ v8::Local<v8::Function> g =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g")));
+
+ // Call f and g without break points.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Call f and g with break point on line 12.
+ int sbp1 = SetScriptBreakPointByNameFromJS("test", 12, 0);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ // Remove the break point again.
+ break_point_hit_count = 0;
+ ClearBreakPointFromJS(sbp1);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Call f and g with break point on line 2.
+ int sbp2 = SetScriptBreakPointByNameFromJS("test", 2, 0);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Call f and g with break point on line 2, 4, 12, 14 and 15.
+ int sbp3 = SetScriptBreakPointByNameFromJS("test", 4, 0);
+ int sbp4 = SetScriptBreakPointByNameFromJS("test", 12, 0);
+ int sbp5 = SetScriptBreakPointByNameFromJS("test", 14, 0);
+ int sbp6 = SetScriptBreakPointByNameFromJS("test", 15, 0);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(7, break_point_hit_count);
+
+ // Remove all the break points again.
+ break_point_hit_count = 0;
+ ClearBreakPointFromJS(sbp2);
+ ClearBreakPointFromJS(sbp3);
+ ClearBreakPointFromJS(sbp4);
+ ClearBreakPointFromJS(sbp5);
+ ClearBreakPointFromJS(sbp6);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+
+ // Make sure that the break point numbers are consecutive.
+ CHECK_EQ(1, sbp1);
+ CHECK_EQ(2, sbp2);
+ CHECK_EQ(3, sbp3);
+ CHECK_EQ(4, sbp4);
+ CHECK_EQ(5, sbp5);
+ CHECK_EQ(6, sbp6);
+}
+
+
+TEST(ScriptBreakPointByIdThroughJavaScript) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::String> source = v8::String::New(
+ "function f() {\n"
+ " function h() {\n"
+ " a = 0; // line 2\n"
+ " }\n"
+ " b = 1; // line 4\n"
+ " return h();\n"
+ "}\n"
+ "\n"
+ "function g() {\n"
+ " function h() {\n"
+ " a = 0;\n"
+ " }\n"
+ " b = 2; // line 12\n"
+ " h();\n"
+ " b = 3; // line 14\n"
+ " f(); // line 15\n"
+ "}");
+
+ // Compile the script and get the two functions.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(v8::String::New("test"));
+ v8::Local<v8::Script> script = v8::Script::Compile(source, &origin);
+ script->Run();
+ v8::Local<v8::Function> f =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ v8::Local<v8::Function> g =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g")));
+
+ // Get the script id knowing that internally it is a 32 integer.
+ uint32_t script_id = script->Id()->Uint32Value();
+
+ // Call f and g without break points.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Call f and g with break point on line 12.
+ int sbp1 = SetScriptBreakPointByIdFromJS(script_id, 12, 0);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ // Remove the break point again.
+ break_point_hit_count = 0;
+ ClearBreakPointFromJS(sbp1);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Call f and g with break point on line 2.
+ int sbp2 = SetScriptBreakPointByIdFromJS(script_id, 2, 0);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Call f and g with break point on line 2, 4, 12, 14 and 15.
+ int sbp3 = SetScriptBreakPointByIdFromJS(script_id, 4, 0);
+ int sbp4 = SetScriptBreakPointByIdFromJS(script_id, 12, 0);
+ int sbp5 = SetScriptBreakPointByIdFromJS(script_id, 14, 0);
+ int sbp6 = SetScriptBreakPointByIdFromJS(script_id, 15, 0);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(7, break_point_hit_count);
+
+ // Remove all the break points again.
+ break_point_hit_count = 0;
+ ClearBreakPointFromJS(sbp2);
+ ClearBreakPointFromJS(sbp3);
+ ClearBreakPointFromJS(sbp4);
+ ClearBreakPointFromJS(sbp5);
+ ClearBreakPointFromJS(sbp6);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+
+ // Make sure that the break point numbers are consecutive.
+ CHECK_EQ(1, sbp1);
+ CHECK_EQ(2, sbp2);
+ CHECK_EQ(3, sbp3);
+ CHECK_EQ(4, sbp4);
+ CHECK_EQ(5, sbp5);
+ CHECK_EQ(6, sbp6);
+}
+
+
+// Test conditional script break points.
+TEST(EnableDisableScriptBreakPoint) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::String> script = v8::String::New(
+ "function f() {\n"
+ " a = 0; // line 1\n"
+ "};");
+
+ // Compile the script and get function f.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(v8::String::New("test"));
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Set script break point on line 1 (in function f).
+ int sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
+
+ // Call f while enabeling and disabling the script break point.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ DisableScriptBreakPointFromJS(sbp);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ EnableScriptBreakPointFromJS(sbp);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ DisableScriptBreakPointFromJS(sbp);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Reload the script and get f again checking that the disabeling survives.
+ v8::Script::Compile(script, &origin)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ EnableScriptBreakPointFromJS(sbp);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(3, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test conditional script break points.
+TEST(ConditionalScriptBreakPoint) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::String> script = v8::String::New(
+ "count = 0;\n"
+ "function f() {\n"
+ " g(count++); // line 2\n"
+ "};\n"
+ "function g(x) {\n"
+ " var a=x; // line 5\n"
+ "};");
+
+ // Compile the script and get function f.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(v8::String::New("test"));
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Set script break point on line 5 (in function g).
+ int sbp1 = SetScriptBreakPointByNameFromJS("test", 5, 0);
+
+ // Call f with different conditions on the script break point.
+ break_point_hit_count = 0;
+ ChangeScriptBreakPointConditionFromJS(sbp1, "false");
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ ChangeScriptBreakPointConditionFromJS(sbp1, "true");
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ ChangeScriptBreakPointConditionFromJS(sbp1, "a % 2 == 0");
+ break_point_hit_count = 0;
+ for (int i = 0; i < 10; i++) {
+ f->Call(env->Global(), 0, NULL);
+ }
+ CHECK_EQ(5, break_point_hit_count);
+
+ // Reload the script and get f again checking that the condition survives.
+ v8::Script::Compile(script, &origin)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ break_point_hit_count = 0;
+ for (int i = 0; i < 10; i++) {
+ f->Call(env->Global(), 0, NULL);
+ }
+ CHECK_EQ(5, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test ignore count on script break points.
+TEST(ScriptBreakPointIgnoreCount) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::String> script = v8::String::New(
+ "function f() {\n"
+ " a = 0; // line 1\n"
+ "};");
+
+ // Compile the script and get function f.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(v8::String::New("test"));
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Set script break point on line 1 (in function f).
+ int sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
+
+ // Call f with different ignores on the script break point.
+ break_point_hit_count = 0;
+ ChangeScriptBreakPointIgnoreCountFromJS(sbp, 1);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ ChangeScriptBreakPointIgnoreCountFromJS(sbp, 5);
+ break_point_hit_count = 0;
+ for (int i = 0; i < 10; i++) {
+ f->Call(env->Global(), 0, NULL);
+ }
+ CHECK_EQ(5, break_point_hit_count);
+
+ // Reload the script and get f again checking that the ignore survives.
+ v8::Script::Compile(script, &origin)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ break_point_hit_count = 0;
+ for (int i = 0; i < 10; i++) {
+ f->Call(env->Global(), 0, NULL);
+ }
+ CHECK_EQ(5, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that script break points survive when a script is reloaded.
+TEST(ScriptBreakPointReload) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::Function> f;
+ v8::Local<v8::String> script = v8::String::New(
+ "function f() {\n"
+ " function h() {\n"
+ " a = 0; // line 2\n"
+ " }\n"
+ " b = 1; // line 4\n"
+ " return h();\n"
+ "}");
+
+ v8::ScriptOrigin origin_1 = v8::ScriptOrigin(v8::String::New("1"));
+ v8::ScriptOrigin origin_2 = v8::ScriptOrigin(v8::String::New("2"));
+
+ // Set a script break point before the script is loaded.
+ SetScriptBreakPointByNameFromJS("1", 2, 0);
+
+ // Compile the script and get the function.
+ v8::Script::Compile(script, &origin_1)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Call f and check that the script break point is active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ // Compile the script again with a different script data and get the
+ // function.
+ v8::Script::Compile(script, &origin_2)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Call f and check that no break points are set.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Compile the script again and get the function.
+ v8::Script::Compile(script, &origin_1)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Call f and check that the script break point is active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test when several scripts has the same script data
+TEST(ScriptBreakPointMultiple) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::Function> f;
+ v8::Local<v8::String> script_f = v8::String::New(
+ "function f() {\n"
+ " a = 0; // line 1\n"
+ "}");
+
+ v8::Local<v8::Function> g;
+ v8::Local<v8::String> script_g = v8::String::New(
+ "function g() {\n"
+ " b = 0; // line 1\n"
+ "}");
+
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(v8::String::New("test"));
+
+ // Set a script break point before the scripts are loaded.
+ int sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
+
+ // Compile the scripts with same script data and get the functions.
+ v8::Script::Compile(script_f, &origin)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ v8::Script::Compile(script_g, &origin)->Run();
+ g = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g")));
+
+ // Call f and g and check that the script break point is active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Clear the script break point.
+ ClearBreakPointFromJS(sbp);
+
+ // Call f and g and check that the script break point is no longer active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Set script break point with the scripts loaded.
+ sbp = SetScriptBreakPointByNameFromJS("test", 1, 0);
+
+ // Call f and g and check that the script break point is active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test the script origin which has both name and line offset.
+TEST(ScriptBreakPointLineOffset) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::Function> f;
+ v8::Local<v8::String> script = v8::String::New(
+ "function f() {\n"
+ " a = 0; // line 8 as this script has line offset 7\n"
+ " b = 0; // line 9 as this script has line offset 7\n"
+ "}");
+
+ // Create script origin both name and line offset.
+ v8::ScriptOrigin origin(v8::String::New("test.html"),
+ v8::Integer::New(7));
+
+ // Set two script break points before the script is loaded.
+ int sbp1 = SetScriptBreakPointByNameFromJS("test.html", 8, 0);
+ int sbp2 = SetScriptBreakPointByNameFromJS("test.html", 9, 0);
+
+ // Compile the script and get the function.
+ v8::Script::Compile(script, &origin)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ // Call f and check that the script break point is active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Clear the script break points.
+ ClearBreakPointFromJS(sbp1);
+ ClearBreakPointFromJS(sbp2);
+
+ // Call f and check that no script break points are active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Set a script break point with the script loaded.
+ sbp1 = SetScriptBreakPointByNameFromJS("test.html", 9, 0);
+
+ // Call f and check that the script break point is active.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test script break points set on lines.
+TEST(ScriptBreakPointLine) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ // Create a function for checking the function when hitting a break point.
+ frame_function_name = CompileFunction(&env,
+ frame_function_name_source,
+ "frame_function_name");
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::Function> f;
+ v8::Local<v8::Function> g;
+ v8::Local<v8::String> script = v8::String::New(
+ "a = 0 // line 0\n"
+ "function f() {\n"
+ " a = 1; // line 2\n"
+ "}\n"
+ " a = 2; // line 4\n"
+ " /* xx */ function g() { // line 5\n"
+ " function h() { // line 6\n"
+ " a = 3; // line 7\n"
+ " }\n"
+ " h(); // line 9\n"
+ " a = 4; // line 10\n"
+ " }\n"
+ " a=5; // line 12");
+
+ // Set a couple script break point before the script is loaded.
+ int sbp1 = SetScriptBreakPointByNameFromJS("test.html", 0, -1);
+ int sbp2 = SetScriptBreakPointByNameFromJS("test.html", 1, -1);
+ int sbp3 = SetScriptBreakPointByNameFromJS("test.html", 5, -1);
+
+ // Compile the script and get the function.
+ break_point_hit_count = 0;
+ v8::ScriptOrigin origin(v8::String::New("test.html"), v8::Integer::New(0));
+ v8::Script::Compile(script, &origin)->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ g = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("g")));
+
+ // Chesk that a break point was hit when the script was run.
+ CHECK_EQ(1, break_point_hit_count);
+ CHECK_EQ(0, strlen(last_function_hit));
+
+ // Call f and check that the script break point.
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+ CHECK_EQ("f", last_function_hit);
+
+ // Call g and check that the script break point.
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(3, break_point_hit_count);
+ CHECK_EQ("g", last_function_hit);
+
+ // Clear the script break point on g and set one on h.
+ ClearBreakPointFromJS(sbp3);
+ int sbp4 = SetScriptBreakPointByNameFromJS("test.html", 6, -1);
+
+ // Call g and check that the script break point in h is hit.
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(4, break_point_hit_count);
+ CHECK_EQ("h", last_function_hit);
+
+ // Clear break points in f and h. Set a new one in the script between
+ // functions f and g and test that there is no break points in f and g any
+ // more.
+ ClearBreakPointFromJS(sbp2);
+ ClearBreakPointFromJS(sbp4);
+ int sbp5 = SetScriptBreakPointByNameFromJS("test.html", 4, -1);
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ g->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Reload the script which should hit two break points.
+ break_point_hit_count = 0;
+ v8::Script::Compile(script, &origin)->Run();
+ CHECK_EQ(2, break_point_hit_count);
+ CHECK_EQ(0, strlen(last_function_hit));
+
+ // Set a break point in the code after the last function decleration.
+ int sbp6 = SetScriptBreakPointByNameFromJS("test.html", 12, -1);
+
+ // Reload the script which should hit three break points.
+ break_point_hit_count = 0;
+ v8::Script::Compile(script, &origin)->Run();
+ CHECK_EQ(3, break_point_hit_count);
+ CHECK_EQ(0, strlen(last_function_hit));
+
+ // Clear the last break points, and reload the script which should not hit any
+ // break points.
+ ClearBreakPointFromJS(sbp1);
+ ClearBreakPointFromJS(sbp5);
+ ClearBreakPointFromJS(sbp6);
+ break_point_hit_count = 0;
+ v8::Script::Compile(script, &origin)->Run();
+ CHECK_EQ(0, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that it is possible to remove the last break point for a function
+// inside the break handling of that break point.
+TEST(RemoveBreakPointInBreak) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ v8::Local<v8::Function> foo =
+ CompileFunction(&env, "function foo(){a=1;}", "foo");
+ debug_event_remove_break_point = SetBreakPoint(foo, 0);
+
+ // Register the debug event listener pasing the function
+ v8::Debug::SetDebugEventListener(DebugEventRemoveBreakPoint, foo);
+
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that the debugger statement causes a break.
+TEST(DebuggerStatement) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("function bar(){debugger}"))->Run();
+ v8::Script::Compile(v8::String::New(
+ "function foo(){debugger;debugger;}"))->Run();
+ v8::Local<v8::Function> foo =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
+ v8::Local<v8::Function> bar =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("bar")));
+
+ // Run function with debugger statement
+ bar->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ // Run function with two debugger statement
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(3, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Thest that the evaluation of expressions when a break point is hit generates
+// the correct results.
+TEST(DebugEvaluate) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ // Create a function for checking the evaluation when hitting a break point.
+ evaluate_check_function = CompileFunction(&env,
+ evaluate_check_source,
+ "evaluate_check");
+ // Register the debug event listener
+ v8::Debug::SetDebugEventListener(DebugEventEvaluate);
+
+ // Different expected vaules of x and a when in a break point (u = undefined,
+ // d = Hello, world!).
+ struct EvaluateCheck checks_uu[] = {
+ {"x", v8::Undefined()},
+ {"a", v8::Undefined()},
+ {NULL, v8::Handle<v8::Value>()}
+ };
+ struct EvaluateCheck checks_hu[] = {
+ {"x", v8::String::New("Hello, world!")},
+ {"a", v8::Undefined()},
+ {NULL, v8::Handle<v8::Value>()}
+ };
+ struct EvaluateCheck checks_hh[] = {
+ {"x", v8::String::New("Hello, world!")},
+ {"a", v8::String::New("Hello, world!")},
+ {NULL, v8::Handle<v8::Value>()}
+ };
+
+ // Simple test function. The "y=0" is in the function foo to provide a break
+ // location. For "y=0" the "y" is at position 15 in the barbar function
+ // therefore setting breakpoint at position 15 will break at "y=0" and
+ // setting it higher will break after.
+ v8::Local<v8::Function> foo = CompileFunction(&env,
+ "function foo(x) {"
+ " var a;"
+ " y=0; /* To ensure break location.*/"
+ " a=x;"
+ "}",
+ "foo");
+ const int foo_break_position = 15;
+
+ // Arguments with one parameter "Hello, world!"
+ v8::Handle<v8::Value> argv_foo[1] = { v8::String::New("Hello, world!") };
+
+ // Call foo with breakpoint set before a=x and undefined as parameter.
+ int bp = SetBreakPoint(foo, foo_break_position);
+ checks = checks_uu;
+ foo->Call(env->Global(), 0, NULL);
+
+ // Call foo with breakpoint set before a=x and parameter "Hello, world!".
+ checks = checks_hu;
+ foo->Call(env->Global(), 1, argv_foo);
+
+ // Call foo with breakpoint set after a=x and parameter "Hello, world!".
+ ClearBreakPoint(bp);
+ SetBreakPoint(foo, foo_break_position + 1);
+ checks = checks_hh;
+ foo->Call(env->Global(), 1, argv_foo);
+
+ // Test function with an inner function. The "y=0" is in function barbar
+ // to provide a break location. For "y=0" the "y" is at position 8 in the
+ // barbar function therefore setting breakpoint at position 8 will break at
+ // "y=0" and setting it higher will break after.
+ v8::Local<v8::Function> bar = CompileFunction(&env,
+ "y = 0;"
+ "x = 'Goodbye, world!';"
+ "function bar(x, b) {"
+ " var a;"
+ " function barbar() {"
+ " y=0; /* To ensure break location.*/"
+ " a=x;"
+ " };"
+ " debug.Debug.clearAllBreakPoints();"
+ " barbar();"
+ " y=0;a=x;"
+ "}",
+ "bar");
+ const int barbar_break_position = 8;
+
+ // Call bar setting breakpoint before a=x in barbar and undefined as
+ // parameter.
+ checks = checks_uu;
+ v8::Handle<v8::Value> argv_bar_1[2] = {
+ v8::Undefined(),
+ v8::Number::New(barbar_break_position)
+ };
+ bar->Call(env->Global(), 2, argv_bar_1);
+
+ // Call bar setting breakpoint before a=x in barbar and parameter
+ // "Hello, world!".
+ checks = checks_hu;
+ v8::Handle<v8::Value> argv_bar_2[2] = {
+ v8::String::New("Hello, world!"),
+ v8::Number::New(barbar_break_position)
+ };
+ bar->Call(env->Global(), 2, argv_bar_2);
+
+ // Call bar setting breakpoint after a=x in barbar and parameter
+ // "Hello, world!".
+ checks = checks_hh;
+ v8::Handle<v8::Value> argv_bar_3[2] = {
+ v8::String::New("Hello, world!"),
+ v8::Number::New(barbar_break_position + 1)
+ };
+ bar->Call(env->Global(), 2, argv_bar_3);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Simple test of the stepping mechanism using only store ICs.
+TEST(DebugStepLinear) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for testing stepping.
+ v8::Local<v8::Function> foo = CompileFunction(&env,
+ "function foo(){a=1;b=1;c=1;}",
+ "foo");
+ SetBreakPoint(foo, 3);
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+
+ // With stepping all break locations are hit.
+ CHECK_EQ(4, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+
+ // Register a debug event listener which just counts.
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount);
+
+ SetBreakPoint(foo, 3);
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+
+ // Without stepping only active break points are hit.
+ CHECK_EQ(1, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test the stepping mechanism with different ICs.
+TEST(DebugStepLinearMixedICs) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for testing stepping.
+ v8::Local<v8::Function> foo = CompileFunction(&env,
+ "function bar() {};"
+ "function foo() {"
+ " var x;"
+ " var index='name';"
+ " var y = {};"
+ " a=1;b=2;x=a;y[index]=3;x=y[index];bar();}", "foo");
+ SetBreakPoint(foo, 0);
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+
+ // With stepping all break locations are hit. For ARM the keyed load/store
+ // is not hit as they are not implemented as ICs.
+#if defined (__arm__) || defined(__thumb__)
+ CHECK_EQ(6, break_point_hit_count);
+#else
+ CHECK_EQ(8, break_point_hit_count);
+#endif
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+
+ // Register a debug event listener which just counts.
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount);
+
+ SetBreakPoint(foo, 0);
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+
+ // Without stepping only active break points are hit.
+ CHECK_EQ(1, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepIf) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " a = 1;"
+ " if (x) {"
+ " b = 1;"
+ " } else {"
+ " c = 1;"
+ " d = 1;"
+ " }"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 0);
+
+ // Stepping through the true part.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_true[argc] = { v8::True() };
+ foo->Call(env->Global(), argc, argv_true);
+ CHECK_EQ(3, break_point_hit_count);
+
+ // Stepping through the false part.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_false[argc] = { v8::False() };
+ foo->Call(env->Global(), argc, argv_false);
+ CHECK_EQ(4, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepSwitch) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " a = 1;"
+ " switch (x) {"
+ " case 1:"
+ " b = 1;"
+ " case 2:"
+ " c = 1;"
+ " break;"
+ " case 3:"
+ " d = 1;"
+ " e = 1;"
+ " break;"
+ " }"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 0);
+
+ // One case with fall-through.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_1[argc] = { v8::Number::New(1) };
+ foo->Call(env->Global(), argc, argv_1);
+ CHECK_EQ(4, break_point_hit_count);
+
+ // Another case.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_2[argc] = { v8::Number::New(2) };
+ foo->Call(env->Global(), argc, argv_2);
+ CHECK_EQ(3, break_point_hit_count);
+
+ // Last case.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_3[argc] = { v8::Number::New(3) };
+ foo->Call(env->Global(), argc, argv_3);
+ CHECK_EQ(4, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepFor) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " a = 1;"
+ " for (i = 0; i < x; i++) {"
+ " b = 1;"
+ " }"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 8); // "a = 1;"
+
+ // Looping 10 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+ foo->Call(env->Global(), argc, argv_10);
+ CHECK_EQ(23, break_point_hit_count);
+
+ // Looping 100 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+ foo->Call(env->Global(), argc, argv_100);
+ CHECK_EQ(203, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(StepInOutSimple) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for checking the function when hitting a break point.
+ frame_function_name = CompileFunction(&env,
+ frame_function_name_source,
+ "frame_function_name");
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStepSequence);
+
+ // Create functions for testing stepping.
+ const char* src = "function a() {b();c();}; "
+ "function b() {c();}; "
+ "function c() {}; ";
+ v8::Local<v8::Function> a = CompileFunction(&env, src, "a");
+ SetBreakPoint(a, 0);
+
+ // Step through invocation of a with step in.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "abcbaca";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of a with step next.
+ step_action = StepNext;
+ break_point_hit_count = 0;
+ expected_step_sequence = "aaa";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of a with step out.
+ step_action = StepOut;
+ break_point_hit_count = 0;
+ expected_step_sequence = "a";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(StepInOutTree) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for checking the function when hitting a break point.
+ frame_function_name = CompileFunction(&env,
+ frame_function_name_source,
+ "frame_function_name");
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStepSequence);
+
+ // Create functions for testing stepping.
+ const char* src = "function a() {b(c(d()),d());c(d());d()}; "
+ "function b(x,y) {c();}; "
+ "function c(x) {}; "
+ "function d() {}; ";
+ v8::Local<v8::Function> a = CompileFunction(&env, src, "a");
+ SetBreakPoint(a, 0);
+
+ // Step through invocation of a with step in.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "adacadabcbadacada";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of a with step next.
+ step_action = StepNext;
+ break_point_hit_count = 0;
+ expected_step_sequence = "aaaa";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of a with step out.
+ step_action = StepOut;
+ break_point_hit_count = 0;
+ expected_step_sequence = "a";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded(true);
+}
+
+
+TEST(StepInOutBranch) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for checking the function when hitting a break point.
+ frame_function_name = CompileFunction(&env,
+ frame_function_name_source,
+ "frame_function_name");
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStepSequence);
+
+ // Create functions for testing stepping.
+ const char* src = "function a() {b(false);c();}; "
+ "function b(x) {if(x){c();};}; "
+ "function c() {}; ";
+ v8::Local<v8::Function> a = CompileFunction(&env, src, "a");
+ SetBreakPoint(a, 0);
+
+ // Step through invocation of a.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "abaca";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test that step in does not step into native functions.
+TEST(DebugStepNatives) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for testing stepping.
+ v8::Local<v8::Function> foo = CompileFunction(
+ &env,
+ "function foo(){debugger;Math.sin(1);}",
+ "foo");
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+
+ // With stepping all break locations are hit.
+ CHECK_EQ(3, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+
+ // Register a debug event listener which just counts.
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount);
+
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+
+ // Without stepping only active break points are hit.
+ CHECK_EQ(1, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test break on exceptions. For each exception break combination the number
+// of debug event exception callbacks and message callbacks are collected. The
+// number of debug event exception callbacks are used to check that the
+// debugger is called correctly and the number of message callbacks is used to
+// check that uncaught exceptions are still returned even if there is a break
+// for them.
+TEST(BreakOnException) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::internal::Top::TraceException(false);
+
+ // Create functions for testing break on exception.
+ v8::Local<v8::Function> throws =
+ CompileFunction(&env, "function throws(){throw 1;}", "throws");
+ v8::Local<v8::Function> caught =
+ CompileFunction(&env,
+ "function caught(){try {throws();} catch(e) {};}",
+ "caught");
+ v8::Local<v8::Function> notCaught =
+ CompileFunction(&env, "function notCaught(){throws();}", "notCaught");
+
+ v8::V8::AddMessageListener(MessageCallbackCount);
+ v8::Debug::SetDebugEventListener(DebugEventCounter);
+
+ // Initial state should be break on uncaught exception.
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // No break on exception
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnException(false, false);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // Break on uncaught exception
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnException(false, true);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // Break on exception and uncaught exception
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnException(true, true);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // Break on exception
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnException(true, false);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // No break on exception using JavaScript
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnExceptionFromJS(false, false);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // Break on uncaught exception using JavaScript
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnExceptionFromJS(false, true);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // Break on exception and uncaught exception using JavaScript
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnExceptionFromJS(true, true);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ // Break on exception using JavaScript
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+ ChangeBreakOnExceptionFromJS(true, false);
+ caught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ notCaught->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+ v8::V8::RemoveMessageListeners(MessageCallbackCount);
+}
+
+
+// Test break on exception from compiler errors. When compiling using
+// v8::Script::Compile there is no JavaScript stack whereas when compiling using
+// eval there are JavaScript frames.
+TEST(BreakOnCompileException) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ v8::internal::Top::TraceException(false);
+
+ // Create a function for checking the function when hitting a break point.
+ frame_count = CompileFunction(&env, frame_count_source, "frame_count");
+
+ v8::V8::AddMessageListener(MessageCallbackCount);
+ v8::Debug::SetDebugEventListener(DebugEventCounter);
+
+ DebugEventCounterClear();
+ MessageCallbackCountClear();
+
+ // Check initial state.
+ CHECK_EQ(0, exception_hit_count);
+ CHECK_EQ(0, uncaught_exception_hit_count);
+ CHECK_EQ(0, message_callback_count);
+ CHECK_EQ(-1, last_js_stack_height);
+
+ // Throws SyntaxError: Unexpected end of input
+ v8::Script::Compile(v8::String::New("+++"));
+ CHECK_EQ(1, exception_hit_count);
+ CHECK_EQ(1, uncaught_exception_hit_count);
+ CHECK_EQ(1, message_callback_count);
+ CHECK_EQ(0, last_js_stack_height); // No JavaScript stack.
+
+ // Throws SyntaxError: Unexpected identifier
+ v8::Script::Compile(v8::String::New("x x"));
+ CHECK_EQ(2, exception_hit_count);
+ CHECK_EQ(2, uncaught_exception_hit_count);
+ CHECK_EQ(2, message_callback_count);
+ CHECK_EQ(0, last_js_stack_height); // No JavaScript stack.
+
+ // Throws SyntaxError: Unexpected end of input
+ v8::Script::Compile(v8::String::New("eval('+++')"))->Run();
+ CHECK_EQ(3, exception_hit_count);
+ CHECK_EQ(3, uncaught_exception_hit_count);
+ CHECK_EQ(3, message_callback_count);
+ CHECK_EQ(1, last_js_stack_height);
+
+ // Throws SyntaxError: Unexpected identifier
+ v8::Script::Compile(v8::String::New("eval('x x')"))->Run();
+ CHECK_EQ(4, exception_hit_count);
+ CHECK_EQ(4, uncaught_exception_hit_count);
+ CHECK_EQ(4, message_callback_count);
+ CHECK_EQ(1, last_js_stack_height);
+}
+
+
+TEST(StepWithException) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Create a function for checking the function when hitting a break point.
+ frame_function_name = CompileFunction(&env,
+ frame_function_name_source,
+ "frame_function_name");
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStepSequence);
+
+ // Create functions for testing stepping.
+ const char* src = "function a() { n(); }; "
+ "function b() { c(); }; "
+ "function c() { n(); }; "
+ "function d() { x = 1; try { e(); } catch(x) { x = 2; } }; "
+ "function e() { n(); }; "
+ "function f() { x = 1; try { g(); } catch(x) { x = 2; } }; "
+ "function g() { h(); }; "
+ "function h() { x = 1; throw 1; }; ";
+
+ // Step through invocation of a.
+ v8::Local<v8::Function> a = CompileFunction(&env, src, "a");
+ SetBreakPoint(a, 0);
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "aa";
+ a->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of b + c.
+ v8::Local<v8::Function> b = CompileFunction(&env, src, "b");
+ SetBreakPoint(b, 0);
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "bcc";
+ b->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of d + e.
+ v8::Local<v8::Function> d = CompileFunction(&env, src, "d");
+ SetBreakPoint(d, 0);
+ ChangeBreakOnException(false, true);
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "dded";
+ d->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of d + e now with break on caught exceptions.
+ ChangeBreakOnException(true, true);
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "ddeed";
+ d->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of f + g + h.
+ v8::Local<v8::Function> f = CompileFunction(&env, src, "f");
+ SetBreakPoint(f, 0);
+ ChangeBreakOnException(false, true);
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "ffghf";
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Step through invocation of f + g + h now with break on caught exceptions.
+ ChangeBreakOnException(true, true);
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ expected_step_sequence = "ffghhf";
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugBreak) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // This test should be run with option --verify-heap. This is an ASSERT and
+ // not a CHECK as --verify-heap is only available in debug mode.
+ ASSERT(v8::internal::FLAG_verify_heap);
+
+ // Register a debug event listener which sets the break flag and counts.
+ v8::Debug::SetDebugEventListener(DebugEventBreak);
+
+ // Create a function for testing stepping.
+ const char* src = "function f0() {}"
+ "function f1(x1) {}"
+ "function f2(x1,x2) {}"
+ "function f3(x1,x2,x3) {}";
+ v8::Local<v8::Function> f0 = CompileFunction(&env, src, "f0");
+ v8::Local<v8::Function> f1 = CompileFunction(&env, src, "f1");
+ v8::Local<v8::Function> f2 = CompileFunction(&env, src, "f2");
+ v8::Local<v8::Function> f3 = CompileFunction(&env, src, "f3");
+
+ // Call the function to make sure it is compiled.
+ v8::Handle<v8::Value> argv[] = { v8::Number::New(1),
+ v8::Number::New(1),
+ v8::Number::New(1),
+ v8::Number::New(1) };
+
+ // Call all functions to make sure that they are compiled.
+ f0->Call(env->Global(), 0, NULL);
+ f1->Call(env->Global(), 0, NULL);
+ f2->Call(env->Global(), 0, NULL);
+ f3->Call(env->Global(), 0, NULL);
+
+ // Set the debug break flag.
+ v8::Debug::DebugBreak();
+
+ // Call all functions with different argument count.
+ break_point_hit_count = 0;
+ for (unsigned int i = 0; i < ARRAY_SIZE(argv); i++) {
+ f0->Call(env->Global(), i, argv);
+ f1->Call(env->Global(), i, argv);
+ f2->Call(env->Global(), i, argv);
+ f3->Call(env->Global(), i, argv);
+ }
+
+ // One break for each function called.
+ CHECK_EQ(4 * ARRAY_SIZE(argv), break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+// Test to ensure that JavaScript code keeps running while the debug break
+// through the stack limit flag is set but breaks are disabled.
+TEST(DisableBreak) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which sets the break flag and counts.
+ v8::Debug::SetDebugEventListener(DebugEventCounter);
+
+ // Create a function for testing stepping.
+ const char* src = "function f() {g()};function g(){i=0; while(i<10){i++}}";
+ v8::Local<v8::Function> f = CompileFunction(&env, src, "f");
+
+ // Set the debug break flag.
+ v8::Debug::DebugBreak();
+
+ // Call all functions with different argument count.
+ break_point_hit_count = 0;
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+
+ {
+ v8::Debug::DebugBreak();
+ v8::internal::DisableBreak disable_break(true);
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ }
+
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+static v8::Handle<v8::Array> NamedEnum(const v8::AccessorInfo&) {
+ v8::Handle<v8::Array> result = v8::Array::New(3);
+ result->Set(v8::Integer::New(0), v8::String::New("a"));
+ result->Set(v8::Integer::New(1), v8::String::New("b"));
+ result->Set(v8::Integer::New(2), v8::String::New("c"));
+ return result;
+}
+
+
+static v8::Handle<v8::Array> IndexedEnum(const v8::AccessorInfo&) {
+ v8::Handle<v8::Array> result = v8::Array::New(2);
+ result->Set(v8::Integer::New(0), v8::Number::New(1));
+ result->Set(v8::Integer::New(1), v8::Number::New(10));
+ return result;
+}
+
+
+static v8::Handle<v8::Value> NamedGetter(v8::Local<v8::String> name,
+ const v8::AccessorInfo& info) {
+ v8::String::AsciiValue n(name);
+ if (strcmp(*n, "a") == 0) {
+ return v8::String::New("AA");
+ } else if (strcmp(*n, "b") == 0) {
+ return v8::String::New("BB");
+ } else if (strcmp(*n, "c") == 0) {
+ return v8::String::New("CC");
+ } else {
+ return v8::Undefined();
+ }
+
+ return name;
+}
+
+
+static v8::Handle<v8::Value> IndexedGetter(uint32_t index,
+ const v8::AccessorInfo& info) {
+ return v8::Number::New(index + 1);
+}
+
+
+TEST(InterceptorPropertyMirror) {
+ // Create a V8 environment with debug access.
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ // Create object with named interceptor.
+ v8::Handle<v8::ObjectTemplate> named = v8::ObjectTemplate::New();
+ named->SetNamedPropertyHandler(NamedGetter, NULL, NULL, NULL, NamedEnum);
+ env->Global()->Set(v8::String::New("intercepted_named"),
+ named->NewInstance());
+
+ // Create object with indexed interceptor.
+ v8::Handle<v8::ObjectTemplate> indexed = v8::ObjectTemplate::New();
+ indexed->SetIndexedPropertyHandler(IndexedGetter,
+ NULL,
+ NULL,
+ NULL,
+ IndexedEnum);
+ env->Global()->Set(v8::String::New("intercepted_indexed"),
+ indexed->NewInstance());
+
+ // Create object with both named and indexed interceptor.
+ v8::Handle<v8::ObjectTemplate> both = v8::ObjectTemplate::New();
+ both->SetNamedPropertyHandler(NamedGetter, NULL, NULL, NULL, NamedEnum);
+ both->SetIndexedPropertyHandler(IndexedGetter, NULL, NULL, NULL, IndexedEnum);
+ env->Global()->Set(v8::String::New("intercepted_both"), both->NewInstance());
+
+ // Get mirrors for the three objects with interceptor.
+ CompileRun(
+ "named_mirror = debug.MakeMirror(intercepted_named);"
+ "indexed_mirror = debug.MakeMirror(intercepted_indexed);"
+ "both_mirror = debug.MakeMirror(intercepted_both)");
+ CHECK(CompileRun(
+ "named_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun(
+ "indexed_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun(
+ "both_mirror instanceof debug.ObjectMirror")->BooleanValue());
+
+ // Get the property names from the interceptors
+ CompileRun(
+ "named_names = named_mirror.propertyNames();"
+ "indexed_names = indexed_mirror.propertyNames();"
+ "both_names = both_mirror.propertyNames()");
+ CHECK_EQ(3, CompileRun("named_names.length")->Int32Value());
+ CHECK_EQ(2, CompileRun("indexed_names.length")->Int32Value());
+ CHECK_EQ(5, CompileRun("both_names.length")->Int32Value());
+
+ // Check the expected number of properties.
+ const char* source;
+ source = "named_mirror.properties().length";
+ CHECK_EQ(3, CompileRun(source)->Int32Value());
+
+ source = "indexed_mirror.properties().length";
+ CHECK_EQ(2, CompileRun(source)->Int32Value());
+
+ source = "both_mirror.properties().length";
+ CHECK_EQ(5, CompileRun(source)->Int32Value());
+
+ // 1 is PropertyKind.Named;
+ source = "both_mirror.properties(1).length";
+ CHECK_EQ(3, CompileRun(source)->Int32Value());
+
+ // 2 is PropertyKind.Indexed;
+ source = "both_mirror.properties(2).length";
+ CHECK_EQ(2, CompileRun(source)->Int32Value());
+
+ // 3 is PropertyKind.Named | PropertyKind.Indexed;
+ source = "both_mirror.properties(3).length";
+ CHECK_EQ(5, CompileRun(source)->Int32Value());
+
+ // Get the interceptor properties for the object with only named interceptor.
+ CompileRun("named_values = named_mirror.properties()");
+
+ // Check that the properties are interceptor properties.
+ for (int i = 0; i < 3; i++) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "named_values[%d] instanceof debug.PropertyMirror", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+
+ // 4 is PropertyType.Interceptor
+ OS::SNPrintF(buffer, "named_values[%d].propertyType()", i);
+ CHECK_EQ(4, CompileRun(buffer.start())->Int32Value());
+
+ OS::SNPrintF(buffer, "named_values[%d].isNative()", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+ }
+
+ // Get the interceptor properties for the object with only indexed
+ // interceptor.
+ CompileRun("indexed_values = indexed_mirror.properties()");
+
+ // Check that the properties are interceptor properties.
+ for (int i = 0; i < 2; i++) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "indexed_values[%d] instanceof debug.PropertyMirror", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+ }
+
+ // Get the interceptor properties for the object with both types of
+ // interceptors.
+ CompileRun("both_values = both_mirror.properties()");
+
+ // Check that the properties are interceptor properties.
+ for (int i = 0; i < 5; i++) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer, "both_values[%d] instanceof debug.PropertyMirror", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+ }
+
+ // Check the property names.
+ source = "both_values[0].name() == 'a'";
+ CHECK(CompileRun(source)->BooleanValue());
+
+ source = "both_values[1].name() == 'b'";
+ CHECK(CompileRun(source)->BooleanValue());
+
+ source = "both_values[2].name() == 'c'";
+ CHECK(CompileRun(source)->BooleanValue());
+
+ source = "both_values[3].name() == 1";
+ CHECK(CompileRun(source)->BooleanValue());
+
+ source = "both_values[4].name() == 10";
+ CHECK(CompileRun(source)->BooleanValue());
+}
+
+
+TEST(HiddenPrototypePropertyMirror) {
+ // Create a V8 environment with debug access.
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Handle<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
+ t0->InstanceTemplate()->Set(v8::String::New("x"), v8::Number::New(0));
+ v8::Handle<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
+ t1->SetHiddenPrototype(true);
+ t1->InstanceTemplate()->Set(v8::String::New("y"), v8::Number::New(1));
+ v8::Handle<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
+ t2->SetHiddenPrototype(true);
+ t2->InstanceTemplate()->Set(v8::String::New("z"), v8::Number::New(2));
+ v8::Handle<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
+ t3->InstanceTemplate()->Set(v8::String::New("u"), v8::Number::New(3));
+
+ // Create object and set them on the global object.
+ v8::Handle<v8::Object> o0 = t0->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o0"), o0);
+ v8::Handle<v8::Object> o1 = t1->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o1"), o1);
+ v8::Handle<v8::Object> o2 = t2->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o2"), o2);
+ v8::Handle<v8::Object> o3 = t3->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o3"), o3);
+
+ // Get mirrors for the four objects.
+ CompileRun(
+ "o0_mirror = debug.MakeMirror(o0);"
+ "o1_mirror = debug.MakeMirror(o1);"
+ "o2_mirror = debug.MakeMirror(o2);"
+ "o3_mirror = debug.MakeMirror(o3)");
+ CHECK(CompileRun("o0_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun("o1_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun("o2_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun("o3_mirror instanceof debug.ObjectMirror")->BooleanValue());
+
+ // Check that each object has one property.
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o1_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o2_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o3_mirror.propertyNames().length")->Int32Value());
+
+ // Set o1 as prototype for o0. o1 has the hidden prototype flag so all
+ // properties on o1 should be seen on o0.
+ o0->Set(v8::String::New("__proto__"), o1);
+ CHECK_EQ(2, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(0, CompileRun(
+ "o0_mirror.property('x').value().value()")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.property('y').value().value()")->Int32Value());
+
+ // Set o2 as prototype for o0 (it will end up after o1 as o1 has the hidden
+ // prototype flag. o2 also has the hidden prototype flag so all properties
+ // on o2 should be seen on o0 as well as properties on o1.
+ o0->Set(v8::String::New("__proto__"), o2);
+ CHECK_EQ(3, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(0, CompileRun(
+ "o0_mirror.property('x').value().value()")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.property('y').value().value()")->Int32Value());
+ CHECK_EQ(2, CompileRun(
+ "o0_mirror.property('z').value().value()")->Int32Value());
+
+ // Set o3 as prototype for o0 (it will end up after o1 and o2 as both o1 and
+ // o2 has the hidden prototype flag. o3 does not have the hidden prototype
+ // flag so properties on o3 should not be seen on o0 whereas the properties
+ // from o1 and o2 should still be seen on o0.
+ // Final prototype chain: o0 -> o1 -> o2 -> o3
+ // Hidden prototypes: ^^ ^^
+ o0->Set(v8::String::New("__proto__"), o3);
+ CHECK_EQ(3, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o3_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(0, CompileRun(
+ "o0_mirror.property('x').value().value()")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.property('y').value().value()")->Int32Value());
+ CHECK_EQ(2, CompileRun(
+ "o0_mirror.property('z').value().value()")->Int32Value());
+ CHECK(CompileRun("o0_mirror.property('u').isUndefined()")->BooleanValue());
+
+ // The prototype (__proto__) for o0 should be o3 as o1 and o2 are hidden.
+ CHECK(CompileRun("o0_mirror.protoObject() == o3_mirror")->BooleanValue());
+}
+
+
+// Multithreaded tests of JSON debugger protocol
+
+// Support classes
+
+// Copies a C string to a 16-bit string. Does not check for buffer overflow.
+// Does not use the V8 engine to convert strings, so it can be used
+// in any thread. Returns the length of the string.
+int AsciiToUtf16(const char* input_buffer, uint16_t* output_buffer) {
+ int i;
+ for (i = 0; input_buffer[i] != '\0'; ++i) {
+ // ASCII does not use chars > 127, but be careful anyway.
+ output_buffer[i] = static_cast<unsigned char>(input_buffer[i]);
+ }
+ output_buffer[i] = 0;
+ return i;
+}
+
+// Copies a 16-bit string to a C string by dropping the high byte of
+// each character. Does not check for buffer overflow.
+// Can be used in any thread. Requires string length as an input.
+int Utf16ToAscii(const uint16_t* input_buffer, int length,
+ char* output_buffer) {
+ for (int i = 0; i < length; ++i) {
+ output_buffer[i] = static_cast<char>(input_buffer[i]);
+ }
+ output_buffer[length] = '\0';
+ return length;
+}
+
+// Provides synchronization between k threads, where k is an input to the
+// constructor. The Wait() call blocks a thread until it is called for the
+// k'th time, then all calls return. Each ThreadBarrier object can only
+// be used once.
+class ThreadBarrier {
+ public:
+ explicit ThreadBarrier(int num_threads);
+ ~ThreadBarrier();
+ void Wait();
+ private:
+ int num_threads_;
+ int num_blocked_;
+ v8::internal::Mutex* lock_;
+ v8::internal::Semaphore* sem_;
+ bool invalid_;
+};
+
+ThreadBarrier::ThreadBarrier(int num_threads)
+ : num_threads_(num_threads), num_blocked_(0) {
+ lock_ = OS::CreateMutex();
+ sem_ = OS::CreateSemaphore(0);
+ invalid_ = false; // A barrier may only be used once. Then it is invalid.
+}
+
+// Do not call, due to race condition with Wait().
+// Could be resolved with Pthread condition variables.
+ThreadBarrier::~ThreadBarrier() {
+ lock_->Lock();
+ delete lock_;
+ delete sem_;
+}
+
+void ThreadBarrier::Wait() {
+ lock_->Lock();
+ ASSERT(!invalid_);
+ if (num_blocked_ == num_threads_ - 1) {
+ // Signal and unblock all waiting threads.
+ for (int i = 0; i < num_threads_ - 1; ++i) {
+ sem_->Signal();
+ }
+ invalid_ = true;
+ printf("BARRIER\n\n");
+ fflush(stdout);
+ lock_->Unlock();
+ } else { // Wait for the semaphore.
+ ++num_blocked_;
+ lock_->Unlock(); // Potential race condition with destructor because
+ sem_->Wait(); // these two lines are not atomic.
+ }
+}
+
+// A set containing enough barriers and semaphores for any of the tests.
+class Barriers {
+ public:
+ Barriers();
+ void Initialize();
+ ThreadBarrier barrier_1;
+ ThreadBarrier barrier_2;
+ ThreadBarrier barrier_3;
+ ThreadBarrier barrier_4;
+ ThreadBarrier barrier_5;
+ v8::internal::Semaphore* semaphore_1;
+ v8::internal::Semaphore* semaphore_2;
+};
+
+Barriers::Barriers() : barrier_1(2), barrier_2(2),
+ barrier_3(2), barrier_4(2), barrier_5(2) {}
+
+void Barriers::Initialize() {
+ semaphore_1 = OS::CreateSemaphore(0);
+ semaphore_2 = OS::CreateSemaphore(0);
+}
+
+
+// We match parts of the message to decide if it is a break message.
+bool IsBreakEventMessage(char *message) {
+ const char* type_event = "\"type\":\"event\"";
+ const char* event_break = "\"event\":\"break\"";
+ // Does the message contain both type:event and event:break?
+ return strstr(message, type_event) != NULL &&
+ strstr(message, event_break) != NULL;
+}
+
+
+/* Test MessageQueues */
+/* Tests the message queues that hold debugger commands and
+ * response messages to the debugger. Fills queues and makes
+ * them grow.
+ */
+Barriers message_queue_barriers;
+
+// This is the debugger thread, that executes no v8 calls except
+// placing JSON debugger commands in the queue.
+class MessageQueueDebuggerThread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+static void MessageHandler(const uint16_t* message, int length, void *data) {
+ static char print_buffer[1000];
+ Utf16ToAscii(message, length, print_buffer);
+ if (IsBreakEventMessage(print_buffer)) {
+ // Lets test script wait until break occurs to send commands.
+ // Signals when a break is reported.
+ message_queue_barriers.semaphore_2->Signal();
+ }
+ // Allow message handler to block on a semaphore, to test queueing of
+ // messages while blocked.
+ message_queue_barriers.semaphore_1->Wait();
+ printf("%s\n", print_buffer);
+ fflush(stdout);
+}
+
+
+void MessageQueueDebuggerThread::Run() {
+ const int kBufferSize = 1000;
+ uint16_t buffer_1[kBufferSize];
+ uint16_t buffer_2[kBufferSize];
+ const char* command_1 =
+ "{\"seq\":117,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"1+2\"}}";
+ const char* command_2 =
+ "{\"seq\":118,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"1+a\"}}";
+ const char* command_3 =
+ "{\"seq\":119,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"c.d * b\"}}";
+ const char* command_continue =
+ "{\"seq\":106,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+ const char* command_single_step =
+ "{\"seq\":107,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\","
+ "\"arguments\":{\"stepaction\":\"next\"}}";
+
+ /* Interleaved sequence of actions by the two threads:*/
+ // Main thread compiles and runs source_1
+ message_queue_barriers.barrier_1.Wait();
+ // Post 6 commands, filling the command queue and making it expand.
+ // These calls return immediately, but the commands stay on the queue
+ // until the execution of source_2.
+ // Note: AsciiToUtf16 executes before SendCommand, so command is copied
+ // to buffer before buffer is sent to SendCommand.
+ v8::Debug::SendCommand(buffer_1, AsciiToUtf16(command_1, buffer_1));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_2, buffer_2));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
+ message_queue_barriers.barrier_2.Wait();
+ // Main thread compiles and runs source_2.
+ // Queued commands are executed at the start of compilation of source_2.
+ message_queue_barriers.barrier_3.Wait();
+ // Free the message handler to process all the messages from the queue.
+ for (int i = 0; i < 20 ; ++i) {
+ message_queue_barriers.semaphore_1->Signal();
+ }
+ // Main thread compiles and runs source_3.
+ // source_3 includes a debugger statement, which causes a break event.
+ // Wait on break event from hitting "debugger" statement
+ message_queue_barriers.semaphore_2->Wait();
+ // These should execute after the "debugger" statement in source_2
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_single_step, buffer_2));
+ // Wait on break event after a single step executes.
+ message_queue_barriers.semaphore_2->Wait();
+ v8::Debug::SendCommand(buffer_1, AsciiToUtf16(command_2, buffer_1));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_continue, buffer_2));
+ // Main thread continues running source_3 to end, waits for this thread.
+}
+
+MessageQueueDebuggerThread message_queue_debugger_thread;
+
+// This thread runs the v8 engine.
+TEST(MessageQueues) {
+ // Create a V8 environment
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ message_queue_barriers.Initialize();
+ v8::Debug::SetMessageHandler(MessageHandler);
+ message_queue_debugger_thread.Start();
+
+ const char* source_1 = "a = 3; b = 4; c = new Object(); c.d = 5;";
+ const char* source_2 = "e = 17;";
+ const char* source_3 = "a = 4; debugger; a = 5; a = 6; a = 7;";
+
+ // See MessageQueueDebuggerThread::Run for interleaved sequence of
+ // API calls and events in the two threads.
+ CompileRun(source_1);
+ message_queue_barriers.barrier_1.Wait();
+ message_queue_barriers.barrier_2.Wait();
+ CompileRun(source_2);
+ message_queue_barriers.barrier_3.Wait();
+ CompileRun(source_3);
+ message_queue_debugger_thread.Join();
+ fflush(stdout);
+}
+
+/* Test ThreadedDebugging */
+/* This test interrupts a running infinite loop that is
+ * occupying the v8 thread by a break command from the
+ * debugger thread. It then changes the value of a
+ * global object, to make the loop terminate.
+ */
+
+Barriers threaded_debugging_barriers;
+
+class V8Thread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+class DebuggerThread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+
+static void ThreadedMessageHandler(const uint16_t* message, int length,
+ void *data) {
+ static char print_buffer[1000];
+ Utf16ToAscii(message, length, print_buffer);
+ if (IsBreakEventMessage(print_buffer)) {
+ threaded_debugging_barriers.barrier_2.Wait();
+ }
+ printf("%s\n", print_buffer);
+ fflush(stdout);
+}
+
+
+void V8Thread::Run() {
+ const char* source_1 =
+ "flag = true;\n"
+ "function bar( new_value ) {\n"
+ " flag = new_value;\n"
+ " return \"Return from bar(\" + new_value + \")\";\n"
+ "}\n"
+ "\n"
+ "function foo() {\n"
+ " var x = 1;\n"
+ " while ( flag == true ) {\n"
+ " x = x + 1;\n"
+ " }\n"
+ "}\n"
+ "\n";
+ const char* source_2 = "foo();\n";
+
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetMessageHandler(&ThreadedMessageHandler);
+
+ CompileRun(source_1);
+ threaded_debugging_barriers.barrier_1.Wait();
+ CompileRun(source_2);
+}
+
+void DebuggerThread::Run() {
+ const int kBufSize = 1000;
+ uint16_t buffer[kBufSize];
+
+ const char* command_1 = "{\"seq\":102,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"bar(false)\"}}";
+ const char* command_2 = "{\"seq\":103,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+
+ threaded_debugging_barriers.barrier_1.Wait();
+ v8::Debug::DebugBreak();
+ threaded_debugging_barriers.barrier_2.Wait();
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
+}
+
+DebuggerThread debugger_thread;
+V8Thread v8_thread;
+
+TEST(ThreadedDebugging) {
+ // Create a V8 environment
+ threaded_debugging_barriers.Initialize();
+
+ v8_thread.Start();
+ debugger_thread.Start();
+
+ v8_thread.Join();
+ debugger_thread.Join();
+}
+
+/* Test RecursiveBreakpoints */
+/* In this test, the debugger evaluates a function with a breakpoint, after
+ * hitting a breakpoint in another function. We do this with both values
+ * of the flag enabling recursive breakpoints, and verify that the second
+ * breakpoint is hit when enabled, and missed when disabled.
+ */
+
+class BreakpointsV8Thread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+class BreakpointsDebuggerThread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+
+Barriers* breakpoints_barriers;
+
+static void BreakpointsMessageHandler(const uint16_t* message,
+ int length,
+ void *data) {
+ static char print_buffer[1000];
+ Utf16ToAscii(message, length, print_buffer);
+ printf("%s\n", print_buffer);
+ fflush(stdout);
+
+ // Is break_template a prefix of the message?
+ if (IsBreakEventMessage(print_buffer)) {
+ breakpoints_barriers->semaphore_1->Signal();
+ }
+}
+
+
+void BreakpointsV8Thread::Run() {
+ const char* source_1 = "var y_global = 3;\n"
+ "function cat( new_value ) {\n"
+ " var x = new_value;\n"
+ " y_global = 4;\n"
+ " x = 3 * x + 1;\n"
+ " y_global = 5;\n"
+ " return x;\n"
+ "}\n"
+ "\n"
+ "function dog() {\n"
+ " var x = 1;\n"
+ " x = y_global;"
+ " var z = 3;"
+ " x += 100;\n"
+ " return x;\n"
+ "}\n"
+ "\n";
+ const char* source_2 = "cat(17);\n"
+ "cat(19);\n";
+
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetMessageHandler(&BreakpointsMessageHandler);
+
+ CompileRun(source_1);
+ breakpoints_barriers->barrier_1.Wait();
+ breakpoints_barriers->barrier_2.Wait();
+ CompileRun(source_2);
+}
+
+
+void BreakpointsDebuggerThread::Run() {
+ const int kBufSize = 1000;
+ uint16_t buffer[kBufSize];
+
+ const char* command_1 = "{\"seq\":101,"
+ "\"type\":\"request\","
+ "\"command\":\"setbreakpoint\","
+ "\"arguments\":{\"type\":\"function\",\"target\":\"cat\",\"line\":3}}";
+ const char* command_2 = "{\"seq\":102,"
+ "\"type\":\"request\","
+ "\"command\":\"setbreakpoint\","
+ "\"arguments\":{\"type\":\"function\",\"target\":\"dog\",\"line\":3}}";
+ const char* command_3 = "{\"seq\":104,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"dog()\",\"disable_break\":false}}";
+ const char* command_4 = "{\"seq\":105,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"x\",\"disable_break\":true}}";
+ const char* command_5 = "{\"seq\":106,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+ const char* command_6 = "{\"seq\":107,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+ const char* command_7 = "{\"seq\":108,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"dog()\",\"disable_break\":true}}";
+ const char* command_8 = "{\"seq\":109,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+
+
+ // v8 thread initializes, runs source_1
+ breakpoints_barriers->barrier_1.Wait();
+ // 1:Set breakpoint in cat().
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
+ // 2:Set breakpoint in dog()
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
+ breakpoints_barriers->barrier_2.Wait();
+ // v8 thread starts compiling source_2.
+ // Automatic break happens, to run queued commands
+ // breakpoints_barriers->semaphore_1->Wait();
+ // Commands 1 through 3 run, thread continues.
+ // v8 thread runs source_2 to breakpoint in cat().
+ // message callback receives break event.
+ breakpoints_barriers->semaphore_1->Wait();
+ // 4:Evaluate dog() (which has a breakpoint).
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_3, buffer));
+ // v8 thread hits breakpoint in dog()
+ breakpoints_barriers->semaphore_1->Wait(); // wait for break event
+ // 5:Evaluate x
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_4, buffer));
+ // 6:Continue evaluation of dog()
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_5, buffer));
+ // dog() finishes.
+ // 7:Continue evaluation of source_2, finish cat(17), hit breakpoint
+ // in cat(19).
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_6, buffer));
+ // message callback gets break event
+ breakpoints_barriers->semaphore_1->Wait(); // wait for break event
+ // 8: Evaluate dog() with breaks disabled
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_7, buffer));
+ // 9: Continue evaluation of source2, reach end.
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_8, buffer));
+}
+
+BreakpointsDebuggerThread breakpoints_debugger_thread;
+BreakpointsV8Thread breakpoints_v8_thread;
+
+TEST(RecursiveBreakpoints) {
+ i::FLAG_debugger_auto_break = true;
+
+ // Create a V8 environment
+ Barriers stack_allocated_breakpoints_barriers;
+ stack_allocated_breakpoints_barriers.Initialize();
+ breakpoints_barriers = &stack_allocated_breakpoints_barriers;
+
+ breakpoints_v8_thread.Start();
+ breakpoints_debugger_thread.Start();
+
+ breakpoints_v8_thread.Join();
+ breakpoints_debugger_thread.Join();
+}
+
+
+static void DummyDebugEventListener(v8::DebugEvent event,
+ v8::Handle<v8::Object> exec_state,
+ v8::Handle<v8::Object> event_data,
+ v8::Handle<v8::Value> data) {
+}
+
+
+TEST(SetDebugEventListenerOnUninitializedVM) {
+ v8::Debug::SetDebugEventListener(DummyDebugEventListener);
+}
+
+
+static void DummyMessageHandler(const uint16_t* message,
+ int length, void *data) {
+}
+
+
+TEST(SetMessageHandlerOnUninitializedVM) {
+ v8::Debug::SetMessageHandler(DummyMessageHandler);
+}
+
+
+TEST(DebugBreakOnUninitializedVM) {
+ v8::Debug::DebugBreak();
+}
+
+
+TEST(SendCommandToUninitializedVM) {
+ const char* dummy_command = "{}";
+ uint16_t dummy_buffer[80];
+ int dummy_length = AsciiToUtf16(dummy_command, dummy_buffer);
+ v8::Debug::SendCommand(dummy_buffer, dummy_length);
+}
+
+
+// Source for a JavaScript function which returns the source line for the top
+// frame.
+static const char* frame_source_line_source =
+ "function frame_source_line(exec_state) {"
+ " return exec_state.frame(0).sourceLine();"
+ "}";
+v8::Handle<v8::Function> frame_source_line;
+
+
+// Source for a JavaScript function which returns the data parameter of a
+// function called in the context of the debugger. If no data parameter is
+// passed it throws an exception.
+static const char* debugger_call_with_data_source =
+ "function debugger_call_with_data(exec_state, data) {"
+ " if (data) return data;"
+ " throw 'No data!'"
+ "}";
+v8::Handle<v8::Function> debugger_call_with_data;
+
+
+// Source for a JavaScript function which returns the data parameter of a
+// function called in the context of the debugger. If no data parameter is
+// passed it throws an exception.
+static const char* debugger_call_with_closure_source =
+ "var x = 3;"
+ "function (exec_state) {"
+ " if (exec_state.y) return x - 1;"
+ " exec_state.y = x;"
+ " return exec_state.y"
+ "}";
+v8::Handle<v8::Function> debugger_call_with_closure;
+
+// Function to retrieve the number of JavaScript frames by calling a JavaScript
+// in the debugger.
+static v8::Handle<v8::Value> CheckFrameCount(const v8::Arguments& args) {
+ CHECK(v8::Debug::Call(frame_count)->IsNumber());
+ CHECK_EQ(args[0]->Int32Value(),
+ v8::Debug::Call(frame_count)->Int32Value());
+ return v8::Undefined();
+}
+
+
+// Function to retrieve the source line of the top JavaScript frame by calling a
+// JavaScript function in the debugger.
+static v8::Handle<v8::Value> CheckSourceLine(const v8::Arguments& args) {
+ CHECK(v8::Debug::Call(frame_source_line)->IsNumber());
+ CHECK_EQ(args[0]->Int32Value(),
+ v8::Debug::Call(frame_source_line)->Int32Value());
+ return v8::Undefined();
+}
+
+
+// Function to test passing an additional parameter to a JavaScript function
+// called in the debugger. It also tests that functions called in the debugger
+// can throw exceptions.
+static v8::Handle<v8::Value> CheckDataParameter(const v8::Arguments& args) {
+ v8::Handle<v8::String> data = v8::String::New("Test");
+ CHECK(v8::Debug::Call(debugger_call_with_data, data)->IsString());
+
+ CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
+ CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
+
+ v8::TryCatch catcher;
+ v8::Debug::Call(debugger_call_with_data);
+ CHECK(catcher.HasCaught());
+ CHECK(catcher.Exception()->IsString());
+
+ return v8::Undefined();
+}
+
+
+// Function to test using a JavaScript with closure in the debugger.
+static v8::Handle<v8::Value> CheckClosure(const v8::Arguments& args) {
+ CHECK(v8::Debug::Call(debugger_call_with_closure)->IsNumber());
+ CHECK_EQ(3, v8::Debug::Call(debugger_call_with_closure)->Int32Value());
+ return v8::Undefined();
+}
+
+
+// Test functions called through the debugger.
+TEST(CallFunctionInDebugger) {
+ // Create and enter a context with the functions CheckFrameCount,
+ // CheckSourceLine and CheckDataParameter installed.
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+ global_template->Set(v8::String::New("CheckFrameCount"),
+ v8::FunctionTemplate::New(CheckFrameCount));
+ global_template->Set(v8::String::New("CheckSourceLine"),
+ v8::FunctionTemplate::New(CheckSourceLine));
+ global_template->Set(v8::String::New("CheckDataParameter"),
+ v8::FunctionTemplate::New(CheckDataParameter));
+ global_template->Set(v8::String::New("CheckClosure"),
+ v8::FunctionTemplate::New(CheckClosure));
+ v8::Handle<v8::Context> context = v8::Context::New(NULL, global_template);
+ v8::Context::Scope context_scope(context);
+
+ // Compile a function for checking the number of JavaScript frames.
+ v8::Script::Compile(v8::String::New(frame_count_source))->Run();
+ frame_count = v8::Local<v8::Function>::Cast(
+ context->Global()->Get(v8::String::New("frame_count")));
+
+ // Compile a function for returning the source line for the top frame.
+ v8::Script::Compile(v8::String::New(frame_source_line_source))->Run();
+ frame_source_line = v8::Local<v8::Function>::Cast(
+ context->Global()->Get(v8::String::New("frame_source_line")));
+
+ // Compile a function returning the data parameter.
+ v8::Script::Compile(v8::String::New(debugger_call_with_data_source))->Run();
+ debugger_call_with_data = v8::Local<v8::Function>::Cast(
+ context->Global()->Get(v8::String::New("debugger_call_with_data")));
+
+ // Compile a function capturing closure.
+ debugger_call_with_closure = v8::Local<v8::Function>::Cast(
+ v8::Script::Compile(
+ v8::String::New(debugger_call_with_closure_source))->Run());
+
+ // Calling a function through the debugger returns undefined if there are no
+ // JavaScript frames.
+ CHECK(v8::Debug::Call(frame_count)->IsUndefined());
+ CHECK(v8::Debug::Call(frame_source_line)->IsUndefined());
+ CHECK(v8::Debug::Call(debugger_call_with_data)->IsUndefined());
+
+ // Test that the number of frames can be retrieved.
+ v8::Script::Compile(v8::String::New("CheckFrameCount(1)"))->Run();
+ v8::Script::Compile(v8::String::New("function f() {"
+ " CheckFrameCount(2);"
+ "}; f()"))->Run();
+
+ // Test that the source line can be retrieved.
+ v8::Script::Compile(v8::String::New("CheckSourceLine(0)"))->Run();
+ v8::Script::Compile(v8::String::New("function f() {\n"
+ " CheckSourceLine(1)\n"
+ " CheckSourceLine(2)\n"
+ " CheckSourceLine(3)\n"
+ "}; f()"))->Run();
+
+ // Test that a parameter can be passed to a function called in the debugger.
+ v8::Script::Compile(v8::String::New("CheckDataParameter()"))->Run();
+
+ // Test that a function with closure can be run in the debugger.
+ v8::Script::Compile(v8::String::New("CheckClosure()"))->Run();
+
+
+ // Test that the source line is correct when there is a line offset.
+ v8::ScriptOrigin origin(v8::String::New("test"),
+ v8::Integer::New(7));
+ v8::Script::Compile(v8::String::New("CheckSourceLine(7)"), &origin)->Run();
+ v8::Script::Compile(v8::String::New("function f() {\n"
+ " CheckSourceLine(8)\n"
+ " CheckSourceLine(9)\n"
+ " CheckSourceLine(10)\n"
+ "}; f()"), &origin)->Run();
+}
+
+
+// Test that clearing the debug event listener actually clears all break points
+// and related information.
+TEST(DebuggerUnload) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Check debugger is unloaded before it is used.
+ CheckDebuggerUnloaded();
+
+ // Add debug event listener.
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ // Create a couple of functions for the test.
+ v8::Local<v8::Function> foo =
+ CompileFunction(&env, "function foo(){x=1}", "foo");
+ v8::Local<v8::Function> bar =
+ CompileFunction(&env, "function bar(){y=2}", "bar");
+
+ // Set some break points.
+ SetBreakPoint(foo, 0);
+ SetBreakPoint(foo, 4);
+ SetBreakPoint(bar, 0);
+ SetBreakPoint(bar, 4);
+
+ // Make sure that the break points are there.
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+ bar->Call(env->Global(), 0, NULL);
+ CHECK_EQ(4, break_point_hit_count);
+
+ // Remove the debug event listener without clearing breakpoints.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded(true);
+
+ // Set a new debug event listener.
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+ // Check that the break points was actually cleared.
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Set break points and run again.
+ SetBreakPoint(foo, 0);
+ SetBreakPoint(foo, 4);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Remove the debug event listener without clearing breakpoints again.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded(true);
+}
+
+
+// Debugger message handler which counts the number of times it is called.
+static int message_handler_hit_count = 0;
+static void MessageHandlerHitCount(const uint16_t* message,
+ int length, void* data) {
+ message_handler_hit_count++;
+
+ const int kBufferSize = 1000;
+ uint16_t buffer[kBufferSize];
+ const char* command_continue =
+ "{\"seq\":0,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
+}
+
+
+// Test clearing the debug message handler.
+TEST(DebuggerClearMessageHandler) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Check debugger is unloaded before it is used.
+ CheckDebuggerUnloaded();
+
+ // Set a debug message handler.
+ v8::Debug::SetMessageHandler(MessageHandlerHitCount);
+
+ // Run code to throw a unhandled exception. This should end up in the message
+ // handler.
+ CompileRun("throw 1");
+
+ // The message handler should be called.
+ CHECK_GT(message_handler_hit_count, 0);
+
+ // Clear debug message handler.
+ message_handler_hit_count = 0;
+ v8::Debug::SetMessageHandler(NULL);
+
+ // Run code to throw a unhandled exception. This should end up in the message
+ // handler.
+ CompileRun("throw 1");
+
+ // The message handler should not be called more.
+ CHECK_EQ(0, message_handler_hit_count);
+
+ CheckDebuggerUnloaded(true);
+}
+
+
+// Debugger message handler which clears the message handler while active.
+static void MessageHandlerClearingMessageHandler(const uint16_t* message,
+ int length,
+ void* data) {
+ message_handler_hit_count++;
+
+ // Clear debug message handler.
+ v8::Debug::SetMessageHandler(NULL);
+}
+
+
+// Test clearing the debug message handler while processing a debug event.
+TEST(DebuggerClearMessageHandlerWhileActive) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Check debugger is unloaded before it is used.
+ CheckDebuggerUnloaded();
+
+ // Set a debug message handler.
+ v8::Debug::SetMessageHandler(MessageHandlerClearingMessageHandler);
+
+ // Run code to throw a unhandled exception. This should end up in the message
+ // handler.
+ CompileRun("throw 1");
+
+ // The message handler should be called.
+ CHECK_EQ(1, message_handler_hit_count);
+
+ CheckDebuggerUnloaded(true);
+}
+
+
+int host_dispatch_hit_count = 0;
+static void HostDispatchHandlerHitCount(void* dispatch, void *data) {
+ host_dispatch_hit_count++;
+}
+
+
+// Test that clearing the debug event listener actually clears all break points
+// and related information.
+TEST(DebuggerHostDispatch) {
+ i::FLAG_debugger_auto_break = true;
+
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ const int kBufferSize = 1000;
+ uint16_t buffer[kBufferSize];
+ const char* command_continue =
+ "{\"seq\":0,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+
+ // Setup message and host dispatch handlers.
+ v8::Debug::SetMessageHandler(DummyMessageHandler);
+ v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount,
+ NULL);
+
+ // Fill a host dispatch and a continue command on the command queue before
+ // running some code.
+ v8::Debug::SendHostDispatch(NULL);
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
+ CompileRun("void 0");
+
+ // The host dispatch callback should be called.
+ CHECK_EQ(1, host_dispatch_hit_count);
+}
+
+
+TEST(DebuggerAgent) {
+ // Make sure this port is not used by other tests to allow tests to run in
+ // parallel.
+ const int kPort = 5858;
+
+ // Make a string with the port number.
+ const int kPortBufferLen = 6;
+ char port_str[kPortBufferLen];
+ OS::SNPrintF(i::Vector<char>(port_str, kPortBufferLen), "%d", kPort);
+
+ bool ok;
+
+ // Initialize the socket library.
+ i::Socket::Setup();
+
+ // Test starting and stopping the agent without any client connection.
+ i::Debugger::StartAgent("test", kPort);
+ i::Debugger::StopAgent();
+
+ // Test starting the agent, connecting a client and shutting down the agent
+ // with the client connected.
+ ok = i::Debugger::StartAgent("test", kPort);
+ CHECK(ok);
+ i::Socket* client = i::OS::CreateSocket();
+ ok = client->Connect("localhost", port_str);
+ CHECK(ok);
+ i::Debugger::StopAgent();
+ delete client;
+
+ // Test starting and stopping the agent with the required port already
+ // occoupied.
+ i::Socket* server = i::OS::CreateSocket();
+ server->Bind(kPort);
+
+ i::Debugger::StartAgent("test", kPort);
+ i::Debugger::StopAgent();
+
+ delete server;
+}
+
+
+class DebuggerAgentProtocolServerThread : public i::Thread {
+ public:
+ explicit DebuggerAgentProtocolServerThread(int port)
+ : port_(port), server_(NULL), client_(NULL),
+ listening_(OS::CreateSemaphore(0)) {
+ }
+ ~DebuggerAgentProtocolServerThread() {
+ // Close both sockets.
+ delete client_;
+ delete server_;
+ delete listening_;
+ }
+
+ void Run();
+ void WaitForListening() { listening_->Wait(); }
+ char* body() { return *body_; }
+
+ private:
+ int port_;
+ i::SmartPointer<char> body_;
+ i::Socket* server_; // Server socket used for bind/accept.
+ i::Socket* client_; // Single client connection used by the test.
+ i::Semaphore* listening_; // Signalled when the server is in listen mode.
+};
+
+
+void DebuggerAgentProtocolServerThread::Run() {
+ bool ok;
+
+ // Create the server socket and bind it to the requested port.
+ server_ = i::OS::CreateSocket();
+ CHECK(server_ != NULL);
+ ok = server_->Bind(port_);
+ CHECK(ok);
+
+ // Listen for new connections.
+ ok = server_->Listen(1);
+ CHECK(ok);
+ listening_->Signal();
+
+ // Accept a connection.
+ client_ = server_->Accept();
+ CHECK(client_ != NULL);
+
+ // Receive a debugger agent protocol message.
+ i::DebuggerAgentUtil::ReceiveMessage(client_);
+}
+
+
+TEST(DebuggerAgentProtocolOverflowHeader) {
+ // Make sure this port is not used by other tests to allow tests to run in
+ // parallel.
+ const int kPort = 5860;
+ static const char* kLocalhost = "localhost";
+
+ // Make a string with the port number.
+ const int kPortBufferLen = 6;
+ char port_str[kPortBufferLen];
+ OS::SNPrintF(i::Vector<char>(port_str, kPortBufferLen), "%d", kPort);
+
+ // Initialize the socket library.
+ i::Socket::Setup();
+
+ // Create a socket server to receive a debugger agent message.
+ DebuggerAgentProtocolServerThread* server =
+ new DebuggerAgentProtocolServerThread(kPort);
+ server->Start();
+ server->WaitForListening();
+
+ // Connect.
+ i::Socket* client = i::OS::CreateSocket();
+ CHECK(client != NULL);
+ bool ok = client->Connect(kLocalhost, port_str);
+ CHECK(ok);
+
+ // Send headers which overflow the receive buffer.
+ static const int kBufferSize = 1000;
+ char buffer[kBufferSize];
+
+ // Long key and short value: XXXX....XXXX:0\r\n.
+ for (int i = 0; i < kBufferSize - 4; i++) {
+ buffer[i] = 'X';
+ }
+ buffer[kBufferSize - 4] = ':';
+ buffer[kBufferSize - 3] = '0';
+ buffer[kBufferSize - 2] = '\r';
+ buffer[kBufferSize - 1] = '\n';
+ client->Send(buffer, kBufferSize);
+
+ // Short key and long value: X:XXXX....XXXX\r\n.
+ buffer[0] = 'X';
+ buffer[1] = ':';
+ for (int i = 2; i < kBufferSize - 2; i++) {
+ buffer[i] = 'X';
+ }
+ buffer[kBufferSize - 2] = '\r';
+ buffer[kBufferSize - 1] = '\n';
+ client->Send(buffer, kBufferSize);
+
+ // Add empty body to request.
+ const char* content_length_zero_header = "Content-Length:0\r\n";
+ client->Send(content_length_zero_header, strlen(content_length_zero_header));
+ client->Send("\r\n", 2);
+
+ // Wait until data is received.
+ server->Join();
+
+ // Check for empty body.
+ CHECK(server->body() == NULL);
+
+ // Close the client before the server to avoid TIME_WAIT issues.
+ client->Shutdown();
+ delete client;
+ delete server;
+}
+
+
+// Test for issue http://code.google.com/p/v8/issues/detail?id=289.
+// Make sure that DebugGetLoadedScripts doesn't return scripts
+// with disposed external source.
+class EmptyExternalStringResource : public v8::String::ExternalStringResource {
+ public:
+ EmptyExternalStringResource() { empty_[0] = 0; }
+ virtual ~EmptyExternalStringResource() {}
+ virtual size_t length() const { return empty_.length(); }
+ virtual const uint16_t* data() const { return empty_.start(); }
+ private:
+ ::v8::internal::EmbeddedVector<uint16_t, 1> empty_;
+};
+
+
+TEST(DebugGetLoadedScripts) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ EmptyExternalStringResource source_ext_str;
+ v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
+ v8::Handle<v8::Script> evil_script = v8::Script::Compile(source);
+ Handle<i::ExternalTwoByteString> i_source(
+ i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
+ // This situation can happen if source was an external string disposed
+ // by its owner.
+ i_source->set_resource(0);
+
+ bool allow_natives_syntax = i::FLAG_allow_natives_syntax;
+ i::FLAG_allow_natives_syntax = true;
+ CompileRun(
+ "var scripts = %DebugGetLoadedScripts();"
+ "for (var i = 0; i < scripts.length; ++i) {"
+ " scripts[i].line_ends;"
+ "}");
+ // Must not crash while accessing line_ends.
+ i::FLAG_allow_natives_syntax = allow_natives_syntax;
+}
diff --git a/v8/test/cctest/test-decls.cc b/v8/test/cctest/test-decls.cc
new file mode 100644
index 0000000..e798fee
--- /dev/null
+++ b/v8/test/cctest/test-decls.cc
@@ -0,0 +1,594 @@
+// 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 "v8.h"
+
+#include "heap.h"
+#include "cctest.h"
+
+using namespace v8;
+
+
+enum Expectations {
+ EXPECT_RESULT,
+ EXPECT_EXCEPTION
+};
+
+
+// A DeclarationContext holds a reference to a v8::Context and keeps
+// track of various declaration related counters to make it easier to
+// track if global declarations in the presence of interceptors behave
+// the right way.
+class DeclarationContext {
+ public:
+ DeclarationContext();
+
+ virtual ~DeclarationContext() {
+ if (is_initialized_) {
+ context_->Exit();
+ context_.Dispose();
+ }
+ }
+
+ void Check(const char* source,
+ int get, int set, int has,
+ Expectations expectations,
+ v8::Handle<Value> value = Local<Value>());
+
+ int get_count() const { return get_count_; }
+ int set_count() const { return set_count_; }
+ int has_count() const { return has_count_; }
+
+ protected:
+ virtual v8::Handle<Value> Get(Local<String> key);
+ virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
+ virtual v8::Handle<Boolean> Has(Local<String> key);
+
+ void InitializeIfNeeded();
+
+ // Get the holder for the interceptor. Default to the instance template
+ // but may be overwritten.
+ virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
+ return function->InstanceTemplate();
+ }
+
+ // The handlers are called as static functions that forward
+ // to the instance specific virtual methods.
+ static v8::Handle<Value> HandleGet(Local<String> key,
+ const AccessorInfo& info);
+ static v8::Handle<Value> HandleSet(Local<String> key,
+ Local<Value> value,
+ const AccessorInfo& info);
+ static v8::Handle<Boolean> HandleHas(Local<String> key,
+ const AccessorInfo& info);
+
+ private:
+ bool is_initialized_;
+ Persistent<Context> context_;
+ Local<String> property_;
+
+ int get_count_;
+ int set_count_;
+ int has_count_;
+
+ static DeclarationContext* GetInstance(const AccessorInfo& info);
+};
+
+
+DeclarationContext::DeclarationContext()
+ : is_initialized_(false), get_count_(0), set_count_(0), has_count_(0) {
+ // Do nothing.
+}
+
+
+void DeclarationContext::InitializeIfNeeded() {
+ if (is_initialized_) return;
+ HandleScope scope;
+ Local<FunctionTemplate> function = FunctionTemplate::New();
+ Local<Value> data = Integer::New(reinterpret_cast<intptr_t>(this));
+ GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
+ &HandleSet,
+ &HandleHas,
+ 0, 0,
+ data);
+ context_ = Context::New(0, function->InstanceTemplate(), Local<Value>());
+ context_->Enter();
+ is_initialized_ = true;
+}
+
+
+void DeclarationContext::Check(const char* source,
+ int get, int set, int has,
+ Expectations expectations,
+ v8::Handle<Value> value) {
+ InitializeIfNeeded();
+ // A retry after a GC may pollute the counts, so perform gc now
+ // to avoid that.
+ v8::internal::Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
+ HandleScope scope;
+ TryCatch catcher;
+ catcher.SetVerbose(true);
+ Local<Value> result = Script::Compile(String::New(source))->Run();
+ CHECK_EQ(get, get_count());
+ CHECK_EQ(set, set_count());
+ CHECK_EQ(has, has_count());
+ if (expectations == EXPECT_RESULT) {
+ CHECK(!catcher.HasCaught());
+ if (!value.IsEmpty()) {
+ CHECK_EQ(value, result);
+ }
+ } else {
+ CHECK(expectations == EXPECT_EXCEPTION);
+ CHECK(catcher.HasCaught());
+ if (!value.IsEmpty()) {
+ CHECK_EQ(value, catcher.Exception());
+ }
+ }
+}
+
+
+v8::Handle<Value> DeclarationContext::HandleGet(Local<String> key,
+ const AccessorInfo& info) {
+ DeclarationContext* context = GetInstance(info);
+ context->get_count_++;
+ return context->Get(key);
+}
+
+
+v8::Handle<Value> DeclarationContext::HandleSet(Local<String> key,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ DeclarationContext* context = GetInstance(info);
+ context->set_count_++;
+ return context->Set(key, value);
+}
+
+
+v8::Handle<Boolean> DeclarationContext::HandleHas(Local<String> key,
+ const AccessorInfo& info) {
+ DeclarationContext* context = GetInstance(info);
+ context->has_count_++;
+ return context->Has(key);
+}
+
+
+DeclarationContext* DeclarationContext::GetInstance(const AccessorInfo& info) {
+ Local<Value> data = info.Data();
+ return reinterpret_cast<DeclarationContext*>(Int32::Cast(*data)->Value());
+}
+
+
+v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
+ return v8::Handle<Value>();
+}
+
+
+v8::Handle<Value> DeclarationContext::Set(Local<String> key,
+ Local<Value> value) {
+ return v8::Handle<Value>();
+}
+
+
+v8::Handle<Boolean> DeclarationContext::Has(Local<String> key) {
+ return v8::Handle<Boolean>();
+}
+
+
+// Test global declaration of a property the interceptor doesn't know
+// about and doesn't handle.
+TEST(Unknown) {
+ HandleScope scope;
+
+ { DeclarationContext context;
+ context.Check("var x; x",
+ 1, // access
+ 1, // declaration
+ 2, // declaration + initialization
+ EXPECT_RESULT, Undefined());
+ }
+
+ { DeclarationContext context;
+ context.Check("var x = 0; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Number::New(0));
+ }
+
+ { DeclarationContext context;
+ context.Check("function x() { }; x",
+ 1, // access
+ 1, // declaration
+ 0,
+ EXPECT_RESULT);
+ }
+
+ { DeclarationContext context;
+ context.Check("const x; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Undefined());
+ }
+
+ { DeclarationContext context;
+ context.Check("const x = 0; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
+ }
+}
+
+
+
+class PresentPropertyContext: public DeclarationContext {
+ protected:
+ virtual v8::Handle<Boolean> Has(Local<String> key) {
+ return True();
+ }
+};
+
+
+
+TEST(Present) {
+ HandleScope scope;
+
+ { PresentPropertyContext context;
+ context.Check("var x; x",
+ 1, // access
+ 0,
+ 2, // declaration + initialization
+ EXPECT_EXCEPTION); // x is not defined!
+ }
+
+ { PresentPropertyContext context;
+ context.Check("var x = 0; x",
+ 1, // access
+ 1, // initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Number::New(0));
+ }
+
+ { PresentPropertyContext context;
+ context.Check("function x() { }; x",
+ 1, // access
+ 1, // declaration
+ 0,
+ EXPECT_RESULT);
+ }
+
+ { PresentPropertyContext context;
+ context.Check("const x; x",
+ 0,
+ 0,
+ 1, // (re-)declaration
+ EXPECT_EXCEPTION); // x has already been declared!
+ }
+
+ { PresentPropertyContext context;
+ context.Check("const x = 0; x",
+ 0,
+ 0,
+ 1, // (re-)declaration
+ EXPECT_EXCEPTION); // x has already been declared!
+ }
+}
+
+
+
+class AbsentPropertyContext: public DeclarationContext {
+ protected:
+ virtual v8::Handle<Boolean> Has(Local<String> key) {
+ return False();
+ }
+};
+
+
+TEST(Absent) {
+ HandleScope scope;
+
+ { AbsentPropertyContext context;
+ context.Check("var x; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Undefined());
+ }
+
+ { AbsentPropertyContext context;
+ context.Check("var x = 0; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Number::New(0));
+ }
+
+ { AbsentPropertyContext context;
+ context.Check("function x() { }; x",
+ 1, // access
+ 1, // declaration
+ 0,
+ EXPECT_RESULT);
+ }
+
+ { AbsentPropertyContext context;
+ context.Check("const x; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initializetion
+ EXPECT_RESULT, Undefined());
+ }
+
+ { AbsentPropertyContext context;
+ context.Check("const x = 0; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
+ }
+
+ { AbsentPropertyContext context;
+ context.Check("if (false) { var x = 0 }; x",
+ 1, // access
+ 1, // declaration
+ 1, // declaration + initialization
+ EXPECT_RESULT, Undefined());
+ }
+}
+
+
+
+class AppearingPropertyContext: public DeclarationContext {
+ public:
+ enum State {
+ DECLARE,
+ INITIALIZE_IF_ASSIGN,
+ UNKNOWN
+ };
+
+ AppearingPropertyContext() : state_(DECLARE) { }
+
+ protected:
+ virtual v8::Handle<Boolean> Has(Local<String> key) {
+ switch (state_) {
+ case DECLARE:
+ // Force declaration by returning that the
+ // property is absent.
+ state_ = INITIALIZE_IF_ASSIGN;
+ return False();
+ case INITIALIZE_IF_ASSIGN:
+ // Return that the property is present so we only get the
+ // setter called when initializing with a value.
+ state_ = UNKNOWN;
+ return True();
+ default:
+ ASSERT(state_ == UNKNOWN);
+ break;
+ }
+ // Do the lookup in the object.
+ return v8::Local<Boolean>();
+ }
+
+ private:
+ State state_;
+};
+
+
+TEST(Appearing) {
+ HandleScope scope;
+
+ { AppearingPropertyContext context;
+ context.Check("var x; x",
+ 1, // access
+ 1, // declaration
+ 2, // declaration + initialization
+ EXPECT_RESULT, Undefined());
+ }
+
+ { AppearingPropertyContext context;
+ context.Check("var x = 0; x",
+ 1, // access
+ 2, // declaration + initialization
+ 2, // declaration + initialization
+ EXPECT_RESULT, Number::New(0));
+ }
+
+ { AppearingPropertyContext context;
+ context.Check("function x() { }; x",
+ 1, // access
+ 1, // declaration
+ 0,
+ EXPECT_RESULT);
+ }
+
+ { AppearingPropertyContext context;
+ context.Check("const x; x",
+ 0,
+ 1, // declaration
+ 2, // declaration + initialization
+ EXPECT_EXCEPTION); // x has already been declared!
+ }
+
+ { AppearingPropertyContext context;
+ context.Check("const x = 0; x",
+ 0,
+ 1, // declaration
+ 2, // declaration + initialization
+ EXPECT_EXCEPTION); // x has already been declared!
+ }
+}
+
+
+
+class ReappearingPropertyContext: public DeclarationContext {
+ public:
+ enum State {
+ DECLARE,
+ DONT_DECLARE,
+ INITIALIZE,
+ UNKNOWN
+ };
+
+ ReappearingPropertyContext() : state_(DECLARE) { }
+
+ protected:
+ virtual v8::Handle<Boolean> Has(Local<String> key) {
+ switch (state_) {
+ case DECLARE:
+ // Force the first declaration by returning that
+ // the property is absent.
+ state_ = DONT_DECLARE;
+ return False();
+ case DONT_DECLARE:
+ // Ignore the second declaration by returning
+ // that the property is already there.
+ state_ = INITIALIZE;
+ return True();
+ case INITIALIZE:
+ // Force an initialization by returning that
+ // the property is absent. This will make sure
+ // that the setter is called and it will not
+ // lead to redeclaration conflicts (yet).
+ state_ = UNKNOWN;
+ return False();
+ default:
+ ASSERT(state_ == UNKNOWN);
+ break;
+ }
+ // Do the lookup in the object.
+ return v8::Local<Boolean>();
+ }
+
+ private:
+ State state_;
+};
+
+
+TEST(Reappearing) {
+ HandleScope scope;
+
+ { ReappearingPropertyContext context;
+ context.Check("const x; var x = 0",
+ 0,
+ 2, // var declaration + const initialization
+ 4, // 2 x declaration + 2 x initialization
+ EXPECT_EXCEPTION); // x has already been declared!
+ }
+}
+
+
+
+class ExistsInPrototypeContext: public DeclarationContext {
+ protected:
+ virtual v8::Handle<Boolean> Has(Local<String> key) {
+ // Let it seem that the property exists in the prototype object.
+ return True();
+ }
+
+ // Use the prototype as the holder for the interceptors.
+ virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
+ return function->PrototypeTemplate();
+ }
+};
+
+
+TEST(ExistsInPrototype) {
+ HandleScope scope;
+
+ // Sanity check to make sure that the holder of the interceptor
+ // really is the prototype object.
+ { ExistsInPrototypeContext context;
+ context.Check("this.x = 87; this.x",
+ 0,
+ 0,
+ 0,
+ EXPECT_RESULT, Number::New(87));
+ }
+
+ { ExistsInPrototypeContext context;
+ context.Check("var x; x",
+ 0,
+ 0,
+ 1, // declaration
+ EXPECT_RESULT, Undefined());
+ }
+
+ { ExistsInPrototypeContext context;
+ context.Check("var x = 0; x",
+ 0,
+ 0,
+ 1, // declaration
+ EXPECT_RESULT, Number::New(0));
+ }
+
+ { ExistsInPrototypeContext context;
+ context.Check("const x; x",
+ 0,
+ 0,
+ 1, // declaration
+ EXPECT_RESULT, Undefined());
+ }
+
+ { ExistsInPrototypeContext context;
+ context.Check("const x = 0; x",
+ 0,
+ 0,
+ 1, // declaration
+ EXPECT_RESULT, Number::New(0));
+ }
+}
+
+
+
+class AbsentInPrototypeContext: public DeclarationContext {
+ protected:
+ virtual v8::Handle<Boolean> Has(Local<String> key) {
+ // Let it seem that the property is absent in the prototype object.
+ return False();
+ }
+
+ // Use the prototype as the holder for the interceptors.
+ virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
+ return function->PrototypeTemplate();
+ }
+};
+
+
+TEST(AbsentInPrototype) {
+ HandleScope scope;
+
+ { AbsentInPrototypeContext context;
+ context.Check("if (false) { var x = 0; }; x",
+ 0,
+ 0,
+ 1, // declaration
+ EXPECT_RESULT, Undefined());
+ }
+}
diff --git a/v8/test/cctest/test-disasm-arm.cc b/v8/test/cctest/test-disasm-arm.cc
new file mode 100644
index 0000000..1cca17d
--- /dev/null
+++ b/v8/test/cctest/test-disasm-arm.cc
@@ -0,0 +1,281 @@
+// 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 "v8.h"
+
+#include "debug.h"
+#include "disasm.h"
+#include "disassembler.h"
+#include "macro-assembler.h"
+#include "serialize.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+static v8::Persistent<v8::Context> env;
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ env = v8::Context::New();
+ }
+}
+
+
+bool DisassembleAndCompare(byte* pc, const char* compare_string) {
+ disasm::NameConverter converter;
+ disasm::Disassembler disasm(converter);
+ EmbeddedVector<char, 128> disasm_buffer;
+
+ disasm.InstructionDecode(disasm_buffer, pc);
+
+ if (strcmp(compare_string, disasm_buffer.start()) != 0) {
+ fprintf(stderr,
+ "expected: \n"
+ "%s\n"
+ "disassembled: \n"
+ "%s\n\n",
+ compare_string, disasm_buffer.start());
+ return false;
+ }
+ return true;
+}
+
+
+// Setup V8 to a state where we can at least run the assembler and
+// disassembler. Declare the variables and allocate the data structures used
+// in the rest of the macros.
+#define SETUP() \
+ InitializeVM(); \
+ v8::HandleScope scope; \
+ byte *buffer = reinterpret_cast<byte*>(malloc(4*1024)); \
+ Assembler assm(buffer, 4*1024); \
+ bool failure = false;
+
+
+// This macro assembles one instruction using the preallocated assembler and
+// disassembles the generated instruction, comparing the output to the expected
+// value. If the comparison fails an error message is printed, but the test
+// continues to run until the end.
+#define COMPARE(asm_, compare_string) \
+ { \
+ int pc_offset = assm.pc_offset(); \
+ byte *pc = &buffer[pc_offset]; \
+ assm.asm_; \
+ if (!DisassembleAndCompare(pc, compare_string)) failure = true; \
+ }
+
+
+// Verify that all invocations of the COMPARE macro passed successfully.
+// Exit with a failure if at least one of the tests failed.
+#define VERIFY_RUN() \
+if (failure) { \
+ V8_Fatal(__FILE__, __LINE__, "ARM Disassembler tests failed.\n"); \
+ }
+
+
+TEST(Type0) {
+ SETUP();
+
+ COMPARE(and_(r0, r1, Operand(r2)),
+ "e0010002 and r0, r1, r2");
+ COMPARE(and_(r1, r2, Operand(r3), LeaveCC),
+ "e0021003 and r1, r2, r3");
+ COMPARE(and_(r2, r3, Operand(r4), SetCC),
+ "e0132004 ands r2, r3, r4");
+ COMPARE(and_(r3, r4, Operand(r5), LeaveCC, eq),
+ "00043005 andeq r3, r4, r5");
+
+ COMPARE(eor(r4, r5, Operand(r6, LSL, 0)),
+ "e0254006 eor r4, r5, r6");
+ COMPARE(eor(r4, r5, Operand(r7, LSL, 1), SetCC),
+ "e0354087 eors r4, r5, r7, lsl #1");
+ COMPARE(eor(r4, r5, Operand(r8, LSL, 2), LeaveCC, ne),
+ "10254108 eorne r4, r5, r8, lsl #2");
+ COMPARE(eor(r4, r5, Operand(r9, LSL, 3), SetCC, cs),
+ "20354189 eorcss r4, r5, r9, lsl #3");
+
+ COMPARE(sub(r5, r6, Operand(r10, LSL, 31), LeaveCC, hs),
+ "20465f8a subcs r5, r6, sl, lsl #31");
+ COMPARE(sub(r5, r6, Operand(r10, LSL, 30), SetCC, cc),
+ "30565f0a subccs r5, r6, sl, lsl #30");
+ COMPARE(sub(r5, r6, Operand(r10, LSL, 24), LeaveCC, lo),
+ "30465c0a subcc r5, r6, sl, lsl #24");
+ COMPARE(sub(r5, r6, Operand(r10, LSL, 16), SetCC, mi),
+ "4056580a submis r5, r6, sl, lsl #16");
+
+ COMPARE(rsb(r6, r7, Operand(fp)),
+ "e067600b rsb r6, r7, fp");
+ COMPARE(rsb(r6, r7, Operand(fp, LSR, 1)),
+ "e06760ab rsb r6, r7, fp, lsr #1");
+ COMPARE(rsb(r6, r7, Operand(fp, LSR, 0), SetCC),
+ "e077602b rsbs r6, r7, fp, lsr #32");
+ COMPARE(rsb(r6, r7, Operand(fp, LSR, 31), LeaveCC, pl),
+ "50676fab rsbpl r6, r7, fp, lsr #31");
+
+ COMPARE(add(r7, r8, Operand(ip, ASR, 1)),
+ "e08870cc add r7, r8, ip, asr #1");
+ COMPARE(add(r7, r8, Operand(ip, ASR, 0)),
+ "e088704c add r7, r8, ip, asr #32");
+ COMPARE(add(r7, r8, Operand(ip), SetCC),
+ "e098700c adds r7, r8, ip");
+ COMPARE(add(r7, r8, Operand(ip, ASR, 31), SetCC, vs),
+ "60987fcc addvss r7, r8, ip, asr #31");
+
+ COMPARE(adc(r7, fp, Operand(ip, ASR, 5)),
+ "e0ab72cc adc r7, fp, ip, asr #5");
+ COMPARE(adc(r4, ip, Operand(ip, ASR, 1), LeaveCC, vc),
+ "70ac40cc adcvc r4, ip, ip, asr #1");
+ COMPARE(adc(r5, sp, Operand(ip), SetCC),
+ "e0bd500c adcs r5, sp, ip");
+ COMPARE(adc(r8, lr, Operand(ip, ASR, 31), SetCC, vc),
+ "70be8fcc adcvcs r8, lr, ip, asr #31");
+
+ COMPARE(sbc(r7, r1, Operand(ip, ROR, 1), LeaveCC, hi),
+ "80c170ec sbchi r7, r1, ip, ror #1");
+ COMPARE(sbc(r7, r9, Operand(ip, ROR, 4)),
+ "e0c9726c sbc r7, r9, ip, ror #4");
+ COMPARE(sbc(r7, r10, Operand(ip), SetCC),
+ "e0da700c sbcs r7, sl, ip");
+ COMPARE(sbc(r7, ip, Operand(ip, ROR, 31), SetCC, hi),
+ "80dc7fec sbchis r7, ip, ip, ror #31");
+
+ COMPARE(rsc(r7, r8, Operand(ip, LSL, r0)),
+ "e0e8701c rsc r7, r8, ip, lsl r0");
+ COMPARE(rsc(r7, r8, Operand(ip, LSL, r1)),
+ "e0e8711c rsc r7, r8, ip, lsl r1");
+ COMPARE(rsc(r7, r8, Operand(ip), SetCC),
+ "e0f8700c rscs r7, r8, ip");
+ COMPARE(rsc(r7, r8, Operand(ip, LSL, r3), SetCC, ls),
+ "90f8731c rsclss r7, r8, ip, lsl r3");
+
+ COMPARE(tst(r7, Operand(r5, ASR, ip), ge),
+ "a1170c55 tstge r7, r5, asr ip");
+ COMPARE(tst(r7, Operand(r6, ASR, sp)),
+ "e1170d56 tst r7, r6, asr sp");
+ COMPARE(tst(r7, Operand(r7), ge),
+ "a1170007 tstge r7, r7");
+ COMPARE(tst(r7, Operand(r8, ASR, fp), ge),
+ "a1170b58 tstge r7, r8, asr fp");
+
+ COMPARE(teq(r7, Operand(r5, ROR, r0), lt),
+ "b1370075 teqlt r7, r5, ror r0");
+ COMPARE(teq(r7, Operand(r6, ROR, lr)),
+ "e1370e76 teq r7, r6, ror lr");
+ COMPARE(teq(r7, Operand(r7), lt),
+ "b1370007 teqlt r7, r7");
+ COMPARE(teq(r7, Operand(r8, ROR, r1)),
+ "e1370178 teq r7, r8, ror r1");
+
+ COMPARE(cmp(r7, Operand(r4)),
+ "e1570004 cmp r7, r4");
+ COMPARE(cmp(r7, Operand(r6, LSL, 1), gt),
+ "c1570086 cmpgt r7, r6, lsl #1");
+ COMPARE(cmp(r7, Operand(r8, LSR, 3), gt),
+ "c15701a8 cmpgt r7, r8, lsr #3");
+ COMPARE(cmp(r7, Operand(r8, ASR, 19)),
+ "e15709c8 cmp r7, r8, asr #19");
+
+ COMPARE(cmn(r0, Operand(r4)),
+ "e1700004 cmn r0, r4");
+ COMPARE(cmn(r1, Operand(r6, ROR, 1)),
+ "e17100e6 cmn r1, r6, ror #1");
+ COMPARE(cmn(r2, Operand(r8)),
+ "e1720008 cmn r2, r8");
+ COMPARE(cmn(r3, Operand(fp), le),
+ "d173000b cmnle r3, fp");
+
+ COMPARE(orr(r7, r8, Operand(lr), LeaveCC, al),
+ "e188700e orr r7, r8, lr");
+ COMPARE(orr(r7, r8, Operand(fp)),
+ "e188700b orr r7, r8, fp");
+ COMPARE(orr(r7, r8, Operand(sp), SetCC),
+ "e198700d orrs r7, r8, sp");
+ COMPARE(orr(r7, r8, Operand(ip), SetCC, al),
+ "e198700c orrs r7, r8, ip");
+
+ COMPARE(mov(r0, Operand(r1), LeaveCC, eq),
+ "01a00001 moveq r0, r1");
+ COMPARE(mov(r0, Operand(r2)),
+ "e1a00002 mov r0, r2");
+ COMPARE(mov(r0, Operand(r3), SetCC),
+ "e1b00003 movs r0, r3");
+ COMPARE(mov(r0, Operand(r4), SetCC, pl),
+ "51b00004 movpls r0, r4");
+
+ COMPARE(bic(r0, lr, Operand(r1), LeaveCC, vs),
+ "61ce0001 bicvs r0, lr, r1");
+ COMPARE(bic(r0, r9, Operand(r2), LeaveCC, vc),
+ "71c90002 bicvc r0, r9, r2");
+ COMPARE(bic(r0, r5, Operand(r3), SetCC),
+ "e1d50003 bics r0, r5, r3");
+ COMPARE(bic(r0, r1, Operand(r4), SetCC, pl),
+ "51d10004 bicpls r0, r1, r4");
+
+ COMPARE(mvn(r10, Operand(r1)),
+ "e1e0a001 mvn sl, r1");
+ COMPARE(mvn(r9, Operand(r2)),
+ "e1e09002 mvn r9, r2");
+ COMPARE(mvn(r0, Operand(r3), SetCC),
+ "e1f00003 mvns r0, r3");
+ COMPARE(mvn(r5, Operand(r4), SetCC, cc),
+ "31f05004 mvnccs r5, r4");
+
+ VERIFY_RUN();
+}
+
+
+TEST(Type1) {
+ SETUP();
+
+ COMPARE(and_(r0, r1, Operand(0x00000000)),
+ "e2010000 and r0, r1, #0");
+ COMPARE(and_(r1, r2, Operand(0x00000001), LeaveCC),
+ "e2021001 and r1, r2, #1");
+ COMPARE(and_(r2, r3, Operand(0x00000010), SetCC),
+ "e2132010 ands r2, r3, #16");
+ COMPARE(and_(r3, r4, Operand(0x00000100), LeaveCC, eq),
+ "02043c01 andeq r3, r4, #256");
+ COMPARE(and_(r4, r5, Operand(0x00001000), SetCC, ne),
+ "12154a01 andnes r4, r5, #4096");
+
+ COMPARE(eor(r4, r5, Operand(0x00001000)),
+ "e2254a01 eor r4, r5, #4096");
+ COMPARE(eor(r4, r4, Operand(0x00010000), LeaveCC),
+ "e2244801 eor r4, r4, #65536");
+ COMPARE(eor(r4, r3, Operand(0x00100000), SetCC),
+ "e2334601 eors r4, r3, #1048576");
+ COMPARE(eor(r4, r2, Operand(0x01000000), LeaveCC, cs),
+ "22224401 eorcs r4, r2, #16777216");
+ COMPARE(eor(r4, r1, Operand(0x10000000), SetCC, cc),
+ "32314201 eorccs r4, r1, #268435456");
+
+ VERIFY_RUN();
+}
diff --git a/v8/test/cctest/test-disasm-ia32.cc b/v8/test/cctest/test-disasm-ia32.cc
new file mode 100644
index 0000000..af9fb97
--- /dev/null
+++ b/v8/test/cctest/test-disasm-ia32.cc
@@ -0,0 +1,384 @@
+// 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 "v8.h"
+
+#include "debug.h"
+#include "disasm.h"
+#include "disassembler.h"
+#include "macro-assembler.h"
+#include "serialize.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static v8::Persistent<v8::Context> env;
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ env = v8::Context::New();
+ }
+}
+
+
+#define __ assm.
+
+
+static void DummyStaticFunction(Object* result) {
+}
+
+
+TEST(DisasmIa320) {
+ InitializeVM();
+ v8::HandleScope scope;
+ v8::internal::byte buffer[1024];
+ Assembler assm(buffer, sizeof buffer);
+ DummyStaticFunction(NULL); // just bloody use it (DELETE; debugging)
+
+ // Short immediate instructions
+ __ adc(eax, 12345678);
+ __ add(Operand(eax), Immediate(12345678));
+ __ or_(eax, 12345678);
+ __ sub(Operand(eax), Immediate(12345678));
+ __ xor_(eax, 12345678);
+ __ and_(eax, 12345678);
+ Handle<FixedArray> foo = Factory::NewFixedArray(10, TENURED);
+ __ cmp(eax, foo);
+
+ // ---- This one caused crash
+ __ mov(ebx, Operand(esp, ecx, times_2, 0)); // [esp+ecx*4]
+
+ // ---- All instructions that I can think of
+ __ add(edx, Operand(ebx));
+ __ add(edx, Operand(12, RelocInfo::NONE));
+ __ add(edx, Operand(ebx, 0));
+ __ add(edx, Operand(ebx, 16));
+ __ add(edx, Operand(ebx, 1999));
+ __ add(edx, Operand(esp, 0));
+ __ add(edx, Operand(esp, 16));
+ __ add(edx, Operand(esp, 1999));
+ __ nop();
+ __ add(edi, Operand(ebp, ecx, times_4, 0));
+ __ add(edi, Operand(ebp, ecx, times_4, 12));
+ __ add(Operand(ebp, ecx, times_4, 12), Immediate(12));
+
+ __ nop();
+ __ add(Operand(ebx), Immediate(12));
+ __ nop();
+ __ adc(ecx, 12);
+ __ adc(ecx, 1000);
+ __ nop();
+ __ and_(edx, 3);
+ __ and_(edx, Operand(esp, 4));
+ __ cmp(edx, 3);
+ __ cmp(edx, Operand(esp, 4));
+ __ cmp(Operand(ebp, ecx, times_4, 0), Immediate(1000));
+ Handle<FixedArray> foo2 = Factory::NewFixedArray(10, TENURED);
+ __ cmp(ebx, foo2);
+ __ or_(edx, 3);
+ __ xor_(edx, 3);
+ __ nop();
+ {
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::CPUID));
+ CpuFeatures::Scope fscope(CpuFeatures::CPUID);
+ __ cpuid();
+ }
+ {
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::RDTSC));
+ CpuFeatures::Scope fscope(CpuFeatures::RDTSC);
+ __ rdtsc();
+ }
+ __ movsx_b(edx, Operand(ecx));
+ __ movsx_w(edx, Operand(ecx));
+ __ movzx_b(edx, Operand(ecx));
+ __ movzx_w(edx, Operand(ecx));
+
+ __ nop();
+ __ imul(edx, Operand(ecx));
+ __ shld(edx, Operand(ecx));
+ __ shrd(edx, Operand(ecx));
+ __ bts(Operand(edx), ecx);
+ __ bts(Operand(ebx, ecx, times_4, 0), ecx);
+ __ nop();
+ __ pushad();
+ __ popad();
+ __ pushfd();
+ __ popfd();
+ __ push(Immediate(12));
+ __ push(Immediate(23456));
+ __ push(ecx);
+ __ push(esi);
+ __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(Operand(ebx, ecx, times_4, 0));
+ __ push(Operand(ebx, ecx, times_4, 0));
+ __ push(Operand(ebx, ecx, times_4, 10000));
+ __ pop(edx);
+ __ pop(eax);
+ __ pop(Operand(ebx, ecx, times_4, 0));
+ __ nop();
+
+ __ add(edx, Operand(esp, 16));
+ __ add(edx, Operand(ecx));
+ __ mov_b(edx, Operand(ecx));
+ __ mov_b(Operand(ecx), 6);
+ __ mov_b(Operand(ebx, ecx, times_4, 10000), 6);
+ __ mov_b(Operand(esp, 16), edx);
+ __ mov_w(edx, Operand(esp, 16));
+ __ mov_w(Operand(esp, 16), edx);
+ __ nop();
+ __ movsx_w(edx, Operand(esp, 12));
+ __ movsx_b(edx, Operand(esp, 12));
+ __ movzx_w(edx, Operand(esp, 12));
+ __ movzx_b(edx, Operand(esp, 12));
+ __ nop();
+ __ mov(edx, 1234567);
+ __ mov(edx, Operand(esp, 12));
+ __ mov(Operand(ebx, ecx, times_4, 10000), Immediate(12345));
+ __ mov(Operand(ebx, ecx, times_4, 10000), edx);
+ __ nop();
+ __ dec_b(edx);
+ __ dec(edx);
+ __ cdq();
+
+ __ nop();
+ __ idiv(edx);
+ __ mul(edx);
+ __ neg(edx);
+ __ not_(edx);
+ __ test(Operand(ebx, ecx, times_4, 10000), Immediate(123456));
+
+ __ imul(edx, Operand(ebx, ecx, times_4, 10000));
+ __ imul(edx, ecx, 12);
+ __ imul(edx, ecx, 1000);
+
+ __ inc(edx);
+ __ inc(Operand(ebx, ecx, times_4, 10000));
+ __ push(Operand(ebx, ecx, times_4, 10000));
+ __ pop(Operand(ebx, ecx, times_4, 10000));
+ __ call(Operand(ebx, ecx, times_4, 10000));
+ __ jmp(Operand(ebx, ecx, times_4, 10000));
+
+ __ lea(edx, Operand(ebx, ecx, times_4, 10000));
+ __ or_(edx, 12345);
+ __ or_(edx, Operand(ebx, ecx, times_4, 10000));
+
+ __ nop();
+
+ __ rcl(edx, 1);
+ __ rcl(edx, 7);
+ __ sar(edx, 1);
+ __ sar(edx, 6);
+ __ sar(edx);
+ __ sbb(edx, Operand(ebx, ecx, times_4, 10000));
+ __ shld(edx, Operand(ebx, ecx, times_4, 10000));
+ __ shl(edx, 1);
+ __ shl(edx, 6);
+ __ shl(edx);
+ __ shrd(edx, Operand(ebx, ecx, times_4, 10000));
+ __ shr(edx, 7);
+ __ shr(edx);
+
+
+ // Immediates
+
+ __ adc(edx, 12345);
+
+ __ add(Operand(ebx), Immediate(12));
+ __ add(Operand(edx, ecx, times_4, 10000), Immediate(12));
+
+ __ and_(ebx, 12345);
+
+ __ cmp(ebx, 12345);
+ __ cmp(Operand(ebx), Immediate(12));
+ __ cmp(Operand(edx, ecx, times_4, 10000), Immediate(12));
+
+ __ or_(ebx, 12345);
+
+ __ sub(Operand(ebx), Immediate(12));
+ __ sub(Operand(edx, ecx, times_4, 10000), Immediate(12));
+
+ __ xor_(ebx, 12345);
+
+ __ imul(edx, ecx, 12);
+ __ imul(edx, ecx, 1000);
+
+
+
+ __ sub(edx, Operand(ebx, ecx, times_4, 10000));
+ __ sub(edx, Operand(ebx));
+
+ __ test(edx, Immediate(12345));
+ __ test(edx, Operand(ebx, ecx, times_8, 10000));
+ __ nop();
+
+ __ xor_(edx, 12345);
+ __ xor_(edx, Operand(ebx, ecx, times_8, 10000));
+ __ bts(Operand(ebx, ecx, times_8, 10000), edx);
+ __ hlt();
+ __ int3();
+ __ ret(0);
+ __ ret(8);
+
+ // Calls
+
+ Label L1, L2;
+ __ bind(&L1);
+ __ nop();
+ __ call(&L1);
+ __ call(&L2);
+ __ nop();
+ __ bind(&L2);
+ __ call(Operand(ebx, ecx, times_4, 10000));
+ __ nop();
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+ __ nop();
+ __ call(FUNCTION_ADDR(DummyStaticFunction), RelocInfo::RUNTIME_ENTRY);
+ __ nop();
+
+ __ jmp(&L1);
+ __ jmp(Operand(ebx, ecx, times_4, 10000));
+ ExternalReference after_break_target =
+ ExternalReference(Debug_Address::AfterBreakTarget());
+ __ jmp(Operand::StaticVariable(after_break_target));
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+ __ nop();
+
+
+ Label Ljcc;
+ __ nop();
+ // long jumps
+ __ j(overflow, &Ljcc);
+ __ j(no_overflow, &Ljcc);
+ __ j(below, &Ljcc);
+ __ j(above_equal, &Ljcc);
+ __ j(equal, &Ljcc);
+ __ j(not_equal, &Ljcc);
+ __ j(below_equal, &Ljcc);
+ __ j(above, &Ljcc);
+ __ j(sign, &Ljcc);
+ __ j(not_sign, &Ljcc);
+ __ j(parity_even, &Ljcc);
+ __ j(parity_odd, &Ljcc);
+ __ j(less, &Ljcc);
+ __ j(greater_equal, &Ljcc);
+ __ j(less_equal, &Ljcc);
+ __ j(greater, &Ljcc);
+ __ nop();
+ __ bind(&Ljcc);
+ // short jumps
+ __ j(overflow, &Ljcc);
+ __ j(no_overflow, &Ljcc);
+ __ j(below, &Ljcc);
+ __ j(above_equal, &Ljcc);
+ __ j(equal, &Ljcc);
+ __ j(not_equal, &Ljcc);
+ __ j(below_equal, &Ljcc);
+ __ j(above, &Ljcc);
+ __ j(sign, &Ljcc);
+ __ j(not_sign, &Ljcc);
+ __ j(parity_even, &Ljcc);
+ __ j(parity_odd, &Ljcc);
+ __ j(less, &Ljcc);
+ __ j(greater_equal, &Ljcc);
+ __ j(less_equal, &Ljcc);
+ __ j(greater, &Ljcc);
+
+ // checking hints
+ __ j(zero, &Ljcc, taken);
+ __ j(zero, &Ljcc, not_taken);
+
+ // __ mov(Operand::StaticVariable(Top::handler_address()), eax);
+ // 0xD9 instructions
+ __ nop();
+
+ __ fld1();
+ __ fldz();
+ __ fabs();
+ __ fchs();
+ __ fprem();
+ __ fprem1();
+ __ fincstp();
+ __ ftst();
+ __ fxch(3);
+ __ fld_s(Operand(ebx, ecx, times_4, 10000));
+ __ fstp_s(Operand(ebx, ecx, times_4, 10000));
+ __ ffree(3);
+ __ fld_d(Operand(ebx, ecx, times_4, 10000));
+ __ fstp_d(Operand(ebx, ecx, times_4, 10000));
+ __ nop();
+
+ __ fild_s(Operand(ebx, ecx, times_4, 10000));
+ __ fistp_s(Operand(ebx, ecx, times_4, 10000));
+ __ fild_d(Operand(ebx, ecx, times_4, 10000));
+ __ fistp_d(Operand(ebx, ecx, times_4, 10000));
+ __ fnstsw_ax();
+ __ nop();
+ __ fadd(3);
+ __ fsub(3);
+ __ fmul(3);
+ __ fdiv(3);
+
+ __ faddp(3);
+ __ fsubp(3);
+ __ fmulp(3);
+ __ fdivp(3);
+ __ fcompp();
+ __ fwait();
+ __ nop();
+ {
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::SSE2));
+ CpuFeatures::Scope fscope(CpuFeatures::SSE2);
+ __ cvttss2si(edx, Operand(ebx, ecx, times_4, 10000));
+ __ cvtsi2sd(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ addsd(xmm1, xmm0);
+ __ mulsd(xmm1, xmm0);
+ __ subsd(xmm1, xmm0);
+ __ divsd(xmm1, xmm0);
+ __ movdbl(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ movdbl(Operand(ebx, ecx, times_4, 10000), xmm1);
+ }
+ __ ret(0);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+ byte* begin = Code::cast(code)->instruction_start();
+ byte* end = begin + Code::cast(code)->instruction_size();
+ disasm::Disassembler::Disassemble(stdout, begin, end);
+#endif
+}
+
+#undef __
diff --git a/v8/test/cctest/test-flags.cc b/v8/test/cctest/test-flags.cc
new file mode 100644
index 0000000..9019a89
--- /dev/null
+++ b/v8/test/cctest/test-flags.cc
@@ -0,0 +1,234 @@
+// Copyright 2006-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 "v8.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+// This test must be executed first!
+TEST(Default) {
+ CHECK(FLAG_testing_bool_flag);
+ CHECK_EQ(13, FLAG_testing_int_flag);
+ CHECK_EQ(2.5, FLAG_testing_float_flag);
+ CHECK_EQ(0, strcmp(FLAG_testing_string_flag, "Hello, world!"));
+}
+
+
+static void SetFlagsToDefault() {
+ FlagList::ResetAllFlags();
+ TestDefault();
+}
+
+
+TEST(Flags1) {
+ FlagList::PrintHelp();
+}
+
+
+TEST(Flags2) {
+ SetFlagsToDefault();
+ int argc = 7;
+ const char* argv[] = { "Test2", "-notesting-bool-flag", "notaflag",
+ "--testing_int_flag=77", "-testing_float_flag=.25",
+ "--testing_string_flag", "no way!" };
+ CHECK_EQ(0, FlagList::SetFlagsFromCommandLine(&argc,
+ const_cast<char **>(argv),
+ false));
+ CHECK_EQ(7, argc);
+ CHECK(!FLAG_testing_bool_flag);
+ CHECK_EQ(77, FLAG_testing_int_flag);
+ CHECK_EQ(.25, FLAG_testing_float_flag);
+ CHECK_EQ(0, strcmp(FLAG_testing_string_flag, "no way!"));
+}
+
+
+TEST(Flags2b) {
+ SetFlagsToDefault();
+ const char* str =
+ " -notesting-bool-flag notaflag --testing_int_flag=77 "
+ "-testing_float_flag=.25 "
+ "--testing_string_flag no_way! ";
+ CHECK_EQ(0, FlagList::SetFlagsFromString(str, strlen(str)));
+ CHECK(!FLAG_testing_bool_flag);
+ CHECK_EQ(77, FLAG_testing_int_flag);
+ CHECK_EQ(.25, FLAG_testing_float_flag);
+ CHECK_EQ(0, strcmp(FLAG_testing_string_flag, "no_way!"));
+}
+
+
+TEST(Flags3) {
+ SetFlagsToDefault();
+ int argc = 8;
+ const char* argv[] =
+ { "Test3", "--testing_bool_flag", "notaflag",
+ "--testing_int_flag", "-666",
+ "--testing_float_flag", "-12E10", "-testing-string-flag=foo-bar" };
+ CHECK_EQ(0, FlagList::SetFlagsFromCommandLine(&argc,
+ const_cast<char **>(argv),
+ true));
+ CHECK_EQ(2, argc);
+ CHECK(FLAG_testing_bool_flag);
+ CHECK_EQ(-666, FLAG_testing_int_flag);
+ CHECK_EQ(-12E10, FLAG_testing_float_flag);
+ CHECK_EQ(0, strcmp(FLAG_testing_string_flag, "foo-bar"));
+}
+
+
+TEST(Flags3b) {
+ SetFlagsToDefault();
+ const char* str =
+ "--testing_bool_flag notaflag --testing_int_flag -666 "
+ "--testing_float_flag -12E10 "
+ "-testing-string-flag=foo-bar";
+ CHECK_EQ(0, FlagList::SetFlagsFromString(str, strlen(str)));
+ CHECK(FLAG_testing_bool_flag);
+ CHECK_EQ(-666, FLAG_testing_int_flag);
+ CHECK_EQ(-12E10, FLAG_testing_float_flag);
+ CHECK_EQ(0, strcmp(FLAG_testing_string_flag, "foo-bar"));
+}
+
+
+TEST(Flags4) {
+ SetFlagsToDefault();
+ int argc = 3;
+ const char* argv[] = { "Test4", "--testing_bool_flag", "--foo" };
+ CHECK_EQ(0, FlagList::SetFlagsFromCommandLine(&argc,
+ const_cast<char **>(argv),
+ true));
+ CHECK_EQ(2, argc);
+}
+
+
+TEST(Flags4b) {
+ SetFlagsToDefault();
+ const char* str = "--testing_bool_flag --foo";
+ CHECK_EQ(2, FlagList::SetFlagsFromString(str, strlen(str)));
+}
+
+
+TEST(Flags5) {
+ SetFlagsToDefault();
+ int argc = 2;
+ const char* argv[] = { "Test5", "--testing_int_flag=\"foobar\"" };
+ CHECK_EQ(1, FlagList::SetFlagsFromCommandLine(&argc,
+ const_cast<char **>(argv),
+ true));
+ CHECK_EQ(2, argc);
+}
+
+
+TEST(Flags5b) {
+ SetFlagsToDefault();
+ const char* str = " --testing_int_flag=\"foobar\"";
+ CHECK_EQ(1, FlagList::SetFlagsFromString(str, strlen(str)));
+}
+
+
+TEST(Flags6) {
+ SetFlagsToDefault();
+ int argc = 4;
+ const char* argv[] = { "Test5", "--testing-int-flag", "0",
+ "--testing_float_flag" };
+ CHECK_EQ(3, FlagList::SetFlagsFromCommandLine(&argc,
+ const_cast<char **>(argv),
+ true));
+ CHECK_EQ(4, argc);
+}
+
+
+TEST(Flags6b) {
+ SetFlagsToDefault();
+ const char* str = " --testing-int-flag 0 --testing_float_flag ";
+ CHECK_EQ(3, FlagList::SetFlagsFromString(str, strlen(str)));
+}
+
+
+TEST(FlagsJSArguments1) {
+ SetFlagsToDefault();
+ int argc = 6;
+ const char* argv[] = {"TestJSArgs1",
+ "--testing-int-flag", "42",
+ "--", "testing-float-flag", "7"};
+ CHECK_EQ(0, FlagList::SetFlagsFromCommandLine(&argc,
+ const_cast<char **>(argv),
+ true));
+ CHECK_EQ(42, FLAG_testing_int_flag);
+ CHECK_EQ(2.5, FLAG_testing_float_flag);
+ CHECK_EQ(2, FLAG_js_arguments.argc());
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[0], "testing-float-flag"));
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[1], "7"));
+ CHECK_EQ(1, argc);
+}
+
+
+TEST(FlagsJSArguments1b) {
+ SetFlagsToDefault();
+ const char* str = "--testing-int-flag 42 -- testing-float-flag 7";
+ CHECK_EQ(0, FlagList::SetFlagsFromString(str, strlen(str)));
+ CHECK_EQ(42, FLAG_testing_int_flag);
+ CHECK_EQ(2.5, FLAG_testing_float_flag);
+ CHECK_EQ(2, FLAG_js_arguments.argc());
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[0], "testing-float-flag"));
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[1], "7"));
+}
+
+
+TEST(FlagsJSArguments2) {
+ SetFlagsToDefault();
+ const char* str = "--testing-int-flag 42 --js-arguments testing-float-flag 7";
+ CHECK_EQ(0, FlagList::SetFlagsFromString(str, strlen(str)));
+ CHECK_EQ(42, FLAG_testing_int_flag);
+ CHECK_EQ(2.5, FLAG_testing_float_flag);
+ CHECK_EQ(2, FLAG_js_arguments.argc());
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[0], "testing-float-flag"));
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[1], "7"));
+}
+
+
+TEST(FlagsJSArguments3) {
+ SetFlagsToDefault();
+ const char* str = "--testing-int-flag 42 --js-arguments=testing-float-flag 7";
+ CHECK_EQ(0, FlagList::SetFlagsFromString(str, strlen(str)));
+ CHECK_EQ(42, FLAG_testing_int_flag);
+ CHECK_EQ(2.5, FLAG_testing_float_flag);
+ CHECK_EQ(2, FLAG_js_arguments.argc());
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[0], "testing-float-flag"));
+ CHECK_EQ(0, strcmp(FLAG_js_arguments[1], "7"));
+}
+
+
+TEST(FlagsJSArguments4) {
+ SetFlagsToDefault();
+ const char* str = "--testing-int-flag 42 --";
+ CHECK_EQ(0, FlagList::SetFlagsFromString(str, strlen(str)));
+ CHECK_EQ(42, FLAG_testing_int_flag);
+ CHECK_EQ(0, FLAG_js_arguments.argc());
+}
+
diff --git a/v8/test/cctest/test-hashmap.cc b/v8/test/cctest/test-hashmap.cc
new file mode 100644
index 0000000..4918d5d
--- /dev/null
+++ b/v8/test/cctest/test-hashmap.cc
@@ -0,0 +1,123 @@
+// Copyright 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 "v8.h"
+#include "hashmap.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static bool DefaultMatchFun(void* a, void* b) {
+ return a == b;
+}
+
+
+class IntSet {
+ public:
+ IntSet() : map_(DefaultMatchFun) {}
+
+ void Insert(int x) {
+ ASSERT(x != 0); // 0 corresponds to (void*)NULL - illegal key value
+ HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), Hash(x), true);
+ CHECK(p != NULL); // insert is set!
+ CHECK_EQ(reinterpret_cast<void*>(x), p->key);
+ // we don't care about p->value
+ }
+
+ bool Present(int x) {
+ HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), Hash(x), false);
+ if (p != NULL) {
+ CHECK_EQ(reinterpret_cast<void*>(x), p->key);
+ }
+ return p != NULL;
+ }
+
+ void Clear() {
+ map_.Clear();
+ }
+
+ uint32_t occupancy() const {
+ uint32_t count = 0;
+ for (HashMap::Entry* p = map_.Start(); p != NULL; p = map_.Next(p)) {
+ count++;
+ }
+ CHECK_EQ(map_.occupancy(), static_cast<double>(count));
+ return count;
+ }
+
+ private:
+ HashMap map_;
+ static uint32_t Hash(uint32_t key) { return key * 23; }
+};
+
+
+TEST(Set) {
+ IntSet set;
+ CHECK_EQ(0, set.occupancy());
+
+ set.Insert(1);
+ set.Insert(2);
+ set.Insert(3);
+ CHECK_EQ(3, set.occupancy());
+
+ set.Insert(2);
+ set.Insert(3);
+ CHECK_EQ(3, set.occupancy());
+
+ CHECK(set.Present(1));
+ CHECK(set.Present(2));
+ CHECK(set.Present(3));
+ CHECK(!set.Present(4));
+ CHECK_EQ(3, set.occupancy());
+
+ set.Clear();
+ CHECK_EQ(0, set.occupancy());
+
+ // Insert a long series of values.
+ const int start = 453;
+ const int factor = 13;
+ const int offset = 7;
+ const uint32_t n = 1000;
+
+ int x = start;
+ for (uint32_t i = 0; i < n; i++) {
+ CHECK_EQ(i, static_cast<double>(set.occupancy()));
+ set.Insert(x);
+ x = x*factor + offset;
+ }
+
+ // Verify the same sequence of values.
+ x = start;
+ for (uint32_t i = 0; i < n; i++) {
+ CHECK(set.Present(x));
+ x = x*factor + offset;
+ }
+
+ CHECK_EQ(n, static_cast<double>(set.occupancy()));
+}
diff --git a/v8/test/cctest/test-heap.cc b/v8/test/cctest/test-heap.cc
new file mode 100644
index 0000000..e35ac5f
--- /dev/null
+++ b/v8/test/cctest/test-heap.cc
@@ -0,0 +1,785 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "execution.h"
+#include "factory.h"
+#include "macro-assembler.h"
+#include "global-handles.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static v8::Persistent<v8::Context> env;
+
+static void InitializeVM() {
+ if (env.IsEmpty()) env = v8::Context::New();
+ v8::HandleScope scope;
+ env->Enter();
+}
+
+
+static void CheckMap(Map* map, int type, int instance_size) {
+ CHECK(map->IsHeapObject());
+#ifdef DEBUG
+ CHECK(Heap::Contains(map));
+#endif
+ CHECK_EQ(Heap::meta_map(), map->map());
+ CHECK_EQ(type, map->instance_type());
+ CHECK_EQ(instance_size, map->instance_size());
+}
+
+
+TEST(HeapMaps) {
+ InitializeVM();
+ CheckMap(Heap::meta_map(), MAP_TYPE, Map::kSize);
+ CheckMap(Heap::heap_number_map(), HEAP_NUMBER_TYPE, HeapNumber::kSize);
+ CheckMap(Heap::fixed_array_map(), FIXED_ARRAY_TYPE, Array::kHeaderSize);
+ CheckMap(Heap::long_string_map(), LONG_STRING_TYPE,
+ SeqTwoByteString::kHeaderSize);
+}
+
+
+static void CheckOddball(Object* obj, const char* string) {
+ CHECK(obj->IsOddball());
+ bool exc;
+ Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc);
+ CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
+}
+
+
+static void CheckSmi(int value, const char* string) {
+ bool exc;
+ Object* print_string =
+ *Execution::ToString(Handle<Object>(Smi::FromInt(value)), &exc);
+ CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
+}
+
+
+static void CheckNumber(double value, const char* string) {
+ Object* obj = Heap::NumberFromDouble(value);
+ CHECK(obj->IsNumber());
+ bool exc;
+ Object* print_string = *Execution::ToString(Handle<Object>(obj), &exc);
+ CHECK(String::cast(print_string)->IsEqualTo(CStrVector(string)));
+}
+
+
+static void CheckFindCodeObject() {
+ // Test FindCodeObject
+#define __ assm.
+
+ Assembler assm(NULL, 0);
+
+ __ nop(); // supported on all architectures
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+
+ HeapObject* obj = HeapObject::cast(code);
+ Address obj_addr = obj->address();
+
+ for (int i = 0; i < obj->Size(); i += kPointerSize) {
+ Object* found = Heap::FindCodeObject(obj_addr + i);
+ CHECK_EQ(code, found);
+ }
+
+ Object* copy = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(copy->IsCode());
+ HeapObject* obj_copy = HeapObject::cast(copy);
+ Object* not_right = Heap::FindCodeObject(obj_copy->address() +
+ obj_copy->Size() / 2);
+ CHECK(not_right != code);
+}
+
+
+TEST(HeapObjects) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ Object* value = Heap::NumberFromDouble(1.000123);
+ CHECK(value->IsHeapNumber());
+ CHECK(value->IsNumber());
+ CHECK_EQ(1.000123, value->Number());
+
+ value = Heap::NumberFromDouble(1.0);
+ CHECK(value->IsSmi());
+ CHECK(value->IsNumber());
+ CHECK_EQ(1.0, value->Number());
+
+ value = Heap::NumberFromInt32(1024);
+ CHECK(value->IsSmi());
+ CHECK(value->IsNumber());
+ CHECK_EQ(1024.0, value->Number());
+
+ value = Heap::NumberFromInt32(Smi::kMinValue);
+ CHECK(value->IsSmi());
+ CHECK(value->IsNumber());
+ CHECK_EQ(Smi::kMinValue, Smi::cast(value)->value());
+
+ value = Heap::NumberFromInt32(Smi::kMaxValue);
+ CHECK(value->IsSmi());
+ CHECK(value->IsNumber());
+ CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value());
+
+ value = Heap::NumberFromInt32(Smi::kMinValue - 1);
+ CHECK(value->IsHeapNumber());
+ CHECK(value->IsNumber());
+ CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
+
+ value = Heap::NumberFromInt32(Smi::kMaxValue + 1);
+ CHECK(value->IsHeapNumber());
+ CHECK(value->IsNumber());
+ CHECK_EQ(static_cast<double>(Smi::kMaxValue + 1), value->Number());
+
+ // nan oddball checks
+ CHECK(Heap::nan_value()->IsNumber());
+ CHECK(isnan(Heap::nan_value()->Number()));
+
+ Object* str = Heap::AllocateStringFromAscii(CStrVector("fisk hest "));
+ if (!str->IsFailure()) {
+ String* s = String::cast(str);
+ CHECK(s->IsString());
+ CHECK_EQ(10, s->length());
+ } else {
+ CHECK(false);
+ }
+
+ String* object_symbol = String::cast(Heap::Object_symbol());
+ CHECK(Top::context()->global()->HasLocalProperty(object_symbol));
+
+ // Check ToString for oddballs
+ CheckOddball(Heap::true_value(), "true");
+ CheckOddball(Heap::false_value(), "false");
+ CheckOddball(Heap::null_value(), "null");
+ CheckOddball(Heap::undefined_value(), "undefined");
+
+ // Check ToString for Smis
+ CheckSmi(0, "0");
+ CheckSmi(42, "42");
+ CheckSmi(-42, "-42");
+
+ // Check ToString for Numbers
+ CheckNumber(1.1, "1.1");
+
+ CheckFindCodeObject();
+}
+
+
+TEST(Tagging) {
+ InitializeVM();
+ CHECK(Smi::FromInt(42)->IsSmi());
+ CHECK(Failure::RetryAfterGC(12, NEW_SPACE)->IsFailure());
+ CHECK_EQ(12, Failure::RetryAfterGC(12, NEW_SPACE)->requested());
+ CHECK_EQ(NEW_SPACE, Failure::RetryAfterGC(12, NEW_SPACE)->allocation_space());
+ CHECK_EQ(OLD_POINTER_SPACE,
+ Failure::RetryAfterGC(12, OLD_POINTER_SPACE)->allocation_space());
+ CHECK(Failure::Exception()->IsFailure());
+ CHECK(Smi::FromInt(Smi::kMinValue)->IsSmi());
+ CHECK(Smi::FromInt(Smi::kMaxValue)->IsSmi());
+}
+
+
+TEST(GarbageCollection) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ // check GC when heap is empty
+ int free_bytes = Heap::MaxHeapObjectSize();
+ CHECK(Heap::CollectGarbage(free_bytes, NEW_SPACE));
+
+ // allocate a function and keep it in global object's property
+ String* func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
+ SharedFunctionInfo* function_share =
+ SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(func_name));
+ JSFunction* function =
+ JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
+ function_share,
+ Heap::undefined_value()));
+ Map* initial_map =
+ Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
+ function->set_initial_map(initial_map);
+ Top::context()->global()->SetProperty(func_name, function, NONE);
+
+ // allocate an object, but it is unrooted
+ String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ String* prop_namex = String::cast(Heap::LookupAsciiSymbol("theSlotx"));
+ JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
+ obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
+ obj->SetProperty(prop_namex, Smi::FromInt(24), NONE);
+
+ CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
+ CHECK_EQ(Smi::FromInt(24), obj->GetProperty(prop_namex));
+
+ CHECK(Heap::CollectGarbage(free_bytes, NEW_SPACE));
+
+ // function should be alive, func_name might be invalid after GC
+ func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
+ CHECK(Top::context()->global()->HasLocalProperty(func_name));
+ // check function is retained
+ Object* func_value = Top::context()->global()->GetProperty(func_name);
+ CHECK(func_value->IsJSFunction());
+ // old function pointer may not be valid
+ function = JSFunction::cast(func_value);
+
+ // allocate another object, make it reachable from global
+ obj = JSObject::cast(Heap::AllocateJSObject(function));
+ String* obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
+ Top::context()->global()->SetProperty(obj_name, obj, NONE);
+ // set property
+ prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
+
+ // after gc, it should survive
+ CHECK(Heap::CollectGarbage(free_bytes, NEW_SPACE));
+
+ obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
+ CHECK(Top::context()->global()->HasLocalProperty(obj_name));
+ CHECK(Top::context()->global()->GetProperty(obj_name)->IsJSObject());
+ obj = JSObject::cast(Top::context()->global()->GetProperty(obj_name));
+ prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
+}
+
+
+static void VerifyStringAllocation(const char* string) {
+ String* s = String::cast(Heap::AllocateStringFromUtf8(CStrVector(string)));
+ CHECK_EQ(static_cast<int>(strlen(string)), s->length());
+ for (int index = 0; index < s->length(); index++) {
+ CHECK_EQ(static_cast<uint16_t>(string[index]), s->Get(index)); }
+}
+
+
+TEST(String) {
+ InitializeVM();
+
+ VerifyStringAllocation("a");
+ VerifyStringAllocation("ab");
+ VerifyStringAllocation("abc");
+ VerifyStringAllocation("abcd");
+ VerifyStringAllocation("fiskerdrengen er paa havet");
+}
+
+
+TEST(LocalHandles) {
+ InitializeVM();
+
+ v8::HandleScope scope;
+ const char* name = "Kasper the spunky";
+ Handle<String> string = Factory::NewStringFromAscii(CStrVector(name));
+ CHECK_EQ(static_cast<int>(strlen(name)), string->length());
+}
+
+
+TEST(GlobalHandles) {
+ InitializeVM();
+
+ Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
+ Object* u = Heap::AllocateHeapNumber(1.12344);
+
+ Handle<Object> h1 = GlobalHandles::Create(i);
+ Handle<Object> h2 = GlobalHandles::Create(u);
+ Handle<Object> h3 = GlobalHandles::Create(i);
+ Handle<Object> h4 = GlobalHandles::Create(u);
+
+ // after gc, it should survive
+ CHECK(Heap::CollectGarbage(0, NEW_SPACE));
+
+ CHECK((*h1)->IsString());
+ CHECK((*h2)->IsHeapNumber());
+ CHECK((*h3)->IsString());
+ CHECK((*h4)->IsHeapNumber());
+
+ CHECK_EQ(*h3, *h1);
+ GlobalHandles::Destroy(h1.location());
+ GlobalHandles::Destroy(h3.location());
+
+ CHECK_EQ(*h4, *h2);
+ GlobalHandles::Destroy(h2.location());
+ GlobalHandles::Destroy(h4.location());
+}
+
+
+static bool WeakPointerCleared = false;
+
+static void TestWeakGlobalHandleCallback(v8::Persistent<v8::Value> handle,
+ void* id) {
+ USE(handle);
+ if (1234 == reinterpret_cast<int>(id)) WeakPointerCleared = true;
+}
+
+
+TEST(WeakGlobalHandlesScavenge) {
+ InitializeVM();
+
+ WeakPointerCleared = false;
+
+ Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
+ Object* u = Heap::AllocateHeapNumber(1.12344);
+
+ Handle<Object> h1 = GlobalHandles::Create(i);
+ Handle<Object> h2 = GlobalHandles::Create(u);
+
+ GlobalHandles::MakeWeak(h2.location(),
+ reinterpret_cast<void*>(1234),
+ &TestWeakGlobalHandleCallback);
+
+ // Scavenge treats weak pointers as normal roots.
+ Heap::PerformScavenge();
+
+ CHECK((*h1)->IsString());
+ CHECK((*h2)->IsHeapNumber());
+
+ CHECK(!WeakPointerCleared);
+ CHECK(!GlobalHandles::IsNearDeath(h2.location()));
+ CHECK(!GlobalHandles::IsNearDeath(h1.location()));
+
+ GlobalHandles::Destroy(h1.location());
+ GlobalHandles::Destroy(h2.location());
+}
+
+
+TEST(WeakGlobalHandlesMark) {
+ InitializeVM();
+
+ WeakPointerCleared = false;
+
+ Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
+ Object* u = Heap::AllocateHeapNumber(1.12344);
+
+ Handle<Object> h1 = GlobalHandles::Create(i);
+ Handle<Object> h2 = GlobalHandles::Create(u);
+
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+ CHECK(Heap::CollectGarbage(0, NEW_SPACE));
+ // Make sure the object is promoted.
+
+ GlobalHandles::MakeWeak(h2.location(),
+ reinterpret_cast<void*>(1234),
+ &TestWeakGlobalHandleCallback);
+ CHECK(!GlobalHandles::IsNearDeath(h1.location()));
+ CHECK(!GlobalHandles::IsNearDeath(h2.location()));
+
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ CHECK((*h1)->IsString());
+
+ CHECK(WeakPointerCleared);
+ CHECK(!GlobalHandles::IsNearDeath(h1.location()));
+ CHECK(GlobalHandles::IsNearDeath(h2.location()));
+
+ GlobalHandles::Destroy(h1.location());
+ GlobalHandles::Destroy(h2.location());
+}
+
+static void TestDeleteWeakGlobalHandleCallback(
+ v8::Persistent<v8::Value> handle,
+ void* id) {
+ if (1234 == reinterpret_cast<int>(id)) WeakPointerCleared = true;
+ handle.Dispose();
+}
+
+TEST(DeleteWeakGlobalHandle) {
+ InitializeVM();
+
+ WeakPointerCleared = false;
+
+ Object* i = Heap::AllocateStringFromAscii(CStrVector("fisk"));
+ Handle<Object> h = GlobalHandles::Create(i);
+
+ GlobalHandles::MakeWeak(h.location(),
+ reinterpret_cast<void*>(1234),
+ &TestDeleteWeakGlobalHandleCallback);
+
+ // Scanvenge does not recognize weak reference.
+ Heap::PerformScavenge();
+
+ CHECK(!WeakPointerCleared);
+
+ // Mark-compact treats weak reference properly.
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ CHECK(WeakPointerCleared);
+}
+
+static const char* not_so_random_string_table[] = {
+ "abstract",
+ "boolean",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "const",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "export",
+ "extends",
+ "false",
+ "final",
+ "finally",
+ "float",
+ "for",
+ "function",
+ "goto",
+ "if",
+ "implements",
+ "import",
+ "in",
+ "instanceof",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "new",
+ "null",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "return",
+ "short",
+ "static",
+ "super",
+ "switch",
+ "synchronized",
+ "this",
+ "throw",
+ "throws",
+ "transient",
+ "true",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "volatile",
+ "while",
+ "with",
+ 0
+};
+
+
+static void CheckSymbols(const char** strings) {
+ for (const char* string = *strings; *strings != 0; string = *strings++) {
+ Object* a = Heap::LookupAsciiSymbol(string);
+ CHECK(a->IsSymbol());
+ Object* b = Heap::LookupAsciiSymbol(string);
+ CHECK_EQ(b, a);
+ CHECK(String::cast(b)->IsEqualTo(CStrVector(string)));
+ }
+}
+
+
+TEST(SymbolTable) {
+ InitializeVM();
+
+ CheckSymbols(not_so_random_string_table);
+ CheckSymbols(not_so_random_string_table);
+}
+
+
+TEST(FunctionAllocation) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ String* name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
+ SharedFunctionInfo* function_share =
+ SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(name));
+ JSFunction* function =
+ JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
+ function_share,
+ Heap::undefined_value()));
+ Map* initial_map =
+ Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
+ function->set_initial_map(initial_map);
+
+ String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
+ obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
+ CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
+ // Check that we can add properties to function objects.
+ function->SetProperty(prop_name, Smi::FromInt(24), NONE);
+ CHECK_EQ(Smi::FromInt(24), function->GetProperty(prop_name));
+}
+
+
+TEST(ObjectProperties) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ JSFunction* constructor =
+ JSFunction::cast(
+ Top::context()->global()->GetProperty(String::cast(
+ Heap::Object_symbol())));
+ JSObject* obj = JSObject::cast(Heap::AllocateJSObject(constructor));
+ String* first = String::cast(Heap::LookupAsciiSymbol("first"));
+ String* second = String::cast(Heap::LookupAsciiSymbol("second"));
+
+ // check for empty
+ CHECK(!obj->HasLocalProperty(first));
+
+ // add first
+ obj->SetProperty(first, Smi::FromInt(1), NONE);
+ CHECK(obj->HasLocalProperty(first));
+
+ // delete first
+ CHECK(obj->DeleteProperty(first));
+ CHECK(!obj->HasLocalProperty(first));
+
+ // add first and then second
+ obj->SetProperty(first, Smi::FromInt(1), NONE);
+ obj->SetProperty(second, Smi::FromInt(2), NONE);
+ CHECK(obj->HasLocalProperty(first));
+ CHECK(obj->HasLocalProperty(second));
+
+ // delete first and then second
+ CHECK(obj->DeleteProperty(first));
+ CHECK(obj->HasLocalProperty(second));
+ CHECK(obj->DeleteProperty(second));
+ CHECK(!obj->HasLocalProperty(first));
+ CHECK(!obj->HasLocalProperty(second));
+
+ // add first and then second
+ obj->SetProperty(first, Smi::FromInt(1), NONE);
+ obj->SetProperty(second, Smi::FromInt(2), NONE);
+ CHECK(obj->HasLocalProperty(first));
+ CHECK(obj->HasLocalProperty(second));
+
+ // delete second and then first
+ CHECK(obj->DeleteProperty(second));
+ CHECK(obj->HasLocalProperty(first));
+ CHECK(obj->DeleteProperty(first));
+ CHECK(!obj->HasLocalProperty(first));
+ CHECK(!obj->HasLocalProperty(second));
+
+ // check string and symbol match
+ static const char* string1 = "fisk";
+ String* s1 =
+ String::cast(Heap::AllocateStringFromAscii(CStrVector(string1)));
+ obj->SetProperty(s1, Smi::FromInt(1), NONE);
+ CHECK(obj->HasLocalProperty(String::cast(Heap::LookupAsciiSymbol(string1))));
+
+ // check symbol and string match
+ static const char* string2 = "fugl";
+ String* s2 = String::cast(Heap::LookupAsciiSymbol(string2));
+ obj->SetProperty(s2, Smi::FromInt(1), NONE);
+ CHECK(obj->HasLocalProperty(
+ String::cast(Heap::AllocateStringFromAscii(CStrVector(string2)))));
+}
+
+
+TEST(JSObjectMaps) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ String* name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
+ SharedFunctionInfo* function_share =
+ SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(name));
+ JSFunction* function =
+ JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
+ function_share,
+ Heap::undefined_value()));
+ Map* initial_map =
+ Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
+ function->set_initial_map(initial_map);
+ String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
+
+ // Set a propery
+ obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
+ CHECK_EQ(Smi::FromInt(23), obj->GetProperty(prop_name));
+
+ // Check the map has changed
+ CHECK(initial_map != obj->map());
+}
+
+
+TEST(JSArray) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ String* name = String::cast(Heap::LookupAsciiSymbol("Array"));
+ JSFunction* function =
+ JSFunction::cast(Top::context()->global()->GetProperty(name));
+
+ // Allocate the object.
+ JSArray* array = JSArray::cast(Heap::AllocateJSObject(function));
+ array->Initialize(0);
+
+ // Set array length to 0.
+ array->SetElementsLength(Smi::FromInt(0));
+ CHECK_EQ(Smi::FromInt(0), array->length());
+ CHECK(array->HasFastElements()); // Must be in fast mode.
+
+ // array[length] = name.
+ array->SetElement(0, name);
+ CHECK_EQ(Smi::FromInt(1), array->length());
+ CHECK_EQ(array->GetElement(0), name);
+
+ // Set array length with larger than smi value.
+ Object* length = Heap::NumberFromInt32(Smi::kMaxValue + 1);
+ array->SetElementsLength(length);
+
+ uint32_t int_length = 0;
+ CHECK(Array::IndexFromObject(length, &int_length));
+ CHECK_EQ(length, array->length());
+ CHECK(!array->HasFastElements()); // Must be in slow mode.
+
+ // array[length] = name.
+ array->SetElement(int_length, name);
+ uint32_t new_int_length = 0;
+ CHECK(Array::IndexFromObject(array->length(), &new_int_length));
+ CHECK_EQ(static_cast<double>(int_length), new_int_length - 1);
+ CHECK_EQ(array->GetElement(int_length), name);
+ CHECK_EQ(array->GetElement(0), name);
+}
+
+
+TEST(JSObjectCopy) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ String* name = String::cast(Heap::Object_symbol());
+ JSFunction* constructor =
+ JSFunction::cast(Top::context()->global()->GetProperty(name));
+ JSObject* obj = JSObject::cast(Heap::AllocateJSObject(constructor));
+ String* first = String::cast(Heap::LookupAsciiSymbol("first"));
+ String* second = String::cast(Heap::LookupAsciiSymbol("second"));
+
+ obj->SetProperty(first, Smi::FromInt(1), NONE);
+ obj->SetProperty(second, Smi::FromInt(2), NONE);
+
+ obj->SetElement(0, first);
+ obj->SetElement(1, second);
+
+ // Make the clone.
+ JSObject* clone = JSObject::cast(Heap::CopyJSObject(obj));
+ CHECK(clone != obj);
+
+ CHECK_EQ(obj->GetElement(0), clone->GetElement(0));
+ CHECK_EQ(obj->GetElement(1), clone->GetElement(1));
+
+ CHECK_EQ(obj->GetProperty(first), clone->GetProperty(first));
+ CHECK_EQ(obj->GetProperty(second), clone->GetProperty(second));
+
+ // Flip the values.
+ clone->SetProperty(first, Smi::FromInt(2), NONE);
+ clone->SetProperty(second, Smi::FromInt(1), NONE);
+
+ clone->SetElement(0, second);
+ clone->SetElement(1, first);
+
+ CHECK_EQ(obj->GetElement(1), clone->GetElement(0));
+ CHECK_EQ(obj->GetElement(0), clone->GetElement(1));
+
+ CHECK_EQ(obj->GetProperty(second), clone->GetProperty(first));
+ CHECK_EQ(obj->GetProperty(first), clone->GetProperty(second));
+}
+
+
+TEST(StringAllocation) {
+ InitializeVM();
+
+
+ const unsigned char chars[] = { 0xe5, 0xa4, 0xa7 };
+ for (int length = 0; length < 100; length++) {
+ v8::HandleScope scope;
+ char* non_ascii = NewArray<char>(3 * length + 1);
+ char* ascii = NewArray<char>(length + 1);
+ non_ascii[3 * length] = 0;
+ ascii[length] = 0;
+ for (int i = 0; i < length; i++) {
+ ascii[i] = 'a';
+ non_ascii[3 * i] = chars[0];
+ non_ascii[3 * i + 1] = chars[1];
+ non_ascii[3 * i + 2] = chars[2];
+ }
+ Handle<String> non_ascii_sym =
+ Factory::LookupSymbol(Vector<const char>(non_ascii, 3 * length));
+ CHECK_EQ(length, non_ascii_sym->length());
+ Handle<String> ascii_sym =
+ Factory::LookupSymbol(Vector<const char>(ascii, length));
+ CHECK_EQ(length, ascii_sym->length());
+ Handle<String> non_ascii_str =
+ Factory::NewStringFromUtf8(Vector<const char>(non_ascii, 3 * length));
+ non_ascii_str->Hash();
+ CHECK_EQ(length, non_ascii_str->length());
+ Handle<String> ascii_str =
+ Factory::NewStringFromUtf8(Vector<const char>(ascii, length));
+ ascii_str->Hash();
+ CHECK_EQ(length, ascii_str->length());
+ DeleteArray(non_ascii);
+ DeleteArray(ascii);
+ }
+}
+
+
+static int ObjectsFoundInHeap(Handle<Object> objs[], int size) {
+ // Count the number of objects found in the heap.
+ int found_count = 0;
+ HeapIterator iterator;
+ while (iterator.has_next()) {
+ HeapObject* obj = iterator.next();
+ CHECK(obj != NULL);
+ for (int i = 0; i < size; i++) {
+ if (*objs[i] == obj) {
+ found_count++;
+ }
+ }
+ }
+ CHECK(!iterator.has_next());
+ return found_count;
+}
+
+
+TEST(Iteration) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ // Array of objects to scan haep for.
+ const int objs_count = 6;
+ Handle<Object> objs[objs_count];
+ int next_objs_index = 0;
+
+ // Allocate a JS array to OLD_POINTER_SPACE and NEW_SPACE
+ objs[next_objs_index++] = Factory::NewJSArray(10);
+ objs[next_objs_index++] = Factory::NewJSArray(10, TENURED);
+
+ // Allocate a small string to OLD_DATA_SPACE and NEW_SPACE
+ objs[next_objs_index++] =
+ Factory::NewStringFromAscii(CStrVector("abcdefghij"));
+ objs[next_objs_index++] =
+ Factory::NewStringFromAscii(CStrVector("abcdefghij"), TENURED);
+
+ // Allocate a large string (for large object space).
+ int large_size = Heap::MaxHeapObjectSize() + 1;
+ char* str = new char[large_size];
+ for (int i = 0; i < large_size - 1; ++i) str[i] = 'a';
+ str[large_size - 1] = '\0';
+ objs[next_objs_index++] =
+ Factory::NewStringFromAscii(CStrVector(str), TENURED);
+ delete[] str;
+
+ // Add a Map object to look for.
+ objs[next_objs_index++] = Handle<Map>(HeapObject::cast(*objs[0])->map());
+
+ CHECK_EQ(objs_count, next_objs_index);
+ CHECK_EQ(objs_count, ObjectsFoundInHeap(objs, objs_count));
+}
diff --git a/v8/test/cctest/test-list.cc b/v8/test/cctest/test-list.cc
new file mode 100644
index 0000000..d10cdd7
--- /dev/null
+++ b/v8/test/cctest/test-list.cc
@@ -0,0 +1,67 @@
+// Copyright 2009 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 <string.h>
+#include "v8.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+// Use a testing allocator that clears memory before deletion.
+class ZeroingAllocationPolicy {
+ public:
+ static void* New(size_t size) {
+ // Stash the size in the first word to use for Delete.
+ size_t true_size = size + sizeof(size_t);
+ size_t* result = reinterpret_cast<size_t*>(malloc(true_size));
+ if (result == NULL) return result;
+ *result = true_size;
+ return result + 1;
+ }
+
+ static void Delete(void* ptr) {
+ size_t* true_ptr = reinterpret_cast<size_t*>(ptr) - 1;
+ memset(true_ptr, 0, *true_ptr);
+ free(true_ptr);
+ }
+};
+
+// Check that we can add (a reference to) an element of the list
+// itself.
+TEST(ListAdd) {
+ // Add elements to the list to grow it to its capacity.
+ List<int, ZeroingAllocationPolicy> list(4);
+ list.Add(1);
+ list.Add(2);
+ list.Add(3);
+ list.Add(4);
+
+ // Add an existing element, the backing store should have to grow.
+ list.Add(list[0]);
+ ASSERT(list[4] == 1);
+}
diff --git a/v8/test/cctest/test-lock.cc b/v8/test/cctest/test-lock.cc
new file mode 100644
index 0000000..5eecfce
--- /dev/null
+++ b/v8/test/cctest/test-lock.cc
@@ -0,0 +1,63 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+//
+// Tests of the TokenLock class from lock.h
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "platform.h"
+#include "cctest.h"
+
+
+using namespace ::v8::internal;
+
+
+// Simple test of locking logic
+TEST(Simple) {
+ Mutex* mutex = OS::CreateMutex();
+ CHECK_EQ(0, mutex->Lock()); // acquire the lock with the right token
+ CHECK_EQ(0, mutex->Unlock()); // can unlock with the right token
+ delete mutex;
+}
+
+
+TEST(MultiLock) {
+ Mutex* mutex = OS::CreateMutex();
+ CHECK_EQ(0, mutex->Lock());
+ CHECK_EQ(0, mutex->Unlock());
+ delete mutex;
+}
+
+
+TEST(ShallowLock) {
+ Mutex* mutex = OS::CreateMutex();
+ CHECK_EQ(0, mutex->Lock());
+ CHECK_EQ(0, mutex->Unlock());
+ CHECK_EQ(0, mutex->Lock());
+ CHECK_EQ(0, mutex->Unlock());
+ delete mutex;
+}
+
+
+TEST(SemaphoreTimeout) {
+ bool ok;
+ Semaphore* sem = OS::CreateSemaphore(0);
+
+ // Semaphore not signalled - timeout.
+ ok = sem->Wait(0);
+ CHECK(!ok);
+ ok = sem->Wait(100);
+ CHECK(!ok);
+ ok = sem->Wait(1000);
+ CHECK(!ok);
+
+ // Semaphore signalled - no timeout.
+ sem->Signal();
+ ok = sem->Wait(0);
+ sem->Signal();
+ ok = sem->Wait(100);
+ sem->Signal();
+ ok = sem->Wait(1000);
+ CHECK(ok);
+}
diff --git a/v8/test/cctest/test-log-ia32.cc b/v8/test/cctest/test-log-ia32.cc
new file mode 100644
index 0000000..b171339
--- /dev/null
+++ b/v8/test/cctest/test-log-ia32.cc
@@ -0,0 +1,314 @@
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+//
+// Tests of profiler-related functions from log.h
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "log.h"
+#include "top.h"
+#include "cctest.h"
+
+using v8::Function;
+using v8::Local;
+using v8::Object;
+using v8::Script;
+using v8::String;
+using v8::Value;
+
+using v8::internal::byte;
+using v8::internal::Handle;
+using v8::internal::JSFunction;
+using v8::internal::StackTracer;
+using v8::internal::TickSample;
+using v8::internal::Top;
+
+
+static v8::Persistent<v8::Context> env;
+
+
+static struct {
+ StackTracer* tracer;
+ TickSample* sample;
+} trace_env = { NULL, NULL };
+
+
+static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
+ trace_env.tracer = tracer;
+ trace_env.sample = sample;
+}
+
+
+static void DoTrace(unsigned int fp) {
+ trace_env.sample->fp = fp;
+ // sp is only used to define stack high bound
+ trace_env.sample->sp =
+ reinterpret_cast<unsigned int>(trace_env.sample) - 10240;
+ trace_env.tracer->Trace(trace_env.sample);
+}
+
+
+// Hide c_entry_fp to emulate situation when sampling is done while
+// pure JS code is being executed
+static void DoTraceHideCEntryFPAddress(unsigned int fp) {
+ v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
+ CHECK(saved_c_frame_fp);
+ *(Top::c_entry_fp_address()) = 0;
+ DoTrace(fp);
+ *(Top::c_entry_fp_address()) = saved_c_frame_fp;
+}
+
+
+static void CheckRetAddrIsInFunction(const char* func_name,
+ unsigned int ret_addr,
+ unsigned int func_start_addr,
+ unsigned int func_len) {
+ printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\n",
+ func_name, func_start_addr, ret_addr, func_start_addr + func_len);
+ CHECK_GE(ret_addr, func_start_addr);
+ CHECK_GE(func_start_addr + func_len, ret_addr);
+}
+
+
+static void CheckRetAddrIsInJSFunction(const char* func_name,
+ unsigned int ret_addr,
+ Handle<JSFunction> func) {
+ v8::internal::Code* func_code = func->code();
+ CheckRetAddrIsInFunction(
+ func_name, ret_addr,
+ reinterpret_cast<unsigned int>(func_code->instruction_start()),
+ func_code->ExecutableSize());
+}
+
+
+// --- T r a c e E x t e n s i o n ---
+
+class TraceExtension : public v8::Extension {
+ public:
+ TraceExtension() : v8::Extension("v8/trace", kSource) { }
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<String> name);
+ static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
+ private:
+ static unsigned int GetFP(const v8::Arguments& args);
+ static const char* kSource;
+};
+
+
+const char* TraceExtension::kSource =
+ "native function trace();"
+ "native function js_trace();";
+
+
+v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
+ v8::Handle<String> name) {
+ if (name->Equals(String::New("trace"))) {
+ return v8::FunctionTemplate::New(TraceExtension::Trace);
+ } else if (name->Equals(String::New("js_trace"))) {
+ return v8::FunctionTemplate::New(TraceExtension::JSTrace);
+ } else {
+ CHECK(false);
+ return v8::Handle<v8::FunctionTemplate>();
+ }
+}
+
+
+unsigned int TraceExtension::GetFP(const v8::Arguments& args) {
+ CHECK_EQ(1, args.Length());
+ unsigned int fp = args[0]->Int32Value() << 2;
+ printf("Trace: %08x\n", fp);
+ return fp;
+}
+
+
+v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
+ DoTrace(GetFP(args));
+ return v8::Undefined();
+}
+
+
+v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
+ DoTraceHideCEntryFPAddress(GetFP(args));
+ return v8::Undefined();
+}
+
+
+static TraceExtension kTraceExtension;
+v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
+
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ v8::HandleScope scope;
+ const char* extensions[] = { "v8/trace" };
+ v8::ExtensionConfiguration config(1, extensions);
+ env = v8::Context::New(&config);
+ }
+ v8::HandleScope scope;
+ env->Enter();
+}
+
+
+static Handle<JSFunction> CompileFunction(const char* source) {
+ return v8::Utils::OpenHandle(*Script::Compile(String::New(source)));
+}
+
+
+static void CompileRun(const char* source) {
+ Script::Compile(String::New(source))->Run();
+}
+
+
+static Local<Value> GetGlobalProperty(const char* name) {
+ return env->Global()->Get(String::New(name));
+}
+
+
+static Handle<JSFunction> GetGlobalJSFunction(const char* name) {
+ Handle<JSFunction> js_func(JSFunction::cast(
+ *(v8::Utils::OpenHandle(
+ *GetGlobalProperty(name)))));
+ return js_func;
+}
+
+
+static void CheckRetAddrIsInJSFunction(const char* func_name,
+ unsigned int ret_addr) {
+ CheckRetAddrIsInJSFunction(func_name, ret_addr,
+ GetGlobalJSFunction(func_name));
+}
+
+
+static void SetGlobalProperty(const char* name, Local<Value> value) {
+ env->Global()->Set(String::New(name), value);
+}
+
+
+static bool Patch(byte* from,
+ size_t num,
+ byte* original,
+ byte* patch,
+ size_t patch_len) {
+ byte* to = from + num;
+ do {
+ from = static_cast<byte*>(memchr(from, *original, to - from));
+ CHECK(from != NULL);
+ if (memcmp(original, from, patch_len) == 0) {
+ memcpy(from, patch, patch_len);
+ return true;
+ } else {
+ from++;
+ }
+ } while (to - from > 0);
+ return false;
+}
+
+
+// Creates a global function named 'func_name' that calls the tracing
+// function 'trace_func_name' with an actual EBP register value,
+// shifted right to be presented as Smi.
+static void CreateTraceCallerFunction(const char* func_name,
+ const char* trace_func_name) {
+ ::v8::internal::EmbeddedVector<char, 256> trace_call_buf;
+ ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name);
+ Handle<JSFunction> func = CompileFunction(trace_call_buf.start());
+ CHECK(!func.is_null());
+ v8::internal::Code* func_code = func->code();
+ CHECK(func_code->IsCode());
+
+ // push 0xcccc (= 0x6666 << 1)
+ byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 };
+ // mov eax,ebp; shr eax; push eax;
+ byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 };
+ // Patch generated code to replace pushing of a constant with
+ // pushing of ebp contents in a Smi
+ CHECK(Patch(func_code->instruction_start(),
+ func_code->instruction_size(),
+ original, patch, sizeof(patch)));
+
+ SetGlobalProperty(func_name, v8::ToApi<Value>(func));
+}
+
+
+TEST(CFromJSStackTrace) {
+ TickSample sample;
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
+ InitTraceEnv(&tracer, &sample);
+
+ InitializeVM();
+ v8::HandleScope scope;
+ CreateTraceCallerFunction("JSFuncDoTrace", "trace");
+ CompileRun(
+ "function JSTrace() {"
+ " JSFuncDoTrace();"
+ "};\n"
+ "JSTrace();");
+ CHECK_GT(sample.frames_count, 1);
+ // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
+ CheckRetAddrIsInJSFunction("JSFuncDoTrace",
+ reinterpret_cast<unsigned int>(sample.stack[0]));
+ CheckRetAddrIsInJSFunction("JSTrace",
+ reinterpret_cast<unsigned int>(sample.stack[1]));
+}
+
+
+TEST(PureJSStackTrace) {
+ TickSample sample;
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
+ InitTraceEnv(&tracer, &sample);
+
+ InitializeVM();
+ v8::HandleScope scope;
+ CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
+ CompileRun(
+ "function JSTrace() {"
+ " JSFuncDoTrace();"
+ "};\n"
+ "function OuterJSTrace() {"
+ " JSTrace();"
+ "};\n"
+ "OuterJSTrace();");
+ CHECK_GT(sample.frames_count, 1);
+ // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
+ CheckRetAddrIsInJSFunction("JSTrace",
+ reinterpret_cast<unsigned int>(sample.stack[0]));
+ CheckRetAddrIsInJSFunction("OuterJSTrace",
+ reinterpret_cast<unsigned int>(sample.stack[1]));
+}
+
+
+static void CFuncDoTrace() {
+ unsigned int fp;
+#ifdef __GNUC__
+ fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0));
+#elif defined _MSC_VER
+ __asm mov [fp], ebp // NOLINT
+#endif
+ DoTrace(fp);
+}
+
+
+static int CFunc(int depth) {
+ if (depth <= 0) {
+ CFuncDoTrace();
+ return 0;
+ } else {
+ return CFunc(depth - 1) + 1;
+ }
+}
+
+
+TEST(PureCStackTrace) {
+ TickSample sample;
+ StackTracer tracer(reinterpret_cast<unsigned int>(&sample));
+ InitTraceEnv(&tracer, &sample);
+ // Check that sampler doesn't crash
+ CHECK_EQ(10, CFunc(10));
+}
+
+
+#endif // ENABLE_LOGGING_AND_PROFILING
diff --git a/v8/test/cctest/test-mark-compact.cc b/v8/test/cctest/test-mark-compact.cc
new file mode 100644
index 0000000..53cff68
--- /dev/null
+++ b/v8/test/cctest/test-mark-compact.cc
@@ -0,0 +1,314 @@
+// Copyright 2006-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 "v8.h"
+
+#include "global-handles.h"
+#include "snapshot.h"
+#include "top.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static v8::Persistent<v8::Context> env;
+
+static void InitializeVM() {
+ if (env.IsEmpty()) env = v8::Context::New();
+ v8::HandleScope scope;
+ env->Enter();
+}
+
+
+TEST(MarkingStack) {
+ int mem_size = 20 * kPointerSize;
+ byte* mem = NewArray<byte>(20*kPointerSize);
+ Address low = reinterpret_cast<Address>(mem);
+ Address high = low + mem_size;
+ MarkingStack s;
+ s.Initialize(low, high);
+
+ Address address = NULL;
+ while (!s.is_full()) {
+ s.Push(HeapObject::FromAddress(address));
+ address += kPointerSize;
+ }
+
+ while (!s.is_empty()) {
+ Address value = s.Pop()->address();
+ address -= kPointerSize;
+ CHECK_EQ(address, value);
+ }
+
+ CHECK_EQ(NULL, address);
+ DeleteArray(mem);
+}
+
+
+TEST(Promotion) {
+ // Test the situation that some objects in new space are promoted to the
+ // old space
+ if (Snapshot::IsEnabled()) return;
+
+ // Ensure that we get a compacting collection so that objects are promoted
+ // from new space.
+ FLAG_gc_global = true;
+ FLAG_always_compact = true;
+ Heap::ConfigureHeap(2*256*KB, 4*MB);
+
+ InitializeVM();
+
+ v8::HandleScope sc;
+
+ // Allocate a fixed array in the new space.
+ int array_size =
+ (Heap::MaxHeapObjectSize() - Array::kHeaderSize) / (kPointerSize * 4);
+ Object* obj = Heap::AllocateFixedArray(array_size);
+ CHECK(!obj->IsFailure());
+
+ Handle<FixedArray> array(FixedArray::cast(obj));
+
+ // Array should be in the new space.
+ CHECK(Heap::InSpace(*array, NEW_SPACE));
+
+ // Call the m-c collector, so array becomes an old object.
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ // Array now sits in the old space
+ CHECK(Heap::InSpace(*array, OLD_POINTER_SPACE));
+}
+
+
+TEST(NoPromotion) {
+ if (Snapshot::IsEnabled()) return;
+ Heap::ConfigureHeap(2*256*KB, 4*MB);
+
+ // Test the situation that some objects in new space are promoted to
+ // the old space
+ InitializeVM();
+
+ v8::HandleScope sc;
+
+ // Do a mark compact GC to shrink the heap.
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ // Allocate a big Fixed array in the new space.
+ int size = (Heap::MaxHeapObjectSize() - Array::kHeaderSize) / kPointerSize;
+ Object* obj = Heap::AllocateFixedArray(size);
+
+ Handle<FixedArray> array(FixedArray::cast(obj));
+
+ // Array still stays in the new space.
+ CHECK(Heap::InSpace(*array, NEW_SPACE));
+
+ // Allocate objects in the old space until out of memory.
+ FixedArray* host = *array;
+ while (true) {
+ Object* obj = Heap::AllocateFixedArray(100, TENURED);
+ if (obj->IsFailure()) break;
+
+ host->set(0, obj);
+ host = FixedArray::cast(obj);
+ }
+
+ // Call mark compact GC, and it should pass.
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ // array should not be promoted because the old space is full.
+ CHECK(Heap::InSpace(*array, NEW_SPACE));
+}
+
+
+TEST(MarkCompactCollector) {
+ InitializeVM();
+
+ v8::HandleScope sc;
+ // call mark-compact when heap is empty
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ // keep allocating garbage in new space until it fails
+ const int ARRAY_SIZE = 100;
+ Object* array;
+ do {
+ array = Heap::AllocateFixedArray(ARRAY_SIZE);
+ } while (!array->IsFailure());
+ CHECK(Heap::CollectGarbage(0, NEW_SPACE));
+
+ array = Heap::AllocateFixedArray(ARRAY_SIZE);
+ CHECK(!array->IsFailure());
+
+ // keep allocating maps until it fails
+ Object* mapp;
+ do {
+ mapp = Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ } while (!mapp->IsFailure());
+ CHECK(Heap::CollectGarbage(0, MAP_SPACE));
+ mapp = Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ CHECK(!mapp->IsFailure());
+
+ // allocate a garbage
+ String* func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
+ SharedFunctionInfo* function_share =
+ SharedFunctionInfo::cast(Heap::AllocateSharedFunctionInfo(func_name));
+ JSFunction* function =
+ JSFunction::cast(Heap::AllocateFunction(*Top::function_map(),
+ function_share,
+ Heap::undefined_value()));
+ Map* initial_map =
+ Map::cast(Heap::AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize));
+ function->set_initial_map(initial_map);
+ Top::context()->global()->SetProperty(func_name, function, NONE);
+
+ JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function));
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ func_name = String::cast(Heap::LookupAsciiSymbol("theFunction"));
+ CHECK(Top::context()->global()->HasLocalProperty(func_name));
+ Object* func_value = Top::context()->global()->GetProperty(func_name);
+ CHECK(func_value->IsJSFunction());
+ function = JSFunction::cast(func_value);
+
+ obj = JSObject::cast(Heap::AllocateJSObject(function));
+ String* obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
+ Top::context()->global()->SetProperty(obj_name, obj, NONE);
+ String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ obj->SetProperty(prop_name, Smi::FromInt(23), NONE);
+
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ obj_name = String::cast(Heap::LookupAsciiSymbol("theObject"));
+ CHECK(Top::context()->global()->HasLocalProperty(obj_name));
+ CHECK(Top::context()->global()->GetProperty(obj_name)->IsJSObject());
+ obj = JSObject::cast(Top::context()->global()->GetProperty(obj_name));
+ prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot"));
+ CHECK(obj->GetProperty(prop_name) == Smi::FromInt(23));
+}
+
+
+static int gc_starts = 0;
+static int gc_ends = 0;
+
+static void GCPrologueCallbackFunc() {
+ CHECK(gc_starts == gc_ends);
+ gc_starts++;
+}
+
+
+static void GCEpilogueCallbackFunc() {
+ CHECK(gc_starts == gc_ends + 1);
+ gc_ends++;
+}
+
+
+TEST(GCCallback) {
+ InitializeVM();
+
+ Heap::SetGlobalGCPrologueCallback(&GCPrologueCallbackFunc);
+ Heap::SetGlobalGCEpilogueCallback(&GCEpilogueCallbackFunc);
+
+ // Scavenge does not call GC callback functions.
+ Heap::PerformScavenge();
+
+ CHECK_EQ(0, gc_starts);
+ CHECK_EQ(gc_ends, gc_starts);
+
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+ CHECK_EQ(1, gc_starts);
+ CHECK_EQ(gc_ends, gc_starts);
+}
+
+
+static int NumberOfWeakCalls = 0;
+static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
+ NumberOfWeakCalls++;
+}
+
+TEST(ObjectGroups) {
+ InitializeVM();
+
+ NumberOfWeakCalls = 0;
+ v8::HandleScope handle_scope;
+
+ Handle<Object> g1s1 =
+ GlobalHandles::Create(Heap::AllocateFixedArray(1));
+ Handle<Object> g1s2 =
+ GlobalHandles::Create(Heap::AllocateFixedArray(1));
+ GlobalHandles::MakeWeak(g1s1.location(),
+ reinterpret_cast<void*>(1234),
+ &WeakPointerCallback);
+ GlobalHandles::MakeWeak(g1s2.location(),
+ reinterpret_cast<void*>(1234),
+ &WeakPointerCallback);
+
+ Handle<Object> g2s1 =
+ GlobalHandles::Create(Heap::AllocateFixedArray(1));
+ Handle<Object> g2s2 =
+ GlobalHandles::Create(Heap::AllocateFixedArray(1));
+ GlobalHandles::MakeWeak(g2s1.location(),
+ reinterpret_cast<void*>(1234),
+ &WeakPointerCallback);
+ GlobalHandles::MakeWeak(g2s2.location(),
+ reinterpret_cast<void*>(1234),
+ &WeakPointerCallback);
+
+ Handle<Object> root = GlobalHandles::Create(*g1s1); // make a root.
+
+ // Connect group 1 and 2, make a cycle.
+ Handle<FixedArray>::cast(g1s2)->set(0, *g2s2);
+ Handle<FixedArray>::cast(g2s1)->set(0, *g1s1);
+
+ {
+ Object** g1_objects[] = { g1s1.location(), g1s2.location() };
+ Object** g2_objects[] = { g2s1.location(), g2s2.location() };
+ GlobalHandles::AddGroup(g1_objects, 2);
+ GlobalHandles::AddGroup(g2_objects, 2);
+ }
+ // Do a full GC
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ // All object should be alive.
+ CHECK_EQ(0, NumberOfWeakCalls);
+
+ // Weaken the root.
+ GlobalHandles::MakeWeak(root.location(),
+ reinterpret_cast<void*>(1234),
+ &WeakPointerCallback);
+
+ // Groups are deleted, rebuild groups.
+ {
+ Object** g1_objects[] = { g1s1.location(), g1s2.location() };
+ Object** g2_objects[] = { g2s1.location(), g2s2.location() };
+ GlobalHandles::AddGroup(g1_objects, 2);
+ GlobalHandles::AddGroup(g2_objects, 2);
+ }
+
+ CHECK(Heap::CollectGarbage(0, OLD_POINTER_SPACE));
+
+ // All objects should be gone. 5 global handles in total.
+ CHECK_EQ(5, NumberOfWeakCalls);
+}
diff --git a/v8/test/cctest/test-platform-linux.cc b/v8/test/cctest/test-platform-linux.cc
new file mode 100644
index 0000000..e1a00e1
--- /dev/null
+++ b/v8/test/cctest/test-platform-linux.cc
@@ -0,0 +1,80 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+//
+// Tests of the TokenLock class from lock.h
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h> // for usleep()
+
+#include "v8.h"
+
+#include "platform.h"
+#include "cctest.h"
+
+using namespace ::v8::internal;
+
+
+static void yield() {
+ usleep(1);
+}
+
+static const int kLockCounterLimit = 50;
+static int busy_lock_counter = 0;
+
+
+static void LoopIncrement(Mutex* mutex, int rem) {
+ while (true) {
+ int count = 0;
+ int last_count = -1;
+ do {
+ CHECK_EQ(0, mutex->Lock());
+ count = busy_lock_counter;
+ CHECK_EQ(0, mutex->Unlock());
+ yield();
+ } while (count % 2 == rem && count < kLockCounterLimit);
+ if (count >= kLockCounterLimit) break;
+ CHECK_EQ(0, mutex->Lock());
+ CHECK_EQ(count, busy_lock_counter);
+ CHECK(last_count == -1 || count == last_count + 1);
+ busy_lock_counter++;
+ last_count = count;
+ CHECK_EQ(0, mutex->Unlock());
+ yield();
+ }
+}
+
+
+static void* RunTestBusyLock(void* arg) {
+ LoopIncrement(static_cast<Mutex*>(arg), 0);
+ return 0;
+}
+
+
+// Runs two threads that repeatedly acquire the lock and conditionally
+// increment a variable.
+TEST(BusyLock) {
+ pthread_t other;
+ Mutex* mutex = OS::CreateMutex();
+ int thread_created = pthread_create(&other,
+ NULL,
+ &RunTestBusyLock,
+ mutex);
+ CHECK_EQ(0, thread_created);
+ LoopIncrement(mutex, 1);
+ pthread_join(other, NULL);
+ delete mutex;
+}
+
+
+TEST(VirtualMemory) {
+ VirtualMemory* vm = new VirtualMemory(1 * MB);
+ CHECK(vm->IsReserved());
+ void* block_addr = vm->address();
+ size_t block_size = 4 * KB;
+ CHECK(vm->Commit(block_addr, block_size, false));
+ // Check whether we can write to memory.
+ int* addr = static_cast<int*>(block_addr);
+ addr[KB-1] = 2;
+ CHECK(vm->Uncommit(block_addr, block_size));
+ delete vm;
+}
diff --git a/v8/test/cctest/test-platform-macos.cc b/v8/test/cctest/test-platform-macos.cc
new file mode 100644
index 0000000..d80fa54
--- /dev/null
+++ b/v8/test/cctest/test-platform-macos.cc
@@ -0,0 +1,10 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+//
+// Tests of the TokenLock class from lock.h
+
+#include <stdlib.h>
+
+#include "v8.h"
+#include "cctest.h"
+
+using namespace ::v8::internal;
diff --git a/v8/test/cctest/test-platform-nullos.cc b/v8/test/cctest/test-platform-nullos.cc
new file mode 100644
index 0000000..c0d6ae5
--- /dev/null
+++ b/v8/test/cctest/test-platform-nullos.cc
@@ -0,0 +1,80 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+//
+// Tests of the TokenLock class from lock.h
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h> // for usleep()
+
+#include "v8.h"
+
+#include "platform.h"
+#include "cctest.h"
+
+using namespace ::v8::internal;
+
+
+static void yield() {
+ UNIMPLEMENTED();
+}
+
+static const int kLockCounterLimit = 50;
+static int busy_lock_counter = 0;
+
+
+static void LoopIncrement(Mutex* mutex, int rem) {
+ while (true) {
+ int count = 0;
+ int last_count = -1;
+ do {
+ CHECK_EQ(0, mutex->Lock());
+ count = busy_lock_counter;
+ CHECK_EQ(0, mutex->Unlock());
+ yield();
+ } while (count % 2 == rem && count < kLockCounterLimit);
+ if (count >= kLockCounterLimit) break;
+ CHECK_EQ(0, mutex->Lock());
+ CHECK_EQ(count, busy_lock_counter);
+ CHECK(last_count == -1 || count == last_count + 1);
+ busy_lock_counter++;
+ last_count = count;
+ CHECK_EQ(0, mutex->Unlock());
+ yield();
+ }
+}
+
+
+static void* RunTestBusyLock(void* arg) {
+ LoopIncrement(static_cast<Mutex*>(arg), 0);
+ return 0;
+}
+
+
+// Runs two threads that repeatedly acquire the lock and conditionally
+// increment a variable.
+TEST(BusyLock) {
+ pthread_t other;
+ Mutex* mutex = OS::CreateMutex();
+ int thread_created = pthread_create(&other,
+ NULL,
+ &RunTestBusyLock,
+ mutex);
+ CHECK_EQ(0, thread_created);
+ LoopIncrement(mutex, 1);
+ pthread_join(other, NULL);
+ delete mutex;
+}
+
+
+TEST(VirtualMemory) {
+ VirtualMemory* vm = new VirtualMemory(1 * MB);
+ CHECK(vm->IsReserved());
+ void* block_addr = vm->address();
+ size_t block_size = 4 * KB;
+ CHECK(vm->Commit(block_addr, block_size, false));
+ // Check whether we can write to memory.
+ int* addr = static_cast<int*>(block_addr);
+ addr[KB-1] = 2;
+ CHECK(vm->Uncommit(block_addr, block_size));
+ delete vm;
+}
diff --git a/v8/test/cctest/test-platform-win32.cc b/v8/test/cctest/test-platform-win32.cc
new file mode 100644
index 0000000..a5a6dd5
--- /dev/null
+++ b/v8/test/cctest/test-platform-win32.cc
@@ -0,0 +1,26 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+//
+// Tests of the TokenLock class from lock.h
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "platform.h"
+#include "cctest.h"
+
+using namespace ::v8::internal;
+
+
+TEST(VirtualMemory) {
+ VirtualMemory* vm = new VirtualMemory(1 * MB);
+ CHECK(vm->IsReserved());
+ void* block_addr = vm->address();
+ size_t block_size = 4 * KB;
+ CHECK(vm->Commit(block_addr, block_size, false));
+ // Check whether we can write to memory.
+ int* addr = static_cast<int*>(block_addr);
+ addr[KB-1] = 2;
+ CHECK(vm->Uncommit(block_addr, block_size));
+ delete vm;
+}
diff --git a/v8/test/cctest/test-regexp.cc b/v8/test/cctest/test-regexp.cc
new file mode 100644
index 0000000..afd9679
--- /dev/null
+++ b/v8/test/cctest/test-regexp.cc
@@ -0,0 +1,1536 @@
+// Copyright 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 <set>
+
+#include "v8.h"
+
+#include "string-stream.h"
+#include "cctest.h"
+#include "zone-inl.h"
+#include "parser.h"
+#include "ast.h"
+#include "jsregexp-inl.h"
+#include "regexp-macro-assembler.h"
+#include "regexp-macro-assembler-irregexp.h"
+#ifdef ARM
+#include "regexp-macro-assembler-arm.h"
+#else // IA32
+#include "macro-assembler-ia32.h"
+#include "regexp-macro-assembler-ia32.h"
+#endif
+#include "interpreter-irregexp.h"
+
+
+using namespace v8::internal;
+
+
+static SmartPointer<const char> Parse(const char* input) {
+ V8::Initialize(NULL);
+ v8::HandleScope scope;
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ FlatStringReader reader(CStrVector(input));
+ RegExpCompileData result;
+ CHECK(v8::internal::ParseRegExp(&reader, false, &result));
+ CHECK(result.tree != NULL);
+ CHECK(result.error.is_null());
+ SmartPointer<const char> output = result.tree->ToString();
+ return output;
+}
+
+static bool CheckSimple(const char* input) {
+ V8::Initialize(NULL);
+ v8::HandleScope scope;
+ unibrow::Utf8InputBuffer<> buffer(input, strlen(input));
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ FlatStringReader reader(CStrVector(input));
+ RegExpCompileData result;
+ CHECK(v8::internal::ParseRegExp(&reader, false, &result));
+ CHECK(result.tree != NULL);
+ CHECK(result.error.is_null());
+ return result.simple;
+}
+
+struct MinMaxPair {
+ int min_match;
+ int max_match;
+};
+
+static MinMaxPair CheckMinMaxMatch(const char* input) {
+ V8::Initialize(NULL);
+ v8::HandleScope scope;
+ unibrow::Utf8InputBuffer<> buffer(input, strlen(input));
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ FlatStringReader reader(CStrVector(input));
+ RegExpCompileData result;
+ CHECK(v8::internal::ParseRegExp(&reader, false, &result));
+ CHECK(result.tree != NULL);
+ CHECK(result.error.is_null());
+ int min_match = result.tree->min_match();
+ int max_match = result.tree->max_match();
+ MinMaxPair pair = { min_match, max_match };
+ return pair;
+}
+
+
+
+#define CHECK_PARSE_EQ(input, expected) CHECK_EQ(expected, *Parse(input))
+#define CHECK_SIMPLE(input, simple) CHECK_EQ(simple, CheckSimple(input));
+#define CHECK_MIN_MAX(input, min, max) \
+ { MinMaxPair min_max = CheckMinMaxMatch(input); \
+ CHECK_EQ(min, min_max.min_match); \
+ CHECK_EQ(max, min_max.max_match); \
+ }
+
+TEST(Parser) {
+ V8::Initialize(NULL);
+ CHECK_PARSE_EQ("abc", "'abc'");
+ CHECK_PARSE_EQ("", "%");
+ CHECK_PARSE_EQ("abc|def", "(| 'abc' 'def')");
+ CHECK_PARSE_EQ("abc|def|ghi", "(| 'abc' 'def' 'ghi')");
+ CHECK_PARSE_EQ("^xxx$", "(: @^i 'xxx' @$i)");
+ CHECK_PARSE_EQ("ab\\b\\d\\bcd", "(: 'ab' @b [0-9] @b 'cd')");
+ CHECK_PARSE_EQ("\\w|\\d", "(| [0-9 A-Z _ a-z] [0-9])");
+ CHECK_PARSE_EQ("a*", "(# 0 - g 'a')");
+ CHECK_PARSE_EQ("a*?", "(# 0 - n 'a')");
+ CHECK_PARSE_EQ("abc+", "(: 'ab' (# 1 - g 'c'))");
+ CHECK_PARSE_EQ("abc+?", "(: 'ab' (# 1 - n 'c'))");
+ CHECK_PARSE_EQ("xyz?", "(: 'xy' (# 0 1 g 'z'))");
+ CHECK_PARSE_EQ("xyz??", "(: 'xy' (# 0 1 n 'z'))");
+ CHECK_PARSE_EQ("xyz{0,1}", "(: 'xy' (# 0 1 g 'z'))");
+ CHECK_PARSE_EQ("xyz{0,1}?", "(: 'xy' (# 0 1 n 'z'))");
+ CHECK_PARSE_EQ("xyz{93}", "(: 'xy' (# 93 93 g 'z'))");
+ CHECK_PARSE_EQ("xyz{93}?", "(: 'xy' (# 93 93 n 'z'))");
+ CHECK_PARSE_EQ("xyz{1,32}", "(: 'xy' (# 1 32 g 'z'))");
+ CHECK_PARSE_EQ("xyz{1,32}?", "(: 'xy' (# 1 32 n 'z'))");
+ CHECK_PARSE_EQ("xyz{1,}", "(: 'xy' (# 1 - g 'z'))");
+ CHECK_PARSE_EQ("xyz{1,}?", "(: 'xy' (# 1 - n 'z'))");
+ CHECK_PARSE_EQ("a\\fb\\nc\\rd\\te\\vf", "'a\\x0cb\\x0ac\\x0dd\\x09e\\x0bf'");
+ CHECK_PARSE_EQ("a\\nb\\bc", "(: 'a\\x0ab' @b 'c')");
+ CHECK_PARSE_EQ("(?:foo)", "'foo'");
+ CHECK_PARSE_EQ("(?: foo )", "' foo '");
+ CHECK_PARSE_EQ("(foo|bar|baz)", "(^ (| 'foo' 'bar' 'baz'))");
+ CHECK_PARSE_EQ("foo|(bar|baz)|quux", "(| 'foo' (^ (| 'bar' 'baz')) 'quux')");
+ CHECK_PARSE_EQ("foo(?=bar)baz", "(: 'foo' (-> + 'bar') 'baz')");
+ CHECK_PARSE_EQ("foo(?!bar)baz", "(: 'foo' (-> - 'bar') 'baz')");
+ CHECK_PARSE_EQ("()", "(^ %)");
+ CHECK_PARSE_EQ("(?=)", "(-> + %)");
+ CHECK_PARSE_EQ("[]", "^[\\x00-\\uffff]"); // Doesn't compile on windows
+ CHECK_PARSE_EQ("[^]", "[\\x00-\\uffff]"); // \uffff isn't in codepage 1252
+ CHECK_PARSE_EQ("[x]", "[x]");
+ CHECK_PARSE_EQ("[xyz]", "[x y z]");
+ CHECK_PARSE_EQ("[a-zA-Z0-9]", "[a-z A-Z 0-9]");
+ CHECK_PARSE_EQ("[-123]", "[- 1 2 3]");
+ CHECK_PARSE_EQ("[^123]", "^[1 2 3]");
+ CHECK_PARSE_EQ("]", "']'");
+ CHECK_PARSE_EQ("}", "'}'");
+ CHECK_PARSE_EQ("[a-b-c]", "[a-b - c]");
+ CHECK_PARSE_EQ("[\\d]", "[0-9]");
+ CHECK_PARSE_EQ("[x\\dz]", "[x 0-9 z]");
+ CHECK_PARSE_EQ("[\\d-z]", "[0-9 - z]");
+ CHECK_PARSE_EQ("[\\d-\\d]", "[0-9 - 0-9]");
+ CHECK_PARSE_EQ("[z-\\d]", "[z - 0-9]");
+ CHECK_PARSE_EQ("\\cj\\cJ\\ci\\cI\\ck\\cK",
+ "'\\x0a\\x0a\\x09\\x09\\x0b\\x0b'");
+ CHECK_PARSE_EQ("\\c!", "'c!'");
+ CHECK_PARSE_EQ("\\c_", "'c_'");
+ CHECK_PARSE_EQ("\\c~", "'c~'");
+ CHECK_PARSE_EQ("[a\\]c]", "[a ] c]");
+ CHECK_PARSE_EQ("\\[\\]\\{\\}\\(\\)\\%\\^\\#\\ ", "'[]{}()%^# '");
+ CHECK_PARSE_EQ("[\\[\\]\\{\\}\\(\\)\\%\\^\\#\\ ]", "[[ ] { } ( ) % ^ # ]");
+ CHECK_PARSE_EQ("\\0", "'\\x00'");
+ CHECK_PARSE_EQ("\\8", "'8'");
+ CHECK_PARSE_EQ("\\9", "'9'");
+ CHECK_PARSE_EQ("\\11", "'\\x09'");
+ CHECK_PARSE_EQ("\\11a", "'\\x09a'");
+ CHECK_PARSE_EQ("\\011", "'\\x09'");
+ CHECK_PARSE_EQ("\\00011", "'\\x0011'");
+ CHECK_PARSE_EQ("\\118", "'\\x098'");
+ CHECK_PARSE_EQ("\\111", "'I'");
+ CHECK_PARSE_EQ("\\1111", "'I1'");
+ CHECK_PARSE_EQ("(x)(x)(x)\\1", "(: (^ 'x') (^ 'x') (^ 'x') (<- 1))");
+ CHECK_PARSE_EQ("(x)(x)(x)\\2", "(: (^ 'x') (^ 'x') (^ 'x') (<- 2))");
+ CHECK_PARSE_EQ("(x)(x)(x)\\3", "(: (^ 'x') (^ 'x') (^ 'x') (<- 3))");
+ CHECK_PARSE_EQ("(x)(x)(x)\\4", "(: (^ 'x') (^ 'x') (^ 'x') '\\x04')");
+ CHECK_PARSE_EQ("(x)(x)(x)\\1*", "(: (^ 'x') (^ 'x') (^ 'x')"
+ " (# 0 - g (<- 1)))");
+ CHECK_PARSE_EQ("(x)(x)(x)\\2*", "(: (^ 'x') (^ 'x') (^ 'x')"
+ " (# 0 - g (<- 2)))");
+ CHECK_PARSE_EQ("(x)(x)(x)\\3*", "(: (^ 'x') (^ 'x') (^ 'x')"
+ " (# 0 - g (<- 3)))");
+ CHECK_PARSE_EQ("(x)(x)(x)\\4*", "(: (^ 'x') (^ 'x') (^ 'x')"
+ " (# 0 - g '\\x04'))");
+ CHECK_PARSE_EQ("(x)(x)(x)(x)(x)(x)(x)(x)(x)(x)\\10",
+ "(: (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x')"
+ " (^ 'x') (^ 'x') (^ 'x') (^ 'x') (<- 10))");
+ CHECK_PARSE_EQ("(x)(x)(x)(x)(x)(x)(x)(x)(x)(x)\\11",
+ "(: (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x') (^ 'x')"
+ " (^ 'x') (^ 'x') (^ 'x') (^ 'x') '\\x09')");
+ CHECK_PARSE_EQ("(a)\\1", "(: (^ 'a') (<- 1))");
+ CHECK_PARSE_EQ("(a\\1)", "(^ 'a')");
+ CHECK_PARSE_EQ("(\\1a)", "(^ 'a')");
+ CHECK_PARSE_EQ("(?=a)?a", "'a'");
+ CHECK_PARSE_EQ("(?=a){0,10}a", "'a'");
+ CHECK_PARSE_EQ("(?=a){1,10}a", "(: (-> + 'a') 'a')");
+ CHECK_PARSE_EQ("(?=a){9,10}a", "(: (-> + 'a') 'a')");
+ CHECK_PARSE_EQ("(?!a)?a", "'a'");
+ CHECK_PARSE_EQ("\\1(a)", "(^ 'a')");
+ CHECK_PARSE_EQ("(?!(a))\\1", "(-> - (^ 'a'))");
+ CHECK_PARSE_EQ("(?!\\1(a\\1)\\1)\\1", "(-> - (: (^ 'a') (<- 1)))");
+ CHECK_PARSE_EQ("[\\0]", "[\\x00]");
+ CHECK_PARSE_EQ("[\\11]", "[\\x09]");
+ CHECK_PARSE_EQ("[\\11a]", "[\\x09 a]");
+ CHECK_PARSE_EQ("[\\011]", "[\\x09]");
+ CHECK_PARSE_EQ("[\\00011]", "[\\x00 1 1]");
+ CHECK_PARSE_EQ("[\\118]", "[\\x09 8]");
+ CHECK_PARSE_EQ("[\\111]", "[I]");
+ CHECK_PARSE_EQ("[\\1111]", "[I 1]");
+ CHECK_PARSE_EQ("\\x34", "'\x34'");
+ CHECK_PARSE_EQ("\\x60", "'\x60'");
+ CHECK_PARSE_EQ("\\x3z", "'x3z'");
+ CHECK_PARSE_EQ("\\c", "'c'");
+ CHECK_PARSE_EQ("\\u0034", "'\x34'");
+ CHECK_PARSE_EQ("\\u003z", "'u003z'");
+ CHECK_PARSE_EQ("foo[z]*", "(: 'foo' (# 0 - g [z]))");
+
+ CHECK_SIMPLE("a", true);
+ CHECK_SIMPLE("a|b", false);
+ CHECK_SIMPLE("a\\n", false);
+ CHECK_SIMPLE("^a", false);
+ CHECK_SIMPLE("a$", false);
+ CHECK_SIMPLE("a\\b!", false);
+ CHECK_SIMPLE("a\\Bb", false);
+ CHECK_SIMPLE("a*", false);
+ CHECK_SIMPLE("a*?", false);
+ CHECK_SIMPLE("a?", false);
+ CHECK_SIMPLE("a??", false);
+ CHECK_SIMPLE("a{0,1}?", false);
+ CHECK_SIMPLE("a{1,1}?", false);
+ CHECK_SIMPLE("a{1,2}?", false);
+ CHECK_SIMPLE("a+?", false);
+ CHECK_SIMPLE("(a)", false);
+ CHECK_SIMPLE("(a)\\1", false);
+ CHECK_SIMPLE("(\\1a)", false);
+ CHECK_SIMPLE("\\1(a)", false);
+ CHECK_SIMPLE("a\\s", false);
+ CHECK_SIMPLE("a\\S", false);
+ CHECK_SIMPLE("a\\d", false);
+ CHECK_SIMPLE("a\\D", false);
+ CHECK_SIMPLE("a\\w", false);
+ CHECK_SIMPLE("a\\W", false);
+ CHECK_SIMPLE("a.", false);
+ CHECK_SIMPLE("a\\q", false);
+ CHECK_SIMPLE("a[a]", false);
+ CHECK_SIMPLE("a[^a]", false);
+ CHECK_SIMPLE("a[a-z]", false);
+ CHECK_SIMPLE("a[\\q]", false);
+ CHECK_SIMPLE("a(?:b)", false);
+ CHECK_SIMPLE("a(?=b)", false);
+ CHECK_SIMPLE("a(?!b)", false);
+ CHECK_SIMPLE("\\x60", false);
+ CHECK_SIMPLE("\\u0060", false);
+ CHECK_SIMPLE("\\cA", false);
+ CHECK_SIMPLE("\\q", false);
+ CHECK_SIMPLE("\\1112", false);
+ CHECK_SIMPLE("\\0", false);
+ CHECK_SIMPLE("(a)\\1", false);
+ CHECK_SIMPLE("(?=a)?a", false);
+ CHECK_SIMPLE("(?!a)?a\\1", false);
+ CHECK_SIMPLE("(?:(?=a))a\\1", false);
+
+ CHECK_PARSE_EQ("a{}", "'a{}'");
+ CHECK_PARSE_EQ("a{,}", "'a{,}'");
+ CHECK_PARSE_EQ("a{", "'a{'");
+ CHECK_PARSE_EQ("a{z}", "'a{z}'");
+ CHECK_PARSE_EQ("a{1z}", "'a{1z}'");
+ CHECK_PARSE_EQ("a{12z}", "'a{12z}'");
+ CHECK_PARSE_EQ("a{12,", "'a{12,'");
+ CHECK_PARSE_EQ("a{12,3b", "'a{12,3b'");
+ CHECK_PARSE_EQ("{}", "'{}'");
+ CHECK_PARSE_EQ("{,}", "'{,}'");
+ CHECK_PARSE_EQ("{", "'{'");
+ CHECK_PARSE_EQ("{z}", "'{z}'");
+ CHECK_PARSE_EQ("{1z}", "'{1z}'");
+ CHECK_PARSE_EQ("{12z}", "'{12z}'");
+ CHECK_PARSE_EQ("{12,", "'{12,'");
+ CHECK_PARSE_EQ("{12,3b", "'{12,3b'");
+
+ CHECK_MIN_MAX("a", 1, 1);
+ CHECK_MIN_MAX("abc", 3, 3);
+ CHECK_MIN_MAX("a[bc]d", 3, 3);
+ CHECK_MIN_MAX("a|bc", 1, 2);
+ CHECK_MIN_MAX("ab|c", 1, 2);
+ CHECK_MIN_MAX("a||bc", 0, 2);
+ CHECK_MIN_MAX("|", 0, 0);
+ CHECK_MIN_MAX("(?:ab)", 2, 2);
+ CHECK_MIN_MAX("(?:ab|cde)", 2, 3);
+ CHECK_MIN_MAX("(?:ab)|cde", 2, 3);
+ CHECK_MIN_MAX("(ab)", 2, 2);
+ CHECK_MIN_MAX("(ab|cde)", 2, 3);
+ CHECK_MIN_MAX("(ab)\\1", 2, 4);
+ CHECK_MIN_MAX("(ab|cde)\\1", 2, 6);
+ CHECK_MIN_MAX("(?:ab)?", 0, 2);
+ CHECK_MIN_MAX("(?:ab)*", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:ab)+", 2, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a?", 0, 1);
+ CHECK_MIN_MAX("a*", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a+", 1, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a??", 0, 1);
+ CHECK_MIN_MAX("a*?", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a+?", 1, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a?)?", 0, 1);
+ CHECK_MIN_MAX("(?:a*)?", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a+)?", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a?)+", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a*)+", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a+)+", 1, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a?)*", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a*)*", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a+)*", 0, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a{0}", 0, 0);
+ CHECK_MIN_MAX("(?:a+){0}", 0, 0);
+ CHECK_MIN_MAX("(?:a+){0,0}", 0, 0);
+ CHECK_MIN_MAX("a*b", 1, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a+b", 2, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a*b|c", 1, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("a+b|c", 1, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:a{5,1000000}){3,1000000}", 15, RegExpTree::kInfinity);
+ CHECK_MIN_MAX("(?:ab){4,7}", 8, 14);
+ CHECK_MIN_MAX("a\\bc", 2, 2);
+ CHECK_MIN_MAX("a\\Bc", 2, 2);
+ CHECK_MIN_MAX("a\\sc", 3, 3);
+ CHECK_MIN_MAX("a\\Sc", 3, 3);
+ CHECK_MIN_MAX("a(?=b)c", 2, 2);
+ CHECK_MIN_MAX("a(?=bbb|bb)c", 2, 2);
+ CHECK_MIN_MAX("a(?!bbb|bb)c", 2, 2);
+}
+
+TEST(ParserRegression) {
+ CHECK_PARSE_EQ("[A-Z$-][x]", "(! [A-Z $ -] [x])");
+ CHECK_PARSE_EQ("a{3,4*}", "(: 'a{3,' (# 0 - g '4') '}')");
+ CHECK_PARSE_EQ("{", "'{'");
+ CHECK_PARSE_EQ("a|", "(| 'a' %)");
+}
+
+static void ExpectError(const char* input,
+ const char* expected) {
+ V8::Initialize(NULL);
+ v8::HandleScope scope;
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ FlatStringReader reader(CStrVector(input));
+ RegExpCompileData result;
+ CHECK_EQ(false, v8::internal::ParseRegExp(&reader, false, &result));
+ CHECK(result.tree == NULL);
+ CHECK(!result.error.is_null());
+ SmartPointer<char> str = result.error->ToCString(ALLOW_NULLS);
+ CHECK_EQ(expected, *str);
+}
+
+
+TEST(Errors) {
+ V8::Initialize(NULL);
+ const char* kEndBackslash = "\\ at end of pattern";
+ ExpectError("\\", kEndBackslash);
+ const char* kUnterminatedGroup = "Unterminated group";
+ ExpectError("(foo", kUnterminatedGroup);
+ const char* kInvalidGroup = "Invalid group";
+ ExpectError("(?", kInvalidGroup);
+ const char* kUnterminatedCharacterClass = "Unterminated character class";
+ ExpectError("[", kUnterminatedCharacterClass);
+ ExpectError("[a-", kUnterminatedCharacterClass);
+ const char* kNothingToRepeat = "Nothing to repeat";
+ ExpectError("*", kNothingToRepeat);
+ ExpectError("?", kNothingToRepeat);
+ ExpectError("+", kNothingToRepeat);
+ ExpectError("{1}", kNothingToRepeat);
+ ExpectError("{1,2}", kNothingToRepeat);
+ ExpectError("{1,}", kNothingToRepeat);
+
+ // Check that we don't allow more than kMaxCapture captures
+ const int kMaxCaptures = 1 << 16; // Must match RegExpParser::kMaxCaptures.
+ const char* kTooManyCaptures = "Too many captures";
+ HeapStringAllocator allocator;
+ StringStream accumulator(&allocator);
+ for (int i = 0; i <= kMaxCaptures; i++) {
+ accumulator.Add("()");
+ }
+ SmartPointer<const char> many_captures(accumulator.ToCString());
+ ExpectError(*many_captures, kTooManyCaptures);
+}
+
+
+static bool IsDigit(uc16 c) {
+ return ('0' <= c && c <= '9');
+}
+
+
+static bool NotDigit(uc16 c) {
+ return !IsDigit(c);
+}
+
+
+static bool IsWhiteSpace(uc16 c) {
+ switch (c) {
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0d:
+ case 0x20:
+ case 0xA0:
+ case 0x2028:
+ case 0x2029:
+ return true;
+ default:
+ return unibrow::Space::Is(c);
+ }
+}
+
+
+static bool NotWhiteSpace(uc16 c) {
+ return !IsWhiteSpace(c);
+}
+
+
+static bool NotWord(uc16 c) {
+ return !IsRegExpWord(c);
+}
+
+
+static void TestCharacterClassEscapes(uc16 c, bool (pred)(uc16 c)) {
+ ZoneScope scope(DELETE_ON_EXIT);
+ ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2);
+ CharacterRange::AddClassEscape(c, ranges);
+ for (unsigned i = 0; i < (1 << 16); i++) {
+ bool in_class = false;
+ for (int j = 0; !in_class && j < ranges->length(); j++) {
+ CharacterRange& range = ranges->at(j);
+ in_class = (range.from() <= i && i <= range.to());
+ }
+ CHECK_EQ(pred(i), in_class);
+ }
+}
+
+
+TEST(CharacterClassEscapes) {
+ TestCharacterClassEscapes('.', IsRegExpNewline);
+ TestCharacterClassEscapes('d', IsDigit);
+ TestCharacterClassEscapes('D', NotDigit);
+ TestCharacterClassEscapes('s', IsWhiteSpace);
+ TestCharacterClassEscapes('S', NotWhiteSpace);
+ TestCharacterClassEscapes('w', IsRegExpWord);
+ TestCharacterClassEscapes('W', NotWord);
+}
+
+
+static RegExpNode* Compile(const char* input, bool multiline, bool is_ascii) {
+ V8::Initialize(NULL);
+ FlatStringReader reader(CStrVector(input));
+ RegExpCompileData compile_data;
+ if (!v8::internal::ParseRegExp(&reader, multiline, &compile_data))
+ return NULL;
+ Handle<String> pattern = Factory::NewStringFromUtf8(CStrVector(input));
+ RegExpEngine::Compile(&compile_data, false, multiline, pattern, is_ascii);
+ return compile_data.node;
+}
+
+
+static void Execute(const char* input,
+ bool multiline,
+ bool is_ascii,
+ bool dot_output = false) {
+ v8::HandleScope scope;
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ RegExpNode* node = Compile(input, multiline, is_ascii);
+ USE(node);
+#ifdef DEBUG
+ if (dot_output) {
+ RegExpEngine::DotPrint(input, node, false);
+ exit(0);
+ }
+#endif // DEBUG
+}
+
+
+class TestConfig {
+ public:
+ typedef int Key;
+ typedef int Value;
+ static const int kNoKey;
+ static const int kNoValue;
+ static inline int Compare(int a, int b) {
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ else
+ return 0;
+ }
+};
+
+
+const int TestConfig::kNoKey = 0;
+const int TestConfig::kNoValue = 0;
+
+
+static int PseudoRandom(int i, int j) {
+ return ~(~((i * 781) ^ (j * 329)));
+}
+
+
+TEST(SplayTreeSimple) {
+ static const int kLimit = 1000;
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ ZoneSplayTree<TestConfig> tree;
+ std::set<int> seen;
+#define CHECK_MAPS_EQUAL() do { \
+ for (int k = 0; k < kLimit; k++) \
+ CHECK_EQ(seen.find(k) != seen.end(), tree.Find(k, &loc)); \
+ } while (false)
+ for (int i = 0; i < 50; i++) {
+ for (int j = 0; j < 50; j++) {
+ int next = PseudoRandom(i, j) % kLimit;
+ if (seen.find(next) != seen.end()) {
+ // We've already seen this one. Check the value and remove
+ // it.
+ ZoneSplayTree<TestConfig>::Locator loc;
+ CHECK(tree.Find(next, &loc));
+ CHECK_EQ(next, loc.key());
+ CHECK_EQ(3 * next, loc.value());
+ tree.Remove(next);
+ seen.erase(next);
+ CHECK_MAPS_EQUAL();
+ } else {
+ // Check that it wasn't there already and then add it.
+ ZoneSplayTree<TestConfig>::Locator loc;
+ CHECK(!tree.Find(next, &loc));
+ CHECK(tree.Insert(next, &loc));
+ CHECK_EQ(next, loc.key());
+ loc.set_value(3 * next);
+ seen.insert(next);
+ CHECK_MAPS_EQUAL();
+ }
+ int val = PseudoRandom(j, i) % kLimit;
+ for (int k = val; k >= 0; k--) {
+ if (seen.find(val) != seen.end()) {
+ ZoneSplayTree<TestConfig>::Locator loc;
+ CHECK(tree.FindGreatestLessThan(val, &loc));
+ CHECK_EQ(loc.key(), val);
+ break;
+ }
+ }
+ val = PseudoRandom(i + j, i - j) % kLimit;
+ for (int k = val; k < kLimit; k++) {
+ if (seen.find(val) != seen.end()) {
+ ZoneSplayTree<TestConfig>::Locator loc;
+ CHECK(tree.FindLeastGreaterThan(val, &loc));
+ CHECK_EQ(loc.key(), val);
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+TEST(DispatchTableConstruction) {
+ // Initialize test data.
+ static const int kLimit = 1000;
+ static const int kRangeCount = 8;
+ static const int kRangeSize = 16;
+ uc16 ranges[kRangeCount][2 * kRangeSize];
+ for (int i = 0; i < kRangeCount; i++) {
+ Vector<uc16> range(ranges[i], 2 * kRangeSize);
+ for (int j = 0; j < 2 * kRangeSize; j++) {
+ range[j] = PseudoRandom(i + 25, j + 87) % kLimit;
+ }
+ range.Sort();
+ for (int j = 1; j < 2 * kRangeSize; j++) {
+ CHECK(range[j-1] <= range[j]);
+ }
+ }
+ // Enter test data into dispatch table.
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ DispatchTable table;
+ for (int i = 0; i < kRangeCount; i++) {
+ uc16* range = ranges[i];
+ for (int j = 0; j < 2 * kRangeSize; j += 2)
+ table.AddRange(CharacterRange(range[j], range[j + 1]), i);
+ }
+ // Check that the table looks as we would expect
+ for (int p = 0; p < kLimit; p++) {
+ OutSet* outs = table.Get(p);
+ for (int j = 0; j < kRangeCount; j++) {
+ uc16* range = ranges[j];
+ bool is_on = false;
+ for (int k = 0; !is_on && (k < 2 * kRangeSize); k += 2)
+ is_on = (range[k] <= p && p <= range[k + 1]);
+ CHECK_EQ(is_on, outs->Get(j));
+ }
+ }
+}
+
+
+TEST(MacroAssembler) {
+ V8::Initialize(NULL);
+ byte codes[1024];
+ RegExpMacroAssemblerIrregexp m(Vector<byte>(codes, 1024));
+ // ^f(o)o.
+ Label fail, fail2, start;
+ uc16 foo_chars[3];
+ foo_chars[0] = 'f';
+ foo_chars[1] = 'o';
+ foo_chars[2] = 'o';
+ Vector<const uc16> foo(foo_chars, 3);
+ m.SetRegister(4, 42);
+ m.PushRegister(4, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.AdvanceRegister(4, 42);
+ m.GoTo(&start);
+ m.Fail();
+ m.Bind(&start);
+ m.PushBacktrack(&fail2);
+ m.CheckCharacters(foo, 0, &fail, true);
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.PushCurrentPosition();
+ m.AdvanceCurrentPosition(3);
+ m.WriteCurrentPositionToRegister(1, 0);
+ m.PopCurrentPosition();
+ m.AdvanceCurrentPosition(1);
+ m.WriteCurrentPositionToRegister(2, 0);
+ m.AdvanceCurrentPosition(1);
+ m.WriteCurrentPositionToRegister(3, 0);
+ m.Succeed();
+
+ m.Bind(&fail);
+ m.Backtrack();
+ m.Succeed();
+
+ m.Bind(&fail2);
+ m.PopRegister(0);
+ m.Fail();
+
+ v8::HandleScope scope;
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector("^f(o)o"));
+ Handle<ByteArray> array = Handle<ByteArray>::cast(m.GetCode(source));
+ int captures[5];
+
+ const uc16 str1[] = {'f', 'o', 'o', 'b', 'a', 'r'};
+ Handle<String> f1_16 =
+ Factory::NewStringFromTwoByte(Vector<const uc16>(str1, 6));
+
+ CHECK(IrregexpInterpreter::Match(array, f1_16, captures, 0));
+ CHECK_EQ(0, captures[0]);
+ CHECK_EQ(3, captures[1]);
+ CHECK_EQ(1, captures[2]);
+ CHECK_EQ(2, captures[3]);
+ CHECK_EQ(84, captures[4]);
+
+ const uc16 str2[] = {'b', 'a', 'r', 'f', 'o', 'o'};
+ Handle<String> f2_16 =
+ Factory::NewStringFromTwoByte(Vector<const uc16>(str2, 6));
+
+ CHECK(!IrregexpInterpreter::Match(array, f2_16, captures, 0));
+ CHECK_EQ(42, captures[0]);
+}
+
+
+#ifndef ARM // IA32 only tests.
+
+class ContextInitializer {
+ public:
+ ContextInitializer() : env_(), scope_(), stack_guard_() {
+ env_ = v8::Context::New();
+ env_->Enter();
+ }
+ ~ContextInitializer() {
+ env_->Exit();
+ env_.Dispose();
+ }
+ private:
+ v8::Persistent<v8::Context> env_;
+ v8::HandleScope scope_;
+ v8::internal::StackGuard stack_guard_;
+};
+
+
+static RegExpMacroAssemblerIA32::Result ExecuteIA32(Code* code,
+ String* input,
+ int start_offset,
+ const byte* input_start,
+ const byte* input_end,
+ int* captures,
+ bool at_start) {
+ return RegExpMacroAssemblerIA32::Execute(
+ code,
+ input,
+ start_offset,
+ input_start,
+ input_end,
+ captures,
+ at_start);
+}
+
+
+TEST(MacroAssemblerIA32Success) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 4);
+
+ m.Succeed();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector(""));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ int captures[4] = {42, 37, 87, 117};
+ Handle<String> input = Factory::NewStringFromAscii(CStrVector("foofoo"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ const byte* start_adr =
+ reinterpret_cast<const byte*>(seq_input->GetCharsAddress());
+
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + seq_input->length(),
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(-1, captures[0]);
+ CHECK_EQ(-1, captures[1]);
+ CHECK_EQ(-1, captures[2]);
+ CHECK_EQ(-1, captures[3]);
+}
+
+
+TEST(MacroAssemblerIA32Simple) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 4);
+
+ uc16 foo_chars[3] = {'f', 'o', 'o'};
+ Vector<const uc16> foo(foo_chars, 3);
+
+ Label fail;
+ m.CheckCharacters(foo, 0, &fail, true);
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.AdvanceCurrentPosition(3);
+ m.WriteCurrentPositionToRegister(1, 0);
+ m.Succeed();
+ m.Bind(&fail);
+ m.Fail();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector("^foo"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ int captures[4] = {42, 37, 87, 117};
+ Handle<String> input = Factory::NewStringFromAscii(CStrVector("foofoo"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, captures[0]);
+ CHECK_EQ(3, captures[1]);
+ CHECK_EQ(-1, captures[2]);
+ CHECK_EQ(-1, captures[3]);
+
+ input = Factory::NewStringFromAscii(CStrVector("barbarbar"));
+ seq_input = Handle<SeqAsciiString>::cast(input);
+ start_adr = seq_input->GetCharsAddress();
+
+ result = ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
+}
+
+
+TEST(MacroAssemblerIA32SimpleUC16) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::UC16, 4);
+
+ uc16 foo_chars[3] = {'f', 'o', 'o'};
+ Vector<const uc16> foo(foo_chars, 3);
+
+ Label fail;
+ m.CheckCharacters(foo, 0, &fail, true);
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.AdvanceCurrentPosition(3);
+ m.WriteCurrentPositionToRegister(1, 0);
+ m.Succeed();
+ m.Bind(&fail);
+ m.Fail();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector("^foo"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ int captures[4] = {42, 37, 87, 117};
+ const uc16 input_data[6] = {'f', 'o', 'o', 'f', 'o', '\xa0'};
+ Handle<String> input =
+ Factory::NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
+ Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, captures[0]);
+ CHECK_EQ(3, captures[1]);
+ CHECK_EQ(-1, captures[2]);
+ CHECK_EQ(-1, captures[3]);
+
+ const uc16 input_data2[9] = {'b', 'a', 'r', 'b', 'a', 'r', 'b', 'a', '\xa0'};
+ input = Factory::NewStringFromTwoByte(Vector<const uc16>(input_data2, 9));
+ seq_input = Handle<SeqTwoByteString>::cast(input);
+ start_adr = seq_input->GetCharsAddress();
+
+ result = ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length() * 2,
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
+}
+
+
+TEST(MacroAssemblerIA32Backtrack) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 0);
+
+ Label fail;
+ Label backtrack;
+ m.LoadCurrentCharacter(10, &fail);
+ m.Succeed();
+ m.Bind(&fail);
+ m.PushBacktrack(&backtrack);
+ m.LoadCurrentCharacter(10, NULL);
+ m.Succeed();
+ m.Bind(&backtrack);
+ m.Fail();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector(".........."));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ Handle<String> input = Factory::NewStringFromAscii(CStrVector("foofoo"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ NULL,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
+}
+
+
+TEST(MacroAssemblerIA32BackReferenceASCII) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 3);
+
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.AdvanceCurrentPosition(2);
+ m.WriteCurrentPositionToRegister(1, 0);
+ Label nomatch;
+ m.CheckNotBackReference(0, &nomatch);
+ m.Fail();
+ m.Bind(&nomatch);
+ m.AdvanceCurrentPosition(2);
+ Label missing_match;
+ m.CheckNotBackReference(0, &missing_match);
+ m.WriteCurrentPositionToRegister(2, 0);
+ m.Succeed();
+ m.Bind(&missing_match);
+ m.Fail();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector("^(..)..\1"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ Handle<String> input = Factory::NewStringFromAscii(CStrVector("fooofo"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ int output[3];
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ output,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, output[0]);
+ CHECK_EQ(2, output[1]);
+ CHECK_EQ(6, output[2]);
+}
+
+
+TEST(MacroAssemblerIA32BackReferenceUC16) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::UC16, 3);
+
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.AdvanceCurrentPosition(2);
+ m.WriteCurrentPositionToRegister(1, 0);
+ Label nomatch;
+ m.CheckNotBackReference(0, &nomatch);
+ m.Fail();
+ m.Bind(&nomatch);
+ m.AdvanceCurrentPosition(2);
+ Label missing_match;
+ m.CheckNotBackReference(0, &missing_match);
+ m.WriteCurrentPositionToRegister(2, 0);
+ m.Succeed();
+ m.Bind(&missing_match);
+ m.Fail();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector("^(..)..\1"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ const uc16 input_data[6] = {'f', 0x2028, 'o', 'o', 'f', 0x2028};
+ Handle<String> input =
+ Factory::NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
+ Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ int output[3];
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length() * 2,
+ output,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, output[0]);
+ CHECK_EQ(2, output[1]);
+ CHECK_EQ(6, output[2]);
+}
+
+
+
+TEST(MacroAssemblerIA32AtStart) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 0);
+
+ Label not_at_start, newline, fail;
+ m.CheckNotAtStart(&not_at_start);
+ // Check that prevchar = '\n' and current = 'f'.
+ m.CheckCharacter('\n', &newline);
+ m.Bind(&fail);
+ m.Fail();
+ m.Bind(&newline);
+ m.LoadCurrentCharacter(0, &fail);
+ m.CheckNotCharacter('f', &fail);
+ m.Succeed();
+
+ m.Bind(&not_at_start);
+ // Check that prevchar = 'o' and current = 'b'.
+ Label prevo;
+ m.CheckCharacter('o', &prevo);
+ m.Fail();
+ m.Bind(&prevo);
+ m.LoadCurrentCharacter(0, &fail);
+ m.CheckNotCharacter('b', &fail);
+ m.Succeed();
+
+ Handle<String> source = Factory::NewStringFromAscii(CStrVector("(^f|ob)"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ Handle<String> input = Factory::NewStringFromAscii(CStrVector("foobar"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ NULL,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+
+ result = ExecuteIA32(*code,
+ *input,
+ 3,
+ start_adr + 3,
+ start_adr + input->length(),
+ NULL,
+ false);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+}
+
+
+TEST(MacroAssemblerIA32BackRefNoCase) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 4);
+
+ Label fail, succ;
+
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.WriteCurrentPositionToRegister(2, 0);
+ m.AdvanceCurrentPosition(3);
+ m.WriteCurrentPositionToRegister(3, 0);
+ m.CheckNotBackReferenceIgnoreCase(2, &fail); // Match "AbC".
+ m.CheckNotBackReferenceIgnoreCase(2, &fail); // Match "ABC".
+ Label expected_fail;
+ m.CheckNotBackReferenceIgnoreCase(2, &expected_fail);
+ m.Bind(&fail);
+ m.Fail();
+
+ m.Bind(&expected_fail);
+ m.AdvanceCurrentPosition(3); // Skip "xYz"
+ m.CheckNotBackReferenceIgnoreCase(2, &succ);
+ m.Fail();
+
+ m.Bind(&succ);
+ m.WriteCurrentPositionToRegister(1, 0);
+ m.Succeed();
+
+ Handle<String> source =
+ Factory::NewStringFromAscii(CStrVector("^(abc)\1\1(?!\1)...(?!\1)"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ Handle<String> input =
+ Factory::NewStringFromAscii(CStrVector("aBcAbCABCxYzab"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ int output[4];
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ output,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, output[0]);
+ CHECK_EQ(12, output[1]);
+ CHECK_EQ(0, output[2]);
+ CHECK_EQ(3, output[3]);
+}
+
+
+
+TEST(MacroAssemblerIA32Registers) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 5);
+
+ uc16 foo_chars[3] = {'f', 'o', 'o'};
+ Vector<const uc16> foo(foo_chars, 3);
+
+ enum registers { out1, out2, out3, out4, out5, sp, loop_cnt };
+ Label fail;
+ Label backtrack;
+ m.WriteCurrentPositionToRegister(out1, 0); // Output: [0]
+ m.PushRegister(out1, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.PushBacktrack(&backtrack);
+ m.WriteStackPointerToRegister(sp);
+ // Fill stack and registers
+ m.AdvanceCurrentPosition(2);
+ m.WriteCurrentPositionToRegister(out1, 0);
+ m.PushRegister(out1, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.PushBacktrack(&fail);
+ // Drop backtrack stack frames.
+ m.ReadStackPointerFromRegister(sp);
+ // And take the first backtrack (to &backtrack)
+ m.Backtrack();
+
+ m.PushCurrentPosition();
+ m.AdvanceCurrentPosition(2);
+ m.PopCurrentPosition();
+
+ m.Bind(&backtrack);
+ m.PopRegister(out1);
+ m.ReadCurrentPositionFromRegister(out1);
+ m.AdvanceCurrentPosition(3);
+ m.WriteCurrentPositionToRegister(out2, 0); // [0,3]
+
+ Label loop;
+ m.SetRegister(loop_cnt, 0); // loop counter
+ m.Bind(&loop);
+ m.AdvanceRegister(loop_cnt, 1);
+ m.AdvanceCurrentPosition(1);
+ m.IfRegisterLT(loop_cnt, 3, &loop);
+ m.WriteCurrentPositionToRegister(out3, 0); // [0,3,6]
+
+ Label loop2;
+ m.SetRegister(loop_cnt, 2); // loop counter
+ m.Bind(&loop2);
+ m.AdvanceRegister(loop_cnt, -1);
+ m.AdvanceCurrentPosition(1);
+ m.IfRegisterGE(loop_cnt, 0, &loop2);
+ m.WriteCurrentPositionToRegister(out4, 0); // [0,3,6,9]
+
+ Label loop3;
+ Label exit_loop3;
+ m.PushRegister(out4, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.PushRegister(out4, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.ReadCurrentPositionFromRegister(out3);
+ m.Bind(&loop3);
+ m.AdvanceCurrentPosition(1);
+ m.CheckGreedyLoop(&exit_loop3);
+ m.GoTo(&loop3);
+ m.Bind(&exit_loop3);
+ m.PopCurrentPosition();
+ m.WriteCurrentPositionToRegister(out5, 0); // [0,3,6,9,9]
+
+ m.Succeed();
+
+ m.Bind(&fail);
+ m.Fail();
+
+ Handle<String> source =
+ Factory::NewStringFromAscii(CStrVector("<loop test>"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ // String long enough for test (content doesn't matter).
+ Handle<String> input =
+ Factory::NewStringFromAscii(CStrVector("foofoofoofoofoo"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ int output[5];
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ output,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, output[0]);
+ CHECK_EQ(3, output[1]);
+ CHECK_EQ(6, output[2]);
+ CHECK_EQ(9, output[3]);
+ CHECK_EQ(9, output[4]);
+}
+
+
+TEST(MacroAssemblerIA32StackOverflow) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 0);
+
+ Label loop;
+ m.Bind(&loop);
+ m.PushBacktrack(&loop);
+ m.GoTo(&loop);
+
+ Handle<String> source =
+ Factory::NewStringFromAscii(CStrVector("<stack overflow test>"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ // String long enough for test (content doesn't matter).
+ Handle<String> input =
+ Factory::NewStringFromAscii(CStrVector("dummy"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ NULL,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::EXCEPTION, result);
+ CHECK(Top::has_pending_exception());
+ Top::clear_pending_exception();
+}
+
+
+TEST(MacroAssemblerIA32LotsOfRegisters) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 2);
+
+ // At least 2048, to ensure the allocated space for registers
+ // span one full page.
+ const int large_number = 8000;
+ m.WriteCurrentPositionToRegister(large_number, 42);
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.WriteCurrentPositionToRegister(1, 1);
+ Label done;
+ m.CheckNotBackReference(0, &done); // Performs a system-stack push.
+ m.Bind(&done);
+ m.PushRegister(large_number, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.PopRegister(1);
+ m.Succeed();
+
+ Handle<String> source =
+ Factory::NewStringFromAscii(CStrVector("<huge register space test>"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ // String long enough for test (content doesn't matter).
+ Handle<String> input =
+ Factory::NewStringFromAscii(CStrVector("sample text"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+
+ int captures[2];
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ *input,
+ 0,
+ start_adr,
+ start_adr + input->length(),
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, captures[0]);
+ CHECK_EQ(42, captures[1]);
+
+ Top::clear_pending_exception();
+}
+
+
+
+#endif // !defined ARM
+
+TEST(AddInverseToTable) {
+ static const int kLimit = 1000;
+ static const int kRangeCount = 16;
+ for (int t = 0; t < 10; t++) {
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ ZoneList<CharacterRange>* ranges =
+ new ZoneList<CharacterRange>(kRangeCount);
+ for (int i = 0; i < kRangeCount; i++) {
+ int from = PseudoRandom(t + 87, i + 25) % kLimit;
+ int to = from + (PseudoRandom(i + 87, t + 25) % (kLimit / 20));
+ if (to > kLimit) to = kLimit;
+ ranges->Add(CharacterRange(from, to));
+ }
+ DispatchTable table;
+ DispatchTableConstructor cons(&table, false);
+ cons.set_choice_index(0);
+ cons.AddInverse(ranges);
+ for (int i = 0; i < kLimit; i++) {
+ bool is_on = false;
+ for (int j = 0; !is_on && j < kRangeCount; j++)
+ is_on = ranges->at(j).Contains(i);
+ OutSet* set = table.Get(i);
+ CHECK_EQ(is_on, set->Get(0) == false);
+ }
+ }
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ ZoneList<CharacterRange>* ranges =
+ new ZoneList<CharacterRange>(1);
+ ranges->Add(CharacterRange(0xFFF0, 0xFFFE));
+ DispatchTable table;
+ DispatchTableConstructor cons(&table, false);
+ cons.set_choice_index(0);
+ cons.AddInverse(ranges);
+ CHECK(!table.Get(0xFFFE)->Get(0));
+ CHECK(table.Get(0xFFFF)->Get(0));
+}
+
+
+static uc32 canonicalize(uc32 c) {
+ unibrow::uchar canon[unibrow::Ecma262Canonicalize::kMaxWidth];
+ int count = unibrow::Ecma262Canonicalize::Convert(c, '\0', canon, NULL);
+ if (count == 0) {
+ return c;
+ } else {
+ CHECK_EQ(1, count);
+ return canon[0];
+ }
+}
+
+
+TEST(LatinCanonicalize) {
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize> un_canonicalize;
+ for (char lower = 'a'; lower <= 'z'; lower++) {
+ char upper = lower + ('A' - 'a');
+ CHECK_EQ(canonicalize(lower), canonicalize(upper));
+ unibrow::uchar uncanon[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int length = un_canonicalize.get(lower, '\0', uncanon);
+ CHECK_EQ(2, length);
+ CHECK_EQ(upper, uncanon[0]);
+ CHECK_EQ(lower, uncanon[1]);
+ }
+ for (uc32 c = 128; c < (1 << 21); c++)
+ CHECK_GE(canonicalize(c), 128);
+ unibrow::Mapping<unibrow::ToUppercase> to_upper;
+ for (uc32 c = 0; c < (1 << 21); c++) {
+ unibrow::uchar upper[unibrow::ToUppercase::kMaxWidth];
+ int length = to_upper.get(c, '\0', upper);
+ if (length == 0) {
+ length = 1;
+ upper[0] = c;
+ }
+ uc32 u = upper[0];
+ if (length > 1 || (c >= 128 && u < 128))
+ u = c;
+ CHECK_EQ(u, canonicalize(c));
+ }
+}
+
+
+static uc32 CanonRange(uc32 c) {
+ unibrow::uchar canon[unibrow::CanonicalizationRange::kMaxWidth];
+ int count = unibrow::CanonicalizationRange::Convert(c, '\0', canon, NULL);
+ if (count == 0) {
+ return c;
+ } else {
+ CHECK_EQ(1, count);
+ return canon[0];
+ }
+}
+
+
+TEST(RangeCanonicalization) {
+ CHECK_NE(CanonRange(0) & CharacterRange::kStartMarker, 0);
+ // Check that we arrive at the same result when using the basic
+ // range canonicalization primitives as when using immediate
+ // canonicalization.
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize> un_canonicalize;
+ for (int i = 0; i < CharacterRange::kRangeCanonicalizeMax; i++) {
+ int range = CanonRange(i);
+ int indirect_length = 0;
+ unibrow::uchar indirect[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ if ((range & CharacterRange::kStartMarker) == 0) {
+ indirect_length = un_canonicalize.get(i - range, '\0', indirect);
+ for (int i = 0; i < indirect_length; i++)
+ indirect[i] += range;
+ } else {
+ indirect_length = un_canonicalize.get(i, '\0', indirect);
+ }
+ unibrow::uchar direct[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int direct_length = un_canonicalize.get(i, '\0', direct);
+ CHECK_EQ(direct_length, indirect_length);
+ }
+ // Check that we arrive at the same results when skipping over
+ // canonicalization ranges.
+ int next_block = 0;
+ while (next_block < CharacterRange::kRangeCanonicalizeMax) {
+ uc32 start = CanonRange(next_block);
+ CHECK_NE((start & CharacterRange::kStartMarker), 0);
+ unsigned dist = start & CharacterRange::kPayloadMask;
+ unibrow::uchar first[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int first_length = un_canonicalize.get(next_block, '\0', first);
+ for (unsigned i = 1; i < dist; i++) {
+ CHECK_EQ(i, CanonRange(next_block + i));
+ unibrow::uchar succ[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int succ_length = un_canonicalize.get(next_block + i, '\0', succ);
+ CHECK_EQ(first_length, succ_length);
+ for (int j = 0; j < succ_length; j++) {
+ int calc = first[j] + i;
+ int found = succ[j];
+ CHECK_EQ(calc, found);
+ }
+ }
+ next_block = next_block + dist;
+ }
+}
+
+
+TEST(UncanonicalizeEquivalence) {
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize> un_canonicalize;
+ unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ for (int i = 0; i < (1 << 16); i++) {
+ int length = un_canonicalize.get(i, '\0', chars);
+ for (int j = 0; j < length; j++) {
+ unibrow::uchar chars2[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int length2 = un_canonicalize.get(chars[j], '\0', chars2);
+ CHECK_EQ(length, length2);
+ for (int k = 0; k < length; k++)
+ CHECK_EQ(static_cast<int>(chars[k]), static_cast<int>(chars2[k]));
+ }
+ }
+}
+
+
+static void TestRangeCaseIndependence(CharacterRange input,
+ Vector<CharacterRange> expected) {
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ int count = expected.length();
+ ZoneList<CharacterRange>* list = new ZoneList<CharacterRange>(count);
+ input.AddCaseEquivalents(list);
+ CHECK_EQ(count, list->length());
+ for (int i = 0; i < list->length(); i++) {
+ CHECK_EQ(expected[i].from(), list->at(i).from());
+ CHECK_EQ(expected[i].to(), list->at(i).to());
+ }
+}
+
+
+static void TestSimpleRangeCaseIndependence(CharacterRange input,
+ CharacterRange expected) {
+ EmbeddedVector<CharacterRange, 1> vector;
+ vector[0] = expected;
+ TestRangeCaseIndependence(input, vector);
+}
+
+
+TEST(CharacterRangeCaseIndependence) {
+ TestSimpleRangeCaseIndependence(CharacterRange::Singleton('a'),
+ CharacterRange::Singleton('A'));
+ TestSimpleRangeCaseIndependence(CharacterRange::Singleton('z'),
+ CharacterRange::Singleton('Z'));
+ TestSimpleRangeCaseIndependence(CharacterRange('a', 'z'),
+ CharacterRange('A', 'Z'));
+ TestSimpleRangeCaseIndependence(CharacterRange('c', 'f'),
+ CharacterRange('C', 'F'));
+ TestSimpleRangeCaseIndependence(CharacterRange('a', 'b'),
+ CharacterRange('A', 'B'));
+ TestSimpleRangeCaseIndependence(CharacterRange('y', 'z'),
+ CharacterRange('Y', 'Z'));
+ TestSimpleRangeCaseIndependence(CharacterRange('a' - 1, 'z' + 1),
+ CharacterRange('A', 'Z'));
+ TestSimpleRangeCaseIndependence(CharacterRange('A', 'Z'),
+ CharacterRange('a', 'z'));
+ TestSimpleRangeCaseIndependence(CharacterRange('C', 'F'),
+ CharacterRange('c', 'f'));
+ TestSimpleRangeCaseIndependence(CharacterRange('A' - 1, 'Z' + 1),
+ CharacterRange('a', 'z'));
+ // Here we need to add [l-z] to complete the case independence of
+ // [A-Za-z] but we expect [a-z] to be added since we always add a
+ // whole block at a time.
+ TestSimpleRangeCaseIndependence(CharacterRange('A', 'k'),
+ CharacterRange('a', 'z'));
+}
+
+
+static bool InClass(uc16 c, ZoneList<CharacterRange>* ranges) {
+ if (ranges == NULL)
+ return false;
+ for (int i = 0; i < ranges->length(); i++) {
+ CharacterRange range = ranges->at(i);
+ if (range.from() <= c && c <= range.to())
+ return true;
+ }
+ return false;
+}
+
+
+TEST(CharClassDifference) {
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ ZoneList<CharacterRange>* base = new ZoneList<CharacterRange>(1);
+ base->Add(CharacterRange::Everything());
+ Vector<const uc16> overlay = CharacterRange::GetWordBounds();
+ ZoneList<CharacterRange>* included = NULL;
+ ZoneList<CharacterRange>* excluded = NULL;
+ CharacterRange::Split(base, overlay, &included, &excluded);
+ for (int i = 0; i < (1 << 16); i++) {
+ bool in_base = InClass(i, base);
+ if (in_base) {
+ bool in_overlay = false;
+ for (int j = 0; !in_overlay && j < overlay.length(); j += 2) {
+ if (overlay[j] <= i && i <= overlay[j+1])
+ in_overlay = true;
+ }
+ CHECK_EQ(in_overlay, InClass(i, included));
+ CHECK_EQ(!in_overlay, InClass(i, excluded));
+ } else {
+ CHECK(!InClass(i, included));
+ CHECK(!InClass(i, excluded));
+ }
+ }
+}
+
+
+TEST(Graph) {
+ V8::Initialize(NULL);
+ Execute("(?:(?:x(.))?\1)+$", false, true, true);
+}
diff --git a/v8/test/cctest/test-serialize.cc b/v8/test/cctest/test-serialize.cc
new file mode 100644
index 0000000..56727dc
--- /dev/null
+++ b/v8/test/cctest/test-serialize.cc
@@ -0,0 +1,268 @@
+// 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 <signal.h>
+#include <map>
+#include <string>
+
+#include "sys/stat.h"
+#include "v8.h"
+
+#include "debug.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "serialize.h"
+#include "scopeinfo.h"
+#include "snapshot.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static int local_counters[256];
+static int counter_count = 0;
+static std::map<std::string, int> counter_table;
+
+
+// Callback receiver to track counters in test.
+static int* counter_function(const char* name) {
+ std::string counter(name);
+ if (counter_table.find(counter) == counter_table.end()) {
+ local_counters[counter_count] = 0;
+ counter_table[counter] = counter_count++;
+ }
+
+ return &local_counters[counter_table[counter]];
+}
+
+
+template <class T>
+static Address AddressOf(T id) {
+ return ExternalReference(id).address();
+}
+
+
+template <class T>
+static uint32_t Encode(const ExternalReferenceEncoder& encoder, T id) {
+ return encoder.Encode(AddressOf(id));
+}
+
+
+static int make_code(TypeCode type, int id) {
+ return static_cast<uint32_t>(type) << kReferenceTypeShift | id;
+}
+
+
+static int register_code(int reg) {
+ return Debug::k_register_address << kDebugIdShift | reg;
+}
+
+
+TEST(ExternalReferenceEncoder) {
+ StatsTable::SetCounterFunction(counter_function);
+ Heap::Setup(false);
+ ExternalReferenceEncoder encoder;
+ CHECK_EQ(make_code(BUILTIN, Builtins::ArrayCode),
+ Encode(encoder, Builtins::ArrayCode));
+ CHECK_EQ(make_code(RUNTIME_FUNCTION, Runtime::kAbort),
+ Encode(encoder, Runtime::kAbort));
+ CHECK_EQ(make_code(IC_UTILITY, IC::kLoadCallbackProperty),
+ Encode(encoder, IC_Utility(IC::kLoadCallbackProperty)));
+ CHECK_EQ(make_code(DEBUG_ADDRESS, register_code(3)),
+ Encode(encoder, Debug_Address(Debug::k_register_address, 3)));
+ ExternalReference keyed_load_function_prototype =
+ ExternalReference(&Counters::keyed_load_function_prototype);
+ CHECK_EQ(make_code(STATS_COUNTER, Counters::k_keyed_load_function_prototype),
+ encoder.Encode(keyed_load_function_prototype.address()));
+ ExternalReference passed_function =
+ ExternalReference::builtin_passed_function();
+ CHECK_EQ(make_code(UNCLASSIFIED, 1),
+ encoder.Encode(passed_function.address()));
+ ExternalReference the_hole_value_location =
+ ExternalReference::the_hole_value_location();
+ CHECK_EQ(make_code(UNCLASSIFIED, 2),
+ encoder.Encode(the_hole_value_location.address()));
+ ExternalReference stack_guard_limit_address =
+ ExternalReference::address_of_stack_guard_limit();
+ CHECK_EQ(make_code(UNCLASSIFIED, 3),
+ encoder.Encode(stack_guard_limit_address.address()));
+ CHECK_EQ(make_code(UNCLASSIFIED, 5),
+ encoder.Encode(ExternalReference::debug_break().address()));
+ CHECK_EQ(make_code(UNCLASSIFIED, 6),
+ encoder.Encode(ExternalReference::new_space_start().address()));
+}
+
+
+TEST(ExternalReferenceDecoder) {
+ StatsTable::SetCounterFunction(counter_function);
+ Heap::Setup(false);
+ ExternalReferenceDecoder decoder;
+ CHECK_EQ(AddressOf(Builtins::ArrayCode),
+ decoder.Decode(make_code(BUILTIN, Builtins::ArrayCode)));
+ CHECK_EQ(AddressOf(Runtime::kAbort),
+ decoder.Decode(make_code(RUNTIME_FUNCTION, Runtime::kAbort)));
+ CHECK_EQ(AddressOf(IC_Utility(IC::kLoadCallbackProperty)),
+ decoder.Decode(make_code(IC_UTILITY, IC::kLoadCallbackProperty)));
+ CHECK_EQ(AddressOf(Debug_Address(Debug::k_register_address, 3)),
+ decoder.Decode(make_code(DEBUG_ADDRESS, register_code(3))));
+ ExternalReference keyed_load_function =
+ ExternalReference(&Counters::keyed_load_function_prototype);
+ CHECK_EQ(keyed_load_function.address(),
+ decoder.Decode(
+ make_code(STATS_COUNTER,
+ Counters::k_keyed_load_function_prototype)));
+ CHECK_EQ(ExternalReference::builtin_passed_function().address(),
+ decoder.Decode(make_code(UNCLASSIFIED, 1)));
+ CHECK_EQ(ExternalReference::the_hole_value_location().address(),
+ decoder.Decode(make_code(UNCLASSIFIED, 2)));
+ CHECK_EQ(ExternalReference::address_of_stack_guard_limit().address(),
+ decoder.Decode(make_code(UNCLASSIFIED, 3)));
+ CHECK_EQ(ExternalReference::debug_break().address(),
+ decoder.Decode(make_code(UNCLASSIFIED, 5)));
+ CHECK_EQ(ExternalReference::new_space_start().address(),
+ decoder.Decode(make_code(UNCLASSIFIED, 6)));
+}
+
+
+static void Serialize() {
+#ifdef DEBUG
+ FLAG_debug_serialization = true;
+#endif
+ StatsTable::SetCounterFunction(counter_function);
+
+ v8::HandleScope scope;
+ const int kExtensionCount = 1;
+ const char* extension_list[kExtensionCount] = { "v8/gc" };
+ v8::ExtensionConfiguration extensions(kExtensionCount, extension_list);
+ Serializer::Enable();
+ v8::Persistent<v8::Context> env = v8::Context::New(&extensions);
+ env->Enter();
+
+ Snapshot::WriteToFile(FLAG_testing_serialization_file);
+}
+
+
+// Test that the whole heap can be serialized when running from the
+// internal snapshot.
+// (Smoke test.)
+TEST(SerializeInternal) {
+ Snapshot::Initialize(NULL);
+ Serialize();
+}
+
+
+// Test that the whole heap can be serialized when running from a
+// bootstrapped heap.
+// (Smoke test.)
+TEST(Serialize) {
+ if (Snapshot::IsEnabled()) return;
+ Serialize();
+}
+
+
+// Test that the heap isn't destroyed after a serialization.
+TEST(SerializeNondestructive) {
+ if (Snapshot::IsEnabled()) return;
+ StatsTable::SetCounterFunction(counter_function);
+ v8::HandleScope scope;
+ Serializer::Enable();
+ v8::Persistent<v8::Context> env = v8::Context::New();
+ v8::Context::Scope context_scope(env);
+ Serializer().Serialize();
+ const char* c_source = "\"abcd\".charAt(2) == 'c'";
+ v8::Local<v8::String> source = v8::String::New(c_source);
+ v8::Local<v8::Script> script = v8::Script::Compile(source);
+ v8::Local<v8::Value> value = script->Run();
+ CHECK(value->BooleanValue());
+}
+
+//----------------------------------------------------------------------------
+// Tests that the heap can be deserialized.
+
+static void Deserialize() {
+#ifdef DEBUG
+ FLAG_debug_serialization = true;
+#endif
+ CHECK(Snapshot::Initialize(FLAG_testing_serialization_file));
+}
+
+
+static void SanityCheck() {
+ v8::HandleScope scope;
+#ifdef DEBUG
+ Heap::Verify();
+#endif
+ CHECK(Top::global()->IsJSObject());
+ CHECK(Top::global_context()->IsContext());
+ CHECK(Top::special_function_table()->IsFixedArray());
+ CHECK(Heap::symbol_table()->IsSymbolTable());
+ CHECK(!Factory::LookupAsciiSymbol("Empty")->IsFailure());
+}
+
+
+DEPENDENT_TEST(Deserialize, Serialize) {
+ v8::HandleScope scope;
+
+ Deserialize();
+
+ SanityCheck();
+}
+
+DEPENDENT_TEST(DeserializeAndRunScript, Serialize) {
+ v8::HandleScope scope;
+
+ Deserialize();
+
+ const char* c_source = "\"1234\".length";
+ v8::Local<v8::String> source = v8::String::New(c_source);
+ v8::Local<v8::Script> script = v8::Script::Compile(source);
+ CHECK_EQ(4, script->Run()->Int32Value());
+}
+
+
+DEPENDENT_TEST(DeserializeNatives, Serialize) {
+ v8::HandleScope scope;
+
+ Deserialize();
+
+ const char* c_source = "\"abcd\".charAt(2) == 'c'";
+ v8::Local<v8::String> source = v8::String::New(c_source);
+ v8::Local<v8::Script> script = v8::Script::Compile(source);
+ v8::Local<v8::Value> value = script->Run();
+ CHECK(value->BooleanValue());
+}
+
+
+DEPENDENT_TEST(DeserializeExtensions, Serialize) {
+ v8::HandleScope scope;
+
+ Deserialize();
+ const char* c_source = "gc();";
+ v8::Local<v8::String> source = v8::String::New(c_source);
+ v8::Local<v8::Script> script = v8::Script::Compile(source);
+ v8::Local<v8::Value> value = script->Run();
+ CHECK(value->IsUndefined());
+}
diff --git a/v8/test/cctest/test-sockets.cc b/v8/test/cctest/test-sockets.cc
new file mode 100644
index 0000000..a4b2285
--- /dev/null
+++ b/v8/test/cctest/test-sockets.cc
@@ -0,0 +1,161 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+
+#include "v8.h"
+#include "platform.h"
+#include "cctest.h"
+
+
+using namespace ::v8::internal;
+
+
+class SocketListenerThread : public Thread {
+ public:
+ explicit SocketListenerThread(int port, int data_size)
+ : port_(port), data_size_(data_size), server_(NULL), client_(NULL),
+ listening_(OS::CreateSemaphore(0)) {
+ data_ = new char[data_size_];
+ }
+ ~SocketListenerThread() {
+ // Close both sockets.
+ delete client_;
+ delete server_;
+ delete listening_;
+ delete[] data_;
+ }
+
+ void Run();
+ void WaitForListening() { listening_->Wait(); }
+ char* data() { return data_; }
+
+ private:
+ int port_;
+ char* data_;
+ int data_size_;
+ Socket* server_; // Server socket used for bind/accept.
+ Socket* client_; // Single client connection used by the test.
+ Semaphore* listening_; // Signalled when the server socket is in listen mode.
+};
+
+
+void SocketListenerThread::Run() {
+ bool ok;
+
+ // Create the server socket and bind it to the requested port.
+ server_ = OS::CreateSocket();
+ CHECK(server_ != NULL);
+ ok = server_->Bind(port_);
+ CHECK(ok);
+
+ // Listen for new connections.
+ ok = server_->Listen(1);
+ CHECK(ok);
+ listening_->Signal();
+
+ // Accept a connection.
+ client_ = server_->Accept();
+ CHECK(client_ != NULL);
+
+ // Read the expected niumber of bytes of data.
+ int bytes_read = 0;
+ while (bytes_read < data_size_) {
+ bytes_read += client_->Receive(data_ + bytes_read, data_size_ - bytes_read);
+ }
+}
+
+
+static bool SendAll(Socket* socket, const char* data, int len) {
+ int sent_len = 0;
+ while (sent_len < len) {
+ int status = socket->Send(data, len);
+ if (status <= 0) {
+ return false;
+ }
+ sent_len += status;
+ }
+ return true;
+}
+
+
+static void SendAndReceive(int port, char *data, int len) {
+ static const char* kLocalhost = "localhost";
+
+ bool ok;
+
+ // Make a string with the port number.
+ const int kPortBuferLen = 6;
+ char port_str[kPortBuferLen];
+ OS::SNPrintF(Vector<char>(port_str, kPortBuferLen), "%d", port);
+
+ // Create a socket listener.
+ SocketListenerThread* listener = new SocketListenerThread(port, len);
+ listener->Start();
+ listener->WaitForListening();
+
+ // Connect and write some data.
+ Socket* client = OS::CreateSocket();
+ CHECK(client != NULL);
+ ok = client->Connect(kLocalhost, port_str);
+ CHECK(ok);
+
+ // Send all the data.
+ ok = SendAll(client, data, len);
+ CHECK(ok);
+
+ // Wait until data is received.
+ listener->Join();
+
+ // Check that data received is the same as data send.
+ for (int i = 0; i < len; i++) {
+ CHECK(data[i] == listener->data()[i]);
+ }
+
+ // Close the client before the listener to avoid TIME_WAIT issues.
+ client->Shutdown();
+ delete client;
+ delete listener;
+}
+
+
+TEST(Socket) {
+ // Make sure this port is not used by other tests to allow tests to run in
+ // parallel.
+ static const int kPort = 5859;
+
+ bool ok;
+
+ // Initialize socket support.
+ ok = Socket::Setup();
+ CHECK(ok);
+
+ // Send and receive some data.
+ static const int kBufferSizeSmall = 20;
+ char small_data[kBufferSizeSmall + 1] = "1234567890abcdefghij";
+ SendAndReceive(kPort, small_data, kBufferSizeSmall);
+
+ // Send and receive some more data.
+ static const int kBufferSizeMedium = 10000;
+ char* medium_data = new char[kBufferSizeMedium];
+ for (int i = 0; i < kBufferSizeMedium; i++) {
+ medium_data[i] = i % 256;
+ }
+ SendAndReceive(kPort, medium_data, kBufferSizeMedium);
+ delete[] medium_data;
+
+ // Send and receive even more data.
+ static const int kBufferSizeLarge = 1000000;
+ char* large_data = new char[kBufferSizeLarge];
+ for (int i = 0; i < kBufferSizeLarge; i++) {
+ large_data[i] = i % 256;
+ }
+ SendAndReceive(kPort, large_data, kBufferSizeLarge);
+ delete[] large_data;
+}
+
+
+TEST(HToNNToH) {
+ uint16_t x = 1234;
+ CHECK_EQ(x, Socket::NToH(Socket::HToN(x)));
+
+ uint32_t y = 12345678;
+ CHECK(y == Socket::NToH(Socket::HToN(y)));
+}
diff --git a/v8/test/cctest/test-spaces.cc b/v8/test/cctest/test-spaces.cc
new file mode 100644
index 0000000..d946a7f
--- /dev/null
+++ b/v8/test/cctest/test-spaces.cc
@@ -0,0 +1,248 @@
+// Copyright 2006-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 "v8.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+static void VerifyRSet(Address page_start) {
+#ifdef DEBUG
+ Page::set_rset_state(Page::IN_USE);
+#endif
+
+ Page* p = Page::FromAddress(page_start);
+
+ p->ClearRSet();
+
+ for (Address addr = p->ObjectAreaStart();
+ addr < p->ObjectAreaEnd();
+ addr += kPointerSize) {
+ CHECK(!Page::IsRSetSet(addr, 0));
+ }
+
+ for (Address addr = p->ObjectAreaStart();
+ addr < p->ObjectAreaEnd();
+ addr += kPointerSize) {
+ Page::SetRSet(addr, 0);
+ }
+
+ for (Address addr = p->ObjectAreaStart();
+ addr < p->ObjectAreaEnd();
+ addr += kPointerSize) {
+ CHECK(Page::IsRSetSet(addr, 0));
+ }
+}
+
+
+TEST(Page) {
+#ifdef DEBUG
+ Page::set_rset_state(Page::NOT_IN_USE);
+#endif
+
+ byte* mem = NewArray<byte>(2*Page::kPageSize);
+ CHECK(mem != NULL);
+
+ Address start = reinterpret_cast<Address>(mem);
+ Address page_start = RoundUp(start, Page::kPageSize);
+
+ Page* p = Page::FromAddress(page_start);
+ CHECK(p->address() == page_start);
+ CHECK(p->is_valid());
+
+ p->opaque_header = 0;
+ p->is_normal_page = 0x1;
+ CHECK(!p->next_page()->is_valid());
+
+ CHECK(p->ObjectAreaStart() == page_start + Page::kObjectStartOffset);
+ CHECK(p->ObjectAreaEnd() == page_start + Page::kPageSize);
+
+ CHECK(p->Offset(page_start + Page::kObjectStartOffset) ==
+ Page::kObjectStartOffset);
+ CHECK(p->Offset(page_start + Page::kPageSize) == Page::kPageSize);
+
+ CHECK(p->OffsetToAddress(Page::kObjectStartOffset) == p->ObjectAreaStart());
+ CHECK(p->OffsetToAddress(Page::kPageSize) == p->ObjectAreaEnd());
+
+ // test remember set
+ VerifyRSet(page_start);
+
+ DeleteArray(mem);
+}
+
+
+TEST(MemoryAllocator) {
+ CHECK(Heap::ConfigureHeapDefault());
+ CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
+
+ OldSpace faked_space(Heap::MaxCapacity(), OLD_POINTER_SPACE, NOT_EXECUTABLE);
+ int total_pages = 0;
+ int requested = 2;
+ int allocated;
+ // If we request two pages, we should get one or two.
+ Page* first_page =
+ MemoryAllocator::AllocatePages(requested, &allocated, &faked_space);
+ CHECK(first_page->is_valid());
+ CHECK(allocated > 0 && allocated <= 2);
+ total_pages += allocated;
+
+ Page* last_page = first_page;
+ for (Page* p = first_page; p->is_valid(); p = p->next_page()) {
+ CHECK(MemoryAllocator::IsPageInSpace(p, &faked_space));
+ last_page = p;
+ }
+
+ // Again, we should get one or two pages.
+ Page* others =
+ MemoryAllocator::AllocatePages(requested, &allocated, &faked_space);
+ CHECK(others->is_valid());
+ CHECK(allocated > 0 && allocated <= 2);
+ total_pages += allocated;
+
+ MemoryAllocator::SetNextPage(last_page, others);
+ int page_count = 0;
+ for (Page* p = first_page; p->is_valid(); p = p->next_page()) {
+ CHECK(MemoryAllocator::IsPageInSpace(p, &faked_space));
+ page_count++;
+ }
+ CHECK(total_pages == page_count);
+
+ Page* second_page = first_page->next_page();
+ CHECK(second_page->is_valid());
+
+ // Freeing pages at the first chunk starting at or after the second page
+ // should free the entire second chunk. It will return the last page in the
+ // first chunk (if the second page was in the first chunk) or else an
+ // invalid page (if the second page was the start of the second chunk).
+ Page* free_return = MemoryAllocator::FreePages(second_page);
+ CHECK(free_return == last_page || !free_return->is_valid());
+ MemoryAllocator::SetNextPage(first_page, free_return);
+
+ // Freeing pages in the first chunk starting at the first page should free
+ // the first chunk and return an invalid page.
+ Page* invalid_page = MemoryAllocator::FreePages(first_page);
+ CHECK(!invalid_page->is_valid());
+
+ MemoryAllocator::TearDown();
+}
+
+
+TEST(NewSpace) {
+ CHECK(Heap::ConfigureHeapDefault());
+ CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
+
+ NewSpace new_space;
+
+ void* chunk =
+ MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize());
+ CHECK(chunk != NULL);
+ Address start = RoundUp(static_cast<Address>(chunk),
+ Heap::YoungGenerationSize());
+ CHECK(new_space.Setup(start, Heap::YoungGenerationSize()));
+ CHECK(new_space.HasBeenSetup());
+
+ while (new_space.Available() >= Page::kMaxHeapObjectSize) {
+ Object* obj = new_space.AllocateRaw(Page::kMaxHeapObjectSize);
+ CHECK(!obj->IsFailure());
+ CHECK(new_space.Contains(HeapObject::cast(obj)));
+ }
+
+ new_space.TearDown();
+ MemoryAllocator::TearDown();
+}
+
+
+TEST(OldSpace) {
+ CHECK(Heap::ConfigureHeapDefault());
+ CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
+
+ OldSpace* s = new OldSpace(Heap::OldGenerationSize(),
+ OLD_POINTER_SPACE,
+ NOT_EXECUTABLE);
+ CHECK(s != NULL);
+
+ void* chunk =
+ MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize());
+ CHECK(chunk != NULL);
+ Address start = static_cast<Address>(chunk);
+ size_t size = RoundUp(start, Heap::YoungGenerationSize()) - start;
+
+ CHECK(s->Setup(start, size));
+
+ while (s->Available() > 0) {
+ Object* obj = s->AllocateRaw(Page::kMaxHeapObjectSize);
+ CHECK(!obj->IsFailure());
+ }
+
+ s->TearDown();
+ delete s;
+ MemoryAllocator::TearDown();
+}
+
+
+TEST(LargeObjectSpace) {
+ CHECK(Heap::Setup(false));
+
+ LargeObjectSpace* lo = Heap::lo_space();
+ CHECK(lo != NULL);
+
+ Map* faked_map = reinterpret_cast<Map*>(HeapObject::FromAddress(0));
+ int lo_size = Page::kPageSize;
+
+ Object* obj = lo->AllocateRaw(lo_size);
+ CHECK(!obj->IsFailure());
+ CHECK(obj->IsHeapObject());
+
+ HeapObject* ho = HeapObject::cast(obj);
+ ho->set_map(faked_map);
+
+ CHECK(lo->Contains(HeapObject::cast(obj)));
+
+ CHECK(lo->FindObject(ho->address()) == obj);
+
+ CHECK(lo->Contains(ho));
+
+ while (true) {
+ int available = lo->Available();
+ obj = lo->AllocateRaw(lo_size);
+ if (obj->IsFailure()) break;
+ HeapObject::cast(obj)->set_map(faked_map);
+ CHECK(lo->Available() < available);
+ };
+
+ CHECK(!lo->IsEmpty());
+
+ obj = lo->AllocateRaw(lo_size);
+ CHECK(obj->IsFailure());
+
+ lo->TearDown();
+ delete lo;
+
+ MemoryAllocator::TearDown();
+}
diff --git a/v8/test/cctest/test-strings.cc b/v8/test/cctest/test-strings.cc
new file mode 100644
index 0000000..b38d645
--- /dev/null
+++ b/v8/test/cctest/test-strings.cc
@@ -0,0 +1,389 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+
+// Check that we can traverse very deep stacks of ConsStrings using
+// StringInputBuffer. Check that Get(int) works on very deep stacks
+// of ConsStrings. These operations may not be very fast, but they
+// should be possible without getting errors due to too deep recursion.
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "factory.h"
+#include "cctest.h"
+#include "zone-inl.h"
+
+unsigned int seed = 123;
+
+static uint32_t gen() {
+ uint64_t z;
+ z = seed;
+ z *= 279470273;
+ z %= 4294967291U;
+ seed = static_cast<unsigned int>(z);
+ return static_cast<uint32_t>(seed >> 16);
+}
+
+
+using namespace v8::internal;
+
+static v8::Persistent<v8::Context> env;
+
+
+static void InitializeVM() {
+ if (env.IsEmpty()) {
+ v8::HandleScope scope;
+ const char* extensions[] = { "v8/print" };
+ v8::ExtensionConfiguration config(1, extensions);
+ env = v8::Context::New(&config);
+ }
+ v8::HandleScope scope;
+ env->Enter();
+}
+
+
+static const int NUMBER_OF_BUILDING_BLOCKS = 128;
+static const int DEEP_DEPTH = 8 * 1024;
+static const int SUPER_DEEP_DEPTH = 80 * 1024;
+
+
+static void InitializeBuildingBlocks(
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) {
+ // A list of pointers that we don't have any interest in cleaning up.
+ // If they are reachable from a root then leak detection won't complain.
+ for (int i = 0; i < NUMBER_OF_BUILDING_BLOCKS; i++) {
+ int len = gen() % 16;
+ if (len > 14) {
+ len += 1234;
+ }
+ switch (gen() % 4) {
+ case 0: {
+ uc16 buf[2000];
+ for (int j = 0; j < len; j++) {
+ buf[j] = gen() % 65536;
+ }
+ building_blocks[i] =
+ Factory::NewStringFromTwoByte(Vector<const uc16>(buf, len));
+ for (int j = 0; j < len; j++) {
+ CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ }
+ break;
+ }
+ case 1: {
+ char buf[2000];
+ for (int j = 0; j < len; j++) {
+ buf[j] = gen() % 128;
+ }
+ building_blocks[i] =
+ Factory::NewStringFromAscii(Vector<const char>(buf, len));
+ for (int j = 0; j < len; j++) {
+ CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ }
+ break;
+ }
+ case 2: {
+ class Resource: public v8::String::ExternalStringResource,
+ public ZoneObject {
+ public:
+ explicit Resource(Vector<const uc16> string): data_(string.start()) {
+ length_ = string.length();
+ }
+ virtual const uint16_t* data() const { return data_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ const uc16* data_;
+ size_t length_;
+ };
+ uc16* buf = Zone::NewArray<uc16>(len);
+ for (int j = 0; j < len; j++) {
+ buf[j] = gen() % 65536;
+ }
+ Resource* resource = new Resource(Vector<const uc16>(buf, len));
+ building_blocks[i] = Factory::NewExternalStringFromTwoByte(resource);
+ for (int j = 0; j < len; j++) {
+ CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ }
+ break;
+ }
+ case 3: {
+ char* buf = NewArray<char>(len);
+ for (int j = 0; j < len; j++) {
+ buf[j] = gen() % 128;
+ }
+ building_blocks[i] =
+ Factory::NewStringFromAscii(Vector<const char>(buf, len));
+ for (int j = 0; j < len; j++) {
+ CHECK_EQ(buf[j], building_blocks[i]->Get(j));
+ }
+ DeleteArray<char>(buf);
+ break;
+ }
+ }
+ }
+}
+
+
+static Handle<String> ConstructLeft(
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS],
+ int depth) {
+ Handle<String> answer = Factory::NewStringFromAscii(CStrVector(""));
+ for (int i = 0; i < depth; i++) {
+ answer = Factory::NewConsString(
+ answer,
+ building_blocks[i % NUMBER_OF_BUILDING_BLOCKS]);
+ }
+ return answer;
+}
+
+
+static Handle<String> ConstructRight(
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS],
+ int depth) {
+ Handle<String> answer = Factory::NewStringFromAscii(CStrVector(""));
+ for (int i = depth - 1; i >= 0; i--) {
+ answer = Factory::NewConsString(
+ building_blocks[i % NUMBER_OF_BUILDING_BLOCKS],
+ answer);
+ }
+ return answer;
+}
+
+
+static Handle<String> ConstructBalancedHelper(
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS],
+ int from,
+ int to) {
+ ASSERT(to > from);
+ if (to - from == 1) {
+ return building_blocks[from % NUMBER_OF_BUILDING_BLOCKS];
+ }
+ if (to - from == 2) {
+ return Factory::NewConsString(
+ building_blocks[from % NUMBER_OF_BUILDING_BLOCKS],
+ building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS]);
+ }
+ Handle<String> part1 =
+ ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2));
+ Handle<String> part2 =
+ ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to);
+ return Factory::NewConsString(part1, part2);
+}
+
+
+static Handle<String> ConstructBalanced(
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) {
+ return ConstructBalancedHelper(building_blocks, 0, DEEP_DEPTH);
+}
+
+
+static StringInputBuffer buffer;
+
+
+static void Traverse(Handle<String> s1, Handle<String> s2) {
+ int i = 0;
+ buffer.Reset(*s1);
+ StringInputBuffer buffer2(*s2);
+ while (buffer.has_more()) {
+ CHECK(buffer2.has_more());
+ uint16_t c = buffer.GetNext();
+ CHECK_EQ(c, buffer2.GetNext());
+ i++;
+ }
+ CHECK_EQ(s1->length(), i);
+ CHECK_EQ(s2->length(), i);
+}
+
+
+static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) {
+ int i = 0;
+ buffer.Reset(*s1);
+ StringInputBuffer buffer2(*s2);
+ while (buffer.has_more() && i < chars) {
+ CHECK(buffer2.has_more());
+ uint16_t c = buffer.GetNext();
+ CHECK_EQ(c, buffer2.GetNext());
+ i++;
+ }
+ s1->Get(s1->length() - 1);
+ s2->Get(s2->length() - 1);
+}
+
+
+TEST(Traverse) {
+ printf("TestTraverse\n");
+ InitializeVM();
+ v8::HandleScope scope;
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS];
+ ZoneScope zone(DELETE_ON_EXIT);
+ InitializeBuildingBlocks(building_blocks);
+ Handle<String> flat = ConstructBalanced(building_blocks);
+ FlattenString(flat);
+ Handle<String> left_asymmetric = ConstructLeft(building_blocks, DEEP_DEPTH);
+ Handle<String> right_asymmetric = ConstructRight(building_blocks, DEEP_DEPTH);
+ Handle<String> symmetric = ConstructBalanced(building_blocks);
+ printf("1\n");
+ Traverse(flat, symmetric);
+ printf("2\n");
+ Traverse(flat, left_asymmetric);
+ printf("3\n");
+ Traverse(flat, right_asymmetric);
+ printf("4\n");
+ Handle<String> left_deep_asymmetric =
+ ConstructLeft(building_blocks, SUPER_DEEP_DEPTH);
+ Handle<String> right_deep_asymmetric =
+ ConstructRight(building_blocks, SUPER_DEEP_DEPTH);
+ printf("5\n");
+ TraverseFirst(left_asymmetric, left_deep_asymmetric, 1050);
+ printf("6\n");
+ TraverseFirst(left_asymmetric, right_deep_asymmetric, 65536);
+ printf("7\n");
+ Handle<String> right_deep_slice =
+ Factory::NewStringSlice(left_deep_asymmetric,
+ left_deep_asymmetric->length() - 1050,
+ left_deep_asymmetric->length() - 50);
+ Handle<String> left_deep_slice =
+ Factory::NewStringSlice(right_deep_asymmetric,
+ right_deep_asymmetric->length() - 1050,
+ right_deep_asymmetric->length() - 50);
+ printf("8\n");
+ Traverse(right_deep_slice, left_deep_slice);
+ printf("9\n");
+ FlattenString(left_asymmetric);
+ printf("10\n");
+ Traverse(flat, left_asymmetric);
+ printf("11\n");
+ FlattenString(right_asymmetric);
+ printf("12\n");
+ Traverse(flat, right_asymmetric);
+ printf("14\n");
+ FlattenString(symmetric);
+ printf("15\n");
+ Traverse(flat, symmetric);
+ printf("16\n");
+ FlattenString(left_deep_asymmetric);
+ printf("18\n");
+}
+
+
+static Handle<String> SliceOf(Handle<String> underlying) {
+ int start = gen() % underlying->length();
+ int end = start + gen() % (underlying->length() - start);
+ return Factory::NewStringSlice(underlying,
+ start,
+ end);
+}
+
+
+static Handle<String> ConstructSliceTree(
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS],
+ int from,
+ int to) {
+ ASSERT(to > from);
+ if (to - from <= 1)
+ return SliceOf(building_blocks[from % NUMBER_OF_BUILDING_BLOCKS]);
+ if (to - from == 2) {
+ Handle<String> lhs = building_blocks[from % NUMBER_OF_BUILDING_BLOCKS];
+ if (gen() % 2 == 0)
+ lhs = SliceOf(lhs);
+ Handle<String> rhs = building_blocks[(from+1) % NUMBER_OF_BUILDING_BLOCKS];
+ if (gen() % 2 == 0)
+ rhs = SliceOf(rhs);
+ return Factory::NewConsString(lhs, rhs);
+ }
+ Handle<String> part1 =
+ ConstructBalancedHelper(building_blocks, from, from + ((to - from) / 2));
+ Handle<String> part2 =
+ ConstructBalancedHelper(building_blocks, from + ((to - from) / 2), to);
+ Handle<String> branch = Factory::NewConsString(part1, part2);
+ if (gen() % 2 == 0)
+ return branch;
+ return(SliceOf(branch));
+}
+
+
+TEST(Slice) {
+ printf("TestSlice\n");
+ InitializeVM();
+ v8::HandleScope scope;
+ Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS];
+ ZoneScope zone(DELETE_ON_EXIT);
+ InitializeBuildingBlocks(building_blocks);
+
+ seed = 42;
+ Handle<String> slice_tree =
+ ConstructSliceTree(building_blocks, 0, DEEP_DEPTH);
+ seed = 42;
+ Handle<String> flat_slice_tree =
+ ConstructSliceTree(building_blocks, 0, DEEP_DEPTH);
+ FlattenString(flat_slice_tree);
+ Traverse(flat_slice_tree, slice_tree);
+}
+
+static const int DEEP_ASCII_DEPTH = 100000;
+
+
+TEST(DeepAscii) {
+ printf("TestDeepAscii\n");
+ InitializeVM();
+ v8::HandleScope scope;
+
+ char* foo = NewArray<char>(DEEP_ASCII_DEPTH);
+ for (int i = 0; i < DEEP_ASCII_DEPTH; i++) {
+ foo[i] = "foo "[i % 4];
+ }
+ Handle<String> string =
+ Factory::NewStringFromAscii(Vector<const char>(foo, DEEP_ASCII_DEPTH));
+ Handle<String> foo_string = Factory::NewStringFromAscii(CStrVector("foo"));
+ for (int i = 0; i < DEEP_ASCII_DEPTH; i += 10) {
+ string = Factory::NewConsString(string, foo_string);
+ }
+ Handle<String> flat_string = Factory::NewConsString(string, foo_string);
+ FlattenString(flat_string);
+
+ for (int i = 0; i < 500; i++) {
+ TraverseFirst(flat_string, string, DEEP_ASCII_DEPTH);
+ }
+ DeleteArray<char>(foo);
+}
+
+
+TEST(Utf8Conversion) {
+ // Smoke test for converting strings to utf-8.
+ InitializeVM();
+ v8::HandleScope handle_scope;
+ // A simple ascii string
+ const char* ascii_string = "abcdef12345";
+ int len = v8::String::New(ascii_string, strlen(ascii_string))->Utf8Length();
+ CHECK_EQ(strlen(ascii_string), len);
+ // A mixed ascii and non-ascii string
+ // U+02E4 -> CB A4
+ // U+0064 -> 64
+ // U+12E4 -> E1 8B A4
+ // U+0030 -> 30
+ // U+3045 -> E3 81 85
+ const uint16_t mixed_string[] = {0x02E4, 0x0064, 0x12E4, 0x0030, 0x3045};
+ // The characters we expect to be output
+ const unsigned char as_utf8[11] = {0xCB, 0xA4, 0x64, 0xE1, 0x8B, 0xA4, 0x30,
+ 0xE3, 0x81, 0x85, 0x00};
+ // The number of bytes expected to be written for each length
+ const int lengths[12] = {0, 0, 2, 3, 3, 3, 6, 7, 7, 7, 10, 11};
+ v8::Handle<v8::String> mixed = v8::String::New(mixed_string, 5);
+ CHECK_EQ(10, mixed->Utf8Length());
+ // Try encoding the string with all capacities
+ char buffer[11];
+ const char kNoChar = static_cast<char>(-1);
+ for (int i = 0; i <= 11; i++) {
+ // Clear the buffer before reusing it
+ for (int j = 0; j < 11; j++)
+ buffer[j] = kNoChar;
+ int written = mixed->WriteUtf8(buffer, i);
+ CHECK_EQ(lengths[i], written);
+ // Check that the contents are correct
+ for (int j = 0; j < lengths[i]; j++)
+ CHECK_EQ(as_utf8[j], static_cast<unsigned char>(buffer[j]));
+ // Check that the rest of the buffer hasn't been touched
+ for (int j = lengths[i]; j < 11; j++)
+ CHECK_EQ(kNoChar, buffer[j]);
+ }
+}
diff --git a/v8/test/cctest/test-threads.cc b/v8/test/cctest/test-threads.cc
new file mode 100644
index 0000000..c0d55f2
--- /dev/null
+++ b/v8/test/cctest/test-threads.cc
@@ -0,0 +1,54 @@
+// Copyright 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 "v8.h"
+
+#include "platform.h"
+
+#include "cctest.h"
+
+
+TEST(Preemption) {
+ v8::Locker locker;
+ v8::V8::Initialize();
+ v8::HandleScope scope;
+ v8::Context::Scope context_scope(v8::Context::New());
+
+ v8::Locker::StartPreemption(100);
+
+ v8::Handle<v8::Script> script = v8::Script::Compile(
+ v8::String::New("var count = 0; var obj = new Object(); count++;\n"));
+
+ script->Run();
+
+ v8::Locker::StopPreemption();
+ v8::internal::OS::Sleep(500); // Make sure the timer fires.
+
+ script->Run();
+}
+
+
diff --git a/v8/test/cctest/test-utils.cc b/v8/test/cctest/test-utils.cc
new file mode 100644
index 0000000..acb5d3c
--- /dev/null
+++ b/v8/test/cctest/test-utils.cc
@@ -0,0 +1,179 @@
+// Copyright 2006-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 "v8.h"
+
+#include "platform.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+enum Mode {
+ forward,
+ backward_unsigned
+};
+
+
+static v8::internal::byte* Write(v8::internal::byte* p, Mode m, int x) {
+ v8::internal::byte* q = NULL;
+ switch (m) {
+ case forward:
+ q = EncodeInt(p, x);
+ CHECK(q <= p + sizeof(x) + 1);
+ break;
+ case backward_unsigned:
+ q = EncodeUnsignedIntBackward(p, x);
+ CHECK(q >= p - sizeof(x) - 1);
+ break;
+ }
+ return q;
+}
+
+
+static v8::internal::byte* Read(v8::internal::byte* p, Mode m, int x) {
+ v8::internal::byte* q = NULL;
+ int y;
+ switch (m) {
+ case forward:
+ q = DecodeInt(p, &y);
+ CHECK(q <= p + sizeof(y) + 1);
+ break;
+ case backward_unsigned: {
+ unsigned int uy;
+ q = DecodeUnsignedIntBackward(p, &uy);
+ y = uy;
+ CHECK(q >= p - sizeof(uy) - 1);
+ break;
+ }
+ }
+ CHECK(y == x);
+ return q;
+}
+
+
+static v8::internal::byte* WriteMany(v8::internal::byte* p, Mode m, int x) {
+ p = Write(p, m, x - 7);
+ p = Write(p, m, x - 1);
+ p = Write(p, m, x);
+ p = Write(p, m, x + 1);
+ p = Write(p, m, x + 2);
+ p = Write(p, m, -x - 5);
+ p = Write(p, m, -x - 1);
+ p = Write(p, m, -x);
+ p = Write(p, m, -x + 1);
+ p = Write(p, m, -x + 3);
+
+ return p;
+}
+
+
+static v8::internal::byte* ReadMany(v8::internal::byte* p, Mode m, int x) {
+ p = Read(p, m, x - 7);
+ p = Read(p, m, x - 1);
+ p = Read(p, m, x);
+ p = Read(p, m, x + 1);
+ p = Read(p, m, x + 2);
+ p = Read(p, m, -x - 5);
+ p = Read(p, m, -x - 1);
+ p = Read(p, m, -x);
+ p = Read(p, m, -x + 1);
+ p = Read(p, m, -x + 3);
+
+ return p;
+}
+
+
+void ProcessValues(int* values, int n, Mode m) {
+ v8::internal::byte buf[4 * KB]; // make this big enough
+ v8::internal::byte* p0 = (m == forward ? buf : buf + ARRAY_SIZE(buf));
+
+ v8::internal::byte* p = p0;
+ for (int i = 0; i < n; i++) {
+ p = WriteMany(p, m, values[i]);
+ }
+
+ v8::internal::byte* q = p0;
+ for (int i = 0; i < n; i++) {
+ q = ReadMany(q, m, values[i]);
+ }
+
+ CHECK(p == q);
+}
+
+
+TEST(Utils0) {
+ int values[] = {
+ 0, 1, 10, 16, 32, 64, 128, 256, 512, 1024, 1234, 5731,
+ 10000, 100000, 1000000, 10000000, 100000000, 1000000000
+ };
+ const int n = ARRAY_SIZE(values);
+
+ ProcessValues(values, n, forward);
+ ProcessValues(values, n, backward_unsigned);
+}
+
+
+TEST(Utils1) {
+ CHECK_EQ(-1000000, FastD2I(-1000000.0));
+ CHECK_EQ(-1, FastD2I(-1.0));
+ CHECK_EQ(0, FastD2I(0.0));
+ CHECK_EQ(1, FastD2I(1.0));
+ CHECK_EQ(1000000, FastD2I(1000000.0));
+
+ CHECK_EQ(-1000000, FastD2I(-1000000.123));
+ CHECK_EQ(-1, FastD2I(-1.234));
+ CHECK_EQ(0, FastD2I(0.345));
+ CHECK_EQ(1, FastD2I(1.234));
+ CHECK_EQ(1000000, FastD2I(1000000.123));
+}
+
+
+TEST(SNPrintF) {
+ // Make sure that strings that are truncated because of too small
+ // buffers are zero-terminated anyway.
+ const char* s = "the quick lazy .... oh forget it!";
+ int length = strlen(s);
+ for (int i = 1; i < length * 2; i++) {
+ static const char kMarker = static_cast<char>(42);
+ Vector<char> buffer = Vector<char>::New(i + 1);
+ buffer[i] = kMarker;
+ int n = OS::SNPrintF(Vector<char>(buffer.start(), i), "%s", s);
+ CHECK(n <= i);
+ CHECK(n == length || n == -1);
+ CHECK_EQ(0, strncmp(buffer.start(), s, i - 1));
+ CHECK_EQ(kMarker, buffer[i]);
+ if (i <= length) {
+ CHECK_EQ(i - 1, strlen(buffer.start()));
+ } else {
+ CHECK_EQ(length, strlen(buffer.start()));
+ }
+ buffer.Dispose();
+ }
+}
diff --git a/v8/test/cctest/testcfg.py b/v8/test/cctest/testcfg.py
new file mode 100644
index 0000000..75377db
--- /dev/null
+++ b/v8/test/cctest/testcfg.py
@@ -0,0 +1,108 @@
+# Copyright 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.
+
+import test
+import os
+from os.path import join, dirname, exists
+import platform
+import utils
+
+DEBUG_FLAGS = ['--enable-slow-asserts', '--debug-code', '--verify-heap']
+
+
+class CcTestCase(test.TestCase):
+
+ def __init__(self, path, executable, mode, raw_name, dependency, context):
+ super(CcTestCase, self).__init__(context, path)
+ self.executable = executable
+ self.mode = mode
+ self.raw_name = raw_name
+ self.dependency = dependency
+
+ def GetLabel(self):
+ return "%s %s %s" % (self.mode, self.path[-2], self.path[-1])
+
+ def GetName(self):
+ return self.path[-1]
+
+ def BuildCommand(self, name):
+ serialization_file = join('obj', 'test', self.mode, 'serdes')
+ serialization_file += '_' + self.GetName()
+ serialization_option = '--testing_serialization_file=' + serialization_file
+ result = [ self.executable, name, serialization_option ]
+ if self.mode == 'debug':
+ result += DEBUG_FLAGS
+ return result
+
+ def GetCommand(self):
+ return self.BuildCommand(self.raw_name)
+
+ def Run(self):
+ if self.dependency != '':
+ dependent_command = self.BuildCommand(self.dependency)
+ output = self.RunCommand(dependent_command)
+ if output.HasFailed():
+ return output
+ return test.TestCase.Run(self)
+
+
+class CcTestConfiguration(test.TestConfiguration):
+
+ def __init__(self, context, root):
+ super(CcTestConfiguration, self).__init__(context, root)
+
+ def GetBuildRequirements(self):
+ return ['cctests']
+
+ def ListTests(self, current_path, path, mode):
+ executable = join('obj', 'test', mode, 'cctest')
+ if utils.IsWindows():
+ executable += '.exe'
+ output = test.Execute([executable, '--list'], self.context)
+ if output.exit_code != 0:
+ print output.stdout
+ print output.stderr
+ return []
+ result = []
+ for test_desc in output.stdout.strip().split():
+ raw_test, dependency = test_desc.split('<')
+ relative_path = raw_test.split('/')
+ full_path = current_path + relative_path
+ if dependency != '':
+ dependency = relative_path[0] + '/' + dependency
+ if self.Contains(path, full_path):
+ result.append(CcTestCase(full_path, executable, mode, raw_test, dependency, self.context))
+ return result
+
+ def GetTestStatus(self, sections, defs):
+ status_file = join(self.root, 'cctest.status')
+ if exists(status_file):
+ test.ReadConfigurationInto(status_file, sections, defs)
+
+
+def GetConfiguration(context, root):
+ return CcTestConfiguration(context, root)