[pypy-svn] r45812 - in pypy/branch/pypy-more-rtti-inprogress: rpython rpython/lltypesystem rpython/module translator/c translator/sandbox translator/sandbox/test

arigo at codespeak.net arigo at codespeak.net
Fri Aug 17 15:05:56 CEST 2007


Author: arigo
Date: Fri Aug 17 15:05:56 2007
New Revision: 45812

Removed:
   pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/sandboxmsg.py
Modified:
   pypy/branch/pypy-more-rtti-inprogress/rpython/annlowlevel.py
   pypy/branch/pypy-more-rtti-inprogress/rpython/extfunc.py
   pypy/branch/pypy-more-rtti-inprogress/rpython/lltypesystem/rstr.py
   pypy/branch/pypy-more-rtti-inprogress/rpython/module/ll_os.py
   pypy/branch/pypy-more-rtti-inprogress/translator/c/database.py
   pypy/branch/pypy-more-rtti-inprogress/translator/c/node.py
   pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/rsandbox.py
   pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/sandlib.py
   pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandbox.py
   pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandlib.py
Log:
Change the level at which --sandbox'ing occurs.  Now, by default the
functions registered by _register_external() are replaced with a
sandboxing version, so that the low-level external functions should no
longer be seen at all.  The _register_external()'ed functions operate at
a slightly higher level - they have regular RPython objects as arguments
and result.  This is a more natural level that can be processed directly
by pypy.rlib.rmarshal with no special-casing needed.  This allows us to
get rid of the strange marshal_input/unmarshal_output hacks in ll_os.py.
As an extra bonus, the protocol used between the sandboxed process and
its parent is now based on the regular CPython marshal format.



Modified: pypy/branch/pypy-more-rtti-inprogress/rpython/annlowlevel.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/rpython/annlowlevel.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/rpython/annlowlevel.py	Fri Aug 17 15:05:56 2007
@@ -156,7 +156,8 @@
         self.pending.append((ll_function, graph, args_s, s_result))
         return graph
 
-    def delayedfunction(self, ll_function, args_s, s_result, needtype=False):
+    def delayedfunction(self, ll_function, args_s, s_result, needtype=False,
+                        **fnobjattrs):
         # get a delayed pointer to the low-level function, annotated as
         # specified.  The pointer is only valid after finish() was called.
         graph = self.getgraph(ll_function, args_s, s_result)
@@ -167,13 +168,13 @@
             FUNCTYPE = lltype.FuncType(ARGS, RESULT)
         else:
             FUNCTYPE = None
-        return self.graph2delayed(graph, FUNCTYPE)
+        return self.graph2delayed(graph, FUNCTYPE, **fnobjattrs)
 
     def constfunc(self, ll_function, args_s, s_result):
         p = self.delayedfunction(ll_function, args_s, s_result)
         return Constant(p, lltype.typeOf(p))
 
-    def graph2delayed(self, graph, FUNCTYPE=None):
+    def graph2delayed(self, graph, FUNCTYPE=None, **fnobjattrs):
         if FUNCTYPE is None:
             FUNCTYPE = lltype.ForwardReference()
         # obscure hack: embed the name of the function in the string, so
@@ -181,7 +182,7 @@
         # is really computed
         name = "delayed!%s" % (graph.name,)
         delayedptr = lltype._ptr(lltype.Ptr(FUNCTYPE), name, solid=True)
-        self.delayedfuncs.append((delayedptr, graph))
+        self.delayedfuncs.append((delayedptr, graph, fnobjattrs))
         return delayedptr
 
     def graph2const(self, graph):
@@ -259,8 +260,10 @@
         for p, repr, obj in self.delayedconsts:
             p._become(repr.convert_const(obj))
         rtyper.call_all_setups()
-        for p, graph in self.delayedfuncs:
+        for p, graph, fnobjattrs in self.delayedfuncs:
             real_p = rtyper.getcallable(graph)
+            for key, value in fnobjattrs.items():
+                setattr(rtyper.type_system.deref(real_p), key, value)
             REAL = lltype.typeOf(real_p).TO
             FUNCTYPE = lltype.typeOf(p).TO
             if isinstance(FUNCTYPE, lltype.ForwardReference):

Modified: pypy/branch/pypy-more-rtti-inprogress/rpython/extfunc.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/rpython/extfunc.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/rpython/extfunc.py	Fri Aug 17 15:05:56 2007
@@ -104,8 +104,12 @@
         impl = getattr(self, method_name, None)
         fakeimpl = getattr(self, fake_method_name, self.instance)
         if impl:
