changeset 392:69adcefe73ae

Js: make it object-oriented
author David Demelier <markand@malikania.fr>
date Mon, 28 Sep 2015 19:40:16 +0200
parents e2cefd0ee511
children 30788c97c58c
files C++/modules/Js/Js.cpp C++/modules/Js/Js.h C++/tests/Js/main.cpp
diffstat 3 files changed, 688 insertions(+), 774 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Js/Js.cpp	Mon Sep 28 13:18:14 2015 +0200
+++ b/C++/modules/Js/Js.cpp	Mon Sep 28 19:40:16 2015 +0200
@@ -56,151 +56,42 @@
 
 } // !namespace
 
-/* ------------------------------------------------------------------
- * Context class
- * ------------------------------------------------------------------ */
-
-Context::Context()
-	: std::unique_ptr<duk_context, void (*)(duk_context *)>(duk_create_heap_default(), duk_destroy_heap)
-{
-}
-
-/* --------------------------------------------------------
- * Push functions
- * -------------------------------------------------------- */
-
-void push(ContextPtr ctx, bool value)
-{
-	duk_push_boolean(ctx, value);
-}
-
-void push(ContextPtr ctx, int value)
-{
-	duk_push_int(ctx, value);
-}
-
-void push(ContextPtr ctx, double value)
-{
-	duk_push_number(ctx, value);
-}
-
-void push(ContextPtr ctx, const std::string &value)
-{
-	duk_push_lstring(ctx, value.c_str(), value.length());
-}
-
-void push(ContextPtr ctx, const char *value)
-{
-	duk_push_string(ctx, value);
-}
-
-void push(ContextPtr ctx, const Function &fn)
-{
-	assert(fn.function);
-
-	duk_push_c_function(ctx, fn.function, fn.nargs);
-}
-
-void push(ContextPtr ctx, const Object &)
-{
-	duk_push_object(ctx);
-}
-
-void push(ContextPtr ctx, const Array &)
-{
-	duk_push_array(ctx);
-}
-
-/* --------------------------------------------------------
- * Get functions
- * -------------------------------------------------------- */
-
-void get(ContextPtr ctx, duk_idx_t index, bool &value)
-{
-	value = duk_get_boolean(ctx, index);
-}
-
-void get(ContextPtr ctx, duk_idx_t index, int &value)
-{
-	value = duk_get_int(ctx, index);
-}
-
-void get(ContextPtr ctx, duk_idx_t index, double &value)
-{
-	value = duk_get_number(ctx, index);
-}
-
-void get(ContextPtr ctx, duk_idx_t index, std::string &value)
-{
-	duk_size_t size;
-	const char *text = duk_get_lstring(ctx, index, &size);
-
-	value = std::string{text, size};
-}
-
-/* --------------------------------------------------------
- * Require functions
- * -------------------------------------------------------- */
-
-void require(ContextPtr ctx, duk_idx_t index, bool &value)
-{
-	value = duk_require_boolean(ctx, index);
-}
-
-void require(ContextPtr ctx, duk_idx_t index, int &value)
-{
-	value = duk_require_int(ctx, index);
-}
-
-void require(ContextPtr ctx, duk_idx_t index, double &value)
-{
-	value = duk_require_number(ctx, index);
-}
-
-void require(ContextPtr ctx, duk_idx_t index, std::string &value)
-{
-	duk_size_t size;
-	const char *text = duk_require_lstring(ctx, index, &size);
-
-	value = std::string{text, size};
-}
-
 /* --------------------------------------------------------
  * Basic functions
  * -------------------------------------------------------- */
 
-duk_int_t type(ContextPtr ctx, duk_idx_t index)
+duk_int_t Context::type(duk_idx_t index)
 {
-	return duk_get_type(ctx, index);
+	return duk_get_type(m_handle.get(), index);
 }
 
-int top(ContextPtr ctx)
+int Context::top()
 {
-	return duk_get_top(ctx);
+	return duk_get_top(m_handle.get());
 }
 
-void pop(ContextPtr ctx, int count)
+void Context::pop(int count)
 {
 	assert(count > 0);
 
-	duk_pop_n(ctx, count);
+	duk_pop_n(m_handle.get(), count);
 }
 
-duk_bool_t instanceof(ContextPtr ctx, duk_idx_t idx1, duk_idx_t idx2)
+duk_bool_t Context::instanceof(duk_idx_t idx1, duk_idx_t idx2)
 {
-	return duk_instanceof(ctx, idx1, idx2);
+	return duk_instanceof(m_handle.get(), idx1, idx2);
 }
 
-void call(ContextPtr ctx, duk_idx_t nargs)
+void Context::call(duk_idx_t nargs)
 {
-	duk_call(ctx, nargs);
+	duk_call(m_handle.get(), nargs);
 }
 
