summaryrefslogtreecommitdiff
path: root/BCT/BytecodeTranslator/Phone/PhoneBackKeyCallbackTraverser.cs
blob: 026cf30138088a762ca9aa9c57c9fae65ca78dde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci;
using Microsoft.Cci.MutableCodeModel;

namespace BytecodeTranslator.Phone {
  class PhoneBackKeyCallbackTraverser : BaseCodeTraverser {
    private ITypeReference typeBeingTraversed;
    private IMetadataHost host;

    public PhoneBackKeyCallbackTraverser(IMetadataHost host) {
      this.host = host;
    }

    public override void Visit(ITypeDefinition typeDef) {
      typeBeingTraversed = typeDef;
      base.Visit(typeDef);
    }
    
    public override void Visit(IMethodCall methodCall) {
      if (methodCall.MethodToCall.ResolvedMethod.IsSpecialName && methodCall.MethodToCall.Name.Value == "add_BackKeyPress") {
        // check if it is a back key handler and if it is...
        // NAVIGATION TODO this only catches really locally delegate expressions. If it is created before, we see it as a BoundExpression
        // NAVIGATION TODO and need (again) data flow analysis
        bool delegateIsIdentified= false;
        bool delegateIsAnonymous = false;
        PhoneCodeHelper.instance().OnBackKeyPressOverriden = true;
        IBlockStatement delegateBody = null;
        IMethodDefinition delegateMethodRef= null;
        if (methodCall.Arguments.Count() == 1) {
          IExpression delegateArg= methodCall.Arguments.First();
          ICreateDelegateInstance localDelegate = delegateArg as ICreateDelegateInstance;
          if (localDelegate != null) {
            delegateIsIdentified = true;
            delegateMethodRef = localDelegate.MethodToCallViaDelegate.ResolvedMethod;
            SourceMethodBody body= delegateMethodRef.Body as SourceMethodBody;
            if (body != null)
              delegateBody = body.Block;

            PhoneCodeHelper.instance().KnownBackKeyHandlers.Add(delegateMethodRef);
          }

          AnonymousDelegate anonDelegate = delegateArg as AnonymousDelegate;
          if (anonDelegate != null) {
            delegateIsIdentified = true;
            delegateIsAnonymous = true;
            delegateBody = anonDelegate.Body;
          }

          // NAVIGATION TODO look for reachable method calls

          // NAVIGATION TODO what if it has no body?
          if (delegateBody != null) {
            bool navigates= false, cancelsNav= false;
            ICollection<string> navTargets;
            parseBlockForNavigation(delegateBody, out navigates, out navTargets);
            if (navigates) {
              ICollection<Tuple<IMethodReference,string>> targets = null;
              if (!PhoneCodeHelper.instance().BackKeyNavigatingOffenders.TryGetValue(typeBeingTraversed, out targets)) {
                targets = new HashSet<Tuple<IMethodReference,string>>();
              }

              foreach (string tgt in navTargets) {
                IMethodReference dummyRef=null;
                if (delegateIsAnonymous) {
                  dummyRef = new Microsoft.Cci.MutableCodeModel.MethodReference();
                  (dummyRef as Microsoft.Cci.MutableCodeModel.MethodReference).ContainingType= typeBeingTraversed;
                  (dummyRef as Microsoft.Cci.MutableCodeModel.MethodReference).Name = Dummy.Name;
                }
                targets.Add(Tuple.Create<IMethodReference, string>((delegateIsAnonymous ? dummyRef : delegateMethodRef), "\"" + tgt + "\""));
              }

              PhoneCodeHelper.instance().BackKeyNavigatingOffenders[typeBeingTraversed] = targets;
            }

            parseBlockForEventCancellation(delegateBody, out cancelsNav);
            if (cancelsNav) {
              string reason= "(via delegate ";
              if (delegateIsIdentified)
                reason += delegateMethodRef.ContainingType.ToString() + "." + delegateMethodRef.Name.Value;
              else
                reason += "anonymous";
              reason += ")";
              PhoneCodeHelper.instance().BackKeyCancellingOffenders.Add(Tuple.Create<ITypeReference,string>(typeBeingTraversed, reason));
            }
          }
        }

        if (!delegateIsIdentified) {
          PhoneCodeHelper.instance().BackKeyHandlerOverridenByUnknownDelegate = true;
          PhoneCodeHelper.instance().BackKeyUnknownDelegateOffenders.Add(typeBeingTraversed);
        }
      }
      base.Visit(methodCall);
    }

    private void parseBlockForNavigation(IBlockStatement block, out bool navigates, out ICollection<string> navTargets) {
      PhoneNavigationCallsTraverser traverser = new PhoneNavigationCallsTraverser(host);
      traverser.Visit(block);
      navigates = traverser.CodeDoesNavigation;
      navTargets = traverser.NavigationTargets;
    }

    private void parseBlockForEventCancellation(IBlockStatement block, out bool cancels) {
      PhoneNavigationCallsTraverser traverser = new PhoneNavigationCallsTraverser(host);
      traverser.Visit(block);
      cancels = traverser.CancelsEvents;
    }
  }

  public class PhoneNavigationCallsTraverser : BaseCodeTraverser {
    private IMetadataHost host;

    public bool CancelsEvents { get; private set; }
    public bool CodeDoesNavigation { get; private set; }
    public ICollection<string> NavigationTargets { get; private set; }
    public PhoneNavigationCallsTraverser(IMetadataHost host) {
      CancelsEvents = CodeDoesNavigation = false;
      NavigationTargets = new HashSet<string>();
      this.host = host;
    }

    public override void Visit(IMethodCall call) {
      checkMethodCallForEventCancellation(call);
      checkMethodCallForNavigation(call);
    }

    private void checkMethodCallForEventCancellation(IMethodCall call) {
      // NAVIGATION TODO this code is duplicated from PhoneNavigationTraverser, refactor that
      if (!call.MethodToCall.Name.Value.StartsWith("set_Cancel"))
        return;

      if (call.Arguments.Count() != 1 || call.Arguments.ToList()[0].Type != host.PlatformType.SystemBoolean)
        return;

      ICompileTimeConstant constant = call.Arguments.ToList()[0] as ICompileTimeConstant;
      if (constant != null && constant.Value != null) {
        CompileTimeConstant falseConstant = new CompileTimeConstant() {
          Type = host.PlatformType.SystemBoolean,
          Value = false,
        };
        if (constant.Value == falseConstant.Value)
          return;
      }

      CancelsEvents = true;
    }

    private void checkMethodCallForNavigation(IMethodCall call) {
      // NAVIGATION TODO this code is duplicated from PhoneNavigationTraverser, refactor that
      string targetUri = null;
      if (!call.MethodToCall.ContainingType.isNavigationServiceClass(host))
        return;

      if (!PhoneCodeHelper.NAV_CALLS.Contains(call.MethodToCall.Name.Value) || call.MethodToCall.Name.Value == "GoBack") // back is actually ok
        return;

      if (call.MethodToCall.Name.Value == "Navigate") {
        try {
          IExpression expr = call.Arguments.First();
          bool isStatic = UriHelper.isArgumentURILocallyCreatedStatic(expr, host, out targetUri) ||
                         UriHelper.isArgumentURILocallyCreatedStaticRoot(expr, host, out targetUri);
          if (!isStatic)
            targetUri = "--Other non inferrable target--";
          else
            targetUri = UriHelper.getURIBase(targetUri);
        } catch (InvalidOperationException) {
        }
      }
      CodeDoesNavigation= true;
      NavigationTargets.Add(targetUri);
    }
  }
}