//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci;
using Microsoft.Cci.MetadataReader;
using Microsoft.Cci.MutableCodeModel;
using Microsoft.Cci.Contracts;
using Bpl = Microsoft.Boogie;
using System.Diagnostics.Contracts;
using TranslationPlugins;
namespace BytecodeTranslator.Phone {
///
/// Traverse code looking for phone specific points of interest, possibly injecting necessary code in-between
///
public class PhoneInitializationCodeTraverser : BaseCodeTraverser {
private readonly IMethodDefinition methodBeingTraversed;
private static bool initializationFound= false;
private MetadataReaderHost host;
private IAssemblyReference coreAssemblyRef;
private IAssemblyReference phoneAssembly;
private IAssemblyReference phoneSystemWindowsAssembly;
private INamespaceTypeReference appBarIconButtonType;
private INamespaceTypeReference checkBoxType;
private INamespaceTypeReference radioButtonType;
private INamespaceTypeReference buttonType;
private INamespaceTypeReference buttonBaseType;
private INamespaceTypeReference toggleButtonType;
private INamespaceTypeReference controlType;
private INamespaceTypeReference uiElementType;
private CompileTimeConstant trueConstant;
private CompileTimeConstant falseConstant;
private IMethodReference isEnabledSetter;
private IMethodReference isEnabledGetter;
private IMethodReference isCheckedSetter;
private IMethodReference isCheckedGetter;
private IMethodReference visibilitySetter;
private IMethodReference visibilityGetter;
private IMethodReference clickHandlerAdder;
private IMethodReference clickHandlerRemover;
private IMethodReference checkedHandlerAdder;
private IMethodReference checkedHandlerRemover;
private IMethodReference uncheckedHandlerAdder;
private IMethodReference uncheckedHandlerRemover;
private ITypeReference getTypeForClassname(String classname) {
if (classname == "Button") {
return buttonType;
} else if (classname == "RadioButton") {
return radioButtonType;
} else if (classname == "CheckBox") {
return checkBoxType;
} else if (classname == "ApplicationBarIconButton") {
return appBarIconButtonType;
} else if (classname == "DummyType") {
return Dummy.Type;
} else {
// TODO avoid throwing exceptions, just log
throw new NotImplementedException("Type " + classname + " is not being monitored yet for phone controls");
}
}
public PhoneInitializationCodeTraverser(MetadataReaderHost host, IMethodDefinition traversedMethod) : base() {
this.methodBeingTraversed = traversedMethod;
this.host = host;
InitializeTraverser();
}
private void InitializeTraverser() {
Microsoft.Cci.Immutable.PlatformType platform = host.PlatformType as Microsoft.Cci.Immutable.PlatformType;
coreAssemblyRef = platform.CoreAssemblyRef;
// TODO obtain version, culture and signature data dynamically
AssemblyIdentity MSPhoneAssemblyId =
new AssemblyIdentity(host.NameTable.GetNameFor("Microsoft.Phone"), "", new Version("7.0.0.0"),
new byte[] { 0x24, 0xEE, 0xC0, 0xD8, 0xC8, 0x6C, 0xDA, 0x1E }, "");
AssemblyIdentity MSPhoneSystemWindowsAssemblyId =
new AssemblyIdentity(host.NameTable.GetNameFor("System.Windows"), coreAssemblyRef.Culture, coreAssemblyRef.Version,
coreAssemblyRef.PublicKeyToken, "");
phoneAssembly = host.FindAssembly(MSPhoneAssemblyId);
phoneSystemWindowsAssembly = host.FindAssembly(MSPhoneSystemWindowsAssemblyId);
// TODO determine the needed types dynamically
appBarIconButtonType= platform.CreateReference(phoneAssembly, "Microsoft", "Phone", "Shell", "ApplicationBarIconButton");
checkBoxType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Controls", "CheckBox");
radioButtonType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Controls", "RadioButton");
buttonType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Controls", "Button");
buttonBaseType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Controls", "Primitives", "ButtonBase");
toggleButtonType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Controls", "Primitives", "ToggleButton");
controlType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Controls", "Control");
uiElementType = platform.CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "UIElement");
trueConstant = new CompileTimeConstant() {
Type = platform.SystemBoolean,
Value = true
};
falseConstant = new CompileTimeConstant() {
Type = platform.SystemBoolean,
Value = false
};
IEnumerable controlProperties = controlType.ResolvedType.Properties;
IEnumerable toggleButtonProperties = toggleButtonType.ResolvedType.Properties;
IEnumerable uiElementProperties = uiElementType.ResolvedType.Properties;
IPropertyDefinition prop = controlProperties.Single(p => p.Name.Value == "IsEnabled");
isEnabledSetter = prop.Setter;
isEnabledGetter = prop.Getter;
prop = toggleButtonProperties.Single(p => p.Name.Value == "IsChecked");
isCheckedSetter = prop.Setter;
isCheckedGetter = prop.Getter;
prop = uiElementProperties.Single(p => p.Name.Value == "Visibility");
visibilitySetter = prop.Setter;
visibilityGetter = prop.Getter;
IEnumerable buttonBaseEvents = buttonBaseType.ResolvedType.Events;
IEnumerable toggleButtonEvents = toggleButtonType.ResolvedType.Events;
IEventDefinition evt = buttonBaseEvents.Single(e => e.Name.Value == "Click");
clickHandlerAdder = evt.Adder;
clickHandlerRemover = evt.Remover;
evt = toggleButtonEvents.Single(e => e.Name.Value == "Checked");
checkedHandlerAdder = evt.Adder;
checkedHandlerRemover = evt.Remover;
evt = toggleButtonEvents.Single(e => e.Name.Value == "Unchecked");
uncheckedHandlerAdder = evt.Adder;
uncheckedHandlerRemover = evt.Remover;
}
public void injectPhoneControlsCode(BlockStatement block) {
this.Visit(block);
}
private void injectPhoneInitializationCode(BlockStatement block, Statement statementAfter) {
// TODO check page name against container name
IEnumerable controls = PhoneCodeHelper.instance().PhonePlugin.getControlsForPage(methodBeingTraversed.Container.ToString());
IEnumerable injectedStatements = new List();
if (controls != null) {
foreach (ControlInfoStructure controlInfo in controls) {
injectedStatements = injectedStatements.Concat(getCodeForSettingEnabledness(controlInfo));
injectedStatements = injectedStatements.Concat(getCodeForSettingCheckedState(controlInfo));
injectedStatements = injectedStatements.Concat(getCodeForSettingVisibility(controlInfo));
}
int stmtPos = block.Statements.IndexOf(statementAfter);
block.Statements.InsertRange(stmtPos + 1, injectedStatements);
}
}
private BoundExpression makeBoundControlFromControlInfo(ControlInfoStructure controlInfo) {
return new BoundExpression() {
Definition = new FieldDefinition() {
ContainingTypeDefinition = methodBeingTraversed.Container,
Name = host.NameTable.GetNameFor(controlInfo.Name),
Type = getTypeForClassname(controlInfo.ClassName),
IsStatic = false,
},
Instance = new ThisReference() { Type = methodBeingTraversed.Container },
Type=getTypeForClassname(controlInfo.ClassName),
};
}
private IEnumerable getCodeForSettingVisibility(ControlInfoStructure controlInfo) {
// TODO I do not want to import System.Windows into this project...and using the underlying uint won't work for dependency properties
/*
IList code = new List();
BoundExpression boundControl = makeBoundControlFromControlInfo(controlInfo);
MethodCall setVisibilityCall= new MethodCall() {
IsStaticCall = false,
IsVirtualCall = true,
IsTailCall = false,
Type = ((Microsoft.Cci.Immutable.PlatformType) host.PlatformType).SystemVoid,
MethodToCall = visibilitySetter,
ThisArgument = boundControl,
};
ITypeReference visibilityType= ((Microsoft.Cci.Immutable.PlatformType) host.PlatformType).CreateReference(phoneSystemWindowsAssembly, "System", "Windows", "Visibility");
switch (controlInfo.Visible) {
case Visibility.Visible:
setVisibilityCall.Arguments.Add(new CompileTimeConstant() {
Type = visibilityType,
Value = 0,
} ); // Visible
break;
case Visibility.Collapsed:
setVisibilityCall.Arguments.Add(new CompileTimeConstant() {
Type = visibilityType,
Value = 1,
} ); // Collapsed
break;
default:
throw new ArgumentException("Invalid visibility value for control " + controlInfo.Name + ": " + controlInfo.Visible);
}
ExpressionStatement callStmt = new ExpressionStatement() {
Expression = setVisibilityCall,
};
code.Add(callStmt);
return code;
* */
return new List();
}
private IEnumerable getCodeForSettingEnabledness(ControlInfoStructure controlInfo) {
IList code = new List();
BoundExpression boundControl = makeBoundControlFromControlInfo(controlInfo);
MethodCall setEnablednessCall = new MethodCall() {
IsStaticCall = false,
IsVirtualCall = true,
IsTailCall = false,
Type = ((Microsoft.Cci.Immutable.PlatformType) host.PlatformType).SystemVoid,
MethodToCall = isEnabledSetter,
ThisArgument = boundControl,
};
setEnablednessCall.Arguments.Add(controlInfo.IsEnabled ? trueConstant : falseConstant);
ExpressionStatement callStmt = new ExpressionStatement() {
Expression = setEnablednessCall,
};
code.Add(callStmt);
return code;
}
private IEnumerable getCodeForSettingCheckedState(ControlInfoStructure controlInfo) {
// IList code = new List();
// BoundExpression boundControl = makeBoundControlFromControlInfo(controlInfo);
// MethodCall setCheckStateCall= new MethodCall() {
// IsStaticCall = false,
// IsVirtualCall = true,
// IsTailCall = false,
// Type = ((Microsoft.Cci.Immutable.PlatformType) host.PlatformType).SystemVoid,
// MethodToCall = isCheckedSetter,
// ThisArgument = boundControl,
// };
// setCheckStateCall.Arguments.Add(controlInfo.IsChecked ? trueConstant : falseConstant);
// ExpressionStatement callStmt = new ExpressionStatement() {
// Expression = setCheckStateCall,
// };
// code.Add(callStmt);
// return code;
return new List();
}
public override void Visit(IBlockStatement block) {
foreach (IStatement statement in block.Statements) {
this.Visit(statement);
if (initializationFound) {
injectPhoneInitializationCode(block as BlockStatement, statement as Statement);
initializationFound = false;
break;
}
}
}
public override void Visit(IMethodCall methodCall) {
if (methodCall.IsStaticCall ||
!methodCall.MethodToCall.ContainingType.ResolvedType.Equals(methodBeingTraversed.Container) ||
methodCall.MethodToCall.Name.Value != "InitializeComponent" ||
methodCall.Arguments.Any())
return;
initializationFound= true;
}
}
///
/// Traverse metadata looking only for PhoneApplicationPage's constructors
///
public class PhoneInitializationMetadataTraverser : BaseMetadataTraverser {
private MetadataReaderHost host;
public PhoneInitializationMetadataTraverser(MetadataReaderHost host)
: base() {
this.host = host;
}
public override void Visit(IModule module) {
base.Visit(module);
}
public override void Visit(IAssembly assembly) {
base.Visit(assembly);
}
///
/// Check if the type being defined is a PhoneApplicationPage, uninteresting otherwise
///
///
public override void Visit(ITypeDefinition typeDefinition) {
if (typeDefinition.isPhoneApplicationClass(host)) {
PhoneCodeHelper.instance().setMainAppTypeReference(typeDefinition);
} else if (typeDefinition.isPhoneApplicationPageClass(host)) {
base.Visit(typeDefinition);
}
}
///
/// Check if it is traversing a constructor. If so, place necessary code after InitializeComponent() call
///
public override void Visit(IMethodDefinition method) {
if (!method.IsConstructor)
return;
PhoneInitializationCodeTraverser codeTraverser = new PhoneInitializationCodeTraverser(host, method);
var methodBody = method.Body as SourceMethodBody;
if (methodBody == null)
return;
var block = methodBody.Block as BlockStatement;
codeTraverser.injectPhoneControlsCode(block);
}
public void InjectPhoneCodeAssemblies(IEnumerable assemblies) {
foreach (var a in assemblies) {
a.Dispatch(this);
}
}
}
}