+            if rtyper.annotator.translator.config.translation.sandbox:
+                impl.dont_inline = True
             obj = rtyper.getannmixlevel().delayedfunction(
-                impl, signature_args, hop.s_result)
+                impl, signature_args, hop.s_result,
+                _name=self.name,
+                _safe_not_sandboxed=self.safe_not_sandboxed)
         else:
             #if not self.safe_not_sandboxed:
             #    print '>>>>>>>>>>>>>-----------------------------------'

Modified: pypy/branch/pypy-more-rtti-inprogress/rpython/lltypesystem/rstr.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/rpython/lltypesystem/rstr.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/rpython/lltypesystem/rstr.py	Fri Aug 17 15:05:56 2007
@@ -167,6 +167,7 @@
         debug_assert(i>=0, "negative str getitem index")
         debug_assert(i<len(chars), "str getitem index out of bound")
         return chars[i]
+    ll_stritem_nonneg._annenforceargs_ = [None, int]
 
     def ll_chr2str(ch):
         s = mallocstr(1)

Modified: pypy/branch/pypy-more-rtti-inprogress/rpython/module/ll_os.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/rpython/module/ll_os.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/rpython/module/ll_os.py	Fri Aug 17 15:05:56 2007
@@ -285,20 +285,6 @@
         self.register(os.read, [int, int], str, "ll_os.ll_os_read",
                       llimpl=os_read_lltypeimpl, oofakeimpl=os_read_oofakeimpl)
 
-        # '--sandbox' support
-        def os_read_marshal_input(msg, fd, buf, size):
-            msg.packnum(rffi.cast(lltype.Signed, fd))
-            msg.packsize_t(size)
-        def os_read_unmarshal_output(msg, fd, buf, size):
-            data = msg.nextstring()
-            if len(data) > rffi.cast(lltype.Signed, size):
-                raise OverflowError
-            for i in range(len(data)):
-                buf[i] = data[i]
-            return rffi.cast(rffi.SIZE_T, len(data))
-        os_read._obj._marshal_input = os_read_marshal_input
-        os_read._obj._unmarshal_output = os_read_unmarshal_output
-
     @registering(os.write)
     def register_os_write(self):
         os_write = rffi.llexternal('write', [rffi.INT, rffi.VOIDP,
@@ -326,12 +312,6 @@
                       "ll_os.ll_os_write", llimpl=os_write_lltypeimpl,
                       oofakeimpl=os_write_oofakeimpl)
 
-        # '--sandbox' support
-        def os_write_marshal_input(msg, fd, buf, size):
-            msg.packnum(rffi.cast(lltype.Signed, fd))
-            msg.packbuf(buf, 0, rffi.cast(lltype.Signed, size))
-        os_write._obj._marshal_input = os_write_marshal_input
-
     @registering(os.close)
     def register_os_close(self):
         os_close = rffi.llexternal('close', [rffi.INT], rffi.INT)
@@ -461,23 +441,6 @@
                       "ll_os.ll_os_getcwd", llimpl=os_getcwd_lltypeimpl,
                       oofakeimpl=os_getcwd_oofakeimpl)
 
-        # '--sandbox' support
-        def os_getcwd_marshal_input(msg, buf, bufsize):
-            msg.packsize_t(bufsize)
-        def os_getcwd_unmarshal_output(msg, buf, bufsize):
-            # the outside process should not send a result larger than
-            # the requested 'bufsize'
-            result = msg.nextstring()
-            n = len(result)
-            if rffi.cast(rffi.SIZE_T, n) >= bufsize:
-                raise OverflowError
-            for i in range(n):
-                buf[i] = result[i]
-            buf[n] = '\x00'
-            return buf
-        os_getcwd._obj._marshal_input = os_getcwd_marshal_input
-        os_getcwd._obj._unmarshal_output = os_getcwd_unmarshal_output
-
     @registering(os.listdir)
     def register_os_listdir(self):
         # we need a different approach on Windows and on Posix

Modified: pypy/branch/pypy-more-rtti-inprogress/translator/c/database.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/translator/c/database.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/translator/c/database.py	Fri Aug 17 15:05:56 2007
@@ -363,5 +363,9 @@
         return result
 
     def need_sandboxing(self, fnobj):
