Skip to content

Commit 2e61352

Browse files
committed
Add CLR callable runtime support
1 parent 769aea7 commit 2e61352

32 files changed

Lines changed: 5973 additions & 1611 deletions

PdVm.Compiler/PdVmClrCompiler.cs

Lines changed: 206 additions & 71 deletions
Large diffs are not rendered by default.

PdVm.Compiler/PdVmDotNetSourceCompiler.cs

Lines changed: 171 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ private sealed record SystemImport(
4747
string SourcePath,
4848
int Line,
4949
string Path,
50+
string Alias,
5051
MemberUse[] UsedMembers)
5152
{
5253
public string TypeName => Path.Replace("::", ".", StringComparison.Ordinal);
@@ -100,6 +101,7 @@ public static string CompileFile(
100101
CopySourceOverlay(sourceRoot, temporaryRoot);
101102
var bindings = BuildBindings(resolvedImports);
102103
var importMap = WriteBindingModules(temporaryRoot, bindings);
104+
RewriteSystemMemberReferences(temporaryRoot, systemImports, bindings);
103105
var relativeSource = Path.GetRelativePath(sourceRoot, fullSourcePath);
104106
var overlaySource = Path.Combine(temporaryRoot, relativeSource);
105107
var vmbc = PdVmNativeCompiler.CompileFile(
@@ -201,6 +203,7 @@ void ScanSource(string sourcePath)
201203
Path.GetRelativePath(sourceRoot, sourcePath),
202204
line,
203205
path,
206+
alias,
204207
usedMembers));
205208
}
206209

@@ -517,12 +520,15 @@ private static bool TryBuildParameters(
517520
}
518521
for (var index = 0; index < clrParameters.Length; index++)
519522
{
520-
if (clrParameters[index].IsOut || !TryGetSchema(clrParameters[index].ParameterType, out var schema))
523+
var parameter = clrParameters[index];
524+
var schemaOverride = parameter.GetCustomAttribute<PdVmInteropSchemaAttribute>()?.Schema;
525+
if (parameter.IsOut ||
526+
(schemaOverride is null && !TryGetSchema(parameter.ParameterType, out schemaOverride)))
521527
{
522528
parameters = [];
523529
return false;
524530
}
525-
generated.Add(($"arg{index}", schema));
531+
generated.Add(($"arg{index}", schemaOverride));
526532
}
527533
parameters = generated.ToArray();
528534
return true;
@@ -675,6 +681,162 @@ private static Dictionary<string, PdVmDotNetBindingDescriptor> WriteBindingModul
675681
return importMap;
676682
}
677683

