Commit e4938dd2 authored by Marius Wachtler's avatar Marius Wachtler

only lookup __[set/get/del]slice__ for slice AST nodes

before this change
C()[slice(1,2)] = "a"
would call into `__setslice__` because it got handled as
C()[1,2] = "a"

This commits adds cpythons assign_slice/apply_slice and add rewriter support for them.
parent 2edfc49b
......@@ -287,24 +287,29 @@ void RewriterVar::addGuard(uint64_t val) {
rewriter->addAction([=]() { rewriter->_addGuard(this, val_var); }, { this, val_var }, ActionType::GUARD);
}
void Rewriter::_nextSlotJump(bool condition_eq) {
void Rewriter::_nextSlotJump(assembler::ConditionCode condition) {
// If a jump offset is larger then 0x80 the instruction encoding requires 6bytes instead of 2bytes.
// This adds up quickly, thats why we will try to find another jump to the slowpath with the same condition with a
// smaller offset and jump to it / use it as a trampoline.
// The benchmark show that this increases the performance slightly even though it introduces additional jumps.
int& last_jmp_offset = condition_eq ? offset_eq_jmp_next_slot : offset_ne_jmp_next_slot;
auto condition = condition_eq ? assembler::COND_EQUAL : assembler::COND_NOT_EQUAL;
int last_jmp_offset = -1;
for (auto it = next_slot_jmps.rbegin(), it_end = next_slot_jmps.rend(); it != it_end; ++it) {
if (std::get<2>(*it) == condition) {
last_jmp_offset = std::get<0>(*it);
break;
}
}
if (last_jmp_offset != -1 && assembler->bytesWritten() - last_jmp_offset < 0x80) {
assembler->jmp_cond(assembler::JumpDestination::fromStart(last_jmp_offset), condition);
} else {
last_jmp_offset = assembler->bytesWritten();
int last_jmp_offset = assembler->bytesWritten();
assembler->jmp_cond(assembler::JumpDestination::fromStart(rewrite->getSlotSize()), condition);
next_slot_jmps.emplace_back(last_jmp_offset, assembler->bytesWritten(), condition);
}
}
void Rewriter::_addGuard(RewriterVar* var, RewriterVar* val_constant) {
void Rewriter::_addGuard(RewriterVar* var, RewriterVar* val_constant, bool negate) {
if (LOG_IC_ASSEMBLY)
assembler->comment("_addGuard");
......@@ -316,12 +321,15 @@ void Rewriter::_addGuard(RewriterVar* var, RewriterVar* val_constant) {
assembler::Register reg = val_constant->getInReg(Location::any(), true, /* otherThan */ var_reg);
assembler->cmp(var_reg, reg);
} else {
if (val == 0)
assembler->test(var_reg, var_reg);
else
assembler->cmp(var_reg, assembler::Immediate(val));
}
restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
assertArgsInPlace();
_nextSlotJump(false /*= not equal jmp */);
_nextSlotJump(negate ? assembler::COND_EQUAL : assembler::COND_NOT_EQUAL);
var->bumpUse();
val_constant->bumpUse();
......@@ -333,32 +341,24 @@ void RewriterVar::addGuardNotEq(uint64_t val) {
STAT_TIMER(t0, "us_timer_rewriter", 10);
RewriterVar* val_var = rewriter->loadConst(val);
rewriter->addAction([=]() { rewriter->_addGuardNotEq(this, val_var); }, { this, val_var }, ActionType::GUARD);
rewriter->addAction([=]() { rewriter->_addGuard(this, val_var, true /* negate */); }, { this, val_var },
ActionType::GUARD);
}
void Rewriter::_addGuardNotEq(RewriterVar* var, RewriterVar* val_constant) {
if (LOG_IC_ASSEMBLY)
assembler->comment("_addGuardNotEq");
void RewriterVar::addGuardNotLt0() {
rewriter->addAction([=]() {
assembler::Register var_reg = this->getInReg();
rewriter->assembler->test(var_reg, var_reg);
assert(val_constant->is_constant);
uint64_t val = val_constant->constant_value;
rewriter->restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
rewriter->assertArgsInPlace();
assembler::Register var_reg = var->getInReg();
if (isLargeConstant(val)) {
assembler::Register reg = val_constant->getInReg(Location::any(), true, /* otherThan */ var_reg);
assembler->cmp(var_reg, reg);
} else {
assembler->cmp(var_reg, assembler::Immediate(val));
}
rewriter->_nextSlotJump(assembler::COND_SIGN);
restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
assertArgsInPlace();
_nextSlotJump(true /*= equal jmp */);
bumpUse();
rewriter->assertConsistent();
var->bumpUse();
val_constant->bumpUse();
assertConsistent();
}, { this }, ActionType::GUARD);
}
void RewriterVar::addAttrGuard(int offset, uint64_t val, bool negate) {
......@@ -405,7 +405,7 @@ void Rewriter::_addAttrGuard(RewriterVar* var, int offset, RewriterVar* val_cons
restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
assertArgsInPlace();
_nextSlotJump(negate);
_nextSlotJump(negate ? assembler::COND_EQUAL : assembler::COND_NOT_EQUAL);
var->bumpUse();
val_constant->bumpUse();
......@@ -2220,8 +2220,6 @@ Rewriter::Rewriter(std::unique_ptr<ICSlotRewrite> rewrite, int num_args, const L
marked_inside_ic(false),
done_guarding(false),
last_guard_action(-1),
offset_eq_jmp_next_slot(-1),
offset_ne_jmp_next_slot(-1),
allocatable_regs(std_allocatable_regs) {
initPhaseCollecting();
......
......@@ -147,6 +147,7 @@ public:
void addGuard(uint64_t val);
void addGuardNotEq(uint64_t val);
void addGuardNotLt0();
void addAttrGuard(int offset, uint64_t val, bool negate = false);
RewriterVar* getAttr(int offset, Location loc = Location::any(), assembler::MovType type = assembler::MovType::Q);
// getAttrFloat casts to double (maybe I should make that separate?)
......@@ -502,8 +503,6 @@ protected:
}
int last_guard_action;
int offset_eq_jmp_next_slot;
int offset_ne_jmp_next_slot;
// keeps track of all jumps to the next slot so we can patch them if the size of the current slot changes
std::vector<NextSlotJumpInfo> next_slot_jmps;
......@@ -540,7 +539,7 @@ protected:
bool finishAssembly(int continue_offset, bool& should_fill_with_nops, bool& variable_size_slots) override;
void _nextSlotJump(bool condition_eq);
void _nextSlotJump(assembler::ConditionCode condition);
void _trap();
void _loadConst(RewriterVar* result, int64_t val);
void _setupCall(bool has_side_effects, llvm::ArrayRef<RewriterVar*> args = {},
......@@ -557,8 +556,7 @@ protected:
void _checkAndThrowCAPIException(RewriterVar* r, int64_t exc_val, assembler::MovType size);
// The public versions of these are in RewriterVar
void _addGuard(RewriterVar* var, RewriterVar* val_constant);
void _addGuardNotEq(RewriterVar* var, RewriterVar* val_constant);
void _addGuard(RewriterVar* var, RewriterVar* val_constant, bool negate = false);
void _addAttrGuard(RewriterVar* var, int offset, RewriterVar* val_constant, bool negate = false);
void _getAttr(RewriterVar* result, RewriterVar* var, int offset, Location loc = Location::any(),
assembler::MovType type = assembler::MovType::Q);
......
......@@ -1242,7 +1242,7 @@ static int slot_tp_descr_set(PyObject* self, PyObject* target, PyObject* value)
PyObject* slot_sq_item(PyObject* self, Py_ssize_t i) noexcept {
STAT_TIMER(t0, "us_timer_slot_sqitem", SLOT_AVOIDABILITY(self));
return getitemInternal<CAPI>(self, autoDecref(boxInt(i)));
return PyObject_GetItem(self, autoDecref(boxInt(i)));
}
/* Pyston change: static */ Py_ssize_t slot_sq_length(PyObject* self) noexcept {
......
......@@ -554,12 +554,27 @@ void ASTInterpreter::doStore(AST_expr* node, STOLEN(Value) value) {
Value target = visit_expr(subscript->value);
AUTO_DECREF(target.o);
bool is_slice = (subscript->slice->type == AST_TYPE::Slice) && (((AST_Slice*)subscript->slice)->step == NULL);
if (is_slice) {
AST_Slice* slice = (AST_Slice*)subscript->slice;
Value lower = slice->lower ? visit_expr(slice->lower) : Value();
AUTO_XDECREF(lower.o);
Value upper = slice->upper ? visit_expr(slice->upper) : Value();
AUTO_XDECREF(upper.o);
assert(slice->step == NULL);
if (jit)
jit->emitAssignSlice(target, lower, upper, value);
assignSlice(target.o, lower.o, upper.o, value.o);
} else {
Value slice = visit_slice(subscript->slice);
AUTO_DECREF(slice.o);
if (jit)
jit->emitSetItem(target, slice, value);
setitem(target.o, slice.o, value.o);
}
} else {
RELEASE_ASSERT(0, "not implemented");
}
......@@ -1321,11 +1336,26 @@ Value ASTInterpreter::visit_delete(AST_Delete* node) {
AST_Subscript* sub = (AST_Subscript*)target_;
Value value = visit_expr(sub->value);
AUTO_DECREF(value.o);
bool is_slice = (sub->slice->type == AST_TYPE::Slice) && (((AST_Slice*)sub->slice)->step == NULL);
if (is_slice) {
AST_Slice* slice = (AST_Slice*)sub->slice;
Value lower = slice->lower ? visit_expr(slice->lower) : Value();
AUTO_XDECREF(lower.o);
Value upper = slice->upper ? visit_expr(slice->upper) : Value();
AUTO_XDECREF(upper.o);
assert(slice->step == NULL);
if (jit)
jit->emitAssignSlice(value, lower, upper, jit->imm(0ul));
assignSlice(value.o, lower.o, upper.o, NULL);
} else {
Value slice = visit_slice(sub->slice);
AUTO_DECREF(slice.o);
if (jit)
jit->emitDelItem(value, slice);
delitem(value.o, slice.o);
}
break;
}
case AST_TYPE::Attribute: {
......@@ -1735,10 +1765,31 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
Value ASTInterpreter::visit_subscript(AST_Subscript* node) {
Value value = visit_expr(node->value);
AUTO_DECREF(value.o);
bool is_slice = (node->slice->type == AST_TYPE::Slice) && (((AST_Slice*)node->slice)->step == NULL);
if (is_slice) {
AST_Slice* slice = (AST_Slice*)node->slice;
Value lower = slice->lower ? visit_expr(slice->lower) : Value();
AUTO_XDECREF(lower.o);
Value upper = slice->upper ? visit_expr(slice->upper) : Value();
AUTO_XDECREF(upper.o);
assert(slice->step == NULL);
Value v;
if (jit)
v.var = jit->emitApplySlice(value, lower, upper);
v.o = applySlice(value.o, lower.o, upper.o);
return v;
} else {
Value slice = visit_slice(node->slice);
AUTO_DECREF(slice.o);
return Value(getitem(value.o, slice.o), jit ? jit->emitGetItem(node, value, slice) : NULL);
Value v;
if (jit)
v.var = jit->emitGetItem(node, value, slice);
v.o = getitem(value.o, slice.o);
return v;
}
}
Value ASTInterpreter::visit_list(AST_List* node) {
......
......@@ -208,6 +208,14 @@ RewriterVar* JitFragmentWriter::emitAugbinop(AST_expr* node, RewriterVar* lhs, R
return emitPPCall((void*)augbinop, { lhs, rhs, imm(op_type) }, 2 * 320, node).first->setType(RefType::OWNED);
}
RewriterVar* JitFragmentWriter::emitApplySlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper) {
if (!lower)
lower = imm(0ul);
if (!upper)
upper = imm(0ul);
return emitPPCall((void*)applySlice, { target, lower, upper }, 256).first->setType(RefType::OWNED);
}
RewriterVar* JitFragmentWriter::emitBinop(AST_expr* node, RewriterVar* lhs, RewriterVar* rhs, int op_type) {
return emitPPCall((void*)binop, { lhs, rhs, imm(op_type) }, 2 * 240, node).first->setType(RefType::OWNED);
}
......@@ -553,6 +561,15 @@ RewriterVar* JitFragmentWriter::emitYield(RewriterVar* v) {
return rtn;
}
void JitFragmentWriter::emitAssignSlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper,
RewriterVar* value) {
if (!lower)
lower = imm(0ul);
if (!upper)
upper = imm(0ul);
emitPPCall((void*)assignSlice, { target, lower, upper, value }, 256).first;
}
void JitFragmentWriter::emitDelAttr(RewriterVar* target, BoxedString* attr) {
emitPPCall((void*)delattr, { target, imm(attr) }, 144).first;
}
......
......@@ -239,6 +239,7 @@ public:
RewriterVar* imm(void* val);
RewriterVar* emitAugbinop(AST_expr* node, RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitApplySlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper);
RewriterVar* emitBinop(AST_expr* node, RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitCallattr(AST_expr* node, RewriterVar* obj, BoxedString* attr, CallattrFlags flags,
const llvm::ArrayRef<RewriterVar*> args, std::vector<BoxedString*>* keyword_names);
......@@ -275,6 +276,7 @@ public:
std::vector<RewriterVar*> emitUnpackIntoArray(RewriterVar* v, uint64_t num);
RewriterVar* emitYield(RewriterVar* v);
void emitAssignSlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper, RewriterVar* value);
void emitDelAttr(RewriterVar* target, BoxedString* attr);
void emitDelGlobal(BoxedString* name);
void emitDelItem(RewriterVar* target, RewriterVar* slice);
......
......@@ -331,6 +331,9 @@ public:
CompilerVariable* getitem(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
CompilerVariable* slice) override {
ExceptionStyle target_exception_style = info.preferredExceptionStyle();
bool do_patchpoint = ENABLE_ICGETITEMS;
if (slice->getType() == UNBOXED_SLICE) {
UnboxedSlice slice_val = extractSlice(slice);
......@@ -340,31 +343,37 @@ public:
= var->getType()->getattrType(attr, true)->callType(ArgPassSpec(1), { SLICE }, NULL);
assert(return_type->getConcreteType() == return_type);
if (return_type != UNDEF) {
llvm::Value* cstart, *cstop;
cstart = slice_val.start ? slice_val.start->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
cstop = slice_val.stop ? slice_val.stop->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
llvm::Value* r = emitter.createCall3(info.unw_info, g.funcs.apply_slice, var->getValue(), cstart,
cstop, CAPI, getNullPtr(g.llvm_value_type_ptr));
llvm::Value* r = NULL;
if (do_patchpoint) {
ICSetupInfo* pp = createGetitemIC(info.getTypeRecorder(), info.getBJitICInfo());
llvm::Instruction* uncasted = emitter.createIC(
pp, (void*)(target_exception_style == CAPI ? pyston::apply_slice : pyston::applySlice),
{ var->getValue(), cstart, cstop }, info.unw_info, target_exception_style,
getNullPtr(g.llvm_value_type_ptr));
r = createAfter<llvm::IntToPtrInst>(uncasted, uncasted, g.llvm_value_type_ptr, "");
} else {
r = emitter.createCall3(
info.unw_info, target_exception_style == CAPI ? g.funcs.apply_slice : g.funcs.applySlice,
var->getValue(), cstart, cstop, target_exception_style, getNullPtr(g.llvm_value_type_ptr));
}
emitter.setType(r, RefType::OWNED);
if (target_exception_style == CAPI)
emitter.setNullable(r, true);
if (return_type != UNDEF)
return new ConcreteCompilerVariable(static_cast<ConcreteCompilerType*>(return_type), r);
} else {
// TODO: we could directly emit an exception if we know getitem is undefined but for now let it just
// call the normal getitem which will raise the exception
}
return new ConcreteCompilerVariable(UNKNOWN, r);
}
}
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
ExceptionStyle target_exception_style = info.preferredExceptionStyle();
bool do_patchpoint = ENABLE_ICGETITEMS;
llvm::Value* rtn;
if (do_patchpoint) {
ICSetupInfo* pp = createGetitemIC(info.getTypeRecorder(), info.getBJitICInfo());
......
......@@ -1508,7 +1508,6 @@ private:
CompilerVariable* evalSubscript(AST_Subscript* node, const UnwindInfo& unw_info) {
CompilerVariable* value = evalExpr(node->value, unw_info);
CompilerVariable* slice = evalSlice(node->slice, unw_info);
CompilerVariable* rtn = value->getitem(emitter, getOpInfoForNode(node, unw_info), slice);
return rtn;
}
......@@ -1992,15 +1991,42 @@ private:
t->setattr(emitter, getEmptyOpInfo(unw_info), target->attr.getBox(), val);
}
void _assignSlice(llvm::Value* target, llvm::Value* value, const UnboxedSlice& slice_val,
const UnwindInfo& unw_info) {
llvm::Value* cstart, *cstop;
cstart = slice_val.start ? slice_val.start->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
cstop = slice_val.stop ? slice_val.stop->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
bool do_patchpoint = ENABLE_ICSETITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createSetitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
std::vector<llvm::Value*> llvm_args;
llvm_args.push_back(target);
llvm_args.push_back(cstart);
llvm_args.push_back(cstop);
llvm_args.push_back(value);
emitter.createIC(pp, (void*)pyston::assignSlice, llvm_args, unw_info);
} else {
emitter.createCall(unw_info, g.funcs.assignSlice, { target, cstart, cstop, value });
}
}
void _doSetitem(AST_Subscript* target, CompilerVariable* val, const UnwindInfo& unw_info) {
CompilerVariable* tget = evalExpr(target->value, unw_info);
CompilerVariable* slice = evalSlice(target->slice, unw_info);
CompilerVariable* slice = evalSlice(target->slice, unw_info);
ConcreteCompilerVariable* converted_target = tget->makeConverted(emitter, tget->getBoxType());
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
ConcreteCompilerVariable* converted_val = val->makeConverted(emitter, val->getBoxType());
if (slice->getType() == UNBOXED_SLICE && !extractSlice(slice).step) {
_assignSlice(converted_target->getValue(), converted_val->getValue(), extractSlice(slice), unw_info);
} else {
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
// TODO add a CompilerVariable::setattr, which can (similar to getitem)
// statically-resolve the function if possible, and only fall back to
// patchpoints if it couldn't.
......@@ -2015,8 +2041,9 @@ private:
emitter.createIC(pp, (void*)pyston::setitem, llvm_args, unw_info);
} else {
emitter.createCall3(unw_info, g.funcs.setitem, converted_target->getValue(), converted_slice->getValue(),
converted_val->getValue());
emitter.createCall3(unw_info, g.funcs.setitem, converted_target->getValue(),
converted_slice->getValue(), converted_val->getValue());
}
}
}
......@@ -2125,6 +2152,12 @@ private:
CompilerVariable* slice = evalSlice(target->slice, unw_info);
ConcreteCompilerVariable* converted_target = tget->makeConverted(emitter, tget->getBoxType());
if (slice->getType() == UNBOXED_SLICE && !extractSlice(slice).step) {
_assignSlice(converted_target->getValue(),
emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED), extractSlice(slice),
unw_info);
} else {
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
bool do_patchpoint = ENABLE_ICDELITEMS;
......@@ -2137,7 +2170,9 @@ private:
emitter.createIC(pp, (void*)pyston::delitem, llvm_args, unw_info);
} else {
emitter.createCall2(unw_info, g.funcs.delitem, converted_target->getValue(), converted_slice->getValue());
emitter.createCall2(unw_info, g.funcs.delitem, converted_target->getValue(),
converted_slice->getValue());
}
}
}
......
......@@ -229,6 +229,8 @@ void initGlobalFuncs(GlobalState& g) {
GET(getiterHelper);
GET(hasnext);
GET(apply_slice);
GET(applySlice);
GET(assignSlice);
GET(unpackIntoArray);
GET(raiseAttributeError);
......
......@@ -39,7 +39,7 @@ struct GlobalFuncs {
llvm::Value* getattr, *getattr_capi, *setattr, *delattr, *delitem, *delGlobal, *nonzero, *binop, *compare,
*augbinop, *unboxedLen, *getitem, *getitem_capi, *getclsattr, *getGlobal, *setitem, *unaryop, *import,
*importFrom, *importStar, *repr, *exceptionMatches, *yield_capi, *getiterHelper, *hasnext, *setGlobal,
*apply_slice;
*apply_slice, *applySlice, *assignSlice;
llvm::Value* unpackIntoArray, *raiseAttributeError, *raiseAttributeErrorStr, *raiseAttributeErrorCapi,
*raiseAttributeErrorStrCapi, *raiseNotIterableError, *raiseIndexErrorStr, *raiseIndexErrorStrCapi,
......
......@@ -777,6 +777,15 @@ Box* instanceGetslice(Box* _inst, Box* i, Box* j) {
return runtimeCall(getslice_func, ArgPassSpec(2), i, j, NULL, NULL, NULL);
}
Box* instance_slice(PyObject* self, Py_ssize_t i, Py_ssize_t j) noexcept {
try {
return instanceGetslice(self, autoDecref(boxInt(i)), autoDecref((boxInt(j))));
} catch (ExcInfo e) {
setCAPIException(e);
return 0;
}
}
Box* instanceSetslice(Box* _inst, Box* i, Box* j, Box** sequence) {
RELEASE_ASSERT(_inst->cls == instance_cls, "");
BoxedInstance* inst = static_cast<BoxedInstance*>(_inst);
......@@ -2005,5 +2014,6 @@ void setupClassobj() {
instance_cls->tp_as_number->nb_index = instance_index;
instance_cls->tp_as_number->nb_power = instance_pow;
instance_cls->tp_as_number->nb_inplace_power = instance_ipow;
instance_cls->tp_as_sequence->sq_slice = instance_slice;
}
}
......@@ -377,13 +377,13 @@ extern "C" BORROWED(PyObject*) PyDict_GetItem(PyObject* dict, PyObject* key) noe
/* preserve the existing exception */
PyObject* err_type, *err_value, *err_tb;
PyErr_Fetch(&err_type, &err_value, &err_tb);
Box* b = getitemInternal<CAPI>(dict, key);
Box* b = PyObject_GetItem(dict, key);
/* ignore errors */
PyErr_Restore(err_type, err_value, err_tb);
Py_XDECREF(b);
return b;
} else {
Box* b = getitemInternal<CAPI>(dict, key);
Box* b = PyObject_GetItem(dict, key);
if (b == NULL)
PyErr_Clear();
else
......@@ -514,13 +514,7 @@ extern "C" int PyDict_DelItem(PyObject* op, PyObject* key) noexcept {
}
ASSERT(op->cls == attrwrapper_cls, "%s", getTypeName(op));
try {
delitem(op, key);
return 0;
} catch (ExcInfo e) {
setCAPIException(e);
return -1;
}
return PyObject_DelItem(op, key);
}
extern "C" int PyDict_DelItemString(PyObject* v, const char* key) noexcept {
......
......@@ -105,6 +105,8 @@ void force() {
FORCE(getiterHelper);
FORCE(hasnext);
FORCE(apply_slice);
FORCE(applySlice);
FORCE(assignSlice);
FORCE(unpackIntoArray);
FORCE(raiseAttributeError);
......
This diff is collapsed.
......@@ -88,6 +88,9 @@ extern "C" Box* getitem_capi(Box* value, Box* slice) noexcept __attribute__((noi
extern "C" void setitem(Box* target, Box* slice, Box* value) __attribute__((noinline));
extern "C" void delitem(Box* target, Box* slice) __attribute__((noinline));
extern "C" PyObject* apply_slice(PyObject* u, PyObject* v, PyObject* w) noexcept;
extern "C" PyObject* applySlice(PyObject* u, PyObject* v, PyObject* w);
extern "C" int assign_slice(PyObject* u, PyObject* v, PyObject* w, PyObject* x) noexcept;
extern "C" void assignSlice(PyObject* u, PyObject* v, PyObject* w, PyObject* x);
extern "C" Box* getclsattr(Box* obj, BoxedString* attr) __attribute__((noinline));
extern "C" Box* getclsattrMaybeNonstring(Box* obj, Box* attr) __attribute__((noinline));
extern "C" Box* unaryop(Box* operand, int op_type) __attribute__((noinline));
......
......@@ -326,7 +326,7 @@ static PyTypeObject slots_tester_map= {
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
......
......@@ -242,3 +242,12 @@ class C(object):
c = C()
proxy = weakref.proxy(c)
print isinstance(proxy, C)
# test if __setslice__ is prioritised over mp_ass_subscript
class D(slots_test.SlotsTesterMap):
def __setslice__(self, *args):
print "setslice", args
d = D(1)
for i in xrange(10):
print i
d[1:2] = 1
......@@ -101,11 +101,13 @@ sliceable[3:8]
both[0]
both[:]
both[3:8]
both[slice(3, 8)]
both[::2] # this should call __getitem__ since __getslice__ doesn't support steps
both[0] = xrange(2)
both[:] = xrange(2)
both[3:8] = xrange(2)
both[slice(3, 8)] = xrange(2)
both[::2] = xrange(2)
# Should all call getitem as a fallback
......@@ -118,6 +120,7 @@ both[1:2:'c']
del both[0]
del both[:]
del both[3:8]
del both[slice(3, 8)]
del both [::2]
try:
......
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