changeset 398:94bfe7ba9a13

Js: - Rename some functions - Cleanup a lot - Add more documentation - Add specialization of getProperty and getGlobal to void to push on the stack
author David Demelier <markand@malikania.fr>
date Wed, 30 Sep 2015 19:59:12 +0200
parents 6b2db5425836
children 73517fd307dd
files C++/modules/Js/Js.cpp C++/modules/Js/Js.h C++/tests/Js/main.cpp
diffstat 3 files changed, 396 insertions(+), 308 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Js/Js.cpp	Tue Sep 29 12:50:28 2015 +0200
+++ b/C++/modules/Js/Js.cpp	Wed Sep 30 19:59:12 2015 +0200
@@ -16,216 +16,51 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <cerrno>
-#include <cstring>
-#include <fstream>
-#include <iterator>
-#include <memory>
-#include <unordered_map>
-
 #include "Js.h"
 
 using namespace std::string_literals;
 
 namespace js {
 
-namespace {
-
-ErrorInfo error(ContextPtr ctx, duk_idx_t index)
+ErrorInfo Context::error(int index)
 {
 	ErrorInfo error;
 
-	index = duk_normalize_index(ctx, index);
+	index = duk_normalize_index(m_handle.get(), index);
 
-	assertBegin(ctx);
-	duk_get_prop_string(ctx, index, "name");
-	error.name = duk_to_string(ctx, -1);
-	duk_get_prop_string(ctx, index, "message");
-	error.message = duk_to_string(ctx, -1);
-	duk_get_prop_string(ctx, index, "fileName");
-	error.fileName = duk_to_string(ctx, -1);
-	duk_get_prop_string(ctx, index, "lineNumber");
-	error.lineNumber = duk_to_int(ctx, -1);
-	duk_get_prop_string(ctx, index, "stack");
-	error.stack = duk_to_string(ctx, -1);
-	duk_pop_n(ctx, 5);
-	assertEquals(ctx);
+	duk_get_prop_string(m_handle.get(), index, "name");
+	error.name = duk_to_string(m_handle.get(), -1);
+	duk_get_prop_string(m_handle.get(), index, "message");
+	error.message = duk_to_string(m_handle.get(), -1);
+	duk_get_prop_string(m_handle.get(), index, "fileName");
+	error.fileName = duk_to_string(m_handle.get(), -1);
+	duk_get_prop_string(m_handle.get(), index, "lineNumber");
+	error.lineNumber = duk_to_int(m_handle.get(), -1);
+	duk_get_prop_string(m_handle.get(), index, "stack");
+	error.stack = duk_to_string(m_handle.get(), -1);
+	duk_pop_n(m_handle.get(), 5);
 
 	return error;
 }
 
-} // !namespace
-
-/* --------------------------------------------------------
- * Basic functions
- * -------------------------------------------------------- */
-
-duk_int_t Context::type(duk_idx_t index)
-{
-	return duk_get_type(m_handle.get(), index);
-}
-
-int Context::top()
-{
-	return duk_get_top(m_handle.get());
-}
-
-void Context::pop(int count)
-{
-	assert(count > 0);
-
-	duk_pop_n(m_handle.get(), count);
-}
-
-duk_bool_t Context::instanceof(duk_idx_t idx1, duk_idx_t idx2)
-{
-	return duk_instanceof(m_handle.get(), idx1, idx2);
-}
-
-void Context::call(duk_idx_t nargs)
-{
-	duk_call(m_handle.get(), nargs);
-}
-
-void Context::pcall(duk_idx_t nargs)
+void Context::pcall(unsigned nargs)
 {
 	if (duk_pcall(m_handle.get(), nargs) != 0) {
-		ErrorInfo info = error(m_handle.get(), -1);
-		pop();
+		ErrorInfo info = error(-1);
+		duk_pop(m_handle.get());
 
 		throw info;
 	}
 }
 
-/* ------------------------------------------------------------------
- * Eval functions
- * ------------------------------------------------------------------ */
-
-void Context::eval()
-{
-	duk_eval(m_handle.get());
-}
-
-void Context::evalString(const std::string &name)
-{
-	duk_eval_string(m_handle.get(), name.c_str());
-}
-
-void Context::evalFile(const std::string &file)
-{
-	duk_eval_file(m_handle.get(), file.c_str());
-}
-
 void Context::peval()
 {
 	if (duk_peval(m_handle.get()) != 0) {
-		throw error(m_handle.get(), -1);
-	}
-}
-
-void Context::pevalString(const std::string &script)
-{
-	if (duk_peval_string(m_handle.get(), script.c_str()) != 0) {
-		ErrorInfo info = error(m_handle.get(), -1);
-		pop();
-
-		throw info;
-	}
-}
-
-void Context::pevalFile(const std::string &file)
-{
-	if (duk_peval_file(m_handle.get(), file.c_str()) != 0) {
-		ErrorInfo info = error(m_handle.get(), -1);
-		pop();
+		ErrorInfo info = error(-1);
+		duk_pop(m_handle.get());
 
 		throw info;
 	}
 }
 
-/* ------------------------------------------------------------------
- * ExceptionAbstract class
- * ------------------------------------------------------------------ */
-
-ExceptionAbstract::ExceptionAbstract(std::string name, std::string message)
-	: m_name(std::move(name))
-	, m_message(std::move(message))
-{
-}
-
-const std::string &ExceptionAbstract::name() const noexcept
-{
-	return m_name;
-}
-
-void ExceptionAbstract::create(ContextPtr ctx) const noexcept
-{
-	duk_get_global_string(ctx, m_name.c_str());
-	duk_push_string(ctx, m_message.c_str());
-	duk_new(ctx, 1);
-}
-
-/* ------------------------------------------------------------------
- * Error class
- * ------------------------------------------------------------------ */
-
-Error::Error(std::string message)
-	: ExceptionAbstract("Error", std::move(message))
-{
-}
-
-/* ------------------------------------------------------------------
- * EvalError class
- * ------------------------------------------------------------------ */
-
-EvalError::EvalError(std::string message)
-	: ExceptionAbstract("EvalError", std::move(message))
-{
-}
-
-/* ------------------------------------------------------------------
- * RangeError class
- * ------------------------------------------------------------------ */
-
-RangeError::RangeError(std::string message)
-	: ExceptionAbstract("RangeError", std::move(message))
-{
-}
-
-/* ------------------------------------------------------------------
- * ReferenceError class
- * ------------------------------------------------------------------ */
-
-ReferenceError::ReferenceError(std::string message)
-	: ExceptionAbstract("ReferenceError", std::move(message))
-{
-}
-
-/* ------------------------------------------------------------------
- * SyntaxError class
- * ------------------------------------------------------------------ */
-
-SyntaxError::SyntaxError(std::string message)
-	: ExceptionAbstract("SyntaxError", std::move(message))
-{
-}
-
-/* ------------------------------------------------------------------
- * TypeError class
- * ------------------------------------------------------------------ */
-
-TypeError::TypeError(std::string message)
-	: ExceptionAbstract("TypeError", std::move(message))
-{
-}
-
-/* ------------------------------------------------------------------
- * URIError class
- * ------------------------------------------------------------------ */
-
-URIError::URIError(std::string message)
-	: ExceptionAbstract("URIError", std::move(message))
-{
-}
-
 } // !js
--- a/C++/modules/Js/Js.h	Tue Sep 29 12:50:28 2015 +0200
+++ b/C++/modules/Js/Js.h	Wed Sep 30 19:59:12 2015 +0200
@@ -30,37 +30,16 @@
  */
 
 #include <cassert>
-#include <cerrno>
-#include <cstring>
+#include <functional>
 #include <memory>
-#include <stack>
 #include <string>
-#include <vector>
+#include <type_traits>
 
 #include <duktape.h>
 
 namespace js {
 
-#if !defined(NDEBUG)
-#define assertBegin(ctx)						\
-	int _topstack = duk_get_top(ctx)
-#else
-#define assertBegin(ctx)
-#endif
-
-#if !defined(NDEBUG)
-#define assertEquals(ctx)						\
-	assert(_topstack == duk_get_top(ctx))
-#else
-#define assertEquals(ctx)
-#endif
-
-#if !defined(NDEBUG)
-#define assertEnd(ctx, count)					\
-	assert(_topstack == (duk_get_top(ctx) - count))
-#else
-#define assertEnd(ctx, count)
-#endif
+class Context;
 
 /**
  * Typedef for readability.
@@ -90,12 +69,12 @@
 	/**
 	 * The function pointer, must not be null.
 	 */
-	duk_c_function function{nullptr};
+	std::function<int (Context &)> function;
 
 	/**
 	 * Number of args that the function takes
 	 */
-	duk_idx_t nargs{0};
+	int nargs{0};
 };
 
 /**
@@ -123,9 +102,50 @@
 	}
 };
 
+/**
+ * @class TypeInfo
+ * @brief Type information to implement new types in JavaScript's context
+ */
 template <typename Type>
 class TypeInfo {
+};
+
+/**
+ * @class File
+ * @brief Evaluate script from file
+ */
+class File {
 public:
+	std::string path;
+
+	inline void eval(duk_context *ctx)
+	{
+		duk_eval_file(ctx, path.c_str());
+	}
+
+	inline int peval(duk_context *ctx)
+	{
+		return duk_peval_file(ctx, path.c_str());
+	}
+};
+
+/**
+ * @class Script
+ * @brief Evaluate script from raw text
+ */
+class Script {
+public:
+	std::string text;
+
+	inline void eval(duk_context *ctx)
+	{
+		duk_eval_string(ctx, text.c_str());
+	}
+
+	inline int peval(duk_context *ctx)
+	{
+		return duk_peval_string(ctx, text.c_str());
+	}
 };
 
 /**
@@ -147,7 +167,6 @@
 	Context(const Context &&) = delete;
 	Context &operator=(const Context &&) = delete;
 
-
 public:
 	/**
 	 * Create default context.
@@ -209,46 +228,79 @@
 	 * @return the value
 	 */
 	template <typename Type>
