Commit e8f0d966 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'exec-in-pass-in-globals'

Conflicts:
	src/runtime/builtin_modules/builtins.cpp
	src/runtime/import.cpp
parents 348e446f e95b1c58
......@@ -407,38 +407,6 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
return result
# Pyston change: use this hacky / slow? version of namedtuple while we work
# on exec support
def namedtuple(name, fields):
if isinstance(fields, str):
fields = fields.split()
assert isinstance(fields, list)
for f in fields:
assert isinstance(f, str)
class NamedTuple(object):
def __init__(self, *args):
assert len(args) == len(fields)
for i in xrange(len(fields)):
setattr(self, fields[i], args[i])
def __getitem__(self, idx):
assert 0 <= idx < len(fields)
return getattr(self, fields[idx])
def __repr__(self):
s = name + "("
first = True
for f in fields:
if not first:
s += ", "
first = False
s += "%s=%r" % (f, getattr(self, f))
s += ")"
return s
return NamedTuple
########################################################################
### Counter
########################################################################
......
......@@ -147,7 +147,7 @@ public:
};
LivenessAnalysis::LivenessAnalysis(CFG* cfg) : cfg(cfg) {
Timer _t("LivenessAnalysis()", 10);
Timer _t("LivenessAnalysis()", 100);
for (CFGBlock* b : cfg->blocks) {
auto visitor = new LivenessBBVisitor(this); // livenessCache unique_ptr will delete it.
......@@ -361,7 +361,7 @@ void DefinednessBBAnalyzer::processBB(Map& starting, CFGBlock* block) const {
block->body[i]->accept(&visitor);
}
if (VERBOSITY("analysis") >= 2) {
if (VERBOSITY("analysis") >= 3) {
printf("At end of block %d:\n", block->idx);
for (const auto& p : starting) {
printf("%s: %d\n", p.first.c_str(), p.second);
......
......@@ -142,10 +142,12 @@ private:
}
};
bool globals_from_module;
public:
EvalExprScopeInfo() {}
EvalExprScopeInfo(bool globals_from_module) : globals_from_module(globals_from_module) {}
EvalExprScopeInfo(AST* node) {
EvalExprScopeInfo(AST* node, bool globals_from_module) : globals_from_module(globals_from_module) {
// 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);
......@@ -271,9 +273,13 @@ private:
std::vector<std::pair<InternedString, DerefInfo>> allDerefVarsAndInfo;
bool allDerefVarsAndInfoCached;
bool globals_from_module;
public:
ScopeInfoBase(ScopeInfo* parent, ScopingAnalysis::ScopeNameUsage* usage, AST* ast, bool usesNameLookup)
: parent(parent), usage(usage), ast(ast), usesNameLookup_(usesNameLookup), allDerefVarsAndInfoCached(false) {
ScopeInfoBase(ScopeInfo* parent, ScopingAnalysis::ScopeNameUsage* usage, AST* ast, bool usesNameLookup,
bool globals_from_module)
: parent(parent), usage(usage), ast(ast), usesNameLookup_(usesNameLookup), allDerefVarsAndInfoCached(false),
globals_from_module(globals_from_module) {
assert(usage);
assert(ast);
......@@ -651,7 +657,7 @@ public:
}
bool visit_exec(AST_Exec* node) override {
if (node->locals == NULL) {
if (node->globals == NULL) {
doBareExec(node);
}
return false;
......@@ -810,8 +816,8 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
switch (node->type) {
case AST_TYPE::ClassDef: {
ScopeInfoBase* scopeInfo
= new ScopeInfoBase(parent_info, usage, usage->node, true /* usesNameLookup */);
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage, usage->node, true /* usesNameLookup */,
globals_from_module);
this->scopes[node] = scopeInfo;
break;
}
......@@ -820,8 +826,9 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
case AST_TYPE::GeneratorExp:
case AST_TYPE::DictComp:
case AST_TYPE::SetComp: {
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage, usage->node,
usage->hasNameForcingSyntax() /* usesNameLookup */);
ScopeInfoBase* scopeInfo
= new ScopeInfoBase(parent_info, usage, usage->node,
usage->hasNameForcingSyntax() /* usesNameLookup */, globals_from_module);
this->scopes[node] = scopeInfo;
break;
}
......@@ -877,16 +884,19 @@ ScopeInfo* ScopingAnalysis::getScopeInfoForNode(AST* node) {
return analyzeSubtree(node);
}
ScopingAnalysis::ScopingAnalysis(AST_Module* m) : parent_module(m), interned_strings(*m->interned_strings.get()) {
ScopingAnalysis::ScopingAnalysis(AST_Module* m)
: parent_module(m), interned_strings(*m->interned_strings.get()), globals_from_module(true) {
scopes[m] = new ModuleScopeInfo();
}
ScopingAnalysis::ScopingAnalysis(AST_Expression* e) : interned_strings(*e->interned_strings.get()) {
ScopingAnalysis::ScopingAnalysis(AST_Expression* e, bool globals_from_module)
: interned_strings(*e->interned_strings.get()), globals_from_module(globals_from_module) {
// It's an expression, so it can't have a `global` statement
scopes[e] = new EvalExprScopeInfo();
scopes[e] = new EvalExprScopeInfo(globals_from_module);
}
ScopingAnalysis::ScopingAnalysis(AST_Suite* s) : interned_strings(*s->interned_strings.get()) {
scopes[s] = new EvalExprScopeInfo(s);
ScopingAnalysis::ScopingAnalysis(AST_Suite* s, bool globals_from_module)
: interned_strings(*s->interned_strings.get()), globals_from_module(globals_from_module) {
scopes[s] = new EvalExprScopeInfo(s, globals_from_module);
}
}
......@@ -158,6 +158,8 @@ private:
ScopeInfo* analyzeSubtree(AST* node);
void processNameUsages(NameUsageMap* usages);
bool globals_from_module;
public:
// The scope-analysis is done before any CFG-ization is done,
// but many of the queries will be done post-CFG-ization.
......@@ -169,11 +171,12 @@ public:
void registerScopeReplacement(AST* original_node, AST* new_node);
ScopingAnalysis(AST_Module* m);
ScopingAnalysis(AST_Expression* e);
ScopingAnalysis(AST_Suite* s);
ScopingAnalysis(AST_Expression* e, bool globals_from_module);
ScopingAnalysis(AST_Suite* s, bool globals_from_module);
ScopeInfo* getScopeInfoForNode(AST* node);
InternedStringPool& getInternedStrings();
bool areGlobalsFromModule() { return globals_from_module; }
};
bool containsYield(AST* ast);
......
......@@ -132,7 +132,7 @@ private:
void* raw_rtn = node->accept_expr(this);
CompilerType* rtn = static_cast<CompilerType*>(raw_rtn);
if (VERBOSITY() >= 2) {
if (VERBOSITY() >= 3) {
print_ast(node);
printf(" %s\n", rtn->debugName().c_str());
}
......@@ -735,10 +735,10 @@ public:
TypeMap ending;
if (VERBOSITY("types")) {
if (VERBOSITY("types") >= 3) {
printf("processing types for block %d\n", block->idx);
}
if (VERBOSITY("types") >= 2) {
if (VERBOSITY("types") >= 3) {
printf("before:\n");
TypeMap& starting = starting_types[block];
for (const auto& p : starting) {
......@@ -750,7 +750,7 @@ public:
BasicBlockTypePropagator::propagate(block, starting_types[block], ending, expr_types, type_speculations,
speculation, scope_info);
if (VERBOSITY("types") >= 2) {
if (VERBOSITY("types") >= 3) {
printf("before (after):\n");
TypeMap& starting = starting_types[block];
for (const auto& p : starting) {
......@@ -775,11 +775,11 @@ public:
}
if (VERBOSITY("types")) {
printf("%ld BBs, %d evaluations = %.1f evaluations/block\n", cfg->blocks.size(), num_evaluations,
1.0 * num_evaluations / cfg->blocks.size());
printf("Type analysis: %ld BBs, %d evaluations = %.1f evaluations/block\n", cfg->blocks.size(),
num_evaluations, 1.0 * num_evaluations / cfg->blocks.size());
}
if (VERBOSITY("types") >= 2) {
if (VERBOSITY("types") >= 3) {
for (CFGBlock* b : cfg->blocks) {
printf("Types at beginning of block %d:\n", b->idx);
......
......@@ -61,7 +61,7 @@ ICSlotRewrite::ICSlotRewrite(ICInfo* ic, const char* debug_name) : ic(ic), debug
assembler = new Assembler(buf, ic->getSlotSize());
assembler->nop();
if (VERBOSITY())
if (VERBOSITY() >= 2)
printf("starting %s icentry\n", debug_name);
}
......@@ -86,7 +86,7 @@ void ICSlotRewrite::commit(CommitHook* hook) {
}
}
if (!still_valid) {
if (VERBOSITY())
if (VERBOSITY() >= 2)
printf("not committing %s icentry since a dependency got updated before commit\n", debug_name);
return;
}
......@@ -165,14 +165,14 @@ ICSlotInfo* ICInfo::pickEntryForRewrite(const char* debug_name) {
if (sinfo.num_inside)
continue;
if (VERBOSITY()) {
if (VERBOSITY() >= 3) {
printf("committing %s icentry to in-use slot %d at %p\n", debug_name, i, start_addr);
}
next_slot_to_try = i + 1;
return &sinfo;
}
if (VERBOSITY())
if (VERBOSITY() >= 3)
printf("not committing %s icentry since there are no available slots\n", debug_name);
return NULL;
}
......@@ -259,7 +259,7 @@ void ICInfo::clear(ICSlotInfo* icentry) {
uint8_t* start = (uint8_t*)start_addr + icentry->idx * getSlotSize();
if (VERBOSITY())
if (VERBOSITY() >= 3)
printf("clearing patchpoint %p, slot at %p\n", start_addr, start);
std::unique_ptr<Assembler> writer(new Assembler(start, getSlotSize()));
......
......@@ -1483,7 +1483,7 @@ bool spillFrameArgumentIfNecessary(StackMap::Record::Location& l, uint8_t*& inst
auto it = remapped.find(ru);
if (it != remapped.end()) {
if (VERBOSITY()) {
if (VERBOSITY() >= 3) {
printf("Already spilled ");
ru.dump();
}
......@@ -1491,7 +1491,7 @@ bool spillFrameArgumentIfNecessary(StackMap::Record::Location& l, uint8_t*& inst
return false;
}
if (VERBOSITY()) {
if (VERBOSITY() >= 3) {
printf("Spilling reg ");
ru.dump();
}
......@@ -1595,7 +1595,7 @@ std::pair<uint8_t*, uint8_t*> initializePatchpoint3(void* slowpath_func, uint8_t
abort();
}
if (VERBOSITY())
if (VERBOSITY() >= 3)
printf("Have to spill %ld regs around the slowpath\n", regs_to_spill.size());
// TODO: some of these registers could already have been pushed via the frame saving code
......
This diff is collapsed.
......@@ -33,13 +33,14 @@ struct LineInfo;
extern const void* interpreter_instr_addr;
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
Box* astInterpretFunctionEval(CompiledFunction* cf, Box* boxedLocals);
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, BoxedDict* globals, Box* arg1,
Box* arg2, Box* arg3, Box** args);
Box* astInterpretFunctionEval(CompiledFunction* cf, BoxedDict* globals, Box* boxedLocals);
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
FrameStackState frame_state);
AST_stmt* getCurrentStatementForInterpretedFrame(void* frame_ptr);
Box* getGlobalsForInterpretedFrame(void* frame_ptr);
CompiledFunction* getCFForInterpretedFrame(void* frame_ptr);
struct FrameInfo;
FrameInfo* getFrameInfoForInterpretedFrame(void* frame_ptr);
......
......@@ -69,7 +69,7 @@ struct GlobalState {
llvm::Type* llvm_value_type, *llvm_value_type_ptr, *llvm_value_type_ptr_ptr;
llvm::Type* llvm_class_type, *llvm_class_type_ptr;
llvm::Type* llvm_opaque_type;
llvm::Type* llvm_str_type_ptr;
llvm::Type* llvm_str_type_ptr, *llvm_dict_type_ptr;
llvm::Type* llvm_frame_info_type;
llvm::Type* llvm_clfunction_type_ptr, *llvm_closure_type_ptr, *llvm_generator_type_ptr;
llvm::Type* llvm_module_type_ptr, *llvm_bool_type_ptr;
......
......@@ -727,7 +727,7 @@ ConcreteCompilerVariable* UnknownType::hasnext(IREmitter& emitter, const OpInfo&
}
CompilerVariable* makeFunction(IREmitter& emitter, CLFunction* f, CompilerVariable* closure, bool isGenerator,
const std::vector<ConcreteCompilerVariable*>& defaults) {
BoxedDict* globals, const std::vector<ConcreteCompilerVariable*>& defaults) {
// Unlike the CLFunction*, which can be shared between recompilations, the Box* around it
// should be created anew every time the functiondef is encountered
......@@ -757,12 +757,15 @@ CompilerVariable* makeFunction(IREmitter& emitter, CLFunction* f, CompilerVariab
llvm::Value* isGenerator_v = llvm::ConstantInt::get(g.i1, isGenerator, false);
assert(globals == NULL);
llvm::Value* globals_v = embedConstantPtr(nullptr, g.llvm_dict_type_ptr);
// We know this function call can't throw, so it's safe to use emitter.getBuilder()->CreateCall() rather than
// emitter.createCall().
llvm::Value* boxed = emitter.getBuilder()->CreateCall(
g.funcs.boxCLFunction,
std::vector<llvm::Value*>{ embedConstantPtr(f, g.llvm_clfunction_type_ptr), closure_v, isGenerator_v, scratch,
getConstantInt(defaults.size(), g.i64) });
std::vector<llvm::Value*>{ embedConstantPtr(f, g.llvm_clfunction_type_ptr), closure_v, isGenerator_v, globals_v,
scratch, getConstantInt(defaults.size(), g.i64) });
if (convertedClosure)
convertedClosure->decvref(emitter);
......
......@@ -399,7 +399,7 @@ ConcreteCompilerVariable* makePureImaginary(IREmitter& emitter, double imag);
CompilerVariable* makeStr(const std::string*);
CompilerVariable* makeUnicode(IREmitter& emitter, const std::string*);
CompilerVariable* makeFunction(IREmitter& emitter, CLFunction*, CompilerVariable* closure, bool isGenerator,
const std::vector<ConcreteCompilerVariable*>& defaults);
BoxedDict* globals, const std::vector<ConcreteCompilerVariable*>& defaults);
ConcreteCompilerVariable* undefVariable();
CompilerVariable* makeTuple(const std::vector<CompilerVariable*>& elts);
......
......@@ -507,11 +507,11 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
CFGBlock* block = traversal_order[_i].first;
CFGBlock* pred = traversal_order[_i].second;
if (VERBOSITY("irgen") >= 1)
if (VERBOSITY("irgen") >= 2)
printf("processing block %d\n", block->idx);
if (!blocks.count(block)) {
if (VERBOSITY("irgen") >= 1)
if (VERBOSITY("irgen") >= 2)
printf("Skipping this block\n");
// created_phis[block] = NULL;
// ending_symbol_tables[block] = NULL;
......@@ -955,7 +955,7 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
assert((entry_descriptor != NULL) + (spec != NULL) == 1);
if (VERBOSITY("irgen") >= 1)
if (VERBOSITY("irgen") >= 2)
source->cfg->print();
assert(g.cur_module == NULL);
......@@ -1056,7 +1056,7 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
delete types;
if (VERBOSITY("irgen") >= 1) {
if (VERBOSITY("irgen") >= 2) {
printf("generated IR:\n");
printf("\033[33m");
fflush(stdout);
......
......@@ -183,7 +183,7 @@ static void compileIR(CompiledFunction* cf, EffortLevel effort) {
// The codegen_lock needs to be held in W mode before calling this function:
CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel effort,
const OSREntryDescriptor* entry_descriptor) {
Timer _t("for compileFunction()");
Timer _t("for compileFunction()", 1000);
assert((entry_descriptor != NULL) + (spec != NULL) == 1);
......@@ -316,26 +316,30 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
EffortLevel effort = initialEffort();
assert(scoping->areGlobalsFromModule());
cf = compileFunction(cl_f, new FunctionSpecialization(VOID), effort, NULL);
assert(cf->clfunc->versions.size());
}
if (cf->is_interpreted)
astInterpretFunction(cf, 0, NULL, NULL, NULL, NULL, NULL, NULL);
astInterpretFunction(cf, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
else
((void (*)())cf->code)();
}
template <typename AST_Type>
Box* evalOrExec(AST_Type* source, std::vector<AST_stmt*>& body, BoxedModule* bm, Box* boxedLocals) {
Box* evalOrExec(AST_Type* source, std::vector<AST_stmt*>& body, BoxedModule* bm, BoxedDict* globals, Box* boxedLocals) {
CompiledFunction* cf;
assert(!globals || globals->cls == dict_cls);
{ // scope for limiting the locked region:
LOCK_REGION(codegen_rwlock.asWrite());
Timer _t("for evalOrExec()");
ScopingAnalysis* scoping = new ScopingAnalysis(source);
ScopingAnalysis* scoping = new ScopingAnalysis(source, globals == NULL);
SourceInfo* si = new SourceInfo(bm, scoping, source, body);
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
......@@ -352,13 +356,17 @@ Box* evalOrExec(AST_Type* source, std::vector<AST_stmt*>& body, BoxedModule* bm,
assert(cf->clfunc->versions.size());
}
return astInterpretFunctionEval(cf, boxedLocals);
return astInterpretFunctionEval(cf, globals, boxedLocals);
}
// Main entrypoints for eval and exec.
Box* eval(Box* boxedCode) {
Box* boxedLocals = fastLocalsToBoxedLocals();
BoxedModule* module = getCurrentModule();
Box* globals = getGlobals();
if (globals == module)
globals = NULL;
// TODO error message if parse fails or if it isn't an expr
// TODO should have a cleaner interface that can parse the Expression directly
......@@ -376,13 +384,44 @@ Box* eval(Box* boxedCode) {
stmt->value = parsedExpr->body;
std::vector<AST_stmt*> body = { stmt };
return evalOrExec<AST_Expression>(parsedExpr, body, module, boxedLocals);
assert(!globals || globals->cls == dict_cls);
return evalOrExec<AST_Expression>(parsedExpr, body, module, static_cast<BoxedDict*>(globals), boxedLocals);
}
Box* exec(Box* boxedCode) {
Box* boxedLocals = fastLocalsToBoxedLocals();
Box* exec(Box* boxedCode, Box* globals, Box* locals) {
if (globals == None)
globals = NULL;
if (locals == None)
locals = NULL;
// TODO boxedCode is allowed to be a tuple
// TODO need to handle passing in globals
if (locals == NULL) {
locals = globals;
}
if (locals == NULL) {
locals = fastLocalsToBoxedLocals();
}
if (globals == NULL)
globals = getGlobals();
BoxedModule* module = getCurrentModule();
if (globals == module)
globals = NULL;
assert(!globals || globals->cls == dict_cls);
if (globals) {
// From CPython (they set it to be f->f_builtins):
if (PyDict_GetItemString(globals, "__builtins__") == NULL)
PyDict_SetItemString(globals, "__builtins__", builtins_module);
}
// TODO same issues as in `eval`
RELEASE_ASSERT(boxedCode->cls == str_cls, "");
const char* code = static_cast<BoxedString*>(boxedCode)->s.data();
......@@ -390,7 +429,7 @@ Box* exec(Box* boxedCode) {
AST_Suite* parsedSuite = new AST_Suite(std::move(parsedModule->interned_strings));
parsedSuite->body = parsedModule->body;
return evalOrExec<AST_Suite>(parsedSuite, parsedSuite->body, module, boxedLocals);
return evalOrExec<AST_Suite>(parsedSuite, parsedSuite->body, module, static_cast<BoxedDict*>(globals), locals);
}
// If a function version keeps failing its speculations, kill it (remove it
......
......@@ -37,7 +37,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm);
// will we always want to generate unique function names? (ie will this function always be reasonable?)
CompiledFunction* cfForMachineFunctionName(const std::string&);
extern "C" Box* exec(Box* boxedCode);
extern "C" Box* exec(Box* boxedCode, Box* globals, Box* locals);
extern "C" Box* eval(Box* boxedCode);
}
......
......@@ -254,6 +254,9 @@ private:
public:
explicit IREmitterImpl(IRGenState* irstate, llvm::BasicBlock*& curblock, IRGenerator* irgenerator)
: irstate(irstate), builder(new IRBuilder(g.context)), curblock(curblock), irgenerator(irgenerator) {
ASSERT(irstate->getSourceInfo()->scoping->areGlobalsFromModule(), "jit doesn't support custom globals yet");
builder->setEmitter(this);
builder->SetInsertPoint(curblock);
}
......@@ -1196,7 +1199,8 @@ private:
// one reason to do this is to pass the closure through if necessary,
// but since the classdef can't create its own closure, shouldn't need to explicitly
// create that scope to pass the closure through.
CompilerVariable* func = makeFunction(emitter, cl, created_closure, false, {});
assert(irstate->getSourceInfo()->scoping->areGlobalsFromModule());
CompilerVariable* func = makeFunction(emitter, cl, created_closure, false, NULL, {});
CompilerVariable* attr_dict = func->call(emitter, getEmptyOpInfo(unw_info), ArgPassSpec(0), {}, NULL);
......@@ -1259,7 +1263,8 @@ private:
assert(created_closure);
}
CompilerVariable* func = makeFunction(emitter, cl, created_closure, is_generator, defaults);
assert(irstate->getSourceInfo()->scoping->areGlobalsFromModule());
CompilerVariable* func = makeFunction(emitter, cl, created_closure, is_generator, NULL, defaults);
for (auto d : defaults) {
d->decvref(emitter);
......@@ -1396,7 +1401,7 @@ private:
assert(rtn);
ConcreteCompilerType* speculated_type = typeFromClass(speculated_class);
if (VERBOSITY("irgen") >= 1) {
if (VERBOSITY("irgen") >= 2) {
printf("Speculating that %s is actually %s, at ", rtn->getConcreteType()->debugName().c_str(),
speculated_type->debugName().c_str());
PrintVisitor printer;
......@@ -1711,15 +1716,29 @@ private:
}
void doExec(AST_Exec* node, UnwindInfo unw_info) {
// TODO locals and globals
RELEASE_ASSERT(!node->globals, "do not support exec with globals or locals yet");
assert(!node->locals);
CompilerVariable* body = evalExpr(node->body, unw_info);
ConcreteCompilerVariable* cbody = body->makeConverted(emitter, body->getBoxType());
llvm::Value* vbody = body->makeConverted(emitter, body->getBoxType())->getValue();
body->decvref(emitter);
emitter.createCall(unw_info, g.funcs.exec, cbody->getValue());
llvm::Value* vglobals;
if (node->globals) {
CompilerVariable* globals = evalExpr(node->globals, unw_info);
vglobals = globals->makeConverted(emitter, globals->getBoxType())->getValue();
globals->decvref(emitter);
} else {
vglobals = embedConstantPtr(NULL, g.llvm_value_type_ptr);
}
llvm::Value* vlocals;
if (node->locals) {
CompilerVariable* locals = evalExpr(node->locals, unw_info);
vlocals = locals->makeConverted(emitter, locals->getBoxType())->getValue();
locals->decvref(emitter);
} else {
vlocals = embedConstantPtr(NULL, g.llvm_value_type_ptr);
}
emitter.createCall3(unw_info, g.funcs.exec, vbody, vglobals, vlocals);
}
void doPrint(AST_Print* node, UnwindInfo unw_info) {
......@@ -2478,7 +2497,7 @@ public:
}
void run(const CFGBlock* block) override {
if (VERBOSITY("irgenerator") >= 1) { // print starting symbol table
if (VERBOSITY("irgenerator") >= 2) { // print starting symbol table
printf(" %d init:", block->idx);
for (auto it = symbol_table.begin(); it != symbol_table.end(); ++it)
printf(" %s", it->first.c_str());
......@@ -2490,7 +2509,7 @@ public:
assert(state != FINISHED);
doStmt(block->body[i], UnwindInfo(block->body[i], NULL));
}
if (VERBOSITY("irgenerator") >= 1) { // print ending symbol table
if (VERBOSITY("irgenerator") >= 2) { // print ending symbol table
printf(" %d fini:", block->idx);
for (auto it = symbol_table.begin(); it != symbol_table.end(); ++it)
printf(" %s", it->first.c_str());
......
......@@ -58,7 +58,7 @@ public:
end -= start;
start = 0;
end += fread(buf + end, 1, BUFSIZE - end, fp);
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("filled, now at %d-%d\n", start, end);
}
......@@ -118,7 +118,7 @@ InternedString BufferedReader::readAndInternString() {
void BufferedReader::readAndInternStringVector(std::vector<InternedString>& v) {
int num_elts = readShort();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("%d elts to read\n", num_elts);
for (int i = 0; i < num_elts; i++) {
v.push_back(readAndInternString());
......@@ -127,7 +127,7 @@ void BufferedReader::readAndInternStringVector(std::vector<InternedString>& v) {
static void readStringVector(std::vector<std::string>& vec, BufferedReader* reader) {
int num_elts = reader->readShort();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("%d elts to read\n", num_elts);
for (int i = 0; i < num_elts; i++) {
vec.push_back(readString(reader));
......@@ -136,7 +136,7 @@ static void readStringVector(std::vector<std::string>& vec, BufferedReader* read
static void readStmtVector(std::vector<AST_stmt*>& vec, BufferedReader* reader) {
int num_elts = reader->readShort();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("%d elts to read\n", num_elts);
for (int i = 0; i < num_elts; i++) {
vec.push_back(readASTStmt(reader));
......@@ -145,7 +145,7 @@ static void readStmtVector(std::vector<AST_stmt*>& vec, BufferedReader* reader)
static void readExprVector(std::vector<AST_expr*>& vec, BufferedReader* reader) {
int num_elts = reader->readShort();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("%d elts to read\n", num_elts);
for (int i = 0; i < num_elts; i++) {
vec.push_back(readASTExpr(reader));
......@@ -154,7 +154,7 @@ static void readExprVector(std::vector<AST_expr*>& vec, BufferedReader* reader)
template <class T> static void readMiscVector(std::vector<T*>& vec, BufferedReader* reader) {
int num_elts = reader->readShort();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("%d elts to read\n", num_elts);
for (int i = 0; i < num_elts; i++) {
AST* read = readASTMisc(reader);
......@@ -182,7 +182,7 @@ AST_alias* read_alias(BufferedReader* reader) {
}
AST_arguments* read_arguments(BufferedReader* reader) {
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("reading arguments\n");
AST_arguments* rtn = new AST_arguments();
......@@ -416,7 +416,7 @@ AST_For* read_for(BufferedReader* reader) {
}
AST_FunctionDef* read_functiondef(BufferedReader* reader) {
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("reading functiondef\n");
AST_FunctionDef* rtn = new AST_FunctionDef();
......@@ -541,7 +541,7 @@ AST_ListComp* read_listcomp(BufferedReader* reader) {
}
AST_Module* read_module(BufferedReader* reader) {
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("reading module\n");
AST_Module* rtn = new AST_Module(reader->createInternedPool());
......@@ -775,7 +775,7 @@ AST_Yield* read_yield(BufferedReader* reader) {
AST_expr* readASTExpr(BufferedReader* reader) {
uint8_t type = reader->readByte();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("type = %d\n", type);
if (type == 0)
return NULL;
......@@ -841,7 +841,7 @@ AST_expr* readASTExpr(BufferedReader* reader) {
AST_stmt* readASTStmt(BufferedReader* reader) {
uint8_t type = reader->readByte();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("type = %d\n", type);
if (type == 0)
return NULL;
......@@ -905,7 +905,7 @@ AST_stmt* readASTStmt(BufferedReader* reader) {
AST* readASTMisc(BufferedReader* reader) {
uint8_t type = reader->readByte();
if (VERBOSITY("parsing") >= 2)
if (VERBOSITY("parsing") >= 3)
printf("type = %d\n", type);
if (type == 0)
return NULL;
......@@ -953,7 +953,7 @@ AST_Module* parse_string(const char* code) {
char* tmpdir = mkdtemp(buf);
assert(tmpdir);
std::string tmp = std::string(tmpdir) + "/in.py";
if (VERBOSITY() >= 1) {
if (VERBOSITY() >= 3) {
printf("writing %d bytes to %s\n", size, tmp.c_str());
}
......
......@@ -168,7 +168,7 @@ void processStackmap(CompiledFunction* cf, StackMap* stackmap) {
PatchpointInfo* pp = reinterpret_cast<PatchpointInfo*>(r->id);
assert(pp);
if (VERBOSITY()) {
if (VERBOSITY() >= 2) {
printf("Processing pp %ld; [%d, %d)\n", reinterpret_cast<int64_t>(pp), r->offset,
r->offset + pp->patchpointSize());
}
......
......@@ -135,6 +135,10 @@ void initGlobalFuncs(GlobalState& g) {
g.llvm_str_type_ptr = lookupFunction("boxStringPtr")->arg_begin()->getType();
g.llvm_dict_type_ptr = g.stdlib_module->getTypeByName("class.pyston::BoxedDict");
assert(g.llvm_dict_type_ptr);
g.llvm_dict_type_ptr = g.llvm_dict_type_ptr->getPointerTo();
// The LLVM vector type for the arguments that we pass to runtimeCall and related functions.
// It will be a pointer to a type named something like class.std::vector or
// class.std::vector.##. We can figure out exactly what it is by looking at the last
......
......@@ -336,6 +336,17 @@ public:
abort();
}
Box* getGlobals() {
if (id.type == PythonFrameId::COMPILED) {
CompiledFunction* cf = getCF();
assert(cf->clfunc->source->scoping->areGlobalsFromModule());
return cf->clfunc->source->parent_module;
} else if (id.type == PythonFrameId::INTERPRETED) {
return getGlobalsForInterpretedFrame((void*)id.bp);
}
abort();
}
FrameInfo* getFrameInfo() {
if (id.type == PythonFrameId::COMPILED) {
CompiledFunction* cf = getCF();
......@@ -536,7 +547,7 @@ BoxedTraceback* getTraceback() {
return new BoxedTraceback();
}
Timer _t("getTraceback");
Timer _t("getTraceback", 1000);
std::vector<const LineInfo*> entries;
for (auto& frame_iter : unwindPythonFrames()) {
......@@ -597,6 +608,21 @@ CompiledFunction* getTopCompiledFunction() {
return getTopPythonFrame()->getCF();
}
Box* getGlobals() {
auto it = getTopPythonFrame();
return it->getGlobals();
}
Box* getGlobalsDict() {
Box* globals = getGlobals();
if (!globals)
return NULL;
if (isSubclass(globals->cls, module_cls))
return makeAttrWrapper(globals);
return globals;
}
BoxedModule* getCurrentModule() {
CompiledFunction* compiledFunction = getTopCompiledFunction();
if (!compiledFunction)
......@@ -692,7 +718,7 @@ Box* fastLocalsToBoxedLocals() {
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());
return getGlobalsDict();
}
if (frame_iter.getId().type == PythonFrameId::COMPILED) {
......
......@@ -28,6 +28,8 @@ class BoxedTraceback;
struct FrameInfo;
BoxedModule* getCurrentModule();
Box* getGlobals(); // returns either the module or a globals dict
Box* getGlobalsDict(); // always returns a dict-like object
BoxedTraceback* getTraceback();
......
......@@ -462,6 +462,10 @@ void AST_Exec::accept(ASTVisitor* v) {
if (body)
body->accept(v);
if (globals)
globals->accept(v);
if (locals)
locals->accept(v);
}
void AST_Exec::accept_stmt(StmtVisitor* v) {
......
......@@ -2465,12 +2465,18 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
if (source->ast->type == AST_TYPE::ClassDef) {
// A classdef always starts with "__module__ = __name__"
Box* module_name = source->parent_module->getattr("__name__", NULL);
assert(module_name->cls == str_cls);
AST_Assign* module_assign = new AST_Assign();
module_assign->targets.push_back(
new AST_Name(source->getInternedStrings().get("__module__"), AST_TYPE::Store, source->ast->lineno));
module_assign->value = new AST_Str(static_cast<BoxedString*>(module_name)->s);
if (source->scoping->areGlobalsFromModule()) {
Box* module_name = source->parent_module->getattr("__name__", NULL);
assert(module_name->cls == str_cls);
module_assign->value = new AST_Str(static_cast<BoxedString*>(module_name)->s);
} else {
module_assign->value
= new AST_Name(source->getInternedStrings().get("__name__"), AST_TYPE::Load, source->ast->lineno);
}
module_assign->lineno = 0;
visitor.push_back(module_assign);
......@@ -2513,7 +2519,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
visitor.push_back(return_stmt);
}
if (VERBOSITY("cfg") >= 2) {
if (VERBOSITY("cfg") >= 3) {
printf("Before cfg checking and transformations:\n");
rtn->print();
}
......@@ -2634,7 +2640,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
break;
}
if (VERBOSITY()) {
if (VERBOSITY("cfg") >= 2) {
// rtn->print();
printf("Joining blocks %d and %d\n", b->idx, b2->idx);
}
......@@ -2653,7 +2659,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
}
}
if (VERBOSITY("cfg") >= 1) {
if (VERBOSITY("cfg") >= 2) {
printf("Final cfg:\n");
rtn->print();
}
......
......@@ -53,7 +53,7 @@ long Timer::end() {
long us = 1000000L * (end.tv_sec - start_time.tv_sec) + (end.tv_usec - start_time.tv_usec);
Timer::level--;
if (VERBOSITY("time") >= 1 && desc) {
if (VERBOSITY("time") >= 2 && desc) {
if (us > min_usec) {
for (int i = 0; i < Timer::level; i++) {
putchar(' ');
......@@ -116,7 +116,7 @@ void removeDirectoryIfExists(const std::string& path) {
if (llvm::sys::fs::is_directory(status)) {
removeDirectoryIfExists(it->path());
} else {
if (VERBOSITY())
if (VERBOSITY() >= 2)
llvm::errs() << "Removing file " << it->path() << '\n';
code = llvm::sys::fs::remove(it->path(), false);
assert(!code);
......@@ -126,7 +126,7 @@ void removeDirectoryIfExists(const std::string& path) {
assert(!code);
}
if (VERBOSITY())
if (VERBOSITY() >= 2)
llvm::errs() << "Removing directory " << path << '\n';
code = llvm::sys::fs::remove(path, false);
assert(!code);
......
......@@ -727,9 +727,8 @@ public:
};
Box* globals() {
BoxedModule* m = getCurrentModule();
// TODO is it ok that we don't return a real dict here?
return makeAttrWrapper(m);
return getGlobalsDict();
}
Box* locals() {
......@@ -776,6 +775,8 @@ Box* execfile(Box* _fn) {
// Run directly inside the current module:
AST_Module* ast = caching_parse_file(fn->s.data());
ASSERT(getExecutionPoint().cf->clfunc->source->scoping->areGlobalsFromModule(), "need to pass custom globals in");
compileAndRunModule(ast, getCurrentModule());
return None;
......
......@@ -95,7 +95,8 @@ void generatorEntry(BoxedGenerator* g) {
BoxedFunctionBase* func = g->function;
Box** args = g->args ? &g->args->elts[0] : nullptr;
callCLFunc(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g, g->arg1, g->arg2, g->arg3, args);
callCLFunc(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g, func->globals, g->arg1, g->arg2,
g->arg3, args);
} catch (ExcInfo e) {
// unhandled exception: propagate the exception to the caller
g->exception = e;
......
......@@ -442,6 +442,7 @@ static bool loadNext(Box* mod, Box* altmod, std::string& name, std::string& buf,
static void ensureFromlist(Box* module, Box* fromlist, std::string& buf, bool recursive);
Box* importModuleLevel(const std::string& name, Box* globals, Box* from_imports, int level) {
assert(!globals || globals == None || isSubclass(globals->cls, module_cls));
bool return_first = from_imports == None;
static StatCounter slowpath_import("slowpath_import");
......
......@@ -2544,6 +2544,9 @@ static CompiledFunction* pickVersion(CLFunction* f, int num_output_args, Box* oa
}
EffortLevel new_effort = initialEffort();
// Only the interpreter currently supports non-module-globals:
if (!f->source->scoping->areGlobalsFromModule())
new_effort = EffortLevel::INTERPRETED;
std::vector<ConcreteCompilerType*> arg_types;
for (int i = 0; i < num_output_args; i++) {
......@@ -2668,7 +2671,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
if (!func->isGenerator) {
if (argspec.num_keywords == 0 && !argspec.has_starargs && !argspec.has_kwargs && argspec.num_args == f->num_args
&& !f->takes_varargs && !f->takes_kwargs) {
return callCLFunc(f, rewrite_args, argspec.num_args, closure, NULL, arg1, arg2, arg3, args);
return callCLFunc(f, rewrite_args, argspec.num_args, closure, NULL, func->globals, arg1, arg2, arg3, args);
}
}
slowpath_callfunc_slowpath.log();
......@@ -2925,21 +2928,24 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
if (func->isGenerator) {
res = createGenerator(func, oarg1, oarg2, oarg3, oargs);
} else {
res = callCLFunc(f, rewrite_args, num_output_args, closure, NULL, oarg1, oarg2, oarg3, oargs);
res = callCLFunc(f, rewrite_args, num_output_args, closure, NULL, func->globals, oarg1, oarg2, oarg3, oargs);
}
return res;
}
Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_args, BoxedClosure* closure,
BoxedGenerator* generator, Box* oarg1, Box* oarg2, Box* oarg3, Box** oargs) {
BoxedGenerator* generator, BoxedDict* globals, Box* oarg1, Box* oarg2, Box* oarg3, Box** oargs) {
CompiledFunction* chosen_cf = pickVersion(f, num_output_args, oarg1, oarg2, oarg3, oargs);
assert(chosen_cf->is_interpreted == (chosen_cf->code == NULL));
if (chosen_cf->is_interpreted) {
return astInterpretFunction(chosen_cf, num_output_args, closure, generator, oarg1, oarg2, oarg3, oargs);
return astInterpretFunction(chosen_cf, num_output_args, closure, generator, globals, oarg1, oarg2, oarg3,
oargs);
}
ASSERT(!globals, "need to update the calling conventions if we want to pass globals");
if (rewrite_args) {
rewrite_args->rewriter->addDependenceOn(chosen_cf->dependent_callsites);
......@@ -3913,8 +3919,12 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
made->setattr(static_cast<BoxedString*>(k)->s, p.second, NULL);
}
if (!made->hasattr("__module__"))
made->giveAttr("__module__", boxString(getCurrentModule()->name()));
if (!made->hasattr("__module__")) {
Box* gl = getGlobalsDict();
Box* attr = PyDict_GetItemString(gl, "__name__");
if (attr)
made->giveAttr("__module__", attr);
}
if (!made->hasattr("__doc__"))
made->giveAttr("__doc__", None);
......@@ -4247,14 +4257,24 @@ Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
return typeCallInternal(NULL, NULL, ArgPassSpec(n + 1, 0, false, true), arg1, arg2, arg3, args, NULL);
}
extern "C" void delGlobal(BoxedModule* m, const std::string* name) {
if (!m->getattr(*name)) {
raiseExcHelper(NameError, "name '%s' is not defined", name->c_str());
extern "C" void delGlobal(Box* globals, const std::string* name) {
if (globals->cls == module_cls) {
BoxedModule* m = static_cast<BoxedModule*>(globals);
if (!m->getattr(*name)) {
raiseExcHelper(NameError, "name '%s' is not defined", name->c_str());
}
m->delattr(*name, NULL);
} else {
assert(globals->cls == dict_cls);
BoxedDict* d = static_cast<BoxedDict*>(globals);
auto it = d->d.find(boxString(*name));
assertNameDefined(it != d->d.end(), name->c_str(), NameError, false /* local_var_msg */);
d->d.erase(it);
}
m->delattr(*name, NULL);
}
extern "C" Box* getGlobal(BoxedModule* m, const std::string* name) {
extern "C" Box* getGlobal(Box* globals, const std::string* name) {
static StatCounter slowpath_getglobal("slowpath_getglobal");
slowpath_getglobal.log();
static StatCounter nopatch_getglobal("nopatch_getglobal");
......@@ -4272,25 +4292,44 @@ extern "C" Box* getGlobal(BoxedModule* m, const std::string* name) {
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "getGlobal"));
Box* r;
if (rewriter.get()) {
// rewriter->trap();
if (globals->cls == module_cls) {
BoxedModule* m = static_cast<BoxedModule*>(globals);
if (rewriter.get()) {
RewriterVar* r_mod = rewriter->getArg(0);
GetattrRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getReturnDestination());
r = m->getattr(*name, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
}
if (r) {
if (rewriter.get()) {
rewriter->commitReturning(rewrite_args.out_rtn);
// Guard on it being a module rather than a dict
// TODO is this guard necessary? I'm being conservative now, but I think we can just
// insist that the type passed in is fixed for any given instance of a getGlobal call.
r_mod->addAttrGuard(BOX_CLS_OFFSET, (intptr_t)module_cls);
GetattrRewriteArgs rewrite_args(rewriter.get(), r_mod, rewriter->getReturnDestination());
r = m->getattr(*name, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
}
if (r) {
if (rewriter.get()) {
rewriter->commitReturning(rewrite_args.out_rtn);
}
return r;
}
} else {
r = m->getattr(*name, NULL);
nopatch_getglobal.log();
if (r) {
return r;
}
return r;
}
} else {
r = m->getattr(*name, NULL);
nopatch_getglobal.log();
if (r) {
return r;
assert(globals->cls == dict_cls);
BoxedDict* d = static_cast<BoxedDict*>(globals);
rewriter.reset(NULL);
REWRITE_ABORTED("Rewriting not implemented for getGlobals with a dict globals yet");
auto it = d->d.find(boxString(*name));
if (it != d->d.end()) {
return it->second;
}
}
......@@ -4342,6 +4381,10 @@ extern "C" Box* importFrom(Box* _m, const std::string* name) {
}
extern "C" Box* importStar(Box* _from_module, BoxedModule* to_module) {
// TODO(kmod): it doesn't seem too bad to update this to take custom globals;
// it looks like mostly a matter of changing the getattr calls to getitem.
RELEASE_ASSERT(getGlobals() == to_module, "importStar doesn't support custom globals yet");
assert(_from_module->cls == module_cls);
BoxedModule* from_module = static_cast<BoxedModule*>(_from_module);
......@@ -4410,8 +4453,7 @@ extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val) {
setitem(boxedLocals, boxString(attr), val);
}
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule* parent_module) {
assert(parent_module->cls == module_cls);
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, Box* globals) {
assert(boxedLocals != NULL);
if (boxedLocals->cls == dict_cls) {
......@@ -4436,7 +4478,7 @@ extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule*
// TODO exception name?
std::string attr_string(attr);
return getGlobal(parent_module, &attr_string);
return getGlobal(globals, &attr_string);
}
extern "C" void boxedLocalsDel(Box* boxedLocals, const char* attr) {
......
......@@ -72,8 +72,6 @@ extern "C" BoxedInt* len(Box* obj);
extern "C" i64 unboxedLen(Box* obj);
extern "C" Box* binop(Box* lhs, Box* rhs, int op_type);
extern "C" Box* augbinop(Box* lhs, Box* rhs, int op_type);
extern "C" Box* getGlobal(BoxedModule* m, const std::string* name);
extern "C" void delGlobal(BoxedModule* m, const std::string* name);
extern "C" Box* getitem(Box* value, Box* slice);
extern "C" void setitem(Box* target, Box* slice, Box* value);
extern "C" void delitem(Box* target, Box* slice);
......@@ -140,7 +138,7 @@ bool isUserDefined(BoxedClass* cls);
Box* processDescriptor(Box* obj, Box* inst, Box* owner);
Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_args, BoxedClosure* closure,
BoxedGenerator* generator, Box* oarg1, Box* oarg2, Box* oarg3, Box** oargs);
BoxedGenerator* generator, BoxedDict* globals, Box* oarg1, Box* oarg2, Box* oarg3, Box** oargs);
static const char* objectNewParameterTypeErrorMsg() {
if (PYTHON_VERSION_HEX >= version_hex(2, 7, 4)) {
......@@ -168,8 +166,13 @@ inline std::tuple<Box*, Box*, Box*, Box**> getTupleFromArgsArray(Box** args, int
return std::make_tuple(arg1, arg2, arg3, argtuple);
}
// The `globals` argument can be either a BoxedModule or a BoxedDict
extern "C" Box* getGlobal(Box* globals, const std::string* name);
extern "C" void delGlobal(Box* globals, const std::string* name);
extern "C" void boxedLocalsSet(Box* boxedLocals, const char* attr, Box* val);
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, BoxedModule* parent_module);
extern "C" Box* boxedLocalsGet(Box* boxedLocals, const char* attr, Box* globals);
extern "C" void boxedLocalsDel(Box* boxedLocals, const char* attr);
}
#endif
......@@ -24,6 +24,7 @@
#include "capi/typeobject.h"
#include "capi/types.h"
#include "codegen/unwinding.h"
#include "core/options.h"
#include "core/stats.h"
#include "core/types.h"
......@@ -272,7 +273,7 @@ extern "C" BoxedFunctionBase::BoxedFunctionBase(CLFunction* f)
: in_weakreflist(NULL), f(f), closure(NULL), isGenerator(false), ndefaults(0), defaults(NULL), modname(NULL),
name(NULL), doc(NULL) {
if (f->source) {
this->modname = f->source->parent_module->getattr("__name__", NULL);
this->modname = PyDict_GetItemString(getGlobalsDict(), "__name__");
this->doc = f->source->getDocString();
} else {
this->modname = boxStringPtr(&builtinStr);
......@@ -295,7 +296,7 @@ extern "C" BoxedFunctionBase::BoxedFunctionBase(CLFunction* f, std::initializer_
}
if (f->source) {
this->modname = f->source->parent_module->getattr("__name__", NULL);
this->modname = PyDict_GetItemString(getGlobalsDict(), "__name__");
this->doc = f->source->getDocString();
} else {
this->modname = boxStringPtr(&builtinStr);
......@@ -309,9 +310,11 @@ BoxedFunction::BoxedFunction(CLFunction* f) : BoxedFunction(f, {}) {
}
BoxedFunction::BoxedFunction(CLFunction* f, std::initializer_list<Box*> defaults, BoxedClosure* closure,
bool isGenerator)
bool isGenerator, BoxedDict* globals)
: BoxedFunctionBase(f, defaults, closure, isGenerator) {
this->globals = globals;
// TODO eventually we want this to assert(f->source), I think, but there are still
// some builtin functions that are BoxedFunctions but really ought to be a type that
// we don't have yet.
......@@ -354,6 +357,9 @@ extern "C" void functionGCHandler(GCVisitor* v, Box* b) {
if (f->closure)
v->visit(f->closure);
if (f->globals)
v->visit(f->globals);
// It's ok for f->defaults to be NULL here even if f->ndefaults isn't,
// since we could be collecting from inside a BoxedFunctionBase constructor
if (f->ndefaults) {
......@@ -411,12 +417,14 @@ extern "C" void moduleGCHandler(GCVisitor* v, Box* b) {
// This mustn't throw; our IR generator generates calls to it without "invoke" even when there are exception handlers /
// finally-blocks in scope.
// TODO: should we use C++11 `noexcept' here?
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator,
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator, BoxedDict* globals,
std::initializer_list<Box*> defaults) {
if (closure)
assert(closure->cls == closure_cls);
if (globals)
assert(globals->cls == dict_cls);
return new BoxedFunction(f, defaults, closure, isGenerator);
return new BoxedFunction(f, defaults, closure, isGenerator, globals);
}
extern "C" CLFunction* unboxCLFunction(Box* b) {
......@@ -669,8 +677,8 @@ extern "C" Box* createUserClass(const std::string* name, Box* _bases, Box* _attr
// an error, then look up ob_type (aka cls)
metaclass = bases->elts[0]->cls;
} else {
BoxedModule* m = getCurrentModule();
metaclass = m->getattr("__metaclass__");
Box* gl = getGlobalsDict();
metaclass = PyDict_GetItemString(gl, "__metaclass__");
if (!metaclass) {
metaclass = classobj_cls;
......@@ -972,7 +980,8 @@ Box* typeRepr(BoxedClass* self) {
Box* m = self->getattr("__module__");
if (m && m->cls == str_cls) {
BoxedString* sm = static_cast<BoxedString*>(m);
os << sm->s << '.';
if (sm->s != "__builtin__")
os << sm->s << '.';
}
os << self->tp_name;
......
......@@ -127,7 +127,7 @@ char* getWriteableStringContents(BoxedString* s);
extern "C" void listAppendInternal(Box* self, Box* v);
extern "C" void listAppendArrayInternal(Box* self, Box** v, int nelts);
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator,
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator, BoxedDict* globals,
std::initializer_list<Box*> defaults);
extern "C" CLFunction* unboxCLFunction(Box* b);
extern "C" Box* createUserClass(const std::string* name, Box* base, Box* attr_dict);
......@@ -584,7 +584,13 @@ public:
Box** in_weakreflist;
CLFunction* f;
// TODO these should really go in BoxedFunction but it's annoying because they don't get
// initializd until after BoxedFunctionBase's constructor is run which means they could have
// garbage values when the GC is run (BoxedFunctionBase's constructor might call the GC).
// So ick... needs to be fixed.
BoxedClosure* closure;
BoxedDict* globals;
bool isGenerator;
int ndefaults;
......@@ -608,7 +614,7 @@ public:
BoxedFunction(CLFunction* f);
BoxedFunction(CLFunction* f, std::initializer_list<Box*> defaults, BoxedClosure* closure = NULL,
bool isGenerator = false);
bool isGenerator = false, BoxedDict* globals = NULL);
DEFAULT_CLASS(function_cls);
};
......
......@@ -13,3 +13,9 @@ d = collections.defaultdict(lambda: [])
print d[1]
d[2].append(3)
print sorted(d.items())
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT.__name__
n = NT(1, "hi")
print n.field1, n.field2, len(n), list(n), n[0], n[-1]
print n
# expected: fail
g = {}
exec """from import_target import *""" in g
del g['__builtins__']
print sorted(g.keys())
d = {}
exec "a = 5" in d
print d['a']
def f():
d2 = {}
exec "b = 6" in d2
print d2['b']
f()
print 'Test getting'
g = {'a': 1}
l = {'b': 2}
exec """global a
print a
print b""" in g, l
print 'Test setting'
g = {}
l = {}
exec """global a
a = 1
b = 2""" in g, l
del g['__builtins__']
print g
print l
print 'Test deleting'
g = {'a': 1}
l = {'b': 2}
exec """global a
del a
del b""" in g, l
del g['__builtins__']
print g
print l
print 'Test global access in a function'
g = {'a': 4, 'b': 5}
l = {}
exec """
def f(i):
global a, b, c
print i, 'a =', a
del b
c = 20
for i in xrange(100):
pass
f(-1)
""" in g, l
# Try to trigger a reopt and osr:
for i in xrange(1000):
g['b'] = 6
l['f'](i)
print l['f'].__module__
print 'Test global access in comprehensions'
g = {'a' : 4, 'b': 5, 'c': 6}
exec """
global a
global b
global c
print [a for i in xrange(1)]
print {b for i in xrange(1)}
print {i : b for i in xrange(1)}
print
""" in g, {}
a = 0
exec "a = 1" in None, None
print a
# Adapted from six.py:
def exec_(_code_, _globs_, _locs_):
exec("""exec _code_ in _globs_, _locs_""")
g = {'a': 1}
l = {'b': 2}
exec_("""global a
print a
print b""", g, l)
exec """print __name__"""
exec """print __name__""" in {}, {}
# Test classdefs in execs:
b = 3
a = 2
s = """class C(object):
print "b =", b
if b:
c = 2
else:
a = -1
print a, b
print C.__module__, C.__name__, repr(C)
"""
exec s in {'a': 1, 'b': 5}, {'b': 2}
exec s
# Test old-style classdefs in execs:
b = 3
a = 2
s = """class C():
print "b =", b
if b:
c = 2
else:
a = -1
print a, b
print C.__module__
"""
exec s in {'a': 1, 'b': 5}, {'b': 2}
exec s
# test eval+exec in exec:
a = 5
exec """print eval('a')""" in {'a': 6}, {}
exec """exec 'print a' """ in {'a': 6}, {}
# test ordering:
def show(obj, msg):
print msg
return obj
exec show("print 'in exec'", "body") in show(None, "globals"), show(None, "locals")
s = """
def f():
a = 1
exec "a = 2"
def g():
print a
g()
f()
"""
try:
exec s
except Exception as e:
print repr(e)
s = """
def f():
a = 1
d = {}
exec "a = 2" in {}, d
def g():
print a
g()
print d
f()
"""
try:
exec s
except Exception as e:
print repr(e)
# expected: fail
# - surprisingly, this is allowed? doesn't seem very different from exec_syntax_error.py
# We print 1, CPython and PyPy print 2
s = """
def f():
a = 1
exec "a = 2" in None
def g():
print a
g()
f()
"""
try:
exec s
except Exception as e:
print repr(e)
# expected: fail
# - needs sys._getframe
import collections
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT
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