[pypy-svn] r68910 - in pypy/trunk/pypy: interpreter interpreter/test module/pypyjit/test objspace objspace/std
cfbolz at codespeak.net
cfbolz at codespeak.net
Mon Nov 2 14:21:56 CET 2009
Author: cfbolz
Date: Mon Nov 2 14:21:55 2009
New Revision: 68910
Modified:
pypy/trunk/pypy/interpreter/function.py
pypy/trunk/pypy/interpreter/gateway.py
pypy/trunk/pypy/interpreter/mixedmodule.py
pypy/trunk/pypy/interpreter/test/test_function.py
pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
pypy/trunk/pypy/objspace/descroperation.py
pypy/trunk/pypy/objspace/std/callmethod.py
Log:
Prevent the changing of the func_code attribute of builtin functions. This makes
some shortcuts less beautiful, but produces one guard and one getfield less
a bit everywhere.
Modified: pypy/trunk/pypy/interpreter/function.py
==============================================================================
--- pypy/trunk/pypy/interpreter/function.py (original)
+++ pypy/trunk/pypy/interpreter/function.py Mon Nov 2 14:21:55 2009
@@ -16,12 +16,20 @@
funccallunrolling = unrolling_iterable(range(4))
+ at jit.purefunction
+def _get_immutable_code(func):
+ assert not func.can_change_code
+ return func.code
+
class Function(Wrappable):
"""A function is a code object captured with some environment:
an object space, a dictionary of globals, default arguments,
and an arbitrary 'closure' passed to the code object."""
- def __init__(self, space, code, w_globals=None, defs_w=[], closure=None, forcename=None):
+ can_change_code = True
+
+ def __init__(self, space, code, w_globals=None, defs_w=[], closure=None,
+ forcename=None):
self.space = space
self.name = forcename or code.co_name
self.w_doc = None # lazily read from code.getdocstring()
@@ -48,7 +56,12 @@
return self.getcode().funcrun_obj(self, w_obj, args)
def getcode(self):
- return jit.hint(self.code, promote=True)
+ if jit.we_are_jitted():
+ if not self.can_change_code:
+ self = jit.hint(self, promote=True)
+ return _get_immutable_code(self)
+ return jit.hint(self.code, promote=True)
+ return self.code
def funccall(self, *args_w): # speed hack
from pypy.interpreter import gateway
@@ -368,6 +381,9 @@
def fset_func_code(space, self, w_code):
from pypy.interpreter.pycode import PyCode
+ if not self.can_change_code:
+ raise OperationError(space.w_TypeError,
+ space.wrap("Cannot change code attribute of builtin functions"))
code = space.interp_w(Code, w_code)
closure_len = 0
if self.closure:
@@ -568,7 +584,11 @@
"'%s' object is not callable" % typename))
return space.wrap(ClassMethod(w_function))
+class FunctionWithFixedCode(Function):
+ can_change_code = False
+
class BuiltinFunction(Function):
+ can_change_code = False
def __init__(self, func):
assert isinstance(func, Function)
Modified: pypy/trunk/pypy/interpreter/gateway.py
==============================================================================
--- pypy/trunk/pypy/interpreter/gateway.py (original)
+++ pypy/trunk/pypy/interpreter/gateway.py Mon Nov 2 14:21:55 2009
@@ -16,6 +16,7 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter import eval
from pypy.interpreter.function import Function, Method, ClassMethod
+from pypy.interpreter.function import FunctionWithFixedCode
from pypy.interpreter.baseobjspace import W_Root, ObjSpace, Wrappable
from pypy.interpreter.baseobjspace import Wrappable, SpaceCache, DescrMismatch
from pypy.interpreter.argument import Arguments, Signature
@@ -788,7 +789,7 @@
space = cache.space
defs = gateway._getdefaults(space) # needs to be implemented by subclass
code = gateway._code
- fn = Function(space, code, None, defs, forcename = gateway.name)
+ fn = FunctionWithFixedCode(space, code, None, defs, forcename = gateway.name)
if not space.config.translating: # for tests and py.py
fn._freeze_()
if gateway.as_classmethod:
Modified: pypy/trunk/pypy/interpreter/mixedmodule.py
==============================================================================
--- pypy/trunk/pypy/interpreter/mixedmodule.py (original)
+++ pypy/trunk/pypy/interpreter/mixedmodule.py Mon Nov 2 14:21:55 2009
@@ -56,7 +56,14 @@
#print "loaded", w_value
# obscure
func = space.interpclass_w(w_value)
- if type(func) is Function:
+ # the idea of the following code is that all functions that are
+ # directly in a mixed-module are "builtin", e.g. they get a
+ # special type without a __get__
+ # note that this is not just all functions that contain a
+ # builtin code object, as e.g. methods of builtin types have to
+ # be normal Functions to get the correct binding behaviour
+ if (isinstance(func, Function) and
+ type(func) is not BuiltinFunction):
try:
bltin = func._builtinversion_
except AttributeError:
Modified: pypy/trunk/pypy/interpreter/test/test_function.py
==============================================================================
--- pypy/trunk/pypy/interpreter/test/test_function.py (original)
+++ pypy/trunk/pypy/interpreter/test/test_function.py Mon Nov 2 14:21:55 2009
@@ -84,7 +84,12 @@
return f() # a closure
raises(ValueError, "f.func_code = h.func_code")
-
+ def test_write_code_builtin_forbidden(self):
+ def f(*args):
+ return 42
+ raises(TypeError, "dir.func_code = f.func_code")
+ raises(TypeError, "list.append.im_func.func_code = f.func_code")
+
class AppTestFunction:
def test_simple_call(self):
Modified: pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
==============================================================================
--- pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py (original)
+++ pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py Mon Nov 2 14:21:55 2009
@@ -252,8 +252,7 @@
assert len(callisinstance.get_opnames("guard")) <= 2
bytecode, = self.get_by_bytecode("STORE_ATTR")
- # XXX where does that come from?
- assert bytecode.get_opnames() == ["getfield_gc", "guard_value"]
+ assert bytecode.get_opnames() == []
def test_mixed_type_loop(self):
self.run_source('''
@@ -272,7 +271,28 @@
bytecode, = self.get_by_bytecode("BINARY_ADD")
assert not bytecode.get_opnames("call")
assert not bytecode.get_opnames("new")
- assert len(bytecode.get_opnames("guard")) <= 3
+ assert len(bytecode.get_opnames("guard")) <= 2
+
+ def test_call_builtin_function(self):
+ self.run_source('''
+ class A(object):
+ pass
+ def main(n):
+ i = 2
+ l = []
+ while i < n:
+ i += 1
+ l.append(i)
+ return i, len(l)
+ ''',
+ ([20], (20, 18)),
+ ([31], (31, 29)))
+
+ bytecode, = self.get_by_bytecode("CALL_METHOD")
+ assert len(bytecode.get_opnames("new_with_vtable")) == 1 # the forcing of the int
+ assert len(bytecode.get_opnames("call")) == 1 # the call to append
+ assert len(bytecode.get_opnames("guard")) == 1 # guard_no_exception after the call
+
class AppTestJIT(PyPyCJITTests):
def setup_class(cls):
@@ -288,7 +308,7 @@
class TestJIT(PyPyCJITTests):
def setup_class(cls):
if option.pypy_c is None:
- py.test.skip("pass --pypy-c!")
+ py.test.skip("pass --pypy!")
cls.tmpdir = udir.join('pypy-jit')
cls.tmpdir.ensure(dir=1)
cls.counter = 0
Modified: pypy/trunk/pypy/objspace/descroperation.py
==============================================================================
--- pypy/trunk/pypy/objspace/descroperation.py (original)
+++ pypy/trunk/pypy/objspace/descroperation.py Mon Nov 2 14:21:55 2009
@@ -1,7 +1,7 @@
import operator
from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import ObjSpace
-from pypy.interpreter.function import Function, Method
+from pypy.interpreter.function import Function, Method, FunctionWithFixedCode
from pypy.interpreter.argument import Arguments
from pypy.interpreter.typedef import default_identity_hash
from pypy.tool.sourcetools import compile2, func_with_new_name
@@ -81,7 +81,7 @@
def get_and_call_args(space, w_descr, w_obj, args):
descr = space.interpclass_w(w_descr)
# a special case for performance and to avoid infinite recursion
- if type(descr) is Function:
+ if isinstance(descr, Function):
return descr.call_obj_args(w_obj, args)
else:
w_impl = space.get(w_descr, w_obj)
@@ -89,8 +89,15 @@
def get_and_call_function(space, w_descr, w_obj, *args_w):
descr = space.interpclass_w(w_descr)
+ typ = type(descr)
# a special case for performance and to avoid infinite recursion
- if type(descr) is Function:
+ if typ is Function or typ is FunctionWithFixedCode:
+ # isinstance(typ, Function) would not be correct here:
+ # for a BuiltinFunction we must not use that shortcut, because a
+ # builtin function binds differently than a normal function
+ # see test_builtin_as_special_method_is_not_bound
+ # in interpreter/test/test_function.py
+
# the fastcall paths are purely for performance, but the resulting
# increase of speed is huge
return descr.funccall(w_obj, *args_w)
Modified: pypy/trunk/pypy/objspace/std/callmethod.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/callmethod.py (original)
+++ pypy/trunk/pypy/objspace/std/callmethod.py Mon Nov 2 14:21:55 2009
@@ -55,14 +55,16 @@
# this handles directly the common case
# module.function(args..)
w_value = w_obj.getdictvalue(space, w_name)
- elif type(w_descr) is function.Function:
- w_value = w_obj.getdictvalue_attr_is_in_class(space, w_name)
- if w_value is None:
- # fast method path: a function object in the class,
- # nothing in the instance
- f.pushvalue(w_descr)
- f.pushvalue(w_obj)
- return
+ else:
+ typ = type(w_descr)
+ if typ is function.Function or typ is function.FunctionWithFixedCode:
+ w_value = w_obj.getdictvalue_attr_is_in_class(space, w_name)
+ if w_value is None:
+ # fast method path: a function object in the class,
+ # nothing in the instance
+ f.pushvalue(w_descr)
+ f.pushvalue(w_obj)
+ return
if w_value is None:
w_value = space.getattr(w_obj, w_name)
f.pushvalue(w_value)
@@ -89,7 +91,8 @@
w_getattribute = space.lookup(w_obj, '__getattribute__')
if w_getattribute is object_getattribute(space):
w_descr = space.lookup(w_obj, methname)
- if type(w_descr) is function.Function:
+ typ = type(w_descr)
+ if typ is function.Function or typ is function.FunctionWithFixedCode:
w_value = w_obj.getdictvalue_attr_is_in_class(space, w_name)
if w_value is None:
# fast method path: a function object in the class,
More information about the Pypy-commit
mailing list