[pypy-commit] pypy default: merge heads

arigo noreply at buildbot.pypy.org
Wed Dec 21 14:30:55 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r50800:9c5561654700
Date: 2011-12-21 10:46 +0100
http://bitbucket.org/pypy/pypy/changeset/9c5561654700/

Log:	merge heads

diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -45,6 +45,8 @@
 import pypy.module.cpyext.longobject
 import pypy.module.cpyext.listobject
 import pypy.module.cpyext.sequence
+import pypy.module.cpyext.buffer
+import pypy.module.cpyext.bufferobject
 import pypy.module.cpyext.eval
 import pypy.module.cpyext.import_
 import pypy.module.cpyext.mapping
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -317,6 +317,10 @@
 
 INTERPLEVEL_API = {}
 FUNCTIONS = {}
+
+# These are C symbols which cpyext will export, but which are defined in .c
+# files somewhere in the implementation of cpyext (rather than being defined in
+# RPython).
 SYMBOLS_C = [
     'Py_FatalError', 'PyOS_snprintf', 'PyOS_vsnprintf', 'PyArg_Parse',
     'PyArg_ParseTuple', 'PyArg_UnpackTuple', 'PyArg_ParseTupleAndKeywords',
diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/buffer.py
@@ -0,0 +1,11 @@
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.module.cpyext.api import (
+    cpython_api, CANNOT_FAIL, Py_buffer)
+
+ at cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real, error=CANNOT_FAIL)
+def PyBuffer_IsContiguous(space, view, fortran):
+    """Return 1 if the memory defined by the view is C-style (fortran is
+    'C') or Fortran-style (fortran is 'F') contiguous or either one
+    (fortran is 'A').  Return 0 otherwise."""
+    # PyPy only supports contiguous Py_buffers for now.
+    return space.wrap(1)
diff --git a/pypy/module/cpyext/bufferobject.py b/pypy/module/cpyext/bufferobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/bufferobject.py
@@ -0,0 +1,66 @@
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.module.cpyext.api import (
+    cpython_api, Py_ssize_t, cpython_struct, bootstrap_function,
+    PyObjectFields, PyObject)
+from pypy.module.cpyext.pyobject import make_typedescr, Py_DecRef
+from pypy.interpreter.buffer import Buffer, StringBuffer, SubBuffer
+
+
+PyBufferObjectStruct = lltype.ForwardReference()
+PyBufferObject = lltype.Ptr(PyBufferObjectStruct)
+PyBufferObjectFields = PyObjectFields + (
+    ("b_base", PyObject),
+    ("b_ptr", rffi.VOIDP),
+    ("b_size", Py_ssize_t),
+    ("b_offset", Py_ssize_t),
+    ("b_readonly", rffi.INT),
+    ("b_hash", rffi.LONG),
+    )
+
+cpython_struct("PyBufferObject", PyBufferObjectFields, PyBufferObjectStruct)
+
+ at bootstrap_function
+def init_bufferobject(space):
+    "Type description of PyBufferObject"
+    make_typedescr(space.gettypefor(Buffer).instancetypedef,
+                   basestruct=PyBufferObject.TO,
+                   attach=buffer_attach,
+                   dealloc=buffer_dealloc,
+                   realize=buffer_realize)
+
+def buffer_attach(space, py_obj, w_obj):
+    """
+    Fills a newly allocated PyBufferObject with the given (str) buffer object.
+    """
+    py_buf = rffi.cast(PyBufferObject, py_obj)
+    py_buf.c_b_offset = 0
+    rffi.setintfield(py_buf, 'c_b_readonly', 1)
+    rffi.setintfield(py_buf, 'c_b_hash', -1)
+
+    if isinstance(w_obj, SubBuffer):
+        py_buf.c_b_offset = w_obj.offset
+        w_obj = w_obj.buffer
+
+    if isinstance(w_obj, StringBuffer):
+        py_buf.c_b_base = rffi.cast(PyObject, 0) # space.wrap(w_obj.value)
+        py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, rffi.str2charp(w_obj.as_str()))
+        py_buf.c_b_size = w_obj.getlength()
+    else:
+        raise Exception("Fail fail fail fail fail")
+
+
+def buffer_realize(space, py_obj):
+    """
+    Creates the buffer in the PyPy interpreter from a cpyext representation.
+    """
+    raise Exception("realize fail fail fail")
+
+
+
+ at cpython_api([PyObject], lltype.Void, external=False)
+def buffer_dealloc(space, py_obj):
+    py_buf = rffi.cast(PyBufferObject, py_obj)
+    Py_DecRef(space, py_buf.c_b_base)
+    rffi.free_charp(rffi.cast(rffi.CCHARP, py_buf.c_b_ptr))
+    from pypy.module.cpyext.object import PyObject_dealloc
+    PyObject_dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/include/bufferobject.h b/pypy/module/cpyext/include/bufferobject.h
--- a/pypy/module/cpyext/include/bufferobject.h
+++ b/pypy/module/cpyext/include/bufferobject.h
@@ -9,6 +9,17 @@
 extern "C" {
 #endif
 
+typedef struct {
+	PyObject_HEAD
+	PyObject *b_base;
+	void *b_ptr;
+	Py_ssize_t b_size;
+	Py_ssize_t b_offset;
+	int b_readonly;
+	long b_hash;
+} PyBufferObject;
+
+
 PyAPI_DATA(PyTypeObject) PyBuffer_Type;
 
 #define PyBuffer_Check(op) (((PyObject*)(op))->ob_type == &PyBuffer_Type)
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -234,7 +234,7 @@
 	writebufferproc bf_getwritebuffer;
 	segcountproc bf_getsegcount;
 	charbufferproc bf_getcharbuffer;
-  getbufferproc bf_getbuffer;
+	getbufferproc bf_getbuffer;
 	releasebufferproc bf_releasebuffer;
 } PyBufferProcs;
 
diff --git a/pypy/module/cpyext/src/bufferobject.c b/pypy/module/cpyext/src/bufferobject.c
--- a/pypy/module/cpyext/src/bufferobject.c
+++ b/pypy/module/cpyext/src/bufferobject.c
@@ -4,17 +4,6 @@
 #include "Python.h"
 
 
