// (C) Copyright 2001 Samuele Pedroni package org.python.compiler; import java.util.*; import org.python.parser.SimpleNode; public class ScopeInfo extends Object implements ScopeConstants { public SimpleNode scope_node; public String scope_name; public int level; public int func_level; public int list_comprehension_count; public void dump() { // for debugging if (org.python.core.Options.verbose < org.python.core.Py.DEBUG) return; for (int i = 0; i < level; i++) System.err.print(' '); System.err.print(((kind != CLASSSCOPE) ? scope_name : "class " + scope_name) + ": "); for (Enumeration e = tbl.keys(); e.hasMoreElements();) { String name = (String) e.nextElement(); SymInfo info = (SymInfo) tbl.get(name); int flags = info.flags; System.err.print(name); if ((flags & BOUND) != 0) System.err.print('='); // func scope global (affect nested scopes) // vs. class scope global if ((flags & NGLOBAL) != 0) System.err.print('G'); else if ((flags & CLASS_GLOBAL) != 0) System.err.print('g'); if ((flags & PARAM) != 0) System.err.print('P'); else if ((flags & FROM_PARAM) != 0) System.err.print('p'); if ((flags & CELL) != 0) System.err.print('!'); if ((flags & FREE) != 0) System.err.print(",f"); System.err.print(" "); } System.err.println(); } public ScopeInfo(String name, SimpleNode node, int level, int kind, int func_level, ArgListCompiler ac) { scope_name = name; scope_node = node; this.level = level; this.kind = kind; this.func_level = func_level; this.ac = ac; } public int kind; public boolean unqual_exec; public boolean exec; public boolean from_import_star; public boolean generator; public int yield_count; public ArgListCompiler ac; public Hashtable tbl = new Hashtable(); public Vector names = new Vector(); public int addGlobal(String name) { // global kind = func vs. class int global = kind == CLASSSCOPE ? CLASS_GLOBAL : NGLOBAL; SymInfo info = (SymInfo) tbl.get(name); if (info == null) { tbl.put(name, new SymInfo(global | BOUND)); return -1; } int prev = info.flags; info.flags |= global | BOUND; return prev; } public int local = 0; public void addParam(String name) { //System.out.println("addParam " + name); tbl.put(name, new SymInfo(PARAM | BOUND, local++)); names.addElement(name); } public void markFromParam() { for (Enumeration e = tbl.elements(); e.hasMoreElements();) { SymInfo info = (SymInfo) e.nextElement(); info.flags |= FROM_PARAM; } } public void addBound(String name) { SymInfo info = (SymInfo) tbl.get(name); if (info == null) { tbl.put(name, new SymInfo(BOUND)); return; } info.flags |= BOUND; } public void addUsed(String name) { if (tbl.get(name) == null) { tbl.put(name, new SymInfo(0)); return; } } private final static Object PRESENT = new Object(); public Hashtable inner_free = new Hashtable(); public Vector cellvars = new Vector(); public Vector jy_paramcells = new Vector(); public int jy_npurecell; public int cell, distance; public ScopeInfo up; //Resolve the names used in the given scope, and mark any freevars used in the up scope public void cook(ScopeInfo up, int distance, CompilationContext ctxt) throws Exception { if (up == null) return; // top level => nop this.up = up; this.distance = distance; boolean func = kind == FUNCSCOPE; Vector purecells = new Vector(); cell = 0; boolean some_inner_free = inner_free.size() > 0; for (Enumeration e = inner_free.keys(); e.hasMoreElements();) { String name = (String) e.nextElement(); SymInfo info = (SymInfo) tbl.get(name); if (info == null) { tbl.put(name, new SymInfo(FREE)); continue; } int flags = info.flags; if (func) { // not func global and bound ? if ((flags & NGLOBAL) == 0 && (flags & BOUND) != 0) { info.flags |= CELL; if ((info.flags & PARAM) != 0) jy_paramcells.addElement(name); cellvars.addElement(name); info.env_index = cell++; if ((flags & PARAM) == 0) purecells.addElement(name); continue; } } else { info.flags |= FREE; } } boolean some_free = false; boolean nested = up.kind != TOPSCOPE; for (Enumeration e = tbl.keys(); e.hasMoreElements();) { String name = (String) e.nextElement(); SymInfo info = (SymInfo) tbl.get(name); int flags = info.flags; if (nested && (flags & FREE) != 0) up.inner_free.put(name, PRESENT); if ((flags & (GLOBAL | PARAM | CELL)) == 0) { if ((flags & BOUND) != 0) { // ?? only func // System.err.println("local: "+name); names.addElement(name); info.locals_index = local++; continue; } info.flags |= FREE; some_free = true; if (nested) up.inner_free.put(name, PRESENT); } } if ((jy_npurecell = purecells.size()) > 0) { int sz = purecells.size(); for (int i = 0; i < sz; i++) { names.addElement(purecells.elementAt(i)); } } if ((unqual_exec || from_import_star)) { if (some_inner_free) dynastuff_trouble(true, ctxt); else if (func_level > 1 && some_free) dynastuff_trouble(false, ctxt); } } private void dynastuff_trouble(boolean inner_free, CompilationContext ctxt) throws Exception { String illegal; if (unqual_exec && from_import_star) illegal = "function '" + scope_name + "' uses import * and bare exec, which are illegal"; else if (unqual_exec) illegal = "unqualified exec is not allowed in function '" + scope_name + "'"; else illegal = "import * is not allowed in function '" + scope_name + "'"; String why; if (inner_free) why = " because it contains a function with free variables"; else why = " because it contains free variables"; ctxt.error(illegal + why, true, scope_node); } public Vector freevars = new Vector(); /** * setup the closure on this scope using the scope passed into cook as up as * the containing scope */ public void setup_closure() { setup_closure(up); } /** * setup the closure on this scope using the passed in scope. This is used * by jythonc to setup its closures. */ public void setup_closure(ScopeInfo up) { int free = cell; // env = cell...,free... Hashtable up_tbl = up.tbl; boolean nested = up.kind != TOPSCOPE; for (Enumeration e = tbl.keys(); e.hasMoreElements();) { String name = (String) e.nextElement(); SymInfo info = (SymInfo) tbl.get(name); int flags = info.flags; if ((flags & FREE) != 0) { SymInfo up_info = (SymInfo) up_tbl.get(name); // ?? differs from CPython -- what is the intended behaviour? if (up_info != null) { int up_flags = up_info.flags; if ((up_flags & (CELL | FREE)) != 0) { info.env_index = free++; freevars.addElement(name); continue; } // ! func global affect nested scopes if (nested && (up_flags & NGLOBAL) != 0) { info.flags = NGLOBAL | BOUND; continue; } } info.flags &= ~FREE; } } } public String toString() { return "ScopeInfo[" + scope_name + " " + kind + "]@" + System.identityHashCode(this); } }