Context.java |
1 // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas 2 3 package jminusminus; 4 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.Map; 8 import java.util.Set; 9 10 /** 11 * A Context encapsulates the environment in which an AST is analyzed. It represents a scope; the 12 * scope of a variable is captured by its context. It's the symbol table. 13 * <p> 14 * Because scopes are lexically nested in Java (and so in j--), the environment can be seen as a 15 * stack of contexts, each of which is a mapping from names to their definitions (IDefns). A 16 * Context keeps track of its (most closely) surrounding context, its surrounding class context, 17 * and its surrounding compilation unit context, and as well as a map from names to definitions 18 * in the level of scope that the Context represents. Contexts are created for the compilation 19 * unit (a CompilationUnitContext), a class (a ClassContext), each method (a MethodContext), and 20 * each block (a LocalContext). If we were to add the for-statement to j--, we would necessarily 21 * create a (local) context. 22 * <p> 23 * From the outside, the structure looks like a tree strung over the AST. But from any location 24 * on the AST, that is from any point along a particular branch, it looks like a stack of context 25 * objects leading back to the root of the AST, that is, back to the JCompilationUnit object at 26 * the root. 27 * <p> 28 * Part of this structure is built during pre-analysis; pre-analysis reaches only into the type 29 * (for example a class) declaration for typing the members; pre-analysis does not reach into the 30 * method bodies. The rest of it is built during analysis. 31 */ 32 class Context { 33 /** 34 * The surrounding context (scope). 35 */ 36 protected Context surroundingContext; 37 38 /** 39 * The surrounding class context. 40 */ 41 protected ClassContext classContext; 42 43 /** 44 * The compilation unit context (for the whole source program or file). 45 */ 46 protected CompilationUnitContext compilationUnitContext; 47 48 /** 49 * Map of (local variable, formal parameters, type) names to their definitions. 50 */ 51 protected Map<String, IDefn> entries; 52 53 /** 54 * Constructs a Context. 55 * 56 * @param surrounding the surrounding context (scope). 57 * @param classContext the surrounding class context. 58 * @param compilationUnitContext the compilation unit context (for the whole source program or 59 * file). 60 */ 61 protected Context(Context surrounding, ClassContext classContext, 62 CompilationUnitContext compilationUnitContext) { 63 this.surroundingContext = surrounding; 64 this.classContext = classContext; 65 this.compilationUnitContext = compilationUnitContext; 66 this.entries = new HashMap<String, IDefn>(); 67 } 68 69 /** 70 * Adds an entry to the symbol table, binding a name to its definition in the current context. 71 * 72 * @param line the line number of the entry. 73 * @param name the name being declared. 74 * @param definition and its definition. 75 */ 76 public void addEntry(int line, String name, IDefn definition) { 77 if (entries.containsKey(name)) { 78 JAST.compilationUnit.reportSemanticError(line, "redefining name: " + name); 79 } else { 80 entries.put(name, definition); 81 } 82 } 83 84 /** 85 * Returns the definition for a name in the current (or surrounding) context, or null. 86 * 87 * @param name the name whose definition we're looking for. 88 * @return the definition for a name in the current (or surrounding) context, or null. 89 */ 90 public IDefn lookup(String name) { 91 IDefn iDefn = (IDefn) entries.get(name); 92 return iDefn != null ? 93 iDefn : surroundingContext != null ? surroundingContext.lookup(name) : null; 94 } 95 96 /** 97 * Returns the definition for a type name in the compilation unit context, or null. 98 * 99 * @param name the name of the type whose definition we're looking for. 100 * @return the definition for a type name in the compilation unit context, or null. 101 */ 102 public Type lookupType(String name) { 103 TypeNameDefn defn = (TypeNameDefn) compilationUnitContext.lookup(name); 104 return defn == null ? null : defn.type(); 105 } 106 107 /** 108 * Adds the given type to the compilation unit context. 109 * 110 * @param line line number of type declaration. 111 * @param type the type we are declaring. 112 */ 113 public void addType(int line, Type type) { 114 IDefn iDefn = new TypeNameDefn(type); 115 compilationUnitContext.addEntry(line, type.simpleName(), iDefn); 116 if (!type.toString().equals(type.simpleName())) { 117 compilationUnitContext.addEntry(line, type.toString(), iDefn); 118 } 119 } 120 121 /** 122 * Returns the type that defines this context (used principally for checking accessibility). 123 * 124 * @return the type that defines this context. 125 */ 126 public Type definingType() { 127 return ((JTypeDecl) classContext.definition()).thisType(); 128 } 129 130 /** 131 * Returns the surrounding context (scope) in the stack of contexts. 132 * 133 * @return the surrounding context. 134 */ 135 public Context surroundingContext() { 136 return surroundingContext; 137 } 138 139 /** 140 * Returns the surrounding class context. 141 * 142 * @return the surrounding class context. 143 */ 144 public ClassContext classContext() { 145 return classContext; 146 } 147 148 /** 149 * Returns the surrounding compilation unit context. This is where imported types and other 150 * types defined in the compilation unit are declared. 151 * 152 * @return the compilation unit context. 153 */ 154 public CompilationUnitContext compilationUnitContext() { 155 return compilationUnitContext; 156 } 157 158 /** 159 * Returns the closest surrounding method context, or null (if we are not within a method). 160 * 161 * @return the closest surrounding method context, or null. 162 */ 163 public MethodContext methodContext() { 164 Context context = this; 165 while (context != null && !(context instanceof MethodContext)) { 166 context = context.surroundingContext(); 167 } 168 return (MethodContext) context; 169 } 170 171 /** 172 * Returns a set containing the names declared in this context. 173 * 174 * @return a set containing the names declared in this context. 175 */ 176 public Set<String> names() { 177 return entries.keySet(); 178 } 179 180 /** 181 * Adds information pertaining to this context to the given JSON element. 182 * 183 * @param json JSON element. 184 */ 185 public void toJSON(JSONElement json) { 186 // Nothing here. 187 } 188 } 189 190 /** 191 * The compilation unit context is always the outermost context and is where imported types and 192 * locally defined types (classes) are declared. 193 */ 194 class CompilationUnitContext extends Context { 195 /** 196 * Constructs a new compilation unit context. 197 */ 198 public CompilationUnitContext() { 199 super(null, null, null); 200 compilationUnitContext = this; 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 public void toJSON(JSONElement json) { 207 JSONElement e = new JSONElement(); 208 json.addChild("CompilationUnitContext", e); 209 if (entries != null) { 210 ArrayList<String> value = new ArrayList<String>(); 211 for (String name : names()) { 212 value.add(String.format("\"%s\"", name)); 213 } 214 e.addAttribute("entries", value); 215 } 216 } 217 } 218 219 /** 220 * Represents the context (scope, environment, symbol table) for a type, for example a class, in 221 * j--. It also keeps track of its surrounding context(s) and the type whose context it represents. 222 */ 223 class ClassContext extends Context { 224 /** 225 * AST node of the type that this class represents. 226 */ 227 private JAST definition; 228 229 /** 230 * Constructs a class context. 231 * 232 * @param definition the AST node of the type that this class represents. 233 * @param surrounding the surrounding context(s). 234 */ 235 public ClassContext(JAST definition, Context surrounding) { 236 super(surrounding, null, surrounding.compilationUnitContext()); 237 classContext = this; 238 this.definition = definition; 239 } 240 241 /** 242 * Returns the AST node of the type defined by this class. 243 * 244 * @return the AST of the type defined by this class. 245 */ 246 public JAST definition() { 247 return definition; 248 } 249 250 /** 251 * {@inheritDoc} 252 */ 253 public void toJSON(JSONElement json) { 254 JSONElement e = new JSONElement(); 255 json.addChild("ClassContext", e); 256 } 257 } 258 259 /** 260 * A local context is a context (scope) in which local variables (including formal parameters) 261 * can be declared. Local variables are allocated at fixed offsets from the base of the current 262 * method's stack frame; this is done during analysis. The definitions for local variables record 263 * these offsets. The offsets are used in code generation. 264 */ 265 class LocalContext extends Context { 266 /** 267 * Next offset for a local variable. 268 */ 269 protected int offset; 270 271 /** 272 * Constructs a local context. A local context is constructed for each block. 273 * 274 * @param surrounding the surrounding context. 275 */ 276 public LocalContext(Context surrounding) { 277 super(surrounding, surrounding.classContext(), surrounding.compilationUnitContext()); 278 offset = (surrounding instanceof LocalContext) ? ((LocalContext) surrounding).offset() : 0; 279 } 280 281 /** 282 * Returns the "next" offset. Not to be used for allocating new offsets (the nextOffset() 283 * method is used for that). 284 * 285 * @return the next available offset. 286 */ 287 public int offset() { 288 return offset; 289 } 290 291 /** 292 * Allocates and returns a new offset (eg, for a parameter or local variable). 293 * 294 * @return the next allocated offset. 295 */ 296 public int nextOffset() { 297 return offset++; 298 } 299 300 /** 301 * {@inheritDoc} 302 */ 303 public void toJSON(JSONElement json) { 304 JSONElement e = new JSONElement(); 305 json.addChild("LocalContext", e); 306 if (entries != null) { 307 ArrayList<String> value = new ArrayList<String>(); 308 for (String name : names()) { 309 IDefn defn = entries.get(name); 310 if (defn instanceof LocalVariableDefn) { 311 int offset = ((LocalVariableDefn) defn).offset(); 312 value.add(String.format("[\"%s\", \"%s\"]", name, offset)); 313 } 314 } 315 e.addAttribute("entries", value); 316 } 317 } 318 } 319 320 /** 321 * A method context is where formal parameters are declared. Also, it's where we start computing 322 * the offsets for local variables (formal parameters included), which are allocated in the 323 * current stack frame (for a method invocation). 324 */ 325 class MethodContext extends LocalContext { 326 /** 327 * Is this method static? 328 */ 329 private boolean isStatic; 330 331 /** 332 * Return type of this method. 333 */ 334 private Type methodReturnType; 335 336 /** 337 * Does (non-void) method have at least one return? 338 */ 339 private boolean hasReturnStatement; 340 341 /** 342 * Constructs a method context. 343 * 344 * @param surrounding the surrounding (class) context. 345 * @param isStatic is this method static? 346 * @param methodReturnType return type of this method. 347 */ 348 public MethodContext(Context surrounding, boolean isStatic, Type methodReturnType) { 349 super(surrounding); 350 super.offset = 0; 351 this.isStatic = isStatic; 352 this.methodReturnType = methodReturnType; 353 hasReturnStatement = false; 354 } 355 356 /** 357 * Returns true if this is a static method, and false otherwise. 358 * 359 * @return true if this is a static method, and false otherwise. 360 */ 361 public boolean isStatic() { 362 return isStatic; 363 } 364 365 /** 366 * Records fact that (non-void) method has at least one return. 367 */ 368 public void confirmMethodHasReturn() { 369 hasReturnStatement = true; 370 } 371 372 /** 373 * Returns true if this (non-void) method has at least one return, and false otherwise. 374 * 375 * @return true if this (non-void) method has at least one return, and false otherwise. 376 */ 377 public boolean methodHasReturn() { 378 return hasReturnStatement; 379 } 380 381 /** 382 * Returns the return type of this method. 383 * 384 * @return the return type of this method. 385 */ 386 public Type methodReturnType() { 387 return methodReturnType; 388 } 389 390 /** 391 * {@inheritDoc} 392 */ 393 public void toJSON(JSONElement json) { 394 JSONElement e = new JSONElement(); 395 json.addChild("MethodContext", e); 396 if (entries != null) { 397 ArrayList<String> value = new ArrayList<String>(); 398 for (String name : names()) { 399 IDefn defn = entries.get(name); 400 if (defn instanceof LocalVariableDefn) { 401 int offset = ((LocalVariableDefn) defn).offset(); 402 value.add(String.format("[\"%s\", \"%s\"]", name, offset)); 403 } 404 } 405 e.addAttribute("entries", value); 406 } 407 } 408 } 409