-typedef struct {
-	PyObject_HEAD
-	PyObject *b_base;
-	void *b_ptr;
-	Py_ssize_t b_size;
-	Py_ssize_t b_offset;
-	int b_readonly;
-	long b_hash;
-} PyBufferObject;
-
-
 enum buffer_t {
     READ_BUFFER,
     WRITE_BUFFER,
diff --git a/pypy/module/cpyext/src/getargs.c b/pypy/module/cpyext/src/getargs.c
--- a/pypy/module/cpyext/src/getargs.c
+++ b/pypy/module/cpyext/src/getargs.c
@@ -777,18 +777,14 @@
 			Py_buffer *p = (Py_buffer *)va_arg(*p_va, Py_buffer *);
 
 			if (PyString_Check(arg)) {
+                            fflush(stdout);
 				PyBuffer_FillInfo(p, arg,
 						  PyString_AS_STRING(arg), PyString_GET_SIZE(arg),
 						  1, 0);
-			} else {
-                            PyErr_SetString(
-                                PyExc_NotImplementedError,
-                                "s* not implemented for non-string values");
-                            return NULL;
-                        }
-#if 0
+			}
 #ifdef Py_USING_UNICODE
 			else if (PyUnicode_Check(arg)) {
+#if 0
 				uarg = UNICODE_DEFAULT_ENCODING(arg);
 				if (uarg == NULL)
 					return converterr(CONV_UNICODE,
@@ -796,6 +792,9 @@
 				PyBuffer_FillInfo(p, arg,
 						  PyString_AS_STRING(uarg), PyString_GET_SIZE(uarg),
 						  1, 0);
+#else
+                                return converterr("string or buffer", arg, msgbuf, bufsize);
+#endif
 			}
 #endif
 			else { /* any buffer-like object */
@@ -803,7 +802,6 @@
 				if (getbuffer(arg, p, &buf) < 0)
 					return converterr(buf, arg, msgbuf, bufsize);
 			}
-#endif
 			if (addcleanup(p, freelist, cleanup_buffer)) {
 				return converterr(
 					"(cleanup problem)",
@@ -1342,7 +1340,6 @@
 	return count;
 }
 
-#if 0 //YYY
 static int
 getbuffer(PyObject *arg, Py_buffer *view, char **errmsg)
 {
@@ -1373,7 +1370,6 @@
 	PyBuffer_FillInfo(view, NULL, buf, count, 1, 0);
 	return 0;
 }
-#endif
 
 /* Support for keyword arguments donated by
    Geoff Philbrick <philbric at delphi.hks.com> */
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -1,5 +1,5 @@
 from pypy.module.cpyext.api import (
-    cpython_api, PyObject, PyObjectP, CANNOT_FAIL
+    cpython_api, PyObject, PyObjectP, CANNOT_FAIL, Py_buffer
     )
 from pypy.module.cpyext.complexobject import Py_complex_ptr as Py_complex
 from pypy.rpython.lltypesystem import rffi, lltype
@@ -10,7 +10,6 @@
 PyMethodDef = rffi.VOIDP
 PyGetSetDef = rffi.VOIDP
 PyMemberDef = rffi.VOIDP
-Py_buffer = rffi.VOIDP
 va_list = rffi.VOIDP
 PyDateTime_Date = rffi.VOIDP
 PyDateTime_DateTime = rffi.VOIDP
@@ -178,13 +177,6 @@
     ~Py_buffer.format."""
     raise NotImplementedError
 
- at cpython_api([Py_buffer, lltype.Char], rffi.INT_real, error=CANNOT_FAIL)
-def PyBuffer_IsContiguous(space, view, fortran):
-    """Return 1 if the memory defined by the view is C-style (fortran is
-    'C') or Fortran-style (fortran is 'F') contiguous or either one
-    (fortran is 'A').  Return 0 otherwise."""
-    raise NotImplementedError
-
 @cpython_api([rffi.INT_real, Py_ssize_t, Py_ssize_t, Py_ssize_t, lltype.Char], lltype.Void)
 def PyBuffer_FillContiguousStrides(space, ndim, shape, strides, itemsize, fortran):
     """Fill the strides array with byte-strides of a contiguous (C-style if
diff --git a/pypy/module/cpyext/test/test_getargs.py b/pypy/module/cpyext/test/test_getargs.py
--- a/pypy/module/cpyext/test/test_getargs.py
+++ b/pypy/module/cpyext/test/test_getargs.py
@@ -129,6 +129,21 @@
         assert 'foo\0bar\0baz' == pybuffer('foo\0bar\0baz')
 
 
+    def test_pyarg_parse_string_old_buffer(self):
+        pybuffer = self.import_parser(
+            '''
+            Py_buffer buf;
+            PyObject *result;
+            if (!PyArg_ParseTuple(args, "s*", &buf)) {
+                return NULL;
+            }
+            result = PyString_FromStringAndSize(buf.buf, buf.len);
+            PyBuffer_Release(&buf);
+            return result;
+            ''')
+        assert 'foo\0bar\0baz' == pybuffer(buffer('foo\0bar\0baz'))
+
+
     def test_pyarg_parse_charbuf_and_length(self):
         """
         The `t#` format specifier can be used to parse a read-only 8-bit
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -28,6 +28,7 @@
     PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs)
 from pypy.module.cpyext.slotdefs import (
     slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function)
+from pypy.interpreter.buffer import Buffer
 from pypy.interpreter.error import OperationError
 from pypy.rlib.rstring import rsplit
 from pypy.rlib.objectmodel import specialize
@@ -418,8 +419,21 @@
     Py_DecRef(space, pyref)
     return space.len_w(w_str)
 
+ at cpython_api([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed,
+             external=False, error=-1)
+def buf_getreadbuffer(space, pyref, segment, ref):
+    from pypy.module.cpyext.bufferobject import PyBufferObject
+    if segment != 0:
+        raise OperationError(space.w_SystemError, space.wrap
+                             ("accessing non-existent string segment"))
+    py_buf = rffi.cast(PyBufferObject, pyref)
+    ref[0] = py_buf.c_b_ptr
+    #Py_DecRef(space, pyref)
+    return py_buf.c_b_size
+
 def setup_string_buffer_procs(space, pto):
     c_buf = lltype.malloc(PyBufferProcs, flavor='raw', zero=True)
+    lltype.render_immortal(c_buf)
     c_buf.c_bf_getsegcount = llhelper(str_segcount.api_func.functype,
                                       str_segcount.api_func.get_wrapper(space))
     c_buf.c_bf_getreadbuffer = llhelper(str_getreadbuffer.api_func.functype,
@@ -429,6 +443,15 @@
     pto.c_tp_as_buffer = c_buf
     pto.c_tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER
 
+def setup_buffer_buffer_procs(space, pto):
+    c_buf = lltype.malloc(PyBufferProcs, flavor='raw', zero=True)
+    lltype.render_immortal(c_buf)
+    c_buf.c_bf_getsegcount = llhelper(str_segcount.api_func.functype,
+                                      str_segcount.api_func.get_wrapper(space))
+    c_buf.c_bf_getreadbuffer = llhelper(buf_getreadbuffer.api_func.functype,
+                                 buf_getreadbuffer.api_func.get_wrapper(space))
+    pto.c_tp_as_buffer = c_buf
+
 @cpython_api([PyObject], lltype.Void, external=False)
 def type_dealloc(space, obj):
     from pypy.module.cpyext.object import PyObject_dealloc
@@ -484,6 +507,8 @@
     # buffer protocol
     if space.is_w(w_type, space.w_str):
         setup_string_buffer_procs(space, pto)
+    if space.is_w(w_type, space.gettypefor(Buffer)):
+        setup_buffer_buffer_procs(space, pto)
 
     pto.c_tp_free = llhelper(PyObject_Del.api_func.functype,
             PyObject_Del.api_func.get_wrapper(space))
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -21,7 +21,6 @@
     _immutable_fields_ = ["itemtype", "num", "kind"]
 
     def __init__(self, itemtype, num, kind, name, char, w_box_type, alternate_constructors=[]):
-        self.signature = signature.BaseSignature()
         self.itemtype = itemtype
         self.num = num
         self.kind = kind
@@ -228,4 +227,4 @@
         )
 
 def get_dtype_cache(space):
-    return space.fromcache(DtypeCache)
\ No newline at end of file
+    return space.fromcache(DtypeCache)
diff --git a/pypy/module/micronumpy/interp_extras.py b/pypy/module/micronumpy/interp_extras.py
--- a/pypy/module/micronumpy/interp_extras.py
+++ b/pypy/module/micronumpy/interp_extras.py
@@ -4,4 +4,4 @@
 
 @unwrap_spec(array=BaseArray)
 def debug_repr(space, array):
-    return space.wrap(array.debug_repr())
+    return space.wrap(array.find_sig().debug_repr())
diff --git a/pypy/module/micronumpy/interp_iter.py b/pypy/module/micronumpy/interp_iter.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/interp_iter.py
@@ -0,0 +1,104 @@
+
+from pypy.rlib import jit
+from pypy.rlib.objectmodel import instantiate
+from pypy.module.micronumpy.strides import calculate_broadcast_strides
+
+# Iterators for arrays
+# --------------------
+# all those iterators with the exception of BroadcastIterator iterate over the
+# entire array in C order (the last index changes the fastest). This will
+# yield all elements. Views iterate over indices and look towards strides and
+# backstrides to find the correct position. Notably the offset between
+# x[..., i + 1] and x[..., i] will be strides[-1]. Offset between
+# x[..., k + 1, 0] and x[..., k, i_max] will be backstrides[-2] etc.
+
+# BroadcastIterator works like that, but for indexes that don't change source
+# in the original array, strides[i] == backstrides[i] == 0
+
+class BaseIterator(object):
+    def next(self, shapelen):
+        raise NotImplementedError
+
+    def done(self):
+        raise NotImplementedError
+
+class ArrayIterator(BaseIterator):
+    def __init__(self, size):
+        self.offset = 0
+        self.size = size
+
+    def next(self, shapelen):
+        arr = instantiate(ArrayIterator)
+        arr.size = self.size
+        arr.offset = self.offset + 1
+        return arr
+
+    def done(self):
+        return self.offset >= self.size
+
+class OneDimIterator(BaseIterator):
+    def __init__(self, start, step, stop):
+        self.offset = start
+        self.step = step
+        self.size = stop * step + start
+
+    def next(self, shapelen):
+        arr = instantiate(OneDimIterator)
+        arr.size = self.size
+        arr.step = self.step
+        arr.offset = self.offset + self.step
+        return arr
+
+    def done(self):
+        return self.offset == self.size
+
+def view_iter_from_arr(arr):
+    return ViewIterator(arr.start, arr.strides, arr.backstrides, arr.shape)
+
+class ViewIterator(BaseIterator):
+    def __init__(self, start, strides, backstrides, shape, res_shape=None):
+        self.offset  = start
+        self._done   = False
+        if res_shape is not None and res_shape != shape:
+            r = calculate_broadcast_strides(strides, backstrides,
+                                            shape, res_shape)
+            self.strides, self.backstrides = r
+            self.res_shape = res_shape
+        else:
+            self.strides = strides
+            self.backstrides = backstrides
+            self.res_shape = shape
+        self.indices = [0] * len(self.res_shape)
+
+    @jit.unroll_safe
+    def next(self, shapelen):
+        offset = self.offset
+        indices = [0] * shapelen
+        for i in range(shapelen):
+            indices[i] = self.indices[i]
+        done = False
+        for i in range(shapelen - 1, -1, -1):
+            if indices[i] < self.res_shape[i] - 1:
+                indices[i] += 1
+                offset += self.strides[i]
+                break
+            else:
+                indices[i] = 0
+                offset -= self.backstrides[i]
+        else:
+            done = True
+        res = instantiate(ViewIterator)
+        res.offset = offset
+        res.indices = indices
+        res.strides = self.strides
+        res.backstrides = self.backstrides
+        res.res_shape = self.res_shape
+        res._done = done
+        return res
+
+    def done(self):
+        return self._done
+
+class ConstantIterator(BaseIterator):
+    def next(self, shapelen):
+        return self
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -3,28 +3,33 @@
 from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
 from pypy.interpreter.typedef import TypeDef, GetSetProperty
 from pypy.module.micronumpy import interp_ufuncs, interp_dtype, signature
+from pypy.module.micronumpy.strides import calculate_slice_strides
 from pypy.rlib import jit
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.rlib.rstring import StringBuilder
-from pypy.rlib.objectmodel import instantiate
-
+from pypy.module.micronumpy.interp_iter import ArrayIterator,\
+     view_iter_from_arr, OneDimIterator
 
 numpy_driver = jit.JitDriver(
-    greens=['shapelen', 'signature'],
-    reds=['result_size', 'i', 'ri', 'self', 'result']
+    greens=['shapelen', 'sig'],
+    virtualizables=['frame'],
+    reds=['result_size', 'frame', 'ri', 'self', 'result']
 )
 all_driver = jit.JitDriver(
-    greens=['shapelen', 'signature'],
-    reds=['i', 'self', 'dtype']
+    greens=['shapelen', 'sig'],
+    virtualizables=['frame'],
+    reds=['frame', 'self', 'dtype']
 )
 any_driver = jit.JitDriver(
-    greens=['shapelen', 'signature'],
-    reds=['i', 'self', 'dtype']
+    greens=['shapelen', 'sig'],
+    virtualizables=['frame'],
+    reds=['frame', 'self', 'dtype']
 )
 slice_driver = jit.JitDriver(
-    greens=['shapelen', 'signature'],
-    reds=['self', 'source', 'source_iter', 'res_iter']
+    greens=['shapelen', 'sig'],
+    virtualizables=['frame'],
+    reds=['self', 'frame', 'source', 'res_iter']
 )
 
 def _find_shape_and_elems(space, w_iterable):
@@ -198,231 +203,17 @@
                 n_old_elems_to_use *= old_shape[oldI]
     return new_strides
 
-# Iterators for arrays
-# --------------------
-# all those iterators with the exception of BroadcastIterator iterate over the
-# entire array in C order (the last index changes the fastest). This will
-# yield all elements. Views iterate over indices and look towards strides and
-# backstrides to find the correct position. Notably the offset between
-# x[..., i + 1] and x[..., i] will be strides[-1]. Offset between
-# x[..., k + 1, 0] and x[..., k, i_max] will be backstrides[-2] etc.
+class BaseArray(Wrappable):
+    _attrs_ = ["invalidates", "shape", 'size']
 
-# BroadcastIterator works like that, but for indexes that don't change source
-# in the original array, strides[i] == backstrides[i] == 0
-
-class BaseIterator(object):
-    def next(self, shapelen):
-        raise NotImplementedError
-
-    def done(self):
-        raise NotImplementedError
-
-    def get_offset(self):
-        raise NotImplementedError
-
-class ArrayIterator(BaseIterator):
-    def __init__(self, size):
-        self.offset = 0
-        self.size = size
-
-    def next(self, shapelen):
-        arr = instantiate(ArrayIterator)
-        arr.size = self.size
-        arr.offset = self.offset + 1
-        return arr
-
-    def done(self):
-        return self.offset >= self.size
-
-    def get_offset(self):
-        return self.offset
-
-class OneDimIterator(BaseIterator):
-    def __init__(self, start, step, stop):
-        self.offset = start
-        self.step = step
-        self.size = stop * step + start
-
-    def next(self, shapelen):
-        arr = instantiate(OneDimIterator)
-        arr.size = self.size
-        arr.step = self.step
-        arr.offset = self.offset + self.step
-        return arr
-
-    def done(self):
-        return self.offset == self.size
-
-    def get_offset(self):
-        return self.offset
-
-class ViewIterator(BaseIterator):
-    def __init__(self, arr):
-        self.indices = [0] * len(arr.shape)
-        self.offset  = arr.start
-        self.arr     = arr
-        self._done   = False
-
-    @jit.unroll_safe
-    def next(self, shapelen):
-        offset = self.offset
-        indices = [0] * shapelen
-        for i in range(shapelen):
-            indices[i] = self.indices[i]
-        done = False
-        for i in range(shapelen - 1, -1, -1):
-            if indices[i] < self.arr.shape[i] - 1:
-                indices[i] += 1
-                offset += self.arr.strides[i]
-                break
-            else:
-                indices[i] = 0
-                offset -= self.arr.backstrides[i]
-        else:
-            done = True
-        res = instantiate(ViewIterator)
-        res.offset = offset
-        res.indices = indices
-        res.arr = self.arr
-        res._done = done
-        return res
-
-    def done(self):
-        return self._done
-
-    def get_offset(self):
-        return self.offset
-
-class BroadcastIterator(BaseIterator):
-    '''Like a view iterator, but will repeatedly access values
-       for all iterations across a res_shape, folding the offset
-       using mod() arithmetic
-    '''
-    def __init__(self, arr, res_shape):
-        self.indices = [0] * len(res_shape)
-        self.offset  = arr.start
-        #strides are 0 where original shape==1
-        self.strides = []
-        self.backstrides = []
-        for i in range(len(arr.shape)):
-            if arr.shape[i] == 1:
-                self.strides.append(0)
-                self.backstrides.append(0)
-            else:
-                self.strides.append(arr.strides[i])
-                self.backstrides.append(arr.backstrides[i])
-        self.res_shape = res_shape
-        self.strides = [0] * (len(res_shape) - len(arr.shape)) + self.strides
-        self.backstrides = [0] * (len(res_shape) - len(arr.shape)) + self.backstrides
-        self._done = False
-
-    @jit.unroll_safe
-    def next(self, shapelen):
-        offset = self.offset
-        indices = [0] * shapelen
-        _done = False
-        for i in range(shapelen):
-            indices[i] = self.indices[i]
-        for i in range(shapelen - 1, -1, -1):
-            if indices[i] < self.res_shape[i] - 1:
-                indices[i] += 1
-                offset += self.strides[i]
-                break
-            else:
-                indices[i] = 0
-                offset -= self.backstrides[i]
-        else:
-            _done = True
-        res = instantiate(BroadcastIterator)
-        res.indices = indices
-        res.offset = offset
-        res._done = _done
-        res.strides = self.strides
-        res.backstrides = self.backstrides
-        res.res_shape = self.res_shape
-        return res
-
-    def done(self):
-        return self._done
-
-    def get_offset(self):
-        return self.offset
-
-class Call2Iterator(BaseIterator):
-    def __init__(self, left, right):
-        self.left = left
-        self.right = right
-
-    def next(self, shapelen):
-        return Call2Iterator(self.left.next(shapelen),
-                             self.right.next(shapelen))
-
-    def done(self):
-        if isinstance(self.left, ConstantIterator):
-            return self.right.done()
-        return self.left.done()
-
-    def get_offset(self):
-        if isinstance(self.left, ConstantIterator):
-            return self.right.get_offset()
-        return self.left.get_offset()
-
-class Call1Iterator(BaseIterator):
-    def __init__(self, child):
-        self.child = child
-
-    def next(self, shapelen):
-        return Call1Iterator(self.child.next(shapelen))
-
-    def done(self):
-        return self.child.done()
-
-    def get_offset(self):
-        return self.child.get_offset()
-
-class ConstantIterator(BaseIterator):
-    def next(self, shapelen):
-        return self
-
-    def done(self):
-        return False
-
-    def get_offset(self):
-        return 0
-
-
-class BaseArray(Wrappable):
-    _attrs_ = ["invalidates", "signature", "shape", "strides", "backstrides",
-               "start", 'order']
-
-    _immutable_fields_ = ['start', "order"]
+    _immutable_fields_ = []
 
     strides = None
     start = 0
 
-    def __init__(self, shape, order):
+    def __init__(self, shape):
         self.invalidates = []
         self.shape = shape
-        self.order = order
-        if self.strides is None:
-            self.calc_strides(shape)
-
-    def calc_strides(self, shape):
-        strides = []
-        backstrides = []
-        s = 1
-        shape_rev = shape[:]
-        if self.order == 'C':
-            shape_rev.reverse()
-        for sh in shape_rev:
-            strides.append(s)
-            backstrides.append(s * (sh - 1))
-            s *= sh
-        if self.order == 'C':
-            strides.reverse()
-            backstrides.reverse()
-        self.strides = strides[:]
-        self.backstrides = backstrides[:]
 
     def invalidated(self):
         if self.invalidates:
@@ -499,33 +290,34 @@
 
     def _reduce_argmax_argmin_impl(op_name):
         reduce_driver = jit.JitDriver(
-            greens=['shapelen', 'signature'],
-            reds=['result', 'idx', 'i', 'self', 'cur_best', 'dtype']
+            greens=['shapelen', 'sig'],
+            reds=['result', 'idx', 'frame', 'self', 'cur_best', 'dtype']
         )
         def loop(self):
-            i = self.start_iter()
-            cur_best = self.eval(i)
+            sig = self.find_sig()
+            frame = sig.create_frame(self)
+            cur_best = sig.eval(frame, self)
             shapelen = len(self.shape)
-            i = i.next(shapelen)
+            frame.next(shapelen)
             dtype = self.find_dtype()
             result = 0
             idx = 1
-            while not i.done():
-                reduce_driver.jit_merge_point(signature=self.signature,
+            while not frame.done():
+                reduce_driver.jit_merge_point(sig=sig,
                                               shapelen=shapelen,
                                               self=self, dtype=dtype,
-                                              i=i, result=result, idx=idx,
+                                              frame=frame, result=result,
+                                              idx=idx,
                                               cur_best=cur_best)
-                new_best = getattr(dtype.itemtype, op_name)(cur_best, self.eval(i))
+                new_best = getattr(dtype.itemtype, op_name)(cur_best, sig.eval(frame, self))
                 if dtype.itemtype.ne(new_best, cur_best):
                     result = idx
                     cur_best = new_best
-                i = i.next(shapelen)
+                frame.next(shapelen)
                 idx += 1
             return result
         def impl(self, space):
-            size = self.find_size()
-            if size == 0:
+            if self.size == 0:
                 raise OperationError(space.w_ValueError,
                     space.wrap("Can't call %s on zero-size arrays" % op_name))
             return space.wrap(loop(self))
@@ -533,15 +325,16 @@
 
     def _all(self):
         dtype = self.find_dtype()
-        i = self.start_iter()
+        sig = self.find_sig()
+        frame = sig.create_frame(self)
         shapelen = len(self.shape)
-        while not i.done():
-            all_driver.jit_merge_point(signature=self.signature,
+        while not frame.done():
+            all_driver.jit_merge_point(sig=sig,
                                        shapelen=shapelen, self=self,
-                                       dtype=dtype, i=i)
-            if not dtype.itemtype.bool(self.eval(i)):
+                                       dtype=dtype, frame=frame)
+            if not dtype.itemtype.bool(sig.eval(frame, self)):
                 return False
-            i = i.next(shapelen)
+            frame.next(shapelen)
         return True
 
     def descr_all(self, space):
@@ -549,15 +342,16 @@
 
     def _any(self):
         dtype = self.find_dtype()
-        i = self.start_iter()
+        sig = self.find_sig()
+        frame = sig.create_frame(self)
         shapelen = len(self.shape)
-        while not i.done():
-            any_driver.jit_merge_point(signature=self.signature,
+        while not frame.done():
+            any_driver.jit_merge_point(sig=sig, frame=frame,
                                        shapelen=shapelen, self=self,
-                                       dtype=dtype, i=i)
-            if dtype.itemtype.bool(self.eval(i)):
+                                       dtype=dtype)
+            if dtype.itemtype.bool(sig.eval(frame, self)):
                 return True
-            i = i.next(shapelen)
+            frame.next(shapelen)
         return False
 
     def descr_any(self, space):
@@ -586,26 +380,33 @@
         return space.newtuple([space.wrap(i) for i in self.shape])
 
     def descr_set_shape(self, space, w_iterable):
-        concrete = self.get_concrete()
         new_shape = get_shape_from_iterable(space,
-                            concrete.find_size(), w_iterable)
-        concrete.setshape(space, new_shape)
+                            self.size, w_iterable)
+        if isinstance(self, Scalar):
+            return
+        self.get_concrete().setshape(space, new_shape)
 
     def descr_get_size(self, space):
-        return space.wrap(self.find_size())
+        return space.wrap(self.size)
 
     def descr_copy(self, space):
+        return self.copy()
+
+    def copy(self):
         return self.get_concrete().copy()
 
     def descr_len(self, space):
-        return self.get_concrete().descr_len(space)
+        if len(self.shape):
+            return space.wrap(self.shape[0])
+        raise OperationError(space.w_TypeError, space.wrap(
+            "len() of unsized object"))
 
     def descr_repr(self, space):
         res = StringBuilder()
         res.append("array(")
         concrete = self.get_concrete()
         dtype = concrete.find_dtype()
-        if not concrete.find_size():
+        if not concrete.size:
             res.append('[]')
             if len(self.shape) > 1:
                 # An empty slice reports its shape
@@ -617,18 +418,417 @@
             concrete.to_str(space, 1, res, indent='       ')
         if (dtype is not interp_dtype.get_dtype_cache(space).w_float64dtype and
             dtype is not interp_dtype.get_dtype_cache(space).w_int64dtype) or \
-            not self.find_size():
+            not self.size:
             res.append(", dtype=" + dtype.name)
         res.append(")")
         return space.wrap(res.build())
 
+    def descr_str(self, space):
+        ret = StringBuilder()
+        concrete = self.get_concrete_or_scalar()
+        concrete.to_str(space, 0, ret, ' ')
+        return space.wrap(ret.build())
+
+    @jit.unroll_safe
+    def _single_item_result(self, space, w_idx):
+        """ The result of getitem/setitem is a single item if w_idx
+        is a list of scalars that match the size of shape
+        """
+        shape_len = len(self.shape)
+        if shape_len == 0:
+            raise OperationError(space.w_IndexError, space.wrap(
+                "0-d arrays can't be indexed"))
+        if shape_len == 1:
+            if space.isinstance_w(w_idx, space.w_int):
+                return True
+            if space.isinstance_w(w_idx, space.w_slice):
+                return False
+        elif (space.isinstance_w(w_idx, space.w_slice) or
+              space.isinstance_w(w_idx, space.w_int)):
+            return False
+        lgt = space.len_w(w_idx)
+        if lgt > shape_len:
+            raise OperationError(space.w_IndexError,
+                                 space.wrap("invalid index"))
+        if lgt < shape_len:
+            return False
+        for w_item in space.fixedview(w_idx):
+            if space.isinstance_w(w_item, space.w_slice):
+                return False
+        return True
+
+    @jit.unroll_safe
+    def _prepare_slice_args(self, space, w_idx):
+        if (space.isinstance_w(w_idx, space.w_int) or
+            space.isinstance_w(w_idx, space.w_slice)):
+            return [space.decode_index4(w_idx, self.shape[0])]
+        return [space.decode_index4(w_item, self.shape[i]) for i, w_item in
+                enumerate(space.fixedview(w_idx))]
+
+    def descr_getitem(self, space, w_idx):
+        if self._single_item_result(space, w_idx):
+            concrete = self.get_concrete()
+            item = concrete._index_of_single_item(space, w_idx)
+            return concrete.getitem(item)
+        chunks = self._prepare_slice_args(space, w_idx)
+        return space.wrap(self.create_slice(chunks))
+
+    def descr_setitem(self, space, w_idx, w_value):
+        self.invalidated()
+        if self._single_item_result(space, w_idx):
+            concrete = self.get_concrete()
+            item = concrete._index_of_single_item(space, w_idx)
+            dtype = concrete.find_dtype()
+            concrete.setitem(item, dtype.coerce(space, w_value))
+            return
+        if not isinstance(w_value, BaseArray):
+            w_value = convert_to_array(space, w_value)
+        chunks = self._prepare_slice_args(space, w_idx)
+        view = self.create_slice(chunks).get_concrete()
+        view.setslice(space, w_value)
+
+    @jit.unroll_safe
+    def create_slice(self, chunks):
+        shape = []
+        i = -1
+        for i, (start_, stop, step, lgt) in enumerate(chunks):
+            if step != 0:
+                shape.append(lgt)
+        s = i + 1
+        assert s >= 0
+        shape += self.shape[s:]
+        if not isinstance(self, ConcreteArray):
+            return VirtualSlice(self, chunks, shape)
+        r = calculate_slice_strides(self.shape, self.start, self.strides,
+                                    self.backstrides, chunks)
+        _, start, strides, backstrides = r
+        return W_NDimSlice(start, strides[:], backstrides[:],
+                           shape[:], self)
+
+    def descr_reshape(self, space, args_w):
+        """reshape(...)
+        a.reshape(shape)
+
+        Returns an array containing the same data with a new shape.
+
+        Refer to `numpypy.reshape` for full documentation.
+
+        See Also
+        --------
+        numpypy.reshape : equivalent function
+        """
+        if len(args_w) == 1:
+            w_shape = args_w[0]
+        else:
+            w_shape = space.newtuple(args_w)
+        concrete = self.get_concrete()
+        new_shape = get_shape_from_iterable(space, concrete.size, w_shape)
+        # Since we got to here, prod(new_shape) == self.size
+        new_strides = calc_new_strides(new_shape,
+                                       concrete.shape, concrete.strides)
+        if new_strides:
+            # We can create a view, strides somehow match up.
+            ndims = len(new_shape)
+            new_backstrides = [0] * ndims
+            for nd in range(ndims):
+                new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd]
+            arr = W_NDimSlice(concrete.start, new_strides, new_backstrides,
+                              new_shape, self)
+        else:
+            # Create copy with contiguous data
+            arr = concrete.copy()
+            arr.setshape(space, new_shape)
+        return arr
+
+    def descr_tolist(self, space):
+        if len(self.shape) == 0:
+            assert isinstance(self, Scalar)
+            return self.value.descr_tolist(space)
+        w_result = space.newlist([])
+        for i in range(self.shape[0]):
+            space.call_method(w_result, "append",
+                space.call_method(self.descr_getitem(space, space.wrap(i)), "tolist")
+            )
+        return w_result
+
+    def descr_mean(self, space):
+        return space.div(self.descr_sum(space), space.wrap(self.size))
+
+    def descr_nonzero(self, space):
+        if self.size > 1:
+            raise OperationError(space.w_ValueError, space.wrap(
+                "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"))
+        concr = self.get_concrete_or_scalar()
+        sig = concr.find_sig()
+        frame = sig.create_frame(self)
+        return space.wrap(space.is_true(
+            sig.eval(frame, concr)))
+
+    def get_concrete_or_scalar(self):
+        return self.get_concrete()
+
+    def descr_get_transpose(self, space):
+        concrete = self.get_concrete()
+        if len(concrete.shape) < 2:
+            return space.wrap(self)
+        strides = []
+        backstrides = []
+        shape = []
+        for i in range(len(concrete.shape) - 1, -1, -1):
+            strides.append(concrete.strides[i])
+            backstrides.append(concrete.backstrides[i])
+            shape.append(concrete.shape[i])
+        return space.wrap(W_NDimSlice(concrete.start, strides[:],
+                                      backstrides[:], shape[:], concrete))
+
+    def descr_get_flatiter(self, space):
+        return space.wrap(W_FlatIterator(self))
+
+    def getitem(self, item):
+        raise NotImplementedError
+
+    def find_sig(self, res_shape=None):
+        """ find a correct signature for the array
+        """
+        res_shape = res_shape or self.shape
+        return signature.find_sig(self.create_sig(res_shape), self)
+
+    def descr_array_iface(self, space):
+        if not self.shape:
+            raise OperationError(space.w_TypeError,
+                space.wrap("can't get the array data of a 0-d array for now")
+            )
+        concrete = self.get_concrete()
+        storage = concrete.storage
+        addr = rffi.cast(lltype.Signed, storage)
+        w_d = space.newdict()
+        space.setitem_str(w_d, 'data', space.newtuple([space.wrap(addr),
+                                                       space.w_False]))
+        return w_d
+
+def convert_to_array(space, w_obj):
+    if isinstance(w_obj, BaseArray):
+        return w_obj
+    elif space.issequence_w(w_obj):
+        # Convert to array.
+        return array(space, w_obj, w_order=None)
+    else:
+        # If it's a scalar
+        dtype = interp_ufuncs.find_dtype_for_scalar(space, w_obj)
+        return scalar_w(space, dtype, w_obj)
+
+def scalar_w(space, dtype, w_obj):
+    return Scalar(dtype, dtype.coerce(space, w_obj))
+
+class Scalar(BaseArray):
+    """
+    Intermediate class representing a literal.
+    """
+    size = 1
+    _attrs_ = ["dtype", "value", "shape"]
+
+    def __init__(self, dtype, value):
+        self.shape = []
+        BaseArray.__init__(self, [])
+        self.dtype = dtype
+        self.value = value
+
+    def find_dtype(self):
+        return self.dtype
+
+    def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
+        builder.append(self.dtype.itemtype.str_format(self.value))
+
+    def copy(self):
+        return Scalar(self.dtype, self.value)
+
+    def create_sig(self, res_shape):
+        return signature.ScalarSignature(self.dtype)
+
+    def get_concrete_or_scalar(self):
+        return self
+
+
+class VirtualArray(BaseArray):
+    """
+    Class for representing virtual arrays, such as binary ops or ufuncs
+    """
+    def __init__(self, name, shape, res_dtype):
+        BaseArray.__init__(self, shape)
+        self.forced_result = None
+        self.res_dtype = res_dtype
+        self.name = name
+
+    def _del_sources(self):
+        # Function for deleting references to source arrays, to allow garbage-collecting them
+        raise NotImplementedError
+
+    def compute(self):
+        result = W_NDimArray(self.size, self.shape, self.find_dtype())
+        shapelen = len(self.shape)
+        sig = self.find_sig()
+        frame = sig.create_frame(self)
+        ri = ArrayIterator(self.size)
+        while not ri.done():
+            numpy_driver.jit_merge_point(sig=sig,
+                                         shapelen=shapelen,
+                                         result_size=self.size,
+                                         frame=frame,
+                                         ri=ri,
+                                         self=self, result=result)
+            result.dtype.setitem(result.storage, ri.offset,
+                                 sig.eval(frame, self))
+            frame.next(shapelen)
+            ri = ri.next(shapelen)
+        return result
+
+    def force_if_needed(self):
+        if self.forced_result is None:
+            self.forced_result = self.compute()
+            self._del_sources()
+
+    def get_concrete(self):
+        self.force_if_needed()
+        res = self.forced_result
+        assert isinstance(res, ConcreteArray)
+        return res
+
+    def getitem(self, item):
+        return self.get_concrete().getitem(item)
+
+    def setitem(self, item, value):
+        return self.get_concrete().setitem(item, value)
+
+    def find_dtype(self):
+        return self.res_dtype
+
+class VirtualSlice(VirtualArray):
+    def __init__(self, child, chunks, shape):
+        size = 1
+        for sh in shape:
+            size *= sh
+        self.child = child
+        self.chunks = chunks
+        self.size = size
+        VirtualArray.__init__(self, 'slice', shape, child.find_dtype())
+
+    def create_sig(self, res_shape):
+        if self.forced_result is not None:
+            return self.forced_result.create_sig(res_shape)
+        return signature.VirtualSliceSignature(
+            self.child.create_sig(res_shape))
+
+    def force_if_needed(self):
+        if self.forced_result is None:
+            concr = self.child.get_concrete()
+            self.forced_result = concr.create_slice(self.chunks)
+
+    def _del_sources(self):
+        self.child = None
+
+class Call1(VirtualArray):
+    def __init__(self, ufunc, name, shape, res_dtype, values):
+        VirtualArray.__init__(self, name, shape, res_dtype)
+        self.values = values
+        self.size = values.size
+        self.ufunc = ufunc
+
+    def _del_sources(self):
+        self.values = None
+
+    def create_sig(self, res_shape):
+        if self.forced_result is not None:
+            return self.forced_result.create_sig(res_shape)
+        return signature.Call1(self.ufunc, self.name,
+                               self.values.create_sig(res_shape))
+
+class Call2(VirtualArray):
+    """
+    Intermediate class for performing binary operations.
+    """
+    def __init__(self, ufunc, name, shape, calc_dtype, res_dtype, left, right):
+        VirtualArray.__init__(self, name, shape, res_dtype)
+        self.ufunc = ufunc
+        self.left = left
+        self.right = right
+        self.calc_dtype = calc_dtype
+        self.size = 1
+        for s in self.shape:
+            self.size *= s
+
+    def _del_sources(self):
+        self.left = None
+        self.right = None
+
+    def create_sig(self, res_shape):
+        if self.forced_result is not None:
+            return self.forced_result.create_sig(res_shape)
+        return signature.Call2(self.ufunc, self.name, self.calc_dtype,
+                               self.left.create_sig(res_shape),
+                               self.right.create_sig(res_shape))
+
+class ConcreteArray(BaseArray):
+    """ An array that have actual storage, whether owned or not
+    """
+    _immutable_fields_ = ['storage']
+
+    def __init__(self, size, shape, dtype, order='C', parent=None):
+        self.size = size
+        self.parent = parent
+        if parent is not None:
+            self.storage = parent.storage
+        else:
+            self.storage = dtype.malloc(size)
+        self.order = order
+        self.dtype = dtype
+        if self.strides is None:
+            self.calc_strides(shape)
+        BaseArray.__init__(self, shape)
+        if parent is not None:
+            self.invalidates = parent.invalidates
+
+    def get_concrete(self):
+        return self
+
+    def find_dtype(self):
+        return self.dtype
+
+    def getitem(self, item):
+        return self.dtype.getitem(self.storage, item)
+
+    def setitem(self, item, value):
+        self.invalidated()
+        self.dtype.setitem(self.storage, item, value)
+
+    def calc_strides(self, shape):
+        strides = []
+        backstrides = []
+        s = 1
+        shape_rev = shape[:]
+        if self.order == 'C':
+            shape_rev.reverse()
+        for sh in shape_rev:
+            strides.append(s)
+            backstrides.append(s * (sh - 1))
+            s *= sh
+        if self.order == 'C':
+            strides.reverse()
+            backstrides.reverse()
+        self.strides = strides[:]
+        self.backstrides = backstrides[:]
+
+    def array_sig(self, res_shape):
+        if res_shape is not None and self.shape != res_shape:
+            return signature.ViewSignature(self.dtype)
+        return signature.ArraySignature(self.dtype)
+
     def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
         '''Modifies builder with a representation of the array/slice
         The items will be seperated by a comma if comma is 1
         Multidimensional arrays/slices will span a number of lines,
         each line will begin with indent.
         '''
-        size = self.find_size()
+        size = self.size
         if size < 1:
             builder.append('[]')
             return
@@ -654,7 +854,7 @@
                             builder.append(indent)
                     # create_slice requires len(chunks) > 1 in order to reduce
                     # shape
-                    view = self.create_slice(space, [(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])])
+                    view = self.create_slice([(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])]).get_concrete()
                     view.to_str(space, comma, builder, indent=indent + ' ', use_ellipsis=use_ellipsis)
                 builder.append('\n' + indent + '..., ')
                 i = self.shape[0] - 3
@@ -669,7 +869,7 @@
                         builder.append(indent)
                 # create_slice requires len(chunks) > 1 in order to reduce
                 # shape
-                view = self.create_slice(space, [(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])])
+                view = self.create_slice([(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])]).get_concrete()
                 view.to_str(space, comma, builder, indent=indent + ' ', use_ellipsis=use_ellipsis)
                 i += 1
         elif ndims == 1:
@@ -705,12 +905,6 @@
             builder.append('[')
         builder.append(']')
 
-    def descr_str(self, space):
-        ret = StringBuilder()
-        concrete = self.get_concrete()
-        concrete.to_str(space, 0, ret, ' ')
-        return space.wrap(ret.build())
-
     @jit.unroll_safe
     def _index_of_single_item(self, space, w_idx):
         if space.isinstance_w(w_idx, space.w_int):
@@ -735,456 +929,55 @@
             item += v * self.strides[i]
         return item
 
-    @jit.unroll_safe
-    def _single_item_result(self, space, w_idx):
-        """ The result of getitem/setitem is a single item if w_idx
-        is a list of scalars that match the size of shape
-        """
-        shape_len = len(self.shape)
-        if shape_len == 0:
-            if not space.isinstance_w(w_idx, space.w_int):
-                raise OperationError(space.w_IndexError, space.wrap(
-                    "wrong index"))
-            return True
-        if shape_len == 1:
-            if space.isinstance_w(w_idx, space.w_int):
-                return True
-            if space.isinstance_w(w_idx, space.w_slice):
-                return False
-        elif (space.isinstance_w(w_idx, space.w_slice) or
-              space.isinstance_w(w_idx, space.w_int)):
-            return False
-        lgt = space.len_w(w_idx)
-        if lgt > shape_len:
-            raise OperationError(space.w_IndexError,
-                                 space.wrap("invalid index"))
-        if lgt < shape_len:
-            return False
-        for w_item in space.fixedview(w_idx):
-            if space.isinstance_w(w_item, space.w_slice):
-                return False
-        return True
 
-    @jit.unroll_safe
-    def _prepare_slice_args(self, space, w_idx):
-        if (space.isinstance_w(w_idx, space.w_int) or
-            space.isinstance_w(w_idx, space.w_slice)):
-            return [space.decode_index4(w_idx, self.shape[0])]
-        return [space.decode_index4(w_item, self.shape[i]) for i, w_item in
-                enumerate(space.fixedview(w_idx))]
+class ViewArray(ConcreteArray):
+    def copy(self):
+        array = W_NDimArray(self.size, self.shape[:], self.find_dtype())
+        iter = view_iter_from_arr(self)
+        a_iter = ArrayIterator(array.size)
+        while not iter.done():
+            array.setitem(a_iter.offset, self.getitem(iter.offset))
+            iter = iter.next(len(self.shape))
+            a_iter = a_iter.next(len(array.shape))
+        return array
 
-    def descr_getitem(self, space, w_idx):
-        if self._single_item_result(space, w_idx):
-            concrete = self.get_concrete()
-            if len(concrete.shape) < 1:
-                raise OperationError(space.w_IndexError, space.wrap(
-                        "0-d arrays can't be indexed"))
-            item = concrete._index_of_single_item(space, w_idx)
-            return concrete.getitem(item)
-        chunks = self._prepare_slice_args(space, w_idx)
-        return space.wrap(self.create_slice(space, chunks))
+    def create_sig(self, res_shape):
+        return signature.ViewSignature(self.dtype)
 
-    def descr_setitem(self, space, w_idx, w_value):
-        self.invalidated()
-        if self._single_item_result(space, w_idx):
-            concrete = self.get_concrete()
-            if len(concrete.shape) < 1:
-                raise OperationError(space.w_IndexError, space.wrap(
-                        "0-d arrays can't be indexed"))
-            item = concrete._index_of_single_item(space, w_idx)
-            dtype = concrete.find_dtype()
-            concrete.setitem(item, dtype.coerce(space, w_value))
-            return
-        if not isinstance(w_value, BaseArray):
-            w_value = convert_to_array(space, w_value)
-        chunks = self._prepare_slice_args(space, w_idx)
-        view = self.create_slice(space, chunks)
-        view.setslice(space, w_value)
 
-    @jit.unroll_safe
-    def create_slice(self, space, chunks):
-        if len(chunks) == 1:
-            start, stop, step, lgt = chunks[0]
-            if step == 0:
-                shape = self.shape[1:]
-                strides = self.strides[1:]
-                backstrides = self.backstrides[1:]
-            else:
-                shape = [lgt] + self.shape[1:]
-                strides = [self.strides[0] * step] + self.strides[1:]
-                backstrides = [(lgt - 1) * self.strides[0] * step] + self.backstrides[1:]
-            start *= self.strides[0]
-            start += self.start
-        else:
-            shape = []
-            strides = []
-            backstrides = []
-            start = self.start
-            i = -1
-            for i, (start_, stop, step, lgt) in enumerate(chunks):
-                if step != 0:
-                    shape.append(lgt)
-                    strides.append(self.strides[i] * step)
-                    backstrides.append(self.strides[i] * (lgt - 1) * step)
-                start += self.strides[i] * start_
-            # add a reminder
-            s = i + 1
-            assert s >= 0
-            shape += self.shape[s:]
-            strides += self.strides[s:]
-            backstrides += self.backstrides[s:]
-        new_sig = signature.Signature.find_sig([
-            W_NDimSlice.signature, self.signature,
-        ])
-        return W_NDimSlice(self, new_sig, start, strides[:], backstrides[:],
-                           shape[:])
-
-    def descr_reshape(self, space, args_w):
-        """reshape(...)
-    a.reshape(shape)
-
-    Returns an array containing the same data with a new shape.
-
-    Refer to `numpypy.reshape` for full documentation.
-
-    See Also
-    --------
-    numpypy.reshape : equivalent function
-"""
-        if len(args_w) == 1:
-            w_shape = args_w[0]
-        else:
-            w_shape = space.newtuple(args_w)
-        concrete = self.get_concrete()
-        new_shape = get_shape_from_iterable(space,
-                                            concrete.find_size(), w_shape)
-        # Since we got to here, prod(new_shape) == self.size
-        new_strides = calc_new_strides(new_shape,
-                                       concrete.shape, concrete.strides)
-        if new_strides:
-            # We can create a view, strides somehow match up.
-            new_sig = signature.Signature.find_sig([
-                W_NDimSlice.signature, self.signature
-            ])
-            ndims = len(new_shape)
-            new_backstrides = [0] * ndims
-            for nd in range(ndims):
-                new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd]
-            arr = W_NDimSlice(self, new_sig, self.start, new_strides,
-                              new_backstrides, new_shape)
-        else:
-            # Create copy with contiguous data
-            arr = concrete.copy()
-            arr.setshape(space, new_shape)
-        return arr
-
-    def descr_tolist(self, space):
-        if len(self.shape) == 0:
-            assert isinstance(self, Scalar)
-            return self.value.descr_tolist(space)
-        w_result = space.newlist([])
-        for i in range(self.shape[0]):
-            space.call_method(w_result, "append",
-                space.call_method(self.descr_getitem(space, space.wrap(i)), "tolist")
-            )
-        return w_result
-
-    def descr_mean(self, space):
-        return space.div(self.descr_sum(space), space.wrap(self.find_size()))
-
-    def descr_nonzero(self, space):
-        if self.find_size() > 1:
-            raise OperationError(space.w_ValueError, space.wrap(
-                "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"))
-        return space.wrap(space.is_true(
-            self.get_concrete().eval(self.start_iter(self.shape))
-        ))
-
-    def descr_get_transpose(self, space):
-        concrete = self.get_concrete()
-        if len(concrete.shape) < 2:
-            return space.wrap(self)
-        new_sig = signature.Signature.find_sig([
-            W_NDimSlice.signature, self.signature
-        ])
-        strides = []
-        backstrides = []
-        shape = []
-        for i in range(len(concrete.shape) - 1, -1, -1):
-            strides.append(concrete.strides[i])
-            backstrides.append(concrete.backstrides[i])
-            shape.append(concrete.shape[i])
-        return space.wrap(W_NDimSlice(concrete, new_sig, self.start, strides[:],
-                                      backstrides[:], shape[:]))
-
-    def descr_get_flatiter(self, space):
-        return space.wrap(W_FlatIterator(self))
-
-    def getitem(self, item):
-        raise NotImplementedError
-
-    def start_iter(self, res_shape=None):
-        raise NotImplementedError
-
-    def descr_array_iface(self, space):
-        concrete = self.get_concrete()
-        storage = concrete.get_storage(space)
-        addr = rffi.cast(lltype.Signed, storage)
-        w_d = space.newdict()
-        space.setitem_str(w_d, 'data', space.newtuple([space.wrap(addr),
-                                                       space.w_False]))
-        return w_d
-
-def convert_to_array(space, w_obj):
-    if isinstance(w_obj, BaseArray):
-        return w_obj
-    elif space.issequence_w(w_obj):
-        # Convert to array.
-        return array(space, w_obj, w_order=None)
-    else:
-        # If it's a scalar
-        dtype = interp_ufuncs.find_dtype_for_scalar(space, w_obj)
-        return scalar_w(space, dtype, w_obj)
-
-def scalar_w(space, dtype, w_obj):
-    return Scalar(dtype, dtype.coerce(space, w_obj))
-
-class Scalar(BaseArray):
-    """
-    Intermediate class representing a literal.
-    """
-    signature = signature.BaseSignature()
-
-    _attrs_ = ["dtype", "value", "shape"]
-
-    def __init__(self, dtype, value):
-        self.shape = self.strides = []
-        BaseArray.__init__(self, [], 'C')
-        self.dtype = dtype
-        self.value = value
-
-    def find_size(self):
-        return 1
-
-    def get_concrete(self):
-        return self
-
-    def find_dtype(self):
-        return self.dtype
-
-    def getitem(self, item):
-        raise NotImplementedError
-
-    def eval(self, iter):
-        return self.value
-
-    def start_iter(self, res_shape=None):
-        return ConstantIterator()
-
-    def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
-        builder.append(self.dtype.itemtype.str_format(self.value))
-
-    def copy(self):
-        return Scalar(self.dtype, self.value)
-
-    def debug_repr(self):
-        return 'Scalar'
-
-    def setshape(self, space, new_shape):
-        # In order to get here, we already checked that prod(new_shape) == 1,
-        # so in order to have a consistent API, let it go through.
-        pass
-
-    def get_storage(self, space):
-        raise OperationError(space.w_TypeError, space.wrap("Cannot get array interface on scalars in pypy"))
-
-class VirtualArray(BaseArray):
-    """
-    Class for representing virtual arrays, such as binary ops or ufuncs
-    """
-    def __init__(self, signature, shape, res_dtype, order):
-        BaseArray.__init__(self, shape, order)
-        self.forced_result = None
-        self.signature = signature
-        self.res_dtype = res_dtype
-
-    def _del_sources(self):
-        # Function for deleting references to source arrays, to allow garbage-collecting them
-        raise NotImplementedError
-
-    def compute(self):
-        i = 0
-        signature = self.signature
-        result_size = self.find_size()
-        result = W_NDimArray(result_size, self.shape, self.find_dtype())
-        shapelen = len(self.shape)
-        i = self.start_iter()
-        ri = result.start_iter()
-        while not ri.done():
-            numpy_driver.jit_merge_point(signature=signature,
-                                         shapelen=shapelen,
-                                         result_size=result_size, i=i, ri=ri,
-                                         self=self, result=result)
-            result.dtype.setitem(result.storage, ri.offset, self.eval(i))
-            i = i.next(shapelen)
-            ri = ri.next(shapelen)
-        return result
-
-    def force_if_needed(self):
-        if self.forced_result is None:
-            self.forced_result = self.compute()
-            self._del_sources()
-
-    def get_concrete(self):
-        self.force_if_needed()
-        return self.forced_result
-
-    def eval(self, iter):
-        if self.forced_result is not None:
-            return self.forced_result.eval(iter)
-        return self._eval(iter)
-
-    def getitem(self, item):
-        return self.get_concrete().getitem(item)
-
-    def setitem(self, item, value):
-        return self.get_concrete().setitem(item, value)
-
-    def find_size(self):
-        if self.forced_result is not None:
-            # The result has been computed and sources may be unavailable
-            return self.forced_result.find_size()
-        return self._find_size()
-
-    def find_dtype(self):
-        return self.res_dtype
-
-
-class Call1(VirtualArray):
-    def __init__(self, signature, shape, res_dtype, values, order):
-        VirtualArray.__init__(self, signature, shape, res_dtype,
-                              values.order)
-        self.values = values
-
-    def _del_sources(self):
-        self.values = None
-
-    def _find_size(self):
-        return self.values.find_size()
-
-    def _find_dtype(self):
-        return self.res_dtype
-
-    def _eval(self, iter):
-        assert isinstance(iter, Call1Iterator)
-        val = self.values.eval(iter.child).convert_to(self.res_dtype)
-        sig = jit.promote(self.signature)
-        assert isinstance(sig, signature.Signature)
-        call_sig = sig.components[0]
-        assert isinstance(call_sig, signature.Call1)
-        return call_sig.func(self.res_dtype, val)
-
-    def start_iter(self, res_shape=None):
-        if self.forced_result is not None:
-            return self.forced_result.start_iter(res_shape)
-        return Call1Iterator(self.values.start_iter(res_shape))
-
-    def debug_repr(self):
-        sig = self.signature
-        assert isinstance(sig, signature.Signature)
-        call_sig = sig.components[0]
-        assert isinstance(call_sig, signature.Call1)
-        if self.forced_result is not None:
-            return 'Call1(%s, forced=%s)' % (call_sig.name,
-                                             self.forced_result.debug_repr())
-        return 'Call1(%s, %s)' % (call_sig.name,
-                                  self.values.debug_repr())
-
-class Call2(VirtualArray):
-    """
-    Intermediate class for performing binary operations.
-    """
-    def __init__(self, signature, shape, calc_dtype, res_dtype, left, right):
-        # XXX do something if left.order != right.order
-        VirtualArray.__init__(self, signature, shape, res_dtype, left.order)
-        self.left = left
-        self.right = right
-        self.calc_dtype = calc_dtype
-        self.size = 1
-        for s in self.shape:
-            self.size *= s
-
-    def _del_sources(self):
-        self.left = None
-        self.right = None
-
-    def _find_size(self):
-        return self.size
-
-    def start_iter(self, res_shape=None):
-        if self.forced_result is not None:
-            return self.forced_result.start_iter(res_shape)
-        if res_shape is None:
-            res_shape = self.shape  # we still force the shape on children
-        return Call2Iterator(self.left.start_iter(res_shape),
-                             self.right.start_iter(res_shape))
-
-    def _eval(self, iter):
-        assert isinstance(iter, Call2Iterator)
-        lhs = self.left.eval(iter.left).convert_to(self.calc_dtype)
-        rhs = self.right.eval(iter.right).convert_to(self.calc_dtype)
-        sig = jit.promote(self.signature)
-        assert isinstance(sig, signature.Signature)
-        call_sig = sig.components[0]
-        assert isinstance(call_sig, signature.Call2)
-        return call_sig.func(self.calc_dtype, lhs, rhs)
-
-    def debug_repr(self):
-        sig = self.signature
-        assert isinstance(sig, signature.Signature)
-        call_sig = sig.components[0]
-        assert isinstance(call_sig, signature.Call2)
-        if self.forced_result is not None:
-            return 'Call2(%s, forced=%s)' % (call_sig.name,
-                self.forced_result.debug_repr())
-        return 'Call2(%s, %s, %s)' % (call_sig.name,
-            self.left.debug_repr(),
-            self.right.debug_repr())
-
-class ViewArray(BaseArray):
-    """
-    Class for representing views of arrays, they will reflect changes of parent
-    arrays. Example: slices
-    """
-    def __init__(self, parent, signature, strides, backstrides, shape):
+class W_NDimSlice(ViewArray):
+    def __init__(self, start, strides, backstrides, shape, parent):
+        assert isinstance(parent, ConcreteArray)
+        if isinstance(parent, W_NDimSlice):
+            parent = parent.parent
+        size = 1
+        for sh in shape:
+            size *= sh
         self.strides = strides
         self.backstrides = backstrides
-        BaseArray.__init__(self, shape, parent.order)
-        self.signature = signature
-        self.parent = parent
-        self.invalidates = parent.invalidates
+        ViewArray.__init__(self, size, shape, parent.dtype, parent.order,
+                               parent)
+        self.start = start
 
-    def get_concrete(self):
-        # in fact, ViewArray never gets "concrete" as it never stores data.
-        # This implementation is needed for BaseArray getitem/setitem to work,
-        # can be refactored.
-        self.parent.get_concrete()
-        return self
+    def setslice(self, space, w_value):
+        res_shape = shape_agreement(space, self.shape, w_value.shape)
+        self._sliceloop(w_value, res_shape)
 
-    def getitem(self, item):
-        return self.parent.getitem(item)
-
-    def eval(self, iter):
-        return self.parent.getitem(iter.get_offset())
-
-    def setitem(self, item, value):
-        # This is currently not possible to be called from anywhere.
-        raise NotImplementedError
-
-    def descr_len(self, space):
-        if self.shape:
-            return space.wrap(self.shape[0])
-        return space.wrap(1)
+    def _sliceloop(self, source, res_shape):
+        sig = source.find_sig(res_shape)
+        frame = sig.create_frame(source, res_shape)
+        res_iter = view_iter_from_arr(self)
+        shapelen = len(res_shape)
+        while not res_iter.done():
+            slice_driver.jit_merge_point(sig=sig,
+                                         frame=frame,
+                                         shapelen=shapelen,
+                                         self=self, source=source,
+                                         res_iter=res_iter)
+            self.setitem(res_iter.offset, sig.eval(frame, source).convert_to(
+                self.find_dtype()))
+            frame.next(shapelen)
+            res_iter = res_iter.next(shapelen)
 
     def setshape(self, space, new_shape):
         if len(self.shape) < 1:
@@ -1220,96 +1013,10 @@
         self.backstrides = new_backstrides[:]
         self.shape = new_shape[:]
 
-class W_NDimSlice(ViewArray):
-    signature = signature.BaseSignature()
-
-    def __init__(self, parent, signature, start, strides, backstrides,
-                 shape):
-        if isinstance(parent, W_NDimSlice):
-            parent = parent.parent
-        ViewArray.__init__(self, parent, signature, strides, backstrides, shape)
-        self.start = start
-        self.size = 1
-        for sh in shape:
-            self.size *= sh
-
-    def find_size(self):
-        return self.size
-
-    def find_dtype(self):
-        return self.parent.find_dtype()
-
-    def setslice(self, space, w_value):
-        res_shape = shape_agreement(space, self.shape, w_value.shape)
-        self._sliceloop(w_value, res_shape)
-
-    def _sliceloop(self, source, res_shape):
-        source_iter = source.start_iter(res_shape)
-        res_iter = self.start_iter(res_shape)
-        shapelen = len(res_shape)
-        while not res_iter.done():
-            slice_driver.jit_merge_point(signature=source.signature,
-                                         shapelen=shapelen,
-                                         self=self, source=source,
-                                         res_iter=res_iter,
-                                         source_iter=source_iter)
-            self.setitem(res_iter.offset, source.eval(source_iter).convert_to(
-                self.find_dtype()))
-            source_iter = source_iter.next(shapelen)
-            res_iter = res_iter.next(shapelen)
-
-    def start_iter(self, res_shape=None):
-        if res_shape is not None and res_shape != self.shape:
-            return BroadcastIterator(self, res_shape)
-        if len(self.shape) == 1:
-            return OneDimIterator(self.start, self.strides[0], self.shape[0])
-        return ViewIterator(self)
-
-    def setitem(self, item, value):
-        self.parent.setitem(item, value)
-
-    def debug_repr(self):
-        return 'Slice(%s)' % self.parent.debug_repr()
-
-    def copy(self):
-        array = W_NDimArray(self.size, self.shape[:], self.find_dtype())
-        iter = self.start_iter()
-        a_iter = array.start_iter()
-        while not iter.done():
-            array.setitem(a_iter.offset, self.getitem(iter.offset))
-            iter = iter.next(len(self.shape))
-            a_iter = a_iter.next(len(array.shape))
-        return array
-
-    def get_storage(self, space):
-        return self.parent.get_storage(space)
-
-class W_NDimArray(BaseArray):
+class W_NDimArray(ConcreteArray):
     """ A class representing contiguous array. We know that each iteration
     by say ufunc will increase the data index by one
     """
-    def __init__(self, size, shape, dtype, order='C'):
-        BaseArray.__init__(self, shape, order)
-        self.size = size
-        self.dtype = dtype
-        self.storage = dtype.malloc(size)
-        self.signature = dtype.signature
-
-    def get_concrete(self):
-        return self
-
-    def find_size(self):
-        return self.size
-
-    def find_dtype(self):
-        return self.dtype
-
-    def getitem(self, item):
-        return self.dtype.getitem(self.storage, item)
-
-    def eval(self, iter):
-        return self.dtype.getitem(self.storage, iter.get_offset())
-
     def copy(self):
         array = W_NDimArray(self.size, self.shape[:], self.dtype, self.order)
         rffi.c_memcpy(
@@ -1319,32 +1026,16 @@
         )
         return array
 
-    def descr_len(self, space):
-        if len(self.shape):
-            return space.wrap(self.shape[0])
-        raise OperationError(space.w_TypeError, space.wrap(
-            "len() of unsized object"))
-
     def setitem(self, item, value):
         self.invalidated()
         self.dtype.setitem(self.storage, item, value)
 
-    def start_iter(self, res_shape=None):
-        if self.order == 'C':
-            if res_shape is not None and res_shape != self.shape:
-                return BroadcastIterator(self, res_shape)
-            return ArrayIterator(self.size)
-        raise NotImplementedError  # use ViewIterator simply, test it
-
     def setshape(self, space, new_shape):
         self.shape = new_shape
         self.calc_strides(new_shape)
 
-    def debug_repr(self):
-        return 'Array'
-
-    def get_storage(self, space):
-        return self.storage
+    def create_sig(self, res_shape):
+        return self.array_sig(res_shape)
 
     def __del__(self):
         lltype.free(self.storage, flavor='raw', track_allocation=False)
@@ -1396,10 +1087,11 @@
     )
     arr = W_NDimArray(size, shape[:], dtype=dtype, order=order)
     shapelen = len(shape)
-    arr_iter = arr.start_iter(arr.shape)
+    arr_iter = ArrayIterator(arr.size)
     for i in range(len(elems_w)):
         w_elem = elems_w[i]
-        dtype.setitem(arr.storage, arr_iter.offset, dtype.coerce(space, w_elem))
+        dtype.setitem(arr.storage, arr_iter.offset,
+                      dtype.coerce(space, w_elem))
         arr_iter = arr_iter.next(shapelen)
     return arr
 
@@ -1492,48 +1184,31 @@
 
 
 class W_FlatIterator(ViewArray):
-    signature = signature.BaseSignature()
 
     @jit.unroll_safe
     def __init__(self, arr):
+        arr = arr.get_concrete()
         size = 1
         for sh in arr.shape:
             size *= sh
-        new_sig = signature.Signature.find_sig([
-            W_FlatIterator.signature, arr.signature
-        ])
-        ViewArray.__init__(self, arr, new_sig, [arr.strides[-1]],
-                           [arr.backstrides[-1]], [size])
+        self.strides = [arr.strides[-1]]
+        self.backstrides = [arr.backstrides[-1]]
+        ViewArray.__init__(self, size, [size], arr.dtype, arr.order,
+                               arr)
         self.shapelen = len(arr.shape)
-        self.arr = arr
-        self.iter = self.start_iter()
-
-    def start_iter(self, res_shape=None):
-        if res_shape is not None and res_shape != self.shape:
-            return BroadcastIterator(self, res_shape)
-        return OneDimIterator(self.arr.start, self.strides[0],
-                              self.shape[0])
-
-    def find_dtype(self):
-        return self.arr.find_dtype()
-
-    def find_size(self):
-        return self.shape[0]
+        self.iter = OneDimIterator(arr.start, self.strides[0],
+                                   self.shape[0])
 
     def descr_next(self, space):
         if self.iter.done():
             raise OperationError(space.w_StopIteration, space.w_None)
-        result = self.eval(self.iter)
+        result = self.getitem(self.iter.offset)
         self.iter = self.iter.next(self.shapelen)
         return result
 
     def descr_iter(self):
         return self
 
-    def debug_repr(self):
-        return 'FlatIter(%s)' % self.arr.debug_repr()
-
-
 W_FlatIterator.typedef = TypeDef(
     'flatiter',
     next = interp2app(W_FlatIterator.descr_next),
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -2,20 +2,21 @@
 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
-from pypy.module.micronumpy import interp_boxes, interp_dtype, signature, types
+from pypy.module.micronumpy import interp_boxes, interp_dtype, types
+from pypy.module.micronumpy.signature import ReduceSignature, ScalarSignature, find_sig
 from pypy.rlib import jit
 from pypy.rlib.rarithmetic import LONG_BIT
 from pypy.tool.sourcetools import func_with_new_name
 
-
 reduce_driver = jit.JitDriver(
-    greens = ['shapelen', "signature"],
-    reds = ["i", "self", "dtype", "value", "obj"]
+    greens = ['shapelen', "sig"],
+    virtualizables = ["frame"],
+    reds = ["frame", "self", "dtype", "value", "obj"]
 )
 
 class W_Ufunc(Wrappable):
     _attrs_ = ["name", "promote_to_float", "promote_bools", "identity"]
-    _immutable_fields_ = ["promote_to_float", "promote_bools"]
+    _immutable_fields_ = ["promote_to_float", "promote_bools", "name"]
 
     def __init__(self, name, promote_to_float, promote_bools, identity):
         self.name = name
@@ -50,6 +51,7 @@
 
     def reduce(self, space, w_obj, multidim):
         from pypy.module.micronumpy.interp_numarray import convert_to_array, Scalar
+        
         if self.argcount != 2:
             raise OperationError(space.w_ValueError, space.wrap("reduce only "
                 "supported for binary functions"))
@@ -60,13 +62,16 @@
             raise OperationError(space.w_TypeError, space.wrap("cannot reduce "
                 "on a scalar"))
 
-        size = obj.find_size()
+        size = obj.size
         dtype = find_unaryop_result_dtype(
             space, obj.find_dtype(),
             promote_to_largest=True
         )
-        start = obj.start_iter(obj.shape)
         shapelen = len(obj.shape)
+        sig = find_sig(ReduceSignature(self.func, self.name, dtype,
+                                       ScalarSignature(dtype),
+                                       obj.create_sig(obj.shape)), obj)
+        frame = sig.create_frame(obj)
         if shapelen > 1 and not multidim:
             raise OperationError(space.w_NotImplementedError,
                 space.wrap("not implemented yet"))
@@ -74,34 +79,33 @@
             if size == 0:
                 raise operationerrfmt(space.w_ValueError, "zero-size array to "
                     "%s.reduce without identity", self.name)
-            value = obj.eval(start).convert_to(dtype)
-            start = start.next(shapelen)
+            value = sig.eval(frame, obj).convert_to(dtype)
+            frame.next(shapelen)
         else:
             value = self.identity.convert_to(dtype)
-        new_sig = signature.Signature.find_sig([
-            self.reduce_signature, obj.signature
-        ])
-        return self.reduce_loop(new_sig, shapelen, start, value, obj, dtype)
+        return self.reduce_loop(shapelen, sig, frame, value, obj, dtype)
 
-    def reduce_loop(self, signature, shapelen, i, value, obj, dtype):
-        while not i.done():
-            reduce_driver.jit_merge_point(signature=signature,
+    def reduce_loop(self, shapelen, sig, frame, value, obj, dtype):
+        while not frame.done():
+            reduce_driver.jit_merge_point(sig=sig,
                                           shapelen=shapelen, self=self,
-                                          value=value, obj=obj, i=i,
+                                          value=value, obj=obj, frame=frame,
                                           dtype=dtype)
-            value = self.func(dtype, value, obj.eval(i).convert_to(dtype))
-            i = i.next(shapelen)
+            assert isinstance(sig, ReduceSignature)
+            value = sig.binfunc(dtype, value, sig.eval(frame, obj).convert_to(dtype))
+            frame.next(shapelen)
         return value
 
 class W_Ufunc1(W_Ufunc):
     argcount = 1
 
+    _immutable_fields_ = ["func", "name"]
+
     def __init__(self, func, name, promote_to_float=False, promote_bools=False,
         identity=None):
 
         W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity)
         self.func = func
-        self.signature = signature.Call1(func)
 
     def call(self, space, args_w):
         from pypy.module.micronumpy.interp_numarray import (Call1,
@@ -117,14 +121,13 @@
         if isinstance(w_obj, Scalar):
             return self.func(res_dtype, w_obj.value.convert_to(res_dtype))
 
-        new_sig = signature.Signature.find_sig([self.signature, w_obj.signature])
-        w_res = Call1(new_sig, w_obj.shape, res_dtype, w_obj, w_obj.order)
+        w_res = Call1(self.func, self.name, w_obj.shape, res_dtype, w_obj)
         w_obj.add_invalidates(w_res)
         return w_res
 
 
 class W_Ufunc2(W_Ufunc):
-    _immutable_fields_ = ["comparison_func", "func"]
+    _immutable_fields_ = ["comparison_func", "func", "name"]
     argcount = 2
 
     def __init__(self, func, name, promote_to_float=False, promote_bools=False,
@@ -133,8 +136,6 @@
         W_Ufunc.__init__(self, name, promote_to_float, promote_bools, identity)
         self.func = func
         self.comparison_func = comparison_func
-        self.signature = signature.Call2(func)
-        self.reduce_signature = signature.BaseSignature()
 
     def call(self, space, args_w):
         from pypy.module.micronumpy.interp_numarray import (Call2,
@@ -158,11 +159,9 @@
                 w_rhs.value.convert_to(calc_dtype)
             )
 
-        new_sig = signature.Signature.find_sig([
-            self.signature, w_lhs.signature, w_rhs.signature
-        ])
         new_shape = shape_agreement(space, w_lhs.shape, w_rhs.shape)
-        w_res = Call2(new_sig, new_shape, calc_dtype,
+        w_res = Call2(self.func, self.name,
+                      new_shape, calc_dtype,
                       res_dtype, w_lhs, w_rhs)
         w_lhs.add_invalidates(w_res)
         w_rhs.add_invalidates(w_res)
diff --git a/pypy/module/micronumpy/signature.py b/pypy/module/micronumpy/signature.py
--- a/pypy/module/micronumpy/signature.py
+++ b/pypy/module/micronumpy/signature.py
@@ -1,54 +1,322 @@
-from pypy.rlib.objectmodel import r_dict, compute_identity_hash
+from pypy.rlib.objectmodel import r_dict, compute_identity_hash, compute_hash
 from pypy.rlib.rarithmetic import intmask
+from pypy.module.micronumpy.interp_iter import ViewIterator, ArrayIterator, \
+     OneDimIterator, ConstantIterator
+from pypy.module.micronumpy.strides import calculate_slice_strides
+from pypy.rlib.jit import hint, unroll_safe, promote
 
+def sigeq(one, two):
+    return one.eq(two)
 
-def components_eq(lhs, rhs):
-    if len(lhs) != len(rhs):
-        return False
-    for i in range(len(lhs)):
-        v1, v2 = lhs[i], rhs[i]
-        if type(v1) is not type(v2) or not v1.eq(v2):
+def sigeq_no_numbering(one, two):
+    """ Cache for iterator numbering should not compare array numbers
+    """
+    return one.eq(two, compare_array_no=False)
+
+def sighash(sig):
+    return sig.hash()
+
+known_sigs = r_dict(sigeq, sighash)
+
+def find_sig(sig, arr):
+    sig.invent_array_numbering(arr)
+    try:
+        return known_sigs[sig]
+    except KeyError:
+        sig.invent_numbering()
+        known_sigs[sig] = sig
+        return sig
+
+class NumpyEvalFrame(object):
+    _virtualizable2_ = ['iterators[*]', 'final_iter', 'arraylist[*]']
+
+    @unroll_safe
+    def __init__(self, iterators, arrays):
+        self = hint(self, access_directly=True, fresh_virtualizable=True)
+        self.iterators = iterators[:]
+        self.arrays = arrays[:]
+        for i in range(len(self.iterators)):
+            iter = self.iterators[i]
+            if not isinstance(iter, ConstantIterator):
+                self.final_iter = i
+                break
+        else:
+            self.final_iter = -1
+
+    def done(self):
+        final_iter = promote(self.final_iter)
+        if final_iter < 0:
             return False
-    return True
+        return self.iterators[final_iter].done()
 
-def components_hash(components):
-    res = 0x345678
-    for component in components:
-        res = intmask((1000003 * res) ^ component.hash())
-    return res
+    @unroll_safe
+    def next(self, shapelen):
+        for i in range(len(self.iterators)):
+            self.iterators[i] = self.iterators[i].next(shapelen)
 
-class BaseSignature(object):
-    _attrs_ = []
+def _add_ptr_to_cache(ptr, cache):
+    i = 0
+    for p in cache:
+        if ptr == p:
+            return i
+        i += 1
+    else:
+        res = len(cache)
+        cache.append(ptr)
+        return res
 
-    def eq(self, other):
-        return self is other
+class Signature(object):
+    _attrs_ = ['iter_no', 'array_no']
+    _immutable_fields_ = ['iter_no', 'array_no']
+
+    array_no = 0
+    iter_no = 0
+
+    def invent_numbering(self):
+        cache = r_dict(sigeq_no_numbering, sighash)
+        allnumbers = []
+        self._invent_numbering(cache, allnumbers)
+
+    def invent_array_numbering(self, arr):
+        cache = []
+        self._invent_array_numbering(arr, cache)
+
+    def _invent_numbering(self, cache, allnumbers):
+        try:
+            no = cache[self]
+        except KeyError:
+            no = len(allnumbers)
+            cache[self] = no
+            allnumbers.append(no)
+        self.iter_no = no
+
+    def create_frame(self, arr, res_shape=None):
+        res_shape = res_shape or arr.shape
+        iterlist = []
+        arraylist = []
+        self._create_iter(iterlist, arraylist, arr, res_shape, [])
+        return NumpyEvalFrame(iterlist, arraylist)
+
+class ConcreteSignature(Signature):
+    _immutable_fields_ = ['dtype']
+
+    def __init__(self, dtype):
+        self.dtype = dtype
+
+    def eq(self, other, compare_array_no=True):
+        if type(self) is not type(other):
+            return False
+        assert isinstance(other, ConcreteSignature)
+        if compare_array_no:
+            if self.array_no != other.array_no:
+                return False
+        return self.dtype is other.dtype
 
     def hash(self):
-        return compute_identity_hash(self)
+        return compute_identity_hash(self.dtype)
 
-class Signature(BaseSignature):
-    _known_sigs = r_dict(components_eq, components_hash)
+    def allocate_view_iter(self, arr, res_shape, chunklist):
+        r = arr.shape, arr.start, arr.strides, arr.backstrides
+        if chunklist:
+            for chunkelem in chunklist:
+                r = calculate_slice_strides(r[0], r[1], r[2], r[3], chunkelem)
+        shape, start, strides, backstrides = r
+        if len(res_shape) == 1:
+            return OneDimIterator(start, strides[0], res_shape[0])
+        return ViewIterator(start, strides, backstrides, shape, res_shape)
 
-    _attrs_ = ["components"]
-    _immutable_fields_ = ["components[*]"]
+class ArraySignature(ConcreteSignature):
+    def debug_repr(self):
+        return 'Array'
 
-    def __init__(self, components):
-        self.components = components
+    def _invent_array_numbering(self, arr, cache):
+        from pypy.module.micronumpy.interp_numarray import ConcreteArray
+        concr = arr.get_concrete()
+        assert isinstance(concr, ConcreteArray)
+        self.array_no = _add_ptr_to_cache(concr.storage, cache)
 
-    @staticmethod
-    def find_sig(components):
-        return Signature._known_sigs.setdefault(components, Signature(components))
+    def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+        from pypy.module.micronumpy.interp_numarray import ConcreteArray
+        concr = arr.get_concrete()
+        assert isinstance(concr, ConcreteArray)
+        storage = concr.storage
+        if self.iter_no >= len(iterlist):
+            iterlist.append(self.allocate_iter(concr, res_shape, chunklist))
+        if self.array_no >= len(arraylist):
+            arraylist.append(storage)
 
-class Call1(BaseSignature):
-    _immutable_fields_ = ["func", "name"]
+    def allocate_iter(self, arr, res_shape, chunklist):
+        if chunklist:
+            return self.allocate_view_iter(arr, res_shape, chunklist)
+        return ArrayIterator(arr.size)
 
-    def __init__(self, func):
-        self.func = func
-        self.name = func.func_name
+    def eval(self, frame, arr):
+        iter = frame.iterators[self.iter_no]
+        return self.dtype.getitem(frame.arrays[self.array_no], iter.offset)
 
-class Call2(BaseSignature):
-    _immutable_fields_ = ["func", "name"]
+class ScalarSignature(ConcreteSignature):
+    def debug_repr(self):
+        return 'Scalar'
 
-    def __init__(self, func):
-        self.func = func
-        self.name = func.func_name
+    def _invent_array_numbering(self, arr, cache):
+        pass
+
+    def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+        if self.iter_no >= len(iterlist):
+            iter = ConstantIterator()
+            iterlist.append(iter)
+
+    def eval(self, frame, arr):
+        from pypy.module.micronumpy.interp_numarray import Scalar
+        assert isinstance(arr, Scalar)
+        return arr.value
+
+class ViewSignature(ArraySignature):
+    def debug_repr(self):
+        return 'Slice'
+
+    def _invent_numbering(self, cache, allnumbers):
+        # always invent a new number for view
+        no = len(allnumbers)
+        allnumbers.append(no)
+        self.iter_no = no
+
+    def allocate_iter(self, arr, res_shape, chunklist):
+        return self.allocate_view_iter(arr, res_shape, chunklist)
+
+class VirtualSliceSignature(Signature):
+    def __init__(self, child):
+        self.child = child
+
+    def _invent_array_numbering(self, arr, cache):
+        from pypy.module.micronumpy.interp_numarray import VirtualSlice
+        assert isinstance(arr, VirtualSlice)
+        self.child._invent_array_numbering(arr.child, cache)
+
+    def hash(self):
+        return intmask(self.child.hash() ^ 1234)
+
+    def eq(self, other, compare_array_no=True):
+        if type(self) is not type(other):
+            return False
+        assert isinstance(other, VirtualSliceSignature)
+        return self.child.eq(other.child, compare_array_no)
+
+    def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+        from pypy.module.micronumpy.interp_numarray import VirtualSlice
+        assert isinstance(arr, VirtualSlice)
+        chunklist.append(arr.chunks)
+        self.child._create_iter(iterlist, arraylist, arr.child, res_shape,
+                                chunklist)
+
+    def eval(self, frame, arr):
+        from pypy.module.micronumpy.interp_numarray import VirtualSlice
+        assert isinstance(arr, VirtualSlice)
+        return self.child.eval(frame, arr.child)
+
+class Call1(Signature):
+    _immutable_fields_ = ['unfunc', 'name', 'child']
+
+    def __init__(self, func, name, child):
+        self.unfunc = func
+        self.child = child
+        self.name = name
+
+    def hash(self):
+        return compute_hash(self.name) ^ intmask(self.child.hash() << 1)
+
+    def eq(self, other, compare_array_no=True):
+        if type(self) is not type(other):
+            return False
+        assert isinstance(other, Call1)
+        return (self.unfunc is other.unfunc and
+                self.child.eq(other.child, compare_array_no))
+
+    def debug_repr(self):
+        return 'Call1(%s, %s)' % (self.name, self.child.debug_repr())
+
+    def _invent_numbering(self, cache, allnumbers):
+        self.child._invent_numbering(cache, allnumbers)
+
+    def _invent_array_numbering(self, arr, cache):
+        from pypy.module.micronumpy.interp_numarray import Call1
+        assert isinstance(arr, Call1)
+        self.child._invent_array_numbering(arr.values, cache)
+
+    def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+        from pypy.module.micronumpy.interp_numarray import Call1
+        assert isinstance(arr, Call1)
+        self.child._create_iter(iterlist, arraylist, arr.values, res_shape,
+                                chunklist)
+
+    def eval(self, frame, arr):
+        from pypy.module.micronumpy.interp_numarray import Call1
+        assert isinstance(arr, Call1)
+        v = self.child.eval(frame, arr.values).convert_to(arr.res_dtype)
+        return self.unfunc(arr.res_dtype, v)
+
+class Call2(Signature):
+    _immutable_fields_ = ['binfunc', 'name', 'calc_dtype', 'left', 'right']
+
+    def __init__(self, func, name, calc_dtype, left, right):
+        self.binfunc = func
+        self.left = left
+        self.right = right
+        self.name = name
+        self.calc_dtype = calc_dtype
+
+    def hash(self):
+        return (compute_hash(self.name) ^ intmask(self.left.hash() << 1) ^
+                intmask(self.right.hash() << 2))
+
+    def eq(self, other, compare_array_no=True):
+        if type(self) is not type(other):
+            return False
+        assert isinstance(other, Call2)
+        return (self.binfunc is other.binfunc and
+                self.calc_dtype is other.calc_dtype and
+                self.left.eq(other.left, compare_array_no) and
+                self.right.eq(other.right, compare_array_no))
+
+    def _invent_array_numbering(self, arr, cache):
+        from pypy.module.micronumpy.interp_numarray import Call2
+        assert isinstance(arr, Call2)
+        self.left._invent_array_numbering(arr.left, cache)
+        self.right._invent_array_numbering(arr.right, cache)
+
+    def _invent_numbering(self, cache, allnumbers):
+        self.left._invent_numbering(cache, allnumbers)
+        self.right._invent_numbering(cache, allnumbers)
+
+    def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+        from pypy.module.micronumpy.interp_numarray import Call2
+
+        assert isinstance(arr, Call2)
+        self.left._create_iter(iterlist, arraylist, arr.left, res_shape,
+                               chunklist)
+        self.right._create_iter(iterlist, arraylist, arr.right, res_shape,
+                                chunklist)
+
+    def eval(self, frame, arr):
+        from pypy.module.micronumpy.interp_numarray import Call2
+        assert isinstance(arr, Call2)
+        lhs = self.left.eval(frame, arr.left).convert_to(self.calc_dtype)
+        rhs = self.right.eval(frame, arr.right).convert_to(self.calc_dtype)
+        return self.binfunc(self.calc_dtype, lhs, rhs)
+
+    def debug_repr(self):
+        return 'Call2(%s, %s, %s)' % (self.name, self.left.debug_repr(),
+                                      self.right.debug_repr())
+
+class ReduceSignature(Call2):
+    def _create_iter(self, iterlist, arraylist, arr, res_shape, chunklist):
+        self.right._create_iter(iterlist, arraylist, arr, res_shape, chunklist)
+
+    def _invent_numbering(self, cache, allnumbers):
+        self.right._invent_numbering(cache, allnumbers)
+
+    def _invent_array_numbering(self, arr, cache):
+        self.right._invent_array_numbering(arr, cache)
+
+    def eval(self, frame, arr):
+        return self.right.eval(frame, arr)
diff --git a/pypy/module/micronumpy/strides.py b/pypy/module/micronumpy/strides.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/strides.py
@@ -0,0 +1,34 @@
+
+def calculate_slice_strides(shape, start, strides, backstrides, chunks):
+    rstrides = []
+    rbackstrides = []
+    rstart = start
+    rshape = []
+    i = -1
+    for i, (start_, stop, step, lgt) in enumerate(chunks):
+        if step != 0:
+            rstrides.append(strides[i] * step)
+            rbackstrides.append(strides[i] * (lgt - 1) * step)
+            rshape.append(lgt)
+        rstart += strides[i] * start_
+    # add a reminder
+    s = i + 1
+    assert s >= 0
+    rstrides += strides[s:]
+    rbackstrides += backstrides[s:]
+    rshape += shape[s:]
+    return rshape, rstart, rstrides, rbackstrides
+
+def calculate_broadcast_strides(strides, backstrides, orig_shape, res_shape):
+    rstrides = []
+    rbackstrides = []
+    for i in range(len(orig_shape)):
+        if orig_shape[i] == 1:
+            rstrides.append(0)
+            rbackstrides.append(0)
+        else:
+            rstrides.append(strides[i])
+            rbackstrides.append(backstrides[i])
+    rstrides = [0] * (len(res_shape) - len(orig_shape)) + rstrides
+    rbackstrides = [0] * (len(res_shape) - len(orig_shape)) + rbackstrides
+    return rstrides, rbackstrides
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
--- a/pypy/module/micronumpy/test/test_base.py
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -4,7 +4,6 @@
 from pypy.module.micronumpy.interp_ufuncs import (find_binop_result_dtype,
         find_unaryop_result_dtype)
 
-
 class BaseNumpyAppTest(object):
     def setup_class(cls):
         cls.space = gettestobjspace(usemodules=['micronumpy'])
@@ -15,20 +14,37 @@
         bool_dtype = get_dtype_cache(space).w_booldtype
 
         ar = W_NDimArray(10, [10], dtype=float64_dtype)
+        ar2 = W_NDimArray(10, [10], dtype=float64_dtype)
         v1 = ar.descr_add(space, ar)
         v2 = ar.descr_add(space, Scalar(float64_dtype, 2.0))
-        assert v1.signature is not v2.signature
+        sig1 = v1.find_sig()
+        sig2 = v2.find_sig()
+        assert v1 is not v2
+        assert sig1.left.iter_no == sig1.right.iter_no
+        assert sig2.left.iter_no != sig2.right.iter_no
+        assert sig1.left.array_no == sig1.right.array_no
+        sig1b = ar2.descr_add(space, ar).find_sig()
+        assert sig1b.left.array_no != sig1b.right.array_no
+        assert sig1b is not sig1
         v3 = ar.descr_add(space, Scalar(float64_dtype, 1.0))
-        assert v2.signature is v3.signature
+        sig3 = v3.find_sig()
+        assert sig2 is sig3
         v4 = ar.descr_add(space, ar)
-        assert v1.signature is v4.signature
+        assert v1.find_sig() is v4.find_sig()
 
         bool_ar = W_NDimArray(10, [10], dtype=bool_dtype)
         v5 = ar.descr_add(space, bool_ar)
-        assert v5.signature is not v1.signature
-        assert v5.signature is not v2.signature
+        assert v5.find_sig() is not v1.find_sig()
+        assert v5.find_sig() is not v2.find_sig()
         v6 = ar.descr_add(space, bool_ar)
-        assert v5.signature is v6.signature
+        assert v5.find_sig() is v6.find_sig()
+        v7 = v6.descr_add(space, v6)
+        sig7 = v7.find_sig()
+        assert sig7.left.left.iter_no == sig7.right.left.iter_no
+        assert sig7.left.left.iter_no != sig7.right.right.iter_no
+        assert sig7.left.right.iter_no == sig7.right.right.iter_no
+        v1.forced_result = ar
+        assert v1.find_sig() is not sig1
 
     def test_slice_signature(self, space):
         float64_dtype = get_dtype_cache(space).w_float64dtype
@@ -36,11 +52,14 @@
         ar = W_NDimArray(10, [10], dtype=float64_dtype)
         v1 = ar.descr_getitem(space, space.wrap(slice(1, 3, 1)))
         v2 = ar.descr_getitem(space, space.wrap(slice(4, 6, 1)))
-        assert v1.signature is v2.signature
+        assert v1.find_sig() is v2.find_sig()
 
         v3 = v2.descr_add(space, v1)
         v4 = v1.descr_add(space, v2)
-        assert v3.signature is v4.signature
+        assert v3.find_sig() is v4.find_sig()
+        v5 = ar.descr_add(space, ar).descr_getitem(space, space.wrap(slice(1, 3, 1)))
+        v6 = ar.descr_add(space, ar).descr_getitem(space, space.wrap(slice(1, 4, 1)))
+        assert v5.find_sig() is v6.find_sig()
 
 class TestUfuncCoerscion(object):
     def test_binops(self, space):
diff --git a/pypy/module/micronumpy/test/test_compile.py b/pypy/module/micronumpy/test/test_compile.py
--- a/pypy/module/micronumpy/test/test_compile.py
+++ b/pypy/module/micronumpy/test/test_compile.py
@@ -137,6 +137,16 @@
         interp = self.run(code)
         assert interp.results[0].value.value == 15
 
+    def test_sum2(self):
+        code = """
+        a = |30|
+        b = a + a
+        sum(b)
+        """
+        interp = self.run(code)
+        assert interp.results[0].value.value == 30 * (30 - 1)
+
+
     def test_array_write(self):
         code = """
         a = [1,2,3,4,5]
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -8,8 +8,6 @@
 
 
 class MockDtype(object):
-    signature = signature.BaseSignature()
-
     def malloc(self, size):
         return None
 
@@ -38,92 +36,86 @@
         assert a.backstrides == [135, 12, 2]
 
     def test_create_slice_f(self):
-        space = self.space
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
-        s = a.create_slice(space, [(3, 0, 0, 1)])
+        s = a.create_slice([(3, 0, 0, 1)])
         assert s.start == 3
         assert s.strides == [10, 50]
         assert s.backstrides == [40, 100]
-        s = a.create_slice(space, [(1, 9, 2, 4)])
+        s = a.create_slice([(1, 9, 2, 4)])
         assert s.start == 1
         assert s.strides == [2, 10, 50]
         assert s.backstrides == [6, 40, 100]
-        s = a.create_slice(space, [(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
+        s = a.create_slice([(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
         assert s.shape == [2, 1]
         assert s.strides == [3, 10]
         assert s.backstrides == [3, 0]
-        s = a.create_slice(space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+        s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
         assert s.start == 20
         assert s.shape == [10, 3]
 
     def test_create_slice_c(self):
-        space = self.space
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'C')
-        s = a.create_slice(space, [(3, 0, 0, 1)])
+        s = a.create_slice([(3, 0, 0, 1)])
         assert s.start == 45
         assert s.strides == [3, 1]
         assert s.backstrides == [12, 2]
-        s = a.create_slice(space, [(1, 9, 2, 4)])
+        s = a.create_slice([(1, 9, 2, 4)])
         assert s.start == 15
         assert s.strides == [30, 3, 1]
         assert s.backstrides == [90, 12, 2]
-        s = a.create_slice(space, [(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
+        s = a.create_slice([(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
         assert s.start == 19
         assert s.shape == [2, 1]
         assert s.strides == [45, 3]
         assert s.backstrides == [45, 0]
-        s = a.create_slice(space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+        s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
         assert s.start == 6
         assert s.shape == [10, 3]
 
     def test_slice_of_slice_f(self):
-        space = self.space
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
-        s = a.create_slice(space, [(5, 0, 0, 1)])
+        s = a.create_slice([(5, 0, 0, 1)])
         assert s.start == 5
-        s2 = s.create_slice(space, [(3, 0, 0, 1)])
+        s2 = s.create_slice([(3, 0, 0, 1)])
         assert s2.shape == [3]
         assert s2.strides == [50]
         assert s2.parent is a
         assert s2.backstrides == [100]
         assert s2.start == 35
-        s = a.create_slice(space, [(1, 5, 3, 2)])
-        s2 = s.create_slice(space, [(0, 2, 1, 2), (2, 0, 0, 1)])
+        s = a.create_slice([(1, 5, 3, 2)])
+        s2 = s.create_slice([(0, 2, 1, 2), (2, 0, 0, 1)])
         assert s2.shape == [2, 3]
         assert s2.strides == [3, 50]
         assert s2.backstrides == [3, 100]
         assert s2.start == 1 * 15 + 2 * 3
 
     def test_slice_of_slice_c(self):
-        space = self.space
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), order='C')
-        s = a.create_slice(space, [(5, 0, 0, 1)])
+        s = a.create_slice([(5, 0, 0, 1)])
         assert s.start == 15 * 5
-        s2 = s.create_slice(space, [(3, 0, 0, 1)])
+        s2 = s.create_slice([(3, 0, 0, 1)])
         assert s2.shape == [3]
         assert s2.strides == [1]
         assert s2.parent is a
         assert s2.backstrides == [2]
         assert s2.start == 5 * 15 + 3 * 3
-        s = a.create_slice(space, [(1, 5, 3, 2)])
-        s2 = s.create_slice(space, [(0, 2, 1, 2), (2, 0, 0, 1)])
+        s = a.create_slice([(1, 5, 3, 2)])
+        s2 = s.create_slice([(0, 2, 1, 2), (2, 0, 0, 1)])
         assert s2.shape == [2, 3]
         assert s2.strides == [45, 1]
         assert s2.backstrides == [45, 2]
         assert s2.start == 1 * 15 + 2 * 3
 
     def test_negative_step_f(self):
-        space = self.space
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
-        s = a.create_slice(space, [(9, -1, -2, 5)])
+        s = a.create_slice([(9, -1, -2, 5)])
         assert s.start == 9
         assert s.strides == [-2, 10, 50]
         assert s.backstrides == [-8, 40, 100]
 
     def test_negative_step_c(self):
-        space = self.space
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), order='C')
-        s = a.create_slice(space, [(9, -1, -2, 5)])
+        s = a.create_slice([(9, -1, -2, 5)])
         assert s.start == 135
         assert s.strides == [-30, 3, 1]
         assert s.backstrides == [-120, 12, 2]
@@ -132,7 +124,7 @@
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
         r = a._index_of_single_item(self.space, self.newtuple(1, 2, 2))
         assert r == 1 + 2 * 10 + 2 * 50
-        s = a.create_slice(self.space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+        s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
         r = s._index_of_single_item(self.space, self.newtuple(1, 0))
         assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 0))
         r = s._index_of_single_item(self.space, self.newtuple(1, 1))
@@ -142,7 +134,7 @@
         a = W_NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'C')
         r = a._index_of_single_item(self.space, self.newtuple(1, 2, 2))
         assert r == 1 * 3 * 5 + 2 * 3 + 2
-        s = a.create_slice(self.space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+        s = a.create_slice([(0, 10, 1, 10), (2, 0, 0, 1)])
         r = s._index_of_single_item(self.space, self.newtuple(1, 0))
         assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 0))
         r = s._index_of_single_item(self.space, self.newtuple(1, 1))
@@ -897,13 +889,32 @@
         a = zeros(1)
         assert debug_repr(a) == 'Array'
         assert debug_repr(a + a) == 'Call2(add, Array, Array)'
-        assert debug_repr(a[::2]) == 'Slice(Array)'
+        assert debug_repr(a[::2]) == 'Slice'
         assert debug_repr(a + 2) == 'Call2(add, Array, Scalar)'
-        assert debug_repr(a + a.flat) == 'Call2(add, Array, FlatIter(Array))'
+        assert debug_repr(a + a.flat) == 'Call2(add, Array, Slice)'
         assert debug_repr(sin(a)) == 'Call1(sin, Array)'
+
         b = a + a
         b[0] = 3
-        assert debug_repr(b) == 'Call2(add, forced=Array)'
+        assert debug_repr(b) == 'Array'
+
+    def test_virtual_views(self):
+        from numpypy import arange
+        a = arange(15)
+        c = (a + a)
+        d = c[::2]
+        assert d[3] == 12
+        c[6] = 5
+        assert d[3] == 5
+        a = arange(15)
+        c = (a + a)
+        d = c[::2][::2]
+        assert d[1] == 8
+        b = a + a
+        c = b[::2]
+        c[:] = 3
+        assert b[0] == 3
+        assert b[1] == 2
 
     def test_tolist_scalar(self):
         from numpypy import int32, bool_
@@ -1075,10 +1086,10 @@
 
     def test_broadcast_setslice(self):
         from numpypy import zeros, ones
-        a = zeros((100, 100))
-        b = ones(100)
+        a = zeros((10, 10))
+        b = ones(10)
         a[:, :] = b
-        assert a[13, 15] == 1
+        assert a[3, 5] == 1
 
     def test_broadcast_shape_agreement(self):
         from numpypy import zeros, array
@@ -1112,6 +1123,14 @@
         b[:] = (a + a)
         assert (b == zeros((4, 3, 5))).all()
 
+    def test_broadcast_virtualview(self):
+        from numpypy import arange, zeros
+        a = arange(8).reshape([2, 2, 2])
+        b = (a + a)[1, 1]
+        c = zeros((2, 2, 2))
+        c[:] = b
+        assert (c == [[[12, 14], [12, 14]], [[12, 14], [12, 14]]]).all()
+
     def test_argmax(self):
         from numpypy import array
         a = array([[1, 2], [3, 4], [5, 6]])
@@ -1173,6 +1192,11 @@
         a = array([1, 2, 3])
         assert dot(a.flat, a.flat) == 14
 
+    def test_flatiter_varray(self):
+        from numpypy import ones
+        a = ones((2, 2))
+        assert list(((a + a).flat)) == [2, 2, 2, 2]
+
     def test_slice_copy(self):
         from numpypy import zeros
         a = zeros((10, 10))
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -49,10 +49,14 @@
             interp.run(space)
             w_res = interp.results[-1]
             if isinstance(w_res, BaseArray):
-                w_res = w_res.eval(w_res.start_iter())
-
+                concr = w_res.get_concrete_or_scalar()
+                sig = concr.find_sig()
+                frame = sig.create_frame(concr)
+                w_res = sig.eval(frame, concr)
             if isinstance(w_res, interp_boxes.W_Float64Box):
                 return w_res.value
+            if isinstance(w_res, interp_boxes.W_Int64Box):
+                return float(w_res.value)
             elif isinstance(w_res, interp_boxes.W_BoolBox):
                 return float(w_res.value)
             raise TypeError(w_res)
@@ -78,8 +82,9 @@
     def test_add(self):
         result = self.run("add")
         self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
-                                'setinteriorfield_raw': 1, 'int_add': 3,
-                                'int_ge': 1, 'guard_false': 1, 'jump': 1})
+                                'setinteriorfield_raw': 1, 'int_add': 2,
+                                'int_ge': 1, 'guard_false': 1, 'jump': 1,
+                                'arraylen_gc': 1})
         assert result == 3 + 3
 
     def define_float_add():
@@ -93,7 +98,8 @@
         assert result == 3 + 3
         self.check_simple_loop({"getinteriorfield_raw": 1, "float_add": 1,
                                 "setinteriorfield_raw": 1, "int_add": 2,
-                                "int_ge": 1, "guard_false": 1, "jump": 1})
+                                "int_ge": 1, "guard_false": 1, "jump": 1,
+                                'arraylen_gc': 1})
 
     def define_sum():
         return """
@@ -106,8 +112,8 @@
         result = self.run("sum")
         assert result == 2 * sum(range(30))
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 2,
-                                "int_add": 2, "int_ge": 1, "guard_false": 1,
-                                "jump": 1})
+                                "int_add": 1, "int_ge": 1, "guard_false": 1,
+                                "jump": 1, 'arraylen_gc': 1})
 
     def define_prod():
         return """
@@ -123,18 +129,22 @@
             expected *= i * 2
         assert result == expected
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
-                                "float_mul": 1, "int_add": 2,
-                                "int_ge": 1, "guard_false": 1, "jump": 1})
+                                "float_mul": 1, "int_add": 1,
+                                "int_ge": 1, "guard_false": 1, "jump": 1,
+                                'arraylen_gc': 1})
 
