Commit 85beb314 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add support for the globals() builtin

Works by using stack unwinding to find the most recent frame,
and then maps that back to the source information.
Soon we're going to need proper frame management (ex, this approach
does not work at for the locals() function), when this will be replaced.

globals() currently returns a non-dict... we'll see if that's ok.
parent fe8fa138
...@@ -121,6 +121,19 @@ static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) { ...@@ -121,6 +121,19 @@ static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) {
patchpoints::processStackmap(stackmap); patchpoints::processStackmap(stackmap);
} }
static std::unordered_map<std::string, CLFunction*> machine_name_to_clfunction;
CLFunction* clFunctionForMachineFunctionName(const std::string& machine_name) {
assert(machine_name.size());
auto r = machine_name_to_clfunction[machine_name];
ASSERT(r, "%s", machine_name.c_str());
return r;
}
void registerMachineName(const std::string& machine_name, CLFunction* cl) {
assert(machine_name_to_clfunction.count(machine_name) == 0);
machine_name_to_clfunction[machine_name] = cl;
}
// Compiles a new version of the function with the given signature and adds it to the list; // Compiles a new version of the function with the given signature and adds it to the list;
// should only be called after checking to see if the other versions would work. // should only be called after checking to see if the other versions would work.
// The codegen_lock needs to be held in W mode before calling this function: // The codegen_lock needs to be held in W mode before calling this function:
...@@ -169,6 +182,8 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E ...@@ -169,6 +182,8 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
CompiledFunction* cf = doCompile(source, entry, effort, spec, name); CompiledFunction* cf = doCompile(source, entry, effort, spec, name);
registerMachineName(cf->func->getName(), f);
compileIR(cf, effort); compileIR(cf, effort);
f->addVersion(cf); f->addVersion(cf);
assert(f->versions.size()); assert(f->versions.size());
......
...@@ -15,9 +15,12 @@ ...@@ -15,9 +15,12 @@
#ifndef PYSTON_CODEGEN_IRGEN_HOOKS_H #ifndef PYSTON_CODEGEN_IRGEN_HOOKS_H
#define PYSTON_CODEGEN_IRGEN_HOOKS_H #define PYSTON_CODEGEN_IRGEN_HOOKS_H
#include <string>
namespace pyston { namespace pyston {
class CompiledFunction; class CompiledFunction;
class CLFunction;
class OSRExit; class OSRExit;
void* compilePartialFunc(OSRExit*); void* compilePartialFunc(OSRExit*);
...@@ -26,6 +29,9 @@ extern "C" char* reoptCompiledFunc(CompiledFunction*); ...@@ -26,6 +29,9 @@ extern "C" char* reoptCompiledFunc(CompiledFunction*);
class AST_Module; class AST_Module;
class BoxedModule; class BoxedModule;
void compileAndRunModule(AST_Module* m, BoxedModule* bm); void compileAndRunModule(AST_Module* m, BoxedModule* bm);
// will we always want to generate unique function names? (ie will this function always be reasonable?)
CLFunction* clFunctionForMachineFunctionName(const std::string&);
} }
#endif #endif
...@@ -756,6 +756,7 @@ private: ...@@ -756,6 +756,7 @@ private:
std::vector<llvm::Value*> args{ cvar->getValue() }; std::vector<llvm::Value*> args{ cvar->getValue() };
llvm::Value* rtn = emitter.createCall(exc_info, g.funcs.repr, args).getInstruction(); llvm::Value* rtn = emitter.createCall(exc_info, g.funcs.repr, args).getInstruction();
cvar->decvref(emitter); cvar->decvref(emitter);
rtn = emitter.getBuilder()->CreateBitCast(rtn, g.llvm_value_type_ptr);
return new ConcreteCompilerVariable(STR, rtn, true); return new ConcreteCompilerVariable(STR, rtn, true);
} }
......
...@@ -512,6 +512,12 @@ public: ...@@ -512,6 +512,12 @@ public:
} }
}; };
Box* globals() {
BoxedModule* m = getCurrentModule();
// TODO is it ok that we don't return a real dict here?
return makeAttrWrapper(m);
}
void setupBuiltins() { void setupBuiltins() {
builtins_module = createModule("__builtin__", "__builtin__"); builtins_module = createModule("__builtin__", "__builtin__");
...@@ -627,6 +633,8 @@ void setupBuiltins() { ...@@ -627,6 +633,8 @@ void setupBuiltins() {
{ boxStrConstant("r") }); { boxStrConstant("r") });
builtins_module->giveAttr("open", open_obj); builtins_module->giveAttr("open", open_obj);
builtins_module->giveAttr("globals", new BoxedFunction(boxRTFunction((void*)globals, UNKNOWN, 0, 0, false, false)));
builtins_module->giveAttr("map", new BoxedFunction(boxRTFunction((void*)map2, LIST, 2))); builtins_module->giveAttr("map", new BoxedFunction(boxRTFunction((void*)map2, LIST, 2)));
builtins_module->giveAttr("filter", new BoxedFunction(boxRTFunction((void*)filter2, LIST, 2))); builtins_module->giveAttr("filter", new BoxedFunction(boxRTFunction((void*)filter2, LIST, 2)));
builtins_module->giveAttr("zip", new BoxedFunction(boxRTFunction((void*)zip2, LIST, 2))); builtins_module->giveAttr("zip", new BoxedFunction(boxRTFunction((void*)zip2, LIST, 2)));
......
...@@ -26,36 +26,6 @@ ...@@ -26,36 +26,6 @@
namespace pyston { namespace pyston {
// A dictionary-like wrapper around the attributes array.
// Not sure if this will be enough to satisfy users who expect __dict__
// or PyModule_GetDict to return real dicts.
BoxedClass* attrwrapper_cls;
class AttrWrapper : public Box {
private:
Box* b;
public:
AttrWrapper(Box* b) : Box(attrwrapper_cls), b(b) {}
static void gcHandler(GCVisitor* v, Box* b) {
boxGCHandler(v, b);
AttrWrapper* aw = (AttrWrapper*)b;
v->visit(aw->b);
}
static Box* setitem(Box* _self, Box* _key, Box* value) {
assert(_self->cls == attrwrapper_cls);
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
RELEASE_ASSERT(_key->cls == str_cls, "");
BoxedString* key = static_cast<BoxedString*>(_key);
self->b->setattr(key->s, value, NULL);
return None;
}
};
BoxedClass* method_cls; BoxedClass* method_cls;
class BoxedMethodDescriptor : public Box { class BoxedMethodDescriptor : public Box {
public: public:
...@@ -93,7 +63,7 @@ extern "C" PyObject* PyModule_GetDict(PyObject* _m) { ...@@ -93,7 +63,7 @@ extern "C" PyObject* PyModule_GetDict(PyObject* _m) {
BoxedModule* m = static_cast<BoxedModule*>(_m); BoxedModule* m = static_cast<BoxedModule*>(_m);
assert(m->cls == module_cls); assert(m->cls == module_cls);
return new AttrWrapper(m); return makeAttrWrapper(m);
} }
extern "C" int PyModule_AddIntConstant(PyObject* _m, const char* name, long value) { extern "C" int PyModule_AddIntConstant(PyObject* _m, const char* name, long value) {
...@@ -505,11 +475,6 @@ void setupCAPI() { ...@@ -505,11 +475,6 @@ void setupCAPI() {
capifunc_cls->freeze(); capifunc_cls->freeze();
attrwrapper_cls = new BoxedClass(object_cls, &AttrWrapper::gcHandler, 0, sizeof(AttrWrapper), false);
attrwrapper_cls->giveAttr("__name__", boxStrConstant("attrwrapper"));
attrwrapper_cls->giveAttr("__setitem__", new BoxedFunction(boxRTFunction((void*)AttrWrapper::setitem, UNKNOWN, 3)));
attrwrapper_cls->freeze();
method_cls = new BoxedClass(object_cls, NULL, 0, sizeof(BoxedMethodDescriptor), false); method_cls = new BoxedClass(object_cls, NULL, 0, sizeof(BoxedMethodDescriptor), false);
method_cls->giveAttr("__name__", boxStrConstant("method")); method_cls->giveAttr("__name__", boxStrConstant("method"));
method_cls->giveAttr("__call__", new BoxedFunction(boxRTFunction((void*)BoxedMethodDescriptor::__call__, UNKNOWN, 2, method_cls->giveAttr("__call__", new BoxedFunction(boxRTFunction((void*)BoxedMethodDescriptor::__call__, UNKNOWN, 2,
......
...@@ -1132,7 +1132,7 @@ extern "C" BoxedString* str(Box* obj) { ...@@ -1132,7 +1132,7 @@ extern "C" BoxedString* str(Box* obj) {
return static_cast<BoxedString*>(obj); return static_cast<BoxedString*>(obj);
} }
extern "C" Box* repr(Box* obj) { extern "C" BoxedString* repr(Box* obj) {
static StatCounter slowpath_repr("slowpath_repr"); static StatCounter slowpath_repr("slowpath_repr");
slowpath_repr.log(); slowpath_repr.log();
......
...@@ -37,6 +37,8 @@ void raiseExc(Box* exc_obj) __attribute__((__noreturn__)); ...@@ -37,6 +37,8 @@ void raiseExc(Box* exc_obj) __attribute__((__noreturn__));
// helper function for raising from the runtime: // helper function for raising from the runtime:
void raiseExcHelper(BoxedClass*, const char* fmt, ...) __attribute__((__noreturn__)); void raiseExcHelper(BoxedClass*, const char* fmt, ...) __attribute__((__noreturn__));
BoxedModule* getCurrentModule();
extern "C" const std::string* getNameOfClass(BoxedClass* cls); extern "C" const std::string* getNameOfClass(BoxedClass* cls);
// TODO sort this // TODO sort this
...@@ -50,7 +52,7 @@ extern "C" Box* runtimeCall(Box*, ArgPassSpec, Box*, Box*, Box*, Box**, const st ...@@ -50,7 +52,7 @@ extern "C" Box* runtimeCall(Box*, ArgPassSpec, Box*, Box*, Box*, Box**, const st
extern "C" Box* callattr(Box*, std::string*, bool, ArgPassSpec, Box*, Box*, Box*, Box**, extern "C" Box* callattr(Box*, std::string*, bool, ArgPassSpec, Box*, Box*, Box*, Box**,
const std::vector<const std::string*>*); const std::vector<const std::string*>*);
extern "C" BoxedString* str(Box* obj); extern "C" BoxedString* str(Box* obj);
extern "C" Box* repr(Box* obj); extern "C" BoxedString* repr(Box* obj);
extern "C" BoxedString* reprOrNull(Box* obj); // similar to repr, but returns NULL on exception extern "C" BoxedString* reprOrNull(Box* obj); // similar to repr, but returns NULL on exception
extern "C" BoxedString* strOrNull(Box* obj); // similar to str, but returns NULL on exception extern "C" BoxedString* strOrNull(Box* obj); // similar to str, but returns NULL on exception
extern "C" bool isinstance(Box* obj, Box* cls, int64_t flags); extern "C" bool isinstance(Box* obj, Box* cls, int64_t flags);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DIContext.h"
#include "codegen/codegen.h" #include "codegen/codegen.h"
#include "codegen/irgen/hooks.h"
#include "codegen/llvm_interpreter.h" #include "codegen/llvm_interpreter.h"
#include "core/options.h" #include "core/options.h"
#include "gc/collector.h" #include "gc/collector.h"
...@@ -207,6 +208,20 @@ static std::vector<const LineInfo*> getTracebackEntries() { ...@@ -207,6 +208,20 @@ static std::vector<const LineInfo*> getTracebackEntries() {
return entries; return entries;
} }
static const LineInfo* getMostRecentLineInfo() {
// TODO not very efficient, could stop after the first one:
return getTracebackEntries().back();
}
BoxedModule* getCurrentModule() {
const LineInfo* last_entry = getMostRecentLineInfo();
assert(last_entry->func.size());
CLFunction* cl = clFunctionForMachineFunctionName(last_entry->func);
assert(cl);
return cl->source->parent_module;
}
void raise0() { void raise0() {
raiseRaw(last_exc); raiseRaw(last_exc);
} }
......
...@@ -465,6 +465,70 @@ CLFunction* unboxRTFunction(Box* b) { ...@@ -465,6 +465,70 @@ CLFunction* unboxRTFunction(Box* b) {
return static_cast<BoxedFunction*>(b)->f; return static_cast<BoxedFunction*>(b)->f;
} }
// A dictionary-like wrapper around the attributes array.
// Not sure if this will be enough to satisfy users who expect __dict__
// or PyModule_GetDict to return real dicts.
BoxedClass* attrwrapper_cls;
class AttrWrapper : public Box {
private:
Box* b;
public:
AttrWrapper(Box* b) : Box(attrwrapper_cls), b(b) {}
static void gcHandler(GCVisitor* v, Box* b) {
boxGCHandler(v, b);
AttrWrapper* aw = (AttrWrapper*)b;
v->visit(aw->b);
}
static Box* setitem(Box* _self, Box* _key, Box* value) {
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
RELEASE_ASSERT(_key->cls == str_cls, "");
BoxedString* key = static_cast<BoxedString*>(_key);
pyston::setattr(self->b, key->s.c_str(), value);
return None;
}
static Box* getitem(Box* _self, Box* _key) {
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
RELEASE_ASSERT(_key->cls == str_cls, "");
BoxedString* key = static_cast<BoxedString*>(_key);
// TODO swap between AttributeError and KeyError?
return pyston::getattr(self->b, key->s.c_str());
}
static Box* str(Box* _self) {
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
std::ostringstream os("");
os << "attrwrapper({";
HCAttrs* attrs = self->b->getAttrsPtr();
bool first = true;
for (const auto& p : attrs->hcls->attr_offsets) {
if (!first)
os << ", ";
first = false;
BoxedString* v = repr(attrs->attr_list->attrs[p.second]);
os << p.first << ": " << v->s;
}
os << "})";
return boxString(os.str());
}
};
Box* makeAttrWrapper(Box* b) {
return new AttrWrapper(b);
}
Box* objectNew(BoxedClass* cls, BoxedTuple* args) { Box* objectNew(BoxedClass* cls, BoxedTuple* args) {
assert(isSubclass(cls->cls, type_cls)); assert(isSubclass(cls->cls, type_cls));
assert(args->cls == tuple_cls); assert(args->cls == tuple_cls);
...@@ -553,6 +617,7 @@ void setupRuntime() { ...@@ -553,6 +617,7 @@ void setupRuntime() {
member_cls = new BoxedClass(object_cls, NULL, 0, sizeof(BoxedMemberDescriptor), false); member_cls = new BoxedClass(object_cls, NULL, 0, sizeof(BoxedMemberDescriptor), false);
closure_cls closure_cls
= new BoxedClass(object_cls, &closureGCHandler, offsetof(BoxedClosure, attrs), sizeof(BoxedClosure), false); = new BoxedClass(object_cls, &closureGCHandler, offsetof(BoxedClosure, attrs), sizeof(BoxedClosure), false);
attrwrapper_cls = new BoxedClass(object_cls, &AttrWrapper::gcHandler, 0, sizeof(AttrWrapper), false);
STR = typeFromClass(str_cls); STR = typeFromClass(str_cls);
BOXED_INT = typeFromClass(int_cls); BOXED_INT = typeFromClass(int_cls);
...@@ -633,6 +698,12 @@ void setupRuntime() { ...@@ -633,6 +698,12 @@ void setupRuntime() {
slice_cls->giveAttr("step", new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, SLICE_STEP_OFFSET)); slice_cls->giveAttr("step", new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, SLICE_STEP_OFFSET));
slice_cls->freeze(); slice_cls->freeze();
attrwrapper_cls->giveAttr("__name__", boxStrConstant("attrwrapper"));
attrwrapper_cls->giveAttr("__setitem__", new BoxedFunction(boxRTFunction((void*)AttrWrapper::setitem, UNKNOWN, 3)));
attrwrapper_cls->giveAttr("__getitem__", new BoxedFunction(boxRTFunction((void*)AttrWrapper::getitem, UNKNOWN, 2)));
attrwrapper_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)AttrWrapper::str, UNKNOWN, 1)));
attrwrapper_cls->freeze();
// sys is the first module that needs to be set up, due to modules // sys is the first module that needs to be set up, due to modules
// being tracked in sys.modules: // being tracked in sys.modules:
setupSys(); setupSys();
......
...@@ -378,5 +378,7 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) { ...@@ -378,5 +378,7 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) {
attrs = new ((void*)attrs) HCAttrs(); attrs = new ((void*)attrs) HCAttrs();
} }
} }
Box* makeAttrWrapper(Box* b);
} }
#endif #endif
# run_args: -n # run_args: -n
# statcheck: stats['slowpath_setattr'] <= 10 # statcheck: noninit_count('slowpath_setattr') <= 10
# statcheck: stats['slowpath_getattr'] <= 10 # statcheck: noninit_count('slowpath_getattr') <= 10
class C(object): class C(object):
pass pass
......
f1 = lambda: globals()['__name__']
f2 = lambda: __name__
print f1()
print f2()
import import_target
print import_target.letMeCallThatForYou(f1)
print import_target.letMeCallThatForYou(f2)
print import_target.letMeCallThatForYou(globals)['__name__']
try:
print x
assert 0, "Expected NameError not thrown"
except NameError:
pass
# You're allowed to assign through globals and have it affect the module:
globals()['x'] = 1
print x
...@@ -16,3 +16,6 @@ class C(object): ...@@ -16,3 +16,6 @@ class C(object):
_x = 1 _x = 1
__all__ = ['x'] __all__ = ['x']
def letMeCallThatForYou(f, *args):
return f(*args)
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