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