Commit bbadf8a6 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #407 from tjhance/locals-globals

parents 55805dc6 d3e045a0
...@@ -105,6 +105,70 @@ public: ...@@ -105,6 +105,70 @@ public:
bool isPassedToViaClosure(InternedString name) override { return false; } bool isPassedToViaClosure(InternedString name) override { return false; }
bool areLocalsFromModule() override { return true; }
InternedString mangleName(InternedString id) override { return id; }
InternedString internString(llvm::StringRef s) override { abort(); }
};
typedef llvm::DenseSet<InternedString> StrSet;
// Handles the scope in eval or exec
// For example for exec, if you write
// exec "global a ; print a ; print b"
// It will give `a` the GLOBAL scope type and `b` the NAME type.
// (For eval, you can't have global statements, so it will just
// mark everything NAME.)
class EvalExprScopeInfo : public ScopeInfo {
private:
StrSet forced_globals;
struct GlobalStmtVisitor : NoopASTVisitor {
StrSet& result;
GlobalStmtVisitor(StrSet& result) : result(result) {}
bool visit_functiondef(AST_FunctionDef*) override { return true; }
bool visit_classdef(AST_ClassDef*) override { return true; }
bool visit_global(AST_Global* global_stmt) override {
for (InternedString name : global_stmt->names) {
result.insert(name);
}
return true;
}
};
public:
EvalExprScopeInfo() {}
EvalExprScopeInfo(AST* node) {
// Find all the global statements in the node's scope (not delving into FuncitonDefs
// or ClassDefs) and put the names in `forced_globals`.
GlobalStmtVisitor visitor(forced_globals);
node->accept(&visitor);
}
ScopeInfo* getParent() override { return NULL; }
bool createsClosure() override { return false; }
bool takesClosure() override { return false; }
bool passesThroughClosure() override { return false; }
VarScopeType getScopeTypeOfName(InternedString name) override {
if (isCompilerCreatedName(name))
return VarScopeType::FAST;
else if (forced_globals.find(name) != forced_globals.end())
return VarScopeType::GLOBAL;
else
return VarScopeType::NAME;
}
bool usesNameLookup() override { return true; }
bool isPassedToViaClosure(InternedString name) override { return false; }
bool areLocalsFromModule() override { return false; }
InternedString mangleName(InternedString id) override { return id; } InternedString mangleName(InternedString id) override { return id; }
InternedString internString(llvm::StringRef s) override { abort(); } InternedString internString(llvm::StringRef s) override { abort(); }
}; };
...@@ -115,8 +179,6 @@ struct ScopingAnalysis::ScopeNameUsage { ...@@ -115,8 +179,6 @@ struct ScopingAnalysis::ScopeNameUsage {
const std::string* private_name; const std::string* private_name;
ScopingAnalysis* scoping; ScopingAnalysis* scoping;
typedef llvm::DenseSet<InternedString> StrSet;
// Properties determined from crawling the scope: // Properties determined from crawling the scope:
StrSet read; StrSet read;
StrSet written; StrSet written;
...@@ -245,6 +307,8 @@ public: ...@@ -245,6 +307,8 @@ public:
return usage->got_from_closure.count(name) > 0 || usage->passthrough_accesses.count(name) > 0; return usage->got_from_closure.count(name) > 0 || usage->passthrough_accesses.count(name) > 0;
} }
bool areLocalsFromModule() override { return false; }
InternedString mangleName(const InternedString id) override { InternedString mangleName(const InternedString id) override {
return pyston::mangleName(id, usage->private_name, usage->scoping->getInternedStrings()); return pyston::mangleName(id, usage->private_name, usage->scoping->getInternedStrings());
} }
...@@ -644,8 +708,6 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) { ...@@ -644,8 +708,6 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
ScopeInfo* parent_info = this->scopes[(usage->parent == NULL) ? this->parent_module : usage->parent->node]; ScopeInfo* parent_info = this->scopes[(usage->parent == NULL) ? this->parent_module : usage->parent->node];
switch (node->type) { switch (node->type) {
case AST_TYPE::Expression:
case AST_TYPE::Suite:
case AST_TYPE::ClassDef: { case AST_TYPE::ClassDef: {
ScopeInfoBase* scopeInfo ScopeInfoBase* scopeInfo
= new ScopeInfoBase(parent_info, usage, usage->node, true /* usesNameLookup */); = new ScopeInfoBase(parent_info, usage, usage->node, true /* usesNameLookup */);
...@@ -719,12 +781,11 @@ ScopingAnalysis::ScopingAnalysis(AST_Module* m) : parent_module(m), interned_str ...@@ -719,12 +781,11 @@ ScopingAnalysis::ScopingAnalysis(AST_Module* m) : parent_module(m), interned_str
} }
ScopingAnalysis::ScopingAnalysis(AST_Expression* e) : interned_strings(*e->interned_strings.get()) { ScopingAnalysis::ScopingAnalysis(AST_Expression* e) : interned_strings(*e->interned_strings.get()) {
auto scope_info = getScopeInfoForNode(e); // It's an expression, so it can't have a `global` statement
scopes[e] = scope_info; scopes[e] = new EvalExprScopeInfo();
} }
ScopingAnalysis::ScopingAnalysis(AST_Suite* s) : interned_strings(*s->interned_strings.get()) { ScopingAnalysis::ScopingAnalysis(AST_Suite* s) : interned_strings(*s->interned_strings.get()) {
auto scope_info = getScopeInfoForNode(s); scopes[s] = new EvalExprScopeInfo(s);
scopes[s] = scope_info;
} }
} }
...@@ -114,6 +114,8 @@ public: ...@@ -114,6 +114,8 @@ public:
// `exec` or `eval` scope. // `exec` or `eval` scope.
virtual bool usesNameLookup() = 0; virtual bool usesNameLookup() = 0;
virtual bool areLocalsFromModule() = 0;
virtual InternedString mangleName(InternedString id) = 0; virtual InternedString mangleName(InternedString id) = 0;
virtual InternedString internString(llvm::StringRef) = 0; virtual InternedString internString(llvm::StringRef) = 0;
}; };
......
...@@ -1094,15 +1094,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) { ...@@ -1094,15 +1094,7 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
return Value(); return Value();
} }
case ScopeInfo::VarScopeType::NAME: { case ScopeInfo::VarScopeType::NAME: {
assert(frame_info.boxedLocals->cls == dict_cls); return boxedLocalsGet(frame_info.boxedLocals, node->id.c_str(), source_info->parent_module);
auto& d = static_cast<BoxedDict*>(frame_info.boxedLocals)->d;
auto it = d.find(boxString(node->id.str()));
if (it != d.end()) {
Box* value = it->second;
return value;
}
return getGlobal(source_info->parent_module, &node->id.str());
} }
default: default:
abort(); abort();
...@@ -1169,7 +1161,6 @@ Box* astInterpretFunctionEval(CompiledFunction* cf, Box* boxedLocals) { ...@@ -1169,7 +1161,6 @@ Box* astInterpretFunctionEval(CompiledFunction* cf, Box* boxedLocals) {
ASTInterpreter interpreter(cf); ASTInterpreter interpreter(cf);
interpreter.initArguments(0, NULL, NULL, NULL, NULL, NULL, NULL); interpreter.initArguments(0, NULL, NULL, NULL, NULL, NULL, NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dicts here yet");
interpreter.setBoxedLocals(boxedLocals); interpreter.setBoxedLocals(boxedLocals);
Value v = ASTInterpreter::execute(interpreter); Value v = ASTInterpreter::execute(interpreter);
......
...@@ -638,12 +638,20 @@ Box* fastLocalsToBoxedLocals() { ...@@ -638,12 +638,20 @@ Box* fastLocalsToBoxedLocals() {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) { for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
BoxedDict* d; BoxedDict* d;
BoxedClosure* closure; BoxedClosure* closure;
CompiledFunction* cf;
FrameInfo* frame_info; FrameInfo* frame_info;
CompiledFunction* cf = frame_iter.getCF();
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
if (scope_info->areLocalsFromModule()) {
// TODO we should cache this in frame_info->locals or something so that locals()
// (and globals() too) will always return the same dict
return makeAttrWrapper(getCurrentModule());
}
if (frame_iter.getId().type == PythonFrameId::COMPILED) { if (frame_iter.getId().type == PythonFrameId::COMPILED) {
d = new BoxedDict(); d = new BoxedDict();
cf = frame_iter.getCF();
uint64_t ip = frame_iter.getId().ip; uint64_t ip = frame_iter.getId().ip;
assert(ip > cf->code_start); assert(ip > cf->code_start);
...@@ -727,7 +735,6 @@ Box* fastLocalsToBoxedLocals() { ...@@ -727,7 +735,6 @@ Box* fastLocalsToBoxedLocals() {
} else if (frame_iter.getId().type == PythonFrameId::INTERPRETED) { } else if (frame_iter.getId().type == PythonFrameId::INTERPRETED) {
d = localsForInterpretedFrame((void*)frame_iter.getId().bp, true); d = localsForInterpretedFrame((void*)frame_iter.getId().bp, true);
closure = passedClosureForInterpretedFrame((void*)frame_iter.getId().bp); closure = passedClosureForInterpretedFrame((void*)frame_iter.getId().bp);
cf = getCFForInterpretedFrame((void*)frame_iter.getId().bp);
frame_info = getFrameInfoForInterpretedFrame((void*)frame_iter.getId().bp); frame_info = getFrameInfoForInterpretedFrame((void*)frame_iter.getId().bp);
} else { } else {
abort(); abort();
...@@ -747,7 +754,6 @@ Box* fastLocalsToBoxedLocals() { ...@@ -747,7 +754,6 @@ Box* fastLocalsToBoxedLocals() {
const std::string& name = attr_offset.first(); const std::string& name = attr_offset.first();
int offset = attr_offset.second; int offset = attr_offset.second;
Box* val = closure->attrs.attr_list->attrs[offset]; Box* val = closure->attrs.attr_list->attrs[offset];
ScopeInfo* scope_info = cf->clfunc->source->getScopeInfo();
if (val != NULL && scope_info->isPassedToViaClosure(scope_info->internString(name))) { if (val != NULL && scope_info->isPassedToViaClosure(scope_info->internString(name))) {
Box* boxedName = boxString(name); Box* boxedName = boxString(name);
if (d->d.count(boxedName) == 0) { if (d->d.count(boxedName) == 0) {
......
...@@ -4441,20 +4441,34 @@ Box* coerceUnicodeToStr(Box* unicode) { ...@@ -4441,20 +4441,34 @@ Box* coerceUnicodeToStr(Box* unicode) {
return r; return r;
} }
// TODO Make these fast, do inline caches and stuff
extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val) { extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val) {
setitem(boxedLocals, boxString(attr), val); setitem(boxedLocals, boxString(attr), val);
} }
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule* parent_module) { extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule* parent_module) {
assert(parent_module->cls == module_cls); assert(parent_module->cls == module_cls);
assert(boxedLocals != NULL); assert(boxedLocals != NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dict here yet");
auto& d = static_cast<BoxedDict*>(boxedLocals)->d; if (boxedLocals->cls == dict_cls) {
auto it = d.find(boxString(attr)); auto& d = static_cast<BoxedDict*>(boxedLocals)->d;
if (it != d.end()) { auto it = d.find(boxString(attr));
Box* value = it->second; if (it != d.end()) {
return value; Box* value = it->second;
return value;
}
} else {
try {
return getitem(boxedLocals, boxString(attr));
} catch (ExcInfo e) {
// TODO should check the exact semantic here but it's something like:
// If it throws a KeyError, then the variable doesn't exist so move on
// and check the globals (below); otherwise, just propogate the exception.
if (!isInstance(e.value, KeyError)) {
throw;
}
}
} }
// TODO exception name? // TODO exception name?
......
# TODO lots of eval functionality not implemented
print eval("3 + 4") print eval("3 + 4")
a = 5 a = 5
print eval("a") print eval("a")
#print eval("[b for b in range(5)]") print eval("[b for b in range(5)]")
#print b print b
#c = 2 c = 2
#print eval("[c for c in range(5)]") print eval("[c for c in range(5)]")
#print c print c
#try: try:
# print eval("int('abc')") print eval("int('abc')")
#except ValueError: except ValueError:
# print 'got ValueError' print 'got ValueError'
d = 19 d = 19
e = 20 e = 20
...@@ -85,10 +83,10 @@ o = 300 ...@@ -85,10 +83,10 @@ o = 300
print 'eval eval o', eval("eval('o')") print 'eval eval o', eval("eval('o')")
# This works in the global scope but not in the local scope, because o1 is a global: # This works in the global scope but not in the local scope, because o1 is a global:
#print eval("[(lambda p1 : p1 + o1)(5) for o1 in range(5)]") print eval("[(lambda p1 : p1 + o1)(5) for o1 in range(5)]")
def lambda_func(): def lambda_func():
try: try:
pass #print eval("[(lambda p2 : p2 + o2)(5) for o2 in range(5)]") print eval("[(lambda p2 : p2 + o2)(5) for o2 in range(5)]")
except NameError as e: except NameError as e:
print e.message print e.message
lambda_func() lambda_func()
...@@ -106,9 +104,9 @@ def func2(): ...@@ -106,9 +104,9 @@ def func2():
print 'shadow3', eval("shadow3 + sum([2 for shadow3 in range(5)]) + shadow3") print 'shadow3', eval("shadow3 + sum([2 for shadow3 in range(5)]) + shadow3")
func2() func2()
#print 'shadow1', shadow2 print 'shadow1', shadow2
#print 'shadow2', shadow2 print 'shadow2', shadow2
#print 'shadow3', shadow3 print 'shadow3', shadow3
def func3(): def func3():
...@@ -123,16 +121,12 @@ def func3(): ...@@ -123,16 +121,12 @@ def func3():
print 'NameError', e.message print 'NameError', e.message
func3() func3()
"""
changing_global = -1 changing_global = -1
def print_changing_global(): def print_changing_global():
print 'changing_global is', changing_global print 'changing_global is', changing_global
return 0 return 0
eval("[print_changing_global() for changing_global in range(5)]") eval("[print_changing_global() for changing_global in range(5)]")
"""
def do_changing_local(): def do_changing_local():
# this won't get modified: # this won't get modified:
changing_local = -1 changing_local = -1
......
...@@ -18,3 +18,8 @@ except NameError: ...@@ -18,3 +18,8 @@ except NameError:
# You're allowed to assign through globals and have it affect the module: # You're allowed to assign through globals and have it affect the module:
globals()['x'] = 1 globals()['x'] = 1
print x print x
# locals should do the same as globals
print locals()['x']
locals()['x'] = 2
print x
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment