Commit 4cf93f94 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Implement the sequence iterator protocol

ie if you just have a __getitem__ method, you are still iterable.

This patch is unfortunately yet another performance regression, since
we obscure the behavior for known types (ie no longer try to go directly
to the __iter__).  Should make this into a call into type-specific behavior,
so that known types can use __iter__ if they have one?
parent d3abbf16
......@@ -361,6 +361,8 @@ private:
return UNKNOWN;
case AST_LangPrimitive::LOCALS:
return DICT;
case AST_LangPrimitive::GET_ITER:
return UNKNOWN;
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......
......@@ -399,6 +399,24 @@ private:
return rtn;
}
case AST_LangPrimitive::GET_ITER: {
// TODO if this is a type that has an __iter__, we could do way better than this, both in terms of
// function call overhead and resulting type information, if we went with that instead of the generic
// version.
// TODO Move this behavior into to the type-specific section (compvars.cpp)?
emitter.getBuilder();
assert(node->args.size() == 1);
CompilerVariable* obj = evalExpr(node->args[0], exc_info);
ConcreteCompilerVariable* converted_obj = obj->makeConverted(emitter, obj->getBoxType());
obj->decvref(emitter);
llvm::Value* v
= emitter.createCall(exc_info, g.funcs.getiter, { converted_obj->getValue() }).getInstruction();
assert(v->getType() == g.llvm_value_type_ptr);
return new ConcreteCompilerVariable(UNKNOWN, v, true);
}
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......
......@@ -196,6 +196,7 @@ void initGlobalFuncs(GlobalState& g) {
GET(str);
GET(isinstance);
GET(yield);
GET(getiter);
GET(checkUnpackingLength);
GET(raiseAttributeError);
......
......@@ -37,7 +37,7 @@ struct GlobalFuncs {
*createUserClass, *createClosure, *createGenerator, *createLong, *createSet;
llvm::Value* getattr, *setattr, *delattr, *delitem, *delGlobal, *print, *nonzero, *binop, *compare, *augbinop,
*unboxedLen, *getitem, *getclsattr, *getGlobal, *setitem, *unaryop, *import, *importFrom, *importStar, *repr,
*str, *isinstance, *yield;
*str, *isinstance, *yield, *getiter;
llvm::Value* checkUnpackingLength, *raiseAttributeError, *raiseAttributeErrorStr, *raiseNotIterableError,
*assertNameDefined, *assertFail;
......
......@@ -1354,13 +1354,16 @@ bool PrintVisitor::visit_langprimitive(AST_LangPrimitive* node) {
printf(":");
switch (node->opcode) {
case AST_LangPrimitive::ISINSTANCE:
printf("isinstance");
printf("ISINSTANCE");
break;
case AST_LangPrimitive::LANDINGPAD:
printf("landingpad");
printf("LANDINGPAD");
break;
case AST_LangPrimitive::LOCALS:
printf("locals");
printf("LOCALS");
break;
case AST_LangPrimitive::GET_ITER:
printf("GET_ITER");
break;
default:
RELEASE_ASSERT(0, "%d", node->opcode);
......
......@@ -912,6 +912,7 @@ public:
ISINSTANCE,
LANDINGPAD,
LOCALS,
GET_ITER,
} opcode;
std::vector<AST_expr*> args;
......
......@@ -156,8 +156,8 @@ private:
bool is_innermost = (i == n - 1);
AST_expr* remapped_iter = remapExpr(c->iter);
AST_expr* iter_attr = makeLoadAttribute(remapped_iter, "__iter__", true);
AST_expr* iter_call = makeCall(iter_attr);
AST_LangPrimitive* iter_call = new AST_LangPrimitive(AST_LangPrimitive::GET_ITER);
iter_call->args.push_back(remapped_iter);
std::string iter_name = nodeName(node, "lc_iter", i);
AST_stmt* iter_assign = makeAssign(iter_name, iter_call);
push_back(iter_assign);
......@@ -1440,8 +1440,8 @@ public:
// critical edges and needed to be broken, otherwise it's not too different.
AST_expr* remapped_iter = remapExpr(node->iter);
AST_expr* iter_attr = makeLoadAttribute(remapped_iter, "__iter__", true);
AST_expr* iter_call = makeCall(iter_attr);
AST_LangPrimitive* iter_call = new AST_LangPrimitive(AST_LangPrimitive::GET_ITER);
iter_call->args.push_back(remapped_iter);
char itername_buf[80];
snprintf(itername_buf, 80, "#iter_%p", node);
......
......@@ -638,6 +638,8 @@ void setupBuiltins() {
builtins_module->giveAttr("globals", new BoxedFunction(boxRTFunction((void*)globals, UNKNOWN, 0, 0, false, false)));
builtins_module->giveAttr("iter", new BoxedFunction(boxRTFunction((void*)getiter, UNKNOWN, 1, 0, false, false)));
builtins_module->giveAttr("map", new BoxedFunction(boxRTFunction((void*)map2, LIST, 2)));
builtins_module->giveAttr("filter", new BoxedFunction(boxRTFunction((void*)filter2, LIST, 2)));
builtins_module->giveAttr("zip", new BoxedFunction(boxRTFunction((void*)zip2, LIST, 2)));
......
......@@ -88,6 +88,7 @@ void force() {
FORCE(str);
FORCE(isinstance);
FORCE(yield);
FORCE(getiter);
FORCE(checkUnpackingLength);
FORCE(raiseAttributeError);
......
// Copyright (c) 2014 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 "runtime/iterobject.h"
#include <cmath>
#include <sstream>
#include "codegen/compvars.h"
#include "core/common.h"
#include "core/options.h"
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/inline/boxing.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
namespace pyston {
BoxedClass* seqiter_cls;
Box* seqiterHasnext(Box* s) {
RELEASE_ASSERT(s->cls == seqiter_cls, "");
BoxedSeqIter* self = static_cast<BoxedSeqIter*>(s);
Box* next;
try {
next = getitem(self->b, boxInt(self->idx));
} catch (Box* b) {
return False;
}
self->idx++;
self->next = next;
return True;
}
Box* seqiterNext(Box* s) {
RELEASE_ASSERT(s->cls == seqiter_cls, "");
BoxedSeqIter* self = static_cast<BoxedSeqIter*>(s);
RELEASE_ASSERT(self->next, "");
Box* r = self->next;
self->next = NULL;
return r;
}
void setupIter() {
seqiter_cls = new BoxedClass(object_cls, NULL, 0, sizeof(BoxedSeqIter), false);
seqiter_cls->giveAttr("__name__", boxStrConstant("iterator"));
seqiter_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)seqiterNext, UNKNOWN, 1)));
seqiter_cls->giveAttr("__hasnext__", new BoxedFunction(boxRTFunction((void*)seqiterHasnext, BOXED_BOOL, 1)));
seqiter_cls->freeze();
}
}
// Copyright (c) 2014 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.
#ifndef PYSTON_RUNTIME_ITEROBJECT_H
#define PYSTON_RUNTIME_ITEROBJECT_H
#include <climits>
#include "core/common.h"
#include "runtime/types.h"
namespace pyston {
extern BoxedClass* seqiter_cls;
class BoxedSeqIter : public Box {
public:
Box* b;
int64_t idx;
Box* next;
BoxedSeqIter(Box* b) : Box(seqiter_cls), b(b), idx(0), next(NULL) {}
};
void setupIter();
}
#endif
......@@ -40,6 +40,7 @@
#include "runtime/capi.h"
#include "runtime/float.h"
#include "runtime/generator.h"
#include "runtime/iterobject.h"
#include "runtime/types.h"
#include "runtime/util.h"
......@@ -3154,6 +3155,21 @@ extern "C" void delattr(Box* obj, const char* attr) {
delattr_internal(obj, attr, true, NULL);
}
extern "C" Box* getiter(Box* o) {
// TODO add rewriting to this? probably want to try to avoid this path though
static const std::string iter_str("__iter__");
Box* r = callattrInternal0(o, &iter_str, LookupScope::CLASS_ONLY, NULL, ArgPassSpec(0));
if (r)
return r;
static const std::string getitem_str("__getitem__");
if (typeLookup(o->cls, getitem_str, NULL)) {
return new BoxedSeqIter(o);
}
raiseExcHelper(TypeError, "'%s' object is not iterable", getTypeName(o)->c_str());
}
// For use on __init__ return values
static void assertInitNone(Box* obj) {
if (obj != None) {
......
......@@ -83,6 +83,7 @@ extern "C" void assertNameDefined(bool b, const char* name, BoxedClass* exc_cls,
extern "C" void assertFail(BoxedModule* inModule, Box* msg);
extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent);
extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure);
extern "C" Box* getiter(Box* o);
class BinopRewriteArgs;
extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, BinopRewriteArgs* rewrite_args);
......
......@@ -27,6 +27,7 @@
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/iterobject.h"
#include "runtime/long.h"
#include "runtime/objmodel.h"
#include "runtime/set.h"
......@@ -721,6 +722,7 @@ void setupRuntime() {
setupTuple();
setupFile();
setupGenerator();
setupIter();
function_cls->giveAttr("__name__", boxStrConstant("function"));
function_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)functionRepr, STR, 1)));
......
# Objects are iterable if they just implement __getitem__:
class C(object):
def __getitem__(self, i):
print "getitem", i
return range(10)[i]
print type(iter(C()))
for i in C():
print i
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