[pypy-svn] rev 956 - in pypy/trunk/src/pypy: interpreter objspace/ann objspace/ann/test
gvanrossum at codespeak.net
gvanrossum at codespeak.net
Sun Jun 22 17:45:32 CEST 2003
Author: gvanrossum
Date: Sun Jun 22 17:45:31 2003
New Revision: 956
Modified:
pypy/trunk/src/pypy/interpreter/pyframe.py
pypy/trunk/src/pypy/objspace/ann/cloningcontext.py
pypy/trunk/src/pypy/objspace/ann/objspace.py
pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py
pypy/trunk/src/pypy/objspace/ann/wrapper.py
Log:
We now handle loops!!!
Modified: pypy/trunk/src/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/pyframe.py (original)
+++ pypy/trunk/src/pypy/interpreter/pyframe.py Sun Jun 22 17:45:31 2003
@@ -313,7 +313,7 @@
class ExitFrame(Exception):
"""Signals the end of the frame execution.
- The argument is the returned or yielded value."""
+ The argument is the returned or yielded value, already wrapped."""
class BytecodeCorruption(ValueError):
"""Detected bytecode corruption. Never caught; it's an error."""
Modified: pypy/trunk/src/pypy/objspace/ann/cloningcontext.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/cloningcontext.py (original)
+++ pypy/trunk/src/pypy/objspace/ann/cloningcontext.py Sun Jun 22 17:45:31 2003
@@ -1,6 +1,7 @@
from pypy.interpreter.executioncontext import ExecutionContext
-from pypy.interpreter.pyframe import ControlFlowException
-from pypy.objspace.ann.wrapper import union
+from pypy.interpreter.pyframe import ControlFlowException, ExitFrame
+from pypy.objspace.ann.wrapper \
+ import union, compatible_frames, unite_frames, W_Anything, W_Constant
class IndeterminateCondition(ControlFlowException):
@@ -9,33 +10,53 @@
self.w_obj = w_obj
def action(self, frame, last_instr):
- frame.next_instr = last_instr
- f2 = frame.clone()
- clones = frame.clones
- clones.append(f2)
- f2.clones = clones # Share the joy
- f2.force_w_obj = self.w_obj
- self.w_obj.force = True
+ frame.next_instr = last_instr # Restart failed opcode (!)
+ frame.restarting = (self.w_obj, True) # For bytecode_trace() below
class CloningExecutionContext(ExecutionContext):
+ def __init__(self, space):
+ ExecutionContext.__init__(self, space)
+ self.knownframes = {}
+ # {(bytecode, w_globals): (result, clones, {next_instr: [frame, ...], ...}), ...}
+
+ def bytecode_trace(self, frame):
+ assert isinstance(frame.w_globals, W_Constant)
+ key = (frame.bytecode, id(frame.w_globals.value))
+ result, clones, subdict = self.knownframes[key]
+
+ if frame.restarting is not None:
+ w_obj, flag = frame.restarting
+ frame.restarting = None
+ w_obj.force = flag
+ if flag:
+ f2 = frame.clone()
+ f2.restarting = (w_obj, False)
+ clones.append(f2)
+ return
+
+ frames = subdict.setdefault(frame.next_instr, [])
+ assert len(frames) <= 1 # We think this is true
+ for f in frames:
+ if compatible_frames(frame, f):
+ c1, c2 = unite_frames(frame, f)
+ if not c2:
+ # A fixpoint
+ raise ExitFrame(None)
+ return
+ frames.append(frame.clone())
def eval_frame(self, frame):
- from pypy.objspace.ann.objspace import W_Anything
assert not hasattr(frame, "clones")
- space = frame.space
- clones = [frame]
- frame.clones = clones
- frame.force_w_obj = None
- result = None # W_Impossible
+ assert self.space == frame.space
+ frame.restarting = None
+ key = (frame.bytecode, id(frame.w_globals.value))
+ result, clones, subdict = self.knownframes.setdefault(key, (None, [], {}))
+ clones.append(frame)
while clones:
f = clones.pop()
- w_obj = f.force_w_obj
- if w_obj is not None:
- assert w_obj.force == True
- w_obj.force = False
r = ExecutionContext.eval_frame(self, f)
+ result, clones, subdict = self.knownframes[key]
result = union(result, r)
- if isinstance(result, W_Anything):
- break
+ self.knownframes[key] = result, clones, subdict
return result
Modified: pypy/trunk/src/pypy/objspace/ann/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/objspace.py (original)
+++ pypy/trunk/src/pypy/objspace/ann/objspace.py Sun Jun 22 17:45:31 2003
@@ -21,6 +21,7 @@
class AnnotationObjSpace(ObjSpace):
def initialize(self):
+ self.bytecodecache = {}
self.w_None = self.wrap(None)
self.w_True = self.wrap(True)
self.w_False = self.wrap(False)
@@ -115,11 +116,40 @@
else:
return W_Anything()
+ def sub(self, w_left, w_right):
+ try:
+ left = self.unwrap(w_left)
+ right = self.unwrap(w_right)
+ except UnwrapException:
+ pass
+ else:
+ return self.wrap(left - right)
+ if is_int(w_left) and is_int(w_right):
+ return W_Integer()
+ else:
+ return W_Anything()
+
+ def mul(self, w_left, w_right):
+ try:
+ left = self.unwrap(w_left)
+ right = self.unwrap(w_right)
+ except UnwrapException:
+ pass
+ else:
+ return self.wrap(left * right)
+ if is_int(w_left) and is_int(w_right):
+ return W_Integer()
+ else:
+ return W_Anything()
+
def call(self, w_func, w_args, w_kwds):
func = self.unwrap(w_func) # Would be bad it it was W_Anything
code = func.func_code
- bytecode = PyByteCode()
- bytecode._from_code(code)
+ bytecode = self.bytecodecache.get(code)
+ if bytecode is None:
+ bytecode = PyByteCode()
+ bytecode._from_code(code)
+ self.bytecodecache[code] = bytecode
w_locals = bytecode.build_arguments(self,
w_args,
w_kwds,
Modified: pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py (original)
+++ pypy/trunk/src/pypy/objspace/ann/test/test_objspace.py Sun Jun 22 17:45:31 2003
@@ -41,6 +41,12 @@
'f', [self.space.wrap(5), W_Anything()])
self.assertEquals(self.space.unwrap(x), 6)
+ def test_constplusstr2any(self):
+ x = self.codetest("def f(i, j):\n"
+ " return i+j\n",
+ 'f', [W_Integer(), self.space.wrap(3.5)])
+ self.assertEquals(type(x), W_Anything)
+
def test_int2int(self):
x = self.codetest("def f(i):\n"
" return i+1\n",
@@ -73,7 +79,7 @@
'f', [W_Integer()])
self.assertEquals(self.space.unwrap(x), 0)
- def dont_test_while(self):
+ def test_while(self):
x = self.codetest("def f(i):\n"
" while i > 0:\n"
" i = i-1\n"
@@ -81,8 +87,16 @@
'f', [W_Integer()])
self.assertEquals(type(x), W_Integer)
+ def test_factorial(self):
+ x = self.codetest("def f(i):\n"
+ " if i > 1:\n"
+ " return i*f(i-1)\n"
+ " return 1\n",
+ 'f', [self.space.wrap(5)])
+ self.assertEquals(type(x), W_Integer)
+
def dont_test_global(self):
- # XXX This doesn't work because we don't handle mutating globals
+ # XXX This will never work, because we don't handle mutating globals
x = self.codetest("def f(i, j):\n"
" global x\n"
" x = i\n"
Modified: pypy/trunk/src/pypy/objspace/ann/wrapper.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/ann/wrapper.py (original)
+++ pypy/trunk/src/pypy/objspace/ann/wrapper.py Sun Jun 22 17:45:31 2003
@@ -9,23 +9,50 @@
"""
class W_Object(object):
+ """Abstract base class. do not instantiate."""
+ def __new__(cls, *args, **kwd):
+ assert cls is not W_Object
+ return object.__new__(cls)
+ def __init__(self):
+ pass
+ def __repr__(self):
+ return "%s(%s)" % (self.__class__.__name__, self.argsrepr())
+ def argsrepr(self):
+ return ""
+ def __eq__(self, other):
+ return type(other) is type(self)
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+class W_Undefined(W_Object):
+ """May be undefined. This is the most contagious type."""
pass
class W_Anything(W_Object):
+ """Any (defined) value. This is the next most contagious type."""
pass
class W_Integer(W_Object):
+ """An integer value (int or long)."""
pass
class W_Constant(W_Object):
+ """A specific constant value."""
def __init__(self, value):
self.value = value
- def __repr__(self):
- return '<constant %r>' % self.value
+ def argsrepr(self):
+ return repr(self.value)[:50]
+ def __eq__(self, other):
+ return type(other) is type(self) and self.value == other.value
class W_KnownKeysContainer(W_Object):
+ """A dict with constant set of keys or a tuple with known length."""
def __init__(self, args_w):
self.args_w = args_w
+ def argsrepr(self):
+ return repr(self.args_w)
+ def __eq__(self, other):
+ return type(other) is type(self) and self.args_w == other.args_w
def __len__(self):
return len(self.args_w)
def __getitem__(self, i):
@@ -37,14 +64,99 @@
# XXX Recurse down the values?
return W_KnownKeysContainer(args_w)
+def unite_frames(f1, f2):
+ """Given two compatible frames, make them the same.
+
+ This changes both f1 and f2 in-place to change all the values into
+ their union. It returns two booleans, indicating whether the
+ frames were changed.
+
+ This requires that the frames are compatible.
+
+ """
+ assert compatible_frames(f1, f2)
+
+ # Compare value stacks
+ # XXX uses stack internals
+ s1 = f1.valuestack.items
+ s2 = f2.valuestack.items
+ c1 = c2 = False # changed flags
+ n = len(s1)
+ assert n == len(s2)
+ for i in range(n):
+ v1 = s1[i]
+ v2 = s2[i]
+ if v1 != v2:
+ u = union(v1, v2)
+ if v1 != u:
+ c1 = True
+ s1[i] = u
+ if v2 != u:
+ c2 = True
+ s2[i] = u
+
+ # Compare locals
+ # XXX uses W_KnownKeysContainer internals
+ assert isinstance(f1.w_locals, W_KnownKeysContainer)
+ assert isinstance(f2.w_locals, W_KnownKeysContainer)
+ l1 = f1.w_locals.args_w
+ l2 = f2.w_locals.args_w
+ keydict = {} # compute set of keys
+ for key in l1.iterkeys():
+ keydict[key] = 1
+ for key in l2.iterkeys():
+ keydict[key] = 1
+ for key in keydict.iterkeys():
+ v1 = l1.get(key, W_Undefined())
+ v2 = l2.get(key, W_Undefined())
+ u = union(v1, v2)
+ if v1 != u:
+ c1 = True
+ l1[key] = u
+ if v2 != u:
+ c2 = True
+ l2[key] = u
+ return c1, c2
+
+def compatible_frames(f1, f2):
+ """Return whether two frames are compatible.
+
+ Informally, this means that they represent different states
+ at the same point in the program.
+
+ """
+ if f1 is f2:
+ return True
+ return (f1.next_instr == f2.next_instr and
+ f1.space is f2.space and
+ f2.bytecode is f2.bytecode and
+ f1.valuestack.depth() == f2.valuestack.depth() and
+ equivalent(f1.w_globals, f2.w_globals) and
+ equivalent(f1.w_builtins, f2.w_builtins) and
+ f1.blockstack.items == f2.blockstack.items)
+
+def equivalent(w1, w2):
+ """Return whether two wrappers are equivalent
+
+ (Helper for compatible_frames.)
+
+ They must be constant wrappers for the same object.
+
+ """
+ return (isinstance(w1, W_Constant) and
+ isinstance(w2, W_Constant) and
+ w1.value is w2.value)
+
def union(r1, r2):
- # Unite two results
+ """Return the union of two wrappers."""
if r1 is r2:
return r1
if r1 is None:
return r2
if r2 is None:
return r1
+ if isinstance(r1, W_Undefined) or isinstance(r2, W_Undefined):
+ return W_Undefined()
if isinstance(r1, W_Anything) or isinstance(r2, W_Anything):
return W_Anything()
if (isinstance(r1, W_Constant) and isinstance(r2, W_Constant) and
@@ -61,5 +173,10 @@
return W_Anything()
def is_int(w_obj):
+ """Return whether a wrapped object is an integer.
+
+ It could either be W_Integer or an constant integer.
+ """
return (isinstance(w_obj, W_Integer) or
- isinstance(w_obj, W_Constant) and isinstance(w_obj.value, int))
+ (isinstance(w_obj, W_Constant) and
+ isinstance(w_obj.value, (int, long))))
More information about the Pypy-commit
mailing list