684+
private static void RewriteSystemMemberReferences(
685+
string root,
686+
IReadOnlyList<SystemImport> imports,
687+
IReadOnlyList<Binding> bindings)
688+
{
689+
var internalNames = bindings.ToDictionary(
690+
binding => (binding.ModulePath, binding.PublicName),
691+
binding => InternalName(CreateDescriptor(binding)));
692+
foreach (var sourceGroup in imports.GroupBy(item => item.SourcePath, StringComparer.Ordinal))
693+
{
694+
var replacements = new Dictionary<(string Alias, string Member), string>();
695+
foreach (var import in sourceGroup)
696+
{
697+
foreach (var member in import.UsedMembers)
698+
{
699+
if (!internalNames.TryGetValue((import.ModulePath, member.Name), out var internalName))
700+
{
701+
throw new PdVmCompilerException(
702+
$"CLR metadata call at {import.SourcePath}:{member.Line}: " +
703+
$"generated binding '{import.Alias}::{member.Name}' was not found");
704+
}
705+
706+
replacements[(import.Alias, member.Name)] = internalName;
707+
}
708+
}
709+
710+
if (replacements.Count == 0)
711+
{
712+
continue;
713+
}
714+
715+
var sourcePath = Path.Combine(root, sourceGroup.Key);
716+
var source = File.ReadAllText(sourcePath);
717+
var rewritten = RewriteQualifiedMemberNames(source, replacements);
718+
if (!string.Equals(source, rewritten, StringComparison.Ordinal))
719+
{
720+
File.WriteAllText(
721+
sourcePath,
722+
rewritten,
723+
new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
724+
}
725+
}
726+
}
727+
728+
private static string RewriteQualifiedMemberNames(
729+
string source,
730+
IReadOnlyDictionary<(string Alias, string Member), string> replacements)
731+
{
732+
var rewritten = new StringBuilder(source.Length);
733+
for (var index = 0; index < source.Length;)
734+
{
735+
if (source[index] is '"' or '\'')
736+
{
737+
var quote = source[index];
738+
rewritten.Append(quote);
739+
index++;
740+
while (index < source.Length)
741+
{
742+
var current = source[index++];
743+
rewritten.Append(current);
744+
if (current == '\\' && index < source.Length)
745+
{
746+
rewritten.Append(source[index++]);
747+
}
748+
else if (current == quote)
749+
{
750+
break;
751+
}
752+
}
753+
continue;
754+
}
755+
756+
if (source[index] == '/' && index + 1 < source.Length && source[index + 1] == '/')
757+
{
758+
var end = source.IndexOf('\n', index + 2);
759+
if (end < 0)
760+
{
761+
rewritten.Append(source, index, source.Length - index);
762+
break;
763+
}
764+
765+
rewritten.Append(source, index, end + 1 - index);
766+
index = end + 1;
767+
continue;
768+
}
769+
770+
if (source[index] == '/' && index + 1 < source.Length && source[index + 1] == '*')
771+
{
772+
var depth = 1;
773+
var end = index + 2;
774+
while (end < source.Length && depth > 0)
775+
{
776+
if (end + 1 < source.Length && source[end] == '/' && source[end + 1] == '*')
777+
{
778+
depth++;
779+
end += 2;
780+
}
781+
else if (end + 1 < source.Length && source[end] == '*' && source[end + 1] == '/')
782+
{
783+
depth--;
784+
end += 2;
785+
}
786+
else
787+
{
788+
end++;
789+
}
790+
}
791+
792+
rewritten.Append(source, index, end - index);
793+
index = end;
794+
continue;
795+
}
796+
797+
if (source[index] == '_' || char.IsAsciiLetter(source[index]))
798+
{
799+
var aliasStart = index;
800+
index++;
801+
while (index < source.Length &&
802+
(source[index] == '_' || char.IsAsciiLetterOrDigit(source[index])))
803+
{
804+
index++;
805+
}
806+
807+
var alias = source[aliasStart..index];
808+
if (index + 2 < source.Length &&
809+
source[index] == ':' &&
810+
source[index + 1] == ':' &&
811+
(source[index + 2] == '_' || char.IsAsciiLetter(source[index + 2])))
812+
{
813+
var memberStart = index + 2;
814+
var memberEnd = memberStart + 1;
815+
while (memberEnd < source.Length &&
816+
(source[memberEnd] == '_' || char.IsAsciiLetterOrDigit(source[memberEnd])))
817+
{
818+
memberEnd++;
819+
}
820+
821+
var member = source[memberStart..memberEnd];
822+
if (replacements.TryGetValue((alias, member), out var replacement))
823+
{
824+
rewritten.Append(alias).Append("::").Append(replacement);
825+
index = memberEnd;
826+
continue;
827+
}
828+
}
829+
830+
rewritten.Append(source, aliasStart, index - aliasStart);
831+
continue;
832+
}
833+
834+
rewritten.Append(source[index++]);
835+
}
836+
837+
return rewritten.ToString();
838+
}
839+
678840
private static void ValidateBinding(Binding binding)
679841
{
680842
_ = (object)(binding.Kind switch
@@ -738,21 +900,9 @@ private static string InternalName(PdVmDotNetBindingDescriptor descriptor)
738900
private static void EmitBinding(StringBuilder source, Binding binding, string internalName)
739901
{
740902
var parameters = string.Join(", ", binding.Parameters.Select(item => $"{item.Name}: {item.Schema}"));
741-
var arguments = string.Join(", ", binding.Parameters.Select(item => item.Name));
742903
source.Append("pub fn ").Append(internalName).Append('(').Append(parameters).Append(')')
743904
.Append(" -> ").Append(binding.ReturnSchema).AppendLine(";");
744-
source.Append("pub fn ").Append(binding.PublicName).Append('(').Append(parameters).Append(')')
745-
.Append(" -> ").Append(binding.ReturnSchema).AppendLine(" {");
746-
source.Append(" ");
747-
if (binding.ReturnSchema == "null")
748-
{
749-
source.Append(internalName).Append('(').Append(arguments).AppendLine(");");
750-
}
751-
else
752-
{
753-
source.Append(internalName).Append('(').Append(arguments).AppendLine(")");
754-
}
755-
source.AppendLine("}").AppendLine();
905+
source.AppendLine();
756906
}
757907

758908
private static PdVmProgramModel RemapImports(
@@ -777,7 +927,12 @@ private static PdVmProgramModel RemapImports(
777927
model.LocalCount,
778928
imports,
779929
model.Instructions,
780-
model.TypeMap);
930+
model.TypeMap,
931+
model.ScriptFunctions,
932+
model.CallablePrototypes,
933+
model.FunctionRegions,
934+
model.RootCallableBindings,
935+
model.ExportedCallables);
781936
}
782937

783938
private static void CopySourceOverlay(string sourceRoot, string destinationRoot)

0 commit comments

Comments
 (0)