1   // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.util.ArrayList;
6   
7   import static jminusminus.CLConstants.*;
8   
9   /**
10   * The AST node for a message expression that has a target, optionally an ambiguous part, a
11   * message name, and zero or more actual arguments.
12   */
13  class JMessageExpression extends JExpression {
14      // The target expression.
15      private JExpression target;
16  
17      // The ambiguous part that is reclassfied in analyze().
18      private AmbiguousName ambiguousPart;
19  
20      // The message name.
21      private String messageName;
22  
23      // Message arguments.
24      private ArrayList<JExpression> arguments;
25  
26      // Types of arguments.
27      private Type[] argTypes;
28  
29      // The Method representing this message.
30      private Method method;
31  
32      /**
33       * Constructs an AST node for a message expression without an ambiguous part.
34       *
35       * @param line        line in which the expression occurs in the source file.
36       * @param target      the target expression.
37       * @param messageName the message name.
38       * @param arguments   the arguments.
39       */
40      public JMessageExpression(int line, JExpression target, String messageName,
41                                ArrayList<JExpression> arguments) {
42          this(line, target, null, messageName, arguments);
43      }
44  
45      /**
46       * Constructs an AST node for a message expression having an ambiguous part.
47       *
48       * @param line          line in which the expression occurs in the source file.
49       * @param target        the target expression.
50       * @param ambiguousPart the ambiguous part.
51       * @param messageName   the message name.
52       * @param arguments     the arguments.
53       */
54      public JMessageExpression(int line, JExpression target, AmbiguousName ambiguousPart,
55                                String messageName, ArrayList<JExpression> arguments) {
56          super(line);
57          this.target = target;
58          this.ambiguousPart = ambiguousPart;
59          this.messageName = messageName;
60          this.arguments = arguments;
61      }
62  
63      /**
64       * {@inheritDoc}
65       */
66      public JExpression analyze(Context context) {
67          // Reclassify the ambiguous part.
68          if (ambiguousPart != null) {
69              JExpression expr = ambiguousPart.reclassify(context);
70              if (expr != null) {
71                  if (target == null) {
72                      target = expr;
73                  } else {
74                      // Can't even happen syntactically.
75                      JAST.compilationUnit.reportSemanticError(line(), "Badly formed suffix");
76                  }
77              }
78          }
79  
80          // Then analyze the arguments, collecting their types (in Class form) as argTypes.
81          argTypes = new Type[arguments.size()];
82          for (int i = 0; i < arguments.size(); i++) {
83              arguments.set(i, (JExpression) arguments.get(i).analyze(context));
84              argTypes[i] = arguments.get(i).type();
85          }
86  
87          // Where are we now? (For access)
88          Type thisType = ((JTypeDecl) context.classContext.definition()).thisType();
89  
90          // Then analyze the target.
91          if (target == null) {
92              // Implied this (or, implied type for statics).
93              if (!context.methodContext().isStatic()) {
94                  target = new JThis(line()).analyze(context);
95              } else {
96                  target = new JVariable(line(), context.definingType().toString()).analyze(context);
97              }
98          } else {
99              target = (JExpression) target.analyze(context);
100             if (target.type().isPrimitive()) {
101                 JAST.compilationUnit.reportSemanticError(line(),
102                         "Cannot invoke a message on a primitive type: " + target.type());
103             }
104         }
105 
106         // Find appropriate Method for this message expression.
107         method = target.type().methodFor(messageName, argTypes);
108         if (method == null) {
109             JAST.compilationUnit.reportSemanticError(line(),
110                     "Cannot find method for: " + Type.signatureFor(messageName, argTypes));
111             type = Type.ANY;
112         } else {
113             context.definingType().checkAccess(line, (Member) method);
114             type = method.returnType();
115 
116             // Non-static method cannot be referenced from a static context.
117             if (!method.isStatic()) {
118                 if (target instanceof JVariable &&
119                         ((JVariable) target).iDefn() instanceof TypeNameDefn) {
120                     JAST.compilationUnit.reportSemanticError(line(),
121                             "Non-static method " + Type.signatureFor(messageName, argTypes) +
122                                     " cannot be referenced from a static context");
123                 }
124             }
125         }
126         return this;
127     }
128 
129     /**
130      * {@inheritDoc}
131      */
132     public void codegen(CLEmitter output) {
133         if (!method.isStatic()) {
134             target.codegen(output);
135         }
136         for (JExpression argument : arguments) {
137             argument.codegen(output);
138         }
139         int mnemonic = method.isStatic() ? INVOKESTATIC : target.type().isInterface() ?
140                 INVOKEINTERFACE : INVOKEVIRTUAL;
141         output.addMemberAccessInstruction(mnemonic, target.type().jvmName(), messageName,
142                 method.toDescriptor());
143         if (isStatementExpression && type != Type.VOID) {
144             // Pop any value left on the stack.
145             output.addNoArgInstruction(POP);
146         }
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     public void codegen(CLEmitter output, String targetLabel, boolean onTrue) {
153         codegen(output);
154         if (onTrue) {
155             output.addBranchInstruction(IFNE, targetLabel);
156         } else {
157             output.addBranchInstruction(IFEQ, targetLabel);
158         }
159     }
160 
161     /**
162      * {@inheritDoc}
163      */
164     public void toJSON(JSONElement json) {
165         JSONElement e = new JSONElement();
166         json.addChild("JMessageExpression:" + line, e);
167         e.addAttribute("ambiguousPart", ambiguousPart == null ? "null" : ambiguousPart.toString());
168         e.addAttribute("name", messageName);
169         if (target != null) {
170             JSONElement e1 = new JSONElement();
171             e.addChild("Target", e1);
172             target.toJSON(e1);
173         }
174         if (arguments != null) {
175             for (JExpression argument : arguments) {
176                 JSONElement e1 = new JSONElement();
177                 e.addChild("Argument", e1);
178                 argument.toJSON(e1);
179             }
180         }
181     }
182 }
183