Commit 8e87df72 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Fix a bunch of exception handling

parent 88c85e0b
...@@ -397,7 +397,7 @@ all: pyston_dbg pyston_release pyston_gcc unittests check-deps $(RUN_DEPS) ...@@ -397,7 +397,7 @@ all: pyston_dbg pyston_release pyston_gcc unittests check-deps $(RUN_DEPS)
ALL_HEADERS := $(wildcard src/*/*.h) $(wildcard src/*/*/*.h) $(wildcard from_cpython/Include/*.h) ALL_HEADERS := $(wildcard src/*/*.h) $(wildcard src/*/*/*.h) $(wildcard from_cpython/Include/*.h)
tags: $(SRCS) $(OPTIONAL_SRCS) $(FROM_CPYTHON_SRCS) $(ALL_HEADERS) tags: $(SRCS) $(OPTIONAL_SRCS) $(FROM_CPYTHON_SRCS) $(ALL_HEADERS)
$(ECHO) Calculating tags... $(ECHO) Calculating tags...
$(VERB) $(CTAGS) -I noexcept -I noexcept+ -I PYSTON_NOEXCEPT $^ $(VERB) $(CTAGS) -I noexcept -I noexcept+ -I PYSTON_NOEXCEPT -I STOLEN -I BORROWED $^
TAGS: $(SRCS) $(OPTIONAL_SRCS) $(FROM_CPYTHON_SRCS) $(ALL_HEADERS) TAGS: $(SRCS) $(OPTIONAL_SRCS) $(FROM_CPYTHON_SRCS) $(ALL_HEADERS)
$(ECHO) Calculating TAGS... $(ECHO) Calculating TAGS...
......
...@@ -19,6 +19,9 @@ AUTO_DECREF, autoDecref, incref ...@@ -19,6 +19,9 @@ AUTO_DECREF, autoDecref, incref
Invariant: all paths through a function should leave each variable with 0 net refs. (This includes paths out via exceptions.) Invariant: all paths through a function should leave each variable with 0 net refs. (This includes paths out via exceptions.)
Special cases:
- Most exception-related functions steal refs. If you throw an exception in C code (via `throw [ExcInfo] e`), it consumes the refs.
## Refcounting in the rewriter, baseline jit, and llvm jit ## Refcounting in the rewriter, baseline jit, and llvm jit
## Debugging ## Debugging
......
...@@ -189,6 +189,7 @@ PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObject** tb) ...@@ -189,6 +189,7 @@ PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObject** tb)
if (res == NULL) if (res == NULL)
goto finally; goto finally;
Py_DECREF(value);
value = res; value = res;
} }
/* if the class of the instance doesn't exactly match the /* if the class of the instance doesn't exactly match the
......
...@@ -936,6 +936,8 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) { ...@@ -936,6 +936,8 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
} else if (node->opcode == AST_LangPrimitive::SET_EXC_INFO) { } else if (node->opcode == AST_LangPrimitive::SET_EXC_INFO) {
assert(node->args.size() == 3); assert(node->args.size() == 3);
assert(0 && "check refcounting -- call setFrameExcInfo instead of setting it directly");
Value type = visit_expr(node->args[0]); Value type = visit_expr(node->args[0]);
assert(type.o); assert(type.o);
Value value = visit_expr(node->args[1]); Value value = visit_expr(node->args[1]);
......
...@@ -882,9 +882,7 @@ private: ...@@ -882,9 +882,7 @@ private:
// trigger an exception, but the irgenerator will know that definitely-defined // trigger an exception, but the irgenerator will know that definitely-defined
// local symbols will not throw. // local symbols will not throw.
emitter.getBuilder()->CreateUnreachable(); emitter.getBuilder()->CreateUnreachable();
exc_type = undefVariable(); exc_type = exc_value = exc_tb = emitter.getNone();
exc_value = undefVariable();
exc_tb = undefVariable();
endBlock(DEAD); endBlock(DEAD);
} }
...@@ -993,16 +991,17 @@ private: ...@@ -993,16 +991,17 @@ private:
auto* builder = emitter.getBuilder(); auto* builder = emitter.getBuilder();
llvm::Value* frame_info = irstate->getFrameInfoVar(); llvm::Value* frame_info = irstate->getFrameInfoVar();
llvm::Value* exc_info = builder->CreateConstInBoundsGEP2_32(frame_info, 0, 0);
assert(exc_info->getType() == g.llvm_excinfo_type->getPointerTo());
ConcreteCompilerVariable* converted_type = type->makeConverted(emitter, UNKNOWN); ConcreteCompilerVariable* converted_type = type->makeConverted(emitter, UNKNOWN);
builder->CreateStore(converted_type->getValue(), builder->CreateConstInBoundsGEP2_32(exc_info, 0, 0));
ConcreteCompilerVariable* converted_value = value->makeConverted(emitter, UNKNOWN); ConcreteCompilerVariable* converted_value = value->makeConverted(emitter, UNKNOWN);
builder->CreateStore(converted_value->getValue(), builder->CreateConstInBoundsGEP2_32(exc_info, 0, 1));
ConcreteCompilerVariable* converted_traceback = traceback->makeConverted(emitter, UNKNOWN); ConcreteCompilerVariable* converted_traceback = traceback->makeConverted(emitter, UNKNOWN);
builder->CreateStore(converted_traceback->getValue(),
builder->CreateConstInBoundsGEP2_32(exc_info, 0, 2)); auto inst = emitter.createCall(UnwindInfo::cantUnwind(), g.funcs.setFrameExcInfo,
{ frame_info, converted_type->getValue(), converted_value->getValue(),
converted_traceback->getValue() });
emitter.refConsumed(converted_type->getValue(), inst);
emitter.refConsumed(converted_value->getValue(), inst);
emitter.refConsumed(converted_traceback->getValue(), inst);
return emitter.getNone(); return emitter.getNone();
} }
...@@ -2421,13 +2420,19 @@ private: ...@@ -2421,13 +2420,19 @@ private:
} }
} }
llvm::Instruction* inst;
if (target_exception_style == CAPI) { if (target_exception_style == CAPI) {
emitter.createCall(unw_info, g.funcs.raise3_capi, args, CAPI); inst = emitter.createCall(unw_info, g.funcs.raise3_capi, args, CAPI);
emitter.checkAndPropagateCapiException(unw_info, getNullPtr(g.llvm_value_type_ptr), emitter.checkAndPropagateCapiException(unw_info, getNullPtr(g.llvm_value_type_ptr),
getNullPtr(g.llvm_value_type_ptr)); getNullPtr(g.llvm_value_type_ptr));
} else { } else {
emitter.createCall(unw_info, g.funcs.raise3, args, CXX); inst = emitter.createCall(unw_info, g.funcs.raise3, args, CXX);
} }
for (auto a : args) {
emitter.refConsumed(a, inst);
}
emitter.getBuilder()->CreateUnreachable(); emitter.getBuilder()->CreateUnreachable();
endBlock(DEAD); endBlock(DEAD);
...@@ -2963,7 +2968,6 @@ public: ...@@ -2963,7 +2968,6 @@ public:
llvm::Value* exc_value = emitter.getBuilder()->CreateLoad(exc_value_ptr); llvm::Value* exc_value = emitter.getBuilder()->CreateLoad(exc_value_ptr);
llvm::Value* exc_traceback = emitter.getBuilder()->CreateLoad(exc_traceback_ptr); llvm::Value* exc_traceback = emitter.getBuilder()->CreateLoad(exc_traceback_ptr);
// TODO: nullable?
emitter.setType(exc_type, RefType::OWNED); emitter.setType(exc_type, RefType::OWNED);
emitter.setType(exc_value, RefType::OWNED); emitter.setType(exc_value, RefType::OWNED);
emitter.setType(exc_traceback, RefType::OWNED); emitter.setType(exc_traceback, RefType::OWNED);
......
...@@ -90,12 +90,15 @@ void remapPhis(llvm::BasicBlock* in_block, llvm::BasicBlock* from_block, llvm::B ...@@ -90,12 +90,15 @@ void remapPhis(llvm::BasicBlock* in_block, llvm::BasicBlock* from_block, llvm::B
} }
} }
typedef llvm::DenseMap<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>, llvm::Instruction*> InsertionCache;
llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* from_bb, llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* from_bb,
llvm::DenseMap<llvm::BasicBlock*, llvm::Instruction*> cache) { InsertionCache& cache) {
assert(BB); assert(BB);
assert(BB != from_bb); assert(BB != from_bb);
auto it = cache.find(BB); auto key = std::make_pair(BB, from_bb);
auto it = cache.find(key);
if (it != cache.end()) if (it != cache.end())
return it->second; return it->second;
...@@ -105,6 +108,8 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr ...@@ -105,6 +108,8 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr
llvm::BasicBlock* breaker_block = llvm::BasicBlock::Create(g.context, "breaker", from_bb->getParent(), BB); llvm::BasicBlock* breaker_block = llvm::BasicBlock::Create(g.context, "breaker", from_bb->getParent(), BB);
llvm::BranchInst::Create(BB, breaker_block); llvm::BranchInst::Create(BB, breaker_block);
// llvm::outs() << "Breaking edge from " << from_bb->getName() << " to " << BB->getName() << "; name is "
//<< breaker_block->getName() << '\n';
auto terminator = from_bb->getTerminator(); auto terminator = from_bb->getTerminator();
...@@ -124,8 +129,8 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr ...@@ -124,8 +129,8 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr
remapPhis(BB, from_bb, breaker_block); remapPhis(BB, from_bb, breaker_block);
cache[BB] = breaker_block->getFirstInsertionPt(); cache[key] = breaker_block->getFirstInsertionPt();
return cache[BB]; return cache[key];
} }
if (llvm::isa<llvm::LandingPadInst>(*BB->begin())) { if (llvm::isa<llvm::LandingPadInst>(*BB->begin())) {
...@@ -134,12 +139,12 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr ...@@ -134,12 +139,12 @@ llvm::Instruction* findInsertionPoint(llvm::BasicBlock* BB, llvm::BasicBlock* fr
++it; ++it;
++it; ++it;
++it; ++it;
cache[BB] = it; cache[key] = it;
return &*it; return &*it;
} else { } else {
for (llvm::Instruction& I : *BB) { for (llvm::Instruction& I : *BB) {
if (!llvm::isa<llvm::PHINode>(I) && !llvm::isa<llvm::AllocaInst>(I)) { if (!llvm::isa<llvm::PHINode>(I) && !llvm::isa<llvm::AllocaInst>(I)) {
cache[BB] = &I; cache[key] = &I;
return &I; return &I;
} }
} }
...@@ -782,9 +787,32 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -782,9 +787,32 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
ASSERT(states.size() == f->size(), "We didn't process all nodes..."); ASSERT(states.size() == f->size(), "We didn't process all nodes...");
llvm::DenseMap<llvm::BasicBlock*, llvm::Instruction*> insertion_pts; // Note: we iterate over the basicblocks in the order they appear in the llvm function;
for (auto&& p : states) { // iterating over states directly is nondeterministic.
auto&& state = p.second; std::vector<llvm::BasicBlock*> basic_blocks;
for (auto&& BB : *f)
basic_blocks.push_back(&BB);
// First, find all insertion points. This may change the CFG by breaking critical edges.
InsertionCache insertion_pts;
for (auto bb : basic_blocks) {
auto&& state = states[bb];
for (auto& op : state.increfs) {
auto insertion_pt = op.insertion_inst;
if (!insertion_pt)
insertion_pt = findInsertionPoint(op.insertion_bb, op.insertion_from_bb, insertion_pts);
}
for (auto& op : state.decrefs) {
auto insertion_pt = op.insertion_inst;
if (!insertion_pt)
insertion_pt = findInsertionPoint(op.insertion_bb, op.insertion_from_bb, insertion_pts);
}
}
// Then use the insertion points (it's the same code but this time it will hit the cache).
// This may change the CFG by adding decref's
for (auto bb : basic_blocks) {
auto&& state = states[bb];
for (auto& op : state.increfs) { for (auto& op : state.increfs) {
auto insertion_pt = op.insertion_inst; auto insertion_pt = op.insertion_inst;
if (!insertion_pt) if (!insertion_pt)
......
...@@ -199,6 +199,7 @@ void initGlobalFuncs(GlobalState& g) { ...@@ -199,6 +199,7 @@ void initGlobalFuncs(GlobalState& g) {
GET(initFrame); GET(initFrame);
GET(deinitFrame); GET(deinitFrame);
GET(makePendingCalls); GET(makePendingCalls);
GET(setFrameExcInfo);
GET(getattr); GET(getattr);
GET(getattr_capi); GET(getattr_capi);
......
...@@ -34,7 +34,8 @@ struct GlobalFuncs { ...@@ -34,7 +34,8 @@ struct GlobalFuncs {
llvm::Value* boxInt, *unboxInt, *boxFloat, *unboxFloat, *createFunctionFromMetadata, *getFunctionMetadata, llvm::Value* boxInt, *unboxInt, *boxFloat, *unboxFloat, *createFunctionFromMetadata, *getFunctionMetadata,
*boxInstanceMethod, *boxBool, *unboxBool, *createTuple, *createDict, *createList, *createSlice, *boxInstanceMethod, *boxBool, *unboxBool, *createTuple, *createDict, *createList, *createSlice,
*createUserClass, *createClosure, *createGenerator, *createSet, *initFrame, *deinitFrame, *makePendingCalls; *createUserClass, *createClosure, *createGenerator, *createSet, *initFrame, *deinitFrame, *makePendingCalls,
*setFrameExcInfo;
llvm::Value* getattr, *getattr_capi, *setattr, *delattr, *delitem, *delGlobal, *nonzero, *binop, *compare, llvm::Value* getattr, *getattr_capi, *setattr, *delattr, *delitem, *delGlobal, *nonzero, *binop, *compare,
*augbinop, *unboxedLen, *getitem, *getitem_capi, *getclsattr, *getGlobal, *setitem, *unaryop, *import, *augbinop, *unboxedLen, *getitem, *getitem_capi, *getclsattr, *getGlobal, *setitem, *unaryop, *import,
*importFrom, *importStar, *repr, *exceptionMatches, *yield, *getiterHelper, *hasnext, *setGlobal, *apply_slice; *importFrom, *importStar, *repr, *exceptionMatches, *yield, *getiterHelper, *hasnext, *setGlobal, *apply_slice;
......
...@@ -1786,6 +1786,7 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa ...@@ -1786,6 +1786,7 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
= rewrite_args->rewriter->call(true, (void*)BaseException->tp_new, rewrite_args->arg1, = rewrite_args->rewriter->call(true, (void*)BaseException->tp_new, rewrite_args->arg1,
rewrite_args->rewriter->loadConst(0, Location::forArg(1)), rewrite_args->rewriter->loadConst(0, Location::forArg(1)),
rewrite_args->rewriter->loadConst(0, Location::forArg(2))); rewrite_args->rewriter->loadConst(0, Location::forArg(2)));
rewrite_args->out_rtn->setType(RefType::OWNED);
rewrite_args->out_success = true; rewrite_args->out_success = true;
} }
return rtn; return rtn;
......
...@@ -92,13 +92,17 @@ bool ExcInfo::matches(BoxedClass* cls) const { ...@@ -92,13 +92,17 @@ bool ExcInfo::matches(BoxedClass* cls) const {
} }
// takes the three arguments of a `raise' and produces the ExcInfo to throw // takes the three arguments of a `raise' and produces the ExcInfo to throw
ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { ExcInfo excInfoForRaise(STOLEN(Box*) type, STOLEN(Box*) value, STOLEN(Box*) tb) {
assert(type && value && tb); // use None for default behavior, not nullptr assert(type && value && tb); // use None for default behavior, not nullptr
// TODO switch this to PyErr_Normalize // TODO switch this to PyErr_Normalize
if (tb == None) { if (tb == None) {
Py_DECREF(tb);
tb = NULL; tb = NULL;
} else if (tb != NULL && !PyTraceBack_Check(tb)) { } else if (tb != NULL && !PyTraceBack_Check(tb)) {
Py_DECREF(type);
Py_DECREF(value);
Py_XDECREF(tb);
raiseExcHelper(TypeError, "raise: arg 3 must be a traceback or None"); raiseExcHelper(TypeError, "raise: arg 3 must be a traceback or None");
} }
...@@ -115,6 +119,9 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { ...@@ -115,6 +119,9 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
PyErr_NormalizeException(&type, &value, &tb); PyErr_NormalizeException(&type, &value, &tb);
if (!PyExceptionInstance_Check(value)) { if (!PyExceptionInstance_Check(value)) {
Py_DECREF(type);
Py_DECREF(value);
Py_XDECREF(tb);
raiseExcHelper(TypeError, "calling %s() should have returned an instance of " raiseExcHelper(TypeError, "calling %s() should have returned an instance of "
"BaseException, not '%s'", "BaseException, not '%s'",
((PyTypeObject*)type)->tp_name, Py_TYPE(value)->tp_name); ((PyTypeObject*)type)->tp_name, Py_TYPE(value)->tp_name);
...@@ -122,6 +129,9 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { ...@@ -122,6 +129,9 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
} else if (PyExceptionInstance_Check(type)) { } else if (PyExceptionInstance_Check(type)) {
/* Raising an instance. The value should be a dummy. */ /* Raising an instance. The value should be a dummy. */
if (value != Py_None) { if (value != Py_None) {
Py_DECREF(type);
Py_DECREF(value);
Py_XDECREF(tb);
raiseExcHelper(TypeError, "instance exception may not have a separate value"); raiseExcHelper(TypeError, "instance exception may not have a separate value");
} else { } else {
/* Normalize to raise <class>, <instance> */ /* Normalize to raise <class>, <instance> */
...@@ -131,6 +141,9 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { ...@@ -131,6 +141,9 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
Py_INCREF(type); Py_INCREF(type);
} }
} else { } else {
Py_DECREF(type);
Py_DECREF(value);
Py_XDECREF(tb);
/* Not something you can raise. You get an exception /* Not something you can raise. You get an exception
anyway, just not what you specified :-) */ anyway, just not what you specified :-) */
raiseExcHelper(TypeError, "exceptions must be old-style classes or " raiseExcHelper(TypeError, "exceptions must be old-style classes or "
...@@ -141,7 +154,7 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { ...@@ -141,7 +154,7 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
assert(PyExceptionClass_Check(type)); assert(PyExceptionClass_Check(type));
if (tb == NULL) { if (tb == NULL) {
tb = None; tb = incref(None);
} }
return ExcInfo(type, value, tb); return ExcInfo(type, value, tb);
...@@ -178,7 +191,7 @@ extern "C" void raise0_capi(ExcInfo* frame_exc_info) noexcept { ...@@ -178,7 +191,7 @@ extern "C" void raise0_capi(ExcInfo* frame_exc_info) noexcept {
PyErr_Restore(frame_exc_info->type, frame_exc_info->value, frame_exc_info->traceback); PyErr_Restore(frame_exc_info->type, frame_exc_info->value, frame_exc_info->traceback);
} }
extern "C" void raise3(Box* arg0, Box* arg1, Box* arg2) { extern "C" void raise3(STOLEN(Box*) arg0, STOLEN(Box*) arg1, STOLEN(Box*) arg2) {
bool reraise = arg2 != NULL && arg2 != None; bool reraise = arg2 != NULL && arg2 != None;
auto exc_info = excInfoForRaise(arg0, arg1, arg2); auto exc_info = excInfoForRaise(arg0, arg1, arg2);
...@@ -189,7 +202,7 @@ extern "C" void raise3(Box* arg0, Box* arg1, Box* arg2) { ...@@ -189,7 +202,7 @@ extern "C" void raise3(Box* arg0, Box* arg1, Box* arg2) {
throw exc_info; throw exc_info;
} }
extern "C" void raise3_capi(Box* arg0, Box* arg1, Box* arg2) noexcept { extern "C" void raise3_capi(STOLEN(Box*) arg0, STOLEN(Box*) arg1, STOLEN(Box*) arg2) noexcept {
bool reraise = arg2 != NULL && arg2 != None; bool reraise = arg2 != NULL && arg2 != None;
ExcInfo exc_info(NULL, NULL, NULL); ExcInfo exc_info(NULL, NULL, NULL);
...@@ -262,9 +275,9 @@ extern "C" void caughtCapiException() { ...@@ -262,9 +275,9 @@ extern "C" void caughtCapiException() {
extern "C" void reraiseCapiExcAsCxx() { extern "C" void reraiseCapiExcAsCxx() {
ensureCAPIExceptionSet(); ensureCAPIExceptionSet();
// TODO: we are normalizing to many times? // TODO: we are normalizing to many times?
ExcInfo e = excInfoForRaise(cur_thread_state.curexc_type, cur_thread_state.curexc_value, Box* type, *value, *tb;
cur_thread_state.curexc_traceback); PyErr_Fetch(&type, &value, &tb);
PyErr_Clear(); ExcInfo e = excInfoForRaise(type, value, tb);
startReraise(); startReraise();
throw e; throw e;
} }
......
...@@ -217,6 +217,23 @@ extern "C" void deinitFrame(FrameInfo* frame_info) { ...@@ -217,6 +217,23 @@ extern "C" void deinitFrame(FrameInfo* frame_info) {
Py_CLEAR(frame_info->frame_obj); Py_CLEAR(frame_info->frame_obj);
} }
Py_CLEAR(frame_info->boxedLocals); Py_CLEAR(frame_info->boxedLocals);
if (frame_info->exc.type) {
Py_CLEAR(frame_info->exc.type);
Py_CLEAR(frame_info->exc.value);
Py_CLEAR(frame_info->exc.traceback);
}
}
extern "C" void setFrameExcInfo(FrameInfo* frame_info, STOLEN(Box*) type, STOLEN(Box*) value, STOLEN(Box*) tb) {
if (frame_info->exc.type) {
Py_DECREF(frame_info->exc.type);
Py_DECREF(frame_info->exc.value);
Py_DECREF(frame_info->exc.traceback);
}
frame_info->exc.type = type;
frame_info->exc.value = value;
frame_info->exc.traceback = tb;
} }
extern "C" int PyFrame_GetLineNumber(PyFrameObject* _f) noexcept { extern "C" int PyFrame_GetLineNumber(PyFrameObject* _f) noexcept {
......
...@@ -74,6 +74,7 @@ void force() { ...@@ -74,6 +74,7 @@ void force() {
FORCE(initFrame); FORCE(initFrame);
FORCE(deinitFrame); FORCE(deinitFrame);
FORCE(makePendingCalls); FORCE(makePendingCalls);
FORCE(setFrameExcInfo);
FORCE(getattr); FORCE(getattr);
FORCE(getattr_capi); FORCE(getattr_capi);
......
...@@ -1270,6 +1270,7 @@ Box* getFrame(int depth); ...@@ -1270,6 +1270,7 @@ Box* getFrame(int depth);
void frameInvalidateBack(BoxedFrame* frame); void frameInvalidateBack(BoxedFrame* frame);
extern "C" void deinitFrame(FrameInfo* frame_info); extern "C" void deinitFrame(FrameInfo* frame_info);
extern "C" void initFrame(FrameInfo* frame_info); extern "C" void initFrame(FrameInfo* frame_info);
extern "C" void setFrameExcInfo(FrameInfo* frame_info, STOLEN(Box*) type, STOLEN(Box*) value, STOLEN(Box*) tb);
inline BoxedString* boxString(llvm::StringRef s) { inline BoxedString* boxString(llvm::StringRef s) {
if (s.size() <= 1) { if (s.size() <= 1) {
......
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