-	inline auto get(duk_idx_t index) -> decltype(TypeInfo<Type>::get(*this, 0))
+	inline auto get(int index) -> decltype(TypeInfo<Type>::get(*this, 0))
 	{
 		return TypeInfo<Type>::get(*this, index);
 	}
 
+	/**
+	 * Require a type at the specified index.
+	 *
+	 * @param index the index
+	 * @return the value
+	 */
 	template <typename Type>
-	inline auto require(duk_idx_t index) -> decltype(TypeInfo<Type>::require(*this, 0))
+	inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0))
 	{
 		return TypeInfo<Type>::require(*this, index);
 	}
 
+	// TODO: add optional
+
 	/* --------------------------------------------------------
-	 * Get functions (for object)
+	 * Get properties (for objects)
 	 * -------------------------------------------------------- */
 
+	/**
+	 * Get the property `name' as value from the object at the specified index.
+	 *
+	 * @param index the object index
+	 * @param name the property name
+	 * @return the value
+	 * @note The stack is unchanged
+	 */
 	template <typename Type>
-	inline auto getObject(duk_idx_t index, const std::string &name) -> decltype(get<Type>(0))
+	inline auto getProperty(int index, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0))
 	{
-		assertBegin(m_handle.get());
 		duk_get_prop_string(m_handle.get(), index, name.c_str());
 		auto &&value = get<Type>(-1);
 		duk_pop(m_handle.get());
-		assertEquals(m_handle.get());
 
 		return value;
 	}
 
+	/**
+	 * Get the property `name' and push it to the stack from the object at the specified index.
+	 *
+	 * @param index the object index
+	 * @param name the property name
+	 * @note The stack contains the property value
+	 */
+	template <typename Type>
+	inline void getProperty(int index, const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr)
+	{
+		duk_get_prop_string(m_handle.get(), index, name.c_str());
+	}
+
 	/* --------------------------------------------------------
-	 * Set functions (for object)
+	 * Put properties functions (for object)
 	 * -------------------------------------------------------- */
 
+	/**
+	 * Set a property to the object at the specified index.
+	 *
+	 * @param index the object index
+	 * @param name the property name
+	 * @param value the value to forward
+	 * @note The stack is unchanged
+	 */
 	template <typename Type>