-void pcall(ContextPtr ctx, duk_idx_t nargs)
+void Context::pcall(duk_idx_t nargs)
 {
-	if (duk_pcall(ctx, nargs) != 0) {
-		ErrorInfo info = error(ctx, -1);
-		pop(ctx);
+	if (duk_pcall(m_handle.get(), nargs) != 0) {
+		ErrorInfo info = error(m_handle.get(), -1);
+		pop();
 
 		throw info;
 	}
@@ -210,43 +101,43 @@
  * Eval functions
  * ------------------------------------------------------------------ */
 
-void eval(ContextPtr ctx)
+void Context::eval()
 {
-	duk_eval(ctx);
+	duk_eval(m_handle.get());
 }
 
-void evalString(ContextPtr ctx, const std::string &name)
+void Context::evalString(const std::string &name)
 {
-	duk_eval_string(ctx, name.c_str());
+	duk_eval_string(m_handle.get(), name.c_str());
 }
 
-void evalFile(ContextPtr ctx, const std::string &file)
+void Context::evalFile(const std::string &file)
 {
-	duk_eval_file(ctx, file.c_str());
+	duk_eval_file(m_handle.get(), file.c_str());
 }
 
-void peval(ContextPtr ctx)
+void Context::peval()
 {
-	if (duk_peval(ctx) != 0) {
-		throw error(ctx, -1);
+	if (duk_peval(m_handle.get()) != 0) {
+		throw error(m_handle.get(), -1);
 	}
 }
 
-void pevalString(ContextPtr ctx, const std::string &script)
+void Context::pevalString(const std::string &script)
 {
-	if (duk_peval_string(ctx, script.c_str()) != 0) {
-		ErrorInfo info = error(ctx, -1);
-		pop(ctx);
+	if (duk_peval_string(m_handle.get(), script.c_str()) != 0) {
+		ErrorInfo info = error(m_handle.get(), -1);
+		pop();
 
 		throw info;
 	}
 }
 
-void pevalFile(ContextPtr ctx, const std::string &file)
+void Context::pevalFile(const std::string &file)
 {
-	if (duk_peval_file(ctx, file.c_str()) != 0) {
-		ErrorInfo info = error(ctx, -1);
-		pop(ctx);
+	if (duk_peval_file(m_handle.get(), file.c_str()) != 0) {
+		ErrorInfo info = error(m_handle.get(), -1);
+		pop();
 
 		throw info;
 	}
--- a/C++/modules/Js/Js.h	Mon Sep 28 13:18:14 2015 +0200
+++ b/C++/modules/Js/Js.h	Mon Sep 28 19:40:16 2015 +0200
@@ -68,47 +68,6 @@
 using ContextPtr = duk_context *;
 
 /**
- * @class Context
- * @brief RAII based Duktape handler
- *
- * This class is implicitly convertible to duk_context for convenience.
- */
-class Context : public std::unique_ptr<duk_context, void (*)(duk_context *)> {
-private:
-	/* Move and copy forbidden */
-	Context(const Context &) = delete;
-	Context &operator=(const Context &) = delete;
-	Context(const Context &&) = delete;
-	Context &operator=(const Context &&) = delete;
-
-public:
-	/**
-	 * Create a Duktape context using defaults.
-	 */
-	Context();
-
-	/**
-	 * Convert the context to the native Duktape/C type.
-	 *
-	 * @return the duk_context
-	 */
-	inline operator duk_context *() noexcept
-	{
-		return get();
-	}
-
-	/**
-	 * Convert the context to the native Duktape/C type.
-	 *
-	 * @return the duk_context
-	 */
-	inline operator duk_context *() const noexcept
-	{
-		return get();
-	}
-};
-
-/**
  * @class Object
  * @brief Empty class tag for push() function
  */
@@ -164,398 +123,515 @@
 	}
 };
 
-/* --------------------------------------------------------
- * Push functions
- * -------------------------------------------------------- */
-
-/**
- * Push a boolean.
- *
- * @param ctx the context
- * @param value the boolean value
- */
-void push(ContextPtr ctx, bool value);
-
-/**
- * Push an integer.
- *
- * @param ctx the context
- * @param value the integer value
- */
-void push(ContextPtr ctx, int value);
-
-/**
- * Push a real value.
- *
- * @param ctx the context
- * @param value the value
- */
-void push(ContextPtr ctx, double value);
-
-/**
- * Push a string.
- *
- * @param ctx the context
- * @param value the string
- */
-void push(ContextPtr ctx, const std::string &value);
-
-/**
- * Push a literal or C-string.
- *
- * @param ctx the context
- * @param value the value
- */
-void push(ContextPtr ctx, const char *value);
-
-/**
- * Push a Duktape/C function.
- *
- * @param ctx the context
- * @param function the function
- */
-void push(ContextPtr ctx, const Function &function);
-
-/**
- * Push an empty object on the stack.
- *
- * @param ctx the context
- * @param object the empty object
- */
-void push(ContextPtr ctx, const Object &object);
-
-/**
- * Push an empty array at the top of the stack.
- *
- * @param ctx the context
- * @param array the empty array
- */
-void push(ContextPtr ctx, const Array &array);
-
-/* --------------------------------------------------------
- * Get functions
- * -------------------------------------------------------- */
-
-/**
- * Get a boolean.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void get(ContextPtr ctx, duk_idx_t index, bool &value);
-
-/**
- * Get an integer.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void get(ContextPtr ctx, duk_idx_t index, int &value);
-
-/**
- * Get a real.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void get(ContextPtr ctx, duk_idx_t index, double &value);
-
-/**
- * Get a string.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void get(ContextPtr ctx, duk_idx_t index, std::string &value);
-
-/**
- * Generic template function to get a value from the stack.
- *
- * @param ctx the context
- * @param index the index
- * @return the value
- */
 template <typename Type>
-inline Type get(ContextPtr ctx, duk_idx_t index)
-{
-	Type value;
-
-	get(ctx, index, value);
-
-	return value;
-}
-
-/* --------------------------------------------------------
- * Get functions (for object)
- * -------------------------------------------------------- */
-
-template <typename Type>
-inline Type getObject(ContextPtr ctx, duk_idx_t index, const std::string &name)
-{
-	Type type;
-
-	assertBegin(ctx);
-	duk_get_prop_string(ctx, index, name.c_str());
-	get(ctx, -1, type);
-	duk_pop(ctx);
-	assertEquals(ctx);
-
-	return type;
-}
-
-/* --------------------------------------------------------
- * Set functions (for object)
- * -------------------------------------------------------- */
-
-template <typename Type>
-void setObject(ContextPtr ctx, duk_idx_t index, const std::string &name, Type&& value)
-{
-	index = duk_normalize_index(ctx, index);
-
-	assertBegin(ctx);
-	push(ctx, std::forward<Type>(value));
-	duk_put_prop_string(ctx, index, name.c_str());
-	assertEquals(ctx);
-}
-
-/* --------------------------------------------------------
- * Require functions
- * -------------------------------------------------------- */
-
-/**
- * Requires a value or throw an exception if not a boolean.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void require(ContextPtr ctx, duk_idx_t index, bool &value);
-
-/**
- * Requires a value or throw an exception if not an integer.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void require(ContextPtr ctx, duk_idx_t index, int &value);
-
-/**
- * Requires a value or throw an exception if not a double.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store the value
- */
-void require(ContextPtr ctx, duk_idx_t index, double &value);
+class TypeInfo {
+public:
+};
 
 /**
- * Requires a value or throw an exception if not a string.
- *
- * @param ctx the context
- * @param index the index
- * @param value the reference where to store string
- */
-void require(ContextPtr ctx, duk_idx_t index, std::string &value);
-
-/**
- * Generic function to require a value from the stack.
- *
- * @param ctx the context
- * @param index the specified index
- * @return the value
- */
-template <typename Type>
-inline Type require(ContextPtr ctx, duk_idx_t index)
-{
-	Type value;
-
-	require(ctx, index, value);
-
-	return value;
-}
-
-/* --------------------------------------------------------
- * Basic functions
- * -------------------------------------------------------- */
-
-/**
- * Get the type of the value at the specified index.
- *
- * @param ctx the context
- * @param index the idnex
- * @return the type
- */
-duk_int_t type(ContextPtr ctx, duk_idx_t index);
-
-/**
- * Get the current stack size.
- *
- * @param ctx the context
- * @return the stack size
- */
-int top(ContextPtr ctx);
-
-/**
- * Pop a certain number of values from the top of the stack.
- *
- * @param ctx the context
- * @param count the number of values to pop
- * @pre count must be positive
- */
-void pop(ContextPtr ctx, int count = 1);
-
-/**
- * Check if idx1 is an instance of idx2.
- *
- * @param ctx the context
- * @param idx1 the value to test
- * @param idx2 the instance requested
- * @return true if idx1 is instance of idx2
- */
-duk_bool_t instanceof(ContextPtr ctx, duk_idx_t idx1, duk_idx_t idx2);
-
-/**
- * Call the object at the top of the stack.
- *
- * @param ctx the context
- * @param nargs the number of arguments
- * @note Non-protected
- */
-void call(ContextPtr ctx, duk_idx_t nargs = 0);
-
-/**
- * Call in protected mode the object at the top of the stack.
+ * @class Context
+ * @brief RAII based Duktape handler
  *
- * @param ctx the context
- * @param nargs the number of arguments
- * @throw ErrorInfo on errors
+ * This class is implicitly convertible to duk_context for convenience.
  */
-void pcall(ContextPtr ctx, duk_idx_t nargs = 0);
+class Context {
+private:
+	using Deleter = void (*)(duk_context *);
+	using Handle = std::unique_ptr<duk_context, Deleter>;
+
+	Handle m_handle;
+
+	/* Move and copy forbidden */
+	Context(const Context &) = delete;
+	Context &operator=(const Context &) = delete;
+	Context(const Context &&) = delete;
+	Context &operator=(const Context &&) = delete;
+
+
+public:
+	inline Context()
+		: m_handle{duk_create_heap_default(), duk_destroy_heap}
+	{
+	}
+
+	inline Context(ContextPtr ctx) noexcept
+		: m_handle{ctx, [] (ContextPtr) {}}
+	{
+	}
 
-/* ------------------------------------------------------------------
- * Eval functions
- * ------------------------------------------------------------------ */
+#if 0
+	/**
+	 * Convert the context to the native Duktape/C type.
+	 *
+	 * @return the duk_context
+	 */
+	inline operator duk_context *() noexcept
+	{
+		return get();
+	}
 
-/**
- * Evaluate a non-protected chunk at the top of the stack.
- *
- * @param ctx
- */
-void eval(ContextPtr ctx);
+	/**
+	 * Convert the context to the native Duktape/C type.
+	 *
+	 * @return the duk_context
+	 */
+	inline operator duk_context *() const noexcept
+	{
+		return get();
+	}
+#endif
+
+	/* --------------------------------------------------------
+	 * Push functions
+	 * -------------------------------------------------------- */
+
+#if 0
 
-/**
- * Evaluate a non-protected string script.
- *
- * @param ctx the context
- * @param script the script content
- */
-void evalString(ContextPtr ctx, const std::string &script);
+	/**
+	 * Push a boolean.
+	 *
+	 * @param ctx the context
+	 * @param value the boolean value
+	 */
+	inline void push(bool value)
+	{
+		duk_push_boolean(m_handle.get(), value);
+	}
+
+	/**
+	 * Push an integer.
+	 *
+	 * @param ctx the context
+	 * @param value the integer value
+	 */
+	inline void push(int value)
+	{
+		duk_push_int(m_handle.get(), value);
+	}
 
-/**
- * Evaluate a non-protected file.
- *
- * @param ctx the context
- * @param file the file
- */
-void evalFile(ContextPtr ctx, const std::string &file);
+	/**
+	 * Push a real value.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	void push(double value)
+	{
+		duk_push_number(m_handle.get(), value);
+	}
 
-/**
- * Evaluate a protected chunk.
- *
- * @param ctx the context
- * @throw ErrorInfo the error
- */
-void peval(ContextPtr ctx);
+	/**
+	 * Push a string.
+	 *
+	 * @param ctx the context
+	 * @param value the string
+	 */
+	void push(const std::string &value)
+	{
+		duk_push_lstring(m_handle.get(), value.c_str(), value.length());
+	}
+
+	/**
+	 * Push a literal or C-string.
+	 *
+	 * @param ctx the context
+	 * @param value the value
+	 */
+	void push(const char *value)
+	{
+		duk_push_string(m_handle.get(), value);
+	}
+
+	/**
+	 * Push a Duktape/C function.
+	 *
+	 * @param ctx the context
+	 * @param function the function
+	 */
+	void push(const Function &fn)
+	{
+		assert(fn.function);
 
-/**
- * Evaluate a protected string script.
- *
- * @param ctx the context
- * @param script the script content
- * @throw ErrorInfo the error
- */
-void pevalString(ContextPtr ctx, const std::string &script);
+		duk_push_c_function(m_handle.get(), fn.function, fn.nargs);
+	}
+
+	/**
+	 * Push an empty object on the stack.
+	 *
+	 * @param ctx the context
+	 * @param object the empty object
+	 */
+	void push(const Object &)
+	{
+		duk_push_object(m_handle.get());
+	}
+
+	/**
+	 * Push an empty array at the top of the stack.
+	 *
+	 * @param ctx the context
+	 * @param array the empty array
+	 */
+	void push(const Array &)
+	{
+		duk_push_array(m_handle.get());
+	}
 
-/**
- * Evaluate a protected file.
- *
- * @param ctx the context
- * @param file the file
- * @throw ErrorInfo the error
- */
-void pevalFile(ContextPtr ctx, const std::string &file);
+	/* --------------------------------------------------------
+	 * Get functions
+	 * -------------------------------------------------------- */
+
+	/**
+	 * Get a boolean.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void get(ContextPtr ctx, duk_idx_t index, bool &value);
+
+	/**
+	 * Get an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void get(ContextPtr ctx, duk_idx_t index, int &value);
+
+	/**
+	 * Get a real.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void get(ContextPtr ctx, duk_idx_t index, double &value);
 
-/* ------------------------------------------------------------------
- * Extended functions
- * ------------------------------------------------------------------ */
+	/**
+	 * Get a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void get(ContextPtr ctx, duk_idx_t index, std::string &value);
+
+#endif
+
+	/* ----------------------------------------------------------
+	 * Push / Get / Require
+	 * ---------------------------------------------------------- */
+
+	template <typename Type>
+	inline void push(Type &&value)
+	{
+		TypeInfo<std::decay_t<Type>>::push(m_handle.get(), std::forward<Type>(value));
+	}
+
+	/**
+	 * Generic template function to get a value from the stack.
+	 *
+	 * @param index the index
+	 * @return the value
+	 */
+	template <typename Type>
+	inline Type get(duk_idx_t index)
+	{
+		return TypeInfo<Type>::get(m_handle.get(), index);
+	}
 
-/**
- * Enumerate an object or an array at the specified index.
- *
- * @param ctx the context
- * @param index the object or array index
- * @param flags the optional flags to pass to duk_enum
- * @param getvalue set to true if you want to extract the value
- * @param func the function to call for each properties
- */
-template <typename Func>
-void enumerate(ContextPtr ctx, duk_idx_t index, duk_uint_t flags, duk_bool_t getvalue, Func&& func)
-{
-	assertBegin(ctx);
-	duk_enum(ctx, index, flags);
+	template <typename Type>
+	inline Type require(duk_idx_t index)
+	{
+		return TypeInfo<Type>::require(m_handle.get(), index);
+	}
+
+	/* --------------------------------------------------------
+	 * Get functions (for object)
+	 * -------------------------------------------------------- */
 
-	while (duk_next(ctx, -1, getvalue)) {
-		func(ctx);
-		duk_pop_n(ctx, 1 + (getvalue ? 1 : 0));
+	template <typename Type>
+	inline Type getObject(duk_idx_t index, const std::string &name)
+	{
+		assertBegin(m_handle.get());
+		duk_get_prop_string(m_handle.get(), index, name.c_str());
+		Type value = get<Type>(-1);
+		duk_pop(m_handle.get());
+		assertEquals(m_handle.get());
+
+		return value;
+	}
+
+	/* --------------------------------------------------------
+	 * Set functions (for object)
+	 * -------------------------------------------------------- */
+
+	template <typename Type>
+	void setObject(duk_idx_t index, const std::string &name, Type&& value)
+	{
+		index = duk_normalize_index(m_handle.get(), index);
+
+		assertBegin(m_handle.get());
+		push(std::forward<Type>(value));
+		duk_put_prop_string(m_handle.get(), index, name.c_str());
+		assertEquals(m_handle.get());
 	}
 
-	duk_pop(ctx);
-	assertEquals(ctx);
-}
+	/* --------------------------------------------------------
+	 * Require functions
+	 * -------------------------------------------------------- */
+
+#if 0
+
+	/**
+	 * Requires a value or throw an exception if not a boolean.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void require(ContextPtr ctx, duk_idx_t index, bool &value);
+
+	/**
+	 * Requires a value or throw an exception if not an integer.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void require(ContextPtr ctx, duk_idx_t index, int &value);
+
+	/**
+	 * Requires a value or throw an exception if not a double.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store the value
+	 */
+	void require(ContextPtr ctx, duk_idx_t index, double &value);
+
+	/**
+	 * Requires a value or throw an exception if not a string.
+	 *
+	 * @param ctx the context
+	 * @param index the index
+	 * @param value the reference where to store string
+	 */
+	void require(ContextPtr ctx, duk_idx_t index, std::string &value);
+
+	/**
+	 * Generic function to require a value from the stack.
+	 *
+	 * @param ctx the context
+	 * @param index the specified index
+	 * @return the value
+	 */
+	template <typename Type>
+	inline Type require(ContextPtr ctx, duk_idx_t index)
+	{
+		Type value;
+
+		require(ctx, index, value);
+
+		return value;
+	}
+#endif
 
-/* --------------------------------------------------------
- * Global functions
- * -------------------------------------------------------- */
+	/* --------------------------------------------------------
+	 * Basic functions
+	 * -------------------------------------------------------- */
+
+	/**
+	 * Get the type of the value at the specified index.
+	 *
+	 * @param ctx the context
+	 * @param index the idnex
+	 * @return the type
+	 */
+	duk_int_t type(duk_idx_t index);
+
+	/**
+	 * Get the current stack size.
+	 *
+	 * @param ctx the context
+	 * @return the stack size
+	 */
+	int top();
+
+	/**
+	 * Pop a certain number of values from the top of the stack.
+	 *
+	 * @param ctx the context
+	 * @param count the number of values to pop
+	 * @pre count must be positive
+	 */
+	void pop(int count = 1);
+
+	/**
+	 * Check if idx1 is an instance of idx2.
+	 *
+	 * @param ctx the context
+	 * @param idx1 the value to test
+	 * @param idx2 the instance requested
+	 * @return true if idx1 is instance of idx2
+	 */
+	duk_bool_t instanceof(duk_idx_t idx1, duk_idx_t idx2);
+
+	/**
+	 * Call the object at the top of the stack.
+	 *
+	 * @param ctx the context
+	 * @param nargs the number of arguments
+	 * @note Non-protected
+	 */
+	void call(duk_idx_t nargs = 0);
+
+	/**
+	 * Call in protected mode the object at the top of the stack.
+	 *
+	 * @param ctx the context
+	 * @param nargs the number of arguments
+	 * @throw ErrorInfo on errors
+	 */
+	void pcall(duk_idx_t nargs = 0);
+
+	/* ------------------------------------------------------------------
+	 * Eval functions
+	 * ------------------------------------------------------------------ */
 
-/**
- * Get a global value.
- *
- * @param ctx the context
- * @param name the name of the global variable
- * @return the value
- */
-template <typename Type>
-inline Type getGlobal(ContextPtr ctx, const std::string &name)
-{
-	assertBegin(ctx);
-	duk_get_global_string(ctx, name.c_str());
-	Type value = get<Type>(ctx, -1);
-	duk_pop(ctx);
-	assertEquals(ctx);
+	/**
+	 * Evaluate a non-protected chunk at the top of the stack.
+	 *
+	 * @param ctx
+	 */
+	void eval();
+
+	/**
+	 * Evaluate a non-protected string script.
+	 *
+	 * @param ctx the context
+	 * @param script the script content
+	 */
+	void evalString(const std::string &script);
+
+	/**
+	 * Evaluate a non-protected file.
+	 *
+	 * @param ctx the context
+	 * @param file the file
+	 */
+	void evalFile(const std::string &file);
+
+	/**
+	 * Evaluate a protected chunk.
+	 *
+	 * @param ctx the context
+	 * @throw ErrorInfo the error
+	 */
+	void peval();
+
+	/**
+	 * Evaluate a protected string script.
+	 *
+	 * @param ctx the context
+	 * @param script the script content
+	 * @throw ErrorInfo the error
+	 */
+	void pevalString(const std::string &script);
+
+	/**
+	 * Evaluate a protected file.
+	 *
+	 * @param ctx the context
+	 * @param file the file
+	 * @throw ErrorInfo the error
+	 */
+	void pevalFile(const std::string &file);
+
+	/* ------------------------------------------------------------------
+	 * Extended functions
+	 * ------------------------------------------------------------------ */
 
-	return value;
-}
+	/**
+	 * Enumerate an object or an array at the specified index.
+	 *
+	 * @param ctx the context
+	 * @param index the object or array index
+	 * @param flags the optional flags to pass to duk_enum
+	 * @param getvalue set to true if you want to extract the value
+	 * @param func the function to call for each properties
+	 */
+	template <typename Func>
+	void enumerate(duk_idx_t index, duk_uint_t flags, duk_bool_t getvalue, Func&& func)
+	{
+		assertBegin(m_handle.get());
+		duk_enum(m_handle.get(), index, flags);
+
+		while (duk_next(m_handle.get(), -1, getvalue)) {
+			func(*this);
+			duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0));
+		}
+
+		duk_pop(m_handle.get());
+		assertEquals(m_handle.get());
+	}
+
+	/* --------------------------------------------------------
+	 * Global functions
+	 * -------------------------------------------------------- */
 
-/**
- * Set a global variable.
- *
- * @param ctx the context
- * @param name the name of the global variable
- * @param type the value to set
- */
-template <typename Type>
-inline void setGlobal(ContextPtr ctx, const std::string &name, Type&& type)
-{
-	assertBegin(ctx);
-	push(ctx, std::forward<Type>(type));
-	duk_put_global_string(ctx, name.c_str());
-	assertEquals(ctx);
-}
+	/**
+	 * Get a global value.
+	 *
+	 * @param ctx the context
+	 * @param name the name of the global variable
+	 * @return the value
+	 */
+	template <typename Type>
+	inline Type getGlobal(const std::string &name)
+	{
+		assertBegin(m_handle.get());
+		duk_get_global_string(m_handle.get(), name.c_str());
+		Type value = get<Type>(-1);
+		duk_pop(m_handle.get());
+		assertEquals(m_handle.get());
+
+		return value;
+	}
+
+	/**
+	 * Set a global variable.
+	 *
+	 * @param ctx the context
+	 * @param name the name of the global variable
+	 * @param type the value to set
+	 */
+	template <typename Type>
+	inline void setGlobal(const std::string &name, Type&& type)
+	{
+		assertBegin(m_handle.get());
+		push(std::forward<Type>(type));
+		duk_put_global_string(m_handle.get(), name.c_str());
+		assertEquals(m_handle.get());
+	}
+
+	/**
+	 * Throw an ECMAScript exception.
+	 *
+	 * @param ctx the context
+	 * @param ex the exception
+	 */
+	template <typename Exception>
+	void raise(const Exception &ex)
+	{
+		ex.create(m_handle.get());
+
+		duk_push_string(m_handle.get(), ex.name().c_str());
+		duk_put_prop_string(m_handle.get(), -2, "name");
+		duk_throw(m_handle.get());
+	}
+};
 
 /* ------------------------------------------------------------------
  * Exception handling
@@ -659,21 +735,106 @@
 	URIError(std::string message);
 };
 
-/**
- * Throw an ECMAScript exception.
- *
- * @param ctx the context
- * @param ex the exception
- */
-template <typename Exception>
-void raise(ContextPtr ctx, const Exception &ex)
-{
-	ex.create(ctx);
+/* ------------------------------------------------------------------
+ * Standard overloads for TypeInfo<T>::get
+ * ------------------------------------------------------------------ */
+
+template <>
+class TypeInfo<int> {
+public:
+	static int get(ContextPtr ctx, duk_idx_t index)
+	{
+		return duk_get_int(ctx, index);
+	}
+
+	static void push(ContextPtr ctx, int value)
+	{
+		duk_push_int(ctx, value);
+	}
+};
+
+template <>
+class TypeInfo<bool> {
+public:
+	static bool get(ContextPtr ctx, duk_idx_t index)
+	{
+		return duk_get_boolean(ctx, index);
+	}
+
+	static void push(ContextPtr ctx, bool value)
+	{
+		duk_push_boolean(ctx, value);
+	}
+};
+
+template <>
+class TypeInfo<double> {
+public:
+	static double get(ContextPtr ctx, duk_idx_t index)
+	{
+		return duk_get_number(ctx, index);
+	}
+
+	static void push(ContextPtr ctx, double value)
+	{
+		duk_push_number(ctx, value);
+	}
+};
 
-	duk_push_string(ctx, ex.name().c_str());
-	duk_put_prop_string(ctx, -2, "name");
-	duk_throw(ctx);
-}
+template <>
+class TypeInfo<std::string> {
+public:
+	static std::string get(ContextPtr ctx, duk_idx_t index)
+	{
+		duk_size_t size;
+		const char *text = duk_get_lstring(ctx, index, &size);
+
+		return std::string{text, size};
+	}
+
+	static void push(ContextPtr ctx, const std::string &value)
+	{
+		duk_push_lstring(ctx, value.c_str(), value.length());
+	}
+};
+
+template <>
+class TypeInfo<const char *> {
+public:
+	static void push(ContextPtr ctx, const char *value)
+	{
+		duk_push_string(ctx, value);
+	}
+};
+
+template <>
+class TypeInfo<Function> {
+public:
+	static void push(ContextPtr ctx, const Function &fn)
+	{
+		assert(fn.function);
+
+		duk_push_c_function(ctx, fn.function, fn.nargs);
+	}
+};
+
+template <>
+class TypeInfo<Object> {
+public:
+	static void push(ContextPtr ctx, const Object &)
+	{
+		duk_push_object(ctx);
+	}
+};
+
+template <>
+class TypeInfo<Array> {
+public:
+	static void push(ContextPtr ctx, const Array &)
+	{
+		duk_push_array(ctx);
+	}
+};
 
 } // !js
 
--- a/C++/tests/Js/main.cpp	Mon Sep 28 13:18:14 2015 +0200
+++ b/C++/tests/Js/main.cpp	Mon Sep 28 19:40:16 2015 +0200
@@ -30,38 +30,38 @@
 {
 	Context context;
 
-	push(context, true);
-	ASSERT_TRUE(get<bool>(context, -1));
-	push(context, false);
-	ASSERT_FALSE(get<bool>(context, -1));
+	context.push(true);
+	ASSERT_TRUE(context.get<bool>(-1));
+	context.push(false);
+	ASSERT_FALSE(context.get<bool>(-1));
 }
 
 TEST(PushAndGet, integer)
 {
 	Context context;
 
-	push(context, 123);
-	ASSERT_EQ(123, get<int>(context, -1));
-	push(context, 456);
-	ASSERT_EQ(456, get<int>(context, -1));
+	context.push(123);
+	ASSERT_EQ(123, context.get<int>(-1));
+	context.push(456);
+	ASSERT_EQ(456, context.get<int>(-1));
 }
 
 TEST(PushAndGet, number)
 {
 	Context context;
 
-	push(context, 10.5);
-	ASSERT_EQ(10.5, get<double>(context, -1));
-	push(context, 50.1);
-	ASSERT_EQ(50.1, get<double>(context, -1));
+	context.push(10.5);
+	ASSERT_EQ(10.5, context.get<double>(-1));
+	context.push(50.1);
+	ASSERT_EQ(50.1, context.get<double>(-1));
 }
 
 TEST(PushAndGet, string)
 {
 	Context context;
 
-	push(context, "hello world!");
-	ASSERT_EQ("hello world!", get<std::string>(context, -1));
+	context.push("hello world!");
+	ASSERT_EQ("hello world!", context.get<std::string>(-1));
 }
 
 /* --------------------------------------------------------
@@ -73,69 +73,68 @@
 	Context context;
 
 	// boolean
-	setGlobal(context, "valueBoolean", true);
-	ASSERT_TRUE(getGlobal<bool>(context, "valueBoolean"));
+	context.setGlobal("valueBoolean", true);
+	ASSERT_TRUE(context.getGlobal<bool>("valueBoolean"));
 
 	// integer
-	setGlobal(context, "valueInteger", 123);
-	ASSERT_EQ(123, getGlobal<int>(context, "valueInteger"));
+	context.setGlobal("valueInteger", 123);
+	ASSERT_EQ(123, context.getGlobal<int>("valueInteger"));
 }
 
 TEST(Basics, top)
 {
 	Context context;
 
-	int current = top(context);
-	push(context, true);
-	ASSERT_EQ(current + 1, top(context));
+	int current = context.top();
+	context.push(true);
+	ASSERT_EQ(current + 1, context.top());
 }
 
 TEST(Basics, pop1)
 {
 	Context context;
 
-	int current = top(context);
-	push(context, true);
-	pop(context);
-	ASSERT_EQ(current, top(context));
+	int current = context.top();
+	context.push(true);
+	context.pop();
+	ASSERT_EQ(current, context.top());
 }
 
 TEST(Basics, pop2)
 {
 	Context context;
 
-	int current = top(context);
-	push(context, true);
-	push(context, true);
-	pop(context, 2);
-	ASSERT_EQ(current, top(context));
+	int current = context.top();
+	context.push(true);
+	context.push(true);
+	context.pop(2);
+	ASSERT_EQ(current, context.top());
 }
 
 TEST(Basics, setObject)
 {
 	Context context;
 
-	push(context, Object{});
-	setObject(context, -1, "x", 123);
-	ASSERT_EQ(123, getObject<int>(context, -1, "x"));
+	context.push(Object{});
+	context.setObject(-1, "x", 123);
+	ASSERT_EQ(123, context.getObject<int>(-1, "x"));
 }
 
 TEST(Basics, enumerate)
 {
 	Context context;
 
-	push(context, Object{});
-	setObject(context, -1, "x", 123);
-	setObject(context, -1, "y", 456);
+	context.push(Object{});
+	context.setObject(-1, "x", 123);
+	context.setObject(-1, "y", 456);
+	context.enumerate(-1, 0, true, [] (Context &ctx) {
+		ASSERT_EQ(DUK_TYPE_STRING, ctx.type(-2));
+		ASSERT_EQ(DUK_TYPE_NUMBER, ctx.type(-1));
 
-	enumerate(context, -1, 0, true, [] (ContextPtr ctx) {
-		ASSERT_EQ(DUK_TYPE_STRING, duk_get_type(ctx, -2));
-		ASSERT_EQ(DUK_TYPE_NUMBER, duk_get_type(ctx, -1));
-
-		if (get<std::string>(ctx, -2) == "x")
-			ASSERT_EQ(123, get<int>(ctx, -1));
-		if (get<std::string>(ctx, -2) == "y")
-			ASSERT_EQ(456, get<int>(ctx, -1));
+		if (ctx.get<std::string>(-2) == "x")
+			ASSERT_EQ(123, ctx.get<int>(-1));
+		if (ctx.get<std::string>(-2) == "y")
+			ASSERT_EQ(456, ctx.get<int>(-1));
 	});
 }
 
@@ -143,14 +142,16 @@
 {
 	Context context;
 
-	push(context, Function{[] (ContextPtr ctx) -> duk_ret_t {
-		setGlobal(ctx, "x", 123);
+	context.push(Function{[] (ContextPtr pctx) -> duk_ret_t {
+		Context ctx{pctx};
+
+		ctx.setGlobal("x", 123);
 
 		return 0;
 	}});
-	call(context);
+	context.call();
 
-	ASSERT_EQ(123, getGlobal<int>(context, "x"));
+	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
 
 /* ------------------------------------------------------------------
@@ -161,29 +162,32 @@
 {
 	Context context;
 
-	evalString(context, "x = 123;");
-	ASSERT_EQ(123, getGlobal<int>(context, "x"));
+	context.evalString("x = 123;");
+	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
 
 TEST(Eval, function)
 {
 	Context context;
 
-	evalString(context, "function f() { x = 123; }; f();");
-	ASSERT_EQ(123, getGlobal<int>(context, "x"));
+	context.evalString("function f() { x = 123; }; f();");
+	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
 
 TEST(Eval, cfunction)
 {
 	Context context;
 
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		setGlobal(ctx, "x", 123);
+	context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t {
+		Context ctx{pctx};
+
+		ctx.setGlobal("x", 123);
 
 		return 0;
 	}});
-	evalString(context, "f()");
-	ASSERT_EQ(123, getGlobal<int>(context, "x"));
+	context.evalString("f()");
+
+	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
 
 /* ------------------------------------------------------------------
@@ -195,7 +199,7 @@
 	Context context;
 
 	try {
-		pevalString(context, "x = 1");
+		context.pevalString("x = 1");
 	} catch (const ErrorInfo &info) {
 		FAIL() << "error unexpected: " << info.what();
 	}
@@ -206,7 +210,7 @@
 	Context context;
 
 	try {
-		pevalString(context, "doesnotexists()");
+		context.pevalString("doesnotexists()");
 
 		FAIL() << "expected exception";
 	} catch (const ErrorInfo &) {
@@ -221,60 +225,14 @@
 {
 	Context context;
 
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, Error{"error thrown"});
+	context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t {
+		Context ctx{pctx};
+
+		ctx.raise(Error{"error thrown"});
 
 		return 0;
 	}});
-	evalString(context,
-		"try {"
-		"  f();"
-		"} catch (ex) {"
-		"  name = ex.name;"
-		"  message = ex.message;"
-		"  received = true;"
-		"}"
-	);
-
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("Error", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("error thrown", getGlobal<std::string>(context, "message"));
-}
-
-TEST(Exception, evalError)
-{
-	Context context;
-
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, EvalError{"failed"});
-
-		return 0;
-	}});
-	evalString(context,
-		"try {"
-		"  f();"
-		"} catch (ex) {"
-		"  name = ex.name;"
-		"  message = ex.message;"
-		"  received = true;"
-		"}"
-	);
-
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("EvalError", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("failed", getGlobal<std::string>(context, "message"));
-}
-
-TEST(Exception, rangeError)
-{
-	Context context;
-
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, RangeError{"e2big"});
-
-		return 0;
-	}});
-	evalString(context,
+	context.evalString(
 		"try {"
 		"  f();"
 		"} catch (ex) {"
@@ -284,105 +242,9 @@
 		"}"
 	);
 
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("RangeError", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("e2big", getGlobal<std::string>(context, "message"));
-}
-
-TEST(Exception, referenceError)
-{
-	Context context;
-
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, ReferenceError{"does not exists"});
-
-		return 0;
-	}});
-	evalString(context,
-		"try {"
-		"  f();"
-		"} catch (ex) {"
-		"  name = ex.name;"
-		"  message = ex.message;"
-		"  received = true;"
-		"}"
-	);
-
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("ReferenceError", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("does not exists", getGlobal<std::string>(context, "message"));
-}
-
-TEST(Exception, syntaxError)
-{
-	Context context;
-
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, SyntaxError{"missing token"});
-
-		return 0;
-	}});
-	evalString(context,
-		"try {"
-		"  f();"
-		"} catch (ex) {"
-		"  name = ex.name;"
-		"  message = ex.message;"
-		"  received = true;"
-		"}"
-	);
-
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("SyntaxError", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("missing token", getGlobal<std::string>(context, "message"));
-}
-
-TEST(Exception, typeError)
-{
-	Context context;
-
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, TypeError{"int requested"});
-
-		return 0;
-	}});
-	evalString(context,
-		"try {"
-		"  f();"
-		"} catch (ex) {"
-		"  name = ex.name;"
-		"  message = ex.message;"
-		"  received = true;"
-		"}"
-	);
-
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("TypeError", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("int requested", getGlobal<std::string>(context, "message"));
-}
-
-TEST(Exception, uriError)
-{
-	Context context;
-
-	setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t {
-		raise(ctx, URIError{"invalid scheme"});
-
-		return 0;
-	}});
-	evalString(context,
-		"try {"
-		"  f();"
-		"} catch (ex) {"
-		"  name = ex.name;"
-		"  message = ex.message;"
-		"  received = true;"
-		"}"
-	);
-
-	ASSERT_TRUE(getGlobal<bool>(context, "received"));
-	ASSERT_EQ("URIError", getGlobal<std::string>(context, "name"));
-	ASSERT_EQ("invalid scheme", getGlobal<std::string>(context, "message"));
+	ASSERT_TRUE(context.getGlobal<bool>("received"));
+	ASSERT_EQ("Error", context.getGlobal<std::string>("name"));
+	ASSERT_EQ("error thrown", context.getGlobal<std::string>("message"));
 }
 
 int main(int argc, char **argv)