1 #pragma once
2 
3 #include "detail_types.h"
4 #include "detail_typeinfo.h"
5 #include "dukvalue.h"
6 
7 #include <vector>
8 #include <stdint.h>
9 #include <memory>  // for std::shared_ptr
10 
11 namespace dukglue {
12 	namespace types {
13 
14 #define DUKGLUE_SIMPLE_VALUE_TYPE(TYPE, DUK_IS_FUNC, DUK_GET_FUNC, DUK_PUSH_FUNC, PUSH_VALUE) \
15 		template<> \
16 		struct DukType<TYPE> { \
17 			typedef std::true_type IsValueType; \
18 			\
19 			template<typename FullT> \
20 			static TYPE read(duk_context* ctx, duk_idx_t arg_idx) { \
21 				if (DUK_IS_FUNC(ctx, arg_idx)) { \
22 					return static_cast<TYPE>(DUK_GET_FUNC(ctx, arg_idx)); \
23 				} else { \
24 					duk_int_t type_idx = duk_get_type(ctx, arg_idx); \
25 					duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected " #TYPE ", got %s", arg_idx, detail::get_type_name(type_idx)); \
26 				} \
27 			} \
28 			\
29 			template<typename FullT> \
30 			static void push(duk_context* ctx, TYPE value) { \
31 				DUK_PUSH_FUNC(ctx, PUSH_VALUE); \
32 			} \
33 		};
34 
35 		DUKGLUE_SIMPLE_VALUE_TYPE(bool, duk_is_boolean, 0 != duk_get_boolean, duk_push_boolean, value)
36 
37 		DUKGLUE_SIMPLE_VALUE_TYPE(uint8_t,  duk_is_number, duk_get_uint, duk_push_uint, value)
38 		DUKGLUE_SIMPLE_VALUE_TYPE(uint16_t, duk_is_number, duk_get_uint, duk_push_uint, value)
39 		DUKGLUE_SIMPLE_VALUE_TYPE(uint32_t, duk_is_number, duk_get_uint, duk_push_uint, value)
40 		DUKGLUE_SIMPLE_VALUE_TYPE(uint64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double
41 
42 		DUKGLUE_SIMPLE_VALUE_TYPE(int8_t,  duk_is_number, duk_get_int, duk_push_int, value)
43 		DUKGLUE_SIMPLE_VALUE_TYPE(int16_t, duk_is_number, duk_get_int, duk_push_int, value)
44 		DUKGLUE_SIMPLE_VALUE_TYPE(int32_t, duk_is_number, duk_get_int, duk_push_int, value)
45 		DUKGLUE_SIMPLE_VALUE_TYPE(int64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double
46 
47 		// signed char and unsigned char are surprisingly *both* different from char, at least in MSVC
48 		DUKGLUE_SIMPLE_VALUE_TYPE(char, duk_is_number, duk_get_int, duk_push_int, value)
49 
50 		DUKGLUE_SIMPLE_VALUE_TYPE(float, duk_is_number, duk_get_number, duk_push_number, value)
51 		DUKGLUE_SIMPLE_VALUE_TYPE(double, duk_is_number, duk_get_number, duk_push_number, value)
52 
53 		DUKGLUE_SIMPLE_VALUE_TYPE(std::string, duk_is_string, duk_get_string, duk_push_string, value.c_str())
54 
55 		// We have to do some magic for const char* to work correctly.
56 		// We override the "bare type" and "storage type" to both be const char*.
57 		// char* is a bit tricky because its "bare type" should still be const char*, to differentiate it from just char
58 		template<>
59 		struct Bare<char*> {
60 			typedef const char* type;
61 		};
62 		template<>
63 		struct Bare<const char*> {
64 			typedef const char* type;
65 		};
66 
67 		// the storage type should also be const char* - if we don't do this, it will end up as just "char"
68 		template<>
69 		struct ArgStorage<const char*> {
70 			typedef const char* type;
71 		};
72 
73 		template<>
74 		struct DukType<const char*> {
75 			typedef std::true_type IsValueType;
76 
77 			template<typename FullT>
78 			static const char* read(duk_context* ctx, duk_idx_t arg_idx) {
79 				if (duk_is_string(ctx, arg_idx)) {
80 					return duk_get_string(ctx, arg_idx);
81 				} else {
82 					duk_int_t type_idx = duk_get_type(ctx, arg_idx);
83 					duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected string, got %s", arg_idx, detail::get_type_name(type_idx));
84 				}
85 			}
86 
87 			template<typename FullT>
88 			static void push(duk_context* ctx, const char* value) {
89 				duk_push_string(ctx, value);
90 			}
91 		};
92 
93 		// DukValue
94 		template<>
95 		struct DukType<DukValue> {
96 			typedef std::true_type IsValueType;
97 
98 			template <typename FullT>
99 			static DukValue read(duk_context* ctx, duk_idx_t arg_idx) {
100 				try {
101 					return DukValue::copy_from_stack(ctx, arg_idx);
102 				} catch (DukException& e) {
103 					// only DukException can be thrown by DukValue::copy_from_stack
104 					duk_error(ctx, DUK_ERR_ERROR, e.what());
105 				}
106 			}
107 
108 			template <typename FullT>
109 			static void push(duk_context* ctx, const DukValue& value) {
110 				if (value.context() == NULL) {
111 					duk_error(ctx, DUK_ERR_ERROR, "DukValue is uninitialized");
112 					return;
113 				}
114 
115 				if (value.context() != ctx) {
116 					duk_error(ctx, DUK_ERR_ERROR, "DukValue comes from a different context");
117 					return;
118 				}
119 
120 				try {
121 					value.push();
122 				} catch (DukException& e) {
123 					// only DukException can be thrown by DukValue::copy_from_stack
124 					duk_error(ctx, DUK_ERR_ERROR, e.what());
125 				}
126 			}
127 		};
128 
129 		// std::vector (as value)
130 		template<typename T>
131 		struct DukType< std::vector<T> > {
132 			typedef std::true_type IsValueType;
133 
134 			template <typename FullT>
135 			static std::vector<T> read(duk_context* ctx, duk_idx_t arg_idx) {
136 				if (!duk_is_array(ctx, arg_idx)) {
137 					duk_int_t type_idx = duk_get_type(ctx, arg_idx);
138 					duk_error(ctx, DUK_ERR_TYPE_ERROR, "Argument %d: expected array, got %s", arg_idx, detail::get_type_name(type_idx));
139 				}
140 
141 				duk_size_t len = duk_get_length(ctx, arg_idx);
142 				const duk_idx_t elem_idx = duk_get_top(ctx);
143 
144 				std::vector<T> vec;
145 				vec.reserve(len);
146 				for (duk_size_t i = 0; i < len; i++) {
147 					duk_get_prop_index(ctx, arg_idx, i);
148 					vec.push_back(DukType< typename Bare<T>::type >::template read<T>(ctx, elem_idx));
149 					duk_pop(ctx);
150 				}
151 				return vec;
152 			}
153 
154 			template <typename FullT>
155 			static void push(duk_context* ctx, const std::vector<T>& value) {
156 				duk_idx_t obj_idx = duk_push_array(ctx);
157 
158 				for (size_t i = 0; i < value.size(); i++) {
159 					DukType< typename Bare<T>::type >::template push<T>(ctx, value[i]);
160 					duk_put_prop_index(ctx, obj_idx, i);
161 				}
162 			}
163 		};
164 
165 		// std::shared_ptr (as value)
166 		template<typename T>
167 		struct DukType< std::shared_ptr<T> > {
168 			typedef std::true_type IsValueType;
169 
170 			static_assert(std::is_same<typename DukType<T>::IsValueType, std::false_type>::value, "Dukglue can only use std::shared_ptr to non-value types!");
171 
172 			template <typename FullT>
173 			static std::shared_ptr<T> read(duk_context* ctx, duk_idx_t arg_idx) {
174 				if (duk_is_null(ctx, arg_idx))
175 					return nullptr;
176 
177 				if (!duk_is_object(ctx, arg_idx)) {
178 					duk_int_t type_idx = duk_get_type(ctx, arg_idx);
179 					duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object, got ", arg_idx, detail::get_type_name(type_idx));
180 				}
181 
182 				duk_get_prop_string(ctx, arg_idx, "\xFF" "type_info");
183 				if (!duk_is_pointer(ctx, -1))  // missing type_info, must not be a native object
184 					duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object (missing type_info)", arg_idx);
185 
186 				// make sure this object can be safely returned as a T*
187 				dukglue::detail::TypeInfo* info = static_cast<dukglue::detail::TypeInfo*>(duk_get_pointer(ctx, -1));
188 				if (!info->can_cast<T>())
189 					duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: wrong type of shared_ptr object", arg_idx);
190 				duk_pop(ctx);  // pop type_info
191 
192 				duk_get_prop_string(ctx, arg_idx, "\xFF" "shared_ptr");
193 				if (!duk_is_pointer(ctx, -1))
194 					duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: not a shared_ptr object (missing shared_ptr)", arg_idx);
195 				void* ptr = duk_get_pointer(ctx, -1);
196 				duk_pop(ctx);  // pop pointer to shared_ptr
197 
198 				return *((std::shared_ptr<T>*) ptr);
199 			}
200 
201 			static duk_ret_t shared_ptr_finalizer(duk_context* ctx)
202 			{
203 				duk_get_prop_string(ctx, 0, "\xFF" "shared_ptr");
204 				std::shared_ptr<T>* ptr = (std::shared_ptr<T>*) duk_require_pointer(ctx, -1);
205 				duk_pop(ctx);  // pop shared_ptr ptr
206 
207 				if (ptr != NULL) {
208 					delete ptr;
209 
210 					// for safety, set the pointer to undefined
211 					// (finalizers can run multiple times)
212 					duk_push_undefined(ctx);
213 					duk_put_prop_string(ctx, 0, "\xFF" "shared_ptr");
214 				}
215 
216 				return 0;
217 			}
218 
219 			template <typename FullT>
220 			static void push(duk_context* ctx, const std::shared_ptr<T>& value) {
221 				if (value == nullptr) {
222 					duk_push_null(ctx);
223 				} else {
224 					dukglue::detail::ProtoManager::make_script_object(ctx, value.get());
225 
226 					// create + set shared_ptr
227 					duk_push_pointer(ctx, new std::shared_ptr<T>(value));
228 					duk_put_prop_string(ctx, -2, "\xFF" "shared_ptr");
229 
230 					// set shared_ptr finalizer
231 					duk_push_c_function(ctx, &shared_ptr_finalizer, 1);
232 					duk_set_finalizer(ctx, -2);
233 				}
234 			}
235 		};
236 
237 		// std::function
238 		/*template <typename RetT, typename... ArgTs>
239 		struct DukType< std::function<RetT(ArgTs...)> > {
240 			typedef std::true_type IsValueType;
241 
242 			template<typename FullT>
243 			static std::function<RetT(ArgTs...)> read(duk_context* ctx, duk_idx_t arg_idx) {
244 				DukValue callable = DukValue::copy_from_stack(ctx, -1, DUK_TYPE_MASK_OBJECT);
245 				return [ctx, callable] (ArgTs... args) -> RetT {
246 					dukglue_call<RetT>(ctx, callable, args...);
247 				};
248 			}
249 
250 			template<typename FullT>
251 			static void push(duk_context* ctx, std::function<RetT(ArgTs...)> value) {
252 				static_assert(false, "Pushing an std::function has not been implemented yet. Sorry!");
253 			}
254 		};*/
255 	}
256 }
257