-    def test_max(self):
-        py.test.skip("broken, investigate")
-        result = self.run("""
+    def define_max():
+        return """
         a = |30|
         a[13] = 128
         b = a + a
         max(b)
-        """)
+        """
+
+    def test_max(self):
+        result = self.run("max")
         assert result == 256
+        py.test.skip("not there yet, getting though")
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
                                 "float_mul": 1, "int_add": 1,
                                 "int_lt": 1, "guard_true": 1, "jump": 1})
@@ -164,9 +174,9 @@
         result = self.run("any")
         assert result == 1
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
-                                "float_ne": 1, "int_add": 2,
+                                "float_ne": 1, "int_add": 1,
                                 "int_ge": 1, "jump": 1,
-                                "guard_false": 2})
+                                "guard_false": 2, 'arraylen_gc': 1})
 
     def define_already_forced():
         return """
@@ -183,14 +193,13 @@
         # This is the sum of the ops for both loops, however if you remove the
         # optimization then you end up with 2 float_adds, so we can still be
         # sure it was optimized correctly.
-        # XXX the comment above is wrong now.  We need preferrably a way to
-        # count the two loops separately
-        self.check_resops({'setinteriorfield_raw': 4, 'guard_nonnull': 1,
-                           'getfield_gc': 35, 'getfield_gc_pure': 6,
-                           'guard_class': 22, 'int_add': 8, 'float_mul': 2,
-                           'guard_isnull': 2, 'jump': 2, 'int_ge': 4,
-                           'getinteriorfield_raw': 4, 'float_add': 2, 'guard_false': 4,
-                           'guard_value': 2})
+        self.check_resops({'setinteriorfield_raw': 4, 'getfield_gc': 26,
+                           'getarrayitem_gc': 4, 'getarrayitem_gc_pure': 2,
+                           'getfield_gc_pure': 4,
+                           'guard_class': 8, 'int_add': 8, 'float_mul': 2,
+                           'jump': 2, 'int_ge': 4,
+                           'getinteriorfield_raw': 4, 'float_add': 2,
+                           'guard_false': 4, 'arraylen_gc': 2, 'same_as': 2})
 
     def define_ufunc():
         return """
