comparison modules/js/js.h @ 486:7ee8da32da98

Unify all in modules/
author David Demelier <markand@malikania.fr>
date Fri, 13 Nov 2015 09:26:46 +0100
parents
children 758a10c6875c
comparison
equal deleted inserted replaced
485:898d8b29a4f1 486:7ee8da32da98
1 /*
2 * js.h -- JavaScript C++14 wrapper for Duktape
3 *
4 * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #ifndef _JS_H_
20 #define _JS_H_
21
22 /**
23 * @file js.h
24 * @brief Bring JavaScript using Duktape
25 *
26 * This file provides usual Duktape function renamed and placed into `js` namespace. It also replaces error
27 * code with exceptions when possible.
28 *
29 * For convenience, this file also provides templated functions, overloads and much more.
30 */
31
32 #include <functional>
33 #include <memory>
34 #include <string>
35 #include <type_traits>
36 #include <unordered_map>
37 #include <utility>
38 #include <vector>
39
40 #include <duktape.h>
41
42 /**
43 * Duktape C++ namespace wrapper.
44 */
45 namespace js {
46
47 class Context;
48
49 /**
50 * Typedef for readability.
51 */
52 using ContextPtr = duk_context *;
53
54 /*
55 * Basic types to manipulate with the stack
56 * ------------------------------------------------------------------
57 *
58 * The following types can be used in some of the operations like Context::push or Context::is, they are defined
59 * usually as empty classes to determine the appropriate action to execute.
60 *
61 * For example, `ctx.push(js::Object{})` will push an empty object into the stack.
62 */
63
64 /**
65 * @class Object
66 * @brief Empty class tag for push() function.
67 */
68 class Object {
69 };
70
71 /**
72 * @class Array
73 * @brief Empty class tag for push() function.
74 */
75 class Array {
76 };
77
78 /**
79 * @class Global
80 * @brief Empty class tag to push the global object.
81 */
82 class Global {
83 };
84
85 /**
86 * @class Undefined
87 * @brief Empty class tag to push undefined to the stack.
88 */
89 class Undefined {
90 };
91
92 /**
93 * @class Null
94 * @brief Empty class tag to push null to the stack.
95 */
96 class Null {
97 };
98
99 /**
100 * @class This
101 * @brief Empty class tag to push this binding to the stack.
102 */
103 class This {
104 };
105
106 /**
107 * @class RawPointer
108 * @brief Push a non-managed pointer to Duktape, the pointer will never be deleted.
109 * @note For a managed pointer with prototype, see Pointer
110 */
111 template <typename T>
112 class RawPointer {
113 public:
114 /**
115 * The pointer to push.
116 */
117 T *object;
118 };
119
120 /*
121 * Extended type manipulation
122 * ------------------------------------------------------------------
123 *
124 * The following types are different as there are no equivalent in the native Duktape API, they are available for
125 * convenience.
126 */
127
128 /**
129 * @brief Manage shared_ptr from C++ and JavaScript
130 *
131 * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership
132 * and deletion.
133 *
134 * The only requirement is to have the function `void prototype(Context &ctx)` in your class T.
135 */
136 template <typename T>
137 class Shared {
138 public:
139 /**
140 * The shared object.
141 */
142 std::shared_ptr<T> object;
143 };
144
145 /**
146 * @brief Manage pointers from C++ and JavaScript
147 *
148 * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when
149 * the JavaScript garbage collectors collect them so never store a pointer created with this.
150 *
151 * The only requirement is to have the function `void prototype(Context &ctx)` in your class T.
152 */
153 template <typename T>
154 class Pointer {
155 public:
156 /**
157 * The object.
158 */
159 T *object{nullptr};
160 };
161
162 /**
163 * @class Function
164 * @brief Duktape/C function definition.
165 *
166 * This class wraps the std::function as a Duktape/C function by storing a copied pointer.
167 */
168 class Function {
169 public:
170 /**
171 * The function pointer, must not be null.
172 */
173 std::function<int (Context &)> function;
174
175 /**
176 * Number of args that the function takes
177 */
178 int nargs{0};
179 };
180
181 /**
182 * Map of functions to set on an object.
183 */
184 using FunctionMap = std::unordered_map<std::string, Function>;
185
186 /**
187 * Map of string to type, ideal for setting constants like enums.
188 */
189 template <typename Type>
190 using Map = std::unordered_map<std::string, Type>;
191
192 /**
193 * @class ErrorInfo
194 * @brief Error description.
195 *
196 * This class fills the fields got in an Error object.
197 */
198 class ErrorInfo : public std::exception {
199 public:
200 std::string name; //!< name of error
201 std::string message; //!< error message
202 std::string stack; //!< stack if available
203 std::string fileName; //!< filename if applicable
204 int lineNumber{0}; //!< line number if applicable
205
206 /**
207 * Get the error message. This effectively returns message field.
208 *
209 * @return the message
210 */
211 const char *what() const noexcept override
212 {
213 return message.c_str();
214 }
215 };
216
217 /**
218 * @class TypeInfo
219 * @brief Type information to implement new types in JavaScript's context.
220 *
221 * This class depending on your needs may have the following functions:
222 *
223 * - `static void construct(Context &ctx, Type value)`
224 * - `static Type get(Context &ctx, int index)`
225 * - `static bool is(Context &ctx, int index)`
226 * - `static Type optional(Context &ctx, int index, Type defaultValue)`
227 * - `static void push(Context &ctx, Type value)`
228 * - `static Type require(Context &ctx, int index)`
229 *
230 * The `construct` function is used in Context::construct to build a new value as this (e.g. constructors).
231 *
232 * The `get` function is used in Context::get, Context::getProperty, Context::getGlobal to retrieve a value from the
233 * stack.
234 *
235 * The `is` function is used in Context::is to check if the value on the stack is of type `Type`.
236 *
237 * The `optional` function is used in Context::optional to get a value or a replacement if not applicable.
238 *
239 * The `push` function is used in Context::push to usually create a new value on the stack but some specializations
240 * may not (e.g. FunctionMap).
241 *
242 * The `require` function is used in Context::require to get a value from the stack or raise a JavaScript exception if
243 * not applicable.
244 *
245 * This class is fully specialized for: `bool`, `const char *`, `double`, `int`, `std::string`.
246 *
247 * It is also partially specialized for : `Global`, `Object`, `Array`, `Undefined`, `Null`, `std::vector<Type>`.
248 */
249 template <typename Type>
250 class TypeInfo {
251 };
252
253 /**
254 * @class File
255 * @brief Evaluate script from file.
256 * @see Context::eval
257 * @see Context::peval
258 */
259 class File {
260 public:
261 /**
262 * Path to the file.
263 */
264 std::string path;
265
266 /**
267 * Evaluate the file.
268 *
269 * @param ctx the context
270 */
271 inline void eval(duk_context *ctx)
272 {
273 duk_eval_file(ctx, path.c_str());
274 }
275
276 /**
277 * Evaluate in protected mode the file.
278 *
279 * @param ctx the context
280 */
281 inline int peval(duk_context *ctx)
282 {
283 return duk_peval_file(ctx, path.c_str());
284 }
285 };
286
287 /**
288 * @class Script
289 * @brief Evaluate script from raw text.
290 * @see Context::eval
291 * @see Context::peval
292 */
293 class Script {
294 public:
295 /**
296 * The script content.
297 */
298 std::string text;
299
300 /**
301 * Evaluate the script.
302 *
303 * @param ctx the context
304 */
305 inline void eval(duk_context *ctx)
306 {
307 duk_eval_string(ctx, text.c_str());
308 }
309
310 /**
311 * Evaluate in protected mode the script.
312 *
313 * @param ctx the context
314 */
315 inline int peval(duk_context *ctx)
316 {
317 return duk_peval_string(ctx, text.c_str());
318 }
319 };
320
321 /**
322 * @class Context
323 * @brief RAII based Duktape handler.
324 *
325 * This class is implicitly convertible to duk_context for convenience.
326 */
327 class Context {
328 private:
329 using Deleter = void (*)(duk_context *);
330 using Handle = std::unique_ptr<duk_context, Deleter>;
331
332 Handle m_handle;
333
334 /* Move and copy forbidden */
335 Context(const Context &) = delete;
336 Context &operator=(const Context &) = delete;
337 Context(const Context &&) = delete;
338 Context &operator=(const Context &&) = delete;
339
340 public:
341 /**
342 * Create default context.
343 */
344 inline Context()
345 : m_handle{duk_create_heap_default(), duk_destroy_heap}
346 {
347 }
348
349 /**
350 * Create borrowed context that will not be deleted.
351 *
352 * @param ctx the pointer to duk_context
353 */
354 inline Context(ContextPtr ctx) noexcept
355 : m_handle{ctx, [] (ContextPtr) {}}
356 {
357 }
358
359 /**
360 * Convert the context to the native Duktape/C type.
361 *
362 * @return the duk_context
363 */
364 inline operator duk_context *() noexcept
365 {
366 return m_handle.get();
367 }
368
369 /**
370 * Convert the context to the native Duktape/C type.
371 *
372 * @return the duk_context
373 */
374 inline operator duk_context *() const noexcept
375 {
376 return m_handle.get();
377 }
378
379 /*
380 * Basic functions
381 * ----------------------------------------------------------
382 *
383 * The following functions are just standard wrappers around the native Duktape C functions, they are
384 * defined with the same signature except that for convenience some parameters have default sane values.
385 */
386
387 /**
388 * Call the object at the top of the stack.
389 *
390 * @param ctx the context
391 * @param nargs the number of arguments
392 * @note Non-protected
393 */
394 inline void call(unsigned nargs = 0)
395 {
396 duk_call(m_handle.get(), nargs);
397 }
398
399 /**
400 * Copy a value from from to to, overwriting the previous value. If either index is invalid, throws an error.
401 *
402 * @param from the from index
403 * @param to the destination
404 */
405 inline void copy(int from, int to)
406 {
407 duk_copy(m_handle.get(), from, to);
408 }
409
410 /**
411 * Define a property.
412 *
413 * @param index the object index
414 * @param flags the flags
415 * @note Wrapper of duk_def_prop
416 */
417 inline void defineProperty(int index, int flags)
418 {
419 duk_def_prop(m_handle.get(), index, flags);
420 }
421
422 /**
423 * Delete a property.
424 *
425 * @param index the object index
426 * @return true if deleted
427 * @note Wrapper of duk_del_prop
428 */
429 inline bool deleteProperty(int index)
430 {
431 return duk_del_prop(m_handle.get(), index);
432 }
433
434 /**
435 * Delete a property by index.
436 *
437 * @param index the object index
438 * @param position the property index
439 * @return true if deleted
440 * @note Wrapper of duk_del_prop_index
441 */
442 inline bool deleteProperty(int index, int position)
443 {
444 return duk_del_prop_index(m_handle.get(), index, position);
445 }
446
447 /**
448 * Delete a property by name.
449 *
450 * @param index the object index
451 * @param name the property name
452 * @return true if deleted
453 * @note Wrapper of duk_del_prop_string
454 */
455 inline bool deleteProperty(int index, const std::string &name)
456 {
457 return duk_del_prop_string(m_handle.get(), index, name.c_str());
458 }
459
460 /**
461 * Push a duplicate of value at from_index to the stack. If from_index is invalid, throws an error.
462 *
463 * @param index the value to copy
464 * @note Wrapper of duk_dup
465 */
466 inline void dup(int index = -1)
467 {
468 duk_dup(m_handle.get(), index);
469 }
470
471 /**
472 * Evaluate a non-protected chunk that is at the top of the stack.
473 */
474 inline void eval()
475 {
476 duk_eval(m_handle.get());
477 }
478
479 /**
480 * Check if the object as a property.
481 *
482 * @param index the object index
483 * @return true if has
484 * @note Wrapper of duk_has_prop
485 */
486 inline bool hasProperty(int index)
487 {
488 return duk_has_prop(m_handle.get(), index);
489 }
490
491 /**
492 * Check if the object as a property by index.
493 *
494 * @param index the object index
495 * @param position the property index
496 * @return true if has
497 * @note Wrapper of duk_has_prop_index
498 */
499 inline bool hasProperty(int index, int position)
500 {
501 return duk_has_prop_index(m_handle.get(), index, position);
502 }
503
504 /**
505 * Check if the object as a property by string
506 *
507 * @param index the object index
508 * @param name the property name
509 * @return true if has
510 * @note Wrapper of duk_has_prop_string
511 */
512 inline bool hasProperty(int index, const std::string &name)
513 {
514 return duk_has_prop_string(m_handle.get(), index, name.c_str());
515 }
516
517 /**
518 * Check if idx1 is an instance of idx2.
519 *
520 * @param ctx the context
521 * @param idx1 the value to test
522 * @param idx2 the instance requested
523 * @return true if idx1 is instance of idx2
524 * @note Wrapper of duk_instanceof
525 */
526 inline bool instanceof(int idx1, int idx2)
527 {
528 return duk_instanceof(m_handle.get(), idx1, idx2);
529 }
530
531 /**
532 * Insert a value at to with a value popped from the stack top. The previous value at to and any values above
533 * it are moved up the stack by a step. If to is an invalid index, throws an error.
534 *
535 * @note Negative indices are evaluated prior to popping the value at the stack top
536 * @param to the destination
537 * @note Wrapper of duk_insert
538 */
539 inline void insert(int to)
540 {
541 duk_insert(m_handle.get(), to);
542 }
543
544 /**
545 * Pop a certain number of values from the top of the stack.
546 *
547 * @param ctx the context
548 * @param count the number of values to pop
549 * @note Wrapper of duk_pop_n
550 */
551 inline void pop(unsigned count = 1)
552 {
553 duk_pop_n(m_handle.get(), count);
554 }
555
556 /**
557 * Remove value at index. Elements above index are shifted down the stack by a step. If to is an invalid index,
558 * throws an error.
559 *
560 * @param index the value to remove
561 * @note Wrapper of duk_remove
562 */
563 inline void remove(int index)
564 {
565 duk_remove(m_handle.get(), index);
566 }
567
568 /**
569 * Replace value at to_index with a value popped from the stack top. If to_index is an invalid index,
570 * throws an error.
571 *
572 * @param index the value to replace by the value at the top of the stack
573 * @note Negative indices are evaluated prior to popping the value at the stack top.
574 * @note Wrapper of duk_replace
575 */
576 inline void replace(int index)
577 {
578 duk_replace(m_handle.get(), index);
579 }
580
581 /**
582 * Swap values at indices index1 and index2. If the indices are the same, the call is a no-op. If either index
583 * is invalid, throws an error.
584 *
585 * @param index1 the first index
586 * @param index2 the second index
587 * @note Wrapper of duk_swap
588 */
589 inline void swap(int index1, int index2)
590 {
591 duk_swap(m_handle.get(), index1, index2);
592 }
593
594 /**
595 * Get the current stack size.
596 *
597 * @param ctx the context
598 * @return the stack size
599 * @note Wrapper of duk_get_top
600 */
601 inline int top() noexcept
602 {
603 return duk_get_top(m_handle.get());
604 }
605
606 /**
607 * Get the type of the value at the specified index.
608 *
609 * @param ctx the context
610 * @param index the idnex
611 * @return the type
612 * @note Wrapper of duk_get_type
613 */
614 inline int type(int index) noexcept
615 {
616 return duk_get_type(m_handle.get(), index);
617 }
618
619 /*
620 * Extended native functions
621 * ----------------------------------------------------------
622 *
623 * The following functions have different behaviour than the original native Duktape C functions, see their
624 * descriptions for more information
625 */
626
627 /**
628 * Call in protected mode the object at the top of the stack.
629 *
630 * @param nargs the number of arguments
631 * @throw ErrorInfo on errors
632 * @note Wrapper of duk_pcall
633 */
634 void pcall(unsigned nargs = 0);
635
636 /**
637 * Evaluate a non-protected source.
638 *
639 * @param source the source
640 * @see File
641 * @see Script
642 * @note Wrapper of duk_eval
643 */
644 template <typename Source>
645 inline void eval(Source &&source)
646 {
647 source.eval(m_handle.get());
648 }
649
650 /**
651 * Evaluate a protected chunk that is at the top of the stack.
652 *
653 * @throw ErrorInfo the error
654 * @note Wrapper of duk_peval
655 */
656 void peval();
657
658 /**
659 * Evaluate a protected source.
660 *
661 * @param source the source
662 * @see File
663 * @see Script
664 * @throw ErrorInfo on failure
665 * @note Wrapper of duk_peval
666 */
667 template <typename Source>
668 inline void peval(Source &&source)
669 {
670 if (source.peval(m_handle.get()) != 0) {
671 ErrorInfo info = error(-1);
672 duk_pop(m_handle.get());
673
674 throw info;
675 }
676 }
677
678 /*
679 * Push / Get / Require / Is / Optional
680 * ----------------------------------------------------------
681 *
682 * The following functions are used to push, get or check values from the stack. They use specialization
683 * of TypeInfo class.
684 */
685
686 /**
687 * Push a value into the stack. Calls TypeInfo<T>::push(*this, value);
688 *
689 * @param value the value to forward
690 */
691 template <typename Type>
692 inline void push(Type &&value)
693 {
694 TypeInfo<std::decay_t<Type>>::push(*this, std::forward<Type>(value));
695 }
696
697 /**
698 * Generic template function to get a value from the stack.
699 *
700 * @param index the index
701 * @return the value
702 */
703 template <typename Type>
704 inline auto get(int index) -> decltype(TypeInfo<Type>::get(*this, 0))
705 {
706 return TypeInfo<Type>::get(*this, index);
707 }
708
709 /**
710 * Require a type at the specified index.
711 *
712 * @param index the index
713 * @return the value
714 */
715 template <typename Type>
716 inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0))
717 {
718 return TypeInfo<Type>::require(*this, index);
719 }
720
721 /**
722 * Check if a value is a type of T.
723 *
724 * The TypeInfo<T> must have `static bool is(ContextPtr ptr, int index)`.
725 *
726 * @param index the value index
727 * @return true if is the type
728 */
729 template <typename T>
730 inline bool is(int index)
731 {
732 return TypeInfo<T>::is(*this, index);
733 }
734
735 /**
736 * Get an optional value from the stack, if the value is not available of not the correct type,
737 * return defaultValue instead.
738 *
739 * The TypeInfo<T> must have `static T optional(Context &, int index, T &&defaultValue)`.
740 *
741 * @param index the value index
742 * @param defaultValue the value replacement
743 * @return the value or defaultValue
744 */
745 template <typename Type>
746 inline auto optional(int index, Type &&defaultValue)
747 {
748 return TypeInfo<std::decay_t<Type>>::optional(*this, index, std::forward<Type>(defaultValue));
749 }
750
751 /*
752 * Properties management
753 * ----------------------------------------------------------
754 *
755 * The following functions are used to read or set properties on objects or globals also using TypeInfo.
756 */
757
758 /**
759 * Get the property `name' as value from the object at the specified index.
760 *
761 * @param index the object index
762 * @param name the property name
763 * @return the value
764 * @note The stack is unchanged
765 */
766 template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr>
767 inline auto getProperty(int index, const std::string &name) -> decltype(get<Type>(0))
768 {
769 duk_get_prop_string(m_handle.get(), index, name.c_str());
770 decltype(get<Type>(0)) value = get<Type>(-1);
771 duk_pop(m_handle.get());
772
773 return value;
774 }
775
776 /**
777 * Get a property by index, for arrays.
778 *
779 * @param index the object index
780 * @param position the position int the object
781 * @return the value
782 * @note The stack is unchanged
783 */
784 template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr>
785 inline auto getProperty(int index, int position) -> decltype(get<Type>(0))
786 {
787 duk_get_prop_index(m_handle.get(), index, position);
788 decltype(get<Type>(0)) value = get<Type>(-1);
789 duk_pop(m_handle.get());
790
791 return value;
792 }
793
794 /**
795 * Get the property `name' and push it to the stack from the object at the specified index.
796 *
797 * @param index the object index
798 * @param name the property name
799 * @note The stack contains the property value
800 */
801 template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr>
802 inline void getProperty(int index, const std::string &name)
803 {
804 duk_get_prop_string(m_handle.get(), index, name.c_str());
805 }
806
807 /**
808 * Get the property by index and push it to the stack from the object at the specified index.
809 *
810 * @param index the object index
811 * @param position the position in the object
812 * @note The stack contains the property value
813 */
814 template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr>
815 inline void getProperty(int index, int position)
816 {
817 duk_get_prop_index(m_handle.get(), index, position);
818 }
819
820 /**
821 * Get an optional property `name` from the object at the specified index.
822 *
823 * @param index the object index
824 * @param name the property name
825 * @param def the default value
826 * @return the value or def
827 * @note The stack is unchanged
828 */
829 template <typename Type, typename DefaultValue>
830 inline auto optionalProperty(int index, const std::string &name, DefaultValue &&def) -> decltype(optional<Type>(0, std::forward<DefaultValue>(def)))
831 {
832 duk_get_prop_string(m_handle.get(), index, name.c_str());
833 decltype(optional<Type>(0, std::forward<DefaultValue>(def))) value = optional<Type>(-1, std::forward<DefaultValue>(def));
834 duk_pop(m_handle.get());
835
836 return value;
837 }
838
839 /**
840 * Get an optional property by index, for arrays
841 *
842 * @param index the object index
843 * @param position the position int the object
844 * @param def the default value
845 * @return the value or def
846 * @note The stack is unchanged
847 */
848 template <typename Type, typename DefaultValue>
849 inline auto optionalProperty(int index, int position, DefaultValue &&def) -> decltype(optional<Type>(0, std::forward<DefaultValue>(def)))
850 {
851 duk_get_prop_index(m_handle.get(), index, position);
852 decltype(optional<Type>(0, std::forward<DefaultValue>(def))) value = optional<Type>(-1, std::forward<DefaultValue>(def));
853 duk_pop(m_handle.get());
854
855 return value;
856 }
857
858 /**
859 * Set a property to the object at the specified index.
860 *
861 * @param index the object index
862 * @param name the property name
863 * @param value the value to forward
864 * @note The stack is unchanged
865 */
866 template <typename Type>
867 void putProperty(int index, const std::string &name, Type &&value)
868 {
869 index = duk_normalize_index(m_handle.get(), index);
870
871 push(std::forward<Type>(value));
872 duk_put_prop_string(m_handle.get(), index, name.c_str());
873 }
874
875 /**
876 * Set a property by index, for arrays.
877 *
878 * @param index the object index
879 * @param position the position in the object
880 * @param value the value to forward
881 * @note The stack is unchanged
882 */
883 template <typename Type>
884 void putProperty(int index, int position, Type &&value)
885 {
886 index = duk_normalize_index(m_handle.get(), index);
887
888 push(std::forward<Type>(value));
889 duk_put_prop_index(m_handle.get(), index, position);
890 }
891
892 /**
893 * Put the value that is at the top of the stack as property to the object.
894 *
895 * @param index the object index
896 * @param name the property name
897 */
898 inline void putProperty(int index, const std::string &name)
899 {
900 duk_put_prop_string(m_handle.get(), index, name.c_str());
901 }
902
903 /**
904 * Put the value that is at the top of the stack to the object as index.
905 *
906 * @param index the object index
907 * @param position the position in the object
908 */
909 inline void putProperty(int index, int position)
910 {
911 duk_put_prop_index(m_handle.get(), index, position);
912 }
913
914 /**
915 * Get a global value.
916 *
917 * @param name the name of the global variable
918 * @return the value
919 */
920 template <typename Type>
921 inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0))
922 {
923 duk_get_global_string(m_handle.get(), name.c_str());
924 decltype(get<Type>(0)) value = get<Type>(-1);
925 duk_pop(m_handle.get());
926
927 return value;
928 }
929
930 /**
931 * Overload that push the value at the top of the stack instead of returning it.
932 */
933 template <typename Type>
934 inline void getGlobal(const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept
935 {
936 duk_get_global_string(m_handle.get(), name.c_str());
937 }
938
939 /**
940 * Set a global variable.
941 *
942 * @param name the name of the global variable
943 * @param type the value to set
944 */
945 template <typename Type>
946 inline void putGlobal(const std::string &name, Type&& type)
947 {
948 push(std::forward<Type>(type));
949 duk_put_global_string(m_handle.get(), name.c_str());
950 }
951
952 /**
953 * Put the value at the top of the stack as global property.
954 *
955 * @param name the property name
956 */
957 inline void putGlobal(const std::string &name)
958 {
959 duk_put_global_string(m_handle.get(), name.c_str());
960 }
961
962 /*
963 * Extra functions
964 * ----------------------------------------------------------
965 *
966 * The following functions are implemented for convenience and do not exists in the native Duktape API.
967 */
968
969 /**
970 * Get the error object when a JavaScript error has been thrown (e.g. eval failure).
971 *
972 * @param index the index
973 * @return the information
974 */
975 ErrorInfo error(int index);
976
977 /**
978 * Enumerate an object or an array at the specified index.
979 *
980 * @param index the object or array index
981 * @param flags the optional flags to pass to duk_enum
982 * @param getvalue set to true if you want to extract the value
983 * @param func the function to call for each properties
984 */
985 template <typename Func>
986 void enumerate(int index, duk_uint_t flags, duk_bool_t getvalue, Func&& func)
987 {
988 duk_enum(m_handle.get(), index, flags);
989
990 while (duk_next(m_handle.get(), -1, getvalue)) {
991 func(*this);
992 duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0));
993 }
994
995 duk_pop(m_handle.get());
996 }
997
998 /**
999 * Return the this binding of the current function.
1000 *
1001 * @return the this binding as the template given
1002 */
1003 template <typename T>
1004 inline auto self() -> decltype(TypeInfo<T>::get(*this, 0))
1005 {
1006 duk_push_this(m_handle.get());
1007 decltype(TypeInfo<T>::get(*this, 0)) value = TypeInfo<T>::get(*this, -1);
1008 duk_pop(m_handle.get());
1009
1010 return value;
1011 }
1012
1013 /**
1014 * Throw an ECMAScript exception.
1015 *
1016 * @param ex the exception
1017 */
1018 template <typename Exception>
1019 void raise(const Exception &ex)
1020 {
1021 ex.create(m_handle.get());
1022
1023 duk_push_string(m_handle.get(), ex.name().c_str());
1024 duk_put_prop_string(m_handle.get(), -2, "name");
1025 duk_throw(m_handle.get());
1026 }
1027
1028 /**
1029 * Construct the object in place, setting value as this binding.
1030 *
1031 * The TypeInfo<T> must have the following requirements:
1032 *
1033 * - static void construct(Context &, T): must update this with the value and keep the stack unchanged
1034 *
1035 * @param value the value to forward
1036 * @see self
1037 */
1038 template <typename T>
1039 inline void construct(T &&value)
1040 {
1041 TypeInfo<std::decay_t<T>>::construct(*this, std::forward<T>(value));
1042 }
1043 };
1044
1045 /* ------------------------------------------------------------------
1046 * Exception handling
1047 * ------------------------------------------------------------------ */
1048
1049 /**
1050 * @class Error
1051 * @brief Base ECMAScript error class.
1052 * @warning Override the function create for your own exceptions
1053 */
1054 class Error {
1055 protected:
1056 std::string m_name; //!< Name of exception (e.g RangeError)
1057 std::string m_message; //!< The message
1058
1059 /**
1060 * Constructor with a type of error specified, specially designed for derived errors.
1061 *
1062 * @param name the error name (e.g RangeError)
1063 * @param message the message
1064 */
1065 inline Error(std::string name, std::string message) noexcept
1066 : m_name{std::move(name)}
1067 , m_message{std::move(message)}
1068 {
1069 }
1070
1071 public:
1072 /**
1073 * Constructor with a message.
1074 *
1075 * @param message the message
1076 */
1077 inline Error(std::string message) noexcept
1078 : m_name{"Error"}
1079 , m_message{std::move(message)}
1080 {
1081 }
1082
1083 /**
1084 * Get the error type (e.g RangeError).
1085 *
1086 * @return the name
1087 */
1088 inline const std::string &name() const noexcept
1089 {
1090 return m_name;
1091 }
1092
1093 /**
1094 * Create the exception on the stack.
1095 *
1096 * @note the default implementation search for the global variables
1097 * @param ctx the context
1098 */
1099 virtual void create(ContextPtr ctx) const noexcept
1100 {
1101 duk_get_global_string(ctx, m_name.c_str());
1102 duk_push_string(ctx, m_message.c_str());
1103 duk_new(ctx, 1);
1104 }
1105 };
1106
1107 /**
1108 * @class EvalError
1109 * @brief Error in eval() function.
1110 */
1111 class EvalError : public Error {
1112 public:
1113 /**
1114 * Construct an EvalError.
1115 *
1116 * @param message the message
1117 */
1118 inline EvalError(std::string message) noexcept
1119 : Error{"EvalError", std::move(message)}
1120 {
1121 }
1122 };
1123
1124 /**
1125 * @class RangeError
1126 * @brief Value is out of range.
1127 */
1128 class RangeError : public Error {
1129 public:
1130 /**
1131 * Construct an RangeError.
1132 *
1133 * @param message the message
1134 */
1135 inline RangeError(std::string message) noexcept
1136 : Error{"RangeError", std::move(message)}
1137 {
1138 }
1139 };
1140
1141 /**
1142 * @class ReferenceError
1143 * @brief Trying to use a variable that does not exist.
1144 */
1145 class ReferenceError : public Error {
1146 public:
1147 /**
1148 * Construct an ReferenceError.
1149 *
1150 * @param message the message
1151 */
1152 inline ReferenceError(std::string message) noexcept
1153 : Error{"ReferenceError", std::move(message)}
1154 {
1155 }
1156 };
1157
1158 /**
1159 * @class SyntaxError
1160 * @brief Syntax error in the script.
1161 */
1162 class SyntaxError : public Error {
1163 public:
1164 /**
1165 * Construct an SyntaxError.
1166 *
1167 * @param message the message
1168 */
1169 inline SyntaxError(std::string message) noexcept
1170 : Error{"SyntaxError", std::move(message)}
1171 {
1172 }
1173 };
1174
1175 /**
1176 * @class TypeError
1177 * @brief Invalid type given.
1178 */
1179 class TypeError : public Error {
1180 public:
1181 /**
1182 * Construct an TypeError.
1183 *
1184 * @param message the message
1185 */
1186 inline TypeError(std::string message) noexcept
1187 : Error{"TypeError", std::move(message)}
1188 {
1189 }
1190 };
1191
1192 /**
1193 * @class URIError
1194 * @brief URI manipulation failure.
1195 */
1196 class URIError : public Error {
1197 public:
1198 /**
1199 * Construct an URIError.
1200 *
1201 * @param message the message
1202 */
1203 inline URIError(std::string message) noexcept
1204 : Error{"URIError", std::move(message)}
1205 {
1206 }
1207 };
1208
1209 /* ------------------------------------------------------------------
1210 * Standard overloads for TypeInfo<T>
1211 * ------------------------------------------------------------------ */
1212
1213 /**
1214 * @class TypeInfo<int>
1215 * @brief Default implementation for int.
1216 *
1217 * Provides: get, is, optional, push, require.
1218 */
1219 template <>
1220 class TypeInfo<int> {
1221 public:
1222 /**
1223 * Get an integer, return 0 if not an integer.
1224 *
1225 * @param ctx the context
1226 * @param index the index
1227 * @return the integer
1228 */
1229 static inline int get(Context &ctx, int index)
1230 {
1231 return duk_get_int(ctx, index);
1232 }
1233
1234 /**
1235 * Check if value is an integer.
1236 *
1237 * @param ctx the context
1238 * @param index the index
1239 * @return true if integer
1240 */
1241 static inline bool is(Context &ctx, int index)
1242 {
1243 return duk_is_number(ctx, index);
1244 }
1245
1246 /**
1247 * Get an integer, return defaultValue if the value is not an integer.
1248 *
1249 * @param ctx the context
1250 * @param index the index
1251 * @param defaultValue the defaultValue
1252 * @return the integer or defaultValue
1253 */
1254 static inline int optional(Context &ctx, int index, int defaultValue)
1255 {
1256 if (!duk_is_number(ctx, index)) {
1257 return defaultValue;
1258 }
1259
1260 return duk_get_int(ctx, index);
1261 }
1262
1263 /**
1264 * Push an integer.
1265 *
1266 * @param ctx the context
1267 * @param value the value
1268 */
1269 static inline void push(Context &ctx, int value)
1270 {
1271 duk_push_int(ctx, value);
1272 }
1273
1274 /**
1275 * Require an integer, throws a JavaScript exception if not an integer.
1276 *
1277 * @param ctx the context
1278 * @param index the index
1279 * @return the integer
1280 */
1281 static inline int require(Context &ctx, int index)
1282 {
1283 return duk_require_int(ctx, index);
1284 }
1285 };
1286
1287 /**
1288 * @class TypeInfo<bool>
1289 * @brief Default implementation for bool.
1290 *
1291 * Provides: get, is, optional, push, require.
1292 */
1293 template <>
1294 class TypeInfo<bool> {
1295 public:
1296 /**
1297 * Get a boolean, return 0 if not a boolean.
1298 *
1299 * @param ctx the context
1300 * @param index the index
1301 * @return the boolean
1302 */
1303 static inline bool get(Context &ctx, int index)
1304 {
1305 return duk_get_boolean(ctx, index);
1306 }
1307
1308 /**
1309 * Check if value is a boolean.
1310 *
1311 * @param ctx the context
1312 * @param index the index
1313 * @return true if boolean
1314 */
1315 static inline bool is(Context &ctx, int index)
1316 {
1317 return duk_is_boolean(ctx, index);
1318 }
1319
1320 /**
1321 * Get a bool, return defaultValue if the value is not a boolean.
1322 *
1323 * @param ctx the context
1324 * @param index the index
1325 * @param defaultValue the defaultValue
1326 * @return the boolean or defaultValue
1327 */
1328 static inline bool optional(Context &ctx, int index, bool defaultValue)
1329 {
1330 if (!duk_is_boolean(ctx, index)) {
1331 return defaultValue;
1332 }
1333
1334 return duk_get_boolean(ctx, index);
1335 }
1336
1337 /**
1338 * Push a boolean.
1339 *
1340 * @param ctx the context
1341 * @param value the value
1342 */
1343 static inline void push(Context &ctx, bool value)
1344 {
1345 duk_push_boolean(ctx, value);
1346 }
1347
1348 /**
1349 * Require a boolean, throws a JavaScript exception if not a boolean.
1350 *
1351 * @param ctx the context
1352 * @param index the index
1353 * @return the boolean
1354 */
1355 static inline bool require(Context &ctx, int index)
1356 {
1357 return duk_require_boolean(ctx, index);
1358 }
1359 };
1360
1361 /**
1362 * @class TypeInfo<double>
1363 * @brief Default implementation for double.
1364 *
1365 * Provides: get, is, optional, push, require.
1366 */
1367 template <>
1368 class TypeInfo<double> {
1369 public:
1370 /**
1371 * Get a double, return 0 if not a double.
1372 *
1373 * @param ctx the context
1374 * @param index the index
1375 * @return the double
1376 */
1377 static inline double get(Context &ctx, int index)
1378 {
1379 return duk_get_number(ctx, index);
1380 }
1381
1382 /**
1383 * Check if value is a double.
1384 *
1385 * @param ctx the context
1386 * @param index the index
1387 * @return true if double
1388 */
1389 static inline bool is(Context &ctx, int index)
1390 {
1391 return duk_is_number(ctx, index);
1392 }
1393
1394 /**
1395 * Get a double, return defaultValue if the value is not a double.
1396 *
1397 * @param ctx the context
1398 * @param index the index
1399 * @param defaultValue the defaultValue
1400 * @return the double or defaultValue
1401 */
1402 static inline double optional(Context &ctx, int index, double defaultValue)
1403 {
1404 if (!duk_is_number(ctx, index)) {
1405 return defaultValue;
1406 }
1407
1408 return duk_get_number(ctx, index);
1409 }
1410
1411 /**
1412 * Push a double.
1413 *
1414 * @param ctx the context
1415 * @param value the value
1416 */
1417 static inline void push(Context &ctx, double value)
1418 {
1419 duk_push_number(ctx, value);
1420 }
1421
1422 /**
1423 * Require a double, throws a JavaScript exception if not a double.
1424 *
1425 * @param ctx the context
1426 * @param index the index
1427 * @return the double
1428 */
1429 static inline double require(Context &ctx, int index)
1430 {
1431 return duk_require_number(ctx, index);
1432 }
1433 };
1434
1435 /**
1436 * @class TypeInfo<std::string>
1437 * @brief Default implementation for std::string.
1438 *
1439 * Provides: get, is, optional, push, require.
1440 *
1441 * Note: the functions allows embedded '\0'.
1442 */
1443 template <>
1444 class TypeInfo<std::string> {
1445 public:
1446 /**
1447 * Get a string, return 0 if not a string.
1448 *
1449 * @param ctx the context
1450 * @param index the index
1451 * @return the string
1452 */
1453 static inline std::string get(Context &ctx, int index)
1454 {
1455 duk_size_t size;
1456 const char *text = duk_get_lstring(ctx, index, &size);
1457
1458 return std::string{text, size};
1459 }
1460
1461 /**
1462 * Check if value is a string.
1463 *
1464 * @param ctx the context
1465 * @param index the index
1466 * @return true if string
1467 */
1468 static inline bool is(Context &ctx, int index)
1469 {
1470 return duk_is_string(ctx, index);
1471 }
1472
1473 /**
1474 * Get a string, return defaultValue if the value is not an string.
1475 *
1476 * @param ctx the context
1477 * @param index the index
1478 * @param defaultValue the defaultValue
1479 * @return the string or defaultValue
1480 */
1481 static inline std::string optional(Context &ctx, int index, std::string defaultValue)
1482 {
1483 if (!duk_is_string(ctx, index)) {
1484 return defaultValue;
1485 }
1486
1487 return get(ctx, index);
1488 }
1489
1490 /**
1491 * Push a string.
1492 *
1493 * @param ctx the context
1494 * @param value the value
1495 */
1496 static inline void push(Context &ctx, const std::string &value)
1497 {
1498 duk_push_lstring(ctx, value.c_str(), value.length());
1499 }
1500
1501 /**
1502 * Require a string, throws a JavaScript exception if not a string.
1503 *
1504 * @param ctx the context
1505 * @param index the index
1506 * @return the string
1507 */
1508 static inline std::string require(Context &ctx, int index)
1509 {
1510 duk_size_t size;
1511 const char *text = duk_require_lstring(ctx, index, &size);
1512
1513 return std::string{text, size};
1514 }
1515 };
1516
1517 /**
1518 * @class TypeInfo<const char *>
1519 * @brief Default implementation for const char literals.
1520 *
1521 * Provides: get, is, optional, push, require.
1522 */
1523 template <>
1524 class TypeInfo<const char *> {
1525 public:
1526 /**
1527 * Get a string, return 0 if not a string.
1528 *
1529 * @param ctx the context
1530 * @param index the index
1531 * @return the string
1532 */
1533 static inline const char *get(Context &ctx, int index)
1534 {
1535 return duk_get_string(ctx, index);
1536 }
1537
1538 /**
1539 * Check if value is a string.
1540 *
1541 * @param ctx the context
1542 * @param index the index
1543 * @return true if string
1544 */
1545 static inline bool is(Context &ctx, int index)
1546 {
1547 return duk_is_string(ctx, index);
1548 }
1549
1550 /**
1551 * Get an integer, return defaultValue if the value is not an integer.
1552 *
1553 * @param ctx the context
1554 * @param index the index
1555 * @param defaultValue the defaultValue
1556 * @return the integer or defaultValue
1557 */
1558 static inline const char *optional(Context &ctx, int index, const char *defaultValue)
1559 {
1560 if (!duk_is_string(ctx, index)) {
1561 return defaultValue;
1562 }
1563
1564 return duk_get_string(ctx, index);
1565 }
1566
1567 /**
1568 * Push a string.
1569 *
1570 * @param ctx the context
1571 * @param value the value
1572 */
1573 static inline void push(Context &ctx, const char *value)
1574 {
1575 duk_push_string(ctx, value);
1576 }
1577
1578 /**
1579 * Require a string, throws a JavaScript exception if not a string.
1580 *
1581 * @param ctx the context
1582 * @param index the index
1583 * @return the string
1584 */
1585 static inline const char *require(Context &ctx, int index)
1586 {
1587 return duk_require_string(ctx, index);
1588 }
1589 };
1590
1591 /**
1592 * @brief Implementation for non-managed pointers.
1593 *
1594 * Provides: get, is, optional, push, require.
1595 */
1596 template <typename T>
1597 class TypeInfo<RawPointer<T>> {
1598 public:
1599 /**
1600 * Get a pointer, return nullptr if not a pointer.
1601 *
1602 * @param ctx the context
1603 * @param index the index
1604 * @return the pointer
1605 */
1606 static inline T *get(Context &ctx, int index)
1607 {
1608 return static_cast<T *>(duk_to_pointer(ctx, index));
1609 }
1610
1611 /**
1612 * Check if value is a pointer.
1613 *
1614 * @param ctx the context
1615 * @param index the index
1616 * @return true if pointer
1617 */
1618 static inline bool is(Context &ctx, int index)
1619 {
1620 return duk_is_pointer(ctx, index);
1621 }
1622
1623 /**
1624 * Get a pointer, return defaultValue if the value is not a pointer.
1625 *
1626 * @param ctx the context
1627 * @param index the index
1628 * @param defaultValue the defaultValue
1629 * @return the pointer or defaultValue
1630 */
1631 static inline T *optional(Context &ctx, int index, RawPointer<T> defaultValue)
1632 {
1633 if (!duk_is_pointer(ctx, index)) {
1634 return defaultValue.object;
1635 }
1636
1637 return static_cast<T *>(duk_to_pointer(ctx, index));
1638 }
1639
1640 /**
1641 * Push a pointer.
1642 *
1643 * @param ctx the context
1644 * @param value the value
1645 */
1646 static inline void push(Context &ctx, const RawPointer<T> &value)
1647 {
1648 duk_push_pointer(ctx, value.object);
1649 }
1650
1651 /**
1652 * Require a pointer, throws a JavaScript exception if not a pointer.
1653 *
1654 * @param ctx the context
1655 * @param index the index
1656 * @return the pointer
1657 */
1658 static inline T *require(Context &ctx, int index)
1659 {
1660 return static_cast<T *>(duk_require_pointer(ctx, index));
1661 }
1662 };
1663
1664 /**
1665 * @class TypeInfo<Function>
1666 * @brief Push C++ function to the stack.
1667 *
1668 * Provides: push.
1669 *
1670 * This implementation push a Duktape/C function that is wrapped as C++ for convenience.
1671 */
1672 template <>
1673 class TypeInfo<Function> {
1674 public:
1675 /**
1676 * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the
1677 * std::function.
1678 *
1679 * @param ctx the context
1680 * @param fn the function
1681 */
1682 static void push(Context &ctx, Function fn);
1683 };
1684
1685 /**
1686 * @class TypeInfo<FunctionMap>
1687 * @brief Put the functions to the object at the top of the stack.
1688 *
1689 * Provides: push.
1690 */
1691 template <>
1692 class TypeInfo<FunctionMap> {
1693 public:
1694 /**
1695 * Push a map of function to the object at the top of the stack.
1696 *
1697 * @param ctx the context
1698 * @param map the map of function
1699 */
1700 static inline void push(Context &ctx, const FunctionMap &map)
1701 {
1702 for (const auto &entry : map) {
1703 ctx.putProperty(-1, entry.first, entry.second);
1704 }
1705 }
1706 };
1707
1708 /**
1709 * @class TypeInfo<Object>
1710 * @brief Push empty object to the stack.
1711 *
1712 * Provides: is, push.
1713 */
1714 template <>
1715 class TypeInfo<Object> {
1716 public:
1717 /**
1718 * Check if value is an object.
1719 *
1720 * @param ctx the context
1721 * @param index the index
1722 * @return true if object
1723 */
1724 static inline bool is(Context &ctx, int index)
1725 {
1726 return duk_is_object(ctx, index);
1727 }
1728
1729 /**
1730 * Create an empty object on the stack.
1731 *
1732 * @param ctx the context
1733 */
1734 static inline void push(Context &ctx, const Object &)
1735 {
1736 duk_push_object(ctx);
1737 }
1738 };
1739
1740 /**
1741 * @class TypeInfo<Array>
1742 * @brief Push empty array to the stack.
1743 *
1744 * Provides: is, push.
1745 */
1746 template <>
1747 class TypeInfo<Array> {
1748 public:
1749 /**
1750 * Check if value is a array.
1751 *
1752 * @param ctx the context
1753 * @param index the index
1754 * @return true if array
1755 */
1756 static inline bool is(Context &ctx, int index)
1757 {
1758 return duk_is_array(ctx, index);
1759 }
1760
1761 /**
1762 * Create an empty array on the stack.
1763 *
1764 * @param ctx the context
1765 */
1766 static inline void push(Context &ctx, const Array &)
1767 {
1768 duk_push_array(ctx);
1769 }
1770 };
1771
1772 /**
1773 * @class TypeInfo<Undefined>
1774 * @brief Push undefined value to the stack.
1775 *
1776 * Provides: is, push.
1777 */
1778 template <>
1779 class TypeInfo<Undefined> {
1780 public:
1781 /**
1782 * Check if value is undefined.
1783 *
1784 * @param ctx the context
1785 * @param index the index
1786 * @return true if undefined
1787 */
1788 static inline bool is(Context &ctx, int index)
1789 {
1790 return duk_is_undefined(ctx, index);
1791 }
1792
1793 /**
1794 * Push undefined value on the stack.
1795 *
1796 * @param ctx the context
1797 */
1798 static inline void push(Context &ctx, const Undefined &)
1799 {
1800 duk_push_undefined(ctx);
1801 }
1802 };
1803
1804 /**
1805 * @class TypeInfo<Null>
1806 * @brief Push null value to the stack.
1807 *
1808 * Provides: is, push.
1809 */
1810 template <>
1811 class TypeInfo<Null> {
1812 public:
1813 /**
1814 * Check if value is null.
1815 *
1816 * @param ctx the context
1817 * @param index the index
1818 * @return true if null
1819 */
1820 static inline bool is(Context &ctx, int index)
1821 {
1822 return duk_is_null(ctx, index);
1823 }
1824
1825 /**
1826 * Push null value on the stack.
1827 *
1828 * @param ctx the context
1829 */
1830 static inline void push(Context &ctx, const Null &)
1831 {
1832 duk_push_null(ctx);
1833 }
1834 };
1835
1836 /**
1837 * @brief Push this binding into the stack.
1838 *
1839 * Provides: push.
1840 */
1841 template <>
1842 class TypeInfo<This> {
1843 public:
1844 /**
1845 * Push this function into the stack.
1846 *
1847 * @param ctx the context
1848 */
1849 static inline void push(Context &ctx, const This &)
1850 {
1851 duk_push_this(ctx);
1852 }
1853 };
1854
1855 /**
1856 * @class TypeInfo<Global>
1857 * @brief Push the global object to the stack.
1858 *
1859 * Provides: push.
1860 */
1861 template <>
1862 class TypeInfo<Global> {
1863 public:
1864 /**
1865 * Push the global object into the stack.
1866 *
1867 * @param ctx the context
1868 */
1869 static inline void push(Context &ctx, const Global &)
1870 {
1871 duk_push_global_object(ctx);
1872 }
1873 };
1874
1875 /**
1876 * @brief Push a map of key-value pair as objects.
1877 *
1878 * Provides: push.
1879 *
1880 * This class is convenient for settings constants such as enums, string and such.
1881 */
1882 template <typename T>
1883 class TypeInfo<std::unordered_map<std::string, T>> {
1884 public:
1885 /**
1886 * Put all values from the map as properties to the object at the top of the stack.
1887 *
1888 * @param ctx the context
1889 * @param map the values
1890 * @note You need an object at the top of the stack before calling this function
1891 */
1892 static void push(Context &ctx, const std::unordered_map<std::string, T> &map)
1893 {
1894 for (const auto &pair : map) {
1895 TypeInfo<T>::push(ctx, pair.second);
1896 duk_put_prop_string(ctx, -2, pair.first.c_str());
1897 }
1898 }
1899 };
1900
1901 /**
1902 * @brief Push or get vectors as JavaScript arrays.
1903 *
1904 * Provides: get, push.
1905 */
1906 template <typename T>
1907 class TypeInfo<std::vector<T>> {
1908 public:
1909 /**
1910 * Get an array from the stack.
1911 *
1912 * @param ctx the context
1913 * @param index the array index
1914 * @return the array or empty array if the value is not an array
1915 */
1916 static std::vector<T> get(Context &ctx, int index)
1917 {
1918 std::vector<T> result;
1919
1920 if (!duk_is_array(ctx, -1)) {
1921 return result;
1922 }
1923
1924 int total = duk_get_length(ctx, index);
1925 for (int i = 0; i < total; ++i) {
1926 result.push_back(ctx.getProperty<T>(index, i));
1927 }
1928
1929 return result;
1930 }
1931
1932 /**
1933 * Create an array with the specified values.
1934 *
1935 * @param ctx the context
1936 * @param array the values
1937 */
1938 static void push(Context &ctx, const std::vector<T> &array)
1939 {
1940 duk_push_array(ctx);
1941
1942 int i = 0;
1943 for (const auto &v : array) {
1944 TypeInfo<T>::push(ctx, v);
1945 duk_put_prop_index(ctx, -2, i++);
1946 }
1947 }
1948 };
1949
1950 /**
1951 * @brief Implementation of managed shared_ptr
1952 * @see Shared
1953 */
1954 template <typename T>
1955 class TypeInfo<Shared<T>> {
1956 private:
1957 static void apply(Context &ctx, std::shared_ptr<T> value)
1958 {
1959 duk_push_boolean(ctx, false);
1960 duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
1961 duk_push_pointer(ctx, new std::shared_ptr<T>(value));
1962 duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr");
1963 duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
1964 duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
1965
1966 if (!duk_to_boolean(ctx, -1)) {
1967 duk_push_boolean(ctx, true);
1968 duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
1969 duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr");
1970 delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
1971 duk_pop(ctx);
1972 }
1973
1974 duk_pop(ctx);
1975
1976 return 0;
1977 }, 1);
1978 duk_set_finalizer(ctx, -2);
1979 }
1980
1981 public:
1982 /**
1983 * Construct the shared_ptr as this.
1984 *
1985 * @param ctx the context
1986 * @param value the value
1987 */
1988 static void construct(Context &ctx, Shared<T> value)
1989 {
1990 duk_push_this(ctx);
1991 apply(ctx, std::move(value.object));
1992 duk_pop(ctx);
1993 }
1994
1995 /**
1996 * Push a managed shared_ptr as object.
1997 *
1998 * @param ctx the context
1999 * @param value the value
2000 */
2001 static void push(Context &ctx, Shared<T> value)
2002 {
2003 duk_push_object(ctx);
2004 apply(ctx, value.object);
2005 value.object->prototype(ctx);
2006 duk_set_prototype(ctx, -2);
2007 }
2008
2009 /**
2010 * Get a managed shared_ptr from the stack.
2011 *
2012 * @param ctx the context
2013 * @param index the object index
2014 * @return the shared_ptr
2015 */
2016 static std::shared_ptr<T> get(Context &ctx, int index)
2017 {
2018 duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr");
2019 std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1));
2020 duk_pop(ctx);
2021
2022 return value;
2023 }
2024 };
2025
2026 /**
2027 * @brief Implementation of managed pointers
2028 * @see Pointer
2029 */
2030 template <typename T>
2031 class TypeInfo<Pointer<T>> {
2032 private:
2033 static void apply(Context &ctx, T *value)
2034 {
2035 duk_push_boolean(ctx, false);
2036 duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted");
2037 duk_push_pointer(ctx, value);
2038 duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr");
2039 duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t {
2040 duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted");
2041
2042 if (!duk_to_boolean(ctx, -1)) {
2043 duk_push_boolean(ctx, true);
2044 duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted");
2045 duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr");
2046 delete static_cast<T *>(duk_to_pointer(ctx, -1));
2047 duk_pop(ctx);
2048 }
2049
2050 duk_pop(ctx);
2051
2052 return 0;
2053 }, 1);
2054 duk_set_finalizer(ctx, -2);
2055 }
2056
2057 public:
2058 /**
2059 * Construct the pointer as this.
2060 *
2061 * @param ctx the context
2062 * @param value the value
2063 */
2064 static void construct(Context &ctx, Pointer<T> value)
2065 {
2066 duk_push_this(ctx);
2067 apply(ctx, value.object);
2068 duk_pop(ctx);
2069 }
2070
2071 /**
2072 * Push a managed pointer as object.
2073 *
2074 * @param ctx the context
2075 * @param value the value
2076 */
2077 static void push(Context &ctx, Pointer<T> value)
2078 {
2079 duk_push_object(ctx);
2080 apply(ctx, value.object);
2081 value.object->prototype(ctx);
2082 duk_set_prototype(ctx, -2);
2083 }
2084
2085 /**
2086 * Get a managed pointer from the stack.
2087 *
2088 * @param ctx the context
2089 * @param index the object index
2090 * @return the pointer
2091 * @warning Do not store the pointer into the C++ side, the object can be deleted at any time
2092 */
2093 static T *get(Context &ctx, int index)
2094 {
2095 duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr");
2096 T *value = static_cast<T *>(duk_to_pointer(ctx, -1));
2097 duk_pop(ctx);
2098
2099 return value;
2100 }
2101 };
2102
2103 } // !js
2104
2105 #endif // !_JS_H_