-	void setObject(duk_idx_t index, const std::string &name, Type&& value)
+	void putProperty(int 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());
 	}
 
 	/* --------------------------------------------------------
@@ -256,13 +308,24 @@
 	 * -------------------------------------------------------- */
 
 	/**
+	 * Get the error object when a JavaScript error has been thrown (e.g. eval failure).
+	 *
+	 * @param index the index
+	 * @return the information
+	 */
+	ErrorInfo error(int index);
+
+	/**
 	 * 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);
+	int type(int index) noexcept
+	{
+		return duk_get_type(m_handle.get(), index);
+	}
 
 	/**
 	 * Get the current stack size.
@@ -270,16 +333,21 @@
 	 * @param ctx the context
 	 * @return the stack size
 	 */
-	int top();
+	inline int top() noexcept
+	{
+		return duk_get_top(m_handle.get());
+	}
 
 	/**
 	 * 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);
+	inline void pop(unsigned count = 1)
+	{
+		duk_pop_n(m_handle.get(), count);
+	}
 
 	/**
 	 * Check if idx1 is an instance of idx2.
@@ -289,7 +357,10 @@
 	 * @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);
+	inline bool instanceof(int idx1, int idx2)
+	{
+		return duk_instanceof(m_handle.get(), idx1, idx2);
+	}
 
 	/**
 	 * Call the object at the top of the stack.
@@ -298,7 +369,10 @@
 	 * @param nargs the number of arguments
 	 * @note Non-protected
 	 */
-	void call(duk_idx_t nargs = 0);
+	inline void call(unsigned nargs = 0)
+	{
+		duk_call(m_handle.get(), nargs);
+	}
 
 	/**
 	 * Call in protected mode the object at the top of the stack.
@@ -307,37 +381,37 @@
 	 * @param nargs the number of arguments
 	 * @throw ErrorInfo on errors
 	 */
-	void pcall(duk_idx_t nargs = 0);
+	void pcall(unsigned nargs = 0);
 
 	/* ------------------------------------------------------------------
 	 * Eval functions
 	 * ------------------------------------------------------------------ */
 
 	/**
-	 * Evaluate a non-protected chunk at the top of the stack.
+	 * Evaluate a non-protected chunk that is at the top of the stack.
 	 *
 	 * @param ctx
 	 */
-	void eval();
+	inline void eval()
+	{
+		duk_eval(m_handle.get());
+	}
 
 	/**
-	 * Evaluate a non-protected string script.
+	 * Evaluate a non-protected source.
 	 *
-	 * @param ctx the context
-	 * @param script the script content
+	 * @param source the source
+	 * @see File
+	 * @see Script
 	 */
-	void evalString(const std::string &script);
+	template <typename Source>
+	inline void eval(Source &&source)
+	{
+		source.eval(m_handle.get());
+	}
 
 	/**
-	 * Evaluate a non-protected file.
-	 *
-	 * @param ctx the context
-	 * @param file the file
-	 */
-	void evalFile(const std::string &file);
-
-	/**
-	 * Evaluate a protected chunk.
+	 * Evaluate a protected chunk that is at the top of the stack.
 	 *
 	 * @param ctx the context
 	 * @throw ErrorInfo the error
@@ -345,22 +419,23 @@
 	void peval();
 
 	/**
-	 * Evaluate a protected string script.
+	 * Evaluate a protected source.
 	 *
-	 * @param ctx the context
-	 * @param script the script content
-	 * @throw ErrorInfo the error
+	 * @param source the source
+	 * @see File
+	 * @see Script
+	 * @throw ErrorInfo on failure
 	 */
-	void pevalString(const std::string &script);
+	template <typename Source>
+	inline void peval(Source &&source)
+	{
+		if (source.peval(m_handle.get()) != 0) {
+			ErrorInfo info = error(-1);
+			duk_pop(m_handle.get());
 
-	/**
-	 * Evaluate a protected file.
-	 *
-	 * @param ctx the context
-	 * @param file the file
-	 * @throw ErrorInfo the error
-	 */
-	void pevalFile(const std::string &file);
+			throw info;
+		}
+	}
 
 	/* ------------------------------------------------------------------
 	 * Extended functions
@@ -376,9 +451,8 @@
 	 * @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)
+	void enumerate(int 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)) {
@@ -387,7 +461,6 @@
 		}
 
 		duk_pop(m_handle.get());
-		assertEquals(m_handle.get());
 	}
 
 	/* --------------------------------------------------------
@@ -402,18 +475,25 @@
 	 * @return the value
 	 */
 	template <typename Type>
