[pypy-commit] pypy win32-cleanup: merge with default

mattip noreply at buildbot.pypy.org
Sun Feb 5 21:57:53 CET 2012


Author: mattip
Branch: win32-cleanup
Changeset: r52114:d11247cc0e08
Date: 2012-02-05 22:56 +0200
http://bitbucket.org/pypy/pypy/changeset/d11247cc0e08/

Log:	merge with default

diff --git a/pypy/annotation/annrpython.py b/pypy/annotation/annrpython.py
--- a/pypy/annotation/annrpython.py
+++ b/pypy/annotation/annrpython.py
@@ -93,6 +93,10 @@
         # make input arguments and set their type
         args_s = [self.typeannotation(t) for t in input_arg_types]
 
+        # XXX hack
+        annmodel.TLS.check_str_without_nul = (
+            self.translator.config.translation.check_str_without_nul)
+
         flowgraph, inputcells = self.get_call_parameters(function, args_s, policy)
         if not isinstance(flowgraph, FunctionGraph):
             assert isinstance(flowgraph, annmodel.SomeObject)
diff --git a/pypy/annotation/binaryop.py b/pypy/annotation/binaryop.py
--- a/pypy/annotation/binaryop.py
+++ b/pypy/annotation/binaryop.py
@@ -434,11 +434,13 @@
 class __extend__(pairtype(SomeString, SomeString)):
 
     def union((str1, str2)):
-        return SomeString(can_be_None=str1.can_be_None or str2.can_be_None)
+        can_be_None = str1.can_be_None or str2.can_be_None
+        no_nul = str1.no_nul and str2.no_nul
+        return SomeString(can_be_None=can_be_None, no_nul=no_nul)
 
     def add((str1, str2)):
         # propagate const-ness to help getattr(obj, 'prefix' + const_name)
-        result = SomeString()
+        result = SomeString(no_nul=str1.no_nul and str2.no_nul)
         if str1.is_immutable_constant() and str2.is_immutable_constant():
             result.const = str1.const + str2.const
         return result
@@ -475,7 +477,16 @@
                 raise NotImplementedError(
                     "string formatting mixing strings and unicode not supported")
         getbookkeeper().count('strformat', str, s_tuple)
-        return SomeString()
+        no_nul = str.no_nul
+        for s_item in s_tuple.items:
+            if isinstance(s_item, SomeFloat):
+                pass   # or s_item is a subclass, like SomeInteger
+            elif isinstance(s_item, SomeString) and s_item.no_nul:
+                pass
+            else:
+                no_nul = False
+                break
+        return SomeString(no_nul=no_nul)
 
 
 class __extend__(pairtype(SomeString, SomeObject)):
@@ -828,7 +839,7 @@
     exec source.compile() in glob
 
 _make_none_union('SomeInstance',   'classdef=obj.classdef, can_be_None=True')
-_make_none_union('SomeString',      'can_be_None=True')
+_make_none_union('SomeString',      'no_nul=obj.no_nul, can_be_None=True')
 _make_none_union('SomeUnicodeString', 'can_be_None=True')
 _make_none_union('SomeList',         'obj.listdef')
 _make_none_union('SomeDict',          'obj.dictdef')
diff --git a/pypy/annotation/bookkeeper.py b/pypy/annotation/bookkeeper.py
--- a/pypy/annotation/bookkeeper.py
+++ b/pypy/annotation/bookkeeper.py
@@ -342,10 +342,11 @@
             else:
                 raise Exception("seeing a prebuilt long (value %s)" % hex(x))
         elif issubclass(tp, str): # py.lib uses annotated str subclasses
+            no_nul = not '\x00' in x
             if len(x) == 1:
-                result = SomeChar()
+                result = SomeChar(no_nul=no_nul)
             else:
-                result = SomeString()
+                result = SomeString(no_nul=no_nul)
         elif tp is unicode:
             if len(x) == 1:
                 result = SomeUnicodeCodePoint()
diff --git a/pypy/annotation/listdef.py b/pypy/annotation/listdef.py
--- a/pypy/annotation/listdef.py
+++ b/pypy/annotation/listdef.py
@@ -86,18 +86,19 @@
             read_locations = self.read_locations.copy()
             other_read_locations = other.read_locations.copy()
             self.read_locations.update(other.read_locations)
-            self.patch()    # which should patch all refs to 'other'
             s_value = self.s_value
             s_other_value = other.s_value
             s_new_value = unionof(s_value, s_other_value)
+            if s_new_value != s_value:
+                if self.dont_change_any_more:
+                    raise TooLateForChange
             if isdegenerated(s_new_value):
                 if self.bookkeeper:
                     self.bookkeeper.ondegenerated(self, s_new_value)
                 elif other.bookkeeper:
                     other.bookkeeper.ondegenerated(other, s_new_value)
+            self.patch()    # which should patch all refs to 'other'
             if s_new_value != s_value:
-                if self.dont_change_any_more:
-                    raise TooLateForChange
                 self.s_value = s_new_value
                 # reflow from reading points
                 for position_key in read_locations:
@@ -222,4 +223,5 @@
 
 MOST_GENERAL_LISTDEF = ListDef(None, SomeObject())
 
-s_list_of_strings = SomeList(ListDef(None, SomeString(), resized = True))
+s_list_of_strings = SomeList(ListDef(None, SomeString(no_nul=True),
+                                     resized = True))
diff --git a/pypy/annotation/model.py b/pypy/annotation/model.py
--- a/pypy/annotation/model.py
+++ b/pypy/annotation/model.py
@@ -39,7 +39,9 @@
 DEBUG = False    # set to False to disable recording of debugging information
 
 class State(object):
-    pass
+    # A global attribute :-(  Patch it with 'True' to enable checking of
+    # the no_nul attribute...
+    check_str_without_nul = False
 TLS = State()
 
 class SomeObject(object):
@@ -225,43 +227,57 @@
     def __init__(self):
         pass
 
-class SomeString(SomeObject):
-    "Stands for an object which is known to be a string."
-    knowntype = str
+class SomeStringOrUnicode(SomeObject):
     immutable = True
-    def __init__(self, can_be_None=False):
-        self.can_be_None = can_be_None
+    can_be_None=False
+    no_nul = False  # No NUL character in the string.
+
+    def __init__(self, can_be_None=False, no_nul=False):
+        if can_be_None:
+            self.can_be_None = True
+        if no_nul:
+            self.no_nul = True
 
     def can_be_none(self):
         return self.can_be_None
 
+    def __eq__(self, other):
+        if self.__class__ is not other.__class__:
+            return False
+        d1 = self.__dict__
+        d2 = other.__dict__
+        if not TLS.check_str_without_nul:
+            d1 = d1.copy(); d1['no_nul'] = 0   # ignored
+            d2 = d2.copy(); d2['no_nul'] = 0   # ignored
+        return d1 == d2
+
+class SomeString(SomeStringOrUnicode):
+    "Stands for an object which is known to be a string."
+    knowntype = str
+
     def nonnoneify(self):
-        return SomeString(can_be_None=False)
+        return SomeString(can_be_None=False, no_nul=self.no_nul)
 
-class SomeUnicodeString(SomeObject):
+class SomeUnicodeString(SomeStringOrUnicode):
     "Stands for an object which is known to be an unicode string"
     knowntype = unicode
-    immutable = True
-    def __init__(self, can_be_None=False):
-        self.can_be_None = can_be_None
-
-    def can_be_none(self):
-        return self.can_be_None
 
     def nonnoneify(self):
-        return SomeUnicodeString(can_be_None=False)
+        return SomeUnicodeString(can_be_None=False, no_nul=self.no_nul)
 
 class SomeChar(SomeString):
     "Stands for an object known to be a string of length 1."
     can_be_None = False
-    def __init__(self):    # no 'can_be_None' argument here
-        pass
+    def __init__(self, no_nul=False):    # no 'can_be_None' argument here
+        if no_nul:
+            self.no_nul = True
 
 class SomeUnicodeCodePoint(SomeUnicodeString):
     "Stands for an object known to be a unicode codepoint."
     can_be_None = False
-    def __init__(self):    # no 'can_be_None' argument here
-        pass
+    def __init__(self, no_nul=False):    # no 'can_be_None' argument here
+        if no_nul:
+            self.no_nul = True
 
 SomeString.basestringclass = SomeString
 SomeString.basecharclass = SomeChar
@@ -502,6 +518,7 @@
 s_None = SomePBC([], can_be_None=True)
 s_Bool = SomeBool()
 s_ImpossibleValue = SomeImpossibleValue()
+s_Str0 = SomeString(no_nul=True)
 
 # ____________________________________________________________
 # weakrefs
@@ -716,8 +733,7 @@
 
 def not_const(s_obj):
     if s_obj.is_constant():
-        new_s_obj = SomeObject()
-        new_s_obj.__class__ = s_obj.__class__
+        new_s_obj = SomeObject.__new__(s_obj.__class__)
         dic = new_s_obj.__dict__ = s_obj.__dict__.copy()
         if 'const' in dic:
             del new_s_obj.const
diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -456,6 +456,20 @@
             return ''.join(g(n))
         s = a.build_types(f, [int])
         assert s.knowntype == str
+        assert s.no_nul
+
+    def test_str_split(self):
+        a = self.RPythonAnnotator()
+        def g(n):
+            if n:
+                return "test string"
+        def f(n):
+            if n:
+                return g(n).split(' ')
+        s = a.build_types(f, [int])
+        assert isinstance(s, annmodel.SomeList)
+        s_item = s.listdef.listitem.s_value
+        assert s_item.no_nul
 
     def test_str_splitlines(self):
         a = self.RPythonAnnotator()
@@ -465,6 +479,18 @@
         assert isinstance(s, annmodel.SomeList)
         assert s.listdef.listitem.resized
 
+    def test_str_strip(self):
+        a = self.RPythonAnnotator()
+        def f(n, a_str):
+            if n == 0:
+                return a_str.strip(' ')
+            elif n == 1:
+                return a_str.rstrip(' ')
+            else: 
+                return a_str.lstrip(' ')
+        s = a.build_types(f, [int, annmodel.SomeString(no_nul=True)])
+        assert s.no_nul
+
     def test_str_mul(self):
         a = self.RPythonAnnotator()
         def f(a_str):
@@ -1841,7 +1867,7 @@
             return obj.indirect()
         a = self.RPythonAnnotator()
         s = a.build_types(f, [bool])
-        assert s == annmodel.SomeString(can_be_None=True)
+        assert annmodel.SomeString(can_be_None=True).contains(s)
 
     def test_dont_see_AttributeError_clause(self):
         class Stuff:
@@ -2018,6 +2044,37 @@
         s = a.build_types(g, [int])
         assert not s.can_be_None
 
+    def test_string_noNUL_canbeNone(self):
+        def f(a):
+            if a:
+                return "abc"
+            else:
+                return None
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        assert s.can_be_None
+        assert s.no_nul
+
+    def test_str_or_None(self):
+        def f(a):
+            if a:
+                return "abc"
+            else:
+                return None
+        def g(a):
+            x = f(a)
+            #assert x is not None
+            if x is None:
+                return "abcd"
+            return x
+            if isinstance(x, str):
+                return x
+            return "impossible"
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        assert s.can_be_None
+        assert s.no_nul
+
     def test_emulated_pbc_call_simple(self):
         def f(a,b):
             return a + b
@@ -2071,6 +2128,19 @@
         assert isinstance(s, annmodel.SomeIterator)
         assert s.variant == ('items',)
 
+    def test_iteritems_str0(self):
+        def it(d):
+            return d.iteritems()
+        def f():
+            d0 = {'1a': '2a', '3': '4'}
+            for item in it(d0):
+                return "%s=%s" % item
+            raise ValueError
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [])
+        assert isinstance(s, annmodel.SomeString)
+        assert s.no_nul
+
     def test_non_none_and_none_with_isinstance(self):
         class A(object):
             pass
diff --git a/pypy/annotation/unaryop.py b/pypy/annotation/unaryop.py
--- a/pypy/annotation/unaryop.py
+++ b/pypy/annotation/unaryop.py
@@ -480,13 +480,13 @@
         return SomeInteger(nonneg=True)
 
     def method_strip(str, chr):
-        return str.basestringclass()
+        return str.basestringclass(no_nul=str.no_nul)
 
     def method_lstrip(str, chr):
-        return str.basestringclass()
+        return str.basestringclass(no_nul=str.no_nul)
 
     def method_rstrip(str, chr):
-        return str.basestringclass()
+        return str.basestringclass(no_nul=str.no_nul)
 
     def method_join(str, s_list):
         if s_None.contains(s_list):
@@ -497,7 +497,8 @@
             if isinstance(str, SomeUnicodeString):
                 return immutablevalue(u"")
             return immutablevalue("")
-        return str.basestringclass()
+        no_nul = str.no_nul and s_item.no_nul
+        return str.basestringclass(no_nul=no_nul)
 
     def iter(str):
         return SomeIterator(str)
@@ -508,18 +509,21 @@
 
     def method_split(str, patt, max=-1):
         getbookkeeper().count("str_split", str, patt)
-        return getbookkeeper().newlist(str.basestringclass())
+        s_item = str.basestringclass(no_nul=str.no_nul)
+        return getbookkeeper().newlist(s_item)
 
     def method_rsplit(str, patt, max=-1):
         getbookkeeper().count("str_rsplit", str, patt)
-        return getbookkeeper().newlist(str.basestringclass())
+        s_item = str.basestringclass(no_nul=str.no_nul)
+        return getbookkeeper().newlist(s_item)
 
     def method_replace(str, s1, s2):
         return str.basestringclass()
 
     def getslice(str, s_start, s_stop):
         check_negative_slice(s_start, s_stop)
-        return str.basestringclass()
+        result = str.basestringclass(no_nul=str.no_nul)
+        return result
 
 class __extend__(SomeUnicodeString):
     def method_encode(uni, s_enc):
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -123,6 +123,9 @@
                  default="off"),
     # jit_ffi is automatically turned on by withmod-_ffi (which is enabled by default)
     BoolOption("jit_ffi", "optimize libffi calls", default=False, cmdline=None),
+    BoolOption("check_str_without_nul",
+               "Forbid NUL chars in strings in some external function calls",
+               default=False, cmdline=None),
 
     # misc
     BoolOption("verbose", "Print extra information", default=False),
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1312,6 +1312,15 @@
     def str_w(self, w_obj):
         return w_obj.str_w(self)
 
