LILAC
Language to language Interop LAyer Compiler
Loading...
Searching...
No Matches
interfacevisitor.cxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024 Yeong-won Seo
3 *
4 * This file is part of LILAC.
5 *
6 * LILAC is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3, or (at your option) any later
9 * version.
10 *
11 * LILAC is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "interfacevisitor.h"
21#include "shared/char.h"
22
23#define TAB "\t"
24#define THIS "__ref"
25
26static std::string Type(std::string src, const frxml::dom& loc)
27{
28 int refC = 0;
29 for (; src[src.size() - 1 - refC] == '*'; refC++)
30 {
31 }
32 if (refC)
33 src = src.erase(src.length() - refC, refC);
34 std::string ref(refC, '*');
35
36 if (src == "__fp128")
37 throw lilac::shared::exception("__fp128 is not supported by C#", loc);
38
39 static std::map<std::string, std::string> builtins = {
40 { "__void", "void" },
41 { "__bool", "bool" },
42 { "__u8", "byte" },
43 { "__u16", "ushort" },
44 { "__u32", "uint" },
45 { "__uptr", "nuint" },
46 { "__u64", "ulong" },
47 { "__u128", "System.UInt128" },
48 { "__s8", "sbyte" },
49 { "__s16", "short" },
50 { "__s32", "int" },
51 { "__sptr", "nint" },
52 { "__s64", "long" },
53 { "__s128", "System.Int128" },
54 { "__fp16", "System.Half" },
55 { "__fp32", "float" },
56 { "__fp64", "double" }
57 };
58 if (builtins.contains(src))
59 return builtins[src] + ref;
60
61 for (char& i: src)
62 if (i == '/')
63 i = '.';
64 return src + ref;
65}
66
77> s_GenericInterfaceVisitor;
78
79void ForeachChildren(lilac::csharp::VisitContext& ctx, const frxml::dom& current, int depth, bool spacing)
80{
81 for (auto i = 0; i < current.children().size(); ++i)
82 {
83 if (spacing && i)
85
86 s_GenericInterfaceVisitor.Begin(ctx, current, current.children()[i], depth);
87 s_GenericInterfaceVisitor.End(ctx, current, current.children()[i], depth);
88 }
89}
90
91#pragma region AssemblyInterfaceVisitor
92
94 VisitContext& ctx,
95 const frxml::dom& parent,
96 const frxml::dom& current,
97 int depth)
98{
99 if (current.tag().view() != "assembly")
100 throw shared::exception("AssemblyInterfaceVisitor should deal with assembly element only", current);
101
102 const auto indent = shared::GetIndent(depth);
103
104 ctx.Output << indent << "// <auto-generated/>" << shared::endl << shared::endl;
105
106 auto addDepth = 0;
107 if (!ctx.RootNamespace.empty())
108 {
109 ctx.Output
110 << indent << "namespace " << ctx.RootNamespace << shared::endl
111 << indent << "{" << shared::endl;
112 addDepth++;
113 }
114
115 ForeachChildren(ctx, current, depth + addDepth, true);
116}
117
119 VisitContext& ctx,
120 const frxml::dom& parent,
121 const frxml::dom& current,
122 int depth)
123{
124 if (ctx.RootNamespace.empty())
125 return;
126
127 const auto indent = shared::GetIndent(depth);
128 ctx.Output << indent << "}" << shared::endl;
129}
130
131#pragma endregion
132
133#pragma region NamespaceVisitor
134
136{
137 return "namespace";
138}
139
141 VisitContext& ctx,
142 const frxml::dom& parent,
143 const frxml::dom& current,
144 int depth)
145{
146 const auto indent = shared::GetIndent(depth);
147
148 ctx.Output
149 << indent << "namespace " << current.attr().at("name").view() << shared::endl
150 << indent << '{' << shared::endl;
151
152 ForeachChildren(ctx, current, depth + 1, true);
153}
154
156 VisitContext& ctx,
157 const frxml::dom& parent,
158 const frxml::dom& current,
159 int depth)
160{
161 const auto indent = shared::GetIndent(depth);
162 ctx.Output << indent << "}" << shared::endl;
163}
164
165#pragma endregion
166
167#pragma region RecordVisitor
168
170{
171 return "record";
172}
173
175 VisitContext& ctx,
176 const frxml::dom& parent,
177 const frxml::dom& current,
178 int depth)
179{
180 const auto indent = shared::GetIndent(depth);
181
182 const auto name = current.attr().at("name").view();
183 const auto size = current.attr().at("size").view();
184 const auto align = current.attr().at("align").view();
185
186#define INTEROP_NS "global::System.Runtime.InteropServices."
187#define LAYOUT_KIND INTEROP_NS "LayoutKind."
188#define COMPILER_NS "global::System.Runtime.CompilerServices."
189
190 ctx.Output
191 << indent << "[" INTEROP_NS"StructLayout(" LAYOUT_KIND"Explicit, Size=" << size << ", Pack=" << align << ")]\n"
192 << indent << "public struct " << name << "\n"
193 << indent << "{\n"
194 << indent << TAB "[" INTEROP_NS"FieldOffset(0)]\n"
195 << indent << TAB "private byte " THIS ";\n"
196 << indent << "\n"
197 << indent << TAB "private IntPtr This\n"
198 << indent << TAB "{\n"
199 << indent << TAB TAB "get\n"
200 << indent << TAB TAB "{\n"
201 << indent << TAB TAB TAB "unsafe\n"
202 << indent << TAB TAB TAB "{\n"
203 << indent << TAB TAB TAB TAB "return (IntPtr)" COMPILER_NS"Unsafe.AsPointer(ref __ref);\n"
204 << indent << TAB TAB TAB "}\n"
205 << indent << TAB TAB "}\n"
206 << indent << TAB "}\n";
207
208#undef COMPILER_NS
209#undef LAYOUT_KIND
210#undef INTEROP_NS
211
212 if (!current.children().empty())
213 ctx.Output << shared::endl;
214
215 ForeachChildren(ctx, current, depth + 1, true);
216}
217
219 VisitContext& ctx,
220 const frxml::dom& parent,
221 const frxml::dom& current,
222 int depth)
223{
224 const auto indent = shared::GetIndent(depth);
225 ctx.Output << indent << "}" << shared::endl;
226}
227
228#pragma endregion
229
230#pragma region EnumVisitor
231
233{
234 return "enum";
235}
236
238 VisitContext& ctx,
239 const frxml::dom& parent,
240 const frxml::dom& current,
241 int depth)
242{
243 const auto indent = shared::GetIndent(depth);
244
245 const auto name = current.attr().at("name").view();
246 const auto type = current.attr().at("type").view();
247
248 ctx.Output
249 << indent << "public enum " << name << " : " << Type(static_cast<std::string>(type), current) << "\n"
250 << indent << "{\n";
251
252 ForeachChildren(ctx, current, depth + 1, false);
253}
254
256 VisitContext& ctx,
257 const frxml::dom& parent,
258 const frxml::dom& current,
259 int depth)
260{
261 const auto indent = shared::GetIndent(depth);
262 ctx.Output << indent << "}" << shared::endl;
263}
264
265#pragma endregion
266
267#pragma region EnumConstantVisitor
268
270{
271 return "constant";
272}
273
275 VisitContext& ctx,
276 const frxml::dom& parent,
277 const frxml::dom& current,
278 int depth)
279{
280 if (parent.tag().view() != EnumVisitor().GetName())
281 throw shared::exception("`constant' element should be child of `enum' element", current);
282
283 const auto indent = shared::GetIndent(depth);
284
285 const auto name = current.attr().at("name").view();
286 const auto value = current.attr().at("value").view();
287
288 ctx.Output << indent << name << " = " << value << ',' << shared::endl;
289}
290
292 VisitContext& ctx,
293 const frxml::dom& parent,
294 const frxml::dom& current,
295 int depth)
296{
297}
298
299#pragma endregion
300
302{
303 return "function";
304}
305
307 VisitContext& ctx,
308 const frxml::dom& parent,
309 const frxml::dom& current,
310 int depth)
311{
312 const auto indent = shared::GetIndent(depth);
313
314 auto isUnsafe = false;
315
316
317 const auto returnType = Type(static_cast<std::string>(current.attr().at("return").view()), current);
318 // ReSharper disable once CppDFAConstantConditions
319 isUnsafe = isUnsafe || returnType.contains('*');
320
321 // parameters
322
323 std::stringstream managedParams;
324 for (auto i = 0; i < current.children().size(); ++i)
325 {
326 if (i) managedParams << ", ";
327
328 auto param = current.children().at(i);
329 auto type = Type(static_cast<std::string>(param.attr().at("type").view()), current.children()[i]);
330 managedParams << type << ' ' << param.attr().at("name").view();
331 }
332
333 std::stringstream nativeParams;
334 for (auto i = 0; i < current.children().size(); ++i)
335 {
336 if (i) nativeParams << ", ";
337
338 auto param = current.children().at(i);
339 auto type = Type(static_cast<std::string>(param.attr().at("type").view()), current.children()[i]);
340 if (!isUnsafe && type.contains('*'))
341 isUnsafe = true;
342 if (type == "bool")
343 nativeParams <<
344 "[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U1)] ";
345 nativeParams << type << ' ' << param.attr().at("name").view();
346 }
347
348 std::stringstream paramRef;
349 for (auto i = 0; i < current.children().size(); ++i)
350 {
351 if (i) paramRef << ", ";
352 paramRef << current.children().at(i).attr().at("name").view();
353 }
354
355 // callconv
356
357#define CALLCONV "System.Runtime.InteropServices.CallingConvention."
358 static std::map<std::string_view, std::string_view> callconvs = {
359 { "cdecl", CALLCONV "Cdecl" },
360 { "stdcall", CALLCONV "StdCall" },
361 { "thiscall", CALLCONV "ThisCall" },
362 };
363#undef CALLCONV
364
365 auto callconv = current.attr().at("callconv").view();
366 if (!callconvs.contains(callconv))
367 throw shared::exception(std::format("Calling convention `{}' is not supported", callconv), current);
368 callconv = callconvs.at(callconv);
369
370 // assembly - managed parameters
371
372 const auto isStatic = current.tag().view() == FunctionVisitor().GetName();
373
374 const auto isMemberFn = parent.tag().view() == RecordVisitor().GetName();
375 if (!isMemberFn && !isStatic)
376 throw shared::exception("'element' can only be member function", current);
377
378 const auto name = current.attr().at("name").view();
379
380 ctx.Output << indent << "public ";
381 if (!isMemberFn)
382 ctx.Output << "static ";
383 if (isUnsafe)
384 ctx.Output << "unsafe ";
385 ctx.Output << returnType << ' ' << name << '(' << managedParams.str() << ')' << shared::endl;
386
387 // assembly - body
388
389 ctx.Output << indent << "{" << shared::endl;
390
391 ctx.Output << indent << TAB;
392 if (returnType != "void")
393 ctx.Output << "return ";
394
395 if (isMemberFn)
396 {
397 ctx.Output << "__PInvoke(This";
398 if (!paramRef.str().empty())
399 ctx.Output << ", " << paramRef.str();
400 ctx.Output << ");" << shared::endl;
401 }
402 else
403 {
404 ctx.Output << "__PInvoke(" << paramRef.str() << ");" << shared::endl;
405 }
406
407 ctx.Output << shared::endl;
408
409 // assembly - P/Invoke def
410
411 const auto mangling = current.attr().at("mangle").view();
412
413#define INTEROP_NS "System.Runtime.InteropServices."
414 ctx.Output
415 << indent << TAB "[" INTEROP_NS "DllImport(" << shared::endl
416 << indent << TAB TAB "\"" << ctx.LibraryName << "\"," << shared::endl
417 << indent << TAB TAB "EntryPoint = \"" << mangling << "\"," << shared::endl
418 << indent << TAB TAB "CallingConvention = " << callconv << ',' << shared::endl
419 << indent << TAB TAB "ExactSpelling = true)]" << shared::endl;
420#undef INTEROP_NS
421
422 ctx.Output << indent << TAB "static extern " << returnType << " __PInvoke(";
423 if (isMemberFn)
424 {
425 ctx.Output << "IntPtr @this";
426 if (!nativeParams.str().empty())
427 ctx.Output << ", ";
428 }
429 ctx.Output << nativeParams.str() << ");" << shared::endl;
430
431 ctx.Output << indent << "}" << shared::endl;
432}
433
435 VisitContext& ctx,
436 const frxml::dom& parent,
437 const frxml::dom& current,
438 int depth)
439{
440}
441
443{
444 return "method";
445}
446
448{
449 return "ctor";
450}
451
453{
454 return "dtor";
455}
void Begin(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
void End(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
A visitor that creates bridge of ctor elements with C#.
std::string GetName() const override
Gets the name of this interface visitor.
A visitor that creates bridge of dtor elements with C#.
std::string GetName() const override
Gets the name of this interface visitor.
A visitor that creates bridge of constant elements with C#.
std::string GetName() const override
Gets the name of this interface visitor.
void End(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
void Begin(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
A visitor that creates bridge of enum elements with C#.
void End(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
void Begin(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
std::string GetName() const override
Gets the name of this interface visitor.
A visitor that creates bridge of function elements with C#.
void Begin(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) final
void End(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) final
std::string GetName() const override
Gets the name of this interface visitor.
A visitor that creates bridge of method elements with C#.
std::string GetName() const override
Gets the name of this interface visitor.
A visitor that creates bridge of namespace elements with C#.
void End(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
void Begin(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
std::string GetName() const override
Gets the name of this interface visitor.
A visitor that creates bridge of record elements with C#.
void End(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
void Begin(VisitContext &ctx, const frxml::dom &parent, const frxml::dom &current, int depth) override
std::string GetName() const override
Gets the name of this interface visitor.
A composite ASTVisitor wrapping other ASTVisitor(s)
#define CALLCONV
#define LAYOUT_KIND
void ForeachChildren(lilac::csharp::VisitContext &ctx, const frxml::dom &current, int depth, bool spacing)
#define THIS
#define TAB
#define COMPILER_NS
#define INTEROP_NS
std::ostream & endl(std::ostream &__os)
Write a newline on stream.
Definition char.h:83
constexpr const char * GetIndent(int c=1)
Gets an indentation string with specified length.
Definition char.h:75