-	inline auto getGlobal(const std::string &name) -> decltype(get<Type>(0))
+	inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0))
 	{
-		assertBegin(m_handle.get());
 		duk_get_global_string(m_handle.get(), name.c_str());
 		auto &&value = get<Type>(-1);
 		duk_pop(m_handle.get());
-		assertEquals(m_handle.get());
 
 		return value;
 	}
 
 	/**
+	 * Overload that push the value at the top of the stack instead of returning it.
+	 */
+	template <typename Type>
+	inline void getGlobal(const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept
+	{
+		duk_get_global_string(m_handle.get(), name.c_str());
+	}
+
+	/**
 	 * Set a global variable.
 	 *
 	 * @param ctx the context
@@ -423,10 +503,8 @@
 	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());
 	}
 
 	/**
@@ -445,6 +523,11 @@
 		duk_throw(m_handle.get());
 	}
 
+	/**
+	 * Return the this binding of the current function.
+	 *
+	 * @return the this binding as the template given
+	 */
 	template <typename T>
 	inline auto self() -> decltype(TypeInfo<T>::get(*this, 0))
 	{
@@ -454,6 +537,22 @@
 
 		return value;
 	}
+
+	/**
+	 * Construct the object in place, setting value as this binding.
+	 *
+	 * The TypeInfo<T> must have the following requirements:
+	 *
+	 * - static void construct(Context &, T): must update this with the value and keep the stack unchanged
+	 *
+	 * @param value the value to forward
+	 * @see self
+	 */
+	template <typename T>
+	inline void construct(T &&value)
+	{
+		TypeInfo<std::remove_reference_t<T>>::construct(*this, std::forward<T>(value));
+	}
 };
 
 /* ------------------------------------------------------------------
@@ -477,14 +576,21 @@
 	 * @param name the name (e.g TypeError)
 	 * @param message the message
 	 */
-	ExceptionAbstract(std::string name, std::string message);
+	inline ExceptionAbstract(std::string name, std::string message) noexcept
+		: m_name(std::move(name))
+		, m_message(std::move(message))
+	{
+	}
 
 	/**
 	 * Get the exception type name.
 	 *
 	 * @return the exception type
 	 */
-	const std::string &name() const noexcept;
+	inline const std::string &name() const noexcept
+	{
+		return m_name;
+	}
 
 	/**
 	 * Create the exception on the stack.
@@ -492,7 +598,12 @@
 	 * @note the default implementation search for the global variables
 	 * @param ctx the context
 	 */
-	virtual void create(ContextPtr ctx) const noexcept;
+	virtual void create(ContextPtr ctx) const noexcept
+	{
+		duk_get_global_string(ctx, m_name.c_str());
+		duk_push_string(ctx, m_message.c_str());
+		duk_new(ctx, 1);
+	}
 };
 
 /**
@@ -501,7 +612,10 @@
  */
 class Error : public ExceptionAbstract {
 public:
-	Error(std::string message);
+	inline Error(std::string message) noexcept
+		: ExceptionAbstract("Error", std::move(message))
+	{
+	}
 };
 
 /**
@@ -510,7 +624,10 @@
  */
 class EvalError : public ExceptionAbstract {
 public:
-	EvalError(std::string message);
+	inline EvalError(std::string message) noexcept
+		: ExceptionAbstract("EvalError", std::move(message))
+	{
+	}
 };
 
 /**
@@ -519,7 +636,10 @@
  */
 class RangeError : public ExceptionAbstract {
 public:
-	RangeError(std::string message);
+	inline RangeError(std::string message) noexcept
+		: ExceptionAbstract("RangeError", std::move(message))
+	{
+	}
 };
 
 /**
@@ -528,7 +648,10 @@
  */
 class ReferenceError : public ExceptionAbstract {
 public:
-	ReferenceError(std::string message);
+	inline ReferenceError(std::string message) noexcept
+		: ExceptionAbstract("ReferenceError", std::move(message))
+	{
+	}
 };
 
 /**
@@ -537,7 +660,10 @@
  */
 class SyntaxError : public ExceptionAbstract {
 public:
-	SyntaxError(std::string message);
+	inline SyntaxError(std::string message) noexcept
+		: ExceptionAbstract("SyntaxError", std::move(message))
+	{
+	}
 };
 
 /**
@@ -546,7 +672,10 @@
  */
 class TypeError : public ExceptionAbstract {
 public:
-	TypeError(std::string message);
+	inline TypeError(std::string message) noexcept
+		: ExceptionAbstract("TypeError", std::move(message))
+	{
+	}
 };
 
 /**
@@ -555,17 +684,26 @@
  */
 class URIError : public ExceptionAbstract {
 public:
-	URIError(std::string message);
+	inline URIError(std::string message) noexcept
+		: ExceptionAbstract("URIError", std::move(message))
+	{
+	}
 };
 
 /* ------------------------------------------------------------------
  * Standard overloads for TypeInfo<T>::get
  * ------------------------------------------------------------------ */
 