+    def str0_w(self, w_obj):
+        "Like str_w, but rejects strings with NUL bytes."
+        from pypy.rlib import rstring
+        result = w_obj.str_w(self)
+        if '\x00' in result:
+            raise OperationError(self.w_TypeError, self.wrap(
+                    'argument must be a string without NUL characters'))
+        return rstring.assert_str0(result)
+
     def int_w(self, w_obj):
         return w_obj.int_w(self)
 
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -130,6 +130,9 @@
     def visit_str_or_None(self, el, app_sig):
         self.checked_space_method(el, app_sig)
 
+    def visit_str0(self, el, app_sig):
+        self.checked_space_method(el, app_sig)
+
     def visit_nonnegint(self, el, app_sig):
         self.checked_space_method(el, app_sig)
 
@@ -249,6 +252,9 @@
     def visit_str_or_None(self, typ):
         self.run_args.append("space.str_or_None_w(%s)" % (self.scopenext(),))
 
+    def visit_str0(self, typ):
+        self.run_args.append("space.str0_w(%s)" % (self.scopenext(),))
+
     def visit_nonnegint(self, typ):
         self.run_args.append("space.gateway_nonnegint_w(%s)" % (
             self.scopenext(),))
@@ -383,6 +389,9 @@
     def visit_str_or_None(self, typ):
         self.unwrap.append("space.str_or_None_w(%s)" % (self.nextarg(),))
 
+    def visit_str0(self, typ):
+        self.unwrap.append("space.str0_w(%s)" % (self.nextarg(),))
+
     def visit_nonnegint(self, typ):
         self.unwrap.append("space.gateway_nonnegint_w(%s)" % (self.nextarg(),))
 
diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py
--- a/pypy/interpreter/mixedmodule.py
+++ b/pypy/interpreter/mixedmodule.py
@@ -50,7 +50,7 @@
             space.call_method(self.w_dict, 'update', self.w_initialdict)
 
         for w_submodule in self.submodules_w:
-            name = space.str_w(w_submodule.w_name)
+            name = space.str0_w(w_submodule.w_name)
             space.setitem(self.w_dict, space.wrap(name.split(".")[-1]), w_submodule)
             space.getbuiltinmodule(name)
 
diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py
--- a/pypy/interpreter/module.py
+++ b/pypy/interpreter/module.py
@@ -31,7 +31,8 @@
     def install(self):
         """NOT_RPYTHON: installs this module into space.builtin_modules"""
         w_mod = self.space.wrap(self)
-        self.space.builtin_modules[self.space.unwrap(self.w_name)] = w_mod
+        modulename = self.space.str0_w(self.w_name)
+        self.space.builtin_modules[modulename] = w_mod
 
     def setup_after_space_initialization(self):
         """NOT_RPYTHON: to allow built-in modules to do some more setup
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -780,6 +780,9 @@
         self.overflow_flag = ovf
         return z
 
+    def op_keepalive(self, _, x):
+        pass
+
     # ----------
     # delegating to the builtins do_xxx() (done automatically for simple cases)
 
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -1463,6 +1463,9 @@
         if jump_op is not None and jump_op.getdescr() is descr:
             self._compute_hint_frame_locations_from_descr(descr)
 
+    def consider_keepalive(self, op):
+        pass
+
     def not_implemented_op(self, op):
         not_implemented("not implemented operation: %s" % op.getopname())
 
diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py
--- a/pypy/jit/metainterp/executor.py
+++ b/pypy/jit/metainterp/executor.py
@@ -254,6 +254,9 @@
         assert isinstance(x, r_longlong)  # 32-bit
         return BoxFloat(x)
 
+def do_keepalive(cpu, _, x):
+    pass
+
 # ____________________________________________________________
 
 ##def do_force_token(cpu):
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -974,13 +974,13 @@
         any_operation = len(self.metainterp.history.operations) > 0
         jitdriver_sd = self.metainterp.staticdata.jitdrivers_sd[jdindex]
         self.verify_green_args(jitdriver_sd, greenboxes)
-        self.debug_merge_point(jitdriver_sd, jdindex, self.metainterp.in_recursion,
+        self.debug_merge_point(jitdriver_sd, jdindex, self.metainterp.portal_call_depth,
                                greenboxes)
         
         if self.metainterp.seen_loop_header_for_jdindex < 0:
             if not any_operation:
                 return
-            if self.metainterp.in_recursion or not self.metainterp.get_procedure_token(greenboxes, True):
+            if self.metainterp.portal_call_depth or not self.metainterp.get_procedure_token(greenboxes, True):
                 if not jitdriver_sd.no_loop_header:
                     return
             # automatically add a loop_header if there is none
@@ -992,7 +992,7 @@
         self.metainterp.seen_loop_header_for_jdindex = -1
 
         #
-        if not self.metainterp.in_recursion:
+        if not self.metainterp.portal_call_depth:
             assert jitdriver_sd is self.metainterp.jitdriver_sd
             # Set self.pc to point to jit_merge_point instead of just after:
             # if reached_loop_header() raises SwitchToBlackhole, then the
@@ -1028,11 +1028,11 @@
                                     assembler_call=True)
             raise ChangeFrame
 
-    def debug_merge_point(self, jitdriver_sd, jd_index, in_recursion, greenkey):
+    def debug_merge_point(self, jitdriver_sd, jd_index, portal_call_depth, greenkey):
         # debugging: produce a DEBUG_MERGE_POINT operation
         loc = jitdriver_sd.warmstate.get_location_str(greenkey)
         debug_print(loc)
-        args = [ConstInt(jd_index), ConstInt(in_recursion)] + greenkey
+        args = [ConstInt(jd_index), ConstInt(portal_call_depth)] + greenkey
         self.metainterp.history.record(rop.DEBUG_MERGE_POINT, args, None)
 
     @arguments("box", "label")
@@ -1346,12 +1346,16 @@
             resbox = self.metainterp.execute_and_record_varargs(
                 rop.CALL_MAY_FORCE, allboxes, descr=descr)
             self.metainterp.vrefs_after_residual_call()
+            vablebox = None
             if assembler_call:
-                self.metainterp.direct_assembler_call(assembler_call_jd)
+                vablebox = self.metainterp.direct_assembler_call(
+                    assembler_call_jd)
             if resbox is not None:
                 self.make_result_of_lastop(resbox)
             self.metainterp.vable_after_residual_call()
             self.generate_guard(rop.GUARD_NOT_FORCED, None)
+            if vablebox is not None:
+                self.metainterp.history.record(rop.KEEPALIVE, [vablebox], None)
             self.metainterp.handle_possible_exception()
             return resbox
         else:
@@ -1552,7 +1556,7 @@
 # ____________________________________________________________
 
 class MetaInterp(object):
-    in_recursion = 0
+    portal_call_depth = 0
     cancel_count = 0
 
     def __init__(self, staticdata, jitdriver_sd):
@@ -1587,7 +1591,7 @@
 
     def newframe(self, jitcode, greenkey=None):
         if jitcode.is_portal:
-            self.in_recursion += 1
+            self.portal_call_depth += 1
         if greenkey is not None and self.is_main_jitcode(jitcode):
             self.portal_trace_positions.append(
                     (greenkey, len(self.history.operations)))
@@ -1603,7 +1607,7 @@
         frame = self.framestack.pop()
         jitcode = frame.jitcode
         if jitcode.is_portal:
-            self.in_recursion -= 1
+            self.portal_call_depth -= 1
         if frame.greenkey is not None and self.is_main_jitcode(jitcode):
             self.portal_trace_positions.append(
                     (None, len(self.history.operations)))
@@ -1662,17 +1666,17 @@
         raise self.staticdata.ExitFrameWithExceptionRef(self.cpu, excvaluebox.getref_base())
 
     def check_recursion_invariant(self):
-        in_recursion = -1
+        portal_call_depth = -1
         for frame in self.framestack:
             jitcode = frame.jitcode
             assert jitcode.is_portal == len([
                 jd for jd in self.staticdata.jitdrivers_sd
                    if jd.mainjitcode is jitcode])
             if jitcode.is_portal:
-                in_recursion += 1
-        if in_recursion != self.in_recursion:
-            print "in_recursion problem!!!"
-            print in_recursion, self.in_recursion
+                portal_call_depth += 1
+        if portal_call_depth != self.portal_call_depth:
+            print "portal_call_depth problem!!!"
+            print portal_call_depth, self.portal_call_depth
             for frame in self.framestack:
                 jitcode = frame.jitcode
                 if jitcode.is_portal:
@@ -2183,11 +2187,11 @@
 
     def initialize_state_from_start(self, original_boxes):
         # ----- make a new frame -----
-        self.in_recursion = -1 # always one portal around
+        self.portal_call_depth = -1 # always one portal around
         self.framestack = []
         f = self.newframe(self.jitdriver_sd.mainjitcode)
         f.setup_call(original_boxes)
-        assert self.in_recursion == 0
+        assert self.portal_call_depth == 0
         self.virtualref_boxes = []
         self.initialize_withgreenfields(original_boxes)
         self.initialize_virtualizable(original_boxes)
@@ -2198,7 +2202,7 @@
         # otherwise the jit_virtual_refs are left in a dangling state.
         rstack._stack_criticalcode_start()
         try:
-            self.in_recursion = -1 # always one portal around
+            self.portal_call_depth = -1 # always one portal around
             self.history = history.History()
             inputargs_and_holes = self.rebuild_state_after_failure(resumedescr)
             self.history.inputargs = [box for box in inputargs_and_holes if box]
@@ -2478,6 +2482,15 @@
         token = warmrunnerstate.get_assembler_token(greenargs)
         op = op.copy_and_change(rop.CALL_ASSEMBLER, args=args, descr=token)
         self.history.operations.append(op)
+        #
+        # To fix an obscure issue, make sure the vable stays alive
+        # longer than the CALL_ASSEMBLER operation.  We do it by
+        # inserting explicitly an extra KEEPALIVE operation.
+        jd = token.outermost_jitdriver_sd
+        if jd.index_of_virtualizable >= 0:
+            return args[jd.index_of_virtualizable]
+        else:
+            return None
 
 # ____________________________________________________________
 
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -503,6 +503,7 @@
     'COPYUNICODECONTENT/5',
     'QUASIIMMUT_FIELD/1d',    # [objptr], descr=SlowMutateDescr
     'RECORD_KNOWN_CLASS/2',   # [objptr, clsptr]
+    'KEEPALIVE/1',
 
     '_CANRAISE_FIRST', # ----- start of can_raise operations -----
     '_CALL_FIRST',
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -322,6 +322,17 @@
         res = self.interp_operations(f, [42])
         assert res == ord(u"?")
 
+    def test_char_in_constant_string(self):
+        def g(string):
+            return '\x00' in string
+        def f():
+            if g('abcdef'): return -60
+            if not g('abc\x00ef'): return -61
+            return 42
+        res = self.interp_operations(f, [])
+        assert res == 42
+        self.check_operations_history({'finish': 1})   # nothing else
+
     def test_residual_call(self):
         @dont_look_inside
         def externfn(x, y):
diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py
--- a/pypy/module/bz2/interp_bz2.py
+++ b/pypy/module/bz2/interp_bz2.py
@@ -328,7 +328,7 @@
     if basemode == "a":
         raise OperationError(space.w_ValueError,
                              space.wrap("cannot append to bz2 file"))
-    stream = open_path_helper(space.str_w(w_path), os_flags, False)
+    stream = open_path_helper(space.str0_w(w_path), os_flags, False)
     if reading:
         bz2stream = ReadBZ2Filter(space, stream, buffering)
         buffering = 0     # by construction, the ReadBZ2Filter acts like
diff --git a/pypy/module/cpyext/include/pythonrun.h b/pypy/module/cpyext/include/pythonrun.h
--- a/pypy/module/cpyext/include/pythonrun.h
+++ b/pypy/module/cpyext/include/pythonrun.h
@@ -13,6 +13,7 @@
 
 #define Py_FrozenFlag 0
 #define Py_VerboseFlag 0
+#define Py_DebugFlag 1
 
 typedef struct {
     int cf_flags;  /* bitmask of CO_xxx flags relevant to future */
diff --git a/pypy/module/gc/interp_gc.py b/pypy/module/gc/interp_gc.py
--- a/pypy/module/gc/interp_gc.py
+++ b/pypy/module/gc/interp_gc.py
@@ -49,7 +49,7 @@
 
 # ____________________________________________________________
 
- at unwrap_spec(filename=str)
+ at unwrap_spec(filename='str0')
 def dump_heap_stats(space, filename):
     tb = rgc._heap_stats()
     if not tb:
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -138,7 +138,7 @@
     ctxt_package = None
     if ctxt_w_package is not None and ctxt_w_package is not space.w_None:
         try:
-            ctxt_package = space.str_w(ctxt_w_package)
+            ctxt_package = space.str0_w(ctxt_w_package)
         except OperationError, e:
             if not e.match(space, space.w_TypeError):
                 raise
@@ -187,7 +187,7 @@
         ctxt_name = None
         if ctxt_w_name is not None:
             try:
-                ctxt_name = space.str_w(ctxt_w_name)
+                ctxt_name = space.str0_w(ctxt_w_name)
             except OperationError, e:
                 if not e.match(space, space.w_TypeError):
                     raise
@@ -230,7 +230,7 @@
     return rel_modulename, rel_level
 
 
- at unwrap_spec(name=str, level=int)
+ at unwrap_spec(name='str0', level=int)
 def importhook(space, name, w_globals=None,
                w_locals=None, w_fromlist=None, level=-1):
     modulename = name
@@ -377,8 +377,8 @@
                     fromlist_w = space.fixedview(w_all)
             for w_name in fromlist_w:
                 if try_getattr(space, w_mod, w_name) is None:
-                    load_part(space, w_path, prefix, space.str_w(w_name), w_mod,
-                              tentative=1)
+                    load_part(space, w_path, prefix, space.str0_w(w_name),
+                              w_mod, tentative=1)
         return w_mod
     else:
         return first
@@ -432,7 +432,7 @@
     def __init__(self, space):
         pass
 
-    @unwrap_spec(path=str)
+    @unwrap_spec(path='str0')
     def descr_init(self, space, path):
         if not path:
             raise OperationError(space.w_ImportError, space.wrap(
@@ -513,7 +513,7 @@
                 if w_loader:
                     return FindInfo.fromLoader(w_loader)
 
-            path = space.str_w(w_pathitem)
+            path = space.str0_w(w_pathitem)
             filepart = os.path.join(path, partname)
             if os.path.isdir(filepart) and case_ok(filepart):
                 initfile = os.path.join(filepart, '__init__')
@@ -671,7 +671,7 @@
             space.wrap("reload() argument must be module"))
 
     w_modulename = space.getattr(w_module, space.wrap("__name__"))
-    modulename = space.str_w(w_modulename)
+    modulename = space.str0_w(w_modulename)
     if not space.is_w(check_sys_modules(space, w_modulename), w_module):
         raise operationerrfmt(
             space.w_ImportError,
diff --git a/pypy/module/imp/interp_imp.py b/pypy/module/imp/interp_imp.py
--- a/pypy/module/imp/interp_imp.py
+++ b/pypy/module/imp/interp_imp.py
@@ -44,7 +44,7 @@
         return space.interp_w(W_File, w_file).stream
 
 def find_module(space, w_name, w_path=None):
-    name = space.str_w(w_name)
+    name = space.str0_w(w_name)
     if space.is_w(w_path, space.w_None):
         w_path = None
 
@@ -75,7 +75,7 @@
 def load_module(space, w_name, w_file, w_filename, w_info):
     w_suffix, w_filemode, w_modtype = space.unpackiterable(w_info)
 
-    filename = space.str_w(w_filename)
+    filename = space.str0_w(w_filename)
     filemode = space.str_w(w_filemode)
     if space.is_w(w_file, space.w_None):
         stream = None
@@ -92,7 +92,7 @@
         space, w_name, find_info, reuse=True)
 
 def load_source(space, w_modulename, w_filename, w_file=None):
-    filename = space.str_w(w_filename)
+    filename = space.str0_w(w_filename)
 
     stream = get_file(space, w_file, filename, 'U')
 
@@ -105,7 +105,7 @@
         stream.close()
     return w_mod
 
- at unwrap_spec(filename=str)
+ at unwrap_spec(filename='str0')
 def _run_compiled_module(space, w_modulename, filename, w_file, w_module):
     # the function 'imp._run_compiled_module' is a pypy-only extension
     stream = get_file(space, w_file, filename, 'rb')
@@ -119,7 +119,7 @@
     if space.is_w(w_file, space.w_None):
         stream.close()
 
- at unwrap_spec(filename=str)
+ at unwrap_spec(filename='str0')
 def load_compiled(space, w_modulename, filename, w_file=None):
     w_mod = space.wrap(Module(space, w_modulename))
     importing._prepare_module(space, w_mod, filename, None)
@@ -138,7 +138,7 @@
     return space.wrap(Module(space, w_name, add_package=False))
 
 def init_builtin(space, w_name):
-    name = space.str_w(w_name)
+    name = space.str0_w(w_name)
     if name not in space.builtin_modules:
         return
     if space.finditem(space.sys.get('modules'), w_name) is not None:
@@ -151,7 +151,7 @@
     return None
 
 def is_builtin(space, w_name):
-    name = space.str_w(w_name)
+    name = space.str0_w(w_name)
     if name not in space.builtin_modules:
         return space.wrap(0)
     if space.finditem(space.sys.get('modules'), w_name) is not None:
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -98,6 +98,10 @@
         ('bitwise_not', 'invert'),
         ('isnan', 'isnan'),
         ('isinf', 'isinf'),
+        ('logical_and', 'logical_and'),
+        ('logical_xor', 'logical_xor'),
+        ('logical_not', 'logical_not'),
+        ('logical_or', 'logical_or'),
     ]:
         interpleveldefs[exposed] = "interp_ufuncs.get(space).%s" % impl
 
diff --git a/pypy/module/micronumpy/interp_iter.py b/pypy/module/micronumpy/interp_iter.py
--- a/pypy/module/micronumpy/interp_iter.py
+++ b/pypy/module/micronumpy/interp_iter.py
@@ -86,8 +86,9 @@
 
     def apply_transformations(self, arr, transformations):
         v = self
-        for transform in transformations:
-            v = v.transform(arr, transform)
+        if transformations is not None:
+            for transform in transformations:
+                v = v.transform(arr, transform)
         return v
 
     def transform(self, arr, t):
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,7 +3,7 @@
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.typedef import TypeDef, GetSetProperty
 from pypy.module.micronumpy import (interp_ufuncs, interp_dtype, interp_boxes,
-    signature, support)
+    signature, support, loop)
 from pypy.module.micronumpy.strides import (calculate_slice_strides,
     shape_agreement, find_shape_and_elems, get_shape_from_iterable,
     calc_new_strides, to_coords)
@@ -12,39 +12,11 @@
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.rlib.rstring import StringBuilder
-from pypy.module.micronumpy.interp_iter import (ArrayIterator, OneDimIterator,
+from pypy.module.micronumpy.interp_iter import (ArrayIterator,
     SkipLastAxisIterator, Chunk, ViewIterator)
 from pypy.module.micronumpy.appbridge import get_appbridge_cache
 
 
-numpy_driver = jit.JitDriver(
-    greens=['shapelen', 'sig'],
-    virtualizables=['frame'],
-    reds=['result_size', 'frame', 'ri', 'self', 'result'],
-    get_printable_location=signature.new_printable_location('numpy'),
-    name='numpy',
-)
-all_driver = jit.JitDriver(
-    greens=['shapelen', 'sig'],
-    virtualizables=['frame'],
-    reds=['frame', 'self', 'dtype'],
-    get_printable_location=signature.new_printable_location('all'),
-    name='numpy_all',
-)
-any_driver = jit.JitDriver(
-    greens=['shapelen', 'sig'],
-    virtualizables=['frame'],
-    reds=['frame', 'self', 'dtype'],
-    get_printable_location=signature.new_printable_location('any'),
-    name='numpy_any',
-)
-slice_driver = jit.JitDriver(
-    greens=['shapelen', 'sig'],
-    virtualizables=['frame'],
-    reds=['self', 'frame', 'arr'],
-    get_printable_location=signature.new_printable_location('slice'),
-    name='numpy_slice',
-)
 count_driver = jit.JitDriver(
     greens=['shapelen'],
     virtualizables=['frame'],
@@ -173,6 +145,8 @@
     descr_prod = _reduce_ufunc_impl("multiply", True)
     descr_max = _reduce_ufunc_impl("maximum")
     descr_min = _reduce_ufunc_impl("minimum")
+    descr_all = _reduce_ufunc_impl('logical_and')
+    descr_any = _reduce_ufunc_impl('logical_or')
 
     def _reduce_argmax_argmin_impl(op_name):
         reduce_driver = jit.JitDriver(
@@ -212,40 +186,6 @@
             return space.wrap(loop(self))
         return func_with_new_name(impl, "reduce_arg%s_impl" % op_name)
 
-    def _all(self):
-        dtype = self.find_dtype()
-        sig = self.find_sig()
-        frame = sig.create_frame(self)
-        shapelen = len(self.shape)
-        while not frame.done():
-            all_driver.jit_merge_point(sig=sig,
-                                       shapelen=shapelen, self=self,
-                                       dtype=dtype, frame=frame)
-            if not dtype.itemtype.bool(sig.eval(frame, self)):
-                return False
-            frame.next(shapelen)
-        return True
-
-    def descr_all(self, space):
-        return space.wrap(self._all())
-
-    def _any(self):
-        dtype = self.find_dtype()
-        sig = self.find_sig()
-        frame = sig.create_frame(self)
-        shapelen = len(self.shape)
-        while not frame.done():
-            any_driver.jit_merge_point(sig=sig, frame=frame,
-                                       shapelen=shapelen, self=self,
-                                       dtype=dtype)
-            if dtype.itemtype.bool(sig.eval(frame, self)):
-                return True
-            frame.next(shapelen)
-        return False
-
-    def descr_any(self, space):
-        return space.wrap(self._any())
-
     descr_argmax = _reduce_argmax_argmin_impl("max")
     descr_argmin = _reduce_argmax_argmin_impl("min")
 
@@ -267,7 +207,7 @@
         out_size = support.product(out_shape)
         result = W_NDimArray(out_size, out_shape, dtype)
         # This is the place to add fpypy and blas
-        return multidim_dot(space, self.get_concrete(), 
+        return multidim_dot(space, self.get_concrete(),
                             other.get_concrete(), result, dtype,
                             other_critical_dim)
 
@@ -280,6 +220,12 @@
     def descr_get_ndim(self, space):
         return space.wrap(len(self.shape))
 
+    def descr_get_itemsize(self, space):
+        return space.wrap(self.find_dtype().itemtype.get_element_size())
+
+    def descr_get_nbytes(self, space):
+        return space.wrap(self.size * self.find_dtype().itemtype.get_element_size())
+
     @jit.unroll_safe
     def descr_get_shape(self, space):
         return space.newtuple([space.wrap(i) for i in self.shape])
@@ -507,7 +453,7 @@
             w_shape = space.newtuple(args_w)
         new_shape = get_shape_from_iterable(space, self.size, w_shape)
         return self.reshape(space, new_shape)
-        
+
     def reshape(self, space, new_shape):
         concrete = self.get_concrete()
         # Since we got to here, prod(new_shape) == self.size
@@ -679,6 +625,9 @@
         raise OperationError(space.w_NotImplementedError, space.wrap(
             "non-int arg not supported"))
 
+    def compute_first_step(self, sig, frame):
+        pass
+
 def convert_to_array(space, w_obj):
     if isinstance(w_obj, BaseArray):
         return w_obj
@@ -744,22 +693,9 @@
         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.setitem(ri.offset, sig.eval(frame, self))
-            frame.next(shapelen)
-            ri = ri.next(shapelen)
-        return result
+        ra = ResultArray(self, self.size, self.shape, self.res_dtype)
+        loop.compute(ra)
+        return ra.left
 
     def force_if_needed(self):
         if self.forced_result is None:
@@ -817,7 +753,8 @@
     def create_sig(self):
         if self.forced_result is not None:
             return self.forced_result.create_sig()
-        return signature.Call1(self.ufunc, self.name, self.values.create_sig())
+        return signature.Call1(self.ufunc, self.name, self.calc_dtype,
+                               self.values.create_sig())
 
 class Call2(VirtualArray):
     """
@@ -858,6 +795,66 @@
         return signature.Call2(self.ufunc, self.name, self.calc_dtype,
                                self.left.create_sig(), self.right.create_sig())
 
+class ResultArray(Call2):
+    def __init__(self, child, size, shape, dtype, res=None, order='C'):
+        if res is None:
+            res = W_NDimArray(size, shape, dtype, order)
+        Call2.__init__(self, None, 'assign', shape, dtype, dtype, res, child)
+
+    def create_sig(self):
+        return signature.ResultSignature(self.res_dtype, self.left.create_sig(),
+                                         self.right.create_sig())
+
+def done_if_true(dtype, val):
+    return dtype.itemtype.bool(val)
+
+def done_if_false(dtype, val):
+    return not dtype.itemtype.bool(val)
+
+class ReduceArray(Call2):
+    def __init__(self, func, name, identity, child, dtype):
+        self.identity = identity
+        Call2.__init__(self, func, name, [1], dtype, dtype, None, child)
+
+    def compute_first_step(self, sig, frame):
+        assert isinstance(sig, signature.ReduceSignature)
+        if self.identity is None:
+            frame.cur_value = sig.right.eval(frame, self.right).convert_to(
+                self.calc_dtype)
+            frame.next(len(self.right.shape))
+        else:
+            frame.cur_value = self.identity.convert_to(self.calc_dtype)
+
+    def create_sig(self):
+        if self.name == 'logical_and':
+            done_func = done_if_false
+        elif self.name == 'logical_or':
+            done_func = done_if_true
+        else:
+            done_func = None
+        return signature.ReduceSignature(self.ufunc, self.name, self.res_dtype,
+                                 signature.ScalarSignature(self.res_dtype),
+                                         self.right.create_sig(), done_func)
+
+class AxisReduce(Call2):
+    _immutable_fields_ = ['left', 'right']
+
+    def __init__(self, ufunc, name, identity, shape, dtype, left, right, dim):
+        Call2.__init__(self, ufunc, name, shape, dtype, dtype,
+                       left, right)
+        self.dim = dim
+        self.identity = identity
+
+    def compute_first_step(self, sig, frame):
+        if self.identity is not None:
+            frame.identity = self.identity.convert_to(self.calc_dtype)
+
+    def create_sig(self):
+        return signature.AxisReduceSignature(self.ufunc, self.name,
+                                             self.res_dtype,
+                                 signature.ScalarSignature(self.res_dtype),
+                                             self.right.create_sig())
+
 class SliceArray(Call2):
     def __init__(self, shape, dtype, left, right, no_broadcast=False):
         self.no_broadcast = no_broadcast
@@ -876,18 +873,6 @@
                                             self.calc_dtype,
                                             lsig, rsig)
 
-class AxisReduce(Call2):
-    """ NOTE: this is only used as a container, you should never
-    encounter such things in the wild. Remove this comment
-    when we'll make AxisReduce lazy
-    """
-    _immutable_fields_ = ['left', 'right']
-
-    def __init__(self, ufunc, name, shape, dtype, left, right, dim):
-        Call2.__init__(self, ufunc, name, shape, dtype, dtype,
-                       left, right)
-        self.dim = dim
-
 class ConcreteArray(BaseArray):
     """ An array that have actual storage, whether owned or not
     """
@@ -973,7 +958,7 @@
             self._fast_setslice(space, w_value)
         else:
             arr = SliceArray(self.shape, self.dtype, self, w_value)
-            self._sliceloop(arr)
+            loop.compute(arr)
 
     def _fast_setslice(self, space, w_value):
         assert isinstance(w_value, ConcreteArray)
@@ -997,17 +982,6 @@
                 source.next()
                 dest.next()
 
-    def _sliceloop(self, arr):
-        sig = arr.find_sig()
-        frame = sig.create_frame(arr)
-        shapelen = len(self.shape)
-        while not frame.done():
-            slice_driver.jit_merge_point(sig=sig, frame=frame, self=self,
-                                         arr=arr,
-                                         shapelen=shapelen)
-            sig.eval(frame, arr)
-            frame.next(shapelen)
-
     def copy(self, space):
         array = W_NDimArray(self.size, self.shape[:], self.dtype, self.order)
         array.setslice(space, self)
@@ -1033,9 +1007,9 @@
                            parent.order, parent)
         self.start = start
 
-    def create_iter(self):
+    def create_iter(self, transforms=None):
         return ViewIterator(self.start, self.strides, self.backstrides,
-                            self.shape)
+                            self.shape).apply_transformations(self, transforms)
 
     def setshape(self, space, new_shape):
         if len(self.shape) < 1:
@@ -1084,8 +1058,8 @@
         self.shape = new_shape
         self.calc_strides(new_shape)
 
-    def create_iter(self):
-        return ArrayIterator(self.size)
+    def create_iter(self, transforms=None):
+        return ArrayIterator(self.size).apply_transformations(self, transforms)
 
     def create_sig(self):
         return signature.ArraySignature(self.dtype)
@@ -1289,11 +1263,13 @@
                            BaseArray.descr_set_shape),
     size = GetSetProperty(BaseArray.descr_get_size),
     ndim = GetSetProperty(BaseArray.descr_get_ndim),
-    item = interp2app(BaseArray.descr_item),
+    itemsize = GetSetProperty(BaseArray.descr_get_itemsize),
+    nbytes = GetSetProperty(BaseArray.descr_get_nbytes),
 
     T = GetSetProperty(BaseArray.descr_get_transpose),
     flat = GetSetProperty(BaseArray.descr_get_flatiter),
     ravel = interp2app(BaseArray.descr_ravel),
+    item = interp2app(BaseArray.descr_item),
 
     mean = interp2app(BaseArray.descr_mean),
     sum = interp2app(BaseArray.descr_sum),
@@ -1345,12 +1321,15 @@
     def descr_iter(self):
         return self
 
+    def descr_len(self, space):
+        return space.wrap(self.size)
+
     def descr_index(self, space):
         return space.wrap(self.index)
 
     def descr_coords(self, space):
-        coords, step, lngth = to_coords(space, self.base.shape, 
-                            self.base.size, self.base.order, 
+        coords, step, lngth = to_coords(space, self.base.shape,
+                            self.base.size, self.base.order,
                             space.wrap(self.index))
         return space.newtuple([space.wrap(c) for c in coords])
 
@@ -1380,7 +1359,7 @@
                                              step=step,
                                              res=res,
                                              ri=ri,
-                                            ) 
+                                            )
             w_val = base.getitem(basei.offset)
             res.setitem(ri.offset,w_val)
             basei = basei.next_skip_x(shapelen, step)
@@ -1408,7 +1387,7 @@
                                              arr=arr,
                                              ai=ai,
                                              lngth=lngth,
-                                            ) 
+                                            )
             v = arr.getitem(ai).convert_to(base.dtype)
             base.setitem(basei.offset, v)
             # need to repeat input values until all assignments are done
@@ -1419,22 +1398,29 @@
     def create_sig(self):
         return signature.FlatSignature(self.base.dtype)
 
+    def create_iter(self, transforms=None):
+        return ViewIterator(self.base.start, self.base.strides,
+                    self.base.backstrides,
+                    self.base.shape).apply_transformations(self.base,
+                                                           transforms)
+
     def descr_base(self, space):
         return space.wrap(self.base)
 
 W_FlatIterator.typedef = TypeDef(
     'flatiter',
-    #__array__  = #MISSING
     __iter__ = interp2app(W_FlatIterator.descr_iter),
+    __len__ = interp2app(W_FlatIterator.descr_len),
     __getitem__ = interp2app(W_FlatIterator.descr_getitem),
     __setitem__ = interp2app(W_FlatIterator.descr_setitem),
+
     __eq__ = interp2app(BaseArray.descr_eq),
     __ne__ = interp2app(BaseArray.descr_ne),
     __lt__ = interp2app(BaseArray.descr_lt),
     __le__ = interp2app(BaseArray.descr_le),
     __gt__ = interp2app(BaseArray.descr_gt),
     __ge__ = interp2app(BaseArray.descr_ge),
-    #__sizeof__ #MISSING
+
     base = GetSetProperty(W_FlatIterator.descr_base),
     index = GetSetProperty(W_FlatIterator.descr_index),
     coords = GetSetProperty(W_FlatIterator.descr_coords),
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,31 +2,10 @@
 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
 from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
-from pypy.module.micronumpy import interp_boxes, interp_dtype, support
-from pypy.module.micronumpy.signature import (ReduceSignature, find_sig,
-    new_printable_location, AxisReduceSignature, ScalarSignature)
-from pypy.rlib import jit
+from pypy.module.micronumpy import interp_boxes, interp_dtype, support, loop
 from pypy.rlib.rarithmetic import LONG_BIT
 from pypy.tool.sourcetools import func_with_new_name
 
-
-reduce_driver = jit.JitDriver(
-    greens=['shapelen', "sig"],
-    virtualizables=["frame"],
-    reds=["frame", "self", "dtype", "value", "obj"],
-    get_printable_location=new_printable_location('reduce'),
-    name='numpy_reduce',
-)
-
-axisreduce_driver = jit.JitDriver(
-    greens=['shapelen', 'sig'],
-    virtualizables=['frame'],
-    reds=['self','arr', 'identity', 'frame'],
-    name='numpy_axisreduce',
-    get_printable_location=new_printable_location('axisreduce'),
-)
-
-
 class W_Ufunc(Wrappable):
     _attrs_ = ["name", "promote_to_float", "promote_bools", "identity"]
     _immutable_fields_ = ["promote_to_float", "promote_bools", "name"]
@@ -140,7 +119,7 @@
     def reduce(self, space, w_obj, multidim, promote_to_largest, dim,
                keepdims=False):
         from pypy.module.micronumpy.interp_numarray import convert_to_array, \
-                                                           Scalar
+                                                           Scalar, ReduceArray
         if self.argcount != 2:
             raise OperationError(space.w_ValueError, space.wrap("reduce only "
                 "supported for binary functions"))
@@ -151,96 +130,37 @@
         if isinstance(obj, Scalar):
             raise OperationError(space.w_TypeError, space.wrap("cannot reduce "
                 "on a scalar"))
-
         size = obj.size
-        dtype = find_unaryop_result_dtype(
-            space, obj.find_dtype(),
-            promote_to_float=self.promote_to_float,
-            promote_to_largest=promote_to_largest,
-            promote_bools=True
-        )
+        if self.comparison_func:
+            dtype = interp_dtype.get_dtype_cache(space).w_booldtype
+        else:
+            dtype = find_unaryop_result_dtype(
+                space, obj.find_dtype(),
+                promote_to_float=self.promote_to_float,
+                promote_to_largest=promote_to_largest,
+                promote_bools=True
+            )
         shapelen = len(obj.shape)
         if self.identity is None and size == 0:
             raise operationerrfmt(space.w_ValueError, "zero-size array to "
                     "%s.reduce without identity", self.name)
         if shapelen > 1 and dim >= 0:
-            res = self.do_axis_reduce(obj, dtype, dim, keepdims)
-            return space.wrap(res)
-        scalarsig = ScalarSignature(dtype)
-        sig = find_sig(ReduceSignature(self.func, self.name, dtype,
-                                       scalarsig,
-                                       obj.create_sig()), obj)
-        frame = sig.create_frame(obj)
-        if self.identity is None:
-            value = sig.eval(frame, obj).convert_to(dtype)
-            frame.next(shapelen)
-        else:
-            value = self.identity.convert_to(dtype)
-        return self.reduce_loop(shapelen, sig, frame, value, obj, dtype)
+            return self.do_axis_reduce(obj, dtype, dim, keepdims)
+        arr = ReduceArray(self.func, self.name, self.identity, obj, dtype)
+        return loop.compute(arr)
 
     def do_axis_reduce(self, obj, dtype, dim, keepdims):
         from pypy.module.micronumpy.interp_numarray import AxisReduce,\
              W_NDimArray
-
         if keepdims:
             shape = obj.shape[:dim] + [1] + obj.shape[dim + 1:]
         else:
             shape = obj.shape[:dim] + obj.shape[dim + 1:]
         result = W_NDimArray(support.product(shape), shape, dtype)
-        rightsig = obj.create_sig()
-        # note - this is just a wrapper so signature can fetch
-        #        both left and right, nothing more, especially
-        #        this is not a true virtual array, because shapes
-        #        don't quite match
-        arr = AxisReduce(self.func, self.name, obj.shape, dtype,
+        arr = AxisReduce(self.func, self.name, self.identity, obj.shape, dtype,
                          result, obj, dim)
-        scalarsig = ScalarSignature(dtype)
-        sig = find_sig(AxisReduceSignature(self.func, self.name, dtype,
-                                           scalarsig, rightsig), arr)
-        assert isinstance(sig, AxisReduceSignature)
-        frame = sig.create_frame(arr)
-        shapelen = len(obj.shape)
-        if self.identity is not None:
-            identity = self.identity.convert_to(dtype)
-        else:
-            identity = None
-        self.reduce_axis_loop(frame, sig, shapelen, arr, identity)
-        return result
-
-    def reduce_axis_loop(self, frame, sig, shapelen, arr, identity):
-        # note - we can be advanterous here, depending on the exact field
-        # layout. For now let's say we iterate the original way and
-        # simply follow the original iteration order
-        while not frame.done():
-            axisreduce_driver.jit_merge_point(frame=frame, self=self,
-                                              sig=sig,
-                                              identity=identity,
-                                              shapelen=shapelen, arr=arr)
-            iterator = frame.get_final_iter()
-            v = sig.eval(frame, arr).convert_to(sig.calc_dtype)
-            if iterator.first_line:
-                if identity is not None:
-                    value = self.func(sig.calc_dtype, identity, v)
-                else:
-                    value = v
-            else:
-                cur = arr.left.getitem(iterator.offset)
-                value = self.func(sig.calc_dtype, cur, v)
-            arr.left.setitem(iterator.offset, value)
-            frame.next(shapelen)
-
-    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, frame=frame,
-                                          dtype=dtype)
-            assert isinstance(sig, ReduceSignature)
-            value = sig.binfunc(dtype, value,
-                                sig.eval(frame, obj).convert_to(dtype))
-            frame.next(shapelen)
-        return value
-
+        loop.compute(arr)
+        return arr.left
 
 class W_Ufunc1(W_Ufunc):
     argcount = 1
@@ -312,7 +232,6 @@
                 w_lhs.value.convert_to(calc_dtype),
                 w_rhs.value.convert_to(calc_dtype)
             ))
-
         new_shape = shape_agreement(space, w_lhs.shape, w_rhs.shape)
         w_res = Call2(self.func, self.name,
                       new_shape, calc_dtype,
@@ -482,6 +401,13 @@
             ("isnan", "isnan", 1, {"bool_result": True}),
             ("isinf", "isinf", 1, {"bool_result": True}),
 
+            ('logical_and', 'logical_and', 2, {'comparison_func': True,
+                                               'identity': 1}),
+            ('logical_or', 'logical_or', 2, {'comparison_func': True,
+                                             'identity': 0}),
+            ('logical_xor', 'logical_xor', 2, {'comparison_func': True}),
+            ('logical_not', 'logical_not', 1, {'bool_result': True}),
+
             ("maximum", "max", 2),
             ("minimum", "min", 2),
 
diff --git a/pypy/module/micronumpy/loop.py b/pypy/module/micronumpy/loop.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/loop.py
@@ -0,0 +1,83 @@
+
+""" This file is the main run loop as well as evaluation loops for various
+signatures
+"""
+
+from pypy.rlib.jit import JitDriver, hint, unroll_safe, promote
+from pypy.module.micronumpy.interp_iter import ConstantIterator
+
+class NumpyEvalFrame(object):
+    _virtualizable2_ = ['iterators[*]', 'final_iter', 'arraylist[*]',
+                        'value', 'identity', 'cur_value']
+
+    @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
+        self.cur_value = None
+        self.identity = None
+
+    def done(self):
+        final_iter = promote(self.final_iter)
+        if final_iter < 0:
+            assert False
+        return self.iterators[final_iter].done()
+
+    @unroll_safe
+    def next(self, shapelen):
+        for i in range(len(self.iterators)):
+            self.iterators[i] = self.iterators[i].next(shapelen)
+
+    @unroll_safe
+    def next_from_second(self, shapelen):
+        """ Don't increase the first iterator
+        """
+        for i in range(1, len(self.iterators)):
+            self.iterators[i] = self.iterators[i].next(shapelen)
+
+    def next_first(self, shapelen):
+        self.iterators[0] = self.iterators[0].next(shapelen)
+
+    def get_final_iter(self):
+        final_iter = promote(self.final_iter)
+        if final_iter < 0:
+            assert False
+        return self.iterators[final_iter]
+
+def get_printable_location(shapelen, sig):
+    return 'numpy ' + sig.debug_repr() + ' [%d dims]' % (shapelen,)
+
+numpy_driver = JitDriver(
+    greens=['shapelen', 'sig'],
+    virtualizables=['frame'],
+    reds=['frame', 'arr'],
+    get_printable_location=get_printable_location,
+    name='numpy',
+)
+
+class ComputationDone(Exception):
+    def __init__(self, value):
+        self.value = value
+
+def compute(arr):
+    sig = arr.find_sig()
+    shapelen = len(arr.shape)
+    frame = sig.create_frame(arr)
+    try:
+        while not frame.done():
+            numpy_driver.jit_merge_point(sig=sig,
+                                         shapelen=shapelen,
+                                         frame=frame, arr=arr)
+            sig.eval(frame, arr)
+            frame.next(shapelen)
+        return frame.cur_value
+    except ComputationDone, e:
+        return e.value
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,9 +1,9 @@
 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, \
-     ConstantIterator, AxisIterator, ViewTransform,\
-     BroadcastTransform
-from pypy.rlib.jit import hint, unroll_safe, promote
+from pypy.module.micronumpy.interp_iter import ConstantIterator, AxisIterator,\
+     ViewTransform, BroadcastTransform
+from pypy.tool.pairtype import extendabletype
+from pypy.module.micronumpy.loop import ComputationDone
 
 """ Signature specifies both the numpy expression that has been constructed
 and the assembler to be compiled. This is a very important observation -
@@ -54,50 +54,6 @@
         known_sigs[sig] = sig
         return sig
 
-class NumpyEvalFrame(object):
-    _virtualizable2_ = ['iterators[*]', 'final_iter', 'arraylist[*]',
-                        'value', 'identity']
-
-    @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:
-            assert False
-        return self.iterators[final_iter].done()
-
-    @unroll_safe
-    def next(self, shapelen):
-        for i in range(len(self.iterators)):
-            self.iterators[i] = self.iterators[i].next(shapelen)
-
-    @unroll_safe
-    def next_from_second(self, shapelen):
-        """ Don't increase the first iterator
-        """
-        for i in range(1, len(self.iterators)):
-            self.iterators[i] = self.iterators[i].next(shapelen)
-
-    def next_first(self, shapelen):
-        self.iterators[0] = self.iterators[0].next(shapelen)
-
-    def get_final_iter(self):
-        final_iter = promote(self.final_iter)
-        if final_iter < 0:
-            assert False
-        return self.iterators[final_iter]
-
 def _add_ptr_to_cache(ptr, cache):
     i = 0
     for p in cache:
@@ -113,6 +69,8 @@
     return r_dict(sigeq_no_numbering, sighash)
 
 class Signature(object):
+    __metaclass_ = extendabletype
+    
     _attrs_ = ['iter_no', 'array_no']
     _immutable_fields_ = ['iter_no', 'array_no']
 
@@ -138,11 +96,15 @@
         self.iter_no = no
 
     def create_frame(self, arr):
+        from pypy.module.micronumpy.loop import NumpyEvalFrame
+        
         iterlist = []
         arraylist = []
         self._create_iter(iterlist, arraylist, arr, [])
-        return NumpyEvalFrame(iterlist, arraylist)
-
+        f = NumpyEvalFrame(iterlist, arraylist)
+        # hook for cur_value being used by reduce
+        arr.compute_first_step(self, f)
+        return f
 
 class ConcreteSignature(Signature):
     _immutable_fields_ = ['dtype']
@@ -182,13 +144,10 @@
         assert isinstance(concr, ConcreteArray)
         storage = concr.storage
         if self.iter_no >= len(iterlist):
-            iterlist.append(self.allocate_iter(concr, transforms))
+            iterlist.append(concr.create_iter(transforms))
         if self.array_no >= len(arraylist):
             arraylist.append(storage)
 
-    def allocate_iter(self, arr, transforms):
-        return ArrayIterator(arr.size).apply_transformations(arr, transforms)
-
     def eval(self, frame, arr):
         iter = frame.iterators[self.iter_no]
         return self.dtype.getitem(frame.arrays[self.array_no], iter.offset)
@@ -220,22 +179,10 @@
         allnumbers.append(no)
         self.iter_no = no
 
-    def allocate_iter(self, arr, transforms):
-        return ViewIterator(arr.start, arr.strides, arr.backstrides,
-                            arr.shape).apply_transformations(arr, transforms)
-
 class FlatSignature(ViewSignature):
     def debug_repr(self):
         return 'Flat'
 
-    def allocate_iter(self, arr, transforms):
-        from pypy.module.micronumpy.interp_numarray import W_FlatIterator
-        assert isinstance(arr, W_FlatIterator)
-        return ViewIterator(arr.base.start, arr.base.strides, 
-                    arr.base.backstrides,
-                    arr.base.shape).apply_transformations(arr.base,
-                                                         transforms)
-
 class VirtualSliceSignature(Signature):
     def __init__(self, child):
         self.child = child
@@ -269,12 +216,13 @@
         return self.child.eval(frame, arr.child)
 
 class Call1(Signature):
-    _immutable_fields_ = ['unfunc', 'name', 'child']
+    _immutable_fields_ = ['unfunc', 'name', 'child', 'dtype']
 
-    def __init__(self, func, name, child):
+    def __init__(self, func, name, dtype, child):
         self.unfunc = func
         self.child = child
         self.name = name
+        self.dtype = dtype
 
     def hash(self):
         return compute_hash(self.name) ^ intmask(self.child.hash() << 1)
@@ -359,6 +307,17 @@
         return 'Call2(%s, %s, %s)' % (self.name, self.left.debug_repr(),
                                       self.right.debug_repr())
 
+class ResultSignature(Call2):
+    def __init__(self, dtype, left, right):
+        Call2.__init__(self, None, 'assign', dtype, left, right)
+
+    def eval(self, frame, arr):
+        from pypy.module.micronumpy.interp_numarray import ResultArray
+
+        assert isinstance(arr, ResultArray)
+        offset = frame.get_final_iter().offset
+        arr.left.setitem(offset, self.right.eval(frame, arr.right))
+
 class BroadcastLeft(Call2):
     def _invent_numbering(self, cache, allnumbers):
         self.left._invent_numbering(new_cache(), allnumbers)
@@ -400,20 +359,24 @@
         self.right._create_iter(iterlist, arraylist, arr.right, rtransforms)
 
 class ReduceSignature(Call2):
-    def _create_iter(self, iterlist, arraylist, arr, transforms):
-        self.right._create_iter(iterlist, arraylist, arr, transforms)
-
-    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)
-
+    _immutable_fields_ = ['binfunc', 'name', 'calc_dtype',
+                          'left', 'right', 'done_func']
+    
+    def __init__(self, func, name, calc_dtype, left, right,
+                 done_func):
+        Call2.__init__(self, func, name, calc_dtype, left, right)
+        self.done_func = done_func
+        
     def eval(self, frame, arr):
-        return self.right.eval(frame, arr)
+        from pypy.module.micronumpy.interp_numarray import ReduceArray
+        assert isinstance(arr, ReduceArray)
+        rval = self.right.eval(frame, arr.right).convert_to(self.calc_dtype)
+        if self.done_func is not None and self.done_func(self.calc_dtype, rval):
+            raise ComputationDone(rval)
+        frame.cur_value = self.binfunc(self.calc_dtype, frame.cur_value, rval)
 
     def debug_repr(self):
-        return 'ReduceSig(%s, %s)' % (self.name, self.right.debug_repr())
+        return 'ReduceSig(%s)' % (self.name, self.right.debug_repr())
 
 class SliceloopSignature(Call2):
     def eval(self, frame, arr):
@@ -467,7 +430,17 @@
         from pypy.module.micronumpy.interp_numarray import AxisReduce
 
         assert isinstance(arr, AxisReduce)
-        return self.right.eval(frame, arr.right).convert_to(self.calc_dtype)
+        iterator = frame.get_final_iter()
+        v = self.right.eval(frame, arr.right).convert_to(self.calc_dtype)
+        if iterator.first_line:
+            if frame.identity is not None:
+                value = self.binfunc(self.calc_dtype, frame.identity, v)
+            else:
+                value = v
+        else:
+            cur = arr.left.getitem(iterator.offset)
+            value = self.binfunc(self.calc_dtype, cur, v)
+        arr.left.setitem(iterator.offset, value)
     
     def debug_repr(self):
         return 'AxisReduceSig(%s, %s)' % (self.name, self.right.debug_repr())
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
@@ -1,11 +1,13 @@
 
 import py
+
+from pypy.conftest import gettestobjspace, option
+from pypy.interpreter.error import OperationError
+from pypy.module.micronumpy import signature
+from pypy.module.micronumpy.appbridge import get_appbridge_cache
+from pypy.module.micronumpy.interp_iter import Chunk
+from pypy.module.micronumpy.interp_numarray import W_NDimArray, shape_agreement
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
-from pypy.module.micronumpy.interp_numarray import W_NDimArray, shape_agreement
-from pypy.module.micronumpy.interp_iter import Chunk
-from pypy.module.micronumpy import signature
-from pypy.interpreter.error import OperationError
-from pypy.conftest import gettestobjspace
 
 
 class MockDtype(object):
@@ -173,7 +175,7 @@
         def _to_coords(index, order):
             return to_coords(self.space, [2, 3, 4], 24, order,
                              self.space.wrap(index))[0]
-        
+
         assert _to_coords(0, 'C') == [0, 0, 0]
         assert _to_coords(1, 'C') == [0, 0, 1]
         assert _to_coords(-1, 'C') == [1, 2, 3]
@@ -306,7 +308,7 @@
         from _numpypy import arange
         a = arange(15).reshape(3, 5)
         assert a[1, 3] == 8
-        assert a.T[1, 2] == 11 
+        assert a.T[1, 2] == 11
 
     def test_setitem(self):
         from _numpypy import array
@@ -936,10 +938,9 @@
                    [[86, 302, 518], [110, 390, 670], [134, 478, 822]]]).all()
         c = dot(a, b[:, 2])
         assert (c == [[62, 214, 366], [518, 670, 822]]).all()
-        a = arange(3*4*5*6).reshape((3,4,5,6))
-        b = arange(3*4*5*6)[::-1].reshape((5,4,6,3))
-        assert dot(a, b)[2,3,2,1,2,2] == 499128
-        assert sum(a[2,3,2,:] * b[1,2,:,2]) == 499128
+        a = arange(3*2*6).reshape((3,2,6))
+        b = arange(3*2*6)[::-1].reshape((2,6,3))
+        assert dot(a, b)[2,0,1,2] == 1140
 
     def test_dot_constant(self):
         from _numpypy import array, dot
@@ -1121,14 +1122,14 @@
         f1 = array([0,1])
         f = concatenate((f1, [2], f1, [7]))
         assert (f == [0,1,2,0,1,7]).all()
-        
+
         bad_axis = raises(ValueError, concatenate, (a1,a2), axis=1)
         assert str(bad_axis.value) == "bad axis argument"
-        
+
         concat_zero = raises(ValueError, concatenate, ())
         assert str(concat_zero.value) == \
             "concatenation of zero-length sequences is impossible"
-        
+
         dims_disagree = raises(ValueError, concatenate, (a1, b1), axis=0)
         assert str(dims_disagree.value) == \
             "array dimensions must agree except for axis being concatenated"
@@ -1163,6 +1164,25 @@
         a = array([[1, 2], [3, 4]])
         assert (a.T.flatten() == [1, 3, 2, 4]).all()
 
+    def test_itemsize(self):
+        from _numpypy import ones, dtype, array
+
+        for obj in [float, bool, int]:
+            assert ones(1, dtype=obj).itemsize == dtype(obj).itemsize
+        assert (ones(1) + ones(1)).itemsize == 8
+        assert array(1.0).itemsize == 8
+        assert ones(1)[:].itemsize == 8
+
+    def test_nbytes(self):
+        from _numpypy import array, ones
+
+        assert ones(1).nbytes == 8
+        assert ones((2, 2)).nbytes == 32
+        assert ones((2, 2))[1:,].nbytes == 16
+        assert (ones(1) + ones(1)).nbytes == 8
+        assert array(3.0).nbytes == 8
+
+
 class AppTestMultiDim(BaseNumpyAppTest):
     def test_init(self):
         import _numpypy
@@ -1458,35 +1478,37 @@
         b = a.T.flat
         assert (b == [0,  4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11]).all()
         assert not (b != [0,  4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11]).any()
-        assert ((b >= range(12)) == [True, True, True,False, True, True, 
+        assert ((b >= range(12)) == [True, True, True,False, True, True,
                              False, False, True, False, False, True]).all()
-        assert ((b < range(12)) != [True, True, True,False, True, True, 
+        assert ((b < range(12)) != [True, True, True,False, True, True,
                              False, False, True, False, False, True]).all()
-        assert ((b <= range(12)) != [False, True, True,False, True, True, 
+        assert ((b <= range(12)) != [False, True, True,False, True, True,
                             False, False, True, False, False, False]).all()
-        assert ((b > range(12)) == [False, True, True,False, True, True, 
+        assert ((b > range(12)) == [False, True, True,False, True, True,
                             False, False, True, False, False, False]).all()
     def test_flatiter_view(self):
         from _numpypy import arange
         a = arange(10).reshape(5, 2)
-        #no == yet.
-        # a[::2].flat == [0, 1, 4, 5, 8, 9]
-        isequal = True
-        for y,z in zip(a[::2].flat, [0, 1, 4, 5, 8, 9]):
-            if y != z:
-                isequal = False
-        assert isequal == True
+        assert (a[::2].flat == [0, 1, 4, 5, 8, 9]).all()
 
     def test_flatiter_transpose(self):
         from _numpypy import arange
-        a = arange(10).reshape(2,5).T
+        a = arange(10).reshape(2, 5).T
         b = a.flat
         assert (b[:5] == [0, 5, 1, 6, 2]).all()
         b.next()
         b.next()
         b.next()
         assert b.index == 3
-        assert b.coords == (1,1)
+        assert b.coords == (1, 1)
+
+    def test_flatiter_len(self):
+        from _numpypy import arange
+
+        assert len(arange(10).flat) == 10
+        assert len(arange(10).reshape(2, 5).flat) == 10
+        assert len(arange(10)[:2].flat) == 2
+        assert len((arange(2) + arange(2)).flat) == 2
 
     def test_slice_copy(self):
         from _numpypy import zeros
@@ -1740,10 +1762,11 @@
         assert len(a) == 8
         assert arange(False, True, True).dtype is dtype(int)
 
-from pypy.module.micronumpy.appbridge import get_appbridge_cache
 
 class AppTestRepr(BaseNumpyAppTest):
     def setup_class(cls):
+        if option.runappdirect:
+            py.test.skip("Can't be run directly.")
         BaseNumpyAppTest.setup_class.im_func(cls)
         cache = get_appbridge_cache(cls.space)
         cls.old_array_repr = cache.w_array_repr
@@ -1757,6 +1780,8 @@
         assert str(array([1, 2, 3])) == 'array([1, 2, 3])'
 
     def teardown_class(cls):
+        if option.runappdirect:
+            return
         cache = get_appbridge_cache(cls.space)
         cache.w_array_repr = cls.old_array_repr
         cache.w_array_str = cls.old_array_str
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -347,8 +347,9 @@
         raises((ValueError, TypeError), add.reduce, 1)
 
     def test_reduce_1d(self):
-        from _numpypy import add, maximum
+        from _numpypy import add, maximum, less
 
+        assert less.reduce([5, 4, 3, 2, 1])
         assert add.reduce([1, 2, 3]) == 6
         assert maximum.reduce([1]) == 1
         assert maximum.reduce([1, 2, 3]) == 3
@@ -433,3 +434,14 @@
         assert (isnan(array([0.2, float('inf'), float('nan')])) == [False, False, True]).all()
         assert (isinf(array([0.2, float('inf'), float('nan')])) == [False, True, False]).all()
         assert isinf(array([0.2])).dtype.kind == 'b'
+
+    def test_logical_ops(self):
+        from _numpypy import logical_and, logical_or, logical_xor, logical_not
+
+        assert (logical_and([True, False , True, True], [1, 1, 3, 0])
+                == [True, False, True, False]).all()
+        assert (logical_or([True, False, True, False], [1, 2, 0, 0])
+                == [True, True, True, False]).all()
+        assert (logical_xor([True, False, True, False], [1, 2, 0, 0])
+                == [False, True, True, False]).all()
+        assert (logical_not([True, False]) == [False, True]).all()
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
@@ -84,7 +84,7 @@
     def test_add(self):
         result = self.run("add")
         self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
-                                'setinteriorfield_raw': 1, 'int_add': 2,
+                                'setinteriorfield_raw': 1, 'int_add': 1,
                                 'int_ge': 1, 'guard_false': 1, 'jump': 1,
                                 'arraylen_gc': 1})
         assert result == 3 + 3
@@ -99,7 +99,7 @@
         result = self.run("float_add")
         assert result == 3 + 3
         self.check_simple_loop({"getinteriorfield_raw": 1, "float_add": 1,
-                                "setinteriorfield_raw": 1, "int_add": 2,
+                                "setinteriorfield_raw": 1, "int_add": 1,
                                 "int_ge": 1, "guard_false": 1, "jump": 1,
                                 'arraylen_gc': 1})
 
@@ -198,7 +198,8 @@
         result = self.run("any")
         assert result == 1
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
-                                "float_ne": 1, "int_add": 1,
+                                "int_and": 1, "int_add": 1,
+                                'cast_float_to_int': 1,
                                 "int_ge": 1, "jump": 1,
                                 "guard_false": 2, 'arraylen_gc': 1})
 
@@ -239,7 +240,7 @@
         assert result == -6
         self.check_simple_loop({"getinteriorfield_raw": 2, "float_add": 1,
                                 "float_neg": 1,
-                                "setinteriorfield_raw": 1, "int_add": 2,
+                                "setinteriorfield_raw": 1, "int_add": 1,
                                 "int_ge": 1, "guard_false": 1, "jump": 1,
                                 'arraylen_gc': 1})
 
@@ -321,7 +322,7 @@
         # 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': 2, 'int_ge': 1,
+                                'guard_false': 1, 'int_add': 1, 'int_ge': 1,
                                 'jump': 1, 'setinteriorfield_raw': 1,
                                 'arraylen_gc': 1})
 
@@ -387,7 +388,7 @@
         assert result == 4
         self.check_trace_count(1)
         self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
-                                'setinteriorfield_raw': 1, 'int_add': 2,
+                                'setinteriorfield_raw': 1, 'int_add': 1,
                                 'int_ge': 1, 'guard_false': 1, 'jump': 1,
                                 'arraylen_gc': 1})
     def define_flat_iter():
@@ -403,7 +404,7 @@
         assert result == 6
         self.check_trace_count(1)
         self.check_simple_loop({'getinteriorfield_raw': 2, 'float_add': 1,
-                                'setinteriorfield_raw': 1, 'int_add': 3,
+                                'setinteriorfield_raw': 1, 'int_add': 2,
                                 'int_ge': 1, 'guard_false': 1,
                                 'arraylen_gc': 1, 'jump': 1})
 
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -181,6 +181,22 @@
     def ge(self, v1, v2):
         return v1 >= v2
 
+    @raw_binary_op
+    def logical_and(self, v1, v2):
+        return bool(v1) and bool(v2)
+
+    @raw_binary_op
+    def logical_or(self, v1, v2):
+        return bool(v1) or bool(v2)
+
+    @raw_unary_op
+    def logical_not(self, v):
+        return not bool(v)
+
+    @raw_binary_op
+    def logical_xor(self, v1, v2):
+        return bool(v1) ^ bool(v2)
+
     def bool(self, v):
         return bool(self.for_computation(self.unbox(v)))
 
diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -37,7 +37,7 @@
     if space.isinstance_w(w_obj, space.w_unicode):
         w_obj = space.call_method(w_obj, 'encode',
                                   getfilesystemencoding(space))
-    return space.str_w(w_obj)
+    return space.str0_w(w_obj)
 
 class FileEncoder(object):
     def __init__(self, space, w_obj):
@@ -56,7 +56,7 @@
         self.w_obj = w_obj
 
     def as_bytes(self):
-        return self.space.str_w(self.w_obj)
+        return self.space.str0_w(self.w_obj)
 
     def as_unicode(self):
         space = self.space
@@ -71,7 +71,7 @@
             fname = FileEncoder(space, w_fname)
             return func(fname, *args)
         else:
-            fname = space.str_w(w_fname)
+            fname = space.str0_w(w_fname)
             return func(fname, *args)
     return dispatch
 
@@ -369,7 +369,7 @@
                                space.wrap(times[3]),
                                space.wrap(times[4])])
 
- at unwrap_spec(cmd=str)
+ at unwrap_spec(cmd='str0')
 def system(space, cmd):
     """Execute the command (a string) in a subshell."""
     try:
@@ -401,7 +401,7 @@
             fullpath = rposix._getfullpathname(path)
             w_fullpath = space.wrap(fullpath)
         else:
-            path = space.str_w(w_path)
+            path = space.str0_w(w_path)
             fullpath = rposix._getfullpathname(path)
             w_fullpath = space.wrap(fullpath)
     except OSError, e:
@@ -512,7 +512,7 @@
     for key, value in os.environ.items():
         space.setitem(w_env, space.wrap(key), space.wrap(value))
 
- at unwrap_spec(name=str, value=str)
+ at unwrap_spec(name='str0', value='str0')
 def putenv(space, name, value):
     """Change or add an environment variable."""
     try:
@@ -520,7 +520,7 @@
     except OSError, e:
         raise wrap_oserror(space, e)
 
- at unwrap_spec(name=str)
+ at unwrap_spec(name='str0')
 def unsetenv(space, name):
     """Delete an environment variable."""
     try:
@@ -548,7 +548,7 @@
                 for s in result
             ]
         else:
-            dirname = space.str_w(w_dirname)
+            dirname = space.str0_w(w_dirname)
             result = rposix.listdir(dirname)
             result_w = [space.wrap(s) for s in result]
     except OSError, e:
@@ -635,7 +635,7 @@
     import signal
     os.kill(os.getpid(), signal.SIGABRT)
 
- at unwrap_spec(src=str, dst=str)
+ at unwrap_spec(src='str0', dst='str0')
 def link(space, src, dst):
     "Create a hard link to a file."
     try:
@@ -650,7 +650,7 @@
     except OSError, e:
         raise wrap_oserror(space, e)
 
- at unwrap_spec(path=str)
+ at unwrap_spec(path='str0')
 def readlink(space, path):
     "Return a string representing the path to which the symbolic link points."
     try:
@@ -765,7 +765,7 @@
     w_keys = space.call_method(w_env, 'keys')
     for w_key in space.unpackiterable(w_keys):
         w_value = space.getitem(w_env, w_key)
-        env[space.str_w(w_key)] = space.str_w(w_value)
+        env[space.str0_w(w_key)] = space.str0_w(w_value)
     return env
 
 def execve(space, w_command, w_args, w_env):
@@ -785,18 +785,18 @@
     except OSError, e:
         raise wrap_oserror(space, e)
 
- at unwrap_spec(mode=int, path=str)
+ at unwrap_spec(mode=int, path='str0')
 def spawnv(space, mode, path, w_args):
-    args = [space.str_w(w_arg) for w_arg in space.unpackiterable(w_args)]
+    args = [space.str0_w(w_arg) for w_arg in space.unpackiterable(w_args)]
     try:
         ret = os.spawnv(mode, path, args)
     except OSError, e:
         raise wrap_oserror(space, e)
     return space.wrap(ret)
 
- at unwrap_spec(mode=int, path=str)
+ at unwrap_spec(mode=int, path='str0')
 def spawnve(space, mode, path, w_args, w_env):
-    args = [space.str_w(w_arg) for w_arg in space.unpackiterable(w_args)]
+    args = [space.str0_w(w_arg) for w_arg in space.unpackiterable(w_args)]
     env = _env2interp(space, w_env)
     try:
         ret = os.spawnve(mode, path, args, env)
@@ -914,7 +914,7 @@
         raise wrap_oserror(space, e)
     return space.w_None
 
- at unwrap_spec(path=str)
+ at unwrap_spec(path='str0')
 def chroot(space, path):
     """ chroot(path)
 
@@ -1103,7 +1103,7 @@
     except OSError, e:
         raise wrap_oserror(space, e)
 
- at unwrap_spec(path=str, uid=c_uid_t, gid=c_gid_t)
+ at unwrap_spec(path='str0', uid=c_uid_t, gid=c_gid_t)
 def chown(space, path, uid, gid):
     check_uid_range(space, uid)
     check_uid_range(space, gid)
@@ -1113,7 +1113,7 @@
         raise wrap_oserror(space, e, path)
     return space.w_None
 
- at unwrap_spec(path=str, uid=c_uid_t, gid=c_gid_t)
+ at unwrap_spec(path='str0', uid=c_uid_t, gid=c_gid_t)
 def lchown(space, path, uid, gid):
     check_uid_range(space, uid)
     check_uid_range(space, gid)
diff --git a/pypy/module/pypyjit/interp_resop.py b/pypy/module/pypyjit/interp_resop.py
--- a/pypy/module/pypyjit/interp_resop.py
+++ b/pypy/module/pypyjit/interp_resop.py
@@ -127,6 +127,7 @@
             l_w.append(DebugMergePoint(space, jit_hooks._cast_to_gcref(op),
                                        logops.repr_of_resop(op),
                                        jd_sd.jitdriver.name,
+                                       op.getarg(1).getint(),
                                        w_greenkey))
         else:
             l_w.append(WrappedOp(jit_hooks._cast_to_gcref(op), ofs,
@@ -163,14 +164,14 @@
         llres = res.llbox
     return WrappedOp(jit_hooks.resop_new(num, args, llres), offset, repr)
 
- at unwrap_spec(repr=str, jd_name=str)
-def descr_new_dmp(space, w_tp, w_args, repr, jd_name, w_greenkey):
+ at unwrap_spec(repr=str, jd_name=str, call_depth=int)
+def descr_new_dmp(space, w_tp, w_args, repr, jd_name, call_depth, w_greenkey):
     args = [space.interp_w(WrappedBox, w_arg).llbox for w_arg in
             space.listview(w_args)]
     num = rop.DEBUG_MERGE_POINT
     return DebugMergePoint(space,
                            jit_hooks.resop_new(num, args, jit_hooks.emptyval()),
-                           repr, jd_name, w_greenkey)
+                           repr, jd_name, call_depth, w_greenkey)
 
 class WrappedOp(Wrappable):
     """ A class representing a single ResOperation, wrapped nicely
@@ -205,10 +206,11 @@
         jit_hooks.resop_setresult(self.op, box.llbox)
 
 class DebugMergePoint(WrappedOp):
-    def __init__(self, space, op, repr_of_resop, jd_name, w_greenkey):
+    def __init__(self, space, op, repr_of_resop, jd_name, call_depth, w_greenkey):
         WrappedOp.__init__(self, op, -1, repr_of_resop)
+        self.jd_name = jd_name
+        self.call_depth = call_depth
         self.w_greenkey = w_greenkey
-        self.jd_name = jd_name
 
     def get_pycode(self, space):
         if self.jd_name == pypyjitdriver.name:
@@ -243,6 +245,7 @@
     greenkey = interp_attrproperty_w("w_greenkey", cls=DebugMergePoint),
     pycode = GetSetProperty(DebugMergePoint.get_pycode),
     bytecode_no = GetSetProperty(DebugMergePoint.get_bytecode_no),
+    call_depth = interp_attrproperty("call_depth", cls=DebugMergePoint),
     jitdriver_name = GetSetProperty(DebugMergePoint.get_jitdriver_name),
 )
 DebugMergePoint.acceptable_as_base_class = False
diff --git a/pypy/module/pypyjit/test/test_jit_hook.py b/pypy/module/pypyjit/test/test_jit_hook.py
--- a/pypy/module/pypyjit/test/test_jit_hook.py
+++ b/pypy/module/pypyjit/test/test_jit_hook.py
@@ -122,7 +122,8 @@
         assert isinstance(dmp, pypyjit.DebugMergePoint)
         assert dmp.pycode is self.f.func_code
         assert dmp.greenkey == (self.f.func_code, 0, False)
-        #assert int_add.name == 'int_add'
+        assert dmp.call_depth == 0
+        assert int_add.name == 'int_add'
         assert int_add.num == self.int_add_num
         self.on_compile_bridge()
         assert len(all) == 2
@@ -223,11 +224,13 @@
         def f():
             pass
 
-        op = DebugMergePoint([Box(0)], 'repr', 'pypyjit', (f.func_code, 0, 0))
+        op = DebugMergePoint([Box(0)], 'repr', 'pypyjit', 2, (f.func_code, 0, 0))
         assert op.bytecode_no == 0
         assert op.pycode is f.func_code
         assert repr(op) == 'repr'
         assert op.jitdriver_name == 'pypyjit'
         assert op.num == self.dmp_num
-        op = DebugMergePoint([Box(0)], 'repr', 'notmain', ('str',))
+        assert op.call_depth == 2
+        op = DebugMergePoint([Box(0)], 'repr', 'notmain', 5, ('str',))
         raises(AttributeError, 'op.pycode')
+        assert op.call_depth == 5
diff --git a/pypy/module/sys/state.py b/pypy/module/sys/state.py
--- a/pypy/module/sys/state.py
+++ b/pypy/module/sys/state.py
@@ -74,7 +74,7 @@
     #
     return importlist
 
- at unwrap_spec(srcdir=str)
+ at unwrap_spec(srcdir='str0')
 def pypy_initial_path(space, srcdir):
     try:
         path = getinitialpath(get(space), srcdir)
diff --git a/pypy/module/zipimport/interp_zipimport.py b/pypy/module/zipimport/interp_zipimport.py
--- a/pypy/module/zipimport/interp_zipimport.py
+++ b/pypy/module/zipimport/interp_zipimport.py
@@ -344,7 +344,7 @@
         space = self.space
         return space.wrap(self.filename)
 
- at unwrap_spec(name=str)
+ at unwrap_spec(name='str0')
 def descr_new_zipimporter(space, w_type, name):
     w = space.wrap
     ok = False
diff --git a/pypy/rlib/rstring.py b/pypy/rlib/rstring.py
--- a/pypy/rlib/rstring.py
+++ b/pypy/rlib/rstring.py
@@ -205,3 +205,45 @@
         assert p.const is None
         return SomeUnicodeBuilder(can_be_None=True)
 
+#___________________________________________________________________
+# Support functions for SomeString.no_nul
+
+def assert_str0(fname):
+    assert '\x00' not in fname, "NUL byte in string"
+    return fname
+
+class Entry(ExtRegistryEntry):
+    _about_ = assert_str0
+
+    def compute_result_annotation(self, s_obj):
+        if s_None.contains(s_obj):
+            return s_obj
+        assert isinstance(s_obj, (SomeString, SomeUnicodeString))
+        if s_obj.no_nul:
+            return s_obj
+        new_s_obj = SomeObject.__new__(s_obj.__class__)
+        new_s_obj.__dict__ = s_obj.__dict__.copy()
+        new_s_obj.no_nul = True
+        return new_s_obj
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        return hop.inputarg(hop.args_r[0], arg=0)
+
+def check_str0(fname):
+    """A 'probe' to trigger a failure at translation time, if the
+    string was not proved to not contain NUL characters."""
+    assert '\x00' not in fname, "NUL byte in string"
+
+class Entry(ExtRegistryEntry):
+    _about_ = check_str0
+
+    def compute_result_annotation(self, s_obj):
+        if not isinstance(s_obj, (SomeString, SomeUnicodeString)):
+            return s_obj
+        if not s_obj.no_nul:
+            raise ValueError("Value is not no_nul")
+
+    def specialize_call(self, hop):
+        pass
+
diff --git a/pypy/rlib/test/test_rmarshal.py b/pypy/rlib/test/test_rmarshal.py
--- a/pypy/rlib/test/test_rmarshal.py
+++ b/pypy/rlib/test/test_rmarshal.py
@@ -169,7 +169,7 @@
         assert st2.st_mode == st.st_mode
         assert st2[9] == st[9]
         return buf
-    fn = compile(f, [str])
+    fn = compile(f, [annmodel.s_Str0])
     res = fn('.')
     st = os.stat('.')
     sttuple = marshal.loads(res)
diff --git a/pypy/rpython/extfunc.py b/pypy/rpython/extfunc.py
--- a/pypy/rpython/extfunc.py
+++ b/pypy/rpython/extfunc.py
@@ -2,7 +2,7 @@
 from pypy.rpython.extregistry import ExtRegistryEntry
 from pypy.rpython.lltypesystem.lltype import typeOf
 from pypy.objspace.flow.model import Constant
-from pypy.annotation.model import unionof
+from pypy.annotation import model as annmodel
 from pypy.annotation.signature import annotation
 
 import py, sys
@@ -138,7 +138,6 @@
     # we defer a bit annotation here
 
     def compute_result_annotation(self):
-        from pypy.annotation import model as annmodel
         return annmodel.SomeGenericCallable([annotation(i, self.bookkeeper)
                                              for i in self.instance.args],
                            annotation(self.instance.result, self.bookkeeper))
@@ -152,8 +151,9 @@
         signature_args = [annotation(arg, None) for arg in args]
         assert len(args_s) == len(signature_args),\
                "Argument number mismatch"
+
         for i, expected in enumerate(signature_args):
-            arg = unionof(args_s[i], expected)
+            arg = annmodel.unionof(args_s[i], expected)
             if not expected.contains(arg):
                 name = getattr(self, 'name', None)
                 if not name:
diff --git a/pypy/rpython/extfuncregistry.py b/pypy/rpython/extfuncregistry.py
--- a/pypy/rpython/extfuncregistry.py
+++ b/pypy/rpython/extfuncregistry.py
@@ -85,7 +85,8 @@
 # llinterpreter
 
 path_functions = [
-    ('join',     [str, str], str),
+    ('join',     [ll_os.str0, ll_os.str0], ll_os.str0),
+    ('dirname',  [ll_os.str0], ll_os.str0),
     ]
 
 for name, args, res in path_functions:
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -1036,13 +1036,8 @@
     libraries = eci.testonly_libraries + eci.libraries + eci.frameworks
 
     FUNCTYPE = lltype.typeOf(funcptr).TO
-    if not libraries:
-        cfunc = get_on_lib(standard_c_lib, funcname)
-        # XXX magic: on Windows try to load the function from 'kernel32' too
-        if cfunc is None and hasattr(ctypes, 'windll'):
-            cfunc = get_on_lib(ctypes.windll.kernel32, funcname)
-    else:
-        cfunc = None
+    cfunc = None
+    if libraries:
         not_found = []
         for libname in libraries:
             libpath = None
@@ -1075,6 +1070,12 @@
                 not_found.append(libname)
 
     if cfunc is None:
+        cfunc = get_on_lib(standard_c_lib, funcname)
+        # XXX magic: on Windows try to load the function from 'kernel32' too
+        if cfunc is None and hasattr(ctypes, 'windll'):
+            cfunc = get_on_lib(ctypes.windll.kernel32, funcname)
+
+    if cfunc is None:
         # function name not found in any of the libraries
         if not libraries:
             place = 'the standard C library (missing libraries=...?)'
diff --git a/pypy/rpython/lltypesystem/rffi.py b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -15,7 +15,7 @@
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 from pypy.rpython.annlowlevel import llhelper
 from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.rstring import StringBuilder, UnicodeBuilder
+from pypy.rlib.rstring import StringBuilder, UnicodeBuilder, assert_str0
 from pypy.rlib import jit
 from pypy.rpython.lltypesystem import llmemory
 import os, sys
@@ -698,7 +698,7 @@
         while cp[i] != lastchar:
             b.append(cp[i])
             i += 1
-        return b.build()
+        return assert_str0(b.build())
 
     # str -> char*
     # Can't inline this because of the raw address manipulation.
@@ -804,7 +804,7 @@
         while i < maxlen and cp[i] != lastchar:
             b.append(cp[i])
             i += 1
-        return b.build()
+        return assert_str0(b.build())
 
     # char* and size -> str (which can contain null bytes)
     def charpsize2str(cp, size):
@@ -842,6 +842,7 @@
         array[i] = str2charp(l[i])
     array[len(l)] = lltype.nullptr(CCHARP.TO)
     return array
+liststr2charpp._annenforceargs_ = [[annmodel.s_Str0]]  # List of strings
 
 def free_charpp(ref):
     """ frees list of char**, NULL terminated
diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -31,6 +31,10 @@
 from pypy.rlib import rgc
 from pypy.rlib.objectmodel import specialize
 
+str0 = SomeString(no_nul=True)
+unicode0 = SomeUnicodeString(no_nul=True)
+
+
 def monkeypatch_rposix(posixfunc, unicodefunc, signature):
     func_name = posixfunc.__name__
 
@@ -44,7 +48,10 @@
 
     args = ', '.join(arglist)
     transformed_args = ', '.join(transformed_arglist)
-    main_arg = 'arg%d' % (signature.index(unicode),)
+    try:
+        main_arg = 'arg%d' % (signature.index(unicode0),)
+    except ValueError:
+        main_arg = 'arg%d' % (signature.index(unicode),)
 
     source = py.code.Source("""
     def %(func_name)s(%(args)s):
@@ -68,6 +75,7 @@
 
 class StringTraits:
     str = str
+    str0 = str0
     CHAR = rffi.CHAR
     CCHARP = rffi.CCHARP
     charp2str = staticmethod(rffi.charp2str)
@@ -85,6 +93,7 @@
 
 class UnicodeTraits:
     str = unicode
+    str0 = unicode0
     CHAR = rffi.WCHAR_T
     CCHARP = rffi.CWCHARP
     charp2str = staticmethod(rffi.wcharp2unicode)
@@ -301,7 +310,7 @@
             rffi.free_charpp(l_args)
             raise OSError(rposix.get_errno(), "execv failed")
 
-        return extdef([str, [str]], s_ImpossibleValue, llimpl=execv_llimpl,
+        return extdef([str0, [str0]], s_ImpossibleValue, llimpl=execv_llimpl,
                       export_name="ll_os.ll_os_execv")
 
 
@@ -319,7 +328,8 @@
             # appropriate
             envstrs = []
             for item in env.iteritems():
-                envstrs.append("%s=%s" % item)
+                envstr = "%s=%s" % item
+                envstrs.append(envstr)
 
             l_args = rffi.liststr2charpp(args)
             l_env = rffi.liststr2charpp(envstrs)
@@ -332,7 +342,7 @@
             raise OSError(rposix.get_errno(), "execve failed")
 
         return extdef(
-            [str, [str], {str: str}],
+            [str0, [str0], {str0: str0}],
             s_ImpossibleValue,
             llimpl=execve_llimpl,
             export_name="ll_os.ll_os_execve")
@@ -353,7 +363,7 @@
                 raise OSError(rposix.get_errno(), "os_spawnv failed")
             return rffi.cast(lltype.Signed, childpid)
 
-        return extdef([int, str, [str]], int, llimpl=spawnv_llimpl,
+        return extdef([int, str0, [str0]], int, llimpl=spawnv_llimpl,
                       export_name="ll_os.ll_os_spawnv")
 
     @registering_if(os, 'spawnve')
@@ -378,7 +388,7 @@
                 raise OSError(rposix.get_errno(), "os_spawnve failed")
             return rffi.cast(lltype.Signed, childpid)
 
-        return extdef([int, str, [str], {str: str}], int,
+        return extdef([int, str0, [str0], {str0: str0}], int,
                       llimpl=spawnve_llimpl,
                       export_name="ll_os.ll_os_spawnve")
 
@@ -517,7 +527,7 @@
             else:
                 raise Exception("os.utime() arg 2 must be None or a tuple of "
                                 "2 floats, got %s" % (s_times,))
-        os_utime_normalize_args._default_signature_ = [traits.str, None]
+        os_utime_normalize_args._default_signature_ = [traits.str0, None]
 
         return extdef(os_utime_normalize_args, s_None,
                       "ll_os.ll_os_utime",
@@ -612,7 +622,7 @@
             if result == -1:
                 raise OSError(rposix.get_errno(), "os_chroot failed")
 
-        return extdef([str], None, export_name="ll_os.ll_os_chroot",
+        return extdef([str0], None, export_name="ll_os.ll_os_chroot",
                       llimpl=chroot_llimpl)
 
     @registering_if(os, 'uname')
@@ -816,7 +826,7 @@
         def os_open_oofakeimpl(path, flags, mode):
             return os.open(OOSupport.from_rstr(path), flags, mode)
 
-        return extdef([traits.str, int, int], int, traits.ll_os_name('open'),
+        return extdef([traits.str0, int, int], int, traits.ll_os_name('open'),
                       llimpl=os_open_llimpl, oofakeimpl=os_open_oofakeimpl)
 
     @registering_if(os, 'getloadavg')
@@ -1050,7 +1060,7 @@
         def os_access_oofakeimpl(path, mode):
             return os.access(OOSupport.from_rstr(path), mode)
 
-        return extdef([traits.str, int], s_Bool, llimpl=access_llimpl,
+        return extdef([traits.str0, int], s_Bool, llimpl=access_llimpl,
                       export_name=traits.ll_os_name("access"),
                       oofakeimpl=os_access_oofakeimpl)
 
@@ -1062,8 +1072,8 @@
         from pypy.rpython.module.ll_win32file import make_getfullpathname_impl
         getfullpathname_llimpl = make_getfullpathname_impl(traits)
 
-        return extdef([traits.str],  # a single argument which is a str
-                      traits.str,    # returns a string
+        return extdef([traits.str0],  # a single argument which is a str
+                      traits.str0,    # returns a string
                       traits.ll_os_name('_getfullpathname'),
                       llimpl=getfullpathname_llimpl)
 
@@ -1174,8 +1184,8 @@
                     raise OSError(error, "os_readdir failed")
                 return result
 
-        return extdef([traits.str],  # a single argument which is a str
-                      [traits.str],  # returns a list of strings
+        return extdef([traits.str0],  # a single argument which is a str
+                      [traits.str0],  # returns a list of strings
                       traits.ll_os_name('listdir'),
                       llimpl=os_listdir_llimpl)
 
@@ -1241,7 +1251,7 @@
             if res == -1:
                 raise OSError(rposix.get_errno(), "os_chown failed")
 
-        return extdef([str, int, int], None, "ll_os.ll_os_chown",
+        return extdef([str0, int, int], None, "ll_os.ll_os_chown",
                       llimpl=os_chown_llimpl)
 
     @registering_if(os, 'lchown')
@@ -1254,7 +1264,7 @@
             if res == -1:
                 raise OSError(rposix.get_errno(), "os_lchown failed")
 
-        return extdef([str, int, int], None, "ll_os.ll_os_lchown",
+        return extdef([str0, int, int], None, "ll_os.ll_os_lchown",
                       llimpl=os_lchown_llimpl)
 
     @registering_if(os, 'readlink')
@@ -1283,12 +1293,11 @@
                     lltype.free(buf, flavor='raw')
                     bufsize *= 4
             # convert the result to a string
-            l = [buf[i] for i in range(res)]
-            result = ''.join(l)
+            result = rffi.charp2strn(buf, res)
             lltype.free(buf, flavor='raw')
             return result
 
-        return extdef([str], str,
+        return extdef([str0], str0,
                       "ll_os.ll_os_readlink",
                       llimpl=os_readlink_llimpl)
 
@@ -1361,7 +1370,7 @@
             res = os_system(command)
             return rffi.cast(lltype.Signed, res)
 
-        return extdef([str], int, llimpl=system_llimpl,
+        return extdef([str0], int, llimpl=system_llimpl,
                       export_name="ll_os.ll_os_system")
 
     @registering_str_unicode(os.unlink)
@@ -1383,7 +1392,7 @@
                 if not win32traits.DeleteFile(path):
                     raise rwin32.lastWindowsError()
 
-        return extdef([traits.str], s_None, llimpl=unlink_llimpl,
+        return extdef([traits.str0], s_None, llimpl=unlink_llimpl,
                       export_name=traits.ll_os_name('unlink'))
 
     @registering_str_unicode(os.chdir)
@@ -1401,7 +1410,7 @@
             from pypy.rpython.module.ll_win32file import make_chdir_impl
             os_chdir_llimpl = make_chdir_impl(traits)
 
-        return extdef([traits.str], s_None, llimpl=os_chdir_llimpl,
+        return extdef([traits.str0], s_None, llimpl=os_chdir_llimpl,
                       export_name=traits.ll_os_name('chdir'))
 
     @registering_str_unicode(os.mkdir)
@@ -1424,7 +1433,7 @@
                 if res < 0:
                     raise OSError(rposix.get_errno(), "os_mkdir failed")
 
-        return extdef([traits.str, int], s_None, llimpl=os_mkdir_llimpl,
+        return extdef([traits.str0, int], s_None, llimpl=os_mkdir_llimpl,
                       export_name=traits.ll_os_name('mkdir'))
 
     @registering_str_unicode(os.rmdir)
@@ -1437,7 +1446,7 @@
             if res < 0:
                 raise OSError(rposix.get_errno(), "os_rmdir failed")
 
-        return extdef([traits.str], s_None, llimpl=rmdir_llimpl,
+        return extdef([traits.str0], s_None, llimpl=rmdir_llimpl,
                       export_name=traits.ll_os_name('rmdir'))
 
     @registering_str_unicode(os.chmod)
@@ -1454,7 +1463,7 @@
             from pypy.rpython.module.ll_win32file import make_chmod_impl
             chmod_llimpl = make_chmod_impl(traits)
 
-        return extdef([traits.str, int], s_None, llimpl=chmod_llimpl,
+        return extdef([traits.str0, int], s_None, llimpl=chmod_llimpl,
                       export_name=traits.ll_os_name('chmod'))
 
     @registering_str_unicode(os.rename)
@@ -1476,7 +1485,7 @@
                 if not win32traits.MoveFile(oldpath, newpath):
                     raise rwin32.lastWindowsError()
 
-        return extdef([traits.str, traits.str], s_None, llimpl=rename_llimpl,
+        return extdef([traits.str0, traits.str0], s_None, llimpl=rename_llimpl,
                       export_name=traits.ll_os_name('rename'))
 
     @registering_str_unicode(getattr(os, 'mkfifo', None))
@@ -1489,7 +1498,7 @@
             if res < 0:
                 raise OSError(rposix.get_errno(), "os_mkfifo failed")
 
-        return extdef([traits.str, int], s_None, llimpl=mkfifo_llimpl,
+        return extdef([traits.str0, int], s_None, llimpl=mkfifo_llimpl,
                       export_name=traits.ll_os_name('mkfifo'))
 
     @registering_str_unicode(getattr(os, 'mknod', None))
@@ -1503,7 +1512,7 @@
             if res < 0:
                 raise OSError(rposix.get_errno(), "os_mknod failed")
 
-        return extdef([traits.str, int, int], s_None, llimpl=mknod_llimpl,
+        return extdef([traits.str0, int, int], s_None, llimpl=mknod_llimpl,
                       export_name=traits.ll_os_name('mknod'))
 
     @registering(os.umask)
@@ -1555,7 +1564,7 @@
             if res < 0:
                 raise OSError(rposix.get_errno(), "os_link failed")
 
-        return extdef([str, str], s_None, llimpl=link_llimpl,
+        return extdef([str0, str0], s_None, llimpl=link_llimpl,
                       export_name="ll_os.ll_os_link")
 
     @registering_if(os, 'symlink')
@@ -1568,7 +1577,7 @@
             if res < 0:
                 raise OSError(rposix.get_errno(), "os_symlink failed")
 
-        return extdef([str, str], s_None, llimpl=symlink_llimpl,
+        return extdef([str0, str0], s_None, llimpl=symlink_llimpl,
                       export_name="ll_os.ll_os_symlink")
 
     @registering_if(os, 'fork')
diff --git a/pypy/rpython/module/ll_os_environ.py b/pypy/rpython/module/ll_os_environ.py
--- a/pypy/rpython/module/ll_os_environ.py
+++ b/pypy/rpython/module/ll_os_environ.py
@@ -3,8 +3,11 @@
 from pypy.rpython.controllerentry import Controller
 from pypy.rpython.extfunc import register_external
 from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rpython.module import ll_os
 from pypy.rlib import rposix
 
+str0 = ll_os.str0
+
 # ____________________________________________________________
 #
 # Annotation support to control access to 'os.environ' in the RPython program
@@ -64,7 +67,7 @@
     rffi.free_charp(l_name)
     return result
 
-register_external(r_getenv, [str], annmodel.SomeString(can_be_None=True),
+register_external(r_getenv, [str0], annmodel.SomeString(can_be_None=True),
                   export_name='ll_os.ll_os_getenv',
                   llimpl=getenv_llimpl)
 
@@ -93,7 +96,7 @@
     if l_oldstring:
         rffi.free_charp(l_oldstring)
 
-register_external(r_putenv, [str, str], annmodel.s_None,
+register_external(r_putenv, [str0, str0], annmodel.s_None,
                   export_name='ll_os.ll_os_putenv',
                   llimpl=putenv_llimpl)
 
@@ -128,7 +131,7 @@
             del envkeepalive.byname[name]
             rffi.free_charp(l_oldstring)
 
-    register_external(r_unsetenv, [str], annmodel.s_None,
+    register_external(r_unsetenv, [str0], annmodel.s_None,
                       export_name='ll_os.ll_os_unsetenv',
                       llimpl=unsetenv_llimpl)
 
@@ -172,7 +175,7 @@
         i += 1
     return result
 
-register_external(r_envkeys, [], [str],   # returns a list of strings
+register_external(r_envkeys, [], [str0],   # returns a list of strings
                   export_name='ll_os.ll_os_envkeys',
                   llimpl=envkeys_llimpl)
 
@@ -193,6 +196,6 @@
         i += 1
     return result
 
-register_external(r_envitems, [], [(str, str)],
+register_external(r_envitems, [], [(str0, str0)],
                   export_name='ll_os.ll_os_envitems',
                   llimpl=envitems_llimpl)
diff --git a/pypy/rpython/module/ll_os_stat.py b/pypy/rpython/module/ll_os_stat.py
--- a/pypy/rpython/module/ll_os_stat.py
+++ b/pypy/rpython/module/ll_os_stat.py
@@ -236,7 +236,7 @@
 def register_stat_variant(name, traits):
     if name != 'fstat':
         arg_is_path = True
-        s_arg = traits.str
+        s_arg = traits.str0
         ARG1 = traits.CCHARP
     else:
         arg_is_path = False
@@ -251,8 +251,6 @@
             [s_arg], s_StatResult, traits.ll_os_name(name),
             llimpl=posix_stat_llimpl)
 
-    assert traits.str is str
-
     if sys.platform.startswith('linux'):
         # because we always use _FILE_OFFSET_BITS 64 - this helps things work that are not a c compiler
         _functions = {'stat':  'stat64',
@@ -283,7 +281,7 @@
 
     @func_renamer('os_%s_fake' % (name,))
     def posix_fakeimpl(arg):
-        if s_arg == str:
+        if s_arg == traits.str0:
             arg = hlstr(arg)
         st = getattr(os, name)(arg)
         fields = [TYPE for fieldname, TYPE in STAT_FIELDS]
diff --git a/pypy/rpython/ootypesystem/test/test_ooann.py b/pypy/rpython/ootypesystem/test/test_ooann.py
--- a/pypy/rpython/ootypesystem/test/test_ooann.py
+++ b/pypy/rpython/ootypesystem/test/test_ooann.py
@@ -231,7 +231,7 @@
 
     a = RPythonAnnotator()
     s = a.build_types(oof, [bool])
-    assert s == annmodel.SomeString(can_be_None=True)
+    assert annmodel.SomeString(can_be_None=True).contains(s)
 
 def test_oostring():
     def oof():
diff --git a/pypy/rpython/test/test_extfunc.py b/pypy/rpython/test/test_extfunc.py
--- a/pypy/rpython/test/test_extfunc.py
+++ b/pypy/rpython/test/test_extfunc.py
@@ -167,3 +167,43 @@
         a = RPythonAnnotator(policy=policy)
         s = a.build_types(f, [])
         assert isinstance(s, annmodel.SomeString)
+
+    def test_str0(self):
+        str0 = annmodel.SomeString(no_nul=True)
+        def os_open(s):
+            pass
+        register_external(os_open, [str0], None)
+        def f(s):
+            return os_open(s)
+        policy = AnnotatorPolicy()
+        policy.allow_someobjects = False
+        a = RPythonAnnotator(policy=policy)
+        a.build_types(f, [str])  # Does not raise
+        assert a.translator.config.translation.check_str_without_nul == False
+        # Now enable the str0 check, and try again with a similar function
+        a.translator.config.translation.check_str_without_nul=True
+        def g(s):
+            return os_open(s)
+        raises(Exception, a.build_types, g, [str])
+        a.build_types(g, [str0])  # Does not raise
+
+    def test_list_of_str0(self):
+        str0 = annmodel.SomeString(no_nul=True)
+        def os_execve(l):
+            pass
+        register_external(os_execve, [[str0]], None)
+        def f(l):
+            return os_execve(l)
+        policy = AnnotatorPolicy()
+        policy.allow_someobjects = False
+        a = RPythonAnnotator(policy=policy)
+        a.build_types(f, [[str]])  # Does not raise
+        assert a.translator.config.translation.check_str_without_nul == False
+        # Now enable the str0 check, and try again with a similar function
+        a.translator.config.translation.check_str_without_nul=True
+        def g(l):
+            return os_execve(l)
+        raises(Exception, a.build_types, g, [[str]])
+        a.build_types(g, [[str0]])  # Does not raise
+        
+
diff --git a/pypy/translator/c/test/test_extfunc.py b/pypy/translator/c/test/test_extfunc.py
--- a/pypy/translator/c/test/test_extfunc.py
+++ b/pypy/translator/c/test/test_extfunc.py
@@ -3,6 +3,7 @@
 import os, time, sys
 from pypy.tool.udir import udir
 from pypy.rlib.rarithmetic import r_longlong
+from pypy.annotation import model as annmodel
 from pypy.translator.c.test.test_genc import compile
 from pypy.translator.c.test.test_standalone import StandaloneTests
 posix = __import__(os.name)
@@ -145,7 +146,7 @@
     filename = str(py.path.local(__file__))
     def call_access(path, mode):
         return os.access(path, mode)
-    f = compile(call_access, [str, int])
+    f = compile(call_access, [annmodel.s_Str0, int])
     for mode in os.R_OK, os.W_OK, os.X_OK, (os.R_OK | os.W_OK | os.X_OK):
         assert f(filename, mode) == os.access(filename, mode)
 
@@ -225,7 +226,7 @@
 def test_system():
     def does_stuff(cmd):
         return os.system(cmd)
-    f1 = compile(does_stuff, [str])
+    f1 = compile(does_stuff, [annmodel.s_Str0])
     res = f1("echo hello")
     assert res == 0
 
@@ -311,7 +312,7 @@
 def test_chdir():
     def does_stuff(path):
         os.chdir(path)
-    f1 = compile(does_stuff, [str])
+    f1 = compile(does_stuff, [annmodel.s_Str0])
     curdir = os.getcwd()
     try:
         os.chdir('..')
@@ -325,7 +326,7 @@
             os.rmdir(path)
         else:
             os.mkdir(path, 0777)
-    f1 = compile(does_stuff, [str, bool])
+    f1 = compile(does_stuff, [annmodel.s_Str0, bool])
     dirname = str(udir.join('test_mkdir_rmdir'))
     f1(dirname, False)
     assert os.path.exists(dirname) and os.path.isdir(dirname)
@@ -628,7 +629,7 @@
             return os.environ[s]
         except KeyError:
             return '--missing--'
-    func = compile(fn, [str])
+    func = compile(fn, [annmodel.s_Str0])
     os.environ.setdefault('USER', 'UNNAMED_USER')
     result = func('USER')
     assert result == os.environ['USER']
@@ -640,7 +641,7 @@
         res = os.environ.get(s)
         if res is None: res = '--missing--'
         return res
-    func = compile(fn, [str])
+    func = compile(fn, [annmodel.s_Str0])
     os.environ.setdefault('USER', 'UNNAMED_USER')
     result = func('USER')
     assert result == os.environ['USER']
@@ -654,7 +655,7 @@
         os.environ[s] = t3
         os.environ[s] = t4
         os.environ[s] = t5
-    func = compile(fn, [str, str, str, str, str, str])
+    func = compile(fn, [annmodel.s_Str0] * 6)
     func('PYPY_TEST_DICTLIKE_ENVIRON', 'a', 'b', 'c', 'FOOBAR', '42',
          expected_extra_mallocs = (2, 3, 4))   # at least two, less than 5
     assert _real_getenv('PYPY_TEST_DICTLIKE_ENVIRON') == '42'
@@ -678,7 +679,7 @@
             else:
                 raise Exception("should have raised!")
             # os.environ[s5] stays
-    func = compile(fn, [str, str, str, str, str])
+    func = compile(fn, [annmodel.s_Str0] * 5)
     if hasattr(__import__(os.name), 'unsetenv'):
         expected_extra_mallocs = range(2, 10)
         # at least 2, less than 10: memory for s1, s2, s3, s4 should be freed
@@ -743,7 +744,7 @@
             raise AssertionError("should have failed!")
         result = os.listdir(s)
         return '/'.join(result)
-    func = compile(mylistdir, [str])
+    func = compile(mylistdir, [annmodel.s_Str0])
     for testdir in [str(udir), os.curdir]:
         result = func(testdir)
         result = result.split('/')
diff --git a/pypy/translator/cli/test/runtest.py b/pypy/translator/cli/test/runtest.py
--- a/pypy/translator/cli/test/runtest.py
+++ b/pypy/translator/cli/test/runtest.py
@@ -276,7 +276,7 @@
 
 def get_annotation(x):
     if isinstance(x, basestring) and len(x) > 1:
-        return SomeString()
+        return SomeString(no_nul='\x00' not in x)
     else:
         return lltype_to_annotation(typeOf(x))
 
diff --git a/pypy/translator/driver.py b/pypy/translator/driver.py
--- a/pypy/translator/driver.py
+++ b/pypy/translator/driver.py
@@ -184,6 +184,7 @@
         self.standalone = standalone
 
         if standalone:
+            # the 'argv' parameter
             inputtypes = [s_list_of_strings]
         self.inputtypes = inputtypes
 
diff --git a/pypy/translator/goal/nanos.py b/pypy/translator/goal/nanos.py
--- a/pypy/translator/goal/nanos.py
+++ b/pypy/translator/goal/nanos.py
@@ -266,7 +266,7 @@
     raise NotImplementedError("os.name == %r" % (os.name,))
 
 def getenv(space, w_name):
-    name = space.str_w(w_name)
+    name = space.str0_w(w_name)
     return space.wrap(os.environ.get(name))
 getenv_w = interp2app(getenv)
 
diff --git a/pypy/translator/goal/targetpypystandalone.py b/pypy/translator/goal/targetpypystandalone.py
--- a/pypy/translator/goal/targetpypystandalone.py
+++ b/pypy/translator/goal/targetpypystandalone.py
@@ -159,6 +159,8 @@
         ## if config.translation.type_system == 'ootype':
         ##     config.objspace.usemodules.suggest(rbench=True)
 
+        config.translation.suggest(check_str_without_nul=True)
+
         if config.translation.thread:
             config.objspace.usemodules.thread = True
         elif config.objspace.usemodules.thread:


More information about the pypy-commit mailing list