xamarin-macios/tools/pmcs/XamarinPreprocessorVisitor.cs

285 строки
8.0 KiB
C#
Исходник Обычный вид История

2016-04-21 15:30:02 +03:00
//
// XamarinPreprocessorVisitor.cs
//
// Author:
// Aaron Bockover <abock@xamarin.com>
//
// Copyright 2015 Xamarin Inc. All rights reserved.
using System;
using System.Linq;
using System.Collections.Generic;
using Xamarin.Pmcs.CSharp;
using Xamarin.Pmcs.CSharp.Ast;
namespace Xamarin.Pmcs
{
class XamarinPreprocessorVisitor : Preprocessor.PreprocessorVisitor
{
public XamarinPreprocessorVisitor (Preprocessor preprocessor) : base (preprocessor)
{
}
public override void VisitInvocationExpression (InvocationExpression invocationExpression)
{
var target = invocationExpression.Target as LiteralExpression;
if (target == null)
return;
switch (target.Value) {
case "Mac":
case "MacAttribute":
VisitShorthandIntroducedAttribute (invocationExpression, "PlatformName.MacOSX");
break;
case "Since":
case "SinceAttribute":
case "iOS":
case "iOSAttribute":
VisitShorthandIntroducedAttribute (invocationExpression, "PlatformName.iOS");
break;
case "Watch":
case "WatchAttribute":
VisitShorthandIntroducedAttribute (invocationExpression, "PlatformName.WatchOS");
break;
case "TV":
case "TVAttribute":
VisitShorthandIntroducedAttribute (invocationExpression, "PlatformName.TvOS");
break;
case "Availability":
case "AvailabilityAttribute":
VisitAvailabilityAttribute (invocationExpression);
break;
default:
base.VisitInvocationExpression (invocationExpression);
break;
}
}
public override void VisitLiteralExpression (LiteralExpression literalExpression)
{
switch (literalExpression.Value) {
case "NoMac":
case "NoMacAttribute":
VisitShorthandUnavailableAttribute (literalExpression, "PlatformName.MacOSX");
break;
case "NoiOS":
case "NoiOSAttribute":
VisitShorthandUnavailableAttribute (literalExpression, "PlatformName.iOS");
break;
case "NoWatch":
case "NoWatchAttribute":
VisitShorthandUnavailableAttribute (literalExpression, "PlatformName.WatchOS");
break;
case "NoTV":
case "NoTVAttribute":
VisitShorthandUnavailableAttribute (literalExpression, "PlatformName.TvOS");
break;
case "Lion":
case "LionAttribute":
VisitMacNamedIntroducedAttribute (literalExpression, "7");
break;
case "MountainLion":
case "MountainLionAttribute":
VisitMacNamedIntroducedAttribute (literalExpression, "8");
break;
case "Mavericks":
case "MavericksAttribute":
VisitMacNamedIntroducedAttribute (literalExpression, "9");
break;
default:
base.VisitLiteralExpression (literalExpression);
break;
}
}
static void VisitMacNamedIntroducedAttribute (LiteralExpression oldAttr, string macMinorVersion)
{
var newAttr = new InvocationExpression ("Introduced");
newAttr.AddArgument (new LiteralExpression ("PlatformName.MacOSX"));
newAttr.AddArgument (new LiteralExpression ("10"));
newAttr.AddArgument (new LiteralExpression (macMinorVersion));
oldAttr.Parent.InsertChildBefore (oldAttr, newAttr);
oldAttr.Remove ();
}
static void VisitShorthandUnavailableAttribute (LiteralExpression oldAttr, string platformName)
{
var newAttr = new InvocationExpression ("Unavailable");
newAttr.AddArgument (new LiteralExpression (platformName));
oldAttr.Parent.InsertChildBefore (oldAttr, newAttr);
oldAttr.Remove ();
}
static bool IsNumber (string s)
{
int n;
return int.TryParse (s, out n);
}
static void VisitShorthandIntroducedAttribute (InvocationExpression invocationExpression,
string platformName)
{
bool isTripleVersion = invocationExpression.Arguments.Count () > 2 && invocationExpression.Arguments.Take (3).All (x => x is LiteralExpression && IsNumber(((LiteralExpression)x).Value));
invocationExpression.Target = new LiteralExpression ("Introduced");
invocationExpression.Parent.InsertChildBefore (
invocationExpression.FirstArgument,
new LiteralExpression (platformName),
InvocationExpression.ArgumentRole
);
foreach (var onlyOn64 in invocationExpression.Arguments.Skip (isTripleVersion ? 4 : 3).Take (2)) {
var onlyOn64BoolValue = onlyOn64 as LiteralExpression;
if (onlyOn64 is NamedExpression)
onlyOn64BoolValue = onlyOn64.GetChild<Expression> (
BinaryExpression.RightOperandRole) as LiteralExpression;
if (onlyOn64BoolValue != null && onlyOn64BoolValue.Value == "true")
invocationExpression.AddArgument (
new LiteralExpression ("PlatformArchitecture.Arch64"));
if (onlyOn64BoolValue != null)
onlyOn64.Remove ();
}
}
void VisitAvailabilityAttribute (InvocationExpression invocationExpression)
{
new AvailabilityAttributeVisitor ().ProcessAvailabilityAttribute (invocationExpression);
}
class AvailabilityAttributeVisitor : DepthFirstAstVisitor
{
readonly List<InvocationExpression> invocations = new List<InvocationExpression> ();
string currentAvailability;
string message;
int originalArgIndex;
public void ProcessAvailabilityAttribute (InvocationExpression invocationExpression)
{
originalArgIndex = 0;
message = null;
invocations.Clear ();
foreach (var arg in invocationExpression.Arguments) {
currentAvailability = null;
arg.AcceptVisitor (this);
foreach (var newAttr in invocations) {
if (message != null)
newAttr.AddArgument (new NamedExpression (
new LiteralExpression ("message"),
TokenType.Colon,
new LiteralExpression (message)
));
invocationExpression.Parent.InsertChildBefore (
invocationExpression, newAttr);
}
originalArgIndex++;
}
invocationExpression.Remove ();
}
public override void VisitNamedExpression (NamedExpression namedExpression)
{
VisitBinaryExpression (namedExpression);
}
public override void VisitBinaryExpression (BinaryExpression binaryExpression)
{
var nameExpr = binaryExpression.LeftOperand as LiteralExpression;
if (nameExpr != null && (binaryExpression.Operator == TokenType.Assign ||
binaryExpression.Operator == TokenType.Colon)) {
switch (nameExpr.Value) {
case "Introduced":
case "introduced":
currentAvailability = "Introduced";
break;
case "Deprecated":
case "deprecated":
currentAvailability = "Deprecated";
break;
case "Obsoleted":
case "obsoleted":
currentAvailability = "Obsoleted";
break;
case "Unavailable":
case "unavailable":
currentAvailability = "Obsoleted";
break;
case "Message":
case "message":
var messageExpr = binaryExpression.RightOperand as LiteralExpression;
if (messageExpr != null)
message = messageExpr.Value;
break;
}
}
base.VisitBinaryExpression (binaryExpression);
}
public override void VisitLiteralExpression (LiteralExpression literalExpression)
{
var parts = literalExpression?.Value?.Split ('_');
if (parts == null || parts.Length < 2)
return;
string platformName;
switch (parts [0]) {
case "Platform.Mac":
platformName = "PlatformName.MacOSX";
break;
case "Platform.iOS":
platformName = "PlatformName.iOS";
break;
default:
return;
}
if (currentAvailability == null) {
switch (originalArgIndex) {
case 0:
currentAvailability = "Introduced";
break;
case 1:
currentAvailability = "Deprecated";
break;
case 2:
currentAvailability = "Obsoleted";
break;
case 3:
currentAvailability = "Unavailable";
break;
}
}
var attr = new InvocationExpression (currentAvailability);
attr.AddArgument (new LiteralExpression (platformName));
if (parts.Length == 2) {
switch (parts [1]) {
case "Version":
break;
case "Arch32":
attr.AddArgument (new LiteralExpression ("PlatformArchitecture.Arch32"));
break;
case "Arch64":
attr.AddArgument (new LiteralExpression ("PlatformArchitecture.Arch64"));
break;
}
} else {
for (int i = 1; i < parts.Length; i++)
attr.AddArgument (new LiteralExpression (parts [i]));
}
invocations.Add (attr);
}
}
}
}