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) {
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;
// 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:
......@@ -169,6 +182,8 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
CompiledFunction* cf = doCompile(source, entry, effort, spec, name);
registerMachineName(cf->func->getName(), f);
compileIR(cf, effort);
f->addVersion(cf);
assert(f->versions.size());
......
......@@ -15,9 +15,12 @@
#ifndef PYSTON_CODEGEN_IRGEN_HOOKS_H
#define PYSTON_CODEGEN_IRGEN_HOOKS_H
#include <string>
namespace pyston {
class CompiledFunction;
class CLFunction;
class OSRExit;
void* compilePartialFunc(OSRExit*);
......@@ -26,6 +29,9 @@ extern "C" char* reoptCompiledFunc(CompiledFunction*);
class AST_Module;
class BoxedModule;
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
......@@ -756,6 +756,7 @@ private:
std::vector<llvm::Value*> args{ cvar->getValue() };
llvm::Value* rtn = emitter.createCall(exc_info, g.funcs.repr, args).getInstruction();
cvar->decvref(emitter);
rtn = emitter.getBuilder()->CreateBitCast(rtn, g.llvm_value_type_ptr);
return new ConcreteCompilerVariable(STR, rtn, true);
}
......
......@@ -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() {
builtins_module = createModule("__builtin__", "__builtin__");
......@@ -627,6 +633,8 @@ void setupBuiltins() {
{ boxStrConstant("r") });
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("filter", new BoxedFunction(boxRTFunction((void*)filter2, LIST, 2)));
builtins_module->giveAttr("zip", new BoxedFunction(boxRTFunction((void*)zip2, LIST, 2)));
......
......@@ -26,36 +26,6 @@
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;
class BoxedMethodDescriptor : public Box {
public:
......@@ -93,7 +63,7 @@ extern "C" PyObject* PyModule_GetDict(PyObject* _m) {
BoxedModule* m = static_cast<BoxedModule*>(_m);
assert(m->cls == module_cls);
return new AttrWrapper(m);
return makeAttrWrapper(m);
}
extern "C" int PyModule_AddIntConstant(PyObject* _m, const char* name, long value) {
......@@ -505,11 +475,6 @@ void setupCAPI() {
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->giveAttr("__name__", boxStrConstant("method"));
method_cls->giveAttr("__call__", new BoxedFunction(boxRTFunction((void*)BoxedMethodDescriptor::__call__, UNKNOWN, 2,
......
......@@ -1132,7 +1132,7 @@ extern "C" BoxedString* str(Box* obj) {
return static_cast<BoxedString*>(obj);
}
extern "C" Box* repr(Box* obj) {
extern "C" BoxedString* repr(Box* obj) {
static StatCounter slowpath_repr("slowpath_repr");
slowpath_repr.log();
......
......@@ -37,6 +37,8 @@ void raiseExc(Box* exc_obj) __attribute__((__noreturn__));
// helper function for raising from the runtime:
void raiseExcHelper(BoxedClass*, const char* fmt, ...) __attribute__((__noreturn__));
BoxedModule* getCurrentModule();
extern "C" const std::string* getNameOfClass(BoxedClass* cls);
// TODO sort this
......@@ -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**,
const std::vector<const std::string*>*);
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* strOrNull(Box* obj); // similar to str, but returns NULL on exception
extern "C" bool isinstance(Box* obj, Box* cls, int64_t flags);
......
......@@ -18,6 +18,7 @@
#include "llvm/DebugInfo/DIContext.h"
#include "codegen/codegen.h"
#include "codegen/irgen/hooks.h"
#include "codegen/llvm_interpreter.h"
#include "core/options.h"
#include "gc/collector.h"
......@@ -207,6 +208,20 @@ static std::vector<const LineInfo*> getTracebackEntries() {
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() {
raiseRaw(last_exc);
}
......
......@@ -465,6 +465,70 @@ CLFunction* unboxRTFunction(Box* b) {
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) {
assert(isSubclass(cls->cls, type_cls));
assert(args->cls == tuple_cls);
......@@ -553,6 +617,7 @@ void setupRuntime() {
member_cls = new BoxedClass(object_cls, NULL, 0, sizeof(BoxedMemberDescriptor), false);
closure_cls
= 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);
BOXED_INT = typeFromClass(int_cls);
......@@ -633,6 +698,12 @@ void setupRuntime() {
slice_cls->giveAttr("step", new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, SLICE_STEP_OFFSET));
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
// being tracked in sys.modules:
setupSys();
......
......@@ -378,5 +378,7 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) {
attrs = new ((void*)attrs) HCAttrs();
}
}
Box* makeAttrWrapper(Box* b);
}
#endif
# run_args: -n
# statcheck: stats['slowpath_setattr'] <= 10
# statcheck: stats['slowpath_getattr'] <= 10
# statcheck: noninit_count('slowpath_setattr') <= 10
# statcheck: noninit_count('slowpath_getattr') <= 10
class C(object):
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):
_x = 1
__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