Mercurial > code
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_ |