-        return self.sandbox and (
-            not getattr(fnobj, '_safe_not_sandboxed', False))
+        if not self.sandbox:
+            return False
+        if hasattr(fnobj, '_safe_not_sandboxed'):
+            return not fnobj._safe_not_sandboxed
+        else:
+            return "if_external"

Modified: pypy/branch/pypy-more-rtti-inprogress/translator/c/node.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/translator/c/node.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/translator/c/node.py	Fri Aug 17 15:05:56 2007
@@ -694,10 +694,28 @@
         del bodyiter
         funcgen.implementation_end()
 
+def sandbox_stub(fnobj, db):
+    # unexpected external function for --sandbox translation: replace it
+    # with a "Not Implemented" stub.  To support these functions, port them
+    # to the new style registry (e.g. rpython.module.ll_os.RegisterOs).
+    from pypy.translator.sandbox import rsandbox
+    graph = rsandbox.get_external_function_sandbox_graph(fnobj, db,
+                                                      force_stub=True)
+    return [FunctionCodeGenerator(graph, db)]
+
+def sandbox_transform(fnobj, db):
+    # for --sandbox: replace a function like os_open_lltypeimpl() with
+    # code that communicates with the external process to ask it to
+    # perform the operation.
+    from pypy.translator.sandbox import rsandbox
+    graph = rsandbox.get_external_function_sandbox_graph(fnobj, db)
+    return [FunctionCodeGenerator(graph, db)]
+
 def select_function_code_generators(fnobj, db, functionname):
     sandbox = db.need_sandboxing(fnobj)
     if hasattr(fnobj, '_external_name'):
-        assert not sandbox
+        if sandbox:
+            return sandbox_stub(fnobj, db)
         db.externalfuncs[fnobj._external_name] = fnobj
         return []
     elif fnobj._callable in extfunc.EXTERNALS:
@@ -710,35 +728,26 @@
                 or fnobj._name.startswith('ll_time_')   # XXX!! TEMPORARY!
                 or fnobj._name.startswith('ll_stack_')   # XXX!! TEMPORARY!
                 ):
-            # deprecated case: apply the sandbox transformation but don't
-            # try to support these extfuncs properly (we just build a
-            # "Not Implemented" stub).  To support these functions, port them
-            # to the new style registry (e.g. rpython.module.ll_os.RegisterOs).
-            from pypy.translator.sandbox import rsandbox
-            graph = rsandbox.get_external_function_sandbox_graph(fnobj, db,
-                                                              force_stub=True)
-            return [FunctionCodeGenerator(graph, db)]
+            return sandbox_stub(fnobj, db)
         db.externalfuncs[fnobj._callable] = fnobj
         return []
     elif getattr(fnobj._callable, 'suggested_primitive', False):
         raise ValueError, "trying to compile suggested primitive %r" % (
             fnobj._callable,)
     elif hasattr(fnobj, 'graph'):
+        if sandbox and sandbox != "if_external":
+            # apply the sandbox transformation
+            return sandbox_transform(fnobj, db)
         exception_policy = getattr(fnobj, 'exception_policy', None)
         return [FunctionCodeGenerator(fnobj.graph, db, exception_policy,
                                       functionname)]
     elif getattr(fnobj, 'external', None) == 'C':
+        if sandbox:
+            return sandbox_stub(fnobj, db)
         if hasattr(fnobj, 'includes'):
-            # apply the sandbox transformation
-            if sandbox:
-                from pypy.translator.sandbox import rsandbox
-                graph = rsandbox.get_external_function_sandbox_graph(fnobj, db)
-                return [FunctionCodeGenerator(graph, db)]
-            else:
-                return []   # assume no wrapper needed
+            return []   # assume no wrapper needed
         else:
             # deprecated case
-            assert not sandbox
             return [CExternalFunctionCodeGenerator(fnobj, db)]
     else:
         raise ValueError, "don't know how to generate code for %r" % (fnobj,)

