Author: Tobias Pape <[email protected]>
Branch:
Changeset: r76:77ba2d81ffc9
Date: 2013-02-20 16:09 +0100
http://bitbucket.org/pypy/lang-smalltalk/changeset/77ba2d81ffc9/
Log: merge
diff --git a/spyvm/fixedstack.py b/spyvm/fixedstack.py
deleted file mode 100644
--- a/spyvm/fixedstack.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""
-A Fixed stack for SPy.
-"""
-
-import types
-
-from rpython.rlib import jit
-from rpython.rlib.rarithmetic import r_uint
-
-class FixedStack(object):
- # _annspecialcase_ = "specialize:ctr_location" # polymorphic
-
- def __init__(self):
- pass
-
- def setup(self, stacksize):
- self.ptr = r_uint(0) # we point after the last element
- self.items = [None] * stacksize
-
- def clone(self):
- # this is only needed if we support flow space
- s = self.__class__()
- s.setup(len(self.items))
- for item in self.items[:self.ptr]:
- try:
- item = item.clone()
- except AttributeError:
- pass
- s.push(item)
- return s
-
- def push(self, item):
- ptr = jit.promote(self.ptr)
- self.items[ptr] = item
- self.ptr = ptr + 1
-
- def pop(self):
- ptr = jit.promote(self.ptr) - 1
- ret = self.items[ptr] # you get OverflowError if the stack is empty
- self.items[ptr] = None
- self.ptr = ptr
- return ret
-
- @jit.unroll_safe
- def drop(self, n):
- jit.promote(self.ptr)
- while n > 0:
- n -= 1
- self.ptr -= 1
- self.items[self.ptr] = None
-
- def top(self, position=0):
- # for a fixed stack, we assume correct indices
- rpos = r_uint(position)
- return self.items[self.ptr + ~rpos]
-
- def set_top(self, value, position=0):
- # for a fixed stack, we assume correct indices
- rpos = r_uint(position)
- self.items[self.ptr + ~rpos] = value
-
- def depth(self):
- return self.ptr
-
- def empty(self):
- return not self.ptr
-
-
diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -370,7 +370,7 @@
self.pop()
# closure bytecodes
- def pushNewArrayPopIntoArray(self, interp):
+ def pushNewArrayBytecode(self, interp):
arraySize, popIntoArray = splitter[7, 1](self.getbytecode())
newArray = None
if popIntoArray == 1:
@@ -388,19 +388,19 @@
w_indirectTemps = self.gettemp(index_of_array)
return index_in_array, w_indirectTemps
- def pushTempAtInTempVectorAt(self, interp):
+ def pushRemoteTempLongBytecode(self, interp):
index_in_array, w_indirectTemps = self._extract_index_and_temps()
self.push(w_indirectTemps.at0(self.space, index_in_array))
- def storeTempAtInTempVectorAt(self, interp):
+ def storeRemoteTempLongBytecode(self, interp):
index_in_array, w_indirectTemps = self._extract_index_and_temps()
w_indirectTemps.atput0(self.space, index_in_array, self.top())
- def popAndStoreTempAtInTempVectorAt(self, interp):
+ def storeAndPopRemoteTempLongBytecode(self, interp):
index_in_array, w_indirectTemps = self._extract_index_and_temps()
w_indirectTemps.atput0(self.space, index_in_array, self.pop())
- def pushClosureNumCopiedNumArgsBlockSize(self, interp):
+ def pushClosureCopyCopiedValuesBytecode(self, interp):
""" Copied from Blogpost:
http://www.mirandabanda.org/cogblog/2008/07/22/closures-part-ii-the-bytecodes/
ContextPart>>pushClosureCopyNumCopiedValues: numCopied numArgs:
numArgs blockSize: blockSize
"Simulate the action of a 'closure copy' bytecode whose result is the
@@ -427,16 +427,8 @@
i = self.getbytecode()
blockSize = (j << 8) | i
#create new instance of BlockClosure
- BlockClosureShadow = space.w_BlockClosure.as_class_get_shadow(space)
- w_closure = BlockClosureShadow.new(numCopied)
- closure = wrapper.BlockClosureWrapper(space, w_closure)
- closure.store_outerContext(self._w_self)
- closure.store_startpc(self.pc())
- closure.store_numArgs(numArgs)
- if numCopied > 0:
- copiedValues = self.pop_and_return_n(numCopied)
- for i0 in range(numCopied):
- closure.atput0(i0, copiedValues[i0])
+ w_closure, closure = space.newClosure(self._w_self, self.pc(),
numArgs,
+ self.pop_and_return_n(numCopied))
self.push(w_closure)
self.jump(blockSize)
@@ -572,12 +564,12 @@
(135, "popStackBytecode"),
(136, "duplicateTopBytecode"),
(137, "pushActiveContextBytecode"),
- (138, "pushNewArrayPopIntoArray"),
+ (138, "pushNewArrayBytecode"),
(139, "experimentalBytecode"),
- (140, "pushTempAtInTempVectorAt"),
- (141, "storeTempAtInTempVectorAt"),
- (142, "popAndStoreTempAtInTempVectorAt"),
- (143, "pushClosureNumCopiedNumArgsBlockSize"),
+ (140, "pushRemoteTempLongBytecode"),
+ (141, "storeRemoteTempLongBytecode"),
+ (142, "storeAndPopRemoteTempLongBytecode"),
+ (143, "pushClosureCopyCopiedValuesBytecode"),
(144, 151, "shortUnconditionalJump"),
(152, 159, "shortConditionalJump"),
(160, 167, "longUnconditionalJump"),
diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -308,10 +308,9 @@
@objectmodel.specialize.arg(2)
def as_special_get_shadow(self, space, TheClass):
shadow = self._shadow
- if shadow is None:
- shadow = self.attach_shadow_of_class(space, TheClass)
- elif not isinstance(shadow, TheClass):
- shadow.detach_shadow()
+ if not isinstance(shadow, TheClass):
+ if shadow is not None:
+ shadow.detach_shadow()
shadow = self.attach_shadow_of_class(space, TheClass)
shadow.sync_shadow()
return shadow
diff --git a/spyvm/objspace.py b/spyvm/objspace.py
--- a/spyvm/objspace.py
+++ b/spyvm/objspace.py
@@ -1,6 +1,4 @@
-from spyvm import constants
-from spyvm import model
-from spyvm import shadow
+from spyvm import constants, model, shadow, wrapper
from spyvm.error import UnwrappingError, WrappingError
from rpython.rlib.objectmodel import instantiate
from rpython.rlib.rarithmetic import intmask, r_uint
@@ -254,9 +252,27 @@
elif isinstance(w_v, model.W_SmallInteger): return float(w_v.value)
raise UnwrappingError()
+ def unwrap_array(self, w_array):
+ # Check that our argument has pointers format and the class:
+ if not w_array.getclass(self).is_same_object(self.w_Array):
+ raise PrimitiveFailedError()
+ assert isinstance(w_array, model.W_PointersObject)
+
+ return [w_array.at0(self, i) for i in range(w_array.size())]
+
def _freeze_(self):
return True
+ def newClosure(self, outerContext, pc, numArgs, copiedValues):
+ BlockClosureShadow = self.w_BlockClosure.as_class_get_shadow(self)
+ w_closure = BlockClosureShadow.new(len(copiedValues))
+ closure = wrapper.BlockClosureWrapper(self, w_closure)
+ closure.store_outerContext(outerContext)
+ closure.store_startpc(pc)
+ closure.store_numArgs(numArgs)
+ for i0 in range(len(copiedValues)):
+ closure.atput0(i0, copiedValues[i0])
+ return w_closure, closure
def bootstrap_class(space, instsize, w_superclass=None, w_metaclass=None,
name='?', format=shadow.POINTERS, varsized=False):
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -97,6 +97,9 @@
elif spec is str:
assert isinstance(w_arg, model.W_BytesObject)
args += (w_arg.as_string(), )
+ elif spec is list:
+ assert isinstance(w_arg, model.W_PointersObject)
+ args += (interp.space.unwrap_array(w_arg), )
elif spec is char:
args += (unwrap_char(w_arg), )
else:
@@ -829,25 +832,20 @@
frame.pop()
finalize_block_ctx(interp, s_block_ctx, frame.w_self())
-@expose_primitive(VALUE_WITH_ARGS, unwrap_spec=[object, object],
+@expose_primitive(VALUE_WITH_ARGS, unwrap_spec=[object, list],
no_result=True)
-def func(interp, w_block_ctx, w_args):
+def func(interp, w_block_ctx, args_w):
assert isinstance(w_block_ctx, model.W_PointersObject)
s_block_ctx = w_block_ctx.as_blockcontext_get_shadow(interp.space)
exp_arg_cnt = s_block_ctx.expected_argument_count()
- # Check that our arguments have pointers format and the right size:
- if not w_args.getclass(interp.space).is_same_object(
- interp.space.w_Array):
- raise PrimitiveFailedError()
- if w_args.size() != exp_arg_cnt:
+ if len(args_w) != exp_arg_cnt:
raise PrimitiveFailedError()
- assert isinstance(w_args, model.W_PointersObject)
# Push all the items from the array
for i in range(exp_arg_cnt):
- s_block_ctx.push(w_args.at0(interp.space, i))
+ s_block_ctx.push(args_w[i])
# XXX Check original logic. Image does not test this anyway
# because falls back to value + internal implementation
@@ -913,6 +911,80 @@
return w_rcvr
# ___________________________________________________________________________
+# BlockClosure Primitives
+
+CLOSURE_COPY_WITH_COPIED_VALUES = 200
+CLOSURE_VALUE = 201
+CLOSURE_VALUE_ = 202
+CLOSURE_VALUE_VALUE = 203
+CLOSURE_VALUE_VALUE_VALUE = 204
+CLOSURE_VALUE_VALUE_VALUE_VALUE = 205
+CLOSURE_VALUE_WITH_ARGS = 206 #valueWithArguments:
+CLOSURE_VALUE_NO_CONTEXT_SWITCH = 221
+CLOSURE_VALUE_NO_CONTEXT_SWITCH_ = 222
+
+@expose_primitive(CLOSURE_COPY_WITH_COPIED_VALUES, unwrap_spec=[object, int,
list])
+def func(interp, outerContext, numArgs, copiedValues):
+ frame = interp.s_active_context()
+ w_context, s_context = interp.space.newClosure(outerContext, frame.pc(),
+ numArgs, copiedValues)
+ frame.push(w_context)
+
+
+def activateClosure(w_block_closure, args_w, mayContextSwitch=True):
+ if not w_block_closure.getclass(interp.space).is_same_object(
+ interp.space.w_BlockClosure):
+ raise PrimitiveFailedError()
+ if not w_block_closure.numArgs == len(args_w):
+ raise PrimitiveFailedError()
+ if not w_block_closure.outerContext.getclass(interp.space).issubclass(
+ interp.space.w_ContextPart):
+ raise PrimitiveFailedError()
+ w_closureMethod = w_block_closure.w_method()
+ assert isinstance(w_closureMethod, W_CompiledMethod)
+ assert w_block_closure is not w_block_closure.outerContext
+ numCopied = w_block_closure.size()
+
+ s_block_closure = w_block_closure.as_blockclosure_get_shadow(interp.space)
+ s_block_closure.push_all(args_w)
+
+ s_block_closure.store_pc(s_block_closure.initialip())
+ s_block_closure.store_w_sender(frame)
+
+
+@expose_primitive(CLOSURE_VALUE, unwrap_spec=[object])
+def func(interp, w_block_closure):
+ activateClosure(w_block_closure, [])
+
+@expose_primitive(CLOSURE_VALUE_, unwrap_spec=[object, object])
+def func(interp, w_block_closure, w_a0):
+ activateClosure(w_block_closure, [w_a0])
+
+@expose_primitive(CLOSURE_VALUE_VALUE, unwrap_spec=[object, object, object])
+def func(interp, w_block_closure, w_a0, w_a1):
+ activateClosure(w_block_closure, [w_a0, w_a1])
+
+@expose_primitive(CLOSURE_VALUE_VALUE_VALUE, unwrap_spec=[object, object,
object, object])
+def func(interp, w_block_closure, w_a0, w_a1, w_a2):
+ activateClosure(w_block_closure, [w_a0, w_a1, w_a2])
+
+@expose_primitive(CLOSURE_VALUE_VALUE_VALUE_VALUE, unwrap_spec=[object,
object, object, object, object])
+def func(interp, w_block_closure, w_a0, w_a1, w_a2, w_a3):
+ activateClosure(w_block_closure, [w_a0, w_a1, w_a2, w_a3])
+
+@expose_primitive(CLOSURE_VALUE_WITH_ARGS, unwrap_spec=[object, list])
+def func(interp, w_block_closure, args_w):
+ activateClosure(w_block_closure, args_w)
+
+@expose_primitive(CLOSURE_VALUE_NO_CONTEXT_SWITCH, unwrap_spec=[object])
+def func(interp, w_block_closure):
+ activateClosure(w_block_closure, [], mayContextSwitch=False)
+
+@expose_primitive(CLOSURE_VALUE_NO_CONTEXT_SWITCH_, unwrap_spec=[object,
object])
+def func(interp, w_block_closure, w_a0):
+ activateClosure(w_block_closure, [w_a0], mayContextSwitch=False)
+
+# ___________________________________________________________________________
# PrimitiveLoadInstVar
#
# These are some wacky bytecodes in squeak. They are defined to do
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -317,10 +317,8 @@
__metaclass__ = extendabletype
def __init__(self, space, w_self):
- from spyvm.fixedstack import FixedStack
self._w_sender = space.w_nil
- self._stack = FixedStack()
self.currentBytecode = -1
AbstractRedirectingShadow.__init__(self, space, w_self)
@@ -339,7 +337,7 @@
if n0 == constants.CTXPART_STACKP_INDEX:
return self.wrap_stackpointer()
if self.stackstart() <= n0 < self.external_stackpointer():
- return self._stack.top(self.stackdepth() - (n0-self.stackstart())
- 1)
+ return self.peek(self.stackdepth() - (n0-self.stackstart()) - 1)
if self.external_stackpointer() <= n0 < self.stackend():
return self.space.w_nil
else:
@@ -354,7 +352,7 @@
if n0 == constants.CTXPART_STACKP_INDEX:
return self.unwrap_store_stackpointer(w_value)
if self.stackstart() <= n0 < self.external_stackpointer():
- return self._stack.set_top(w_value,
+ return self.set_top(w_value,
self.stackdepth() -
(n0-self.stackstart()) - 1)
return
if self.external_stackpointer() <= n0 < self.stackend():
@@ -375,10 +373,10 @@
if size < depth:
# TODO Warn back to user
assert size >= 0
- self._stack.drop(depth - size)
+ self.pop_n(depth - size)
else:
for i in range(depth, size):
- self._stack.push(self.space.w_nil)
+ self.push(self.space.w_nil)
def wrap_stackpointer(self):
return self.space.wrap_int(self.stackdepth() +
@@ -475,20 +473,32 @@
def settemp(self, index, w_value):
self.s_home().settemp(index, w_value)
+ @jit.unroll_safe
+ def init_stack_and_temps(self):
+ stacksize = self.stackend() - self.stackstart()
+ tempsize = self.tempsize()
+ self._temps_and_stack = [None] * (stacksize + tempsize)
+ for i in range(tempsize):
+ self._temps_and_stack[i] = self.space.w_nil
+ self._stack_ptr = rarithmetic.r_uint(tempsize) # we point after the
last element
+
# ______________________________________________________________________
# Stack Manipulation
- def init_stack(self):
- self._stack.setup(self.stackend() - self.stackstart())
-
def stack(self):
"""NOT_RPYTHON""" # purely for testing
- return self._stack.items[:self.stackdepth()]
+ return self._temps_and_stack[self.tempsize():self._stack_ptr]
def pop(self):
- return self._stack.pop()
+ ptr = jit.promote(self._stack_ptr) - 1
+ ret = self._temps_and_stack[ptr] # you get OverflowError if the
stack is empty
+ self._temps_and_stack[ptr] = None
+ self._stack_ptr = ptr
+ return ret
def push(self, w_v):
- self._stack.push(w_v)
+ ptr = jit.promote(self._stack_ptr)
+ self._temps_and_stack[ptr] = w_v
+ self._stack_ptr = ptr + 1
def push_all(self, lst):
for elt in lst:
@@ -496,18 +506,29 @@
def top(self):
return self.peek(0)
-
+
+ def set_top(self, value, position=0):
+ rpos = rarithmetic.r_uint(position)
+ self._temps_and_stack[self._stack_ptr + ~rpos] = value
+
def peek(self, idx):
- return self._stack.top(idx)
+ rpos = rarithmetic.r_uint(idx)
+ return self._temps_and_stack[self._stack_ptr + ~rpos]
+ @jit.unroll_safe
def pop_n(self, n):
- self._stack.drop(n)
+ jit.promote(self._stack_ptr)
+ while n > 0:
+ n -= 1
+ self._stack_ptr -= 1
+ self._temps_and_stack[self._stack_ptr] = None
def stackdepth(self):
- return rarithmetic.intmask(self._stack.depth())
+ return rarithmetic.intmask(self._stack_ptr - self.tempsize())
+ @jit.unroll_safe
def pop_and_return_n(self, n):
- result = [self._stack.top(i) for i in range(n - 1, -1, -1)]
+ result = [self.peek(i) for i in range(n - 1, -1, -1)]
self.pop_n(n)
return result
@@ -533,7 +554,7 @@
s_result.store_initialip(initialip)
s_result.store_w_home(w_home)
s_result.store_pc(initialip)
- s_result.init_stack()
+ s_result.init_stack_and_temps()
return w_result
def fetch(self, n0):
@@ -559,7 +580,7 @@
def attach_shadow(self):
# Make sure the home context is updated first
self.copy_from_w_self(constants.BLKCTX_HOME_INDEX)
- self.init_stack()
+ self.init_stack_and_temps()
ContextPartShadow.attach_shadow(self)
def unwrap_store_initialip(self, w_value):
@@ -617,6 +638,7 @@
ContextPartShadow.__init__(self, space, w_self)
@staticmethod
+ @jit.unroll_safe
def make_context(space, w_method, w_receiver,
arguments, w_sender=None):
# From blue book: normal mc have place for 12 temps+maxstack
@@ -634,10 +656,9 @@
s_result.store_w_sender(w_sender)
s_result.store_w_receiver(w_receiver)
s_result.store_pc(0)
- s_result._temps = [space.w_nil] * w_method.tempsize
+ s_result.init_stack_and_temps()
for i in range(len(arguments)):
s_result.settemp(i, arguments[i])
- s_result.init_stack()
return w_result
def fetch(self, n0):
@@ -672,9 +693,7 @@
def attach_shadow(self):
# Make sure the method is updated first
self.copy_from_w_self(constants.MTHDCTX_METHOD)
- self.init_stack()
- # And that there is space for the temps
- self._temps = [self.space.w_nil] * self.tempsize()
+ self.init_stack_and_temps()
ContextPartShadow.attach_shadow(self)
def tempsize(self):
@@ -694,10 +713,10 @@
self._w_receiver = w_receiver
def gettemp(self, index0):
- return self._temps[index0]
+ return self._temps_and_stack[index0]
def settemp(self, index0, w_value):
- self._temps[index0] = w_value
+ self._temps_and_stack[index0] = w_value
def w_home(self):
return self.w_self()
diff --git a/spyvm/test/test_interpreter.py b/spyvm/test/test_interpreter.py
--- a/spyvm/test/test_interpreter.py
+++ b/spyvm/test/test_interpreter.py
@@ -831,7 +831,7 @@
option.bc_trace = bc_trace
# Closure Bytecodes
-def test_bc_pushNewArrayPopIntoArray(bytecode=pushNewArrayPopIntoArray):
+def test_bc_pushNewArrayBytecode(bytecode=pushNewArrayBytecode):
interp = new_interpreter(bytecode + chr(0x83))
context = interp.s_active_context()
context.push(fakeliterals(space, "egg"))
@@ -843,7 +843,7 @@
assert array.at0(space, 1) == fakeliterals(space, "bar")
assert array.at0(space, 2) == fakeliterals(space, "baz")
-def test_bc_pushNewArray(bytecode=pushNewArrayPopIntoArray):
+def test_bc_pushNewArray(bytecode=pushNewArrayBytecode):
interp = new_interpreter(bytecode + chr(0x07))
context = interp.s_active_context()
interp.step(interp.s_active_context())
@@ -851,7 +851,7 @@
assert array.size() == 7
assert array.at0(space, 0) == space.w_nil
-def test_pushTempAt0InTempVectorAt0(bytecode = pushTempAtInTempVectorAt):
+def test_bc_pushRemoteTempLongBytecode(bytecode = pushRemoteTempLongBytecode):
interp = new_interpreter(bytecode + chr(0) + chr(0))
context = interp.s_active_context()
context.push(fakeliterals(space, "jam"))
@@ -871,21 +871,21 @@
interp.step(context)
return context, temp_array
-def test_pushTempAt3InTempVectorAt1(bytecode = pushTempAtInTempVectorAt):
+def test_bc_pushRemoteTempLongBytecode2(bytecode = pushRemoteTempLongBytecode):
context, _ = setupTempArrayAndContext(bytecode)
assert context.top() == fakeliterals(space, "pub")
-def test_storeTempAtInTempVectorAt(bytecode = storeTempAtInTempVectorAt):
+def test_bc_storeRemoteTempLongBytecode(bytecode =
storeRemoteTempLongBytecode):
context, temp_array = setupTempArrayAndContext(bytecode)
assert context.top() == fakeliterals(space, "bar")
assert temp_array.at0(space, 2) == fakeliterals(space, "bar")
-def test_popAndStoreTempAtInTempVectorAt(bytecode =
popAndStoreTempAtInTempVectorAt):
+def test_bc_storeAndPopRemoteTempLongBytecode(bytecode =
storeAndPopRemoteTempLongBytecode):
context, temp_array = setupTempArrayAndContext(bytecode)
assert temp_array.at0(space, 2) == fakeliterals(space, "bar")
assert context.top() == fakeliterals(space, "english")
-def test_pushClosureNumCopied0NumArgsBlockSize(bytecode =
pushClosureNumCopiedNumArgsBlockSize):
+def test_bc_pushClosureCopyCopied0ValuesBytecode(bytecode =
pushClosureCopyCopiedValuesBytecode):
for i in (0, 0xF0, 0x0FF0, 0xFFF0):
interp = new_interpreter(bytecode + chr(2) + chr(i >> 8) + chr(i &
0xFF))
context = interp.s_active_context()
@@ -897,7 +897,7 @@
assert closure.startpc() == pc + 4
assert closure.outerContext() is context._w_self
-def test_pushClosureNumCopied2NumArgsBlockSize(bytecode =
pushClosureNumCopiedNumArgsBlockSize):
+def test_bc_pushClosureCopyCopied2ValuesBytecode(bytecode =
pushClosureCopyCopiedValuesBytecode):
interp = new_interpreter(bytecode + chr(0x23) + chr(0) + chr(0))
context = interp.s_active_context()
context.push("english")
diff --git a/spyvm/test/test_primitives.py b/spyvm/test/test_primitives.py
--- a/spyvm/test/test_primitives.py
+++ b/spyvm/test/test_primitives.py
@@ -16,7 +16,7 @@
def __init__(self, stack):
self._vars = [None] * 6 + stack
s_self = self.as_blockcontext_get_shadow()
- s_self.init_stack()
+ s_self.init_stack_and_temps()
s_self.reset_stack()
s_self.push_all(stack)
s_self.store_expected_argument_count(0)
@@ -30,6 +30,7 @@
if isinstance(x, model.W_Object): return x
if isinstance(x, str) and len(x) == 1: return space.wrap_char(x)
if isinstance(x, str): return space.wrap_string(x)
+ if isinstance(x, list): return space.wrap_list(x)
raise NotImplementedError
def mock(stack):
diff --git a/spyvm/todo.txt b/spyvm/todo.txt
--- a/spyvm/todo.txt
+++ b/spyvm/todo.txt
@@ -22,5 +22,3 @@
Shadows:
[ ] Fix invalidation of methoddictshadow when the w_self of its values array
changes
-Lars ToDo
-[ ] different image with BlockClosure instead of BlockContext
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit