├── .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 | --------------------------------------------------------------------------------