Modified: pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/rsandbox.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/rsandbox.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/rsandbox.py	Fri Aug 17 15:05:56 2007
@@ -3,7 +3,7 @@
 trampolines that marshal their input arguments, dump them to STDOUT,
 and wait for an answer on STDIN.  Enable with 'translate.py --sandbox'.
 """
-from pypy.translator.sandbox.sandboxmsg import MessageBuilder, LLMessage
+from pypy.rlib import rmarshal
 
 # ____________________________________________________________
 #
@@ -45,102 +45,61 @@
         buf = rffi.cast(rffi.CCHARP, buf)
 writeall_not_sandboxed._annenforceargs_ = [int, rffi.CCHARP, int]
 
-def readall_not_sandboxed(fd, length):
-    buf = lltype.malloc(rffi.CCHARP.TO, length, flavor='raw')
-    p = buf
-    got = 0
-    while got < length:
-        size1 = rffi.cast(rffi.SIZE_T, length - got)
-        count = rffi.cast(lltype.Signed, ll_read_not_sandboxed(fd, p, size1))
+##def readall_not_sandboxed(fd, length):
+##    buf = lltype.malloc(rffi.CCHARP.TO, length, flavor='raw')
+##    p = buf
+##    got = 0
+##    while got < length:
+##        size1 = rffi.cast(rffi.SIZE_T, length - got)
+##        count = rffi.cast(lltype.Signed, ll_read_not_sandboxed(fd, p, size1))
+##        if count <= 0:
+##            raise IOError
+##        got += count
+##        p = lltype.direct_ptradd(lltype.direct_arrayitems(p), count)
+##        p = rffi.cast(rffi.CCHARP, p)
+##    return buf
+##readall_not_sandboxed._annenforceargs_ = [int, int]
+
+
+class FdLoader(rmarshal.Loader):
+    def __init__(self, fd):
+        rmarshal.Loader.__init__(self, "")
+        self.fd = fd
+        self.buflen = 4096
+
+    def need_more_data(self):
+        buflen = self.buflen
+        buf = lltype.malloc(rffi.CCHARP.TO, buflen, flavor='raw')
+        buflen = rffi.cast(rffi.SIZE_T, buflen)
+        count = ll_read_not_sandboxed(self.fd, buf, buflen)
+        count = rffi.cast(lltype.Signed, count)
         if count <= 0:
             raise IOError
-        got += count
-        p = lltype.direct_ptradd(lltype.direct_arrayitems(p), count)
-        p = rffi.cast(rffi.CCHARP, p)
-    return buf
-readall_not_sandboxed._annenforceargs_ = [int, int]
-
-def buf2num(buf, index=0):
-    c0 = ord(buf[index  ])
-    c1 = ord(buf[index+1])
-    c2 = ord(buf[index+2])
-    c3 = ord(buf[index+3])
-    if c0 >= 0x80:
-        c0 -= 0x100
-    return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3
+        self.buf += ''.join([buf[i] for i in range(count)])
+        self.buflen *= 2
 
-def build_default_marshal_input(FUNCTYPE, namehint, cache={}):
-    # return a default 'marshal_input' function
-    try:
-        return cache[FUNCTYPE]
-    except KeyError:
-        pass
-    unroll_args = []
-    for i, ARG in enumerate(FUNCTYPE.ARGS):
-        if ARG == rffi.INT:       # 'int' argument
-            methodname = "packnum"
-        elif ARG == rffi.SIZE_T:  # 'size_t' argument
-            methodname = "packsize_t"
-        elif ARG == rffi.CCHARP:  # 'char*' argument, assumed zero-terminated
-            methodname = "packccharp"
-        else:
-            raise NotImplementedError("external function %r argument type %s" %
-                                      (namehint, ARG))
-        unroll_args.append((i, methodname))
-    unroll_args = unrolling_iterable(unroll_args)
-
-    def marshal_input(msg, *args):
-        assert len(args) == len(FUNCTYPE.ARGS)
-        for index, methodname in unroll_args:
-            getattr(msg, methodname)(args[index])
-
-    cache[FUNCTYPE] = marshal_input
-    return marshal_input
-
-def unmarshal_int(msg):    return msg.nextnum()
-def unmarshal_size_t(msg): return msg.nextsize_t()
-def unmarshal_void(msg):   pass
-
-def build_default_unmarshal_output(FUNCTYPE, namehint,
-                                   cache={rffi.INT   : unmarshal_int,
-                                          rffi.SIZE_T: unmarshal_size_t,
-                                          lltype.Void: unmarshal_void}):
-    try:
-        return cache[FUNCTYPE.RESULT]
-    except KeyError:
-        raise NotImplementedError("external function %r return type %s" % (
-            namehint, FUNCTYPE.RESULT))
-
-CFalse = CDefinedIntSymbolic('0')    # hack hack
+##CFalse = CDefinedIntSymbolic('0')    # hack hack
 
-def sandboxed_io(msg):
+def sandboxed_io(buf):
     STDIN = 0
     STDOUT = 1
-    buf = msg.as_rffi_buf()
-    if CFalse:  # hack hack to force a method to be properly annotated/rtyped
-        msg.packstring(chr(CFalse) + chr(CFalse))
-        msg.packsize_t(rffi.cast(rffi.SIZE_T, CFalse))
-        msg.packbuf(buf, CFalse * 5, CFalse * 6)
-        msg.packccharp(rffi.str2charp(str(CFalse)))
-    try:
-        writeall_not_sandboxed(STDOUT, buf, msg.getlength())
-    finally:
-        lltype.free(buf, flavor='raw')
-    # wait for the answer
-    buf = readall_not_sandboxed(STDIN, 4)
+    # send the buffer with the marshalled fnname and input arguments to STDOUT
+    p = lltype.malloc(rffi.CCHARP.TO, len(buf), flavor='raw')
     try:
-        length = buf2num(buf)
+        for i in range(len(buf)):
+            p[i] = buf[i]
+        writeall_not_sandboxed(STDOUT, p, len(buf))
     finally:
-        lltype.free(buf, flavor='raw')
-    length -= 4     # the original length includes the header
-    if length < 0:
-        raise IOError
-    buf = readall_not_sandboxed(STDIN, length)
-    msg = LLMessage(buf, 0, length)
-    if CFalse:  # hack hack to force a method to be properly annotated/rtyped
-        msg.nextstring()
-        msg.nextsize_t()
-    return msg
+        lltype.free(p, flavor='raw')
+    # build a Loader that will get the answer from STDIN
+    loader = FdLoader(STDIN)
+    # check for errors
+    error = load_int(loader)
+    if error != 0:
+        raise IOError     # XXX add support to raise the correct exception
+    else:
+        # no exception; the caller will decode the actual result
+        return loader
 
 def not_implemented_stub(msg):
     STDERR = 2
@@ -150,40 +109,40 @@
     raise RuntimeError(msg)  # XXX in RPython, the msg is ignored at the moment
 not_implemented_stub._annenforceargs_ = [str]
 
+dump_string = rmarshal.get_marshaller(str)
+load_int    = rmarshal.get_loader(int)
+
 def get_external_function_sandbox_graph(fnobj, db, force_stub=False):
     """Build the graph of a helper trampoline function to be used
     in place of real calls to the external function 'fnobj'.  The
     trampoline marshals its input arguments, dumps them to STDOUT,
     and waits for an answer on STDIN.
     """
-    # XXX for now, only supports function with int and string arguments
-    # and returning an int or void.  Other cases need a custom
-    # _marshal_input and/or _unmarshal_output function on fnobj.
-    FUNCTYPE = lltype.typeOf(fnobj)
     fnname = fnobj._name
+    if hasattr(fnobj, 'graph'):
+        # get the annotation of the input arguments and the result
+        graph = fnobj.graph
+        annotator = db.translator.annotator
+        args_s = [annotator.binding(v) for v in graph.getargs()]
+        s_result = annotator.binding(graph.getreturnvar())
+    else:
+        # pure external function - fall back to the annotations
+        # corresponding to the ll types
+        FUNCTYPE = lltype.typeOf(fnobj)
+        args_s = [annmodel.lltype_to_annotation(ARG) for ARG in FUNCTYPE.ARGS]
+        s_result = annmodel.lltype_to_annotation(FUNCTYPE.RESULT)
+
     try:
         if force_stub:   # old case - don't try to support suggested_primitive
-            raise NotImplementedError("external function '%s' using "
-                                      "deprecated 'suggested_primitive'" % (
-                fnname,))
-        if hasattr(fnobj, '_marshal_input'):
-            marshal_input = fnobj._marshal_input
-        else:
-            marshal_input = build_default_marshal_input(FUNCTYPE, fnname)
-        if hasattr(fnobj, '_unmarshal_output'):
-            # _unmarshal_output() also receives the input arguments of the
-            # original call because some functions need them to decode the
-            # result properly.
-            unmarshal_output = fnobj._unmarshal_output
-            unmarshal_takes_input_args = True
-        else:
-            # The default unmarshal_output do not receive the original input
-            # arguments because they would cause annotation troubles
-            # (see e.g. test_sandbox_3).
-            unmarshal_output = build_default_unmarshal_output(FUNCTYPE, fnname)
-            unmarshal_takes_input_args = False
+            raise NotImplementedError("sandboxing for external function '%s'"
+                                      % (fnname,))
+
+        dump_arguments = rmarshal.get_marshaller(tuple(args_s))
+        load_result = rmarshal.get_loader(s_result)
 
-    except NotImplementedError, e:
+    except (NotImplementedError,
+            rmarshal.CannotMarshal,
+            rmarshal.CannotUnmarshall), e:
         msg = 'Not Implemented: %s' % (e,)
         log.WARNING(msg)
         def execute(*args):
@@ -191,29 +150,19 @@
 
     else:
         def execute(*args):
-            # marshal the input arguments
-            msg = MessageBuilder()
-            msg.packstring(fnname)
-            marshal_input(msg, *args)
+            # marshal the function name and input arguments
+            buf = []
+            dump_string(buf, fnname)
+            dump_arguments(buf, args)
             # send the buffer and wait for the answer
-            msg = sandboxed_io(msg)
-            try:
-                # decode the answer
-                errcode = msg.nextnum()
-                if errcode != 0:
-                    raise IOError
-                if unmarshal_takes_input_args:
-                    result = unmarshal_output(msg, *args)
-                else:
-                    result = unmarshal_output(msg)
-            finally:
-                lltype.free(msg.value, flavor='raw')
+            loader = sandboxed_io(buf)
+            # decode the answer
+            result = load_result(loader)
+            loader.check_finished()
             return result
     execute = func_with_new_name(execute, 'sandboxed_' + fnname)
 
     ann = MixLevelHelperAnnotator(db.translator.rtyper)
-    args_s = [annmodel.lltype_to_annotation(ARG) for ARG in FUNCTYPE.ARGS]
-    s_result = annmodel.lltype_to_annotation(FUNCTYPE.RESULT)
     graph = ann.getgraph(execute, args_s, s_result)
     ann.finish()
     return graph

Modified: pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/sandlib.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/sandlib.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/sandlib.py	Fri Aug 17 15:05:56 2007
@@ -4,9 +4,27 @@
 for the outer process, which can run CPython or PyPy.
 """
 
