Commit 55805dc6 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #349 from tjhance/exec-basic

Conflicts:
	src/codegen/ast_interpreter.cpp
	src/codegen/irgen/irgenerator.cpp
	src/codegen/pypa-parser.cpp
parents 9e30d6dc 2bfcd7d1
......@@ -340,6 +340,8 @@ public:
return true;
}
virtual bool visit_exec(AST_Exec* node) { return true; }
friend class DefinednessBBAnalyzer;
};
......@@ -376,7 +378,8 @@ DefinednessAnalysis::DefinednessAnalysis(const ParamNames& arg_names, CFG* cfg,
for (const auto& p : results) {
RequiredSet required;
for (const auto& p2 : p.second) {
if (scope_info->refersToGlobal(p2.first))
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(p2.first);
if (vst == ScopeInfo::VarScopeType::GLOBAL || vst == ScopeInfo::VarScopeType::NAME)
continue;
// printf("%d %s %d\n", p.first->idx, p2.first.c_str(), p2.second);
......
This diff is collapsed.
......@@ -23,6 +23,7 @@ namespace pyston {
class AST;
class AST_Module;
class AST_Expression;
class AST_Suite;
class ScopeInfo {
public:
......@@ -61,13 +62,58 @@ public:
// import dis
// print dis.dis(g)
enum class VarScopeType { FAST, GLOBAL, CLOSURE, DEREF, NAME };
virtual bool refersToGlobal(InternedString name) = 0;
virtual bool refersToClosure(InternedString name) = 0;
virtual bool saveInClosure(InternedString name) = 0;
enum class VarScopeType {
FAST,
GLOBAL,
CLOSURE,
DEREF,
NAME,
// This is never returned by any function in this class, but it is used by
// the ast_interpreter currently.
UNKNOWN
};
virtual VarScopeType getScopeTypeOfName(InternedString name) = 0;
// Returns true if the variable should be passed via a closure to this scope.
// Note that:
// (a) This can be false even if there is an entry in the closure object
// passed to the scope, if the variable is not actually used in this
// scope or any child scopes. This can happen, because the variable
// could be in the closure to be accessed by a different function, e.g.
//
// def f();
// a = 0
// b = 0
// def g():
// print a
// def h():
// print b
// # locals() should not contain `a` even though `h` is
// # passed a closure object with `a` in it
// print locals()
//
// (b) This can be true even if it is not used in this scope, if it
// is used in a child scope. For example:
//
// def f():
// a = 0
// def g():
// def h():
// print a
// print locals() # should contain `a`
//
// This is useful because it determines whether a variable from a closure
// into the locals() dictionary.
virtual bool isPassedToViaClosure(InternedString name) = 0;
// Returns true if the scope may contain NAME variables.
// In particular, it returns true for ClassDef scope, for any scope
// with an `exec` statement or `import *` statement in it, or for any
// `exec` or `eval` scope.
virtual bool usesNameLookup() = 0;
virtual InternedString mangleName(InternedString id) = 0;
virtual InternedString internString(llvm::StringRef) = 0;
};
......@@ -99,13 +145,12 @@ public:
ScopingAnalysis(AST_Module* m);
ScopingAnalysis(AST_Expression* e);
ScopingAnalysis(AST_Suite* s);
ScopeInfo* getScopeInfoForNode(AST* node);
InternedStringPool& getInternedStrings();
};
ScopingAnalysis* runScopingAnalysis(AST_Module* m);
bool containsYield(AST* ast);
}
......
......@@ -590,6 +590,14 @@ private:
_visit_alias(alias);
}
void visit_exec(AST_Exec* node) override {
getType(node->body);
if (node->globals)
getType(node->globals);
if (node->locals)
getType(node->locals);
}
void visit_invoke(AST_Invoke* node) override { node->stmt->accept_stmt(this); }
void visit_jump(AST_Jump* node) override {}
......
This diff is collapsed.
......@@ -15,6 +15,8 @@
#ifndef PYSTON_CODEGEN_ASTINTERPRETER_H
#define PYSTON_CODEGEN_ASTINTERPRETER_H
#include "codegen/unwinding.h"
namespace pyston {
namespace gc {
......@@ -33,9 +35,9 @@ 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, BoxedDict* locals);
Box* astInterpretFunctionEval(CompiledFunction* cf, Box* boxedLocals);
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
BoxedDict* locals);
FrameStackState frame_state);
AST_stmt* getCurrentStatementForInterpretedFrame(void* frame_ptr);
CompiledFunction* getCFForInterpretedFrame(void* frame_ptr);
......
......@@ -41,6 +41,7 @@ SourceInfo::SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, AST* ast, const
case AST_TYPE::Lambda:
case AST_TYPE::Module:
case AST_TYPE::Expression:
case AST_TYPE::Suite:
is_generator = false;
break;
case AST_TYPE::FunctionDef:
......
......@@ -70,7 +70,7 @@ struct GlobalState {
llvm::Type* llvm_class_type, *llvm_class_type_ptr;
llvm::Type* llvm_opaque_type;
llvm::Type* llvm_str_type_ptr;
llvm::Type* frame_info_type;
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;
llvm::Type* llvm_excinfo_type;
......
......@@ -1822,11 +1822,32 @@ public:
Box* deserializeFromFrame(const FrameVals& vals) override {
assert(vals.size() == numFrameArgs());
abort();
return reinterpret_cast<Box*>(vals[0]);
}
} _GENERATOR;
ConcreteCompilerType* GENERATOR = &_GENERATOR;
class FrameInfoType : public ConcreteCompilerType {
public:
llvm::Type* llvmType() override { return g.llvm_frame_info_type->getPointerTo(); }
std::string debugName() override { return "FrameInfo"; }
ConcreteCompilerType* getConcreteType() override { return this; }
ConcreteCompilerType* getBoxType() override { return FRAME_INFO; }
void drop(IREmitter& emitter, VAR* var) override {
// pass
}
void grab(IREmitter& emitter, VAR* var) override {
// pass
}
Box* deserializeFromFrame(const FrameVals& vals) override {
RELEASE_ASSERT(false, "should not be called"); // This function shouldn't be called.
}
} _FRAME_INFO;
ConcreteCompilerType* FRAME_INFO = &_FRAME_INFO;
class StrConstantType : public ValuedCompilerType<const std::string*> {
public:
std::string debugName() override { return "str_constant"; }
......
......@@ -415,6 +415,9 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
ptr = entry_emitter->getBuilder()->CreateBitCast(ptr, g.llvm_generator_type_ptr->getPointerTo());
} else if (p.second == CLOSURE) {
ptr = entry_emitter->getBuilder()->CreateBitCast(ptr, g.llvm_closure_type_ptr->getPointerTo());
} else if (p.second == FRAME_INFO) {
ptr = entry_emitter->getBuilder()->CreateBitCast(
ptr, g.llvm_frame_info_type->getPointerTo()->getPointerTo());
} else {
assert(p.second->llvmType() == g.llvm_value_type_ptr);
}
......@@ -422,6 +425,13 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
assert(from_arg->getType() == p.second->llvmType());
}
if (from_arg->getType() == g.llvm_frame_info_type->getPointerTo()) {
assert(p.first.str() == FRAME_INFO_PTR_NAME);
irstate->setFrameInfoArgument(from_arg);
// Don't add the frame info to the symbol table since we will store it separately:
continue;
}
ConcreteCompilerType* phi_type;
phi_type = getTypeAtBlockStart(types, p.first, target_block);
......@@ -595,6 +605,11 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
assert(phis);
for (const auto& p : entry_descriptor->args) {
// Don't add the frame info to the symbol table since we will store it separately
// (we manually added it during the calculation of osr_syms):
if (p.first.str() == FRAME_INFO_PTR_NAME)
continue;
ConcreteCompilerType* analyzed_type = getTypeAtBlockStart(types, p.first, block);
// printf("For %s, given %s, analyzed for %s\n", p.first.c_str(), p.second->debugName().c_str(),
......@@ -634,7 +649,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
if (source->is_generator)
names.insert(source->getInternedStrings().get(PASSED_GENERATOR_NAME));
for (const auto& s : names) {
for (const InternedString& s : names) {
// printf("adding guessed phi for %s\n", s.c_str());
ConcreteCompilerType* type = getTypeAtBlockStart(types, s, block);
llvm::PHINode* phi
......@@ -698,7 +713,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// You might think I need to check whether `name' is being assigned globally or locally,
// since a global assign doesn't affect the symbol table. However, the CFG pass only
// generates invoke-assigns to temporary variables. Just to be sure, we assert:
assert(!source->getScopeInfo()->refersToGlobal(name));
assert(source->getScopeInfo()->getScopeTypeOfName(name) != ScopeInfo::VarScopeType::GLOBAL);
// TODO: inefficient
sym_table = new SymbolTable(*sym_table);
......
......@@ -31,39 +31,12 @@ const std::map<std::string, FutureOption> future_options
{ "nested_scopes", { version_hex(2, 1, 0), version_hex(2, 2, 0), FF_NESTED_SCOPES } },
{ "with_statement", { version_hex(2, 5, 0), version_hex(3, 6, 0), FF_WITH_STATEMENT } } };
// Helper function:
void raiseSyntaxError(const char* file, AST* node_at, const char* msg, ...) {
va_list ap;
va_start(ap, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, ap);
// TODO I'm not sure that it's safe to raise an exception here, since I think
// there will be things that end up not getting cleaned up.
// Then again, there are a huge number of things that don't get cleaned up even
// if an exception doesn't get thrown...
// TODO output is still a little wrong, should be, for example
//
// File "../test/tests/future_non_existent.py", line 1
// from __future__ import rvalue_references # should cause syntax error
//
// but instead it is
//
// Traceback (most recent call last):
// File "../test/tests/future_non_existent.py", line -1, in :
// from __future__ import rvalue_references # should cause syntax error
::pyston::raiseSyntaxError(buf, node_at->lineno, node_at->col_offset, file, "");
}
void raiseFutureImportErrorNotFound(const char* file, AST* node, const char* name) {
raiseSyntaxError(file, node, "future feature %s is not defined", name);
raiseSyntaxErrorHelper(file, "", node, "future feature %s is not defined", name);
}
void raiseFutureImportErrorNotBeginning(const char* file, AST* node) {
raiseSyntaxError(file, node, "from __future__ imports must occur at the beginning of the file");
raiseSyntaxErrorHelper(file, "", node, "from __future__ imports must occur at the beginning of the file");
}
class BadFutureImportVisitor : public NoopASTVisitor {
......
......@@ -30,6 +30,7 @@
#include "codegen/parser.h"
#include "codegen/patchpoints.h"
#include "codegen/stackmaps.h"
#include "codegen/unwinding.h"
#include "core/ast.h"
#include "core/cfg.h"
#include "core/common.h"
......@@ -44,7 +45,8 @@ namespace pyston {
// TODO terrible place for these!
ParamNames::ParamNames(AST* ast) : takes_param_names(true) {
if (ast->type == AST_TYPE::Module || ast->type == AST_TYPE::ClassDef || ast->type == AST_TYPE::Expression) {
if (ast->type == AST_TYPE::Module || ast->type == AST_TYPE::ClassDef || ast->type == AST_TYPE::Expression
|| ast->type == AST_TYPE::Suite) {
kwarg = "";
vararg = "";
} else if (ast->type == AST_TYPE::FunctionDef || ast->type == AST_TYPE::Lambda) {
......@@ -95,6 +97,7 @@ const std::string SourceInfo::getName() {
return "<lambda>";
case AST_TYPE::Module:
case AST_TYPE::Expression:
case AST_TYPE::Suite:
return "<module>";
default:
RELEASE_ASSERT(0, "%d", ast->type);
......@@ -304,7 +307,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
bm->future_flags = getFutureFlags(m, bm->fn.c_str());
ScopingAnalysis* scoping = runScopingAnalysis(m);
ScopingAnalysis* scoping = new ScopingAnalysis(m);
SourceInfo* si = new SourceInfo(bm, scoping, m, m->body);
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
......@@ -323,40 +326,71 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
((void (*)())cf->code)();
}
static Box* compileAndRunExpression(AST_Expression* expr, BoxedModule* bm, BoxedDict* locals) {
template <typename AST_Type>
Box* evalOrExec(AST_Type* source, std::vector<AST_stmt*>& body, BoxedModule* bm, Box* boxedLocals) {
CompiledFunction* cf;
{ // scope for limiting the locked region:
LOCK_REGION(codegen_rwlock.asWrite());
Timer _t("for compileEval()");
Timer _t("for evalOrExec()");
ScopingAnalysis* scoping = new ScopingAnalysis(expr);
ScopingAnalysis* scoping = new ScopingAnalysis(source);
AST_Return* stmt = new AST_Return();
stmt->value = expr->body;
SourceInfo* si = new SourceInfo(bm, scoping, expr, { stmt });
SourceInfo* si = new SourceInfo(bm, scoping, source, body);
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
// TODO Right now we only support going into an exec or eval through the
// intepretter, since the interpretter has a special function which lets
// us set the locals object. We should probably support it for optimized
// code as well, so we could use initialEffort() here instead of hard-coding
// INTERPRETED. This could actually be useful if we actually cache the parse
// results (since sometimes eval or exec might be called on constant strings).
EffortLevel effort = EffortLevel::INTERPRETED;
cf = compileFunction(cl_f, new FunctionSpecialization(VOID), effort, NULL);
assert(cf->clfunc->versions.size());
}
return astInterpretFunctionEval(cf, locals);
return astInterpretFunctionEval(cf, boxedLocals);
}
Box* runEval(const char* code, BoxedDict* locals, BoxedModule* module) {
// Main entrypoints for eval and exec.
Box* eval(Box* boxedCode) {
Box* boxedLocals = fastLocalsToBoxedLocals();
BoxedModule* module = getCurrentModule();
// 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
// TODO this memory leaks
RELEASE_ASSERT(boxedCode->cls == str_cls, "");
const char* code = static_cast<BoxedString*>(boxedCode)->s.c_str();
AST_Module* parsedModule = parse_string(code);
assert(parsedModule->body[0]->type == AST_TYPE::Expr);
RELEASE_ASSERT(parsedModule->body[0]->type == AST_TYPE::Expr, "");
AST_Expression* parsedExpr = new AST_Expression(std::move(parsedModule->interned_strings));
parsedExpr->body = static_cast<AST_Expr*>(parsedModule->body[0])->value;
return compileAndRunExpression(parsedExpr, module, locals);
// We need body (list of statements) to compile.
// Obtain this by simply making a single statement which contains the expression.
AST_Return* stmt = new AST_Return();
stmt->value = parsedExpr->body;
std::vector<AST_stmt*> body = { stmt };
return evalOrExec<AST_Expression>(parsedExpr, body, module, boxedLocals);
}
Box* exec(Box* boxedCode) {
Box* boxedLocals = fastLocalsToBoxedLocals();
BoxedModule* module = getCurrentModule();
// TODO same issues as in `eval`
RELEASE_ASSERT(boxedCode->cls == str_cls, "");
const char* code = static_cast<BoxedString*>(boxedCode)->s.c_str();
AST_Module* parsedModule = parse_string(code);
AST_Suite* parsedSuite = new AST_Suite(std::move(parsedModule->interned_strings));
parsedSuite->body = parsedModule->body;
return evalOrExec<AST_Suite>(parsedSuite, parsedSuite->body, module, boxedLocals);
}
// If a function version keeps failing its speculations, kill it (remove it
......@@ -456,9 +490,8 @@ CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
assert(exit->parent_cf->clfunc);
CompiledFunction*& new_cf = exit->parent_cf->clfunc->osr_versions[exit->entry];
if (new_cf == NULL) {
EffortLevel new_effort = EffortLevel::MAXIMAL;
if (exit->parent_cf->effort == EffortLevel::INTERPRETED)
new_effort = EffortLevel::MINIMAL;
EffortLevel new_effort = exit->parent_cf->effort == EffortLevel::INTERPRETED ? EffortLevel::MINIMAL
: EffortLevel::MAXIMAL;
CompiledFunction* compiled = compileFunction(exit->parent_cf->clfunc, NULL, new_effort, exit->entry);
assert(compiled == new_cf);
......
......@@ -37,7 +37,8 @@ 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&);
Box* runEval(const char* code, BoxedDict* locals, BoxedModule* module);
extern "C" Box* exec(Box* boxedCode);
extern "C" Box* eval(Box* boxedCode);
}
#endif
This diff is collapsed.
......@@ -46,6 +46,7 @@ typedef std::unordered_map<InternedString, ConcreteCompilerVariable*> ConcreteSy
extern const std::string CREATED_CLOSURE_NAME;
extern const std::string PASSED_CLOSURE_NAME;
extern const std::string PASSED_GENERATOR_NAME;
extern const std::string FRAME_INFO_PTR_NAME;
// Class that holds state of the current IR generation, that might not be local
......@@ -61,13 +62,16 @@ private:
llvm::AllocaInst* scratch_space;
llvm::Value* frame_info;
llvm::Value* boxed_locals;
llvm::Value* frame_info_arg;
int scratch_size;
public:
IRGenState(CompiledFunction* cf, SourceInfo* source_info, ParamNames* param_names, GCBuilder* gc,
llvm::MDNode* func_dbg_info)
: cf(cf), source_info(source_info), param_names(param_names), gc(gc), func_dbg_info(func_dbg_info),
scratch_space(NULL), frame_info(NULL), scratch_size(0) {
scratch_space(NULL), frame_info(NULL), frame_info_arg(NULL), scratch_size(0) {
assert(cf->func);
assert(!cf->clfunc); // in this case don't need to pass in sourceinfo
}
......@@ -82,6 +86,7 @@ public:
llvm::Value* getScratchSpace(int min_bytes);
llvm::Value* getFrameInfoVar();
llvm::Value* getBoxedLocalsVar();
ConcreteCompilerType* getReturnType() { return cf->getReturnType(); }
......@@ -93,6 +98,8 @@ public:
llvm::MDNode* getFuncDbgInfo() { return func_dbg_info; }
ParamNames* getParamNames() { return param_names; }
void setFrameInfoArgument(llvm::Value* v) { frame_info_arg = v; }
};
// turns CFGBlocks into LLVM IR
......
......@@ -643,6 +643,7 @@ struct stmt_dispatcher {
ptr->locals = readItem(e.locals, interned_strings);
else
ptr->locals = NULL;
assert(ptr->globals || !ptr->locals);
return ptr;
}
......
......@@ -150,8 +150,8 @@ void initGlobalFuncs(GlobalState& g) {
g.llvm_excinfo_type = g.stdlib_module->getTypeByName("struct.pyston::ExcInfo");
assert(g.llvm_excinfo_type);
g.frame_info_type = g.stdlib_module->getTypeByName("struct.pyston::FrameInfo");
assert(g.frame_info_type);
g.llvm_frame_info_type = g.stdlib_module->getTypeByName("struct.pyston::FrameInfo");
assert(g.llvm_frame_info_type);
#define GET(N) g.funcs.N = getFunc((void*)N, STRINGIFY(N))
......@@ -222,6 +222,11 @@ void initGlobalFuncs(GlobalState& g) {
GET(listAppendInternal);
GET(getSysStdout);
GET(exec);
GET(boxedLocalsSet);
GET(boxedLocalsGet);
GET(boxedLocalsDel);
g.funcs.runtimeCall = getFunc((void*)runtimeCall, "runtimeCall");
g.funcs.runtimeCall0 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i32);
g.funcs.runtimeCall1
......
......@@ -46,6 +46,8 @@ struct GlobalFuncs {
llvm::Value* runtimeCall0, *runtimeCall1, *runtimeCall2, *runtimeCall3, *runtimeCall, *runtimeCallN;
llvm::Value* callattr0, *callattr1, *callattr2, *callattr3, *callattr, *callattrN;
llvm::Value* reoptCompiledFunc, *compilePartialFunc;
llvm::Value* exec;
llvm::Value* boxedLocalsSet, *boxedLocalsGet, *boxedLocalsDel;
llvm::Value* __cxa_begin_catch, *__cxa_end_catch;
llvm::Value* raise0, *raise3;
......
This diff is collapsed.
......@@ -21,14 +21,18 @@
namespace pyston {
class Box;
class BoxedDict;
class BoxedModule;
class BoxedTraceback;
struct FrameInfo;
BoxedModule* getCurrentModule();
class BoxedTraceback;
BoxedTraceback* getTraceback();
class BoxedDict;
BoxedDict* getLocals(bool only_user_visible, bool includeClosure);
// Adds stack locals and closure locals into the locals dict, and returns it.
Box* fastLocalsToBoxedLocals();
// Fetches a writeable pointer to the frame-local excinfo object,
// calculating it if necessary (from previous frames).
......@@ -39,6 +43,23 @@ struct ExecutionPoint {
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
struct FrameStackState {
// This includes all # variables (but not the ! ones).
// Therefore, it's not the same as the BoxedLocals.
// This also means that it contains
// CREATED_CLOSURE_NAME, PASSED_CLOSURE_NAME, and GENERATOR_NAME.
BoxedDict* locals;
// The frame_info is a pointer to the frame_info on the stack, so it is invalid
// after the frame ends.
FrameInfo* frame_info;
FrameStackState(BoxedDict* locals, FrameInfo* frame_info) : locals(locals), frame_info(frame_info) {}
};
// Returns all the stack locals, including hidden ones.
FrameStackState getFrameStackState();
}
#endif
......@@ -700,6 +700,14 @@ void AST_Expression::accept(ASTVisitor* v) {
body->accept(v);
}
void AST_Suite::accept(ASTVisitor* v) {
bool skip = v->visit_suite(this);
if (skip)
return;
visitVector(body, v);
}
void AST_Name::accept(ASTVisitor* v) {
bool skip = v->visit_name(this);
}
......@@ -1561,6 +1569,15 @@ bool PrintVisitor::visit_expression(AST_Expression* node) {
return true;
}
bool PrintVisitor::visit_suite(AST_Suite* node) {
for (int i = 0; i < node->body.size(); i++) {
printIndent();
node->body[i]->accept(this);
printf("\n");
}
return true;
}
bool PrintVisitor::visit_name(AST_Name* node) {
printf("%s", node->id.c_str());
// printf("%s(%d)", node->id.c_str(), node->ctx_type);
......
......@@ -23,6 +23,7 @@
#include "llvm/ADT/StringRef.h"
#include "analysis/scoping_analysis.h"
#include "core/common.h"
#include "core/stringpool.h"
......@@ -120,6 +121,7 @@ enum AST_TYPE {
Ellipsis = 87,
Expression = 88, // like Module, but used for eval.
SetComp = 89,
Suite = 90,
// Pseudo-nodes that are specific to this compiler:
Branch = 200,
......@@ -676,6 +678,20 @@ public:
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Module;
};
class AST_Suite : public AST {
public:
std::unique_ptr<InternedStringPool> interned_strings;
std::vector<AST_stmt*> body;
virtual void accept(ASTVisitor* v);
AST_Suite(std::unique_ptr<InternedStringPool> interned_strings)
: AST(AST_TYPE::Suite), interned_strings(std::move(interned_strings)) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Suite;
};
class AST_Name : public AST_expr {
public:
AST_TYPE::AST_TYPE ctx_type;
......@@ -684,20 +700,14 @@ public:
// The resolved scope of this name. Kind of hacky to be storing it in the AST node;
// in CPython it ends up getting "cached" by being translated into one of a number of
// different bytecodes.
// We don't have a separate bytecode representation, so just store it in here for now.
enum LookupType {
UNKNOWN,
GLOBAL,
CLOSURE,
FAST_LOCAL,
LOCAL,
} lookup_type;
ScopeInfo::VarScopeType lookup_type;
virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v);
AST_Name(InternedString id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0)
: AST_expr(AST_TYPE::Name, lineno, col_offset), ctx_type(ctx_type), id(id), lookup_type(UNKNOWN) {}
: AST_expr(AST_TYPE::Name, lineno, col_offset), ctx_type(ctx_type), id(id),
lookup_type(ScopeInfo::VarScopeType::UNKNOWN) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Name;
};
......@@ -1105,6 +1115,7 @@ public:
virtual bool visit_exec(AST_Exec* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_expr(AST_Expr* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_expression(AST_Expression* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_suite(AST_Suite* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_extslice(AST_ExtSlice* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_for(AST_For* node) { RELEASE_ASSERT(0, ""); }
virtual bool visit_functiondef(AST_FunctionDef* node) { RELEASE_ASSERT(0, ""); }
......@@ -1177,6 +1188,7 @@ public:
virtual bool visit_exec(AST_Exec* node) { return false; }
virtual bool visit_expr(AST_Expr* node) { return false; }
virtual bool visit_expr(AST_Expression* node) { return false; }
virtual bool visit_suite(AST_Suite* node) { return false; }
virtual bool visit_extslice(AST_ExtSlice* node) { return false; }
virtual bool visit_for(AST_For* node) { return false; }
virtual bool visit_functiondef(AST_FunctionDef* node) { return false; }
......@@ -1326,6 +1338,7 @@ public:
virtual bool visit_exec(AST_Exec* node);
virtual bool visit_expr(AST_Expr* node);
virtual bool visit_expression(AST_Expression* node);
virtual bool visit_suite(AST_Suite* node);
virtual bool visit_extslice(AST_ExtSlice* node);
virtual bool visit_for(AST_For* node);
virtual bool visit_functiondef(AST_FunctionDef* node);
......
......@@ -1913,7 +1913,16 @@ public:
return true;
}
bool visit_exec(AST_Exec* node) override { raiseExcHelper(SyntaxError, "'exec' currently not supported"); }
bool visit_exec(AST_Exec* node) override {
AST_Exec* astexec = new AST_Exec();
astexec->lineno = node->lineno;
astexec->col_offset = node->col_offset;
astexec->body = remapExpr(node->body);
astexec->globals = remapExpr(node->globals);
astexec->locals = remapExpr(node->locals);
push_back(astexec);
return true;
}
bool visit_while(AST_While* node) override {
assert(curblock);
......
......@@ -110,7 +110,8 @@ typedef ValuedCompilerType<llvm::Value*> ConcreteCompilerType;
ConcreteCompilerType* typeFromClass(BoxedClass*);
extern ConcreteCompilerType* INT, *BOXED_INT, *LONG, *FLOAT, *BOXED_FLOAT, *VOID, *UNKNOWN, *BOOL, *STR, *NONE, *LIST,
*SLICE, *MODULE, *DICT, *BOOL, *BOXED_BOOL, *BOXED_TUPLE, *SET, *FROZENSET, *CLOSURE, *GENERATOR, *BOXED_COMPLEX;
*SLICE, *MODULE, *DICT, *BOOL, *BOXED_BOOL, *BOXED_TUPLE, *SET, *FROZENSET, *CLOSURE, *GENERATOR, *BOXED_COMPLEX,
*FRAME_INFO;
extern CompilerType* UNDEF;
class CompilerVariable;
......@@ -535,6 +536,7 @@ void addToSysArgv(const char* str);
// The traceback given to the user will include this,
// even though the execution didn't actually arrive there.
void raiseSyntaxError(const char* msg, int lineno, int col_offset, const std::string& file, const std::string& func);
void raiseSyntaxErrorHelper(const std::string& file, const std::string& func, AST* node_at, const char* msg, ...);
struct LineInfo {
public:
......@@ -564,7 +566,9 @@ struct FrameInfo {
// - This makes frame entering+leaving faster at the expense of slower exceptions.
ExcInfo exc;
FrameInfo(ExcInfo exc) : exc(exc) {}
Box* boxedLocals;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL) {}
};
struct CallattrFlags {
......
......@@ -673,16 +673,6 @@ Box* zip2(Box* container1, Box* container2) {
return rtn;
}
Box* eval(Box* code) {
// TODO implement full functionality (args and stuff)
RELEASE_ASSERT(code->cls == str_cls, "eval not implemented for non-strings");
BoxedDict* locals = getLocals(true /* only_user_visible */, true /* includeClosure */);
BoxedModule* module = getCurrentModule();
return runEval(static_cast<BoxedString*>(code)->s.c_str(), locals, module);
}
static Box* callable(Box* obj) {
Box* r = PyBool_FromLong((long)PyCallable_Check(obj));
checkAndThrowCAPIException();
......@@ -849,7 +839,7 @@ Box* globals() {
}
Box* locals() {
return getLocals(true /* filter */, true /* includeClosure */);
return fastLocalsToBoxedLocals();
}
Box* divmod(Box* lhs, Box* rhs) {
......
......@@ -15,6 +15,7 @@
// This file is for forcing the inclusion of function declarations into the stdlib.
// This is so that the types of the functions are available to the compiler.
#include "codegen/irgen/hooks.h"
#include "core/types.h"
#include "gc/heap.h"
#include "runtime/complex.h"
......@@ -119,6 +120,8 @@ void force() {
FORCE(mod_float_float);
FORCE(pow_float_float);
FORCE(exec);
FORCE(dump);
FORCE(boxFloat);
......@@ -127,6 +130,10 @@ void force() {
FORCE(gc::sizes);
FORCE(boxedLocalsSet);
FORCE(boxedLocalsGet);
FORCE(boxedLocalsDel);
// FORCE(listIter);
}
}
......
......@@ -162,13 +162,13 @@ extern "C" Box* deopt(AST_expr* expr, Box* value) {
static StatCounter num_deopt("num_deopt");
num_deopt.log();
auto locals = getLocals(false /* filter */, false /* includeClosure */);
FrameStackState frame_state = getFrameStackState();
auto execution_point = getExecutionPoint();
// Should we only do this selectively?
execution_point.cf->speculationFailed();
return astInterpretFrom(execution_point.cf, expr, execution_point.current_stmt, value, locals);
return astInterpretFrom(execution_point.cf, expr, execution_point.current_stmt, value, frame_state);
}
extern "C" bool softspace(Box* b, bool newval) {
......@@ -4440,4 +4440,36 @@ Box* coerceUnicodeToStr(Box* unicode) {
return r;
}
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);
assert(boxedLocals != NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dict here yet");
auto& d = static_cast<BoxedDict*>(boxedLocals)->d;
auto it = d.find(boxString(attr));
if (it != d.end()) {
Box* value = it->second;
return value;
}
// TODO exception name?
std::string attr_string(attr);
return getGlobal(parent_module, &attr_string);
}
extern "C" void boxedLocalsDel(Box* boxedLocals, const char* attr) {
assert(boxedLocals != NULL);
RELEASE_ASSERT(boxedLocals->cls == dict_cls, "we don't support non-dict here yet");
auto& d = static_cast<BoxedDict*>(boxedLocals)->d;
auto it = d.find(boxString(attr));
if (it == d.end()) {
assertNameDefined(0, attr, NameError, false /* local_var_msg */);
}
d.erase(it);
}
}
......@@ -167,5 +167,9 @@ inline std::tuple<Box*, Box*, Box*, Box**> getTupleFromArgsArray(Box** args, int
Box** argtuple = num_args >= 4 ? &args[3] : nullptr;
return std::make_tuple(arg1, arg2, arg3, argtuple);
}
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" void boxedLocalsDel(Box* boxedLocals, const char* attr);
}
#endif
......@@ -123,6 +123,32 @@ void raiseSyntaxError(const char* msg, int lineno, int col_offset, const std::st
raiseRaw(ExcInfo(exc->cls, exc, new BoxedTraceback(std::move(entries))));
}
void raiseSyntaxErrorHelper(const std::string& file, const std::string& func, AST* node_at, const char* msg, ...) {
va_list ap;
va_start(ap, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, ap);
// TODO I'm not sure that it's safe to raise an exception here, since I think
// there will be things that end up not getting cleaned up.
// Then again, there are a huge number of things that don't get cleaned up even
// if an exception doesn't get thrown...
// TODO output is still a little wrong, should be, for example
//
// File "../test/tests/future_non_existent.py", line 1
// from __future__ import rvalue_references # should cause syntax error
//
// but instead it is
//
// Traceback (most recent call last):
// File "../test/tests/future_non_existent.py", line -1, in :
// from __future__ import rvalue_references # should cause syntax error
raiseSyntaxError(buf, node_at->lineno, node_at->col_offset, file, "");
}
void _printStacktrace() {
printTraceback(getTraceback());
}
......
# skip-if: '-O' in EXTRA_JIT_ARGS
# statcheck: 4 <= noninit_count('num_deopt') < 50
# statcheck: 1 <= stats["num_osr_exits"] <= 2
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
__pyston__.setOption("REOPT_THRESHOLD_BASELINE", 50)
__pyston__.setOption("SPECULATION_THRESHOLD", 10)
except ImportError:
pass
def main():
class C(object):
def __repr__(self):
return "<C>"
def f_gen(o):
print "starting f_gen, yielding:"
yield 8
try:
print o.a
if o.b:
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
print "yielding again:"
yield 9
c = C()
c.a = 1
c.b = 0
c.c = 3
c.d = 4
for i in xrange(2000):
print i
if i == 500:
c.a = []
if i == 600:
c.b = 1
if i == 700:
c.c = []
if i == 800:
c.b = 0
c.d = 1.0
g = f_gen(c)
print 'yielded(1):', g.next()
print 'yielded(2):', g.next()
main()
# skip-if: '-O' in EXTRA_JIT_ARGS
# statcheck: 4 <= noninit_count('num_deopt') < 50
# statcheck: 1 <= stats["num_osr_exits"] <= 2
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
__pyston__.setOption("REOPT_THRESHOLD_BASELINE", 50)
__pyston__.setOption("SPECULATION_THRESHOLD", 10)
except ImportError:
pass
# This test makes sure that the boxedLocals survive a deopt.
# TODO Write a test case to make sure exc_info survives the deopt.
def f_with_name_scoping(o):
print "starting f"
exec "k = 5"
l = 6
try:
print o.a
if o.b:
raise Exception('')
except Exception, e:
print o.c
print e
print o.d
print sorted(locals().items())
print "k =", k
print l
print "Done"
def main():
class C(object):
def __repr__(self):
return "<C>"
c = C()
c.a = 1
c.b = 0
c.c = 3
c.d = 4
for i in xrange(300):
print i
if i == 60:
c.a = []
if i == 120:
c.b = 1
if i == 180:
c.c = []
if i == 240:
c.b = 0
c.d = 1.0
f_with_name_scoping(c)
main()
......@@ -11,8 +11,10 @@ except ImportError:
pass
def main():
var_in_closure1 = 0
def f(o):
print "starting"
print "starting f"
try:
print o.a
......@@ -24,6 +26,12 @@ def main():
print o.d
print sorted(locals().items())
print var_in_closure1
var_in_closure2 = 1
def g():
print var_in_closure2
g()
print "Done"
class C(object):
......@@ -54,8 +62,6 @@ def main():
f(c)
# Regression test reduced from subprocess.py:
import types
def f2(self, args):
......
......@@ -84,7 +84,14 @@ print eval("eval('3 + 2342')")
o = 300
print 'eval eval o', eval("eval('o')")
#print eval("[(lambda p : p + o)(5) for o in range(5)]")
# 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)]")
def lambda_func():
try:
pass #print eval("[(lambda p2 : p2 + o2)(5) for o2 in range(5)]")
except NameError as e:
print e.message
lambda_func()
shadow1 = 1000
shadow2 = 1000
......
exec """print 'hi'
a = 5
print a"""
exec ""
# We need to be able to parse files with exec statements in them,
# even if we don't support the actual exec statement yet.
def dont_run_me():
exec "1/0"
def f():
exec "a = 5"
print a
f()
# expected: fail
def f():
print eval("[a for a in xrange(2)]")
print eval("a")
f()
def f():
a = 0
b = 0
e = 0
r = 0
def g():
def h():
print b
print e
print a
c = 0
print c
eval("[a for a in xrange(2)]")
eval("[c for c in xrange(2)]")
eval("[d for d in xrange(2)]")
eval("[e for e in xrange(2)]")
print a
print c
# d not found, because it's read as a stack variable
try:
print d
except NameError:
print 'd not found'
# but d it *is* in the locals()
# On the other hand, a, c, and e don't get over-written
# and b appears even though it only gets passed-through.
# So it should look like:
# a:0, b:0, c:0, d:2, e:0
print locals()
def unused():
print r
g()
f()
def meh(l):
l['a'] = 5
return 3
def f():
print eval("meh(locals()) + a")
f()
def f():
print eval("meh(locals()) + a", globals(), {})
f()
def f():
d = locals()
a = 2
d['a'] = 3
print a
print d
f()
def f():
exec "print 'hi'"
d = locals()
a = 2
d['a'] = 3
print a
print d
f()
def f():
d = locals()
a = 2
d['a'] = 3
print a
print d
exec "print 'hi'"
f()
def f(arg):
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
locals()
print d
f(12)
def f(arg):
exec "r = 12"
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
locals()
print d
f(12)
def f():
a = 5
def g():
print a
print locals()
f()
def f():
def g():
a = 0
def h():
print a
print locals()
yield 12
print locals()
yield 13
yield h
a = 1
yield 2
gen = g()
h1 = gen.next()
hgen = h1()
hgen.next()
gen.next()
hgen.next()
f()
foo = 0
class C(object):
try:
del foo
except NameError:
print 'foo NameError'
foo = 0
class C(object):
foo = 1
print foo
del foo
print foo
class C(object):
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
def f(moo):
class C(object):
a = 2
d = locals()
print d
a = 3
print d
locals()
print d
del a
print d
print moo
f(2134)
some_glob = 2
def f():
global some_glob
def g():
exec "some_glob = 5"
print some_glob
g()
f()
some_glob = 2
def f():
def g():
global some_glob
exec "some_glob = 5"
print some_glob
g()
f()
some_glob = 2
def f():
global some_glob
exec "some_glob = 5"
def g():
print some_glob
g()
f()
# We could just have a file for each, but if we Do these in execs,
# we don't need separate files for each, and that makes it easier
# to just spam all the permutations.
# The logic beyond this error message is oddly complicated.
cases = [
# protip: delete this first """ to get your editor to syntax-highlight the code
"""
# This should compile fine
def f():
a = 0
exec "b = 0"
""", """
# This should compile fine
def addpackage(sitedir, name, known_paths):
print a
exec "b = 0"
""", """
def f():
exec "a = 5"
def g():
print a
""", """
def f():
exec "a = 5"
def g():
def h():
print a
""", """
def f():
exec "a = 5"
class C(object):
def h():
print a
""", """
def f():
exec "a = 5"
def g():
b = 2
def h():
print b
""", """
def f():
def g():
print a
exec "a = 5"
""", """
def f():
from string import *
def g():
print a
""", """
def f():
from string import *
def g():
def h():
print a
""", """
def f():
from string import *
class C(object):
def h():
print a
""", """
def f():
from string import *
def g():
b = 2
def h():
print b
""", """
def f():
def g():
print a
from string import *
""", """
def f():
exec "a = 5"
from string import *
def g():
print a
""", """
def f():
from string import *
exec "a = 5"
def g():
print a
""", """
def f():
def g():
print a
from string import *
exec "a = 5"
""", """
def f():
def g():
print a
exec "a = 5"
from string import *
""", """
def f():
def g():
exec "a = 5"
""", """
class C(object):
def g():
a = 5
exec "a = 5"
""", """
class C(object):
def g():
exec "a = 5"
""", """
def f():
exec "a = 5"
return {b for b in xrange(3)}
""", """
def f():
exec "a = 5"
return [b for b in xrange(3)]
""", """
def f():
exec "a = 5"
return {b:b for b in xrange(3)}
""", """
def f():
exec "a = 5"
return {c for b in xrange(3)}
""", """
def f():
exec "a = 5"
return [c for b in xrange(3)]
""", """
def f():
exec "a = 5"
return {c:b for b in xrange(3)}
""", """
def f():
global a
def g():
print a
exec ""
""", """
def f():
global a
exec ""
def g():
print a
""", """
def f():
global a
def g():
a = 0
def h():
exec ""
print a
""", """
def f():
a = 0
def g():
global a
def h():
exec ""
print a
""", """
def f():
a = 0
def g():
exec ""
def h():
print a
"""
]
#import traceback
for case in cases:
print case
try:
exec case
except SyntaxError as se:
print se.message
# TODO uncomment this
# traceback.print_exc()
......@@ -77,6 +77,25 @@ class MyClass(object):
import sys as __bar
print __bar
class MyClass(object):
try:
from __foo import __bar
except ImportError, e:
print e
#TODO enable this once we support `import *` in functions
#class MyClass(object):
# try:
# from __foo import *
# except ImportError, e:
# print e
class MyClass(object):
try:
from sys import __bar
except ImportError, e:
print e
class MyClass(object):
try:
# Except if it's a dotted name:
......
try:
import __pyston__
__pyston__.setOption("OSR_THRESHOLD_BASELINE", 50)
except ImportError:
pass
def f1(x):
exec """
for i in xrange(x):
pass
print x
"""
f1(200)
def f3():
exec """
def f2(x):
def inner():
return x
return inner
"""
g = f2(200)
for i in xrange(200):
g()
print g()
f3()
......@@ -26,14 +26,14 @@ TEST_F(AnalysisTest, augassign) {
AST_Module* module = caching_parse_file(fn.c_str());
assert(module);
ScopingAnalysis *scoping = runScopingAnalysis(module);
ScopingAnalysis *scoping = new ScopingAnalysis(module);
assert(module->body[0]->type == AST_TYPE::FunctionDef);
AST_FunctionDef* func = static_cast<AST_FunctionDef*>(module->body[0]);
ScopeInfo* scope_info = scoping->getScopeInfoForNode(func);
ASSERT_FALSE(scope_info->refersToGlobal(module->interned_strings->get("a")));
ASSERT_FALSE(scope_info->refersToGlobal(module->interned_strings->get("b")));
ASSERT_FALSE(scope_info->getScopeTypeOfName(module->interned_strings->get("a")) == ScopeInfo::VarScopeType::GLOBAL);
ASSERT_FALSE(scope_info->getScopeTypeOfName(module->interned_strings->get("b")) == ScopeInfo::VarScopeType::GLOBAL);
SourceInfo* si = new SourceInfo(createModule("__main__", fn), scoping, func, func->body);
......
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