README.md
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