+import marshal, sys
 from py.compat import subprocess
-from pypy.translator.sandbox.sandboxmsg import Message, encode_message
-from pypy.translator.sandbox.sandboxmsg import read_message
+
+def read_message(f, timeout=None):
+    # warning: 'timeout' is not really reliable and should only be used
+    # for testing.  Also, it doesn't work if the file f does any buffering.
+    if timeout is not None:
+        import select
+        iwtd, owtd, ewtd = select.select([f], [], [], timeout)
+        if not iwtd:
+            raise EOFError("timed out waiting for data")
+    return marshal.load(f)
+
+if sys.version_info < (2, 4):
+    def write_message(g, msg):
+        marshal.dump(msg, g)
+        g.flush()
+else:
+    def write_message(g, msg):
+        marshal.dump(msg, g, 0)
+        g.flush()
 
 class SandboxedProc(object):
     """Base class to control a sandboxed subprocess.
@@ -36,38 +54,21 @@
     def handle_until_return(self):
         while True:
             try:
-                msg = read_message(self.popen.stdout)
+                fnname = read_message(self.popen.stdout)
+                args   = read_message(self.popen.stdout)
             except EOFError, e:
                 break
-            answer = self.handle_message(msg)
-            self.popen.stdin.write(answer)
+            answer = self.handle_message(fnname, *args)
+            write_message(self.popen.stdin, 0)  # error code - always 0 for now
+            write_message(self.popen.stdin, answer)
         returncode = self.popen.wait()
         return returncode
 
-    def handle_message(self, msg):
-        fn = msg.nextstring()
-        try:
-            argtypes, restypes = self.TYPES[fn]
-        except KeyError:
-            raise IOError("trying to invoke unknown external function %r" % (
-                fn,))
-        handler = getattr(self, 'do_' + fn)
-        answers = handler(*msg.decode(argtypes))
-        if len(restypes) == 0:
-            assert answers is None
-            answers = (0,)
-        elif len(restypes) == 1:
-            answers = (0, answers)
-        else:
-            answers = (0,) + answers
-        return encode_message("i" + restypes, answers)
-
-    TYPES = {
-        "open": ("sii", "i"),
-        "read": ("iI", "s"),
-        "write": ("is", "I"),
-        "close": ("i", "i"),
-        }
+    def handle_message(self, fnname, *args):
+        if '__' in fnname:
+            raise ValueError("unsafe fnname")
+        handler = getattr(self, 'do_' + fnname.replace('.', '__'))
+        return handler(*args)
 
 
 class SimpleIOSandboxedProc(SandboxedProc):
@@ -111,7 +112,7 @@
         self._output = None
         self._error = None
 
-    def do_read(self, fd, size):
+    def do_ll_os__ll_os_read(self, fd, size):
         if fd == 0:
             if self._input is None:
                 return ""
@@ -119,7 +120,7 @@
                 return self._input.read(size)
         raise OSError("trying to read from fd %d" % (fd,))
 
-    def do_write(self, fd, data):
+    def do_ll_os__ll_os_write(self, fd, data):
         if fd == 1:
             self._output.write(data)
             return len(data)

Modified: pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandbox.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandbox.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandbox.py	Fri Aug 17 15:05:56 2007
@@ -1,34 +1,28 @@
+import py
 import sys, os
 import struct
 
 from pypy.rpython.lltypesystem import rffi
-from pypy.translator.sandbox.sandboxmsg import Message, MessageBuilder
-from pypy.translator.sandbox.sandboxmsg import read_message
 from pypy.translator.interactive import Translation
+from pypy.translator.sandbox.sandlib import read_message, write_message
 
+def expect(f, g, fnname, args, result, resulttype=None):
+    msg = read_message(f, timeout=10.0)
+    assert msg == fnname
+    msg = read_message(f, timeout=10.0)
+    assert msg == args
+    write_message(g, 0)
+    if resulttype is None:
+        write_message(g, result)
+    else:
+        # use the exact result type for encoding
+        from pypy.rlib.rmarshal import get_marshaller
+        buf = []
+        get_marshaller(resulttype)(buf, result)
+        g.write(''.join(buf))
 
-def test_sandbox_message():
-    def num(n):
-        return struct.pack("!i", n)
-    msg = MessageBuilder()
-    msg.packstring("open")
-    msg.packccharp(rffi.str2charp("/tmp/foobar"))
-    msg.packnum(123)
-    res = msg.getvalue()
-    assert res == (num(len(res)) +
-                   "s" + num(4) + "open" +
-                   "s" + num(11) + "/tmp/foobar" +
-                   "i" + num(123))
-
-    msg = Message(res[4:])
-    m1 = msg.nextstring()
-    assert m1 == "open"
-    m2 = msg.nextstring()
-    assert m2 == "/tmp/foobar"
-    m3 = msg.nextnum()
-    assert m3 == 123
 
-def test_sandbox():
+def test_sandbox_1():
     def entry_point(argv):
         fd = os.open("/tmp/foobar", os.O_RDONLY, 0777)
         assert fd == 77
@@ -39,29 +33,8 @@
     t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
     exe = t.compile()
     g, f = os.popen2(exe, "t", 0)
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "open"
-    m2 = msg.nextstring()
-    assert m2 == "/tmp/foobar"
-    m3 = msg.nextnum()
-    assert m3 == os.O_RDONLY
-    m4 = msg.nextnum()
-    assert m4 == 0777
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packnum(77).getvalue())
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "dup"
-    m2 = msg.nextnum()
-    assert m2 == 77
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packnum(78).getvalue())
-
+    expect(f, g, "ll_os.ll_os_open", ("/tmp/foobar", os.O_RDONLY, 0777), 77)
+    expect(f, g, "ll_os.ll_os_dup",  (77,), 78)
     g.close()
     tail = f.read()
     f.close()
@@ -81,51 +54,10 @@
     t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
     exe = t.compile()
     g, f = os.popen2(exe, "t", 0)
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "open"
-    m2 = msg.nextstring()
-    assert m2 == "/tmp/foobar"
-    m3 = msg.nextnum()
-    assert m3 == os.O_RDONLY
-    m4 = msg.nextnum()
-    assert m4 == 0777
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packnum(77).getvalue())
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "read"
-    m2 = msg.nextnum()
-    assert m2 == 77
-    m3 = msg.nextsize_t()
-    assert m3 == 123
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packstring("he\x00llo").getvalue())
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "write"
-    m2 = msg.nextnum()
-    assert m2 == 77
-    m3 = msg.nextstring()
-    assert m3 == "world\x00!\x00"
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packsize_t(42).getvalue())
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "close"
-    m2 = msg.nextnum()
-    assert m2 == 77
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packnum(0).getvalue())
-
+    expect(f, g, "ll_os.ll_os_open",  ("/tmp/foobar", os.O_RDONLY, 0777), 77)
+    expect(f, g, "ll_os.ll_os_read",  (77, 123), "he\x00llo")
+    expect(f, g, "ll_os.ll_os_write", (77, "world\x00!\x00"), 42)
+    expect(f, g, "ll_os.ll_os_close", (77,), None)
     g.close()
     tail = f.read()
     f.close()
@@ -140,29 +72,31 @@
     t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
     exe = t.compile()
     g, f = os.popen2(exe, "t", 0)
+    expect(f, g, "ll_os.ll_os_dup2",   (34, 56), None)
+    expect(f, g, "ll_os.ll_os_access", ("spam", 77), True)
+    g.close()
+    tail = f.read()
+    f.close()
+    assert tail == ""
 
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "dup2"
-    m2 = msg.nextnum()
-    assert m2 == 34
-    m3 = msg.nextnum()
-    assert m3 == 56
-    assert msg.end()
-
-    g.write(MessageBuilder().packnum(0).packnum(0).getvalue())
-
-    msg = read_message(f, timeout=10.0)
-    m1 = msg.nextstring()
-    assert m1 == "access"
-    m2 = msg.nextstring()
-    assert m2 == "spam"
-    m3 = msg.nextnum()
-    assert m3 == 77
-    assert msg.end()
+def test_sandbox_4():
+    from pypy.rpython.module.ll_os_stat import STAT_FIELDS, s_tuple_StatResult
+    from pypy.rlib.rarithmetic import r_longlong
+    r0x12380000007 = r_longlong(0x12380000007)
 
-    g.write(MessageBuilder().packnum(0).packnum(0).getvalue())
+    def entry_point(argv):
+        st = os.stat("somewhere")
+        os.ftruncate(st.st_mode, st.st_size)  # nonsense, just to see outside
+        return 0
 
+    t = Translation(entry_point, backend='c', standalone=True, sandbox=True)
+    exe = t.compile()
+    g, f = os.popen2(exe, "t", 0)
+    sttuple = (55, 0, 0, 0, 0, 0, 0x12380000007, 0, 0, 0)
+    sttuple += (0,) * (len(STAT_FIELDS)-len(sttuple))
+    expect(f, g, "ll_os.ll_os_stat", ("somewhere",), sttuple,
+           resulttype = s_tuple_StatResult)
+    expect(f, g, "ll_os.ll_os_ftruncate", (55, 0x12380000007), None)
     g.close()
     tail = f.read()
     f.close()

Modified: pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandlib.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandlib.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/translator/sandbox/test/test_sandlib.py	Fri Aug 17 15:05:56 2007
@@ -1,3 +1,4 @@
+import py
 import os, StringIO
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.rpython.lltypesystem import rffi
@@ -23,14 +24,10 @@
             return output
         return func_with_new_name(do_xxx, 'do_%s' % name)
 
-    do_open  = _make_method("open")
-    do_read  = _make_method("read")
-    do_write = _make_method("write")
-    do_close = _make_method("close")
-    do_foobar = _make_method("foobar")
-
-    TYPES = SandboxedProc.TYPES.copy()
-    TYPES["foobar"] = "s", "i"
+    do_ll_os__ll_os_open  = _make_method("open")
+    do_ll_os__ll_os_read  = _make_method("read")
+    do_ll_os__ll_os_write = _make_method("write")
+    do_ll_os__ll_os_close = _make_method("close")
 
 
 def test_lib():
@@ -56,12 +53,13 @@
         ("write", (77, exe), 61),
         ("write", (77, "x1"), 61),
         ("write", (77, "y2"), 61),
-        ("close", (77,), 0),
+        ("close", (77,), None),
         ])
     proc.handle_forever()
     assert proc.seen == len(proc.expected)
 
 def test_foobar():
+    py.test.skip("to be updated")
     foobar = rffi.llexternal("foobar", [rffi.CCHARP], rffi.LONG)
     def entry_point(argv):
         s = rffi.str2charp(argv[1]); n = foobar(s); rffi.free_charp(s)



More information about the Pypy-commit mailing list