@@ -204,8 +213,9 @@
         result = self.run("ufunc")
         assert result == -6
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1, "float_neg": 1,
-                                "setinteriorfield_raw": 1, "int_add": 3,
-                                "int_ge": 1, "guard_false": 1, "jump": 1})
+                                "setinteriorfield_raw": 1, "int_add": 2,
+                                "int_ge": 1, "guard_false": 1, "jump": 1,
+                                'arraylen_gc': 1})
 
     def define_specialization():
         return """
@@ -248,7 +258,8 @@
                                 'setinteriorfield_raw': 1,
                                 'int_add': 3,
                                 'int_ge': 1, 'guard_false': 1,
-                                'jump': 1})
+                                'jump': 1,
+                                'arraylen_gc': 1})
 
     def define_multidim():
         return """
@@ -263,8 +274,9 @@
         # int_add might be 1 here if we try slightly harder with
         # reusing indexes or some optimization
         self.check_simple_loop({'float_add': 1, 'getinteriorfield_raw': 2,
-                                'guard_false': 1, 'int_add': 3, 'int_ge': 1,
-                                'jump': 1, 'setinteriorfield_raw': 1})
+                                'guard_false': 1, 'int_add': 2, 'int_ge': 1,
+                                'jump': 1, 'setinteriorfield_raw': 1,
+                                'arraylen_gc': 1})
 
     def define_multidim_slice():
         return """
@@ -312,7 +324,25 @@
         self.check_trace_count(1)
         self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add' : 1,
                                 'setinteriorfield_raw': 1, 'int_add': 3,
-                                'int_eq': 1, 'guard_false': 1, 'jump': 1})
+                                'int_lt': 1, 'guard_true': 1, 'jump': 1,
+                                'arraylen_gc': 3})
+
+    def define_virtual_slice():
+        return """
+        a = |30|
+        c = a + a
+        d = c -> 1:20
+        d -> 1
+        """
+
+    def test_virtual_slice(self):
+        result = self.run("virtual_slice")
+        assert result == 4
+        self.check_trace_count(1)
+        self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add' : 1,
+                                'setinteriorfield_raw': 1, 'int_add': 2,
+                                'int_ge': 1, 'guard_false': 1, 'jump': 1,
+                                'arraylen_gc': 1})
 
 class TestNumpyOld(LLJitMixin):
     def setup_class(cls):
diff --git a/pypy/module/micronumpy/test/test_ztranslation.py b/pypy/module/micronumpy/test/test_ztranslation.py
--- a/pypy/module/micronumpy/test/test_ztranslation.py
+++ b/pypy/module/micronumpy/test/test_ztranslation.py
@@ -1,5 +1,8 @@
-
+from pypy.module.micronumpy import signature
 from pypy.objspace.fake.checkmodule import checkmodule
 
 def test_numpy_translates():
+    # XXX: If there are signatures floating around this might explode. This fix
+    # is ugly.
+    signature.known_sigs.clear()
     checkmodule('micronumpy')
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -252,7 +252,7 @@
         # grow the list
         done = 0
         while done < len(self._seen_extras):
-            print self._seen_extras
+            #print self._seen_extras
             ann.build_types(self._seen_extras[done], [],
                             complete_now=False)
             done += 1
diff --git a/pypy/objspace/std/stringobject.py b/pypy/objspace/std/stringobject.py
--- a/pypy/objspace/std/stringobject.py
+++ b/pypy/objspace/std/stringobject.py
@@ -514,29 +514,34 @@
     if maxsplit == 0:
         return space.wrap(input)
 
-    # An ok guess at the default size
-    builder = StringBuilder(len(input))
-    first = True
-
     if not sub:
         upper = len(input)
         if maxsplit > 0 and maxsplit < upper + 2:
             upper = maxsplit - 1
             assert upper >= 0
-        first = False
+        
         try:
-            for i in range(upper):
-                builder.append(by)
-                builder.append(input[i])
+            result_size = ovfcheck(upper * len(by))
+            result_size = ovfcheck(result_size + upper)
+            result_size = ovfcheck(result_size + len(by))
+            remaining_size = len(input) - upper
+            result_size = ovfcheck(result_size + remaining_size)
+        except OverflowError:
+            raise OperationError(space.w_OverflowError,
+                space.wrap("replace string is too long")
+            )
+        builder = StringBuilder(result_size)
+        for i in range(upper):
             builder.append(by)
-            builder.append_slice(input, upper, len(input))
-        except MemoryError:
-            raise OperationError(space.w_OverflowError,
-                space.wrap("replace string too long")
-            )
+            builder.append(input[i])
+        builder.append(by)
+        builder.append_slice(input, upper, len(input))
     else:
+        # An ok guess for the result size
+        builder = StringBuilder(len(input))
         start = 0
         sublen = len(sub)
+        first = True
 
         while maxsplit != 0:
             next = input.find(sub, start)
diff --git a/testrunner/runner.py b/testrunner/runner.py
--- a/testrunner/runner.py
+++ b/testrunner/runner.py
@@ -21,6 +21,16 @@
                 win32api.CloseHandle(proch)
             except pywintypes.error, e:
                 pass
+    #Try to avoid opeing a dialog box if one of the tests causes a system error
+    import ctypes
+    winapi = ctypes.windll.kernel32
+    SEM_FAILCRITICALERRORS = 1
+    SEM_NOGPFAULTERRORBOX  = 2
+    SEM_NOOPENFILEERRORBOX = 0x8000
+    flags = SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX
+    #Since there is no GetErrorMode, do a double Set
+    old_mode = winapi.SetErrorMode(flags)
+    winapi.SetErrorMode(old_mode | flags)
 
     SIGKILL = SIGTERM = 0
     READ_MODE = 'rU'


More information about the pypy-commit mailing list