+/**
+ * @class TypeInfo<int>
+ * @brief Default implementation for int
+ *
+ * Provides: get, push, require, optional
+ */
 template <>
 class TypeInfo<int> {
 public:
-	static int get(ContextPtr ctx, duk_idx_t index)
+	static int get(ContextPtr ctx, int index)
 	{
 		return duk_get_int(ctx, index);
 	}
@@ -576,10 +714,16 @@
 	}
 };
 
+/**
+ * @class TypeInfo<bool>
+ * @brief Default implementation for bool
+ *
+ * Provides: get, push, require, optional
+ */
 template <>
 class TypeInfo<bool> {
 public:
-	static bool get(Context &ctx, duk_idx_t index)
+	static bool get(Context &ctx, int index)
 	{
 		return duk_get_boolean(ctx, index);
 	}
@@ -590,10 +734,16 @@
 	}
 };
 
+/**
+ * @class TypeInfo<double>
+ * @brief Default implementation for double
+ *
+ * Provides: get, push, require, optional
+ */
 template <>
 class TypeInfo<double> {
 public:
-	static double get(Context &ctx, duk_idx_t index)
+	static double get(Context &ctx, int index)
 	{
 		return duk_get_number(ctx, index);
 	}
@@ -604,10 +754,18 @@
 	}
 };
 
+/**
+ * @class TypeInfo<std::string>
+ * @brief Default implementation for std::string
+ *
+ * Provides: get, push, require, optional
+ *
+ * Note: the functions allows embedded '\0'.
+ */
 template <>
 class TypeInfo<std::string> {
 public:
-	static std::string get(Context &ctx, duk_idx_t index)
+	static std::string get(Context &ctx, int index)
 	{
 		duk_size_t size;
 		const char *text = duk_get_lstring(ctx, index, &size);
@@ -621,6 +779,12 @@
 	}
 };
 
+/**
+ * @class TypeInfo<const char *>
+ * @brief Default implementation for const char literals
+ *
+ * Provides: push
+ */
 template <>
 class TypeInfo<const char *> {
 public:
@@ -630,17 +794,69 @@
 	}
 };
 
