1# es2panda_lib generation 2 3> What is it? 4 5Automatic export of ArkTS public classes and methods into C API. 6 7> I fixed CODECHECK and got a new failure "UNSUPPORTED TYPE". 8 91) Check if your new method should be private and automatically excluded from C API, error will disappear. 10 112) Otherwise support method arguments types in `cppToCTypes.yaml`. 12 13> What is `cppToCTypes.yaml` 14 15It describes how C++ types should be transformed into C types in plugin API. 16Tranformation "one to many" is supported to express map of e.g. `std::vector<int>` to `int*, size_t pair` pair. 17 18## How to add new type: 19 201) Copy the template below to the end of [cppToCTypes.yaml](./cppToCTypes.yaml). 21 22 <details><summary>Template</summary> 23 24 ```yaml 25 # Describes C++ original argument. 26 - es2panda_arg: 27 name: '|arg_name|' 28 type: 29 name: 'FunctionSignature' 30 namespace: 'ir' # optional 31 min_ptr_depth: 1 # optional 32 max_ptr_depth: 1 # optional 33 34 # Describes C arguments, into which the original argument will be expanded. 35 new_args: 36 - type: 37 name: "es2panda_FunctionSignature" 38 ptr_depth: '|es2panda_arg.type.ptr_depth_int|' 39 name: '|arg_name|' 40 namespace: "ir::" 41 42 # Describes additional C return arguments IF original argument is return type and expands to multiple arguments. 43 return_args: 44 - type: 45 name: size_t 46 ptr_depth: 1 47 name: '|arg_name|Len' 48 49 cast: 50 # Create C++ variable from C argument. 51 expression: >- 52 auto |es2panda_arg.type.ptr_depth||arg_name|E2p = 53 reinterpret_cast<ir::FunctionSignature |es2panda_arg.type.ptr_depth|>(|arg_name|); 54 55 # Create C variable from C++ return value. 56 reverse_cast: 57 start: >- 58 reinterpret_cast<?const? es2panda_FunctionSignature |es2panda_arg.type.ptr_depth|> 59 60 # Cast C argument to C++ class, to call method from it. 61 call_cast: 62 call_var: 63 name: classInstance 64 type: 65 name: es2panda_FunctionSignature 66 ptr_depth: 1 67 start: >- 68 (reinterpret_cast<?const? ir::FunctionSignature *>(classInstance))-> 69 70 # Create new class and return C++ pointer to it. 71 constructor_cast: 72 start: >- 73 ctxAllocator->New<ir::FunctionSignature>( 74 end: ) 75 76 # Name of veriable, created by expression. 77 var_name: '|arg_name|E2p' 78 ``` 79 80 </details> 81 822) Remove comments. 833) Remove unnecessary fields: 84 - `return_args` is needed only if you return new type from function and this type expands to multiple arguments. 85 - `reverse_cast` is needed only if you return new type from function. 86 - `call_cast` is needed only if you add new class to generation. 87 - `constructor_cast` is needed only if you add new class to generation. 884) Modify it for new type. Below you can see [Template description](#template-description). 89 90## Template description: 91All non-basic types are described in [cppToCTypes.yaml](./cppToCTypes.yaml). 92 93There are 4 keys in first layer: 941) `es2panda_arg`: describes original C++ argument. It is used to match which **template** from `cppToCTypes.yaml` should be used for current argument. 95 96 <details><summary>More info</summary> 97 98 FunctionSignature in `cppToCTypes.yaml`: 99 ```yaml 100 es2panda_arg: 101 name: '|arg_name|' 102 type: 103 name: 'FunctionSignature' 104 namespace: 'ir' 105 min_ptr_depth: 1 106 ``` 107 108 ### Generator finds match if: 109 ``` 110 original_argument['type']['name'] == es2panda_arg['type']['name'] && 111 original_argument['type']['namespace'] == es2panda_arg['type']['namespace'] && 112 original_argument['type']['ptr_depth'] >= es2panda_arg['min_ptr_depth'] && 113 original_argument['type']['ptr_depth'] <= es2panda_arg['max_ptr_depth'] 114 ``` 115 If any of the fields are missing, the generator will skip the corresponding check (except for the type::name field). 116 117 ### What is `|arg_name|`: 118 It is placeholder. After matching **template**, generator stores placeholder values: 119 ```ruby 120 # Generator finds placeholder |arg_name| in es2panda_arg['name'] 121 # It stores the same value from original_argument: 122 |arg_name| = original_argument['type'] 123 ``` 124 125 You can utilize this placeholder in various contexts, and it will be substituted with the saved value. 126 127 ### Addressing other fields not outlined in the **template**: 128 Following the alignment of the **template** and retention of placeholder values, es2panda_arg is supplanted by original_argument. Hence, other attributes are preserved. 129 130 ### Clarification on ptr_depth and ref_depth: 131 132 `ptr_depth` is number of `*` in argument. 133 `ref_depth` is number of `&` in argument. 134 135 #### Why is it needed: 136 `min_ptr_depth` and `max_ptr_depth` are needed to separate 0 and 1+ ptr-cases, because the es2panda API stores pointers to empty structures and is not able to provide an instance of the class, only a pointer to it (except for primitive C types). 137 For example: 138 `AstNode` -> `es2panda_AstNode *` 139 `AstNode *` -> `es2panda_AstNode *` 140 `AstNode **` -> `es2panda_AstNode **` 141 Where es2panda_AstNode is pointer to empty structure in es2panda API. 142 143 --- 144 </details> 145 1462) `new_args`: describes C arguments, into which the original argument will be expanded. 147 148 <details><summary>More info</summary> 149 150 FunctionSignature in `cppToCTypes.yaml`: 151 ```yaml 152 new_args: 153 - type: 154 name: "es2panda_FunctionSignature" 155 ptr_depth: 1 156 name: '|arg_name|' 157 namespace: "ir::" 158 ``` 159 160 Describes argument for C-API: 161 ```c++ 162 // original C++ argument: 163 ir::FunctionSignature *MyVarName 164 165 // new C argument: 166 es2panda_FunctionSignature *MyVarName 167 ``` 168 169 **Note:** please manually write namespace in the format like `ir::` (with `::`). 170 171 --- 172 </details> 173 1743) `return_args`: describes additional C return arguments IF original argument is return type and expands to multiple arguments. 175 176 <details><summary>More info</summary> 177 178 FunctionSignature in `cppToCTypes.yaml`: 179 ```yaml 180 - name: '|arg_name|Len' 181 type: 182 name: size_t 183 ptr_depth: 1 184 ``` 185 186 ### When it is needed: 187 If `original_argument` expands to **several argument** and if it is **return type** additional arguments should appear, through which the necessary values will be returned. 188 For example: 189 ```c++ 190 // Example: ArenaVector<int> -> int *, size_t * 191 192 // C++ function 193 ArenaVector<int> Foo(); 194 195 // C-API function 196 int *FooInAPI(size_t *arenaVectorLen /* return argument appeared */) 197 ``` 198 199 --- 200 </details> 201 2024) `cast`: 203 - `expression`: create C++ variable from C argument. 204 205 <details><summary>More info</summary> 206 207 FunctionSignature in `cppToCTypes.yaml`: 208 ```yaml 209 expression: >- 210 auto |es2panda_arg.type.ptr_depth||arg_name|E2p = 211 reinterpret_cast<ir::FunctionSignature |es2panda_arg.type.ptr_depth|>(|arg_name|); 212 ``` 213 214 Result: 215 ```c++ 216 // C++ function 217 void Foo(FunctionSignature *myArgument); 218 219 // C-API function 220 void FooInAPI(es2panda_FunctionSignature *myArgument) { 221 auto *myArgumentE2p = reinterpret_cast<ir::FunctionSignature *>(myArgument); 222 // ... other code 223 } 224 ``` 225 226 ### Note: 227 You can see clever placeholder `|es2panda_arg.type.ptr_depth|`. It allows to get value from `es2panda_arg['type']['ptr_depth']`. 228 If `es2panda_arg['type']['ptr_depth'] = 2`, then `|es2panda_arg.type.ptr_depth|` will be raplaced with `**` and `|es2panda_arg.type.ptr_depth_int|` will be replaced with `2`. 229 230 --- 231 </details> 232 233 - `reverse_cast`: create C variable from C++ return value. 234 235 <details><summary>More info</summary> 236 237 FunctionSignature in `cppToCTypes.yaml`: 238 ```yaml 239 reverse_cast: 240 start: >- 241 reinterpret_cast<?const? es2panda_FunctionSignature |es2panda_arg.type.ptr_depth|> 242 ``` 243 244 Result: 245 ```c++ 246 // C++ function 247 FunctionSignature *Foo(); 248 249 // C-API function 250 es2panda_FunctionSignature *FooInAPI() { 251 // auto res = reverse_cast['start']( Foo() )reverse_cast['end'] 252 auto res = reinterpret_cast<es2panda_FunctionSignature *>(Foo()); 253 return res; 254 } 255 ``` 256 257 ### Note: 258 You can see `?const?`, it will be replaced with `const` if the type is const, and will be deleted otherwise. 259 260 --- 261 </details> 262 263 - `call_cast`: cast C argument to C++ class, to call method from it. 264 265 <details><summary>More info</summary> 266 267 FunctionSignature in `cppToCTypes.yaml`: 268 ```yaml 269 call_cast: 270 call_var: 271 name: classInstance 272 type: 273 name: es2panda_FunctionSignature 274 ptr_depth: 1 275 start: >- 276 (reinterpret_cast<?const? ir::FunctionSignature *>(classInstance))-> 277 ``` 278 279 Result: 280 ```c++ 281 // C++ method 282 class FunctionSignature { 283 void Foo(); 284 } 285 286 // C-API function 287 void FooInAPI(es2panda_FunctionSignature *classInstance /* call_var appeared */) { 288 // call_cast['start']Foo(); 289 (reinterpret_cast<ir::FunctionSignature *>(classInstance))->Foo(); 290 } 291 292 ``` 293 294 `call_var`: additional C argument - class pointer, to call method from. 295 296 ### Note: 297 You can see `?const?`, it will be replaced with `const` if the type is const, and will be deleted otherwise. 298 299 --- 300 </details> 301 302 303 - `constructor_cast`: create new class and return C++ pointer to it. 304 305 <details><summary>More info</summary> 306 307 FunctionSignature in `cppToCTypes.yaml`: 308 ```yaml 309 constructor_cast: 310 start: >- 311 ctxAllocator->New<ir::FunctionSignature>( 312 end: ) 313 ``` 314 315 Result: 316 ```c++ 317 // C++ constructor 318 class FunctionSignature { 319 FunctionSignature(int arg1, int arg2); 320 } 321 322 // C-API function 323 es2panda_FunctionSignature FunctionSignatureInAPI(int arg1, int arg2) { 324 // return reverse_cast['start']( constructor_cast['start'] <ARGUMENTS> constructor_cast['end'] )reverse_cast['end'] 325 return reinterpret_cast<es2panda_FunctionSignature *>(/* constructor cast -> */ ctxAllocator->New<ir::FunctionSignature>(arg1, arg2)); 326 } 327 ``` 328 329 ### Note: 330 Using only in constructors. Constructor cast is wrapped by reverse cast. 331 332 --- 333 </details> 334 335 - `var_name`: name of veriable, created by expression. 336 337 <details><summary>More info</summary> 338 339 FunctionSignature in `cppToCTypes.yaml`: 340 ```yaml 341 var_name: '|arg_name|E2p' 342 ``` 343 344 Result: 345 ```c++ 346 // C++ function 347 void Foo(FunctionSignature *myArgument); 348 349 // C-API function 350 void FooInAPI(es2panda_FunctionSignature *myArgument) { 351 // expression: 352 auto *myArgumentE2p = reinterpret_cast<ir::FunctionSignature *>(myArgument); 353 354 // ... other code 355 356 // calling C++ function, using new variable with name 'var_name' 357 Foo(myArgumentE2p /* var_name */); 358 } 359 ``` 360 361 --- 362 </details> 363 364 365es2panda_lib_decl.inc, es2panda_lib_enums.inc, es2panda_lib_impl.inc, es2panda_lib_include.inc, es2panda_lib_list.inc 366 367## How to run: 368```bash 369ninja gen_api # only generates *.inc files. 370 371ninja es2panda-public # compiles es2panda_lib 372``` 373You can find generated files in `<build>/tools/es2panda/generated/es2panda_lib`. 374 375## IDL 376 377### About our IDL specification (idlize package): 378- IDL is basically Webidl2 with some adjustments. 379- Webidl uses DomString, idlize uses string. 380 381Usefull links: 382- `idlize` package - [link](https://gitee.com/nikolay-igotti/idlize). 383- `sdk-idl` examples - [link](https://gitee.com/nikolay-igotti/interface_sdk-idl). 384 385 386### How to check IDL for correctness: 387```bash 388# Cmake 389mkdir build && cd build && cmake -GNinja -DCMAKE_BUILD_TYPE=Debug <runtime_core>/static_core 390 391# Run generation 392ninja gen_api 393 394# Cd to generated dir 395cd <build>/tools/es2panda/generated/es2panda_lib 396mkdir test 397 398# Check idl for correctness 399cp ./es2panda_lib.idl ./test/ 400npx @azanat/idlize --idl2h --input-dir=<build>/tools/es2panda/generated/es2panda_lib/test 401 402# See results 403cat <build>/tools/es2panda/generated/es2panda_lib/generated/headers/arkoala_api_generated.h 404``` 405 406 407### IDL generation rules: 408Manually written functions from the `es2panda_lib.h` were also manually transferred to `es2panda_lib.idl.rb`. 409Other methods are generated automatically. 410 411#### 1. Classes, structures, types from C++ represented in IDL as interfaces: 412```c++ 413// C++ 414typedef struct es2panda_Config es2panda_Config; 415 416// IDL 417[Entity=Class] interface es2panda_Config {}; 418``` 419 420#### 2. Enums are represented as `dictionary` in IDL with pre-calculated values. 421 422#### 3. Pointers to a function are represented as an interface with a single `Do` method whose signature matches that of the target function. 423```c++ 424// C++ 425typedef es2panda_AstNode *(*NodeTransformer)(es2panda_AstNode *); 426 427// IDL 428interface NodeTransformer { 429 AstNode Do(AstNode e2p_node); 430}; 431``` 432 433#### 4. C-pointers representation: 434All arguments except primitive types in IDL are the first pointer to an object. 435E.g. `AstNode *` in C equals to `AstNode` in IDL. 436 437For deeper ones: 438- `AstNode **` => `sequence<AstNode>` 439- `AstNode ***` => `sequence<sequence<AstNode>>` 440- etc. 441 442For primitive types: 443- `int` => `i32` 444- `int *` => `sequence<i32>` 445- `int **` => `sequence<sequence<i32>>` 446- etc. 447 448Exception: 449- `void *` => `VoidPtr` 450 451#### 5. Return argument `size_t *returnTypeLen` is omitted and not represented in IDL. Other return arguments are represented. 452 453#### 6. es2panda classes conversion: 454All the types/classes listed below are represented in the IDL without any changes: 455- ast_nodes: 456 - Classes from macro `AST_NODE_MAPPING` 457 - Classes from macro `AST_NODE_REINTERPRET_MAPPING` 458 - `AstNode` 459 - `Expression` 460 - `Statement` 461 - `TypeNode` 462- ast_node_additional_children: 463 - `TypedStatement` 464 - `ClassElement` 465 - `AnnotatedExpression` 466 - `Literal` 467 - `LoopStatement` 468 - `MaybeOptionalExpression` 469 - `Property` 470- ast_types: 471 - Classes from macro`TYPE_MAPPING` 472 - `Type` 473- ast_type_additional_children: 474 - `ETSStringType` 475 - `ETSDynamicType` 476 - `ETSAsyncFuncReturnType` 477 - `ETSDynamicFunctionType` 478 - `ETSEnumType` 479 - `ETSBigIntType` 480- ast_variables: 481 - `Variable` 482 - `LocalVariable` 483 - `GlobalVariable` 484 - `ModuleVariable` 485 - `EnumVariable` 486- scopes: 487 - Classes from macro `SCOPE_TYPES` 488 - `Scope` 489 - `VariableScope` 490- declarations: 491 - Classes from macro `DECLARATION_KINDS` 492 - `Decl` 493 494Other types/classes are represented in the same way as in C-API. 495 496 497#### 7. Primitive types conversion: 498- `bool` => `boolean` 499- `int` => `i32` 500- `size_t` => `u32` 501- `char` => `i8` 502- `int8_t` => `i8` 503- `uint8_t` => `u8` 504- `int16_t` => `i16` 505- `char16_t` => `i16` 506- `int32_t` => `i32` 507- `uint32_t` => `u32` 508- `int64_t` => `i64` 509- `uint64_t` => `u64` 510- `float` => `f32` 511- `double` => `f64` 512- `sequence<i8>` => `String` 513 514#### 8. Classes representation: 515Example: 516```c++ 517// C++ 518class UnaryExpression : public Expression { 519 explicit UnaryExpression(Expression *const argument); 520 Expression *Argument(); 521 const Expression *Argument() const 522} 523 524// C-API 525es2panda_AstNode *(*CreateUnaryExpression)(es2panda_Context *context, es2panda_AstNode *argument); 526es2panda_AstNode *(*UnaryExpressionArgument)(es2panda_Context *context, es2panda_AstNode *classInstance); 527const es2panda_AstNode *(*UnaryExpressionArgumentConst)(es2panda_Context *context, es2panda_AstNode *classInstance); 528 529// IDL 530[Entity=Class, Es2pandaAstNodeType=147, cpp_namespace=ir] interface UnaryExpression: Expression { 531 static UnaryExpression Create(es2panda_Context context, Expression argument); 532 [get] Expression Argument(es2panda_Context context); 533 [get] Expression ArgumentConst(es2panda_Context context); 534} 535``` 536 537More detailed: 538- `Entity=Class` indicates that it's a class in es2panda. 539- `Es2pandaAstNodeType` precalculated AstNodeType. This property is not present if the class does not have `AstNodeType` value. 540- `cpp_namespace` namespace in C++ es2panda. 541- `: Expression` Inherits the `Expression` class. 542- `Create` - class constructor. 543- `static` - all constructors marked as static methods. 544- `[get]` - marker for probably getter-methods. We do not guarantee 100% accuracy! 545 546#### 9. Class inheritance in IDL: 547- Some classes inherit `T`. It was decided to leave them for the sake of completeness. 548- If class inherits Template class e.g. `: public Annotated<Expression>` in IDL it is represented as `: Expression`. 549- If class has multi-inheritance, then inheritance is omitted and not represented in IDL. 550 551## Tests: 552Tests are located in `ets_frontend/ets2panda/test/unit/public`, their names start with "e2p_test_plugin". 553 554Run tests: 555```bash 556ninja es2panda-plugin-test 557``` 558