Move V8 to external/v8
Change-Id: If68025d67453785a651c5dfb34fad298c16676a4
diff --git a/test/cctest/test-decls.cc b/test/cctest/test-decls.cc
new file mode 100644
index 0000000..f083027
--- /dev/null
+++ b/test/cctest/test-decls.cc
@@ -0,0 +1,593 @@
+// 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 = External::New(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) {
+ return static_cast<DeclarationContext*>(External::Unwrap(info.Data()));
+}
+
+
+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:
+ CHECK(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:
+ CHECK(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",
+ 1, // get
+ 0,
+ 1, // declaration
+ EXPECT_EXCEPTION);
+ }
+
+ { 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());
+ }
+}