├── .hgignore
├── ChakraCore
├── ChakraCore.props
├── bin
│ ├── Win32
│ │ ├── Debug
│ │ │ └── README.md
│ │ └── Release
│ │ │ └── README.md
│ └── x64
│ │ ├── Debug
│ │ └── README.md
│ │ └── Release
│ │ └── README.md
└── inc
│ └── README.md
├── ChakraCoreCppBridge.sln
├── LICENSE.txt
├── README.md
├── bridge.props
├── example
├── example.cpp
├── example.vcxproj
└── example.vcxproj.filters
└── include
└── chakra_bridge
├── chakra_bridge.h
└── chakra_macros.h
/.hgignore:
--------------------------------------------------------------------------------
1 | syntax: glob
2 | ChakraCore/bin/*
3 | ChakraCoreCppBridge.VC.*
4 | */x64/*
5 | x64/*
6 | */Win32/*
7 | Win32/*
8 | .vs/*
9 |
--------------------------------------------------------------------------------
/ChakraCore/ChakraCore.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | $(SolutionDir)ChakraCore\bin\$(Platform)\$(Configuration);$(LibraryPath)
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ChakraCore/bin/Win32/Debug/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | To build a sample code, clone and compile a ChakraCore from
4 |
5 |
6 |
7 | Then build and copy resulting binaries (DLL) and import library files (LIB) to corresponding subfolders under bin folder.
8 |
--------------------------------------------------------------------------------
/ChakraCore/bin/Win32/Release/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | To build a sample code, clone and compile a ChakraCore from
4 |
5 |
6 |
7 | Then build and copy resulting binaries (DLL) and import library files (LIB) to corresponding subfolders under bin folder.
8 |
--------------------------------------------------------------------------------
/ChakraCore/bin/x64/Debug/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | To build a sample code, clone and compile a ChakraCore from
4 |
5 |
6 |
7 | Then build and copy resulting binaries (DLL) and import library files (LIB) to corresponding subfolders under bin folder.
8 |
--------------------------------------------------------------------------------
/ChakraCore/bin/x64/Release/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | To build a sample code, clone and compile a ChakraCore from
4 |
5 |
6 |
7 | Then build and copy resulting binaries (DLL) and import library files (LIB) to corresponding subfolders under bin folder.
8 |
--------------------------------------------------------------------------------
/ChakraCore/inc/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | To build a sample code, clone a ChakraCore repo from
4 |
5 |
6 |
7 | And copy two files: chakracommon.h and ChakraCore.h to the current folder.
8 |
9 |
--------------------------------------------------------------------------------
/ChakraCoreCppBridge.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example\example.vcxproj", "{CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Debug|x64.ActiveCfg = Debug|x64
17 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Debug|x64.Build.0 = Debug|x64
18 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Debug|x86.ActiveCfg = Debug|Win32
19 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Debug|x86.Build.0 = Debug|Win32
20 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Release|x64.ActiveCfg = Release|x64
21 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Release|x64.Build.0 = Release|x64
22 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Release|x86.ActiveCfg = Release|Win32
23 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | ChakraCoreCppBridge
2 | The MIT License (MIT)
3 |
4 | Copyright (c) HHD Software Ltd.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Overview of ChakraCoreCppBridge Library
2 |
3 | The purpose of this single-header library is to provide a convenient bridge between [ChakraCore] C-style API and C++.
4 |
5 | [ChakraCore]: https://github.com/Microsoft/ChakraCore
6 |
7 | To start using this library, clone or download it and then `#include` the single header "chakra_bridge/chakra_bridge.h". Library has no external dependencies, except, obviously, for the ChakraCore itself.
8 |
9 | ## Code Structure
10 |
11 | This repository consists of the following subdirectories:
12 |
13 | * **include**
14 | * Contains `chakra_bridge.h` header as well as `chakra_macros.h` header with additional macros for advanced usage scenarios.
15 | * **example**
16 | * Contains an example project that illustrates the library usage.
17 | * **ChakraCore**
18 | * Contains a copy of ChakraCore include files and binary files (binary files are not included, see README.md file for instructions on getting them). This directory is only required to build an example project.
19 |
20 | ## Compiler Support
21 |
22 | The library has been tested on Microsoft Visual Studio 2015 Update 3.
23 |
24 | ## Documentation
25 |
26 | ChakraCoreCppBridge defines all its identifiers in `jsc` namespace. It also brings a few helper functions into the global namespace. If you do not want to bring them into global namespace, make sure `CBRIDGE_NO_GLOBAL_NAMESPACE` preprocessor constant is defined before including the library header.
27 |
28 | ### `runtime` class
29 |
30 | `runtime` class is a RAII-style wrapper for `JsRuntimeHandle`. To use it, construct the object of this class and call its `create` method:
31 |
32 | ```C++
33 | jsc::runtime runtime;
34 | check(runtime.create(JsRuntimeAttributeNone));
35 | ```
36 |
37 | When `runtime` object goes out of scope, `JsDisposeRuntime` function is called to dispose the ChakraCore runtime.
38 |
39 | ### `context` class
40 |
41 | `context` class is a RAII-style wrapper for `JsContextHandle`. To use it, construct the object of this class and call its `create` method, passing a reference to an initialized `runtime` object:
42 |
43 | ```C++
44 | jsc::runtime runtime;
45 | jsc::context context;
46 | check(runtime.create(JsRuntimeAttributeNone));
47 | check(context.create(runtime));
48 | ```
49 |
50 | After the context is successfully created, it may be set as current context with a help of another RAII-style class `scoped_context`:
51 |
52 | ```C++
53 | {
54 | jsc::scoped_context sc(context);
55 | // context is set as current context until the end of scope
56 | }
57 | ```
58 |
59 | Remember that unless the current context is set, all other ChakraCore functions will return error code!
60 |
61 | ### `value` class
62 |
63 | This is a "heart" of the ChakraCoreCppBridge library. It is essentially a RAII-style wrapper for `JsValueRef` type with a number of useful methods and operators.
64 |
65 | First, let us note that the `value` class is a thin wrapper which has the same size as `JsValueRef`, that is, a size of a pointer. They are cheap to copy, pass and store.
66 |
67 | Remember that values of class `value` are never to be stored anywhere except as in a local variable inside the current scope! ChakraCore manages the lifetime of these objects. If you need to save the value outside of the current scope, use the `referenced_value` class instead.
68 |
69 | #### Well-known Constants
70 |
71 | There are several static methods which should be called to get a reference to built-in or well-known ChakraCore objects and values:
72 |
73 | ```C++
74 | static value value::null(); // returns a reference to a `Null` object.
75 | static value value::undefined(); // returns a reference to an `undefined` value
76 | static value value::true_(); // returns a reference to `true` boolean value
77 | static value value::false_(); // returns a reference to `false` boolean value
78 | ```
79 |
80 | #### Creating ChakraCore Immediate Values
81 |
82 | Immediate values are integer values, floating-point values, boolean values and strings. Class `value` has implicit constructors that take values of different C++ types and convert them to JavaScript types. The following table shows the conversion rules:
83 |
84 | | C++ type | JavaScript type |
85 | |-------------|-------------|
86 | | integer types | `Number` (either through a call to `JsIntToNumber` or to `JsDoubleToNumber`, depending on type size and value) |
87 | | floating-point types | `Number` (through a call to `JsDoubleToNumber`) |
88 | | bool | `Boolean` |
89 | | std::wstring | `String` |
90 | | nullptr_t | Null (equivalent to calling `value::null`) |
91 | | enum | `Number`, based on underlying integer type |
92 |
93 | #### Creating Arrays
94 |
95 | There are a number of static methods that can be used to construct JavaScript array objects:
96 |
97 | ```C++
98 | // construct JavaScript array object and fill it with passed arguments
99 | static value value::array(std::initializer_list arguments);
100 |
101 | // construct JavaScript array object and fill it with passed arguments
102 | template
103 | static value value::array(Args &&...args);
104 |
105 | // construct JavaScript array object and fill it with passed arguments
106 | // requires boost.range
107 | template
108 | static value value::array_from_range(const Range &range);
109 |
110 | // construct uninitialized JavaScript array object of a given size
111 | static value value::uninitialized_array(unsigned int size = 0);
112 | ```
113 |
114 | Sample usage:
115 |
116 | ```C++
117 | auto array1 = jsc::value::array(true, false, 10, L"test"s);
118 | std::vector v {10, 20, 30};
119 | auto array2 = jsc::value::array_from_range(v);
120 | auto array3 = jsc::value::uninitialized_array(10);
121 | ```
122 |
123 | `ArrayBuffer` object may be created using one of the following static methods:
124 |
125 | ```C++
126 | // construct JavaScript ArrayBuffer object referencing external memory (check object lifetime!)
127 | static value value::array_buffer(void *pdata, size_t size);
128 | // construct JavaScript ArrayBuffer object referencing copy of external memory
129 | static value value::array_buffer_copy(const void *pdata, size_t size);
130 | ```
131 |
132 | The difference between them is that the second method copies the passed buffer and does not require that the buffer outlives the created array object.
133 |
134 | Typed arrays are created with a call to a following static method:
135 |
136 | ```C++
137 | static value value::typed_array(JsTypedArrayType arrayType, const value &baseArray, unsigned int byteOffset = 0, unsigned int elementLength = 0);
138 | ```
139 |
140 | #### Converting `value` to C++ Types
141 |
142 | Before we continue with functions and objects, let us describe how the values of class `value` may be converted back to C++ types.
143 |
144 | There are several conversion options:
145 |
146 | 1. A number of as_xxx methods:
147 | ```C++
148 | bool value::as_bool() const;
149 | int value::as_int() const;
150 | double value::as_double() const;
151 | std::wstring value::as_string() const;
152 | ```
153 |
154 | 2. A templated `as` method:
155 | ```C++
156 | auto int_value = v1.as();
157 | auto dbl_value = v2.as();
158 | auto bool_value = v3.as();
159 | auto str_value = v4.as();
160 | auto uint_value = v5.as();
161 | ```
162 |
163 | 3. "Direct" conversion using static_cast:
164 | ```C++
165 | auto str_value = static_cast(v4);
166 | ```
167 |
168 | Conversion to `enum` types are also supported.
169 |
170 | Note that all conversion methods require the ChakraCore value be of correct or compatible type. For example, if you try to convert a string or array value into an integer, an exception is thrown.
171 |
172 | You may use one of the following methods before converting to C++ type if you want to change the underlying JavaScript type:
173 |
174 | ```C++
175 | value value::to_number() const; // convert to number
176 | value value::to_object() const; // convert to object
177 | value value::to_string() const; // convert to string
178 | ```
179 |
180 | Use the following method to determine the type of the ChakraCore value:
181 |
182 | ```C++
183 | JsValueType value::value_type() const;
184 | ```
185 |
186 | In addition, the following methods may be used to test the properties of a type:
187 |
188 | ```C++
189 | // Test if the value is empty (uninitialized)
190 | bool value::is_empty() const noexcept;
191 | bool value::empty() const noexcept;
192 |
193 | // Test if value is null
194 | bool value::is_null() const;
195 |
196 | // Test if value is undefined
197 | bool value::is_undefined() const;
198 |
199 | // Test is value is a number
200 | bool value::is_number() const;
201 |
202 | // Test if value is a boolean
203 | bool value::is_boolean() const;
204 |
205 | // Test if value is string
206 | bool value::is_string() const;
207 |
208 | // Test if value is object
209 | bool value::is_object() const;
210 |
211 | // Test if value is array
212 | bool value::is_array() const;
213 |
214 | // Test if value is function
215 | bool value::is_function() const;
216 |
217 | // Test if value is typed array
218 | bool value::is_typed_array() const;
219 |
220 | // Test if value is an instance of ArrayBuffer class
221 | bool value::is_array_buffer() const;
222 |
223 | // Test if value is an instance of DataView class
224 | bool value::is_data_view() const;
225 | ```
226 |
227 | #### Creating Functions
228 |
229 | One of the most powerful features of the ChakraCoreCppBridge library is an ability to easily bind a C++ function to a JavaScript function object. The JavaScript function object is created with a call to a following static method:
230 |
231 | ```C++
232 | template
233 | value value::function(Callable callback);
234 | ```
235 |
236 | Note that the caller **must** pass the number of callback function arguments as a first template parameter. The C++ language syntax does not allow the library to discover this information and it must be specified manually.
237 |
238 | The `callback` parameter can be any C++ callable object, like a plain function pointer, function object, lambda, a result of a call to `std::bind` and so on. It is allowed to return value of any type implicitly convertible to `value` (see above) or be declared as `void`.
239 |
240 | Callable object may have any number of arguments of any compatible type. `value` type is also allowed and is useful to consume JavaScript objects or optional arguments. It is recommended to take string arguments by reference (`const std::wstring &`) to avoid extra copies.
241 |
242 | If the callback returns void, `undefined` will be returned to JavaScript code.
243 |
244 | ChakraCore explicitly forbids exceptions flowing through the API boundary. ChakraCoreCppBridge library tries to catch any unhandled exception and convert it into JavaScript exception. That means that the calling JavaScript code may use the language construct to catch exceptions thrown by C++ code.
245 |
246 | The library defines a `callback_exception` class. If the callback function should throw, it is recommended to throw an instance of this class or at least an instance of a class derived from `std::exception`.
247 |
248 | `callback_exception::callback_exception` takes a single `std::wstring` value with a description of an error which is then propagated to caller in JavaScript `Error` object. If an instance of `std::exception` (or derived class) is thrown, library calls the `what` method and uses the returned string to construct JavaScript `Error` object. Otherwise, a generic "Unhandled Exception" message is used.
249 |
250 | The library allows the JavaScript to call the passed C++ callback with unmatched number of arguments. If more arguments are passed, extra arguments are lost. If less arguments are passed, the remaining are assumed to be empty `value` values. This means that if the callback is about to have optional parameters, they all have to be of type `value`, otherwise the exception will be thrown during implicit conversion and will propagate to a caller. Although, this might be a required behavior.
251 |
252 | Sample code:
253 |
254 | ```C++
255 | auto f = jsc::value::function<2>([](const std::wstring &text, int order)
256 | {
257 | return text + std::to_wstring(order);
258 | });
259 | ```
260 |
261 | #### Calling JavaScript Functions
262 |
263 | In addition to the ability to create JavaScript function objects with C++ callbacks, the library simplifies the calling of JavaScript functions. An overloaded `operator ()` is used for this:
264 |
265 | ```C++
266 | template
267 | value value::operator()(Args &&...args) const;
268 | value value::operator()(const value *begin, const value *end) const;
269 | ```
270 |
271 | The first overload allows you to directly pass function arguments. As usual, you may pass arguments of any type allowed in `value` constructor.
272 |
273 | **Important!** ChakraCore exposes the first parameter as `this` to the callee. If you are calling a global function, remember passing the `nullptr` as a first argument. If you are calling a method of a "class", you must manually pass a reference to an object as a first parameter.
274 |
275 | The second overload is provided for convenience.
276 |
277 | Alternative syntax for calling functions which are properties of JavaScript objects (aka methods):
278 |
279 | ```C++
280 | template
281 | value value::call(JsPropertyIdRef methodid, Args &&...args) const;
282 |
283 | template
284 | value value::call(const wchar_t *method_name, Args &&...args) const;
285 | ```
286 |
287 | Sample usage:
288 |
289 | ```C++
290 | jsc::value obj = ...;
291 |
292 | // The following two lines are equivalent:
293 | obj[L"methodName"](obj, arg1, arg2, ... argN);
294 | obj.call(L"methodName", arg1, arg2, ... argN);
295 | ```
296 |
297 | #### Accessing Object Properties
298 |
299 | Overloaded `operator []` is used to access the JavaScript object properties. It returns a proxy object (not the `value` directly), allowing you not only to query property values, but also to assign them:
300 |
301 | ```C++
302 | auto prop = obj[L"propName"];
303 | int prop_value = static_cast(prop);
304 | obj[L"propName"] = prop_value;
305 | ```
306 |
307 | The proxy object is designed to seamlessly inter-operate with `value` and provides the same conversion methods and operators like `operator []` (nested properties) and `operator ()` (function calls).
308 |
309 | ```C++
310 | auto value::operator [](JsPropertyIdRef propid) const;
311 | auto value::operator [](const wchar_t *propname) const;
312 | auto value::operator [](value index) const;
313 | ```
314 |
315 | As you see, you can pass either the value name or property identifier.
316 |
317 | Third overload is for working with indexed properties. Working with indexed properties is also possible with following methods:
318 |
319 | ```C++
320 | void value::set_indexed(value ordinal, const value &value);
321 | value value::get_indexed(value ordinal) const;
322 | ```
323 |
324 | A wrapper method around `JsDefineProperty` is also provided in two overloads:
325 |
326 | ```C++
327 | bool value::define_property(JsPropertyIdRef id, const value &descriptor) const;
328 | bool value::define_property(const wchar_t *propname, const value &descriptor) const;
329 | ```
330 |
331 | Use the `value::prototype` method to get object's prototype.
332 |
333 | #### Creating Objects
334 |
335 | The second key feature of ChakraCoreCppBridge library is an ability to easily define JavaScript objects backed by C++. Let us first see an example:
336 |
337 | ```C++
338 | int c = 42;
339 | auto obj = jsc::value::object()
340 | .field(L"a", 10) // constant property value
341 | .property(L"b", [] { return L"Read-only property"; })
342 | .property(L"c", [&] { return c; }, [&](int new_c) { c = new_c; })
343 | .method<1>(L"print", [](const std::wstring &message)
344 | {
345 | std::wcout << message << L"\r\n";
346 | });
347 | ```
348 |
349 | This example creates an object (by calling static `value::object()` function) and then defines its "structure". First it defines a property `a` with a constant value `10`. Then it adds two properties for which a getter lambda and setter lambda are specified. And finally, it declares a method `print` taking a single string value and returning nothing.
350 |
351 | Here's the definition of used methods:
352 |
353 | ```C++
354 | // Constant property
355 | value value::field(const wchar_t *name, value value) const;
356 |
357 | // Method. Remember to pass a number of method arguments
358 | template
359 | value value::method(const wchar_t *name, Callable &&handler) const;
360 |
361 | // Read-only property with getter
362 | template
363 | value value::property(const wchar_t *name, Getter &&getter) const;
364 |
365 | // Read-write property with getter and setter
366 | template
367 | value value::property(const wchar_t *name, Getter &&getter, Setter &&setter) const;
368 | ```
369 |
370 | There is also an overload of `value::object` method taking a pointer to `IUnknown` interface. It makes sure the COM object is not deleted until the ChakraCore garbage collector deletes the JavaScript object.
371 |
372 | ##### Creating Dual Interfaces for C++ and JavaScript
373 |
374 | Combined with a few macros in `chakra_macros.h` (requires boost.preprocessor library), it allows to easily expose C++ interfaces to JavaScript:
375 |
376 | ```C++
377 | #include
378 |
379 | struct ISomeObject
380 | {
381 | // Define read-only property a (type int)
382 | JSC_DECLARE_PROP_GET(int, a);
383 |
384 | // Define read-write property b (type bool)
385 | JSC_DECLARE_PROP(bool, b);
386 |
387 | virtual void print(const std::wstring &) = 0;
388 | };
389 |
390 | class SomeObject : public ISomeObject
391 | {
392 | bool b{false};
393 | public:
394 | // This method creates a JavaScript object
395 | // representing this C++ object
396 | jsc::value to_javascript_object() const
397 | {
398 | return jsc::value::object()
399 | JSC_PROP_GET(a)
400 | JSC_PROP(b)
401 | JSC_METHOD(1, print)
402 | ;
403 | }
404 |
405 | // Property a getter
406 | virtual int get_a() override
407 | {
408 | return 42;
409 | }
410 |
411 | // Property b getter
412 | virtual bool get_b() override
413 | {
414 | return b;
415 | }
416 |
417 | // Property b setter
418 | virtual void set_b(bool v) override
419 | {
420 | b = v;
421 | }
422 |
423 | // Method print
424 | virtual void print(const std::wstring &) override
425 | {
426 | // ...
427 | }
428 | };
429 | ```
430 |
431 | This code shows how easy it is to create an interface consumable both by C++ and JavaScript. `ISomeObject` may also derive from `IUnknown`, in which case a call to `jsc::value::object()` is replaced with `jsc::value::object(this)`.
432 |
433 | ### `referenced_value` class
434 |
435 | If you need to store `value` objects outside of the current scope, use the `referenced_value` class:
436 |
437 | **Bad code:**
438 |
439 | ```C++
440 | class Some
441 | {
442 | jsc::runtime runtime;
443 | jsc::context context;
444 | jsc::value obj;
445 |
446 | public:
447 | Some()
448 | {
449 | // initialize runtime, context and execute script
450 | // ...
451 | obj = jsc::value::object(); // BAD!!!
452 | }
453 | };
454 | ```
455 |
456 | **Correct code:**
457 |
458 | ```C++
459 | class Some
460 | {
461 | jsc::runtime runtime;
462 | jsc::context context;
463 | jsc::referenced_value obj;
464 |
465 | public:
466 | Some()
467 | {
468 | // initialize runtime, context and execute script
469 | // ...
470 | obj = jsc::value::object(); // Correct
471 | }
472 | };
473 | ```
474 |
475 | ### Exception Handling
476 |
477 | For convenience, library tries to work with instances of `value` class directly, that is, take them as arguments and return them as resulting values. If ChakraCore reports an error during its execution, the error information is packaged into the instance of `exception` class and thrown. Use the `exception::code` method to get the `JsErrorCode` of a failed operation or call the `exception::to_js_exception` method to create a JavaScript `Error` object with the description.
478 |
479 | The following helper functions are defined:
480 |
481 | ```C++
482 | // True if failing error code
483 | bool failed(JsErrorCode error) noexcept;
484 |
485 | // True if successful error code
486 | bool succeeded(JsErrorCode error) noexcept;
487 |
488 | // Throw an exception if failed(error) is true
489 | void check(JsErrorCode error);
490 | ```
491 |
492 | These functions are also brought into global namespace, unless the `CBRIDGE_NO_GLOBAL_NAMESPACE` preprocessor constant is defined.
493 |
494 | #### Getting Exception Information
495 |
496 | ```C++
497 | std::tuple print_exception(JsErrorCode code);
498 | ```
499 |
500 | Use this function to convert the exception information to a text description with optional line and position of an error. See the source code for more options.
501 |
502 | ### Running Scripts
503 |
504 | The library provides wrappers for a number of ChakraCore functions that are used to execute scripts. They are exactly the same as their ChakraCore counterparts except that they return `value` objects directly and throw if an error occurs:
505 |
506 | ```C++
507 | value RunScript(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl);
508 | value ParseScript(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl);
509 | value ParseScriptWithAttributes(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl, JsParseScriptAttributes parseAttributes);
510 | value ExperimentalApiRunModule(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl);
511 | ```
512 |
--------------------------------------------------------------------------------
/bridge.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | $(SolutionDir)\include;$(SolutionDir);$(VC_IncludePath);$(WindowsSDK_IncludePath);
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/example.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------------------------------
2 | // Copyright (C) 2016 HHD Software Ltd.
3 | // Written by Alexander Bessonov
4 | //
5 | // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
6 | //-------------------------------------------------------------------------------------------------------
7 |
8 | #include
9 | #include
10 |
11 | #include
12 | #pragma comment(lib,"ChakraCore")
13 |
14 | void main()
15 | {
16 | using namespace std::string_literals;
17 |
18 | jsc::runtime runtime;
19 | jsc::context ctx;
20 | try
21 | {
22 | // Create a runtime
23 | check(runtime.create(JsRuntimeAttributeNone));
24 |
25 | // Create a context
26 | check(ctx.create(runtime));
27 |
28 | // Make this a current context for this scope
29 | jsc::scoped_context sc(ctx);
30 |
31 | // JavaScript code
32 | auto script_body = LR"==(
33 | function sum(arg1, arg2) {
34 | return arg1 + arg2;
35 | }
36 |
37 | function testExternalFunction() {
38 | external_function("string value", true, { a: 20, b: [ "a1", null, undefined ] });
39 | }
40 |
41 | function testExternalObject(obj) {
42 | obj.print("a: " + obj.a);
43 | obj.print("b: " + obj.b);
44 | obj.print("c: " + obj.c);
45 |
46 | // re-assign property (will cause property put accessor to be called)
47 | obj.c = 2;
48 | obj.print("c: " + obj.c);
49 | }
50 | )=="s;
51 |
52 | // Execute script
53 | auto result = jsc::RunScript(script_body.c_str(), 0, L"");
54 |
55 | // 1. Run JavaScript function sum to add two integer values
56 | std::wcout << jsc::value::global()[L"sum"](nullptr, 2, 3).as_int() << L"\r\n"s; // prints 5
57 |
58 | // 2. Add an external function to a script
59 | jsc::value::global()[L"external_function"] =
60 | jsc::value::function<3>([](const std::wstring &sval, bool bval, jsc::value object)
61 | {
62 | std::wcout <<
63 | L"String argument: "s << sval <<
64 | L"\r\nBoolean argument: "s << bval <<
65 | L"\r\nInteger argument in object: "s << static_cast(object[L"a"]) <<
66 | L"\r\nLength of JavaScript array: "s << object[L"b"][L"length"].as() << L"\r\n"s;
67 |
68 | // Return value to JavaScript
69 | return true;
70 | });
71 |
72 | // 3. Run JavaScript function. Alternative call syntax
73 | jsc::value::global().call(L"testExternalFunction"); // will make a lambda above called
74 |
75 | // 4. Create a JavaScript object
76 | int c = 42;
77 | auto obj = jsc::value::object()
78 | .field(L"a", 10) // constant property value
79 | .property(L"b", [] { return L"Read-only property"; })
80 | .property(L"c", [&] { return c; }, [&](int new_c) { c = new_c; })
81 | .method<1>(L"print", [](const std::wstring &message)
82 | {
83 | std::wcout << message << L"\r\n";
84 | });
85 |
86 | // 5. Run JavaScript function
87 | jsc::value::global().call(L"testExternalObject", obj);
88 | }
89 | catch (const jsc::exception &e)
90 | {
91 | std::wcout << L"Exception code: "s << e.code() << L"\r\n"s;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/example/example.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {CD8E33A0-F0C8-4ACD-9758-7A9A0CF0FB50}
23 | Win32Proj
24 | example
25 | 8.1
26 |
27 |
28 |
29 | Application
30 | true
31 | v140
32 | Unicode
33 |
34 |
35 | Application
36 | false
37 | v140
38 | true
39 | Unicode
40 |
41 |
42 | Application
43 | true
44 | v140
45 | Unicode
46 |
47 |
48 | Application
49 | false
50 | v140
51 | true
52 | Unicode
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | true
82 |
83 |
84 | true
85 |
86 |
87 | false
88 |
89 |
90 | false
91 |
92 |
93 |
94 |
95 |
96 | Level3
97 | Disabled
98 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
99 |
100 |
101 | Console
102 | true
103 |
104 |
105 |
106 |
107 |
108 |
109 | Level3
110 | Disabled
111 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
112 |
113 |
114 | Console
115 | true
116 |
117 |
118 |
119 |
120 | Level3
121 |
122 |
123 | MaxSpeed
124 | true
125 | true
126 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
127 |
128 |
129 | Console
130 | true
131 | true
132 | true
133 |
134 |
135 |
136 |
137 | Level3
138 |
139 |
140 | MaxSpeed
141 | true
142 | true
143 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
144 |
145 |
146 | Console
147 | true
148 | true
149 | true
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/example/example.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
--------------------------------------------------------------------------------
/include/chakra_bridge/chakra_bridge.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------------------------------
2 | // Copyright (C) 2016 HHD Software Ltd.
3 | // Written by Alexander Bessonov
4 | //
5 | // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
6 | //-------------------------------------------------------------------------------------------------------
7 |
8 | #pragma once
9 |
10 | // STL
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | // ChakraCore
25 | #include
26 |
27 | #pragma push_macro("max")
28 | #pragma push_macro("new")
29 | #undef max
30 | #undef new
31 |
32 | namespace jsc
33 | {
34 | namespace details
35 | {
36 | using position_conversion_functor_t = std::function(int line, int pos)>;
37 | inline position_conversion_functor_t identity()
38 | {
39 | return [](int line, int pos) { return std::make_pair(line, pos); };
40 | }
41 |
42 | enum class remapped_error
43 | {
44 | InvalidArgument,
45 | NullArgument,
46 | NotAnObject,
47 | OutOfMemory,
48 | ScriptError,
49 | SyntaxError,
50 | FatalError,
51 | Exception,
52 | Unexpected,
53 | };
54 |
55 | inline remapped_error map_error(JsErrorCode code) noexcept
56 | {
57 | switch (code)
58 | {
59 | case JsErrorCode::JsErrorInvalidArgument:
60 | return remapped_error::InvalidArgument;
61 | case JsErrorCode::JsErrorNullArgument:
62 | return remapped_error::NullArgument;
63 | case JsErrorCode::JsErrorArgumentNotObject:
64 | return remapped_error::NotAnObject;
65 | case JsErrorCode::JsErrorOutOfMemory:
66 | return remapped_error::OutOfMemory;
67 | case JsErrorCode::JsErrorScriptException:
68 | return remapped_error::ScriptError;
69 | case JsErrorCode::JsErrorScriptCompile:
70 | return remapped_error::SyntaxError;
71 | case JsErrorCode::JsErrorFatal:
72 | return remapped_error::FatalError;
73 | case JsErrorCode::JsErrorInExceptionState:
74 | return remapped_error::Exception;
75 | default:
76 | return remapped_error::Unexpected;
77 | }
78 | }
79 |
80 | inline bool failed(JsErrorCode error) noexcept
81 | {
82 | return error != JsNoError;
83 | }
84 |
85 | inline bool succeeded(JsErrorCode error) noexcept
86 | {
87 | return error == JsNoError;
88 | }
89 |
90 | class value;
91 |
92 | class exception
93 | {
94 | JsErrorCode error;
95 |
96 | public:
97 | exception(JsErrorCode error) noexcept :
98 | error{ error }
99 | {}
100 |
101 | JsErrorCode code() const noexcept
102 | {
103 | return error;
104 | }
105 |
106 | value to_js_exception() const;
107 | value to_js_exception(const position_conversion_functor_t &posmap) const;
108 | };
109 |
110 | class callback_exception : public std::runtime_error
111 | {
112 | std::wstring message_;
113 |
114 | public:
115 | template
116 | callback_exception(String &&message) :
117 | message_{ std::forward(message) },
118 | std::runtime_error{ "" }
119 | {}
120 |
121 | const auto &message() const
122 | {
123 | return message_;
124 | }
125 | };
126 |
127 | inline void check(JsErrorCode error)
128 | {
129 | if (failed(error))
130 | throw exception(error);
131 | }
132 |
133 | class runtime
134 | {
135 | JsRuntimeHandle handle{ JS_INVALID_RUNTIME_HANDLE };
136 |
137 | public:
138 | runtime(const runtime &) = delete;
139 | runtime &operator =(const runtime &) = delete;
140 |
141 | runtime(runtime &&o) noexcept :
142 | handle{ o.handle }
143 | {
144 | o.handle = JS_INVALID_RUNTIME_HANDLE;
145 | }
146 |
147 | runtime &operator =(runtime &&o) noexcept
148 | {
149 | using std::swap;
150 | swap(handle, o.handle);
151 | return *this;
152 | }
153 |
154 | runtime() = default;
155 | ~runtime()
156 | {
157 | if (handle != JS_INVALID_RUNTIME_HANDLE)
158 | {
159 | JsSetCurrentContext(JS_INVALID_REFERENCE);
160 | JsDisposeRuntime(handle);
161 | }
162 | }
163 |
164 | JsErrorCode create(JsRuntimeAttributes attributes, JsThreadServiceCallback threadService = nullptr) noexcept
165 | {
166 | return JsCreateRuntime(attributes, threadService, &handle);
167 | }
168 |
169 | operator JsRuntimeHandle() const noexcept
170 | {
171 | return handle;
172 | }
173 | };
174 |
175 | class context
176 | {
177 | JsContextRef handle{ nullptr };
178 |
179 | public:
180 | JsErrorCode create(JsRuntimeHandle runtime) noexcept
181 | {
182 | return JsCreateContext(runtime, &handle);
183 | }
184 |
185 | operator JsContextRef() const noexcept
186 | {
187 | return handle;
188 | }
189 | };
190 |
191 | class scoped_context
192 | {
193 | public:
194 | scoped_context(JsContextRef context)
195 | {
196 | check(JsSetCurrentContext(context));
197 | }
198 |
199 | ~scoped_context()
200 | {
201 | auto success = succeeded(JsSetCurrentContext(JS_INVALID_REFERENCE));
202 | success;
203 | assert(success && "Error exiting context");
204 | }
205 | };
206 |
207 | // forward declare property access proxies
208 | class prop_ref_propid;
209 | class prop_ref_indexed;
210 |
211 | template
212 | class prop_ref;
213 |
214 | // type dispatch helpers
215 | template
216 | struct is_string_v
217 | {
218 | static const bool value = std::is_same::value;
219 | };
220 |
221 | template
222 | struct is_bool_v
223 | {
224 | static const bool value = std::is_same::value;
225 | };
226 |
227 | template
228 | using is_enum_v = std::is_enum;
229 |
230 | template
231 | struct is_small_int_v
232 | {
233 | static const bool value = !is_enum_v::value && !is_bool_v::value && std::is_integral::value && sizeof(T) <= sizeof(int) &&
234 | !std::is_same::value && !std::is_same::value;
235 | };
236 |
237 | template
238 | struct is_big_number_v
239 | {
240 | static const bool value = !is_enum_v::value &&
241 | (
242 | std::is_floating_point::value ||
243 | (std::is_integral::value && !is_bool_v::value && !is_small_int_v::value && sizeof(T)>sizeof(int))
244 | );
245 | };
246 |
247 | template
248 | struct is_supported_v
249 | {
250 | static const bool value =/*is_string::value && */is_enum_v::value || is_bool_v::value || is_small_int_v::value || is_big_number_v::value;
251 | };
252 |
253 | class referenced_value;
254 |
255 | class value
256 | {
257 | JsValueRef val{ JS_INVALID_REFERENCE };
258 |
259 | // helper functions to call C++ functions with passed arguments
260 | template
261 | static auto apply_helper(const F &f, const std::array ¶ms, std::index_sequence)
262 | {
263 | (params);
264 | return std::invoke(f, params[I]...);
265 | }
266 |
267 | template
268 | static auto apply(const F &f, const std::array ¶ms)
269 | {
270 | return apply_helper(f, params, std::make_index_sequence{});
271 | }
272 |
273 | template
274 | static value execute_functor_helper(const Callable &f, const std::array &values, std::true_type)
275 | {
276 | // void
277 | apply(f, values);
278 | return undefined();
279 | }
280 |
281 | template
282 | static value execute_functor_helper(const Callable &f, const std::array &values, std::false_type)
283 | {
284 | return value{ apply(f, values) };
285 | }
286 |
287 | template
288 | static value execute_functor(const Callable &f, const std::array &values)
289 | {
290 | return execute_functor_helper(f, values, std::is_same{});
291 | }
292 |
293 | // helpers to construct value from different types
294 | static JsValueRef from(const std::wstring &text)
295 | {
296 | JsValueRef result;
297 | check(JsPointerToString(text.c_str(), text.size(), &result));
298 | return result;
299 | }
300 |
301 | static JsValueRef from(const wchar_t *text)
302 | {
303 | JsValueRef result;
304 | check(JsPointerToString(text, wcslen(text), &result));
305 | return result;
306 | }
307 |
308 | static JsValueRef from(double v)
309 | {
310 | JsValueRef result;
311 | check(JsDoubleToNumber(v, &result));
312 | return result;
313 | }
314 |
315 | static JsValueRef from(int v)
316 | {
317 | JsValueRef result;
318 | check(JsIntToNumber(v, &result));
319 | return result;
320 | }
321 |
322 | static JsValueRef from(bool v)
323 | {
324 | JsValueRef result;
325 | check(JsBoolToBoolean(v, &result));
326 | return result;
327 | }
328 |
329 | static JsValueRef from(nullptr_t)
330 | {
331 | JsValueRef result;
332 | check(JsGetNullValue(&result));
333 | return result;
334 | }
335 |
336 | // enum
337 | template
338 | static std::enable_if_t::value, JsValueRef> from(T val)
339 | {
340 | return from(static_cast>(val));
341 | }
342 |
343 | template
344 | static std::enable_if_t::value, JsValueRef> from(T val)
345 | {
346 | return from(static_cast(val));
347 | }
348 |
349 | template
350 | static std::enable_if_t::value, JsValueRef> from(T val)
351 | {
352 | return from(val, std::integral_constant::value > {});
353 | }
354 |
355 | template
356 | static JsValueRef from(T val, std::true_type)
357 | {
358 | return from(static_cast(val));
359 | }
360 |
361 | template
362 | static JsValueRef from(T val, std::false_type)
363 | {
364 | if (val > static_cast(std::numeric_limits::max()))
365 | return from(static_cast(val));
366 | else
367 | return from(static_cast(val));
368 | }
369 |
370 | // special integer constructors
371 | // construct through integer
372 | template
373 | value(T val, std::true_type) noexcept :
374 | val{ from(static_cast(val)) }
375 | {}
376 |
377 | template
378 | value(T val, std::false_type) noexcept :
379 | val{ from(static_cast(val)) }
380 | {}
381 |
382 | // value retrieval
383 | // string
384 | template
385 | T as(std::true_type string, std::false_type, std::false_type, std::false_type, std::false_type) const
386 | {
387 | string;
388 | return as_string();
389 | }
390 |
391 | // bool
392 | template
393 | T as(std::false_type, std::true_type boolean, std::false_type, std::false_type, std::false_type) const
394 | {
395 | boolean;
396 | return as_bool();
397 | }
398 |
399 | // enum
400 | template
401 | T as(std::false_type, std::false_type, std::true_type enumeration, std::false_type, std::false_type) const
402 | {
403 | enumeration;
404 | return static_cast(as>());
405 | }
406 |
407 | // big number
408 | template
409 | T as(std::false_type, std::false_type, std::false_type, std::true_type bignum, std::false_type) const
410 | {
411 | bignum;
412 | return static_cast(as_double());
413 | }
414 |
415 | // small number
416 | template
417 | T as(std::false_type, std::false_type, std::false_type, std::false_type, std::true_type smallnum) const
418 | {
419 | smallnum;
420 | return as_small_int(std::integral_constant::value > {});
421 | }
422 |
423 | template
424 | T as_small_int(std::true_type) const
425 | {
426 | return static_cast(as_int());
427 | }
428 |
429 | template
430 | T as_small_int(std::false_type) const
431 | {
432 | using namespace std::string_literals;
433 | auto val = as_int();
434 | if (val < 0)
435 | {
436 | JsValueRef error;
437 | check(JsCreateRangeError(value{ L"Value is out of range" }, &error));
438 | JsSetException(error);
439 | throw exception(JsErrorScriptException);
440 | }
441 | else
442 | return static_cast(val);
443 | }
444 | public:
445 | // static members that construct different types of ChakraCore values
446 |
447 | // construct JavaScript array object and fill it with passed arguments
448 | static value array(std::initializer_list arguments)
449 | {
450 | JsValueRef result_;
451 | check(JsCreateArray(static_cast(arguments.size()), &result_));
452 | auto result = value{ result_ };
453 | for (size_t i = 0; i < arguments.size(); ++i)
454 | result.set_indexed((int)i, arguments.begin()[i]);
455 |
456 | return result;
457 | }
458 |
459 | // construct JavaScript array object and fill it with passed arguments
460 | template
461 | static value array(Args &&...args)
462 | {
463 | return array({ std::forward(args)... });
464 | }
465 |
466 | // construct JavaScript array object and fill it with passed arguments
467 | // requires boost.range
468 | template
469 | static value array_from_range(const Range &range)
470 | {
471 | const auto size = boost::size(range);
472 | JsValueRef result_;
473 | check(JsCreateArray((unsigned)size, &result_));
474 | auto result = value{ result_ };
475 | int i = 0;
476 | for (const auto &e : range)
477 | result.set_indexed(i++, e);
478 | return result;
479 | }
480 |
481 | // construct JavaScript array object of a given size
482 | static value uninitialized_array(unsigned int size = 0)
483 | {
484 | JsValueRef result;
485 | check(JsCreateArray(size, &result));
486 | return value{ result };
487 | }
488 |
489 | // construct JavaScript ArrayBuffer object referencing external memory (check object lifetime!)
490 | static value array_buffer(void *pdata, size_t size)
491 | {
492 | JsValueRef result;
493 | check(JsCreateExternalArrayBuffer(pdata, (unsigned int)size, nullptr, nullptr, &result));
494 | return value{ result };
495 | }
496 |
497 | // construct JavaScript ArrayBuffer object referencing copy of external memory
498 | static value array_buffer_copy(const void *pdata, size_t size)
499 | {
500 | auto copy = std::make_unique(size);
501 | memcpy(copy.get(), pdata, size);
502 | JsValueRef result;
503 | check(JsCreateExternalArrayBuffer(copy.get(), (unsigned int)size, [](void *data)
504 | {
505 | std::unique_ptr d(static_cast(data));
506 | }, copy.get(), &result));
507 | copy.release(); // will be deleted later in callback
508 | return value{ result };
509 | }
510 |
511 | // construct JavaScript TypedArray object
512 | static value typed_array(JsTypedArrayType arrayType, const value &baseArray, unsigned int byteOffset = 0, unsigned int elementLength = 0)
513 | {
514 | JsValueRef result;
515 | check(JsCreateTypedArray(arrayType, baseArray, byteOffset, elementLength, &result));
516 | return value{ result };
517 | }
518 |
519 | // return null JavaScript value
520 | static value null()
521 | {
522 | return value{ nullptr };
523 | }
524 |
525 | // return undefined JavaScript value
526 | static value undefined()
527 | {
528 | JsValueRef result;
529 | check(JsGetUndefinedValue(&result));
530 | return value{ result };
531 | }
532 |
533 | // return true boolean value
534 | static value true_()
535 | {
536 | JsValueRef result;
537 | check(JsGetTrueValue(&result));
538 | return value{ result };
539 | }
540 |
541 | // return false boolean value
542 | static value false_()
543 | {
544 | JsValueRef result;
545 | check(JsGetFalseValue(&result));
546 | return value{ result };
547 | }
548 |
549 | // construct JavaScript function object. When invoked, a passed function is called with parameters converted
550 | template
551 | static value function(Callable function)
552 | {
553 | using namespace std::string_literals;
554 | auto pfcopy = std::make_unique(std::move(function));
555 | JsValueRef result;
556 | check(JsCreateFunction([](JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *pfcopy)->JsValueRef
557 | {
558 | callee;
559 | isConstructCall;
560 | auto runtime_args = argumentCount - 1;
561 | auto begin = arguments + 1;
562 |
563 | auto *pf = static_cast(pfcopy);
564 | try
565 | {
566 | if (runtime_args >= ArgCount)
567 | return execute_functor(*pf, *reinterpret_cast *>(begin));
568 | else
569 | {
570 | std::array params;
571 | auto cbegin = reinterpret_cast(begin);
572 | std::copy(cbegin, cbegin + runtime_args, params.begin());
573 | return execute_functor(*pf, params);
574 | }
575 | }
576 | catch (const exception &e)
577 | {
578 | return e.to_js_exception();
579 | }
580 | catch (const callback_exception &e)
581 | {
582 | JsValueRef result;
583 | check(JsCreateError(value{ e.message() }, &result));
584 | JsSetException(result);
585 | return result;
586 | }
587 | catch (const std::exception &e)
588 | {
589 | JsValueRef result;
590 | check(JsCreateError(value{ std::wstring_convert>{}.from_bytes(e.what()) }, &result));
591 | JsSetException(result);
592 | return result;
593 | }
594 | catch (...)
595 | {
596 | JsValueRef result;
597 | check(JsCreateError(value{ L"Unknown error"s }, &result));
598 | JsSetException(result);
599 | return result;
600 | }
601 | }, pfcopy.get(), &result));
602 |
603 | check(JsSetObjectBeforeCollectCallback(result, pfcopy.get(), [](JsRef ref, void *callbackState)
604 | {
605 | ref;
606 | delete static_cast(callbackState);
607 | }));
608 | pfcopy.release(); // will be deleted in callback above
609 | return value{ result };
610 | }
611 |
612 | // return and clear the current runtime exception
613 | static value current_exception()
614 | {
615 | JsValueRef result;
616 | check(JsGetAndClearException(&result));
617 | return value{ result };
618 | }
619 |
620 | // return a global object
621 | static value global()
622 | {
623 | JsValueRef global;
624 | check(JsGetGlobalObject(&global));
625 | return value{ global };
626 | }
627 |
628 | // construct new JavaScript object
629 | static value object()
630 | {
631 | JsValueRef result;
632 | check(JsCreateObject(&result));
633 | return value{ result };
634 | }
635 |
636 | // construct new JavaScript object based on COM object
637 | static value object(IUnknown *pObj)
638 | {
639 | JsValueRef result;
640 | check(JsCreateExternalObject(pObj, [](void *p)
641 | {
642 | static_cast(p)->Release();
643 | }, &result));
644 | pObj->AddRef();
645 | return value{ result };
646 | }
647 |
648 | static value ref(JsValueRef v) noexcept
649 | {
650 | return value{ v };
651 | }
652 |
653 | // default constructor
654 | value() = default;
655 | // destructor
656 | // explicit value ref constructor
657 | explicit value(JsValueRef v) noexcept :
658 | val{ v }
659 | {
660 | }
661 |
662 | // Conversion constructors
663 | // bool
664 | value(bool v) :
665 | val{ from(v) }
666 | {
667 | }
668 |
669 | // null
670 | value(nullptr_t) :
671 | val{ from(nullptr) }
672 | {
673 | }
674 |
675 | // string
676 | value(const std::wstring &text) :
677 | val{ from(text) }
678 | {
679 | }
680 |
681 | value(const wchar_t *text) :
682 | val{ from(text) }
683 | {
684 | }
685 |
686 | // double
687 | value(double v) :
688 | val{ from(v) }
689 | {
690 | }
691 |
692 | // integer
693 | value(int v) :
694 | val{ from(v) }
695 | {
696 | }
697 |
698 | value(const referenced_value &);
699 |
700 | template
701 | value(T v) :
702 | val{ from(v) }
703 | {}
704 |
705 | template
706 | value(const prop_ref &prop);
707 |
708 | // value type retrieval
709 | JsValueType value_type() const
710 | {
711 | JsValueType result;
712 | check(JsGetValueType(val, &result));
713 | return result;
714 | }
715 |
716 | // conversion functions
717 | value to_object() const
718 | {
719 | JsValueRef result;
720 | check(JsConvertValueToObject(val, &result));
721 | return value{ result };
722 | }
723 |
724 | value to_number() const
725 | {
726 | JsValueRef result;
727 | check(JsConvertValueToNumber(val, &result));
728 | return value{ result };
729 | }
730 |
731 | std::wstring to_string() const
732 | {
733 | JsValueRef result;
734 | check(JsConvertValueToString(val, &result));
735 | return static_cast(value{ result });
736 | }
737 |
738 | // prototype
739 | value prototype() const
740 | {
741 | JsValueRef result;
742 | check(JsGetPrototype(val, &result));
743 | return value{ result };
744 | }
745 |
746 | // index operators
747 | prop_ref operator [](value index) const;
748 | prop_ref operator[](JsPropertyIdRef propid) const;
749 | prop_ref operator [](const wchar_t *propname) const;
750 |
751 | void set_indexed(value ordinal, const value &value) const
752 | {
753 | check(JsSetIndexedProperty(this->val, ordinal, value));
754 | }
755 |
756 | value get_indexed(value ordinal) const
757 | {
758 | JsValueRef result;
759 | check(JsGetIndexedProperty(this->val, ordinal, &result));
760 | return value{ result };
761 | }
762 |
763 | // properties
764 | void set(const wchar_t *propname, const value &value) const
765 | {
766 | JsPropertyIdRef propid;
767 | check(JsGetPropertyIdFromName(propname, &propid));
768 | set(propid, value);
769 | }
770 |
771 | void set(JsPropertyIdRef propid, const value &value) const
772 | {
773 | check(JsSetProperty(this->val, propid, value, true));
774 | }
775 |
776 | bool define_property(JsPropertyIdRef id, const value &descriptor) const
777 | {
778 | bool result;
779 | check(JsDefineProperty(val, id, descriptor, &result));
780 | return result;
781 | }
782 |
783 | bool define_property(const wchar_t *propname, const value &descriptor) const
784 | {
785 | JsPropertyIdRef propid;
786 | check(JsGetPropertyIdFromName(propname, &propid));
787 | return define_property(propid, descriptor);
788 | }
789 |
790 | template
791 | value method(const wchar_t *name, Callable &&handler) const
792 | {
793 | (*this)[name] = function(std::forward(handler));
794 | return *this; // copies are cheap
795 | }
796 |
797 | value field(const wchar_t *name, value value_) const;
798 |
799 | template
800 | value property(const wchar_t *name, Getter &&getter) const
801 | {
802 | define_property(name, object()
803 | .field(L"configurable", false_())
804 | .field(L"get", function<0>(std::forward(getter)))
805 | .field(L"set", function<1>([name = std::wstring{ name }](value)->value
806 | {
807 | using namespace std::string_literals;
808 | JsValueRef exc;
809 | check(JsCreateError(value{ name + L": property is read-only"s }, &exc));
810 | check(JsSetException(exc));
811 | return value{ exc };
812 | })))
813 | ;
814 | return *this;
815 | }
816 |
817 | template
818 | value property(const wchar_t *name, Getter &&getter, Setter &&setter) const
819 | {
820 | define_property(name, object()
821 | .field(L"configurable", false_())
822 | .field(L"get", function<0>(std::forward(getter)))
823 | .field(L"set", function<1>(std::forward(setter)))
824 | );
825 | return *this;
826 | }
827 |
828 | // function call
829 | value operator()(std::initializer_list arguments) const
830 | {
831 | JsValueRef result;
832 | check(JsCallFunction(val, reinterpret_cast(const_cast((arguments.begin()))), (unsigned short)arguments.size(), &result));
833 | return value{ result };
834 | }
835 |
836 | value operator()(const value *begin, const value *end) const
837 | {
838 | JsValueRef result;
839 | check(JsCallFunction(val, reinterpret_cast(const_cast(begin)), (unsigned short)std::distance(begin, end), &result));
840 | return value{ result };
841 | }
842 |
843 | template
844 | value operator()(Args &&...args) const
845 | {
846 | return operator()({ value{ std::forward(args) }... });
847 | }
848 |
849 | // method call
850 | template
851 | value call(JsPropertyIdRef methodid, Args &&...args) const
852 | {
853 | return (*this)[methodid](*this, std::forward(args)...);
854 | }
855 |
856 | template
857 | value call(const wchar_t *method_name, Args &&...args) const
858 | {
859 | JsPropertyIdRef propid;
860 | check(JsGetPropertyIdFromName(method_name, &propid));
861 | return call(propid, std::forward(args)...);
862 | }
863 |
864 | // value accessors
865 | operator JsValueRef() const noexcept
866 | {
867 | return val;
868 | }
869 |
870 | bool as_bool() const
871 | {
872 | bool result;
873 | check(JsBooleanToBool(val, &result));
874 | return result;
875 | }
876 |
877 | int as_int() const
878 | {
879 | int result;
880 | check(JsNumberToInt(val, &result));
881 | return result;
882 | }
883 |
884 | double as_double() const
885 | {
886 | double result;
887 | check(JsNumberToDouble(val, &result));
888 | return result;
889 | }
890 |
891 | std::wstring as_string()const
892 | {
893 | const wchar_t *ptr;
894 | size_t length;
895 | check(JsStringToPointer(val, &ptr, &length));
896 | return{ ptr, length };
897 | }
898 |
899 | template
900 | T as() const
901 | {
902 | return as(
903 | std::integral_constant::value>{},
904 | std::integral_constant::value>{},
905 | std::integral_constant::value>{},
906 | std::integral_constant::value>{},
907 | std::integral_constant::value>{}
908 | );
909 | }
910 |
911 | operator std::wstring() const
912 | {
913 | return as_string();
914 | }
915 |
916 | template::value>>
917 | operator T() const
918 | {
919 | return as();
920 | }
921 |
922 | operator wchar_t()const = delete;
923 | operator char()const = delete;
924 |
925 | void *data() const
926 | {
927 | void *res;
928 | check(JsGetExternalData(val, &res));
929 | return res;
930 | }
931 |
932 | //
933 | bool is_empty() const noexcept
934 | {
935 | return val == JS_INVALID_REFERENCE;
936 | }
937 |
938 | bool empty() const noexcept
939 | {
940 | return is_empty();
941 | }
942 |
943 | bool is_null() const
944 | {
945 | return value_type() == JsNull;
946 | }
947 |
948 | bool is_undefined() const
949 | {
950 | return value_type() == JsUndefined;
951 | }
952 |
953 | bool is_string() const
954 | {
955 | return value_type() == JsString;
956 | }
957 |
958 | bool is_object() const
959 | {
960 | return value_type() == JsObject;
961 | }
962 |
963 | bool is_array() const
964 | {
965 | return value_type() == JsArray;
966 | }
967 |
968 | bool is_function() const
969 | {
970 | return value_type() == JsFunction;
971 | }
972 |
973 | bool is_typed_array() const
974 | {
975 | return value_type() == JsTypedArray;
976 | }
977 |
978 | bool is_array_buffer() const
979 | {
980 | return value_type() == JsArrayBuffer;
981 | }
982 |
983 | bool is_data_view() const
984 | {
985 | return value_type() == JsDataView;
986 | }
987 |
988 | bool is_number() const
989 | {
990 | return value_type() == JsNumber;
991 | }
992 |
993 | bool is_boolean() const
994 | {
995 | return value_type() == JsBoolean;
996 | }
997 | };
998 |
999 | class referenced_value : public value
1000 | {
1001 | void add_ref() noexcept
1002 | {
1003 | auto v = static_cast(*this);
1004 | if (v != JS_INVALID_REFERENCE)
1005 | JsAddRef(v, nullptr);
1006 | }
1007 |
1008 | void release() noexcept
1009 | {
1010 | auto v = static_cast(*this);
1011 | if (v != JS_INVALID_REFERENCE)
1012 | JsRelease(v, nullptr);
1013 | }
1014 |
1015 | public:
1016 | using value::value;
1017 |
1018 | referenced_value() = default;
1019 | referenced_value(const value &o) noexcept :
1020 | value{ o }
1021 | {
1022 | add_ref();
1023 | }
1024 |
1025 | template
1026 | referenced_value(const T &o) noexcept :
1027 | value{ o }
1028 | {
1029 | add_ref();
1030 | }
1031 |
1032 | template
1033 | referenced_value(const prop_ref &prop) noexcept :
1034 | value{ prop }
1035 | {
1036 | add_ref();
1037 | }
1038 |
1039 | referenced_value(referenced_value &&o) :
1040 | value{ static_cast(o) }
1041 | {
1042 | static_cast(o) = value{ JS_INVALID_REFERENCE };
1043 | }
1044 |
1045 | referenced_value &operator =(referenced_value &&o)
1046 | {
1047 | using std::swap;
1048 | swap(static_cast(*this), static_cast(o));
1049 | return *this;
1050 | }
1051 |
1052 | ~referenced_value()
1053 | {
1054 | release();
1055 | }
1056 |
1057 | referenced_value(const referenced_value &o) noexcept :
1058 | value{ static_cast(o) }
1059 | {
1060 | add_ref();
1061 | }
1062 |
1063 | referenced_value &operator =(const referenced_value &o)
1064 | {
1065 | release();
1066 | static_cast(*this) = static_cast(o);
1067 | add_ref();
1068 | return *this;
1069 | }
1070 | };
1071 |
1072 | class prop_ref_propid
1073 | {
1074 | JsPropertyIdRef propid;
1075 |
1076 | protected:
1077 | prop_ref_propid(JsPropertyIdRef propid) noexcept :
1078 | propid{ propid }
1079 | {}
1080 |
1081 | auto get(value obj) const
1082 | {
1083 | JsValueRef result;
1084 | check(JsGetProperty(obj, propid, &result));
1085 | return value{ result };
1086 | }
1087 |
1088 | void set(value obj, value value)
1089 | {
1090 | obj.set(propid, value);
1091 | }
1092 | };
1093 |
1094 | class prop_ref_indexed
1095 | {
1096 | value index;
1097 |
1098 | protected:
1099 | prop_ref_indexed(value index) noexcept :
1100 | index{ index }
1101 | {}
1102 |
1103 | auto get(value obj) const
1104 | {
1105 | JsValueRef result;
1106 | check(JsGetIndexedProperty(obj, index, &result));
1107 | return value{ result };
1108 | }
1109 |
1110 | void set(value obj, value value)
1111 | {
1112 | obj.set_indexed(index, value);
1113 | }
1114 | };
1115 |
1116 | template
1117 | class prop_ref : public Base
1118 | {
1119 | value obj;
1120 |
1121 | friend class value;
1122 |
1123 | template
1124 | prop_ref(value obj, const Index &index) noexcept :
1125 | obj{ obj },
1126 | Base{ index }
1127 | {}
1128 |
1129 | public:
1130 | value get() const
1131 | {
1132 | return Base::get(obj);
1133 | }
1134 |
1135 | template::value>>
1136 | operator T()const
1137 | {
1138 | return as();
1139 | }
1140 |
1141 | operator std::wstring() const
1142 | {
1143 | return get().as_string();
1144 | }
1145 |
1146 | template
1147 | T as() const
1148 | {
1149 | return get().as();
1150 | }
1151 |
1152 | auto as_string() const
1153 | {
1154 | return as();
1155 | }
1156 |
1157 | template
1158 | auto operator()(Args &&...args) const
1159 | {
1160 | return get()(std::forward(args)...);
1161 | }
1162 |
1163 | prop_ref &operator =(value value)
1164 | {
1165 | Base::set(obj, value);
1166 | return *this;
1167 | }
1168 |
1169 | template
1170 | auto operator[](const Index &index) const
1171 | {
1172 | return get()[index];
1173 | }
1174 |
1175 | auto to_number() const
1176 | {
1177 | return get().to_number();
1178 | }
1179 |
1180 | auto to_object() const
1181 | {
1182 | return get().to_object();
1183 | }
1184 |
1185 | auto to_string() const
1186 | {
1187 | return get().to_string();
1188 | }
1189 |
1190 | auto value_type() const
1191 | {
1192 | return get().value_type();
1193 | }
1194 | };
1195 |
1196 | inline value::value(const referenced_value &o) :
1197 | val{ o.val }
1198 | {}
1199 |
1200 | inline prop_ref value::operator[](JsPropertyIdRef propid) const
1201 | {
1202 | return prop_ref{ *this, propid };
1203 | }
1204 |
1205 | inline prop_ref value::operator [](const wchar_t *propname) const
1206 | {
1207 | JsPropertyIdRef propid;
1208 | check(JsGetPropertyIdFromName(propname, &propid));
1209 | return operator[](propid);
1210 | }
1211 |
1212 | inline prop_ref value::operator[](value index) const
1213 | {
1214 | return prop_ref{ *this, index };
1215 | }
1216 |
1217 | inline value value::field(const wchar_t *name, value value_) const
1218 | {
1219 | (*this)[name] = value_;
1220 | return *this;
1221 | }
1222 |
1223 | template
1224 | inline value::value(const prop_ref &prop) :
1225 | value{ prop.get() }
1226 | {
1227 | }
1228 |
1229 | class exception_details : public value
1230 | {
1231 | public:
1232 | exception_details() :
1233 | value{ value::current_exception() }
1234 | {
1235 | }
1236 |
1237 | std::wstring message() const
1238 | {
1239 | try
1240 | {
1241 | return operator[](L"message").as_string();
1242 | }
1243 | catch (const exception &)
1244 | {
1245 | return{};
1246 | }
1247 | }
1248 |
1249 | std::wstring stack() const
1250 | {
1251 | try
1252 | {
1253 | return operator[](L"stack").as_string();
1254 | }
1255 | catch (const exception &)
1256 | {
1257 | return{};
1258 | }
1259 | }
1260 |
1261 | std::wstring description() const
1262 | {
1263 | try
1264 | {
1265 | return operator [](L"description").as_string();
1266 | }
1267 | catch (const exception &)
1268 | {
1269 | return{};
1270 | }
1271 | }
1272 | };
1273 |
1274 | inline bool has_exception() noexcept
1275 | {
1276 | bool has;
1277 | return JsNoError == JsHasException(&has) && has;
1278 | }
1279 |
1280 | inline std::tuple print_exception(JsErrorCode code, const position_conversion_functor_t &posmap)
1281 | {
1282 | using namespace std::string_literals;
1283 | auto remappedcode = map_error(code);
1284 | std::wstring message;
1285 |
1286 | try
1287 | {
1288 | if (code == JsErrorCode::JsErrorScriptCompile || code == JsErrorCode::JsErrorScriptException)
1289 | {
1290 | exception_details einfo;
1291 | message = einfo.to_string();
1292 | if (code == JsErrorCode::JsErrorScriptCompile)
1293 | {
1294 | auto line = einfo[L"line"].as();
1295 | auto column = einfo[L"column"].as();
1296 |
1297 | std::tie(line, column) = posmap(line, column);
1298 |
1299 | message += L" ("s + std::to_wstring(line) + L":"s + std::to_wstring(column) + L")"s;
1300 | }
1301 | else
1302 | {
1303 | try
1304 | {
1305 | message = einfo[L"stack"].as_string();
1306 | }
1307 | catch (const exception &)
1308 | {
1309 | }
1310 | }
1311 | }
1312 | }
1313 | catch (const exception &)
1314 | {
1315 | }
1316 |
1317 | return{ remappedcode,message };
1318 | }
1319 |
1320 | inline auto print_exception(JsErrorCode code)
1321 | {
1322 | return print_exception(code, identity());
1323 | }
1324 |
1325 | inline auto print_exception(const exception &e, const position_conversion_functor_t &posmap)
1326 | {
1327 | return print_exception(e.code(), posmap);
1328 | }
1329 |
1330 | inline auto print_exception(const exception &e)
1331 | {
1332 | return print_exception(e.code());
1333 | }
1334 |
1335 | inline value exception::to_js_exception() const
1336 | {
1337 | return to_js_exception(identity());
1338 | }
1339 |
1340 | inline value exception::to_js_exception(const position_conversion_functor_t &posmap) const
1341 | {
1342 | using namespace std::string_literals;
1343 | auto einfo = print_exception(code(), posmap);
1344 | try
1345 | {
1346 | JsValueRef exc;
1347 | static const wchar_t *error_messages[] = {
1348 | L"Invalid argument",
1349 | L"Null argument",
1350 | L"Argument not an object",
1351 | L"Out of memory",
1352 | L"Script error",
1353 | L"Syntax error",
1354 | L"Fatal error",
1355 | L"Exception",
1356 | L"Unexpected code"
1357 | };
1358 | check(JsCreateError(value{ error_messages[static_cast(std::get<0>(einfo))] + L": "s + std::get<1>(einfo) }, &exc));
1359 | check(JsSetException(exc));
1360 | return value{ exc };
1361 | }
1362 | catch (const exception &)
1363 | {
1364 | return nullptr;
1365 | }
1366 | }
1367 |
1368 | // Run script helpers
1369 | inline value RunScript(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl)
1370 | {
1371 | JsValueRef result;
1372 | check(JsRunScript(script, sourceContext, sourceUrl, &result));
1373 | return value{ result };
1374 | }
1375 |
1376 | inline value ParseScript(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl)
1377 | {
1378 | JsValueRef result;
1379 | check(JsParseScript(script, sourceContext, sourceUrl, &result));
1380 | return value{ result };
1381 | }
1382 |
1383 | inline value ParseScriptWithAttributes(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl, JsParseScriptAttributes parseAttributes)
1384 | {
1385 | JsValueRef result;
1386 | check(JsParseScriptWithAttributes(script, sourceContext, sourceUrl, parseAttributes, &result));
1387 | return value{ result };
1388 | }
1389 |
1390 | inline value ExperimentalApiRunModule(const wchar_t *script, JsSourceContext sourceContext, const wchar_t *sourceUrl)
1391 | {
1392 | JsValueRef result;
1393 | check(JsExperimentalApiRunModule(script, sourceContext, sourceUrl, &result));
1394 | return value{ result };
1395 | }
1396 | }
1397 |
1398 | // Bring several items into jsc namespace
1399 | using details::value;
1400 | using details::referenced_value;
1401 | using details::exception;
1402 | using details::runtime;
1403 | using details::context;
1404 | using details::exception_details;
1405 | using details::print_exception;
1406 | using details::position_conversion_functor_t;
1407 | using details::remapped_error;
1408 | using details::scoped_context;
1409 | using details::callback_exception;
1410 | using details::RunScript;
1411 | using details::ParseScript;
1412 | using details::ParseScriptWithAttributes;
1413 | using details::ExperimentalApiRunModule;
1414 | }
1415 |
1416 | #if !defined(CBRIDGE_NO_GLOBAL_NAMESPACE)
1417 | // Bring several items into global namespace
1418 | using jsc::details::check;
1419 | using jsc::details::succeeded;
1420 | using jsc::details::failed;
1421 | #endif
1422 |
1423 | #pragma pop_macro("max")
1424 | #pragma pop_macro("new")
1425 |
--------------------------------------------------------------------------------
/include/chakra_bridge/chakra_macros.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------------------------------
2 | // Copyright (C) 2016 HHD Software Ltd.
3 | // Written by Alexander Bessonov
4 | //
5 | // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
6 | //-------------------------------------------------------------------------------------------------------
7 |
8 | #pragma once
9 |
10 | // Boost.Preprocessor
11 | #include
12 |
13 | #include "chakra_bridge.h"
14 |
15 | // Use the following macros when creating Chakra object bound to [this].
16 | // JSC_PROP and JSC_PROP_GET bind property accessors to get_ and set_ functions
17 | #define JSC_PROP(name) \
18 | .property(L#name,[this]{return get_##name();}, [this](const auto &v) { set_##name(v); }) \
19 | // end of macro
20 | #define JSC_PROP_GET(name) \
21 | .property(L#name,[this]{return get_##name();}) \
22 | // end of macro
23 |
24 | // JSC_METHOD binds the method. Only total number of arguments and method name must be specified.
25 | #define JSC_METHOD(ArgCount,name) \
26 | .method(L#name,[this](BOOST_PP_ENUM_PARAMS(ArgCount,const auto &p)) { return name(BOOST_PP_ENUM_PARAMS(ArgCount,p)); }) \
27 | // end of macro
28 |
29 | // Use the following macros when declaring Chakra-compatible interface:
30 | // JSC_DECLARE_PROP and JSC_DECLARE_PROP_GET declare property accessor functions
31 | #define JSC_DECLARE_PROP(type, name) \
32 | virtual type get_##name()=0; \
33 | virtual void set_##name(type v)=0 \
34 | // end of macro
35 |
36 | #define JSC_DECLARE_PROP_GET(type,name) \
37 | virtual type get_##name()=0 \
38 | // end of macro
39 |
40 | // No separate macro exists for methods, use plain C++ virtual abstract function for them
41 |
--------------------------------------------------------------------------------