Commit 79ad8be8 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'getframe_merge'

parents 62794865 45225091
......@@ -71,15 +71,9 @@ else:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
# next bit filched from 1.5.2's inspect.py
def currentframe():
"""Return the frame object for the caller's stack frame."""
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# pyston changes: we don't support tb_frame or f_back, so always use sys._getframe
currentframe = lambda: sys._getframe(4)
start_getframe = 4
# done filching
# _srcfile is only used in conjunction with sys._getframe().
......@@ -1229,20 +1223,22 @@ class Logger(Filterer):
file name, line number and function name.
"""
# Pyston change:
return "(unknown file)", 0, "(unknown function)"
f = currentframe()
# pyston change: we can't use f_back to walk back up the stack, so increment a counter of
# frames to skip and repeatedly call sys._getframe
fn = start_getframe
#On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
#if f is not None:
# f = f.f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile:
f = f.f_back
fn += 1
f = sys._getframe(fn);
#f = f.f_back
continue
rv = (co.co_filename, f.f_lineno, co.co_name)
break
......
......@@ -54,6 +54,12 @@ const int dwarf_to_gp[] = {
// 17-32: xmm0-xmm15
};
Register Register::fromDwarf(int dwarf_regnum) {
assert(dwarf_regnum >= 0 && dwarf_regnum <= 16);
return Register(dwarf_to_gp[dwarf_regnum]);
}
GenericRegister GenericRegister::fromDwarf(int dwarf_regnum) {
assert(dwarf_regnum >= 0);
......@@ -856,6 +862,20 @@ void Assembler::je(JumpDestination dest) {
jmp_cond(dest, COND_EQUAL);
}
void Assembler::jmpq(Register dest) {
int reg_idx = dest.regnum;
if (reg_idx >= 8) {
emitRex(REX_B);
reg_idx -= 8;
}
assert(0 <= reg_idx && reg_idx < 8);
emitByte(0xff);
emitModRM(0b11, 0b100, reg_idx);
}
void Assembler::set_cond(Register reg, ConditionCode condition) {
......
......@@ -140,6 +140,7 @@ public:
void jmp_cond(JumpDestination dest, ConditionCode condition);
void jmp(JumpDestination dest);
void jmpq(Register dest);
void je(JumpDestination dest);
void jne(JumpDestination dest);
......
......@@ -45,6 +45,8 @@ struct Register {
bool operator!=(const Register& rhs) const { return !(*this == rhs); }
void dump() const;
static Register fromDwarf(int dwarf_regnum);
};
const Register RAX(0);
......
......@@ -469,8 +469,8 @@ extern "C" PyObject* PyObject_CallObject(PyObject* obj, PyObject* args) noexcept
r = runtimeCall(obj, ArgPassSpec(0, 0, false, false), NULL, NULL, NULL, NULL, NULL);
return r;
} catch (ExcInfo e) {
fatalOrError(PyExc_NotImplementedError, "unimplemented");
return nullptr;
setCAPIException(e);
return NULL;
}
}
......
......@@ -100,6 +100,16 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v
return builder.CreateConstInBoundsGEP2_32(builder.CreateConstInBoundsGEP2_32(v, 0, 0), 0, 0);
}
static llvm::Value* getFrameObjGep(llvm::IRBuilder<true>& builder, llvm::Value* v) {
static_assert(offsetof(FrameInfo, exc) == 0, "");
static_assert(sizeof(ExcInfo) == 24, "");
static_assert(sizeof(Box*) == 8, "");
static_assert(offsetof(FrameInfo, frame_obj) == 32, "");
return builder.CreateConstInBoundsGEP2_32(v, 0, 2);
// TODO: this could be made more resilient by doing something like
// gep->accumulateConstantOffset(g.tm->getDataLayout(), ap_offset)
}
llvm::Value* IRGenState::getFrameInfoVar() {
/*
There is a matrix of possibilities here.
......@@ -162,6 +172,11 @@ llvm::Value* IRGenState::getFrameInfoVar() {
builder.CreateStore(this->boxed_locals, boxed_locals_gep);
}
// frame_info.frame_obj = NULL
static llvm::Type* llvm_frame_obj_type_ptr
= llvm::cast<llvm::StructType>(g.llvm_frame_info_type)->getElementType(2);
builder.CreateStore(embedConstantPtr(NULL, llvm_frame_obj_type_ptr), getFrameObjGep(builder, al));
this->frame_info = al;
}
}
......
This diff is collapsed.
......@@ -33,19 +33,47 @@ Box* getGlobalsDict(); // always returns a dict-like object
BoxedTraceback* getTraceback();
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
// Adds stack locals and closure locals into the locals dict, and returns it.
Box* fastLocalsToBoxedLocals();
class PythonFrameIteratorImpl;
class PythonFrameIterator {
private:
std::unique_ptr<PythonFrameIteratorImpl> impl;
public:
CompiledFunction* getCF();
FrameInfo* getFrameInfo();
bool exists() { return impl.get() != NULL; }
std::unique_ptr<ExecutionPoint> getExecutionPoint();
Box* fastLocalsToBoxedLocals();
// Gets the "current version" of this frame: if the frame has executed since
// the iterator was obtained, the methods may return old values. This returns
// an updated copy that returns the updated values.
// The "current version" will live at the same stack location, but any other
// similarities need to be verified by the caller.
// This function can only be called from the thread that created this object.
PythonFrameIterator getCurrentVersion();
PythonFrameIterator(PythonFrameIterator&& rhs);
void operator=(PythonFrameIterator&& rhs);
PythonFrameIterator(std::unique_ptr<PythonFrameIteratorImpl>&& impl);
~PythonFrameIterator();
};
PythonFrameIterator getPythonFrame(int depth);
// Fetches a writeable pointer to the frame-local excinfo object,
// calculating it if necessary (from previous frames).
ExcInfo* getFrameExcInfo();
struct ExecutionPoint {
CompiledFunction* cf;
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.
......
......@@ -561,16 +561,29 @@ struct ExcInfo {
void printExcAndTraceback() const;
};
class BoxedFrame;
struct FrameInfo {
// Note(kmod): we have a number of fields here that all have independent
// initialization rules. We could potentially save time on every function-entry
// by having an "initialized" variable (or condition) that guards all of them.
// *Not the same semantics as CPython's frame->f_exc*
// In CPython, f_exc is the saved exc_info from the previous frame.
// In Pyston, exc is the frame-local value of sys.exc_info.
// - This makes frame entering+leaving faster at the expense of slower exceptions.
//
// exc.type is initialized to NULL at function entry, and exc.value and exc.tb are left
// uninitialized. When one wants to access any of the values, you need to check if exc.type
// is NULL, and if so crawl up the stack looking for the first frame with a non-null exc.type
// and copy that.
ExcInfo exc;
// This field is always initialized:
Box* boxedLocals;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL) {}
BoxedFrame* frame_obj;
FrameInfo(ExcInfo exc) : exc(exc), boxedLocals(NULL), frame_obj(0) {}
};
struct CallattrFlags {
......
......@@ -99,6 +99,22 @@ Box* getSysStdout() {
return sys_stdout;
}
Box* sysGetFrame(Box* val) {
int depth = 0;
if (val) {
if (!isSubclass(val->cls, int_cls)) {
raiseExcHelper(TypeError, "TypeError: an integer is required");
}
depth = static_cast<BoxedInt*>(val)->n;
}
Box* frame = getFrame(depth);
if (!frame) {
raiseExcHelper(ValueError, "call stack is not deep enough");
}
return frame;
}
Box* sysGetDefaultEncoding() {
return boxStrConstant(PyUnicode_GetDefaultEncoding());
}
......@@ -281,6 +297,8 @@ void setupSys() {
main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL);
sys_module->giveAttr("executable", boxString(main_fn.str()));
sys_module->giveAttr("_getframe",
new BoxedFunction(boxRTFunction((void*)sysGetFrame, UNKNOWN, 1, 1, false, false), { NULL }));
sys_module->giveAttr(
"getdefaultencoding",
new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding"));
......
......@@ -39,6 +39,16 @@ public:
boxGCHandler(v, b);
}
static Box* name(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxString(static_cast<BoxedCode*>(b)->f->source->getName());
}
static Box* filename(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxString(static_cast<BoxedCode*>(b)->f->source->parent_module->fn);
}
static Box* argcount(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
......@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) {
return new BoxedCode(f->f);
}
Box* codeForCLFunction(CLFunction* f) {
return new BoxedCode(f);
}
void setupCode() {
code_cls
= BoxedHeapClass::create(type_cls, object_cls, &BoxedCode::gcHandler, 0, 0, sizeof(BoxedCode), false, "code");
code_cls->giveAttr("__new__", None); // Hacky way of preventing users from instantiating this
code_cls->giveAttr("co_name", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::name, NULL, NULL));
code_cls->giveAttr("co_filename", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::filename, NULL, NULL));
code_cls->giveAttr("co_argcount", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::argcount, NULL, NULL));
code_cls->giveAttr("co_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, NULL, NULL));
code_cls->giveAttr("co_flags", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::flags, NULL, NULL));
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Python.h"
#include "pythread.h"
#include "codegen/unwinding.h"
#include "runtime/types.h"
namespace pyston {
BoxedClass* frame_cls;
// Issues:
// - breaks gdb backtraces
// - breaks c++ exceptions
// - we never free the trampolines
class BoxedFrame : public Box {
public:
BoxedFrame(PythonFrameIterator&& it) __attribute__((visibility("default")))
: it(std::move(it)), thread_id(PyThread_get_thread_ident()) {}
PythonFrameIterator it;
long thread_id;
Box* _globals;
Box* _code;
void update() {
// This makes sense as an exception, but who knows how the user program would react
// (it might swallow it and do something different)
RELEASE_ASSERT(thread_id == PyThread_get_thread_ident(),
"frame objects can only be accessed from the same thread");
PythonFrameIterator new_it = it.getCurrentVersion();
RELEASE_ASSERT(new_it.exists() && new_it.getFrameInfo()->frame_obj == this, "frame has exited");
it = std::move(new_it);
}
// cpython frame objects have the following attributes
// read-only attributes
//
// f_back[*] : previous stack frame (toward caller)
// f_code : code object being executed in this frame
// f_locals : dictionary used to look up local variables in this frame
// f_globals : dictionary used to look up global variables in this frame
// f_builtins[*] : dictionary to look up built-in (intrinsic) names
// f_restricted[*] : whether this function is executing in restricted execution mode
// f_lasti[*] : precise instruction (this is an index into the bytecode string of the code object)
// writable attributes
//
// f_trace[*] : if not None, is a function called at the start of each source code line (used by debugger)
// f_exc_type[*], : represent the last exception raised in the parent frame provided another exception was
// f_exc_value[*], : ever raised in the current frame (in all other cases they are None).
// f_exc_traceback[*] :
// f_lineno[**] : the current line number of the frame -- writing to this from within a trace function jumps
// to
// : the given line (only for the bottom-most frame). A debugger can implement a Jump command
// (aka
// : Set Next Statement) by writing to f_lineno
//
// * = unsupported in Pyston
// ** = getter supported, but setter unsupported
static void gchandler(GCVisitor* v, Box* b) {
boxGCHandler(v, b);
auto f = static_cast<BoxedFrame*>(b);
v->visit(f->_code);
v->visit(f->_globals);
}
static void simpleDestructor(Box* b) {
auto f = static_cast<BoxedFrame*>(b);
f->it.~PythonFrameIterator();
}
static Box* code(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_code;
}
static Box* locals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
f->update();
return f->it.fastLocalsToBoxedLocals();
}
static Box* globals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_globals;
}
static Box* lineno(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
f->update();
std::unique_ptr<ExecutionPoint> fr = f->it.getExecutionPoint();
return boxInt(fr->current_stmt->lineno);
}
DEFAULT_CLASS(frame_cls);
};
Box* getFrame(int depth) {
auto it = getPythonFrame(depth);
if (!it.exists())
return NULL;
FrameInfo* fi = it.getFrameInfo();
if (fi->frame_obj == NULL) {
auto cf = it.getCF();
BoxedFrame* f = fi->frame_obj = new BoxedFrame(std::move(it));
assert(cf->clfunc->source->scoping->areGlobalsFromModule());
f->_globals = makeAttrWrapper(cf->clfunc->source->parent_module);
f->_code = codeForCLFunction(cf->clfunc);
}
return fi->frame_obj;
}
void setupFrame() {
frame_cls = BoxedHeapClass::create(type_cls, object_cls, &BoxedFrame::gchandler, 0, 0, sizeof(BoxedFrame), false,
"frame");
frame_cls->simple_destructor = BoxedFrame::simpleDestructor;
frame_cls->giveAttr("f_code", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::code, NULL, NULL));
frame_cls->giveAttr("f_locals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::locals, NULL, NULL));
frame_cls->giveAttr("f_lineno", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::lineno, NULL, NULL));
frame_cls->giveAttr("f_globals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::globals, NULL, NULL));
frame_cls->freeze();
}
}
......@@ -158,6 +158,20 @@ static void writeTrivialEhFrame(void* eh_frame_addr, void* func_addr, uint64_t f
*size_ptr = func_size;
}
void EHFrameManager::writeAndRegister(void* func_addr, uint64_t func_size) {
assert(eh_frame_addr == NULL);
eh_frame_addr = malloc(EH_FRAME_SIZE);
writeTrivialEhFrame(eh_frame_addr, func_addr, func_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
}
EHFrameManager::~EHFrameManager() {
if (eh_frame_addr) {
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(eh_frame_addr);
}
}
#if RUNTIMEICS_OMIT_FRAME_PTR
// If you change this, you *must* update the value in _eh_frame_template
// (set the -9'th byte to this value plus 8)
......@@ -256,10 +270,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
// TODO: ideally would be more intelligent about allocation strategies.
// The code sections should be together and the eh sections together
eh_frame_addr = malloc(EH_FRAME_SIZE);
writeTrivialEhFrame(eh_frame_addr, addr, total_size);
registerEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
eh_frame.writeAndRegister(addr, total_size);
} else {
addr = func_addr;
}
......@@ -268,9 +279,7 @@ RuntimeIC::RuntimeIC(void* func_addr, int num_slots, int slot_size) {
RuntimeIC::~RuntimeIC() {
if (ENABLE_RUNTIME_ICS) {
deregisterCompiledPatchpoint(icinfo.get());
deregisterEHFrames((uint8_t*)eh_frame_addr, (uint64_t)eh_frame_addr, EH_FRAME_SIZE);
free(addr);
free(eh_frame_addr);
} else {
}
}
......
......@@ -22,10 +22,20 @@ namespace pyston {
class ICInfo;
class EHFrameManager {
private:
void* eh_frame_addr;
public:
EHFrameManager() : eh_frame_addr(NULL) {}
~EHFrameManager();
void writeAndRegister(void* func_addr, uint64_t func_size);
};
class RuntimeIC {
private:
void* addr;
void* eh_frame_addr;
EHFrameManager eh_frame;
std::unique_ptr<ICInfo> icinfo;
......
......@@ -2146,6 +2146,7 @@ void setupRuntime() {
setupDescr();
setupTraceback();
setupCode();
setupFrame();
function_cls->giveAttr("__dict__", dict_descr);
function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL));
......
......@@ -68,6 +68,7 @@ void setupGenerator();
void setupDescr();
void teardownDescr();
void setupCode();
void setupFrame();
void setupSys();
void setupBuiltins();
......@@ -815,6 +816,9 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern Box* dict_descr;
Box* codeForFunction(BoxedFunction*);
Box* codeForCLFunction(CLFunction*);
Box* getFrame(int depth);
}
#endif
......@@ -15,7 +15,7 @@ d[2].append(3)
print sorted(d.items())
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT.__name__
print NT.__name__, NT
n = NT(1, "hi")
print n.field1, n.field2, len(n), list(n), n[0], n[-1]
print n
import logging
import warnings
# output something using the root logger
def foo():
logging.warning("Houston, we have a %s", "bit of a problem")
foo()
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Trigger a warning.
fxn()
# Verify some things
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "deprecated" in str(w[-1].message)
print "warnings module works"
# expected: fail
# - needs sys._getframe
import collections
NT = collections.namedtuple("NT", ["field1", "field2"])
print NT
"""
Frame Hack Recipe #1: Ruby-style string interpolation (version 1)
"""
# from http://farmdev.com/src/secrets/framehack/interpolate/solutions/interpolate1.py
import sys
from string import Template
def interpolate(templateStr):
frame = sys._getframe(1)
framedict = frame.f_locals
t = Template(templateStr)
return t.substitute(**framedict)
name = 'Feihong'
place = 'Chicago'
print interpolate("My name is ${name}. I work in ${place}.")
import sys
def sysframetest():
return sys._getframe(0)
def sysframetestwrapper():
return sysframetest()
fr = sysframetest()
print sysframetest.__name__
print fr.f_code.co_name
print fr.f_code.co_filename
fr = sysframetestwrapper()
print sysframetestwrapper.__name__
print fr.f_code.co_name
print fr.f_code.co_filename
import sys
def f():
fr = sys._getframe(0)
print fr.f_lineno
print fr.f_lineno
print sorted(fr.f_locals.keys())
a = 1
print sorted(fr.f_locals.keys())
f()
assert sys._getframe(0) is sys._getframe(0)
def f2():
f1 = sys._getframe(0)
# trigger osr:
for i in xrange(20000):
pass
assert f1 is sys._getframe(0)
f2()
# Make sure we can throw exceptions through frame we called _getframe
import sys
def g():
sys._getframe(1)
1/0
def f():
g()
try:
f()
except Exception as e:
print e
# expected: fail
# - we don't (yet?) support looking at frame objects after
# their frame has exited
import sys
def f():
return sys._getframe(0)
fr = f()
print fr.f_locals
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