Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Common/Utility/Platform.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Amethyst.Common.Utility {
public enum PlatformType {
WinClient,
WinServer,
WinAny
}

public static class PlatformUtility {
public static bool TryParse(string str, out PlatformType type) {
type = PlatformType.WinClient;
switch (str) {
case "win-client" or "wc":
type = PlatformType.WinClient;
return true;
case "win-server" or "ws":
type = PlatformType.WinServer;
return true;
case "win-any" or "wa":
type = PlatformType.WinAny;
return true;
default:
return false;
}
}
}
}
30 changes: 13 additions & 17 deletions SymbolGenerator/Commands/MainCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,18 @@ public partial class MainCommand : ICommand
[CommandOption("filters", 'f', Description = "List of filters to apply when generating symbols.")]
public IReadOnlyList<string> Filters { get; set; } = null!;

[CommandOption("platform", 'p', Description = "Target platform for symbol generation (e.g., win-client, win-server).", IsRequired = false)]
public string Platform { get; set; } = "win-client";

public ValueTask ExecuteAsync(IConsole console)
{
DirectoryInfo Input = new(InputPath);
DirectoryInfo Output = new(OutputPath);
if (Platform == "win-any")
Logger.Fatal("Platform 'win-any' is not supported for symbol generation. Please specify either 'win-client' or 'win-server'.");

if (!PlatformUtility.TryParse(Platform, out PlatformType PlatformType))
Logger.Fatal($"Invalid platform '{Platform}'. Supported platforms are: win-client, win-server.");

// Ensure input directory exists
if (Input.Exists is false)
Expand All @@ -43,7 +51,7 @@ public ValueTask ExecuteAsync(IConsole console)
{
headerTracker = new(
inputDirectory: Input,
checksumFile: new FileInfo(Path.Combine(Output.FullName, "header_checksums.json")),
checksumFile: new FileInfo(Path.Combine(Output.FullName, $"{Platform}_header_checksums.json")),
searchPatterns: ["*.h", "*.hpp", "*.hh", "*.hxx"],
filters: [.. Filters]
);
Expand Down Expand Up @@ -108,11 +116,7 @@ public ValueTask ExecuteAsync(IConsole console)
{
if (variable.RawComment is null || variable.Location is null || !willBeParsed.Contains(variable.Location.File))
continue;
RawAnnotation[] anns = CommentParser.ParseAnnotations(variable.RawComment, variable.Location).ToArray();
for (int i = 0; i < anns.Length; i++)
{
anns[i].Target = variable;
}
RawAnnotation[] anns = CommentParser.ParseAnnotations(variable, variable.RawComment, variable.Location).ToArray();
annotations.AddRange(anns);
}

Expand All @@ -121,11 +125,7 @@ public ValueTask ExecuteAsync(IConsole console)
{
if (cls.RawComment is null || cls.Location is null)
continue;
RawAnnotation[] anns = CommentParser.ParseAnnotations(cls.RawComment, cls.Location).ToArray();
for (int i = 0; i < anns.Length; i++)
{
anns[i].Target = cls;
}
RawAnnotation[] anns = CommentParser.ParseAnnotations(cls, cls.RawComment, cls.Location).ToArray();
annotations.AddRange(anns);
}

Expand All @@ -134,19 +134,15 @@ public ValueTask ExecuteAsync(IConsole console)
{
if (method.RawComment is null || method.Location is null)
continue;
RawAnnotation[] anns = CommentParser.ParseAnnotations(method.RawComment, method.Location).ToArray();
for (int i = 0; i < anns.Length; i++)
{
anns[i].Target = method;
}
RawAnnotation[] anns = CommentParser.ParseAnnotations(method, method.RawComment, method.Location).ToArray();
annotations.AddRange(anns);
}
});