+/**
+ * @class TypeInfo<Function>
+ * @brief Default implementation for Function
+ *
+ * Provides: push
+ *
+ * This implementation push a Duktape/C function that is wrapped as C++ for convenience.
+ */
 template <>
 class TypeInfo<Function> {
 public:
-	static void push(Context &ctx, const Function &fn)
+	static void push(Context &ctx, Function fn)
 	{
 		assert(fn.function);
 
-		duk_push_c_function(ctx, fn.function, fn.nargs);
+		/* 1. Push function wrapper */
+		duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
+			Context context{ctx};
+
+			duk_push_current_function(ctx);
+			duk_get_prop_string(ctx, -1, "\xff""\xff""js-func");
+			Function *f = static_cast<Function *>(duk_to_pointer(ctx, -1));
+			duk_pop_2(ctx);
+
+			return static_cast<duk_ret_t>(f->function(context));
+		}, fn.nargs);
+
+		/* 2. Store the moved function */
+		duk_push_pointer(ctx, new Function(std::move(fn)));
+		duk_put_prop_string(ctx, -2, "\xff""\xff""js-func");
+
+		/* 3. Store deletion flags */
+		duk_push_boolean(ctx, false);
+		duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
+
+		/* 4. Push and set a finalizer */
+		duk_push_c_function(ctx, [] (duk_context *ctx) {
+			duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
+
+			if (duk_to_boolean(ctx, -1)) {
+				duk_push_boolean(ctx, true);
+				duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
+				duk_get_prop_string(ctx, 0, "\xff""\xff""js-func");
+				delete static_cast<Function *>(duk_to_pointer(ctx, -1));
+				duk_pop(ctx);
+			}
+
+			duk_pop(ctx);
+
+			return 0;
+		}, 1);
+		duk_set_finalizer(ctx, -2);
 	}
 };
 
+/**
+ * @class TypeInfo<Object>
+ * @brief Default implementation for Object
+ *
+ * Provides: push
+ *
+ * This implementation is provided to push empty object into the stack.
+ */
 template <>
 class TypeInfo<Object> {
 public:
@@ -650,6 +866,14 @@
 	}
 };
 
+/**
+ * @class TypeInfo<Array>
+ * @brief Default implementation for Array
+ *
+ * Provides: push
+ *
+ * This implementation is provided to push empty array into the stack.
+ */
 template <>
 class TypeInfo<Array> {
 public:
@@ -665,39 +889,72 @@
 
 /**
  * @class TypeInfoShared
- * @brief Generates push / get / require for std::shared_ptr<T>
+ * @brief Generates push / get / require / optional / construct for std::shared_ptr
  *
  * Specialize TypeInfo<std::shared_ptr<T>> and inherits from this class to implement the push(),
- * get() and require() functions.
+ * get(), require() and optional() functions.
  *
  * You only need to implement `static void prototype(Context &ctx)` which must push the prototype
  * to use for the underlying object.
+ *
+ * This class can be used to both push and construct shared_ptr objects.
  */
 template <typename T>
 class TypeInfoShared {
+private:
+	static void apply(Context &ctx, std::shared_ptr<T> value);
+
 public:
+	static void construct(Context &ctx, std::shared_ptr<T> value);
 	static void push(Context &ctx, std::shared_ptr<T> value);
-	static std::shared_ptr<T> get(Context &ctx, duk_idx_t index);
+	static std::shared_ptr<T> get(Context &ctx, int index);
 };
 
 template <typename T>
-void TypeInfoShared<T>::push(Context &ctx, std::shared_ptr<T> value)
+void TypeInfoShared<T>::apply(Context &ctx, std::shared_ptr<T> value)
 {
-	duk_push_object(ctx);
 	duk_push_boolean(ctx, false);
 	duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
 	duk_push_pointer(ctx, new std::shared_ptr<T>(value));
 	duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr");
 
 	TypeInfo<std::shared_ptr<T>>::prototype(ctx);
+	duk_set_prototype(ctx, -2);
+	duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
+		duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
 
-	// TODO: set deleter
+		if (duk_to_boolean(ctx, -1)) {
+			duk_push_boolean(ctx, true);
+			duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
+			duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr");
+			delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
+			duk_pop(ctx);
+		}
 
-	duk_set_prototype(ctx, -2);
+		duk_pop(ctx);
+
+		return 0;
+	}, 1);
+	duk_set_finalizer(ctx, -2);
 }
 
 template <typename T>
-std::shared_ptr<T> TypeInfoShared<T>::get(Context &ctx, duk_idx_t index)
+void TypeInfoShared<T>::construct(Context &ctx, std::shared_ptr<T> value)
+{
+	duk_push_this(ctx);
+	apply(ctx, std::move(value));
+	duk_pop(ctx);
+}
+
+template <typename T>
+void TypeInfoShared<T>::push(Context &ctx, std::shared_ptr<T> value)
+{
+	duk_push_object(ctx);
+	apply(ctx, std::move(value));
+}
+
+template <typename T>
+std::shared_ptr<T> TypeInfoShared<T>::get(Context &ctx, int index)
 {
 	duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr");
 	std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
--- a/C++/tests/Js/main.cpp	Tue Sep 29 12:50:28 2015 +0200
+++ b/C++/tests/Js/main.cpp	Wed Sep 30 19:59:12 2015 +0200
@@ -111,13 +111,13 @@
 	ASSERT_EQ(current, context.top());
 }
 
-TEST(Basics, setObject)
+TEST(Basics, putProperty)
 {
 	Context context;
 
 	context.push(Object{});
-	context.setObject(-1, "x", 123);
-	ASSERT_EQ(123, context.getObject<int>(-1, "x"));
+	context.putProperty(-1, "x", 123);
+	ASSERT_EQ(123, context.getProperty<int>(-1, "x"));
 }
 
 TEST(Basics, enumerate)
@@ -125,8 +125,8 @@
 	Context context;
 
 	context.push(Object{});
-	context.setObject(-1, "x", 123);
-	context.setObject(-1, "y", 456);
+	context.putProperty(-1, "x", 123);
+	context.putProperty(-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));
@@ -142,9 +142,7 @@
 {
 	Context context;
 
-	context.push(Function{[] (ContextPtr pctx) -> duk_ret_t {
-		Context ctx{pctx};
-
+	context.push(Function{[] (Context &ctx) -> int {
 		ctx.setGlobal("x", 123);
 
 		return 0;
@@ -162,7 +160,7 @@
 {
 	Context context;
 
-	context.evalString("x = 123;");
+	context.eval(Script{"x = 123;"});
 	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
 
@@ -170,7 +168,7 @@
 {
 	Context context;
 
-	context.evalString("function f() { x = 123; }; f();");
+	context.eval(Script{"function f() { x = 123; }; f();"});
 	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
 
@@ -178,14 +176,12 @@
 {
 	Context context;
 
-	context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t {
-		Context ctx{pctx};
-
+	context.setGlobal("f", Function{[] (Context &ctx) -> int {
 		ctx.setGlobal("x", 123);
 
 		return 0;
 	}});
-	context.evalString("f()");
+	context.eval(Script{"f()"});
 
 	ASSERT_EQ(123, context.getGlobal<int>("x"));
 }
@@ -199,7 +195,7 @@
 	Context context;
 
 	try {
-		context.pevalString("x = 1");
+		context.peval(Script{"x = 1"});
 	} catch (const ErrorInfo &info) {
 		FAIL() << "error unexpected: " << info.what();
 	}
@@ -210,10 +206,10 @@
 	Context context;
 
 	try {
-		context.pevalString("doesnotexists()");
+		context.peval(Script{"doesnotexists()"});
 
 		FAIL() << "expected exception";
-	} catch (const ErrorInfo &) {
+	} catch (const std::exception &) {
 	}
 }
 
@@ -232,7 +228,7 @@
 
 		return 0;
 	}});
-	context.evalString(
+	context.eval(Script{
 		"try {"
 		"  f();"
 		"} catch (ex) {"
@@ -240,7 +236,7 @@
 		"  message = ex.message;"
 		"  received = true;"
 		"}"
-	);
+	});
 
 	ASSERT_TRUE(context.getGlobal<bool>("received"));
 	ASSERT_EQ("Error", context.getGlobal<std::string>("name"));
@@ -265,7 +261,7 @@
 		ctx.push(Object{});
 
 		/* "update" method */
-		ctx.setObject(-1, "update", Function{[] (ContextPtr pctx) -> duk_ret_t {
+		ctx.putProperty(-1, "update", Function{[] (ContextPtr pctx) -> duk_ret_t {
 			Context{pctx}.self<std::shared_ptr<Player>>()->m_updated = true;
 
 			return 0;
@@ -293,7 +289,7 @@
 
 	try {
 		ctx.setGlobal("player", p);
-		ctx.pevalString("player.update();");
+		ctx.peval(Script{"player.update();"});
 
 		ASSERT_TRUE(p->m_updated);
 	} catch (const std::exception &ex) {