Utils.Benchmark("Process annotations", () =>
{
// Process extracted annotations
var processor = new AnnotationProcessor();
var processor = new AnnotationProcessor(PlatformType);
Utils.Benchmark("Process and resolve annotations", () => processor.ProcessAndResolve(annotations));
foreach (var processed in processor.ProcessedAnnotations)
{
Expand Down
10 changes: 8 additions & 2 deletions SymbolGenerator/Parsing/Annotations/AbstractAnnotationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

namespace Amethyst.SymbolGenerator.Parsing.Annotations
{
public abstract class AbstractAnnotationHandler(AnnotationProcessor processor)
public enum HandlerAction {
Handle,
SilentlySkip
}

public abstract class AbstractAnnotationHandler(AnnotationProcessor processor, RawAnnotation annotation)
{
public readonly AnnotationProcessor Processor = processor;
public readonly RawAnnotation Annotation = annotation;

public abstract void CanHandle(RawAnnotation annotation);
public abstract HandlerAction CanHandle(RawAnnotation annotation);
public abstract ProcessedAnnotation Handle(RawAnnotation annotation);
}
}
11 changes: 5 additions & 6 deletions SymbolGenerator/Parsing/Annotations/AbstractAnnotationTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ public abstract class AbstractAnnotationTarget

public HashSet<ProcessedAnnotation> Annotations { get; set; } = [];

public bool HasAnnotation(string tag)
public bool HasAnnotation(AnnotationID id)
{
string officialTag = AnnotationProcessor.GetOfficialTagForAlias(tag);
return Annotations.Any(a => a.Annotation.Tag.Equals(officialTag, StringComparison.OrdinalIgnoreCase));
return Annotations.Any(a => a.ID == id);
}

public bool HasAnyOfAnnotations(IEnumerable<string> tags)
public bool HasAnyOfAnnotations(IEnumerable<AnnotationID> ids)
{
foreach (var tag in tags)
foreach (var id in ids)
{
if (HasAnnotation(tag))
if (HasAnnotation(id))
return true;
}
return false;
Expand Down
14 changes: 14 additions & 0 deletions SymbolGenerator/Parsing/Annotations/AbstractParameterPack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Amethyst.SymbolGenerator.Parsing.Annotations.Comments;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Amethyst.SymbolGenerator.Parsing.Annotations {
public abstract class AbstractParameterPack<T>(RawAnnotation annotation) {
public RawAnnotation Annotation { get; } = annotation;

public abstract T Parse();
}
}
42 changes: 42 additions & 0 deletions SymbolGenerator/Parsing/Annotations/AnnotationID.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Amethyst.Common.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Amethyst.SymbolGenerator.Parsing.Annotations {
public class AnnotationID(string tag, PlatformType platform) {
public string Tag { get; } = tag;
public PlatformType Platform { get; } = platform;

public override int GetHashCode() {
var canonicalTag = AnnotationProcessor.GetCanonicalTagForAlias(Tag);
var platformHash = Platform == PlatformType.WinAny ? 0 : (int)Platform;
return HashCode.Combine(canonicalTag, platformHash);
}

public override bool Equals(object? otherV)
{
if (otherV is null || otherV is not AnnotationID other)
return false;
var thisCanonicalTag = AnnotationProcessor.GetCanonicalTagForAlias(Tag);
var thatCanonicalTag = AnnotationProcessor.GetCanonicalTagForAlias(other.Tag);

if (thisCanonicalTag != thatCanonicalTag)
return false;

if (Platform != PlatformType.WinAny && other.Platform != PlatformType.WinAny && Platform != other.Platform)
return false;
return true;
}

public static bool operator==(AnnotationID lhs, AnnotationID rhs) {
return lhs.Equals(rhs);
}

public static bool operator!=(AnnotationID lhs, AnnotationID rhs) {
return !lhs.Equals(rhs);
}
}
}
19 changes: 12 additions & 7 deletions SymbolGenerator/Parsing/Annotations/AnnotationProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
using Amethyst.Common.Diagnostics;
using Amethyst.Common.Utility;
using Amethyst.SymbolGenerator.Parsing.Annotations.Comments;
using System.Reflection;

namespace Amethyst.SymbolGenerator.Parsing.Annotations
{
public class AnnotationProcessor
public class AnnotationProcessor(PlatformType type)
{
public static Dictionary<AnnotationHandlerAttribute, Type> Handlers { get; private set; }
public HashSet<ProcessedAnnotation> ProcessedAnnotations { get; } = [];
public HashSet<ProcessedAnnotation> ResolvedAnnotations { get; } = [];
public PlatformType PlatformType { get; private set; } = type;

static AnnotationProcessor()
{
Expand All @@ -23,7 +25,7 @@ static AnnotationProcessor()
.ToDictionary(t => t.attr, t => t.type);
}

public static string GetOfficialTagForAlias(string tag)
public static string GetCanonicalTagForAlias(string tag)
{
string tagLower = tag.ToLowerInvariant();
var handler = Handlers.FirstOrDefault(h => h.Key.Tags.Select(t => t.ToLowerInvariant()).Contains(tagLower));
Expand Down Expand Up @@ -59,25 +61,28 @@ private void Process(RawAnnotation annotation)
return;
}

AbstractAnnotationHandler handler = (AbstractAnnotationHandler)Activator.CreateInstance(handlerType, [this])!;
ProcessedAnnotation processed;
try
{
handler.CanHandle(annotation);
AbstractAnnotationHandler handler = (AbstractAnnotationHandler)Activator.CreateInstance(handlerType, [this, annotation])!;
if (handler.CanHandle(annotation) == HandlerAction.SilentlySkip)
return;
processed = handler.Handle(annotation);
annotation.Target.Annotations.Add(processed);
}
catch (UnhandledAnnotationException ex)
catch (UnhandledAnnotationException ex)
{
Logger.Warn($"Skipping annotation '{annotation}' at {annotation.Location}: {ex.Message}");
return;
}
catch
catch (Exception ex)
{
Logger.Warn($"Skipping annotation '{annotation}' at {annotation.Location}: {ex.InnerException?.Message}");
return;
}


ProcessedAnnotations.Add(processed);
annotation.Target.Annotations.Add(processed);
}
}
}
4 changes: 2 additions & 2 deletions SymbolGenerator/Parsing/Annotations/Comments/CommentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Amethyst.SymbolGenerator.Parsing.Annotations
{
public static partial class CommentParser
{
public static IEnumerable<RawAnnotation> ParseAnnotations(string comment, ASTCursorLocation location)
public static IEnumerable<RawAnnotation> ParseAnnotations(AbstractAnnotationTarget target, string comment, ASTCursorLocation location)
{
using var sr = new StringReader(comment);
string? line;
Expand All @@ -21,7 +21,7 @@ public static IEnumerable<RawAnnotation> ParseAnnotations(string comment, ASTCur
string name = match.Groups[1].Value;
string? parameters = match.Groups[2].Success ? match.Groups[2].Value : null;
IEnumerable<string> parts = (parameters?.Split(',') ?? []).Select(a => a.Trim().TrimStart('"').TrimEnd('"'));
yield return new RawAnnotation(name, parts, location);
yield return new RawAnnotation(name, parts, location, target);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
namespace Amethyst.SymbolGenerator.Parsing.Annotations.Comments
{
public record RawAnnotation(string Tag, IEnumerable<string> Arguments, ASTCursorLocation Location)
public record RawAnnotation(string Tag, IEnumerable<string> Arguments, ASTCursorLocation Location, AbstractAnnotationTarget Target)
{
public AbstractAnnotationTarget Target { get; set; } = null!;

public override string ToString()
{
return $"[{Tag}(...)]";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
using Amethyst.Common.Models;
using Amethyst.SymbolGenerator.Parsing.Annotations.Comments;
using Amethyst.SymbolGenerator.Parsing.Annotations.ParameterPacks;

namespace Amethyst.SymbolGenerator.Parsing.Annotations.Handlers
{
[AnnotationHandler("address", ["addr", "absolute", "abs", "at"])]
public class AddressAnnotationHandler(AnnotationProcessor processor) : AbstractAnnotationHandler(processor)
public class AddressAnnotationHandler(AnnotationProcessor processor, RawAnnotation annotation) : AbstractAnnotationHandler(processor, annotation)
{
public override void CanHandle(RawAnnotation annotation)
public AddressAnnotationParameterPack ParameterPack { get; } = new AddressAnnotationParameterPack(annotation).Parse();

public override HandlerAction CanHandle(RawAnnotation annotation)
{
if (ParameterPack.Platform != Processor.PlatformType)
return HandlerAction.SilentlySkip;

if (annotation.Target is ASTMethod method)
{
if (!method.IsImported)
Expand All @@ -23,39 +29,37 @@ public override void CanHandle(RawAnnotation annotation)
throw new UnhandledAnnotationException($"Address annotation can only be applied to methods or variables. Applied to {annotation.Target.GetType().Name} instead.", annotation);
}

if (annotation.Target.HasAnyOfAnnotations([annotation.Tag, "signature"]))
if (annotation.Target.HasAnyOfAnnotations([
new(annotation.Tag, ParameterPack.Platform),
new("signature", ParameterPack.Platform)
]))
throw new UnhandledAnnotationException($"Multiple address or signature annotations applied to the same target {annotation.Target}.", annotation);

string[] args = [.. annotation.Arguments];
if (args.Length != 1)
throw new UnhandledAnnotationException($"Address annotation requires exactly one argument. Received {args.Length}", annotation);

if (!ulong.TryParse(args[0].Replace("0x", ""), System.Globalization.NumberStyles.HexNumber, null, out _))
throw new UnhandledAnnotationException($"Address annotation argument must be a valid hexadecimal number. Received {args[0]}", annotation);
return HandlerAction.Handle;
}

public override ProcessedAnnotation Handle(RawAnnotation annotation)
{
string[] args = [.. annotation.Arguments];
if (annotation.Target is ASTVariable variable)
{
return new ProcessedAnnotation(
annotation,
new(annotation.Tag, ParameterPack.Platform),
new VariableSymbolModel
{
Name = variable.MangledName,
Address = args[0]
Address = $"0x{ParameterPack.Address:x}"
}
);
}
else if (annotation.Target is ASTMethod method)
{
return new ProcessedAnnotation(
annotation,
new(annotation.Tag, ParameterPack.Platform),
new FunctionSymbolModel
{
Name = method.MangledName,
Address = args[0]
Address = $"0x{ParameterPack.Address:x}"
}
);
}
Expand Down
Loading