[pypy-commit] pypy stm: hg merge default
arigo
noreply at buildbot.pypy.org
Fri Oct 21 14:08:48 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: stm
Changeset: r48304:e56876588573
Date: 2011-10-20 22:26 +0200
http://bitbucket.org/pypy/pypy/changeset/e56876588573/
Log: hg merge default
diff too long, truncating to 10000 out of 13877 lines
diff --git a/lib-python/conftest.py b/lib-python/conftest.py
--- a/lib-python/conftest.py
+++ b/lib-python/conftest.py
@@ -317,7 +317,7 @@
RegrTest('test_multibytecodec.py', usemodules='_multibytecodec'),
RegrTest('test_multibytecodec_support.py', skip="not a test"),
RegrTest('test_multifile.py'),
- RegrTest('test_multiprocessing.py', skip='FIXME leaves subprocesses'),
+ RegrTest('test_multiprocessing.py', skip="FIXME leaves subprocesses"),
RegrTest('test_mutants.py', core="possibly"),
RegrTest('test_mutex.py'),
RegrTest('test_netrc.py'),
diff --git a/lib-python/modified-2.7/httplib.py b/lib-python/modified-2.7/httplib.py
new file mode 100644
--- /dev/null
+++ b/lib-python/modified-2.7/httplib.py
@@ -0,0 +1,1377 @@
+"""HTTP/1.1 client library
+
+<intro stuff goes here>
+<other stuff, too>
+
+HTTPConnection goes through a number of "states", which define when a client
+may legally make another request or fetch the response for a particular
+request. This diagram details these state transitions:
+
+ (null)
+ |
+ | HTTPConnection()
+ v
+ Idle
+ |
+ | putrequest()
+ v
+ Request-started
+ |
+ | ( putheader() )* endheaders()
+ v
+ Request-sent
+ |
+ | response = getresponse()
+ v
+ Unread-response [Response-headers-read]
+ |\____________________
+ | |
+ | response.read() | putrequest()
+ v v
+ Idle Req-started-unread-response
+ ______/|
+ / |
+ response.read() | | ( putheader() )* endheaders()
+ v v
+ Request-started Req-sent-unread-response
+ |
+ | response.read()
+ v
+ Request-sent
+
+This diagram presents the following rules:
+ -- a second request may not be started until {response-headers-read}
+ -- a response [object] cannot be retrieved until {request-sent}
+ -- there is no differentiation between an unread response body and a
+ partially read response body
+
+Note: this enforcement is applied by the HTTPConnection class. The
+ HTTPResponse class does not enforce this state machine, which
+ implies sophisticated clients may accelerate the request/response
+ pipeline. Caution should be taken, though: accelerating the states
+ beyond the above pattern may imply knowledge of the server's
+ connection-close behavior for certain requests. For example, it
+ is impossible to tell whether the server will close the connection
+ UNTIL the response headers have been read; this means that further
+ requests cannot be placed into the pipeline until it is known that
+ the server will NOT be closing the connection.
+
+Logical State __state __response
+------------- ------- ----------
+Idle _CS_IDLE None
+Request-started _CS_REQ_STARTED None
+Request-sent _CS_REQ_SENT None
+Unread-response _CS_IDLE <response_class>
+Req-started-unread-response _CS_REQ_STARTED <response_class>
+Req-sent-unread-response _CS_REQ_SENT <response_class>
+"""
+
+from array import array
+import os
+import socket
+from sys import py3kwarning
+from urlparse import urlsplit
+import warnings
+with warnings.catch_warnings():
+ if py3kwarning:
+ warnings.filterwarnings("ignore", ".*mimetools has been removed",
+ DeprecationWarning)
+ import mimetools
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+__all__ = ["HTTP", "HTTPResponse", "HTTPConnection",
+ "HTTPException", "NotConnected", "UnknownProtocol",
+ "UnknownTransferEncoding", "UnimplementedFileMode",
+ "IncompleteRead", "InvalidURL", "ImproperConnectionState",
+ "CannotSendRequest", "CannotSendHeader", "ResponseNotReady",
+ "BadStatusLine", "error", "responses"]
+
+HTTP_PORT = 80
+HTTPS_PORT = 443
+
+_UNKNOWN = 'UNKNOWN'
+
+# connection states
+_CS_IDLE = 'Idle'
+_CS_REQ_STARTED = 'Request-started'
+_CS_REQ_SENT = 'Request-sent'
+
+# status codes
+# informational
+CONTINUE = 100
+SWITCHING_PROTOCOLS = 101
+PROCESSING = 102
+
+# successful
+OK = 200
+CREATED = 201
+ACCEPTED = 202
+NON_AUTHORITATIVE_INFORMATION = 203
+NO_CONTENT = 204
+RESET_CONTENT = 205
+PARTIAL_CONTENT = 206
+MULTI_STATUS = 207
+IM_USED = 226
+
+# redirection
+MULTIPLE_CHOICES = 300
+MOVED_PERMANENTLY = 301
+FOUND = 302
+SEE_OTHER = 303
+NOT_MODIFIED = 304
+USE_PROXY = 305
+TEMPORARY_REDIRECT = 307
+
+# client error
+BAD_REQUEST = 400
+UNAUTHORIZED = 401
+PAYMENT_REQUIRED = 402
+FORBIDDEN = 403
+NOT_FOUND = 404
+METHOD_NOT_ALLOWED = 405
+NOT_ACCEPTABLE = 406
+PROXY_AUTHENTICATION_REQUIRED = 407
+REQUEST_TIMEOUT = 408
+CONFLICT = 409
+GONE = 410
+LENGTH_REQUIRED = 411
+PRECONDITION_FAILED = 412
+REQUEST_ENTITY_TOO_LARGE = 413
+REQUEST_URI_TOO_LONG = 414
+UNSUPPORTED_MEDIA_TYPE = 415
+REQUESTED_RANGE_NOT_SATISFIABLE = 416
+EXPECTATION_FAILED = 417
+UNPROCESSABLE_ENTITY = 422
+LOCKED = 423
+FAILED_DEPENDENCY = 424
+UPGRADE_REQUIRED = 426
+
+# server error
+INTERNAL_SERVER_ERROR = 500
+NOT_IMPLEMENTED = 501
+BAD_GATEWAY = 502
+SERVICE_UNAVAILABLE = 503
+GATEWAY_TIMEOUT = 504
+HTTP_VERSION_NOT_SUPPORTED = 505
+INSUFFICIENT_STORAGE = 507
+NOT_EXTENDED = 510
+
+# Mapping status codes to official W3C names
+responses = {
+ 100: 'Continue',
+ 101: 'Switching Protocols',
+
+ 200: 'OK',
+ 201: 'Created',
+ 202: 'Accepted',
+ 203: 'Non-Authoritative Information',
+ 204: 'No Content',
+ 205: 'Reset Content',
+ 206: 'Partial Content',
+
+ 300: 'Multiple Choices',
+ 301: 'Moved Permanently',
+ 302: 'Found',
+ 303: 'See Other',
+ 304: 'Not Modified',
+ 305: 'Use Proxy',
+ 306: '(Unused)',
+ 307: 'Temporary Redirect',
+
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 402: 'Payment Required',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 405: 'Method Not Allowed',
+ 406: 'Not Acceptable',
+ 407: 'Proxy Authentication Required',
+ 408: 'Request Timeout',
+ 409: 'Conflict',
+ 410: 'Gone',
+ 411: 'Length Required',
+ 412: 'Precondition Failed',
+ 413: 'Request Entity Too Large',
+ 414: 'Request-URI Too Long',
+ 415: 'Unsupported Media Type',
+ 416: 'Requested Range Not Satisfiable',
+ 417: 'Expectation Failed',
+
+ 500: 'Internal Server Error',
+ 501: 'Not Implemented',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ 504: 'Gateway Timeout',
+ 505: 'HTTP Version Not Supported',
+}
+
+# maximal amount of data to read at one time in _safe_read
+MAXAMOUNT = 1048576
+
+class HTTPMessage(mimetools.Message):
+
+ def addheader(self, key, value):
+ """Add header for field key handling repeats."""
+ prev = self.dict.get(key)
+ if prev is None:
+ self.dict[key] = value
+ else:
+ combined = ", ".join((prev, value))
+ self.dict[key] = combined
+
+ def addcontinue(self, key, more):
+ """Add more field data from a continuation line."""
+ prev = self.dict[key]
+ self.dict[key] = prev + "\n " + more
+
+ def readheaders(self):
+ """Read header lines.
+
+ Read header lines up to the entirely blank line that terminates them.
+ The (normally blank) line that ends the headers is skipped, but not
+ included in the returned list. If a non-header line ends the headers,
+ (which is an error), an attempt is made to backspace over it; it is
+ never included in the returned list.
+
+ The variable self.status is set to the empty string if all went well,
+ otherwise it is an error message. The variable self.headers is a
+ completely uninterpreted list of lines contained in the header (so
+ printing them will reproduce the header exactly as it appears in the
+ file).
+
+ If multiple header fields with the same name occur, they are combined
+ according to the rules in RFC 2616 sec 4.2:
+
+ Appending each subsequent field-value to the first, each separated
+ by a comma. The order in which header fields with the same field-name
+ are received is significant to the interpretation of the combined
+ field value.
+ """
+ # XXX The implementation overrides the readheaders() method of
+ # rfc822.Message. The base class design isn't amenable to
+ # customized behavior here so the method here is a copy of the
+ # base class code with a few small changes.
+
+ self.dict = {}
+ self.unixfrom = ''
+ self.headers = hlist = []
+ self.status = ''
+ headerseen = ""
+ firstline = 1
+ startofline = unread = tell = None
+ if hasattr(self.fp, 'unread'):
+ unread = self.fp.unread
+ elif self.seekable:
+ tell = self.fp.tell
+ while True:
+ if tell:
+ try:
+ startofline = tell()
+ except IOError:
+ startofline = tell = None
+ self.seekable = 0
+ line = self.fp.readline()
+ if not line:
+ self.status = 'EOF in headers'
+ break
+ # Skip unix From name time lines
+ if firstline and line.startswith('From '):
+ self.unixfrom = self.unixfrom + line
+ continue
+ firstline = 0
+ if headerseen and line[0] in ' \t':
+ # XXX Not sure if continuation lines are handled properly
+ # for http and/or for repeating headers
+ # It's a continuation line.
+ hlist.append(line)
+ self.addcontinue(headerseen, line.strip())
+ continue
+ elif self.iscomment(line):
+ # It's a comment. Ignore it.
+ continue
+ elif self.islast(line):
+ # Note! No pushback here! The delimiter line gets eaten.
+ break
+ headerseen = self.isheader(line)
+ if headerseen:
+ # It's a legal header line, save it.
+ hlist.append(line)
+ self.addheader(headerseen, line[len(headerseen)+1:].strip())
+ continue
+ else:
+ # It's not a header line; throw it back and stop here.
+ if not self.dict:
+ self.status = 'No headers'
+ else:
+ self.status = 'Non-header line where header expected'
+ # Try to undo the read.
+ if unread:
+ unread(line)
+ elif tell:
+ self.fp.seek(startofline)
+ else:
+ self.status = self.status + '; bad seek'
+ break
+
+class HTTPResponse:
+
+ # strict: If true, raise BadStatusLine if the status line can't be
+ # parsed as a valid HTTP/1.0 or 1.1 status line. By default it is
+ # false because it prevents clients from talking to HTTP/0.9
+ # servers. Note that a response with a sufficiently corrupted
+ # status line will look like an HTTP/0.9 response.
+
+ # See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details.
+
+ def __init__(self, sock, debuglevel=0, strict=0, method=None, buffering=False):
+ if buffering:
+ # The caller won't be using any sock.recv() calls, so buffering
+ # is fine and recommended for performance.
+ self.fp = sock.makefile('rb')
+ else:
+ # The buffer size is specified as zero, because the headers of
+ # the response are read with readline(). If the reads were
+ # buffered the readline() calls could consume some of the
+ # response, which make be read via a recv() on the underlying
+ # socket.
+ self.fp = sock.makefile('rb', 0)
+ self.debuglevel = debuglevel
+ self.strict = strict
+ self._method = method
+
+ self.msg = None
+
+ # from the Status-Line of the response
+ self.version = _UNKNOWN # HTTP-Version
+ self.status = _UNKNOWN # Status-Code
+ self.reason = _UNKNOWN # Reason-Phrase
+
+ self.chunked = _UNKNOWN # is "chunked" being used?
+ self.chunk_left = _UNKNOWN # bytes left to read in current chunk
+ self.length = _UNKNOWN # number of bytes left in response
+ self.will_close = _UNKNOWN # conn will close at end of response
+
+ def _read_status(self):
+ # Initialize with Simple-Response defaults
+ line = self.fp.readline()
+ if self.debuglevel > 0:
+ print "reply:", repr(line)
+ if not line:
+ # Presumably, the server closed the connection before
+ # sending a valid response.
+ raise BadStatusLine(line)
+ try:
+ [version, status, reason] = line.split(None, 2)
+ except ValueError:
+ try:
+ [version, status] = line.split(None, 1)
+ reason = ""
+ except ValueError:
+ # empty version will cause next test to fail and status
+ # will be treated as 0.9 response.
+ version = ""
+ if not version.startswith('HTTP/'):
+ if self.strict:
+ self.close()
+ raise BadStatusLine(line)
+ else:
+ # assume it's a Simple-Response from an 0.9 server
+ self.fp = LineAndFileWrapper(line, self.fp)
+ return "HTTP/0.9", 200, ""
+
+ # The status code is a three-digit number
+ try:
+ status = int(status)
+ if status < 100 or status > 999:
+ raise BadStatusLine(line)
+ except ValueError:
+ raise BadStatusLine(line)
+ return version, status, reason
+
+ def begin(self):
+ if self.msg is not None:
+ # we've already started reading the response
+ return
+
+ # read until we get a non-100 response
+ while True:
+ version, status, reason = self._read_status()
+ if status != CONTINUE:
+ break
+ # skip the header from the 100 response
+ while True:
+ skip = self.fp.readline().strip()
+ if not skip:
+ break
+ if self.debuglevel > 0:
+ print "header:", skip
+
+ self.status = status
+ self.reason = reason.strip()
+ if version == 'HTTP/1.0':
+ self.version = 10
+ elif version.startswith('HTTP/1.'):
+ self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1
+ elif version == 'HTTP/0.9':
+ self.version = 9
+ else:
+ raise UnknownProtocol(version)
+
+ if self.version == 9:
+ self.length = None
+ self.chunked = 0
+ self.will_close = 1
+ self.msg = HTTPMessage(StringIO())
+ return
+
+ self.msg = HTTPMessage(self.fp, 0)
+ if self.debuglevel > 0:
+ for hdr in self.msg.headers:
+ print "header:", hdr,
+
+ # don't let the msg keep an fp
+ self.msg.fp = None
+
+ # are we using the chunked-style of transfer encoding?
+ tr_enc = self.msg.getheader('transfer-encoding')
+ if tr_enc and tr_enc.lower() == "chunked":
+ self.chunked = 1
+ self.chunk_left = None
+ else:
+ self.chunked = 0
+
+ # will the connection close at the end of the response?
+ self.will_close = self._check_close()
+
+ # do we have a Content-Length?
+ # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
+ length = self.msg.getheader('content-length')
+ if length and not self.chunked:
+ try:
+ self.length = int(length)
+ except ValueError:
+ self.length = None
+ else:
+ if self.length < 0: # ignore nonsensical negative lengths
+ self.length = None
+ else:
+ self.length = None
+
+ # does the body have a fixed length? (of zero)
+ if (status == NO_CONTENT or status == NOT_MODIFIED or
+ 100 <= status < 200 or # 1xx codes
+ self._method == 'HEAD'):
+ self.length = 0
+
+ # if the connection remains open, and we aren't using chunked, and
+ # a content-length was not provided, then assume that the connection
+ # WILL close.
+ if not self.will_close and \
+ not self.chunked and \
+ self.length is None:
+ self.will_close = 1
+
+ def _check_close(self):
+ conn = self.msg.getheader('connection')
+ if self.version == 11:
+ # An HTTP/1.1 proxy is assumed to stay open unless
+ # explicitly closed.
+ conn = self.msg.getheader('connection')
+ if conn and "close" in conn.lower():
+ return True
+ return False
+
+ # Some HTTP/1.0 implementations have support for persistent
+ # connections, using rules different than HTTP/1.1.
+
+ # For older HTTP, Keep-Alive indicates persistent connection.
+ if self.msg.getheader('keep-alive'):
+ return False
+
+ # At least Akamai returns a "Connection: Keep-Alive" header,
+ # which was supposed to be sent by the client.
+ if conn and "keep-alive" in conn.lower():
+ return False
+
+ # Proxy-Connection is a netscape hack.
+ pconn = self.msg.getheader('proxy-connection')
+ if pconn and "keep-alive" in pconn.lower():
+ return False
+
+ # otherwise, assume it will close
+ return True
+
+ def close(self):
+ if self.fp:
+ self.fp.close()
+ self.fp = None
+
+ def isclosed(self):
+ # NOTE: it is possible that we will not ever call self.close(). This
+ # case occurs when will_close is TRUE, length is None, and we
+ # read up to the last byte, but NOT past it.
+ #
+ # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be
+ # called, meaning self.isclosed() is meaningful.
+ return self.fp is None
+
+ # XXX It would be nice to have readline and __iter__ for this, too.
+
+ def read(self, amt=None):
+ if self.fp is None:
+ return ''
+
+ if self._method == 'HEAD':
+ self.close()
+ return ''
+
+ if self.chunked:
+ return self._read_chunked(amt)
+
+ if amt is None:
+ # unbounded read
+ if self.length is None:
+ s = self.fp.read()
+ else:
+ s = self._safe_read(self.length)
+ self.length = 0
+ self.close() # we read everything
+ return s
+
+ if self.length is not None:
+ if amt > self.length:
+ # clip the read to the "end of response"
+ amt = self.length
+
+ # we do not use _safe_read() here because this may be a .will_close
+ # connection, and the user is reading more bytes than will be provided
+ # (for example, reading in 1k chunks)
+ s = self.fp.read(amt)
+ if self.length is not None:
+ self.length -= len(s)
+ if not self.length:
+ self.close()
+ return s
+
+ def _read_chunked(self, amt):
+ assert self.chunked != _UNKNOWN
+ chunk_left = self.chunk_left
+ value = []
+ while True:
+ if chunk_left is None:
+ line = self.fp.readline()
+ i = line.find(';')
+ if i >= 0:
+ line = line[:i] # strip chunk-extensions
+ try:
+ chunk_left = int(line, 16)
+ except ValueError:
+ # close the connection as protocol synchronisation is
+ # probably lost
+ self.close()
+ raise IncompleteRead(''.join(value))
+ if chunk_left == 0:
+ break
+ if amt is None:
+ value.append(self._safe_read(chunk_left))
+ elif amt < chunk_left:
+ value.append(self._safe_read(amt))
+ self.chunk_left = chunk_left - amt
+ return ''.join(value)
+ elif amt == chunk_left:
+ value.append(self._safe_read(amt))
+ self._safe_read(2) # toss the CRLF at the end of the chunk
+ self.chunk_left = None
+ return ''.join(value)
+ else:
+ value.append(self._safe_read(chunk_left))
+ amt -= chunk_left
+
+ # we read the whole chunk, get another
+ self._safe_read(2) # toss the CRLF at the end of the chunk
+ chunk_left = None
+
+ # read and discard trailer up to the CRLF terminator
+ ### note: we shouldn't have any trailers!
+ while True:
+ line = self.fp.readline()
+ if not line:
+ # a vanishingly small number of sites EOF without
+ # sending the trailer
+ break
+ if line == '\r\n':
+ break
+
+ # we read everything; close the "file"
+ self.close()
+
+ return ''.join(value)
+
+ def _safe_read(self, amt):
+ """Read the number of bytes requested, compensating for partial reads.
+
+ Normally, we have a blocking socket, but a read() can be interrupted
+ by a signal (resulting in a partial read).
+
+ Note that we cannot distinguish between EOF and an interrupt when zero
+ bytes have been read. IncompleteRead() will be raised in this
+ situation.
+
+ This function should be used when <amt> bytes "should" be present for
+ reading. If the bytes are truly not available (due to EOF), then the
+ IncompleteRead exception can be used to detect the problem.
+ """
+ # NOTE(gps): As of svn r74426 socket._fileobject.read(x) will never
+ # return less than x bytes unless EOF is encountered. It now handles
+ # signal interruptions (socket.error EINTR) internally. This code
+ # never caught that exception anyways. It seems largely pointless.
+ # self.fp.read(amt) will work fine.
+ s = []
+ while amt > 0:
+ chunk = self.fp.read(min(amt, MAXAMOUNT))
+ if not chunk:
+ raise IncompleteRead(''.join(s), amt)
+ s.append(chunk)
+ amt -= len(chunk)
+ return ''.join(s)
+
+ def fileno(self):
+ return self.fp.fileno()
+
+ def getheader(self, name, default=None):
+ if self.msg is None:
+ raise ResponseNotReady()
+ return self.msg.getheader(name, default)
+
+ def getheaders(self):
+ """Return list of (header, value) tuples."""
+ if self.msg is None:
+ raise ResponseNotReady()
+ return self.msg.items()
+
+
+class HTTPConnection:
+
+ _http_vsn = 11
+ _http_vsn_str = 'HTTP/1.1'
+
+ response_class = HTTPResponse
+ default_port = HTTP_PORT
+ auto_open = 1
+ debuglevel = 0
+ strict = 0
+
+ def __init__(self, host, port=None, strict=None,
+ timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None):
+ self.timeout = timeout
+ self.source_address = source_address
+ self.sock = None
+ self._buffer = []
+ self.__response = None
+ self.__state = _CS_IDLE
+ self._method = None
+ self._tunnel_host = None
+ self._tunnel_port = None
+ self._tunnel_headers = {}
+
+ self._set_hostport(host, port)
+ if strict is not None:
+ self.strict = strict
+
+ def set_tunnel(self, host, port=None, headers=None):
+ """ Sets up the host and the port for the HTTP CONNECT Tunnelling.
+
+ The headers argument should be a mapping of extra HTTP headers
+ to send with the CONNECT request.
+ """
+ self._tunnel_host = host
+ self._tunnel_port = port
+ if headers:
+ self._tunnel_headers = headers
+ else:
+ self._tunnel_headers.clear()
+
+ def _set_hostport(self, host, port):
+ if port is None:
+ i = host.rfind(':')
+ j = host.rfind(']') # ipv6 addresses have [...]
+ if i > j:
+ try:
+ port = int(host[i+1:])
+ except ValueError:
+ raise InvalidURL("nonnumeric port: '%s'" % host[i+1:])
+ host = host[:i]
+ else:
+ port = self.default_port
+ if host and host[0] == '[' and host[-1] == ']':
+ host = host[1:-1]
+ self.host = host
+ self.port = port
+
+ def set_debuglevel(self, level):
+ self.debuglevel = level
+
+ def _tunnel(self):
+ self._set_hostport(self._tunnel_host, self._tunnel_port)
+ self.send("CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port))
+ for header, value in self._tunnel_headers.iteritems():
+ self.send("%s: %s\r\n" % (header, value))
+ self.send("\r\n")
+ response = self.response_class(self.sock, strict = self.strict,
+ method = self._method)
+ (version, code, message) = response._read_status()
+
+ if code != 200:
+ self.close()
+ raise socket.error("Tunnel connection failed: %d %s" % (code,
+ message.strip()))
+ while True:
+ line = response.fp.readline()
+ if line == '\r\n': break
+
+
+ def connect(self):
+ """Connect to the host and port specified in __init__."""
+ self.sock = socket.create_connection((self.host,self.port),
+ self.timeout, self.source_address)
+
+ if self._tunnel_host:
+ self._tunnel()
+
+ def close(self):
+ """Close the connection to the HTTP server."""
+ if self.sock:
+ self.sock.close() # close it manually... there may be other refs
+ self.sock = None
+ if self.__response:
+ self.__response.close()
+ self.__response = None
+ self.__state = _CS_IDLE
+
+ def send(self, data):
+ """Send `data' to the server."""
+ if self.sock is None:
+ if self.auto_open:
+ self.connect()
+ else:
+ raise NotConnected()
+
+ if self.debuglevel > 0:
+ print "send:", repr(data)
+ blocksize = 8192
+ if hasattr(data,'read') and not isinstance(data, array):
+ if self.debuglevel > 0: print "sendIng a read()able"
+ datablock = data.read(blocksize)
+ while datablock:
+ self.sock.sendall(datablock)
+ datablock = data.read(blocksize)
+ else:
+ self.sock.sendall(data)
+
+ def _output(self, s):
+ """Add a line of output to the current request buffer.
+
+ Assumes that the line does *not* end with \\r\\n.
+ """
+ self._buffer.append(s)
+
+ def _send_output(self, message_body=None):
+ """Send the currently buffered request and clear the buffer.
+
+ Appends an extra \\r\\n to the buffer.
+ A message_body may be specified, to be appended to the request.
+ """
+ self._buffer.extend(("", ""))
+ msg = "\r\n".join(self._buffer)
+ del self._buffer[:]
+ # If msg and message_body are sent in a single send() call,
+ # it will avoid performance problems caused by the interaction
+ # between delayed ack and the Nagle algorithim.
+ if isinstance(message_body, str):
+ msg += message_body
+ message_body = None
+ self.send(msg)
+ if message_body is not None:
+ #message_body was not a string (i.e. it is a file) and
+ #we must run the risk of Nagle
+ self.send(message_body)
+
+ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
+ """Send a request to the server.
+
+ `method' specifies an HTTP request method, e.g. 'GET'.
+ `url' specifies the object being requested, e.g. '/index.html'.
+ `skip_host' if True does not add automatically a 'Host:' header
+ `skip_accept_encoding' if True does not add automatically an
+ 'Accept-Encoding:' header
+ """
+
+ # if a prior response has been completed, then forget about it.
+ if self.__response and self.__response.isclosed():
+ self.__response = None
+
+
+ # in certain cases, we cannot issue another request on this connection.
+ # this occurs when:
+ # 1) we are in the process of sending a request. (_CS_REQ_STARTED)
+ # 2) a response to a previous request has signalled that it is going
+ # to close the connection upon completion.
+ # 3) the headers for the previous response have not been read, thus
+ # we cannot determine whether point (2) is true. (_CS_REQ_SENT)
+ #
+ # if there is no prior response, then we can request at will.
+ #
+ # if point (2) is true, then we will have passed the socket to the
+ # response (effectively meaning, "there is no prior response"), and
+ # will open a new one when a new request is made.
+ #
+ # Note: if a prior response exists, then we *can* start a new request.
+ # We are not allowed to begin fetching the response to this new
+ # request, however, until that prior response is complete.
+ #
+ if self.__state == _CS_IDLE:
+ self.__state = _CS_REQ_STARTED
+ else:
+ raise CannotSendRequest()
+
+ # Save the method we use, we need it later in the response phase
+ self._method = method
+ if not url:
+ url = '/'
+ hdr = '%s %s %s' % (method, url, self._http_vsn_str)
+
+ self._output(hdr)
+
+ if self._http_vsn == 11:
+ # Issue some standard headers for better HTTP/1.1 compliance
+
+ if not skip_host:
+ # this header is issued *only* for HTTP/1.1
+ # connections. more specifically, this means it is
+ # only issued when the client uses the new
+ # HTTPConnection() class. backwards-compat clients
+ # will be using HTTP/1.0 and those clients may be
+ # issuing this header themselves. we should NOT issue
+ # it twice; some web servers (such as Apache) barf
+ # when they see two Host: headers
+
+ # If we need a non-standard port,include it in the
+ # header. If the request is going through a proxy,
+ # but the host of the actual URL, not the host of the
+ # proxy.
+
+ netloc = ''
+ if url.startswith('http'):
+ nil, netloc, nil, nil, nil = urlsplit(url)
+
+ if netloc:
+ try:
+ netloc_enc = netloc.encode("ascii")
+ except UnicodeEncodeError:
+ netloc_enc = netloc.encode("idna")
+ self.putheader('Host', netloc_enc)
+ else:
+ try:
+ host_enc = self.host.encode("ascii")
+ except UnicodeEncodeError:
+ host_enc = self.host.encode("idna")
+ # Wrap the IPv6 Host Header with [] (RFC 2732)
+ if host_enc.find(':') >= 0:
+ host_enc = "[" + host_enc + "]"
+ if self.port == self.default_port:
+ self.putheader('Host', host_enc)
+ else:
+ self.putheader('Host', "%s:%s" % (host_enc, self.port))
+
+ # note: we are assuming that clients will not attempt to set these
+ # headers since *this* library must deal with the
+ # consequences. this also means that when the supporting
+ # libraries are updated to recognize other forms, then this
+ # code should be changed (removed or updated).
+
+ # we only want a Content-Encoding of "identity" since we don't
+ # support encodings such as x-gzip or x-deflate.
+ if not skip_accept_encoding:
+ self.putheader('Accept-Encoding', 'identity')
+
+ # we can accept "chunked" Transfer-Encodings, but no others
+ # NOTE: no TE header implies *only* "chunked"
+ #self.putheader('TE', 'chunked')
+
+ # if TE is supplied in the header, then it must appear in a
+ # Connection header.
+ #self.putheader('Connection', 'TE')
+
+ else:
+ # For HTTP/1.0, the server will assume "not chunked"
+ pass
+
+ def putheader(self, header, *values):
+ """Send a request header line to the server.
+
+ For example: h.putheader('Accept', 'text/html')
+ """
+ if self.__state != _CS_REQ_STARTED:
+ raise CannotSendHeader()
+
+ hdr = '%s: %s' % (header, '\r\n\t'.join([str(v) for v in values]))
+ self._output(hdr)
+
+ def endheaders(self, message_body=None):
+ """Indicate that the last header line has been sent to the server.
+
+ This method sends the request to the server. The optional
+ message_body argument can be used to pass message body
+ associated with the request. The message body will be sent in
+ the same packet as the message headers if possible. The
+ message_body should be a string.
+ """
+ if self.__state == _CS_REQ_STARTED:
+ self.__state = _CS_REQ_SENT
+ else:
+ raise CannotSendHeader()
+ self._send_output(message_body)
+
+ def request(self, method, url, body=None, headers={}):
+ """Send a complete request to the server."""
+ self._send_request(method, url, body, headers)
+
+ def _set_content_length(self, body):
+ # Set the content-length based on the body.
+ thelen = None
+ try:
+ thelen = str(len(body))
+ except TypeError, te:
+ # If this is a file-like object, try to
+ # fstat its file descriptor
+ try:
+ thelen = str(os.fstat(body.fileno()).st_size)
+ except (AttributeError, OSError):
+ # Don't send a length if this failed
+ if self.debuglevel > 0: print "Cannot stat!!"
+
+ if thelen is not None:
+ self.putheader('Content-Length', thelen)
+
+ def _send_request(self, method, url, body, headers):
+ # Honor explicitly requested Host: and Accept-Encoding: headers.
+ header_names = dict.fromkeys([k.lower() for k in headers])
+ skips = {}
+ if 'host' in header_names:
+ skips['skip_host'] = 1
+ if 'accept-encoding' in header_names:
+ skips['skip_accept_encoding'] = 1
+
+ self.putrequest(method, url, **skips)
+
+ if body and ('content-length' not in header_names):
+ self._set_content_length(body)
+ for hdr, value in headers.iteritems():
+ self.putheader(hdr, value)
+ self.endheaders(body)
+
+ def getresponse(self, buffering=False):
+ "Get the response from the server."
+
+ # if a prior response has been completed, then forget about it.
+ if self.__response and self.__response.isclosed():
+ self.__response = None
+
+ #
+ # if a prior response exists, then it must be completed (otherwise, we
+ # cannot read this response's header to determine the connection-close
+ # behavior)
+ #
+ # note: if a prior response existed, but was connection-close, then the
+ # socket and response were made independent of this HTTPConnection
+ # object since a new request requires that we open a whole new
+ # connection
+ #
+ # this means the prior response had one of two states:
+ # 1) will_close: this connection was reset and the prior socket and
+ # response operate independently
+ # 2) persistent: the response was retained and we await its
+ # isclosed() status to become true.
+ #
+ if self.__state != _CS_REQ_SENT or self.__response:
+ raise ResponseNotReady()
+
+ args = (self.sock,)
+ kwds = {"strict":self.strict, "method":self._method}
+ if self.debuglevel > 0:
+ args += (self.debuglevel,)
+ if buffering:
+ #only add this keyword if non-default, for compatibility with
+ #other response_classes.
+ kwds["buffering"] = True;
+ response = self.response_class(*args, **kwds)
+
+ try:
+ response.begin()
+ except:
+ response.close()
+ raise
+ assert response.will_close != _UNKNOWN
+ self.__state = _CS_IDLE
+
+ if response.will_close:
+ # this effectively passes the connection to the response
+ self.close()
+ else:
+ # remember this, so we can tell when it is complete
+ self.__response = response
+
+ return response
+
+
+class HTTP:
+ "Compatibility class with httplib.py from 1.5."
+
+ _http_vsn = 10
+ _http_vsn_str = 'HTTP/1.0'
+
+ debuglevel = 0
+
+ _connection_class = HTTPConnection
+
+ def __init__(self, host='', port=None, strict=None):
+ "Provide a default host, since the superclass requires one."
+
+ # some joker passed 0 explicitly, meaning default port
+ if port == 0:
+ port = None
+
+ # Note that we may pass an empty string as the host; this will throw
+ # an error when we attempt to connect. Presumably, the client code
+ # will call connect before then, with a proper host.
+ self._setup(self._connection_class(host, port, strict))
+
+ def _setup(self, conn):
+ self._conn = conn
+
+ # set up delegation to flesh out interface
+ self.send = conn.send
+ self.putrequest = conn.putrequest
+ self.putheader = conn.putheader
+ self.endheaders = conn.endheaders
+ self.set_debuglevel = conn.set_debuglevel
+
+ conn._http_vsn = self._http_vsn
+ conn._http_vsn_str = self._http_vsn_str
+
+ self.file = None
+
+ def connect(self, host=None, port=None):
+ "Accept arguments to set the host/port, since the superclass doesn't."
+
+ if host is not None:
+ self._conn._set_hostport(host, port)
+ self._conn.connect()
+
+ def getfile(self):
+ "Provide a getfile, since the superclass' does not use this concept."
+ return self.file
+
+ def getreply(self, buffering=False):
+ """Compat definition since superclass does not define it.
+
+ Returns a tuple consisting of:
+ - server status code (e.g. '200' if all goes well)
+ - server "reason" corresponding to status code
+ - any RFC822 headers in the response from the server
+ """
+ try:
+ if not buffering:
+ response = self._conn.getresponse()
+ else:
+ #only add this keyword if non-default for compatibility
+ #with other connection classes
+ response = self._conn.getresponse(buffering)
+ except BadStatusLine, e:
+ ### hmm. if getresponse() ever closes the socket on a bad request,
+ ### then we are going to have problems with self.sock
+
+ ### should we keep this behavior? do people use it?
+ # keep the socket open (as a file), and return it
+ self.file = self._conn.sock.makefile('rb', 0)
+
+ # close our socket -- we want to restart after any protocol error
+ self.close()
+
+ self.headers = None
+ return -1, e.line, None
+
+ self.headers = response.msg
+ self.file = response.fp
+ return response.status, response.reason, response.msg
+
+ def close(self):
+ self._conn.close()
+
+ # note that self.file == response.fp, which gets closed by the
+ # superclass. just clear the object ref here.
+ ### hmm. messy. if status==-1, then self.file is owned by us.
+ ### well... we aren't explicitly closing, but losing this ref will
+ ### do it
+ self.file = None
+
+try:
+ import ssl
+except ImportError:
+ pass
+else:
+ class HTTPSConnection(HTTPConnection):
+ "This class allows communication via SSL."
+
+ default_port = HTTPS_PORT
+
+ def __init__(self, host, port=None, key_file=None, cert_file=None,
+ strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+ source_address=None):
+ HTTPConnection.__init__(self, host, port, strict, timeout,
+ source_address)
+ self.key_file = key_file
+ self.cert_file = cert_file
+
+ def connect(self):
+ "Connect to a host on a given (SSL) port."
+
+ sock = socket.create_connection((self.host, self.port),
+ self.timeout, self.source_address)
+ if self._tunnel_host:
+ self.sock = sock
+ self._tunnel()
+ self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
+
+ __all__.append("HTTPSConnection")
+
+ class HTTPS(HTTP):
+ """Compatibility with 1.5 httplib interface
+
+ Python 1.5.2 did not have an HTTPS class, but it defined an
+ interface for sending http requests that is also useful for
+ https.
+ """
+
+ _connection_class = HTTPSConnection
+
+ def __init__(self, host='', port=None, key_file=None, cert_file=None,
+ strict=None):
+ # provide a default host, pass the X509 cert info
+
+ # urf. compensate for bad input.
+ if port == 0:
+ port = None
+ self._setup(self._connection_class(host, port, key_file,
+ cert_file, strict))
+
+ # we never actually use these for anything, but we keep them
+ # here for compatibility with post-1.5.2 CVS.
+ self.key_file = key_file
+ self.cert_file = cert_file
+
+
+ def FakeSocket (sock, sslobj):
+ warnings.warn("FakeSocket is deprecated, and won't be in 3.x. " +
+ "Use the result of ssl.wrap_socket() directly instead.",
+ DeprecationWarning, stacklevel=2)
+ return sslobj
+
+
+class HTTPException(Exception):
+ # Subclasses that define an __init__ must call Exception.__init__
+ # or define self.args. Otherwise, str() will fail.
+ pass
+
+class NotConnected(HTTPException):
+ pass
+
+class InvalidURL(HTTPException):
+ pass
+
+class UnknownProtocol(HTTPException):
+ def __init__(self, version):
+ self.args = version,
+ self.version = version
+
+class UnknownTransferEncoding(HTTPException):
+ pass
+
+class UnimplementedFileMode(HTTPException):
+ pass
+
+class IncompleteRead(HTTPException):
+ def __init__(self, partial, expected=None):
+ self.args = partial,
+ self.partial = partial
+ self.expected = expected
+ def __repr__(self):
+ if self.expected is not None:
+ e = ', %i more expected' % self.expected
+ else:
+ e = ''
+ return 'IncompleteRead(%i bytes read%s)' % (len(self.partial), e)
+ def __str__(self):
+ return repr(self)
+
+class ImproperConnectionState(HTTPException):
+ pass
+
+class CannotSendRequest(ImproperConnectionState):
+ pass
+
+class CannotSendHeader(ImproperConnectionState):
+ pass
+
+class ResponseNotReady(ImproperConnectionState):
+ pass
+
+class BadStatusLine(HTTPException):
+ def __init__(self, line):
+ if not line:
+ line = repr(line)
+ self.args = line,
+ self.line = line
+
+# for backwards compatibility
+error = HTTPException
+
+class LineAndFileWrapper:
+ """A limited file-like object for HTTP/0.9 responses."""
+
+ # The status-line parsing code calls readline(), which normally
+ # get the HTTP status line. For a 0.9 response, however, this is
+ # actually the first line of the body! Clients need to get a
+ # readable file object that contains that line.
+
+ def __init__(self, line, file):
+ self._line = line
+ self._file = file
+ self._line_consumed = 0
+ self._line_offset = 0
+ self._line_left = len(line)
+
+ def __getattr__(self, attr):
+ return getattr(self._file, attr)
+
+ def _done(self):
+ # called when the last byte is read from the line. After the
+ # call, all read methods are delegated to the underlying file
+ # object.
+ self._line_consumed = 1
+ self.read = self._file.read
+ self.readline = self._file.readline
+ self.readlines = self._file.readlines
+
+ def read(self, amt=None):
+ if self._line_consumed:
+ return self._file.read(amt)
+ assert self._line_left
+ if amt is None or amt > self._line_left:
+ s = self._line[self._line_offset:]
+ self._done()
+ if amt is None:
+ return s + self._file.read()
+ else:
+ return s + self._file.read(amt - len(s))
+ else:
+ assert amt <= self._line_left
+ i = self._line_offset
+ j = i + amt
+ s = self._line[i:j]
+ self._line_offset = j
+ self._line_left -= amt
+ if self._line_left == 0:
+ self._done()
+ return s
+
+ def readline(self):
+ if self._line_consumed:
+ return self._file.readline()
+ assert self._line_left
+ s = self._line[self._line_offset:]
+ self._done()
+ return s
+
+ def readlines(self, size=None):
+ if self._line_consumed:
+ return self._file.readlines(size)
+ assert self._line_left
+ L = [self._line[self._line_offset:]]
+ self._done()
+ if size is None:
+ return L + self._file.readlines()
+ else:
+ return L + self._file.readlines(size)
+
+def test():
+ """Test this module.
+
+ A hodge podge of tests collected here, because they have too many
+ external dependencies for the regular test suite.
+ """
+
+ import sys
+ import getopt
+ opts, args = getopt.getopt(sys.argv[1:], 'd')
+ dl = 0
+ for o, a in opts:
+ if o == '-d': dl = dl + 1
+ host = 'www.python.org'
+ selector = '/'
+ if args[0:]: host = args[0]
+ if args[1:]: selector = args[1]
+ h = HTTP()
+ h.set_debuglevel(dl)
+ h.connect(host)
+ h.putrequest('GET', selector)
+ h.endheaders()
+ status, reason, headers = h.getreply()
+ print 'status =', status
+ print 'reason =', reason
+ print "read", len(h.getfile().read())
+ print
+ if headers:
+ for header in headers.headers: print header.strip()
+ print
+
+ # minimal test that code to extract host from url works
+ class HTTP11(HTTP):
+ _http_vsn = 11
+ _http_vsn_str = 'HTTP/1.1'
+
+ h = HTTP11('www.python.org')
+ h.putrequest('GET', 'http://www.python.org/~jeremy/')
+ h.endheaders()
+ h.getreply()
+ h.close()
+
+ try:
+ import ssl
+ except ImportError:
+ pass
+ else:
+
+ for host, selector in (('sourceforge.net', '/projects/python'),
+ ):
+ print "https://%s%s" % (host, selector)
+ hs = HTTPS()
+ hs.set_debuglevel(dl)
+ hs.connect(host)
+ hs.putrequest('GET', selector)
+ hs.endheaders()
+ status, reason, headers = hs.getreply()
+ print 'status =', status
+ print 'reason =', reason
+ print "read", len(hs.getfile().read())
+ print
+ if headers:
+ for header in headers.headers: print header.strip()
+ print
+
+if __name__ == '__main__':
+ test()
diff --git a/lib-python/modified-2.7/test/test_array.py b/lib-python/modified-2.7/test/test_array.py
--- a/lib-python/modified-2.7/test/test_array.py
+++ b/lib-python/modified-2.7/test/test_array.py
@@ -295,9 +295,10 @@
)
b = array.array(self.badtypecode())
- self.assertRaises(TypeError, "a + b")
-
- self.assertRaises(TypeError, "a + 'bad'")
+ with self.assertRaises(TypeError):
+ a + b
+ with self.assertRaises(TypeError):
+ a + 'bad'
def test_iadd(self):
a = array.array(self.typecode, self.example[::-1])
@@ -316,9 +317,10 @@
)
b = array.array(self.badtypecode())
- self.assertRaises(TypeError, "a += b")
-
- self.assertRaises(TypeError, "a += 'bad'")
+ with self.assertRaises(TypeError):
+ a += b
+ with self.assertRaises(TypeError):
+ a += 'bad'
def test_mul(self):
a = 5*array.array(self.typecode, self.example)
@@ -345,7 +347,8 @@
array.array(self.typecode)
)
- self.assertRaises(TypeError, "a * 'bad'")
+ with self.assertRaises(TypeError):
+ a * 'bad'
def test_imul(self):
a = array.array(self.typecode, self.example)
@@ -374,7 +377,8 @@
a *= -1
self.assertEqual(a, array.array(self.typecode))
- self.assertRaises(TypeError, "a *= 'bad'")
+ with self.assertRaises(TypeError):
+ a *= 'bad'
def test_getitem(self):
a = array.array(self.typecode, self.example)
diff --git a/lib-python/modified-2.7/test/test_multiprocessing.py b/lib-python/modified-2.7/test/test_multiprocessing.py
--- a/lib-python/modified-2.7/test/test_multiprocessing.py
+++ b/lib-python/modified-2.7/test/test_multiprocessing.py
@@ -510,7 +510,6 @@
p.join()
- @unittest.skipIf(os.name == 'posix', "PYPY: FIXME")
def test_qsize(self):
q = self.Queue()
try:
@@ -532,7 +531,6 @@
time.sleep(DELTA)
q.task_done()
- @unittest.skipIf(os.name == 'posix', "PYPY: FIXME")
def test_task_done(self):
queue = self.JoinableQueue()
@@ -1091,7 +1089,6 @@
class _TestPoolWorkerLifetime(BaseTestCase):
ALLOWED_TYPES = ('processes', )
- @unittest.skipIf(os.name == 'posix', "PYPY: FIXME")
def test_pool_worker_lifetime(self):
p = multiprocessing.Pool(3, maxtasksperchild=10)
self.assertEqual(3, len(p._pool))
@@ -1280,7 +1277,6 @@
queue = manager.get_queue()
queue.put('hello world')
- @unittest.skipIf(os.name == 'posix', "PYPY: FIXME")
def test_rapid_restart(self):
authkey = os.urandom(32)
manager = QueueManager(
@@ -1297,6 +1293,7 @@
queue = manager.get_queue()
self.assertEqual(queue.get(), 'hello world')
del queue
+ test_support.gc_collect()
manager.shutdown()
manager = QueueManager(
address=addr, authkey=authkey, serializer=SERIALIZER)
@@ -1573,7 +1570,6 @@
ALLOWED_TYPES = ('processes',)
- @unittest.skipIf(os.name == 'posix', "PYPY: FIXME")
def test_heap(self):
iterations = 5000
maxblocks = 50
diff --git a/lib-python/modified-2.7/test/test_sys_settrace.py b/lib-python/modified-2.7/test/test_sys_settrace.py
--- a/lib-python/modified-2.7/test/test_sys_settrace.py
+++ b/lib-python/modified-2.7/test/test_sys_settrace.py
@@ -286,11 +286,11 @@
self.compare_events(func.func_code.co_firstlineno,
tracer.events, func.events)
- def set_and_retrieve_none(self):
+ def test_set_and_retrieve_none(self):
sys.settrace(None)
assert sys.gettrace() is None
- def set_and_retrieve_func(self):
+ def test_set_and_retrieve_func(self):
def fn(*args):
pass
diff --git a/lib-python/modified-2.7/test/test_urllib2.py b/lib-python/modified-2.7/test/test_urllib2.py
--- a/lib-python/modified-2.7/test/test_urllib2.py
+++ b/lib-python/modified-2.7/test/test_urllib2.py
@@ -307,6 +307,9 @@
def getresponse(self):
return MockHTTPResponse(MockFile(), {}, 200, "OK")
+ def close(self):
+ pass
+
class MockHandler:
# useful for testing handler machinery
# see add_ordered_mock_handlers() docstring
diff --git a/lib-python/modified-2.7/urllib2.py b/lib-python/modified-2.7/urllib2.py
new file mode 100644
--- /dev/null
+++ b/lib-python/modified-2.7/urllib2.py
@@ -0,0 +1,1440 @@
+"""An extensible library for opening URLs using a variety of protocols
+
+The simplest way to use this module is to call the urlopen function,
+which accepts a string containing a URL or a Request object (described
+below). It opens the URL and returns the results as file-like
+object; the returned object has some extra methods described below.
+
+The OpenerDirector manages a collection of Handler objects that do
+all the actual work. Each Handler implements a particular protocol or
+option. The OpenerDirector is a composite object that invokes the
+Handlers needed to open the requested URL. For example, the
+HTTPHandler performs HTTP GET and POST requests and deals with
+non-error returns. The HTTPRedirectHandler automatically deals with
+HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler
+deals with digest authentication.
+
+urlopen(url, data=None) -- Basic usage is the same as original
+urllib. pass the url and optionally data to post to an HTTP URL, and
+get a file-like object back. One difference is that you can also pass
+a Request instance instead of URL. Raises a URLError (subclass of
+IOError); for HTTP errors, raises an HTTPError, which can also be
+treated as a valid response.
+
+build_opener -- Function that creates a new OpenerDirector instance.
+Will install the default handlers. Accepts one or more Handlers as
+arguments, either instances or Handler classes that it will
+instantiate. If one of the argument is a subclass of the default
+handler, the argument will be installed instead of the default.
+
+install_opener -- Installs a new opener as the default opener.
+
+objects of interest:
+
+OpenerDirector -- Sets up the User Agent as the Python-urllib client and manages
+the Handler classes, while dealing with requests and responses.
+
+Request -- An object that encapsulates the state of a request. The
+state can be as simple as the URL. It can also include extra HTTP
+headers, e.g. a User-Agent.
+
+BaseHandler --
+
+exceptions:
+URLError -- A subclass of IOError, individual protocols have their own
+specific subclass.
+
+HTTPError -- Also a valid HTTP response, so you can treat an HTTP error
+as an exceptional event or valid response.
+
+internals:
+BaseHandler and parent
+_call_chain conventions
+
+Example usage:
+
+import urllib2
+
+# set up authentication info
+authinfo = urllib2.HTTPBasicAuthHandler()
+authinfo.add_password(realm='PDQ Application',
+ uri='https://mahler:8092/site-updates.py',
+ user='klem',
+ passwd='geheim$parole')
+
+proxy_support = urllib2.ProxyHandler({"http" : "http://ahad-haam:3128"})
+
+# build a new opener that adds authentication and caching FTP handlers
+opener = urllib2.build_opener(proxy_support, authinfo, urllib2.CacheFTPHandler)
+
+# install it
+urllib2.install_opener(opener)
+
+f = urllib2.urlopen('http://www.python.org/')
+
+
+"""
+
+# XXX issues:
+# If an authentication error handler that tries to perform
+# authentication for some reason but fails, how should the error be
+# signalled? The client needs to know the HTTP error code. But if
+# the handler knows that the problem was, e.g., that it didn't know
+# that hash algo that requested in the challenge, it would be good to
+# pass that information along to the client, too.
+# ftp errors aren't handled cleanly
+# check digest against correct (i.e. non-apache) implementation
+
+# Possible extensions:
+# complex proxies XXX not sure what exactly was meant by this
+# abstract factory for opener
+
+import base64
+import hashlib
+import httplib
+import mimetools
+import os
+import posixpath
+import random
+import re
+import socket
+import sys
+import time
+import urlparse
+import bisect
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+from urllib import (unwrap, unquote, splittype, splithost, quote,
+ addinfourl, splitport, splittag,
+ splitattr, ftpwrapper, splituser, splitpasswd, splitvalue)
+
+# support for FileHandler, proxies via environment variables
+from urllib import localhost, url2pathname, getproxies, proxy_bypass
+
+# used in User-Agent header sent
+__version__ = sys.version[:3]
+
+_opener = None
+def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
+ global _opener
+ if _opener is None:
+ _opener = build_opener()
+ return _opener.open(url, data, timeout)
+
+def install_opener(opener):
+ global _opener
+ _opener = opener
+
+# do these error classes make sense?
+# make sure all of the IOError stuff is overridden. we just want to be
+# subtypes.
+
+class URLError(IOError):
+ # URLError is a sub-type of IOError, but it doesn't share any of
+ # the implementation. need to override __init__ and __str__.
+ # It sets self.args for compatibility with other EnvironmentError
+ # subclasses, but args doesn't have the typical format with errno in
+ # slot 0 and strerror in slot 1. This may be better than nothing.
+ def __init__(self, reason):
+ self.args = reason,
+ self.reason = reason
+
+ def __str__(self):
+ return '<urlopen error %s>' % self.reason
+
+class HTTPError(URLError, addinfourl):
+ """Raised when HTTP error occurs, but also acts like non-error return"""
+ __super_init = addinfourl.__init__
+
+ def __init__(self, url, code, msg, hdrs, fp):
+ self.code = code
+ self.msg = msg
+ self.hdrs = hdrs
+ self.fp = fp
+ self.filename = url
+ # The addinfourl classes depend on fp being a valid file
+ # object. In some cases, the HTTPError may not have a valid
+ # file object. If this happens, the simplest workaround is to
+ # not initialize the base classes.
+ if fp is not None:
+ self.__super_init(fp, hdrs, url, code)
+
+ def __str__(self):
+ return 'HTTP Error %s: %s' % (self.code, self.msg)
+
+# copied from cookielib.py
+_cut_port_re = re.compile(r":\d+$")
+def request_host(request):
+ """Return request-host, as defined by RFC 2965.
+
+ Variation from RFC: returned value is lowercased, for convenient
+ comparison.
+
+ """
+ url = request.get_full_url()
+ host = urlparse.urlparse(url)[1]
+ if host == "":
+ host = request.get_header("Host", "")
+
+ # remove port, if present
+ host = _cut_port_re.sub("", host, 1)
+ return host.lower()
+
+class Request:
+
+ def __init__(self, url, data=None, headers={},
+ origin_req_host=None, unverifiable=False):
+ # unwrap('<URL:type://host/path>') --> 'type://host/path'
+ self.__original = unwrap(url)
+ self.__original, fragment = splittag(self.__original)
+ self.type = None
+ # self.__r_type is what's left after doing the splittype
+ self.host = None
+ self.port = None
+ self._tunnel_host = None
+ self.data = data
+ self.headers = {}
+ for key, value in headers.items():
+ self.add_header(key, value)
+ self.unredirected_hdrs = {}
+ if origin_req_host is None:
+ origin_req_host = request_host(self)
+ self.origin_req_host = origin_req_host
+ self.unverifiable = unverifiable
+
+ def __getattr__(self, attr):
+ # XXX this is a fallback mechanism to guard against these
+ # methods getting called in a non-standard order. this may be
+ # too complicated and/or unnecessary.
+ # XXX should the __r_XXX attributes be public?
+ if attr[:12] == '_Request__r_':
+ name = attr[12:]
+ if hasattr(Request, 'get_' + name):
+ getattr(self, 'get_' + name)()
+ return getattr(self, attr)
+ raise AttributeError, attr
+
+ def get_method(self):
+ if self.has_data():
+ return "POST"
+ else:
+ return "GET"
+
+ # XXX these helper methods are lame
+
+ def add_data(self, data):
+ self.data = data
+
+ def has_data(self):
+ return self.data is not None
+
+ def get_data(self):
+ return self.data
+
+ def get_full_url(self):
+ return self.__original
+
+ def get_type(self):
+ if self.type is None:
+ self.type, self.__r_type = splittype(self.__original)
+ if self.type is None:
+ raise ValueError, "unknown url type: %s" % self.__original
+ return self.type
+
+ def get_host(self):
+ if self.host is None:
+ self.host, self.__r_host = splithost(self.__r_type)
+ if self.host:
+ self.host = unquote(self.host)
+ return self.host
+
+ def get_selector(self):
+ return self.__r_host
+
+ def set_proxy(self, host, type):
+ if self.type == 'https' and not self._tunnel_host:
+ self._tunnel_host = self.host
+ else:
+ self.type = type
+ self.__r_host = self.__original
+
+ self.host = host
+
+ def has_proxy(self):
+ return self.__r_host == self.__original
+
+ def get_origin_req_host(self):
+ return self.origin_req_host
+
+ def is_unverifiable(self):
+ return self.unverifiable
+
+ def add_header(self, key, val):
+ # useful for something like authentication
+ self.headers[key.capitalize()] = val
+
+ def add_unredirected_header(self, key, val):
+ # will not be added to a redirected request
+ self.unredirected_hdrs[key.capitalize()] = val
+
+ def has_header(self, header_name):
+ return (header_name in self.headers or
+ header_name in self.unredirected_hdrs)
+
+ def get_header(self, header_name, default=None):
+ return self.headers.get(
+ header_name,
+ self.unredirected_hdrs.get(header_name, default))
+
+ def header_items(self):
+ hdrs = self.unredirected_hdrs.copy()
+ hdrs.update(self.headers)
+ return hdrs.items()
+
+class OpenerDirector:
+ def __init__(self):
+ client_version = "Python-urllib/%s" % __version__
+ self.addheaders = [('User-agent', client_version)]
+ # manage the individual handlers
+ self.handlers = []
+ self.handle_open = {}
+ self.handle_error = {}
+ self.process_response = {}
+ self.process_request = {}
+
+ def add_handler(self, handler):
+ if not hasattr(handler, "add_parent"):
+ raise TypeError("expected BaseHandler instance, got %r" %
+ type(handler))
+
+ added = False
+ for meth in dir(handler):
+ if meth in ["redirect_request", "do_open", "proxy_open"]:
+ # oops, coincidental match
+ continue
+
+ i = meth.find("_")
+ protocol = meth[:i]
+ condition = meth[i+1:]
+
+ if condition.startswith("error"):
+ j = condition.find("_") + i + 1
+ kind = meth[j+1:]
+ try:
+ kind = int(kind)
+ except ValueError:
+ pass
+ lookup = self.handle_error.get(protocol, {})
+ self.handle_error[protocol] = lookup
+ elif condition == "open":
+ kind = protocol
+ lookup = self.handle_open
+ elif condition == "response":
+ kind = protocol
+ lookup = self.process_response
+ elif condition == "request":
+ kind = protocol
+ lookup = self.process_request
+ else:
+ continue
+
+ handlers = lookup.setdefault(kind, [])
+ if handlers:
+ bisect.insort(handlers, handler)
+ else:
+ handlers.append(handler)
+ added = True
+
+ if added:
+ # the handlers must work in an specific order, the order
+ # is specified in a Handler attribute
+ bisect.insort(self.handlers, handler)
+ handler.add_parent(self)
+
+ def close(self):
+ # Only exists for backwards compatibility.
+ pass
+
+ def _call_chain(self, chain, kind, meth_name, *args):
+ # Handlers raise an exception if no one else should try to handle
+ # the request, or return None if they can't but another handler
+ # could. Otherwise, they return the response.
+ handlers = chain.get(kind, ())
+ for handler in handlers:
+ func = getattr(handler, meth_name)
+
+ result = func(*args)
+ if result is not None:
+ return result
+
+ def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
+ # accept a URL or a Request object
+ if isinstance(fullurl, basestring):
+ req = Request(fullurl, data)
+ else:
+ req = fullurl
+ if data is not None:
+ req.add_data(data)
+
+ req.timeout = timeout
+ protocol = req.get_type()
+
+ # pre-process request
+ meth_name = protocol+"_request"
+ for processor in self.process_request.get(protocol, []):
+ meth = getattr(processor, meth_name)
+ req = meth(req)
+
+ response = self._open(req, data)
+
+ # post-process response
+ meth_name = protocol+"_response"
+ for processor in self.process_response.get(protocol, []):
+ meth = getattr(processor, meth_name)
+ try:
+ response = meth(req, response)
+ except:
+ response.close()
+ raise
+
+ return response
+
+ def _open(self, req, data=None):
+ result = self._call_chain(self.handle_open, 'default',
+ 'default_open', req)
+ if result:
+ return result
+
+ protocol = req.get_type()
+ result = self._call_chain(self.handle_open, protocol, protocol +
+ '_open', req)
+ if result:
+ return result
+
+ return self._call_chain(self.handle_open, 'unknown',
+ 'unknown_open', req)
+
+ def error(self, proto, *args):
+ if proto in ('http', 'https'):
+ # XXX http[s] protocols are special-cased
+ dict = self.handle_error['http'] # https is not different than http
+ proto = args[2] # YUCK!
+ meth_name = 'http_error_%s' % proto
+ http_err = 1
+ orig_args = args
+ else:
+ dict = self.handle_error
+ meth_name = proto + '_error'
+ http_err = 0
+ args = (dict, proto, meth_name) + args
+ result = self._call_chain(*args)
+ if result:
+ return result
+
+ if http_err:
+ args = (dict, 'default', 'http_error_default') + orig_args
+ return self._call_chain(*args)
+
+# XXX probably also want an abstract factory that knows when it makes
+# sense to skip a superclass in favor of a subclass and when it might
+# make sense to include both
+
+def build_opener(*handlers):
+ """Create an opener object from a list of handlers.
+
+ The opener will use several default handlers, including support
+ for HTTP, FTP and when applicable, HTTPS.
+
+ If any of the handlers passed as arguments are subclasses of the
+ default handlers, the default handlers will not be used.
+ """
+ import types
+ def isclass(obj):
+ return isinstance(obj, (types.ClassType, type))
+
+ opener = OpenerDirector()
+ default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
+ HTTPDefaultErrorHandler, HTTPRedirectHandler,
+ FTPHandler, FileHandler, HTTPErrorProcessor]
+ if hasattr(httplib, 'HTTPS'):
+ default_classes.append(HTTPSHandler)
+ skip = set()
+ for klass in default_classes:
+ for check in handlers:
+ if isclass(check):
+ if issubclass(check, klass):
+ skip.add(klass)
+ elif isinstance(check, klass):
+ skip.add(klass)
+ for klass in skip:
+ default_classes.remove(klass)
+
+ for klass in default_classes:
+ opener.add_handler(klass())
+
+ for h in handlers:
+ if isclass(h):
+ h = h()
+ opener.add_handler(h)
+ return opener
+
+class BaseHandler:
+ handler_order = 500
+
+ def add_parent(self, parent):
+ self.parent = parent
+
+ def close(self):
+ # Only exists for backwards compatibility
+ pass
+
+ def __lt__(self, other):
+ if not hasattr(other, "handler_order"):
+ # Try to preserve the old behavior of having custom classes
+ # inserted after default ones (works only for custom user
+ # classes which are not aware of handler_order).
+ return True
+ return self.handler_order < other.handler_order
+
+
+class HTTPErrorProcessor(BaseHandler):
+ """Process HTTP error responses."""
+ handler_order = 1000 # after all other processing
+
+ def http_response(self, request, response):
+ code, msg, hdrs = response.code, response.msg, response.info()
+
+ # According to RFC 2616, "2xx" code indicates that the client's
+ # request was successfully received, understood, and accepted.
+ if not (200 <= code < 300):
+ response = self.parent.error(
+ 'http', request, response, code, msg, hdrs)
+
+ return response
+
+ https_response = http_response
+
+class HTTPDefaultErrorHandler(BaseHandler):
+ def http_error_default(self, req, fp, code, msg, hdrs):
+ raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
+
+class HTTPRedirectHandler(BaseHandler):
+ # maximum number of redirections to any single URL
+ # this is needed because of the state that cookies introduce
+ max_repeats = 4
+ # maximum total number of redirections (regardless of URL) before
+ # assuming we're in a loop
+ max_redirections = 10
+
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
+ """Return a Request or None in response to a redirect.
+
+ This is called by the http_error_30x methods when a
+ redirection response is received. If a redirection should
+ take place, return a new Request to allow http_error_30x to
+ perform the redirect. Otherwise, raise HTTPError if no-one
+ else should try to handle this url. Return None if you can't
+ but another Handler might.
+ """
+ m = req.get_method()
+ if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
+ or code in (301, 302, 303) and m == "POST"):
+ # Strictly (according to RFC 2616), 301 or 302 in response
+ # to a POST MUST NOT cause a redirection without confirmation
+ # from the user (of urllib2, in this case). In practice,
+ # essentially all clients do redirect in this case, so we
+ # do the same.
+ # be conciliant with URIs containing a space
+ newurl = newurl.replace(' ', '%20')
+ newheaders = dict((k,v) for k,v in req.headers.items()
+ if k.lower() not in ("content-length", "content-type")
+ )
+ return Request(newurl,
+ headers=newheaders,
+ origin_req_host=req.get_origin_req_host(),
+ unverifiable=True)
+ else:
+ raise HTTPError(req.get_full_url(), code, msg, headers, fp)
+
+ # Implementation note: To avoid the server sending us into an
+ # infinite loop, the request object needs to track what URLs we
+ # have already seen. Do this by adding a handler-specific
+ # attribute to the Request object.
+ def http_error_302(self, req, fp, code, msg, headers):
+ # Some servers (incorrectly) return multiple Location headers
+ # (so probably same goes for URI). Use first header.
+ if 'location' in headers:
+ newurl = headers.getheaders('location')[0]
+ elif 'uri' in headers:
+ newurl = headers.getheaders('uri')[0]
+ else:
+ return
+
+ # fix a possible malformed URL
+ urlparts = urlparse.urlparse(newurl)
+ if not urlparts.path:
+ urlparts = list(urlparts)
+ urlparts[2] = "/"
+ newurl = urlparse.urlunparse(urlparts)
+
+ newurl = urlparse.urljoin(req.get_full_url(), newurl)
+
+ # XXX Probably want to forget about the state of the current
+ # request, although that might interact poorly with other
+ # handlers that also use handler-specific request attributes
+ new = self.redirect_request(req, fp, code, msg, headers, newurl)
+ if new is None:
+ return
+
+ # loop detection
+ # .redirect_dict has a key url if url was previously visited.
+ if hasattr(req, 'redirect_dict'):
+ visited = new.redirect_dict = req.redirect_dict
+ if (visited.get(newurl, 0) >= self.max_repeats or
+ len(visited) >= self.max_redirections):
+ raise HTTPError(req.get_full_url(), code,
+ self.inf_msg + msg, headers, fp)
+ else:
+ visited = new.redirect_dict = req.redirect_dict = {}
+ visited[newurl] = visited.get(newurl, 0) + 1
+
+ # Don't close the fp until we are sure that we won't use it
+ # with HTTPError.
+ fp.read()
+ fp.close()
+
+ return self.parent.open(new, timeout=req.timeout)
+
+ http_error_301 = http_error_303 = http_error_307 = http_error_302
+
+ inf_msg = "The HTTP server returned a redirect error that would " \
+ "lead to an infinite loop.\n" \
+ "The last 30x error message was:\n"
+
+
+def _parse_proxy(proxy):
+ """Return (scheme, user, password, host/port) given a URL or an authority.
+
+ If a URL is supplied, it must have an authority (host:port) component.
+ According to RFC 3986, having an authority component means the URL must
+ have two slashes after the scheme:
+
+ >>> _parse_proxy('file:/ftp.example.com/')
+ Traceback (most recent call last):
+ ValueError: proxy URL with no authority: 'file:/ftp.example.com/'
+
+ The first three items of the returned tuple may be None.
+
+ Examples of authority parsing:
+
+ >>> _parse_proxy('proxy.example.com')
+ (None, None, None, 'proxy.example.com')
+ >>> _parse_proxy('proxy.example.com:3128')
+ (None, None, None, 'proxy.example.com:3128')
+
+ The authority component may optionally include userinfo (assumed to be
+ username:password):
+
+ >>> _parse_proxy('joe:password at proxy.example.com')
+ (None, 'joe', 'password', 'proxy.example.com')
+ >>> _parse_proxy('joe:password at proxy.example.com:3128')
+ (None, 'joe', 'password', 'proxy.example.com:3128')
+
+ Same examples, but with URLs instead:
+
+ >>> _parse_proxy('http://proxy.example.com/')
+ ('http', None, None, 'proxy.example.com')
+ >>> _parse_proxy('http://proxy.example.com:3128/')
+ ('http', None, None, 'proxy.example.com:3128')
+ >>> _parse_proxy('http://joe:password@proxy.example.com/')
+ ('http', 'joe', 'password', 'proxy.example.com')
+ >>> _parse_proxy('http://joe:password@proxy.example.com:3128')
+ ('http', 'joe', 'password', 'proxy.example.com:3128')
+
+ Everything after the authority is ignored:
+
+ >>> _parse_proxy('ftp://joe:password@proxy.example.com/rubbish:3128')
+ ('ftp', 'joe', 'password', 'proxy.example.com')
+
+ Test for no trailing '/' case:
+
+ >>> _parse_proxy('http://joe:password@proxy.example.com')
+ ('http', 'joe', 'password', 'proxy.example.com')
+
+ """
+ scheme, r_scheme = splittype(proxy)
+ if not r_scheme.startswith("/"):
+ # authority
+ scheme = None
+ authority = proxy
+ else:
+ # URL
+ if not r_scheme.startswith("//"):
+ raise ValueError("proxy URL with no authority: %r" % proxy)
+ # We have an authority, so for RFC 3986-compliant URLs (by ss 3.
+ # and 3.3.), path is empty or starts with '/'
+ end = r_scheme.find("/", 2)
+ if end == -1:
+ end = None
+ authority = r_scheme[2:end]
+ userinfo, hostport = splituser(authority)
+ if userinfo is not None:
+ user, password = splitpasswd(userinfo)
+ else:
+ user = password = None
+ return scheme, user, password, hostport
+
+class ProxyHandler(BaseHandler):
+ # Proxies must be in front
+ handler_order = 100
+
+ def __init__(self, proxies=None):
+ if proxies is None:
+ proxies = getproxies()
+ assert hasattr(proxies, 'has_key'), "proxies must be a mapping"
+ self.proxies = proxies
+ for type, url in proxies.items():
+ setattr(self, '%s_open' % type,
+ lambda r, proxy=url, type=type, meth=self.proxy_open: \
+ meth(r, proxy, type))
+
+ def proxy_open(self, req, proxy, type):
+ orig_type = req.get_type()
+ proxy_type, user, password, hostport = _parse_proxy(proxy)
+
+ if proxy_type is None:
+ proxy_type = orig_type
+
+ if req.host and proxy_bypass(req.host):
+ return None
+
+ if user and password:
+ user_pass = '%s:%s' % (unquote(user), unquote(password))
+ creds = base64.b64encode(user_pass).strip()
+ req.add_header('Proxy-authorization', 'Basic ' + creds)
+ hostport = unquote(hostport)
+ req.set_proxy(hostport, proxy_type)
+
+ if orig_type == proxy_type or orig_type == 'https':
+ # let other handlers take care of it
+ return None
+ else:
+ # need to start over, because the other handlers don't
+ # grok the proxy's URL type
+ # e.g. if we have a constructor arg proxies like so:
+ # {'http': 'ftp://proxy.example.com'}, we may end up turning
+ # a request for http://acme.example.com/a into one for
+ # ftp://proxy.example.com/a
+ return self.parent.open(req, timeout=req.timeout)
+
+class HTTPPasswordMgr:
+
+ def __init__(self):
+ self.passwd = {}
+
+ def add_password(self, realm, uri, user, passwd):
+ # uri could be a single URI or a sequence
+ if isinstance(uri, basestring):
+ uri = [uri]
+ if not realm in self.passwd:
+ self.passwd[realm] = {}
+ for default_port in True, False:
+ reduced_uri = tuple(
+ [self.reduce_uri(u, default_port) for u in uri])
+ self.passwd[realm][reduced_uri] = (user, passwd)
+
+ def find_user_password(self, realm, authuri):
+ domains = self.passwd.get(realm, {})
+ for default_port in True, False:
+ reduced_authuri = self.reduce_uri(authuri, default_port)
+ for uris, authinfo in domains.iteritems():
+ for uri in uris:
+ if self.is_suburi(uri, reduced_authuri):
+ return authinfo
+ return None, None
+
+ def reduce_uri(self, uri, default_port=True):
+ """Accept authority or URI and extract only the authority and path."""
+ # note HTTP URLs do not have a userinfo component
+ parts = urlparse.urlsplit(uri)
+ if parts[1]:
+ # URI
+ scheme = parts[0]
+ authority = parts[1]
+ path = parts[2] or '/'
+ else:
+ # host or host:port
+ scheme = None
+ authority = uri
+ path = '/'
+ host, port = splitport(authority)
+ if default_port and port is None and scheme is not None:
+ dport = {"http": 80,
+ "https": 443,
+ }.get(scheme)
+ if dport is not None:
+ authority = "%s:%d" % (host, dport)
+ return authority, path
+
+ def is_suburi(self, base, test):
+ """Check if test is below base in a URI tree
+
+ Both args must be URIs in reduced form.
+ """
+ if base == test:
+ return True
+ if base[0] != test[0]:
+ return False
+ common = posixpath.commonprefix((base[1], test[1]))
+ if len(common) == len(base[1]):
+ return True
+ return False
+
+
+class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
+
+ def find_user_password(self, realm, authuri):
+ user, password = HTTPPasswordMgr.find_user_password(self, realm,
+ authuri)
+ if user is not None:
+ return user, password
+ return HTTPPasswordMgr.find_user_password(self, None, authuri)
+
+
+class AbstractBasicAuthHandler:
+
+ # XXX this allows for multiple auth-schemes, but will stupidly pick
+ # the last one with a realm specified.
+
+ # allow for double- and single-quoted realm values
+ # (single quotes are a violation of the RFC, but appear in the wild)
+ rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
+ 'realm=(["\'])(.*?)\\2', re.I)
+
+ # XXX could pre-emptively send auth info already accepted (RFC 2617,
+ # end of section 2, and section 1.2 immediately after "credentials"
+ # production).
+
+ def __init__(self, password_mgr=None):
+ if password_mgr is None:
+ password_mgr = HTTPPasswordMgr()
+ self.passwd = password_mgr
+ self.add_password = self.passwd.add_password
+ self.retried = 0
+
+ def reset_retry_count(self):
+ self.retried = 0
+
+ def http_error_auth_reqed(self, authreq, host, req, headers):
+ # host may be an authority (without userinfo) or a URL with an
+ # authority
+ # XXX could be multiple headers
+ authreq = headers.get(authreq, None)
+
+ if self.retried > 5:
+ # retry sending the username:password 5 times before failing.
+ raise HTTPError(req.get_full_url(), 401, "basic auth failed",
+ headers, None)
+ else:
+ self.retried += 1
+
+ if authreq:
+ mo = AbstractBasicAuthHandler.rx.search(authreq)
+ if mo:
+ scheme, quote, realm = mo.groups()
+ if scheme.lower() == 'basic':
+ response = self.retry_http_basic_auth(host, req, realm)
+ if response and response.code != 401:
+ self.retried = 0
+ return response
+
+ def retry_http_basic_auth(self, host, req, realm):
+ user, pw = self.passwd.find_user_password(realm, host)
+ if pw is not None:
+ raw = "%s:%s" % (user, pw)
+ auth = 'Basic %s' % base64.b64encode(raw).strip()
+ if req.headers.get(self.auth_header, None) == auth:
+ return None
+ req.add_unredirected_header(self.auth_header, auth)
+ return self.parent.open(req, timeout=req.timeout)
+ else:
+ return None
+
+
+class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
+
+ auth_header = 'Authorization'
+
+ def http_error_401(self, req, fp, code, msg, headers):
+ url = req.get_full_url()
+ response = self.http_error_auth_reqed('www-authenticate',
+ url, req, headers)
+ self.reset_retry_count()
+ return response
+
+
+class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
+
+ auth_header = 'Proxy-authorization'
+
+ def http_error_407(self, req, fp, code, msg, headers):
+ # http_error_auth_reqed requires that there is no userinfo component in
+ # authority. Assume there isn't one, since urllib2 does not (and
+ # should not, RFC 3986 s. 3.2.1) support requests for URLs containing
+ # userinfo.
+ authority = req.get_host()
+ response = self.http_error_auth_reqed('proxy-authenticate',
+ authority, req, headers)
+ self.reset_retry_count()
+ return response
+
+
+def randombytes(n):
+ """Return n random bytes."""
+ # Use /dev/urandom if it is available. Fall back to random module
+ # if not. It might be worthwhile to extend this function to use
+ # other platform-specific mechanisms for getting random bytes.
+ if os.path.exists("/dev/urandom"):
+ f = open("/dev/urandom")
+ s = f.read(n)
+ f.close()
+ return s
+ else:
+ L = [chr(random.randrange(0, 256)) for i in range(n)]
+ return "".join(L)
+
+class AbstractDigestAuthHandler:
+ # Digest authentication is specified in RFC 2617.
+
+ # XXX The client does not inspect the Authentication-Info header
+ # in a successful response.
+
+ # XXX It should be possible to test this implementation against
+ # a mock server that just generates a static set of challenges.
+
+ # XXX qop="auth-int" supports is shaky
+
+ def __init__(self, passwd=None):
+ if passwd is None:
+ passwd = HTTPPasswordMgr()
+ self.passwd = passwd
+ self.add_password = self.passwd.add_password
+ self.retried = 0
+ self.nonce_count = 0
+ self.last_nonce = None
+
+ def reset_retry_count(self):
+ self.retried = 0
+
+ def http_error_auth_reqed(self, auth_header, host, req, headers):
+ authreq = headers.get(auth_header, None)
+ if self.retried > 5:
+ # Don't fail endlessly - if we failed once, we'll probably
+ # fail a second time. Hm. Unless the Password Manager is
+ # prompting for the information. Crap. This isn't great
+ # but it's better than the current 'repeat until recursion
+ # depth exceeded' approach <wink>
+ raise HTTPError(req.get_full_url(), 401, "digest auth failed",
+ headers, None)
+ else:
+ self.retried += 1
+ if authreq:
+ scheme = authreq.split()[0]
+ if scheme.lower() == 'digest':
+ return self.retry_http_digest_auth(req, authreq)
+
+ def retry_http_digest_auth(self, req, auth):
+ token, challenge = auth.split(' ', 1)
+ chal = parse_keqv_list(parse_http_list(challenge))
+ auth = self.get_authorization(req, chal)
+ if auth:
+ auth_val = 'Digest %s' % auth
+ if req.headers.get(self.auth_header, None) == auth_val:
+ return None
+ req.add_unredirected_header(self.auth_header, auth_val)
+ resp = self.parent.open(req, timeout=req.timeout)
+ return resp
+
+ def get_cnonce(self, nonce):
+ # The cnonce-value is an opaque
+ # quoted string value provided by the client and used by both client
+ # and server to avoid chosen plaintext attacks, to provide mutual
+ # authentication, and to provide some message integrity protection.
+ # This isn't a fabulous effort, but it's probably Good Enough.
+ dig = hashlib.sha1("%s:%s:%s:%s" % (self.nonce_count, nonce, time.ctime(),
+ randombytes(8))).hexdigest()
+ return dig[:16]
+
+ def get_authorization(self, req, chal):
+ try:
+ realm = chal['realm']
+ nonce = chal['nonce']
+ qop = chal.get('qop')
+ algorithm = chal.get('algorithm', 'MD5')
+ # mod_digest doesn't send an opaque, even though it isn't
+ # supposed to be optional
+ opaque = chal.get('opaque', None)
+ except KeyError:
+ return None
+
+ H, KD = self.get_algorithm_impls(algorithm)
+ if H is None:
+ return None
+
+ user, pw = self.passwd.find_user_password(realm, req.get_full_url())
+ if user is None:
+ return None
+
+ # XXX not implemented yet
+ if req.has_data():
+ entdig = self.get_entity_digest(req.get_data(), chal)
+ else:
+ entdig = None
+
+ A1 = "%s:%s:%s" % (user, realm, pw)
+ A2 = "%s:%s" % (req.get_method(),
+ # XXX selector: what about proxies and full urls
+ req.get_selector())
+ if qop == 'auth':
+ if nonce == self.last_nonce:
+ self.nonce_count += 1
+ else:
+ self.nonce_count = 1
+ self.last_nonce = nonce
+
+ ncvalue = '%08x' % self.nonce_count
+ cnonce = self.get_cnonce(nonce)
+ noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2))
+ respdig = KD(H(A1), noncebit)
+ elif qop is None:
+ respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
+ else:
+ # XXX handle auth-int.
+ raise URLError("qop '%s' is not supported." % qop)
+
+ # XXX should the partial digests be encoded too?
+
+ base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
+ 'response="%s"' % (user, realm, nonce, req.get_selector(),
+ respdig)
+ if opaque:
+ base += ', opaque="%s"' % opaque
+ if entdig:
+ base += ', digest="%s"' % entdig
+ base += ', algorithm="%s"' % algorithm
+ if qop:
+ base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
+ return base
+
+ def get_algorithm_impls(self, algorithm):
+ # algorithm should be case-insensitive according to RFC2617
+ algorithm = algorithm.upper()
+ # lambdas assume digest modules are imported at the top level
+ if algorithm == 'MD5':
+ H = lambda x: hashlib.md5(x).hexdigest()
+ elif algorithm == 'SHA':
+ H = lambda x: hashlib.sha1(x).hexdigest()
+ # XXX MD5-sess
+ KD = lambda s, d: H("%s:%s" % (s, d))
+ return H, KD
+
+ def get_entity_digest(self, data, chal):
+ # XXX not implemented yet
+ return None
+
+
+class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
+ """An authentication protocol defined by RFC 2069
+
+ Digest authentication improves on basic authentication because it
+ does not transmit passwords in the clear.
+ """
+
+ auth_header = 'Authorization'
+ handler_order = 490 # before Basic auth
+
+ def http_error_401(self, req, fp, code, msg, headers):
+ host = urlparse.urlparse(req.get_full_url())[1]
+ retry = self.http_error_auth_reqed('www-authenticate',
+ host, req, headers)
+ self.reset_retry_count()
+ return retry
+
+
+class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
+
+ auth_header = 'Proxy-Authorization'
+ handler_order = 490 # before Basic auth
+
+ def http_error_407(self, req, fp, code, msg, headers):
+ host = req.get_host()
+ retry = self.http_error_auth_reqed('proxy-authenticate',
+ host, req, headers)
+ self.reset_retry_count()
+ return retry
+
+class AbstractHTTPHandler(BaseHandler):
+
+ def __init__(self, debuglevel=0):
+ self._debuglevel = debuglevel
+
+ def set_http_debuglevel(self, level):
+ self._debuglevel = level
+
+ def do_request_(self, request):
+ host = request.get_host()
+ if not host:
+ raise URLError('no host given')
+
+ if request.has_data(): # POST
+ data = request.get_data()
+ if not request.has_header('Content-type'):
+ request.add_unredirected_header(
+ 'Content-type',
+ 'application/x-www-form-urlencoded')
+ if not request.has_header('Content-length'):
+ request.add_unredirected_header(
+ 'Content-length', '%d' % len(data))
+
+ sel_host = host
+ if request.has_proxy():
+ scheme, sel = splittype(request.get_selector())
+ sel_host, sel_path = splithost(sel)
+
+ if not request.has_header('Host'):
+ request.add_unredirected_header('Host', sel_host)
+ for name, value in self.parent.addheaders:
+ name = name.capitalize()
+ if not request.has_header(name):
+ request.add_unredirected_header(name, value)
+
+ return request
+
+ def do_open(self, http_class, req):
+ """Return an addinfourl object for the request, using http_class.
+
+ http_class must implement the HTTPConnection API from httplib.
+ The addinfourl return value is a file-like object. It also
+ has methods and attributes including:
+ - info(): return a mimetools.Message object for the headers
+ - geturl(): return the original request URL
+ - code: HTTP status code
+ """
+ host = req.get_host()
+ if not host:
+ raise URLError('no host given')
+
+ h = http_class(host, timeout=req.timeout) # will parse host:port
+ h.set_debuglevel(self._debuglevel)
+
+ headers = dict(req.unredirected_hdrs)
+ headers.update(dict((k, v) for k, v in req.headers.items()
+ if k not in headers))
+
+ # We want to make an HTTP/1.1 request, but the addinfourl
+ # class isn't prepared to deal with a persistent connection.
+ # It will try to read all remaining data from the socket,
+ # which will block while the server waits for the next request.
+ # So make sure the connection gets closed after the (only)
+ # request.
+ headers["Connection"] = "close"
+ headers = dict(
+ (name.title(), val) for name, val in headers.items())
+
+ if req._tunnel_host:
+ tunnel_headers = {}
+ proxy_auth_hdr = "Proxy-Authorization"
+ if proxy_auth_hdr in headers:
+ tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr]
+ # Proxy-Authorization should not be sent to origin
+ # server.
+ del headers[proxy_auth_hdr]
+ h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
+
+ try:
+ h.request(req.get_method(), req.get_selector(), req.data, headers)
+ try:
+ r = h.getresponse(buffering=True)
+ except TypeError: #buffering kw not supported
+ r = h.getresponse()
+ except socket.error, err: # XXX what error?
+ h.close()
+ raise URLError(err)
+
+ # Pick apart the HTTPResponse object to get the addinfourl
+ # object initialized properly.
+
+ # Wrap the HTTPResponse object in socket's file object adapter
+ # for Windows. That adapter calls recv(), so delegate recv()
+ # to read(). This weird wrapping allows the returned object to
+ # have readline() and readlines() methods.
+
+ # XXX It might be better to extract the read buffering code
+ # out of socket._fileobject() and into a base class.
+
+ r.recv = r.read
+ fp = socket._fileobject(r, close=True)
+
+ resp = addinfourl(fp, r.msg, req.get_full_url())
+ resp.code = r.status
+ resp.msg = r.reason
+ return resp
+
+
+class HTTPHandler(AbstractHTTPHandler):
+
+ def http_open(self, req):
+ return self.do_open(httplib.HTTPConnection, req)
+
+ http_request = AbstractHTTPHandler.do_request_
+
+if hasattr(httplib, 'HTTPS'):
+ class HTTPSHandler(AbstractHTTPHandler):
+
+ def https_open(self, req):
+ return self.do_open(httplib.HTTPSConnection, req)
+
+ https_request = AbstractHTTPHandler.do_request_
+
+class HTTPCookieProcessor(BaseHandler):
+ def __init__(self, cookiejar=None):
+ import cookielib
+ if cookiejar is None:
+ cookiejar = cookielib.CookieJar()
+ self.cookiejar = cookiejar
+
+ def http_request(self, request):
+ self.cookiejar.add_cookie_header(request)
+ return request
+
+ def http_response(self, request, response):
+ self.cookiejar.extract_cookies(response, request)
+ return response
+
+ https_request = http_request
+ https_response = http_response
+
+class UnknownHandler(BaseHandler):
+ def unknown_open(self, req):
+ type = req.get_type()
+ raise URLError('unknown url type: %s' % type)
+
+def parse_keqv_list(l):
+ """Parse list of key=value strings where keys are not duplicated."""
+ parsed = {}
+ for elt in l:
+ k, v = elt.split('=', 1)
+ if v[0] == '"' and v[-1] == '"':
+ v = v[1:-1]
+ parsed[k] = v
+ return parsed
+
+def parse_http_list(s):
+ """Parse lists as described by RFC 2068 Section 2.
+
+ In particular, parse comma-separated lists where the elements of
+ the list may include quoted-strings. A quoted-string could
+ contain a comma. A non-quoted string could have quotes in the
+ middle. Neither commas nor quotes count if they are escaped.
+ Only double-quotes count, not single-quotes.
+ """
+ res = []
+ part = ''
+
+ escape = quote = False
+ for cur in s:
+ if escape:
+ part += cur
+ escape = False
+ continue
+ if quote:
+ if cur == '\\':
+ escape = True
+ continue
+ elif cur == '"':
+ quote = False
+ part += cur
+ continue
+
+ if cur == ',':
+ res.append(part)
+ part = ''
+ continue
+
+ if cur == '"':
+ quote = True
+
+ part += cur
+
+ # append last part
+ if part:
+ res.append(part)
+
+ return [part.strip() for part in res]
+
+def _safe_gethostbyname(host):
+ try:
+ return socket.gethostbyname(host)
+ except socket.gaierror:
+ return None
+
+class FileHandler(BaseHandler):
+ # Use local file or FTP depending on form of URL
+ def file_open(self, req):
+ url = req.get_selector()
+ if url[:2] == '//' and url[2:3] != '/' and (req.host and
+ req.host != 'localhost'):
+ req.type = 'ftp'
+ return self.parent.open(req)
+ else:
+ return self.open_local_file(req)
+
+ # names for the localhost
+ names = None
+ def get_names(self):
+ if FileHandler.names is None:
+ try:
+ FileHandler.names = tuple(
+ socket.gethostbyname_ex('localhost')[2] +
+ socket.gethostbyname_ex(socket.gethostname())[2])
+ except socket.gaierror:
+ FileHandler.names = (socket.gethostbyname('localhost'),)
+ return FileHandler.names
+
+ # not entirely sure what the rules are here
+ def open_local_file(self, req):
+ import email.utils
+ import mimetypes
+ host = req.get_host()
+ filename = req.get_selector()
+ localfile = url2pathname(filename)
+ try:
+ stats = os.stat(localfile)
+ size = stats.st_size
+ modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
+ mtype = mimetypes.guess_type(filename)[0]
+ headers = mimetools.Message(StringIO(
+ 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
+ (mtype or 'text/plain', size, modified)))
+ if host:
+ host, port = splitport(host)
+ if not host or \
+ (not port and _safe_gethostbyname(host) in self.get_names()):
+ if host:
+ origurl = 'file://' + host + filename
+ else:
+ origurl = 'file://' + filename
+ return addinfourl(open(localfile, 'rb'), headers, origurl)
+ except OSError, msg:
+ # urllib2 users shouldn't expect OSErrors coming from urlopen()
+ raise URLError(msg)
+ raise URLError('file not on local host')
+
+class FTPHandler(BaseHandler):
+ def ftp_open(self, req):
+ import ftplib
+ import mimetypes
+ host = req.get_host()
+ if not host:
+ raise URLError('ftp error: no host given')
+ host, port = splitport(host)
+ if port is None:
+ port = ftplib.FTP_PORT
+ else:
+ port = int(port)
+
+ # username/password handling
+ user, host = splituser(host)
+ if user:
+ user, passwd = splitpasswd(user)
+ else:
+ passwd = None
+ host = unquote(host)
+ user = user or ''
+ passwd = passwd or ''
+
+ try:
+ host = socket.gethostbyname(host)
+ except socket.error, msg:
+ raise URLError(msg)
+ path, attrs = splitattr(req.get_selector())
+ dirs = path.split('/')
+ dirs = map(unquote, dirs)
+ dirs, file = dirs[:-1], dirs[-1]
+ if dirs and not dirs[0]:
+ dirs = dirs[1:]
+ try:
+ fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout)
+ type = file and 'I' or 'D'
+ for attr in attrs:
+ attr, value = splitvalue(attr)
+ if attr.lower() == 'type' and \
+ value in ('a', 'A', 'i', 'I', 'd', 'D'):
+ type = value.upper()
+ fp, retrlen = fw.retrfile(file, type)
+ headers = ""
+ mtype = mimetypes.guess_type(req.get_full_url())[0]
+ if mtype:
+ headers += "Content-type: %s\n" % mtype
+ if retrlen is not None and retrlen >= 0:
+ headers += "Content-length: %d\n" % retrlen
+ sf = StringIO(headers)
+ headers = mimetools.Message(sf)
+ return addinfourl(fp, headers, req.get_full_url())
+ except ftplib.all_errors, msg:
+ raise URLError, ('ftp error: %s' % msg), sys.exc_info()[2]
+
+ def connect_ftp(self, user, passwd, host, port, dirs, timeout):
+ fw = ftpwrapper(user, passwd, host, port, dirs, timeout)
+## fw.ftp.set_debuglevel(1)
+ return fw
+
+class CacheFTPHandler(FTPHandler):
+ # XXX would be nice to have pluggable cache strategies
+ # XXX this stuff is definitely not thread safe
+ def __init__(self):
+ self.cache = {}
+ self.timeout = {}
+ self.soonest = 0
+ self.delay = 60
+ self.max_conns = 16
+
+ def setTimeout(self, t):
+ self.delay = t
+
+ def setMaxConns(self, m):
+ self.max_conns = m
+
+ def connect_ftp(self, user, passwd, host, port, dirs, timeout):
+ key = user, host, port, '/'.join(dirs), timeout
+ if key in self.cache:
+ self.timeout[key] = time.time() + self.delay
+ else:
+ self.cache[key] = ftpwrapper(user, passwd, host, port, dirs, timeout)
+ self.timeout[key] = time.time() + self.delay
+ self.check_cache()
+ return self.cache[key]
+
+ def check_cache(self):
+ # first check for old ones
+ t = time.time()
+ if self.soonest <= t:
+ for k, v in self.timeout.items():
+ if v < t:
+ self.cache[k].close()
+ del self.cache[k]
+ del self.timeout[k]
+ self.soonest = min(self.timeout.values())
+
+ # then check the size
+ if len(self.cache) == self.max_conns:
+ for k, v in self.timeout.items():
+ if v == self.soonest:
+ del self.cache[k]
+ del self.timeout[k]
+ break
+ self.soonest = min(self.timeout.values())
diff --git a/lib_pypy/pyrepl/readline.py b/lib_pypy/pyrepl/readline.py
--- a/lib_pypy/pyrepl/readline.py
+++ b/lib_pypy/pyrepl/readline.py
@@ -395,9 +395,21 @@
_wrapper.f_in = f_in
_wrapper.f_out = f_out
- if hasattr(sys, '__raw_input__'): # PyPy
- _old_raw_input = sys.__raw_input__
+ if '__pypy__' in sys.builtin_module_names: # PyPy
+
+ def _old_raw_input(prompt=''):
+ # sys.__raw_input__() is only called when stdin and stdout are
+ # as expected and are ttys. If it is the case, then get_reader()
+ # should not really fail in _wrapper.raw_input(). If it still
+ # does, then we will just cancel the redirection and call again
+ # the built-in raw_input().
+ try:
+ del sys.__raw_input__
+ except AttributeError:
+ pass
+ return raw_input(prompt)
sys.__raw_input__ = _wrapper.raw_input
+
else:
# this is not really what readline.c does. Better than nothing I guess
import __builtin__
diff --git a/lib_pypy/resource.py b/lib_pypy/resource.py
--- a/lib_pypy/resource.py
+++ b/lib_pypy/resource.py
@@ -7,7 +7,7 @@
from ctypes_support import standard_c_lib as libc
from ctypes_support import get_errno
-from ctypes import Structure, c_int, c_long, byref, sizeof, POINTER
+from ctypes import Structure, c_int, c_long, byref, POINTER
from errno import EINVAL, EPERM
import _structseq
@@ -165,7 +165,6 @@
@builtinify
def getpagesize():
- pagesize = 0
if _getpagesize:
return _getpagesize()
else:
diff --git a/pypy/annotation/classdef.py b/pypy/annotation/classdef.py
--- a/pypy/annotation/classdef.py
+++ b/pypy/annotation/classdef.py
@@ -276,8 +276,8 @@
# create the Attribute and do the generalization asked for
newattr = Attribute(attr, self.bookkeeper)
if s_value:
- if newattr.name == 'intval' and getattr(s_value, 'unsigned', False):
- import pdb; pdb.set_trace()
+ #if newattr.name == 'intval' and getattr(s_value, 'unsigned', False):
+ # import pdb; pdb.set_trace()
newattr.s_value = s_value
# keep all subattributes' values
diff --git a/pypy/annotation/policy.py b/pypy/annotation/policy.py
--- a/pypy/annotation/policy.py
+++ b/pypy/annotation/policy.py
@@ -1,6 +1,6 @@
# base annotation policy for specialization
from pypy.annotation.specialize import default_specialize as default
-from pypy.annotation.specialize import specialize_argvalue, specialize_argtype, specialize_arglistitemtype
+from pypy.annotation.specialize import specialize_argvalue, specialize_argtype, specialize_arglistitemtype, specialize_arg_or_var
from pypy.annotation.specialize import memo, specialize_call_location
# for some reason, model must be imported first,
# or we create a cycle.
@@ -73,6 +73,7 @@
default_specialize = staticmethod(default)
specialize__memo = staticmethod(memo)
specialize__arg = staticmethod(specialize_argvalue) # specialize:arg(N)
+ specialize__arg_or_var = staticmethod(specialize_arg_or_var)
specialize__argtype = staticmethod(specialize_argtype) # specialize:argtype(N)
specialize__arglistitemtype = staticmethod(specialize_arglistitemtype)
specialize__call_location = staticmethod(specialize_call_location)
diff --git a/pypy/annotation/specialize.py b/pypy/annotation/specialize.py
--- a/pypy/annotation/specialize.py
+++ b/pypy/annotation/specialize.py
@@ -353,6 +353,16 @@
key = tuple(key)
return maybe_star_args(funcdesc, key, args_s)
+def specialize_arg_or_var(funcdesc, args_s, *argindices):
+ for argno in argindices:
+ if not args_s[argno].is_constant():
+ break
+ else:
+ # all constant
+ return specialize_argvalue(funcdesc, args_s, *argindices)
+ # some not constant
+ return maybe_star_args(funcdesc, None, args_s)
+
def specialize_argtype(funcdesc, args_s, *argindices):
key = tuple([args_s[i].knowntype for i in argindices])
for cls in key:
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
@@ -1194,6 +1194,20 @@
assert len(executedesc._cache[(0, 'star', 2)].startblock.inputargs) == 4
assert len(executedesc._cache[(1, 'star', 3)].startblock.inputargs) == 5
+ def test_specialize_arg_or_var(self):
+ def f(a):
+ return 1
+ f._annspecialcase_ = 'specialize:arg_or_var(0)'
+
+ def fn(a):
+ return f(3) + f(a)
+
+ a = self.RPythonAnnotator()
+ a.build_types(fn, [int])
+ executedesc = a.bookkeeper.getdesc(f)
+ assert sorted(executedesc._cache.keys()) == [None, (3,)]
+ # we got two different special
+
def test_specialize_call_location(self):
def g(a):
return a
@@ -3190,6 +3204,8 @@
s = a.build_types(f, [])
assert isinstance(s, annmodel.SomeList)
assert not s.listdef.listitem.resized
+ assert not s.listdef.listitem.immutable
+ assert s.listdef.listitem.mutated
def test_delslice(self):
def f():
diff --git a/pypy/annotation/unaryop.py b/pypy/annotation/unaryop.py
--- a/pypy/annotation/unaryop.py
+++ b/pypy/annotation/unaryop.py
@@ -352,6 +352,7 @@
check_negative_slice(s_start, s_stop)
if not isinstance(s_iterable, SomeList):
raise Exception("list[start:stop] = x: x must be a list")
+ lst.listdef.mutate()
lst.listdef.agree(s_iterable.listdef)
# note that setslice is not allowed to resize a list in RPython
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -72,6 +72,7 @@
del working_modules['fcntl'] # LOCK_NB not defined
del working_modules["_minimal_curses"]
del working_modules["termios"]
+ del working_modules["_multiprocessing"] # depends on rctime
@@ -127,7 +128,7 @@
pypy_optiondescription = OptionDescription("objspace", "Object Space Options", [
ChoiceOption("name", "Object Space name",
- ["std", "flow", "thunk", "dump", "taint"],
+ ["std", "flow", "thunk", "dump"],
"std",
cmdline='--objspace -o'),
diff --git a/pypy/doc/__pypy__-module.rst b/pypy/doc/__pypy__-module.rst
--- a/pypy/doc/__pypy__-module.rst
+++ b/pypy/doc/__pypy__-module.rst
@@ -37,29 +37,6 @@
.. _`thunk object space docs`: objspace-proxies.html#thunk
.. _`interface section of the thunk object space docs`: objspace-proxies.html#thunk-interface
-.. broken:
-
- Taint Object Space Functionality
- ================================
-
- When the taint object space is used (choose with :config:`objspace.name`),
- the following names are put into ``__pypy__``:
-
- - ``taint``
- - ``is_tainted``
- - ``untaint``
- - ``taint_atomic``
- - ``_taint_debug``
- - ``_taint_look``
- - ``TaintError``
-
- Those are all described in the `interface section of the taint object space
- docs`_.
-
- For more detailed explanations and examples see the `taint object space docs`_.
-
- .. _`taint object space docs`: objspace-proxies.html#taint
- .. _`interface section of the taint object space docs`: objspace-proxies.html#taint-interface
Transparent Proxy Functionality
===============================
diff --git a/pypy/doc/config/objspace.name.txt b/pypy/doc/config/objspace.name.txt
--- a/pypy/doc/config/objspace.name.txt
+++ b/pypy/doc/config/objspace.name.txt
@@ -4,7 +4,6 @@
for normal usage):
* thunk_: The thunk object space adds lazy evaluation to PyPy.
- * taint_: The taint object space adds soft security features.
* dump_: Using this object spaces results in the dumpimp of all operations
to a log.
@@ -12,5 +11,4 @@
.. _`Object Space Proxies`: ../objspace-proxies.html
.. _`Standard Object Space`: ../objspace.html#standard-object-space
.. _thunk: ../objspace-proxies.html#thunk
-.. _taint: ../objspace-proxies.html#taint
.. _dump: ../objspace-proxies.html#dump
diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst
--- a/pypy/doc/index.rst
+++ b/pypy/doc/index.rst
@@ -309,7 +309,6 @@
.. _`object space`: objspace.html
.. _FlowObjSpace: objspace.html#the-flow-object-space
.. _`trace object space`: objspace.html#the-trace-object-space
-.. _`taint object space`: objspace-proxies.html#taint
.. _`thunk object space`: objspace-proxies.html#thunk
.. _`transparent proxies`: objspace-proxies.html#tproxy
.. _`Differences between PyPy and CPython`: cpython_differences.html
diff --git a/pypy/doc/objspace-proxies.rst b/pypy/doc/objspace-proxies.rst
--- a/pypy/doc/objspace-proxies.rst
+++ b/pypy/doc/objspace-proxies.rst
@@ -129,297 +129,6 @@
function behaves lazily: all calls to it return a thunk object.
-.. broken right now:
-
- .. _taint:
-
- The Taint Object Space
- ======================
-
- Motivation
- ----------
-
- The Taint Object Space provides a form of security: "tainted objects",
- inspired by various sources, see [D12.1]_ for a more detailed discussion.
-
- The basic idea of this kind of security is not to protect against
- malicious code but to help with handling and boxing sensitive data.
- It covers two kinds of sensitive data: secret data which should not leak,
- and untrusted data coming from an external source and that must be
- validated before it is used.
-
- The idea is that, considering a large application that handles these
- kinds of sensitive data, there are typically only a small number of
- places that need to explicitly manipulate that sensitive data; all the
- other places merely pass it around, or do entirely unrelated things.
-
- Nevertheless, if a large application needs to be reviewed for security,
- it must be entirely carefully checked, because it is possible that a
- bug at some apparently unrelated place could lead to a leak of sensitive
- information in a way that an external attacker could exploit. For
- example, if any part of the application provides web services, an
- attacker might be able to issue unexpected requests with a regular web
- browser and deduce secret information from the details of the answers he
- gets. Another example is the common CGI attack where an attacker sends
- malformed inputs and causes the CGI script to do unintended things.
-
- An approach like that of the Taint Object Space allows the small parts
- of the program that manipulate sensitive data to be explicitly marked.
- The effect of this is that although these small parts still need a
- careful security review, the rest of the application no longer does,
- because even a bug would be unable to leak the information.
-
- We have implemented a simple two-level model: objects are either
- regular (untainted), or sensitive (tainted). Objects are marked as
- sensitive if they are secret or untrusted, and only declassified at
- carefully-checked positions (e.g. where the secret data is needed, or
- after the untrusted data has been fully validated).
-
- It would be simple to extend the code for more fine-grained scales of
- secrecy. For example it is typical in the literature to consider
- user-specified lattices of secrecy levels, corresponding to multiple
- "owners" that cannot access data belonging to another "owner" unless
- explicitly authorized to do so.
-
- Tainting and untainting
- -----------------------
-
- Start a py.py with the Taint Object Space and try the following example::
-
- $ py.py -o taint
- >>>> from __pypy__ import taint
- >>>> x = taint(6)
-
- # x is hidden from now on. We can pass it around and
- # even operate on it, but not inspect it. Taintness
- # is propagated to operation results.
-
- >>>> x
- TaintError
-
- >>>> if x > 5: y = 2 # see below
- TaintError
-
- >>>> y = x + 5 # ok
- >>>> lst = [x, y]
- >>>> z = lst.pop()
- >>>> t = type(z) # type() works too, tainted answer
- >>>> t
- TaintError
- >>>> u = t is int # even 'is' works
- >>>> u
- TaintError
-
- Notice that using a tainted boolean like ``x > 5`` in an ``if``
- statement is forbidden. This is because knowing which path is followed
- would give away a hint about ``x``; in the example above, if the
- statement ``if x > 5: y = 2`` was allowed to run, we would know
- something about the value of ``x`` by looking at the (untainted) value
- in the variable ``y``.
-
- Of course, there is a way to inspect tainted objects. The basic way is
- to explicitly "declassify" it with the ``untaint()`` function. In an
- application, the places that use ``untaint()`` are the places that need
- careful security review. To avoid unexpected objects showing up, the
- ``untaint()`` function must be called with the exact type of the object
- to declassify. It will raise ``TaintError`` if the type doesn't match::
-
- >>>> from __pypy__ import taint
- >>>> untaint(int, x)
- 6
- >>>> untaint(int, z)
- 11
- >>>> untaint(bool, x > 5)
- True
- >>>> untaint(int, x > 5)
- TaintError
-
-
- Taint Bombs
- -----------
-
- In this area, a common problem is what to do about failing operations.
- If an operation raises an exception when manipulating a tainted object,
- then the very presence of the exception can leak information about the
- tainted object itself. Consider::
-
- >>>> 5 / (x-6)
-
- By checking if this raises ``ZeroDivisionError`` or not, we would know
- if ``x`` was equal to 6 or not. The solution to this problem in the
- Taint Object Space is to introduce *Taint Bombs*. They are a kind of
- tainted object that doesn't contain a real object, but a pending
- exception. Taint Bombs are indistinguishable from normal tainted
- objects to unprivileged code. See::
-
- >>>> x = taint(6)
- >>>> i = 5 / (x-6) # no exception here
- >>>> j = i + 1 # nor here
- >>>> k = j + 5 # nor here
- >>>> untaint(int, k)
- TaintError
-
- In the above example, all of ``i``, ``j`` and ``k`` contain a Taint
- Bomb. Trying to untaint it raises an exception - a generic
- ``TaintError``. What we win is that the exception gives little away,
- and most importantly it occurs at the point where ``untaint()`` is
- called, not where the operation failed. This means that all calls to
- ``untaint()`` - but not the rest of the code - must be carefully
- reviewed for what occurs if they receive a Taint Bomb; they might catch
- the ``TaintError`` and give the user a generic message that something
- went wrong, if we are reasonably careful that the message or even its
- presence doesn't give information away. This might be a
- problem by itself, but there is no satisfying general solution here:
- it must be considered on a case-by-case basis. Again, what the
- Taint Object Space approach achieves is not solving these problems, but
- localizing them to well-defined small parts of the application - namely,
- around calls to ``untaint()``.
-
- The ``TaintError`` exception deliberately does not include any
- useful error messages, because they might give information away.
- Of course, this makes debugging quite a bit harder; a difficult
- problem to solve properly. So far we have implemented a way to peek in a Taint
- Box or Bomb, ``__pypy__._taint_look(x)``, and a "debug mode" that
- prints the exception as soon as a Bomb is created - both write
- information to the low-level stderr of the application, where we hope
- that it is unlikely to be seen by anyone but the application
- developer.
-
-
- Taint Atomic functions
- ----------------------
-
- Occasionally, a more complicated computation must be performed on a
- tainted object. This requires first untainting the object, performing the
- computations, and then carefully tainting the result again (including
- hiding all exceptions into Bombs).
-
- There is a built-in decorator that does this for you::
-
- >>>> @__pypy__.taint_atomic
- >>>> def myop(x, y):
- .... while x > 0:
- .... x -= y
- .... return x
- ....
- >>>> myop(42, 10)
- -8
- >>>> z = myop(taint(42), 10)
- >>>> z
- TaintError
- >>>> untaint(int, z)
- -8
-
- The decorator makes a whole function behave like a built-in operation.
- If no tainted argument is passed in, the function behaves normally. But
- if any of the arguments is tainted, it is automatically untainted - so
- the function body always sees untainted arguments - and the eventual
- result is tainted again (possibly in a Taint Bomb).
-
- It is important for the function marked as ``taint_atomic`` to have no
- visible side effects, as these could cause information leakage.
- This is currently not enforced, which means that all ``taint_atomic``
- functions have to be carefully reviewed for security (but not the
- callers of ``taint_atomic`` functions).
-
- A possible future extension would be to forbid side-effects on
- non-tainted objects from all ``taint_atomic`` functions.
-
- An example of usage: given a tainted object ``passwords_db`` that
- references a database of passwords, we can write a function
- that checks if a password is valid as follows::
-
- @taint_atomic
- def validate(passwords_db, username, password):
- assert type(passwords_db) is PasswordDatabase
- assert type(username) is str
- assert type(password) is str
- ...load username entry from passwords_db...
- return expected_password == password
-
- It returns a tainted boolean answer, or a Taint Bomb if something
- went wrong. A caller can do::
-
- ok = validate(passwords_db, 'john', '1234')
- ok = untaint(bool, ok)
-
- This can give three outcomes: ``True``, ``False``, or a ``TaintError``
- exception (with no information on it) if anything went wrong. If even
- this is considered giving too much information away, the ``False`` case
- can be made indistinguishable from the ``TaintError`` case (simply by
- raising an exception in ``validate()`` if the password is wrong).
-
- In the above example, the security results achieved are the following:
- as long as ``validate()`` does not leak information, no other part of
- the code can obtain more information about a passwords database than a
- Yes/No answer to a precise query.
-
- A possible extension of the ``taint_atomic`` decorator would be to check
- the argument types, as ``untaint()`` does, for the same reason: to
- prevent bugs where a function like ``validate()`` above is accidentally
- called with the wrong kind of tainted object, which would make it
- misbehave. For now, all ``taint_atomic`` functions should be
- conservative and carefully check all assumptions on their input
- arguments.
-
-
- .. _`taint-interface`:
-
- Interface
- ---------
-
- .. _`like a built-in operation`:
-
- The basic rule of the Tainted Object Space is that it introduces two new
- kinds of objects, Tainted Boxes and Tainted Bombs (which are not types
- in the Python sense). Each box internally contains a regular object;
- each bomb internally contains an exception object. An operation
- involving Tainted Boxes is performed on the objects contained in the
- boxes, and gives a Tainted Box or a Tainted Bomb as a result (such an
- operation does not let an exception be raised). An operation called
- with a Tainted Bomb argument immediately returns the same Tainted Bomb.
-
- In a PyPy running with (or translated with) the Taint Object Space,
- the ``__pypy__`` module exposes the following interface:
-
- * ``taint(obj)``
-
- Return a new Tainted Box wrapping ``obj``. Return ``obj`` itself
- if it is already tainted (a Box or a Bomb).
-
- * ``is_tainted(obj)``
-
- Check if ``obj`` is tainted (a Box or a Bomb).
-
- * ``untaint(type, obj)``
-
- Untaints ``obj`` if it is tainted. Raise ``TaintError`` if the type
- of the untainted object is not exactly ``type``, or if ``obj`` is a
- Bomb.
-
- * ``taint_atomic(func)``
-
- Return a wrapper function around the callable ``func``. The wrapper
- behaves `like a built-in operation`_ with respect to untainting the
- arguments, tainting the result, and returning a Bomb.
-
- * ``TaintError``
-
- Exception. On purpose, it provides no attribute or error message.
-
- * ``_taint_debug(level)``
-
- Set the debugging level to ``level`` (0=off). At level 1 or above,
- all Taint Bombs print a diagnostic message to stderr when they are
- created.
-
- * ``_taint_look(obj)``
-
- For debugging purposes: prints (to stderr) the type and address of
- the object in a Tainted Box, or prints the exception if ``obj`` is
- a Taint Bomb.
-
-
.. _dump:
The Dump Object Space
diff --git a/pypy/doc/project-ideas.rst b/pypy/doc/project-ideas.rst
--- a/pypy/doc/project-ideas.rst
+++ b/pypy/doc/project-ideas.rst
@@ -53,6 +53,18 @@
this is an ideal task to get started, because it does not require any deep
knowledge of the internals.
+Optimized Unicode Representation
+--------------------------------
+
+CPython 3.3 will use an `optimized unicode representation`_ which switches between
+different ways to represent a unicode string, depending on whether the string
+fits into ASCII, has only two-byte characters or needs four-byte characters.
+
+The actual details would be rather differen in PyPy, but we would like to have
+the same optimization implemented.
+
+.. _`optimized unicode representation`: http://www.python.org/dev/peps/pep-0393/
+
Translation Toolchain
---------------------
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -3,12 +3,12 @@
from pypy.interpreter.executioncontext import ExecutionContext, ActionFlag
from pypy.interpreter.executioncontext import UserDelAction, FrameTraceAction
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.error import new_exception_class
+from pypy.interpreter.error import new_exception_class, typed_unwrap_error_msg
from pypy.interpreter.argument import Arguments
from pypy.interpreter.miscutils import ThreadLocals
from pypy.tool.cache import Cache
from pypy.tool.uid import HUGEVAL_BYTES
-from pypy.rlib.objectmodel import we_are_translated, newlist
+from pypy.rlib.objectmodel import we_are_translated, newlist, compute_unique_id
from pypy.rlib.debug import make_sure_not_resized
from pypy.rlib.timer import DummyTimer, Timer
from pypy.rlib.rarithmetic import r_uint
@@ -186,6 +186,28 @@
def _set_mapdict_storage_and_map(self, storage, map):
raise NotImplementedError
+ # -------------------------------------------------------------------
+
+ def str_w(self, space):
+ w_msg = typed_unwrap_error_msg(space, "string", self)
+ raise OperationError(space.w_TypeError, w_msg)
+
+ def unicode_w(self, space):
+ raise OperationError(space.w_TypeError,
+ typed_unwrap_error_msg(space, "unicode", self))
+
+ def int_w(self, space):
+ raise OperationError(space.w_TypeError,
+ typed_unwrap_error_msg(space, "integer", self))
+
+ def uint_w(self, space):
+ raise OperationError(space.w_TypeError,
+ typed_unwrap_error_msg(space, "integer", self))
+
+ def bigint_w(self, space):
+ raise OperationError(space.w_TypeError,
+ typed_unwrap_error_msg(space, "integer", self))
+
class Wrappable(W_Root):
"""A subclass of Wrappable is an internal, interpreter-level class
@@ -901,7 +923,7 @@
ec.c_call_trace(frame, w_func, args)
try:
w_res = self.call_args(w_func, args)
- except OperationError, e:
+ except OperationError:
ec.c_exception_trace(frame, w_func)
raise
ec.c_return_trace(frame, w_func, args)
@@ -947,6 +969,9 @@
def isinstance_w(self, w_obj, w_type):
return self.is_true(self.isinstance(w_obj, w_type))
+ def id(self, w_obj):
+ return self.wrap(compute_unique_id(w_obj))
+
# The code below only works
# for the simple case (new-style instance).
# These methods are patched with the full logic by the __builtin__
@@ -999,8 +1024,6 @@
def eval(self, expression, w_globals, w_locals, hidden_applevel=False):
"NOT_RPYTHON: For internal debugging."
- import types
- from pypy.interpreter.pycode import PyCode
if isinstance(expression, str):
compiler = self.createcompiler()
expression = compiler.compile(expression, '?', 'eval', 0,
@@ -1012,7 +1035,6 @@
def exec_(self, statement, w_globals, w_locals, hidden_applevel=False,
filename=None):
"NOT_RPYTHON: For internal debugging."
- import types
if filename is None:
filename = '?'
from pypy.interpreter.pycode import PyCode
@@ -1210,6 +1232,18 @@
return None
return self.str_w(w_obj)
+ def str_w(self, w_obj):
+ return w_obj.str_w(self)
+
+ def int_w(self, w_obj):
+ return w_obj.int_w(self)
+
+ def uint_w(self, w_obj):
+ return w_obj.uint_w(self)
+
+ def bigint_w(self, w_obj):
+ return w_obj.bigint_w(self)
+
def realstr_w(self, w_obj):
# Like str_w, but only works if w_obj is really of type 'str'.
if not self.is_true(self.isinstance(w_obj, self.w_str)):
@@ -1217,6 +1251,9 @@
self.wrap('argument must be a string'))
return self.str_w(w_obj)
+ def unicode_w(self, w_obj):
+ return w_obj.unicode_w(self)
+
def realunicode_w(self, w_obj):
# Like unicode_w, but only works if w_obj is really of type
# 'unicode'.
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -458,3 +458,7 @@
if module:
space.setattr(w_exc, space.wrap("__module__"), space.wrap(module))
return w_exc
+
+def typed_unwrap_error_msg(space, expected, w_obj):
+ type_name = space.type(w_obj).getname(space)
+ return space.wrap("expected %s, got %s object" % (expected, type_name))
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -175,6 +175,9 @@
self.w_tracefunc = w_func
self.space.frame_trace_action.fire()
+ def gettrace(self):
+ return self.w_tracefunc
+
def setprofile(self, w_func):
"""Set the global trace function."""
if self.space.is_w(w_func, self.space.w_None):
@@ -307,7 +310,11 @@
self._nonperiodic_actions = []
self.has_bytecode_counter = False
self.fired_actions = None
- self.checkinterval_scaled = 100 * TICK_COUNTER_STEP
+ # the default value is not 100, unlike CPython 2.7, but a much
+ # larger value, because we use a technique that not only allows
+ # but actually *forces* another thread to run whenever the counter
+ # reaches zero.
+ self.checkinterval_scaled = 10000 * TICK_COUNTER_STEP
self._rebuild_action_dispatcher()
def fire(self, action):
@@ -346,6 +353,7 @@
elif interval > MAX:
interval = MAX
self.checkinterval_scaled = interval * TICK_COUNTER_STEP
+ self.reset_ticker(-1)
def _rebuild_action_dispatcher(self):
periodic_actions = unrolling_iterable(self._periodic_actions)
@@ -383,8 +391,11 @@
def decrement_ticker(self, by):
value = self._ticker
if self.has_bytecode_counter: # this 'if' is constant-folded
- value -= by
- self._ticker = value
+ if jit.isconstant(by) and by == 0:
+ pass # normally constant-folded too
+ else:
+ value -= by
+ self._ticker = value
return value
diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py
--- a/pypy/interpreter/pyparser/pytokenizer.py
+++ b/pypy/interpreter/pyparser/pytokenizer.py
@@ -226,7 +226,7 @@
parenlev = parenlev - 1
if parenlev < 0:
raise TokenError("unmatched '%s'" % initial, line,
- lnum-1, 0, token_list)
+ lnum, start + 1, token_list)
if token in python_opmap:
punct = python_opmap[token]
else:
diff --git a/pypy/interpreter/pyparser/test/test_pyparse.py b/pypy/interpreter/pyparser/test/test_pyparse.py
--- a/pypy/interpreter/pyparser/test/test_pyparse.py
+++ b/pypy/interpreter/pyparser/test/test_pyparse.py
@@ -87,6 +87,10 @@
assert exc.lineno == 1
assert exc.offset == 5
assert exc.lastlineno == 5
+ exc = py.test.raises(SyntaxError, parse, "abc)").value
+ assert exc.msg == "unmatched ')'"
+ assert exc.lineno == 1
+ assert exc.offset == 4
def test_is(self):
self.parse("x is y")
diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py
--- a/pypy/interpreter/test/test_executioncontext.py
+++ b/pypy/interpreter/test/test_executioncontext.py
@@ -42,6 +42,7 @@
assert i == 9
def test_periodic_action(self):
+ from pypy.interpreter.executioncontext import ActionFlag
class DemoAction(executioncontext.PeriodicAsyncAction):
counter = 0
@@ -53,17 +54,20 @@
space = self.space
a2 = DemoAction(space)
- space.actionflag.register_periodic_action(a2, True)
try:
- for i in range(500):
- space.appexec([], """():
- n = 5
- return n + 2
- """)
- except Finished:
- pass
- checkinterval = space.actionflag.getcheckinterval()
- assert checkinterval / 10 < i < checkinterval * 1.1
+ space.actionflag.setcheckinterval(100)
+ space.actionflag.register_periodic_action(a2, True)
+ try:
+ for i in range(500):
+ space.appexec([], """():
+ n = 5
+ return n + 2
+ """)
+ except Finished:
+ pass
+ finally:
+ space.actionflag = ActionFlag() # reset to default
+ assert 10 < i < 110
def test_llprofile(self):
l = []
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
@@ -165,6 +165,7 @@
'unicodegetitem' : (('ref', 'int'), 'int'),
'unicodesetitem' : (('ref', 'int', 'int'), 'int'),
'cast_ptr_to_int' : (('ref',), 'int'),
+ 'cast_int_to_ptr' : (('int',), 'ref'),
'debug_merge_point': (('ref', 'int'), None),
'force_token' : ((), 'int'),
'call_may_force' : (('int', 'varargs'), 'intorptr'),
@@ -875,9 +876,6 @@
def op_new_array(self, arraydescr, count):
return do_new_array(arraydescr.ofs, count)
- def op_cast_ptr_to_int(self, descr, ptr):
- return cast_to_int(ptr)
-
def op_force_token(self, descr):
opaque_frame = _to_opaque(self)
return llmemory.cast_ptr_to_adr(opaque_frame)
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -45,6 +45,14 @@
def freeing_block(self, start, stop):
pass
+ def record_constptrs(self, op, gcrefs_output_list):
+ for i in range(op.numargs()):
+ v = op.getarg(i)
+ if isinstance(v, ConstPtr) and bool(v.value):
+ p = v.value
+ rgc._make_sure_does_not_move(p)
+ gcrefs_output_list.append(p)
+
# ____________________________________________________________
class GcLLDescr_boehm(GcLLDescription):
@@ -141,6 +149,14 @@
get_funcptr_for_newstr = None
get_funcptr_for_newunicode = None
+ def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
+ # record all GCREFs too, because Boehm cannot see them and keep them
+ # alive if they end up as constants in the assembler
+ for op in operations:
+ self.record_constptrs(op, gcrefs_output_list)
+ return GcLLDescription.rewrite_assembler(self, cpu, operations,
+ gcrefs_output_list)
+
# ____________________________________________________________
# All code below is for the hybrid or minimark GC
@@ -757,14 +773,6 @@
funcptr(llmemory.cast_ptr_to_adr(gcref_struct),
llmemory.cast_ptr_to_adr(gcref_newptr))
- def record_constptrs(self, op, gcrefs_output_list):
- for i in range(op.numargs()):
- v = op.getarg(i)
- if isinstance(v, ConstPtr) and bool(v.value):
- p = v.value
- rgc._make_sure_does_not_move(p)
- gcrefs_output_list.append(p)
-
def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
# Perform two kinds of rewrites in parallel:
#
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -1468,20 +1468,16 @@
return u''.join(u.chars)
- def test_casts(self):
- py.test.skip("xxx fix or kill")
- from pypy.rpython.lltypesystem import lltype, llmemory
- TP = lltype.GcStruct('x')
- x = lltype.malloc(TP)
- x = lltype.cast_opaque_ptr(llmemory.GCREF, x)
+ def test_cast_int_to_ptr(self):
+ res = self.execute_operation(rop.CAST_INT_TO_PTR,
+ [BoxInt(-17)], 'ref').value
+ assert lltype.cast_ptr_to_int(res) == -17
+
+ def test_cast_ptr_to_int(self):
+ x = lltype.cast_int_to_ptr(llmemory.GCREF, -19)
res = self.execute_operation(rop.CAST_PTR_TO_INT,
- [BoxPtr(x)], 'int').value
- expected = self.cpu.cast_adr_to_int(llmemory.cast_ptr_to_adr(x))
- assert rffi.get_real_int(res) == rffi.get_real_int(expected)
- res = self.execute_operation(rop.CAST_PTR_TO_INT,
- [ConstPtr(x)], 'int').value
- expected = self.cpu.cast_adr_to_int(llmemory.cast_ptr_to_adr(x))
- assert rffi.get_real_int(res) == rffi.get_real_int(expected)
+ [BoxPtr(x)], 'int').value
+ assert res == -19
def test_ooops_non_gc(self):
x = lltype.malloc(lltype.Struct('x'), flavor='raw')
@@ -2299,13 +2295,6 @@
#
cpu.bh_strsetitem(x, 4, ord('/'))
assert str.chars[4] == '/'
- #
-## x = cpu.bh_newstr(5)
-## y = cpu.bh_cast_ptr_to_int(x)
-## z = cpu.bh_cast_ptr_to_int(x)
-## y = rffi.get_real_int(y)
-## z = rffi.get_real_int(z)
-## assert type(y) == type(z) == int and y == z
def test_sorting_of_fields(self):
S = self.S
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -1387,7 +1387,8 @@
def genop_same_as(self, op, arglocs, resloc):
self.mov(arglocs[0], resloc)
- #genop_cast_ptr_to_int = genop_same_as
+ genop_cast_ptr_to_int = genop_same_as
+ genop_cast_int_to_ptr = genop_same_as
def genop_int_mod(self, op, arglocs, resloc):
if IS_X86_32:
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
@@ -1152,7 +1152,8 @@
self.possibly_free_var(op.getarg(0))
resloc = self.force_allocate_reg(op.result)
self.Perform(op, [argloc], resloc)
- #consider_cast_ptr_to_int = consider_same_as
+ consider_cast_ptr_to_int = consider_same_as
+ consider_cast_int_to_ptr = consider_same_as
def consider_strlen(self, op):
args = op.getarglist()
diff --git a/pypy/jit/backend/x86/tool/viewcode.py b/pypy/jit/backend/x86/tool/viewcode.py
--- a/pypy/jit/backend/x86/tool/viewcode.py
+++ b/pypy/jit/backend/x86/tool/viewcode.py
@@ -9,7 +9,12 @@
"""
import autopath
-import operator, sys, os, re, py, new
+import new
+import operator
+import py
+import re
+import sys
+import subprocess
from bisect import bisect_left
# don't use pypy.tool.udir here to avoid removing old usessions which
@@ -44,14 +49,16 @@
f = open(tmpfile, 'wb')
f.write(data)
f.close()
- g = os.popen(objdump % {
+ p = subprocess.Popen(objdump % {
'file': tmpfile,
'origin': originaddr,
'backend': objdump_backend_option[backend_name],
- }, 'r')
- result = g.readlines()
- g.close()
- lines = result[6:] # drop some objdump cruft
+ }, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ assert not p.returncode, ('Encountered an error running objdump: %s' %
+ stderr)
+ # drop some objdump cruft
+ lines = stdout.splitlines()[6:]
return format_code_dump_with_labels(originaddr, lines, label_list)
def format_code_dump_with_labels(originaddr, lines, label_list):
@@ -85,8 +92,12 @@
#
print 'loading symbols from %s...' % (filename,)
symbols = {}
- g = os.popen(symbollister % filename, "r")
- for line in g:
+ p = subprocess.Popen(symbollister % filename, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ assert not p.returncode, ('Encountered an error running nm: %s' %
+ stderr)
+ for line in stdout.splitlines():
match = re_symbolentry.match(line)
if match:
addr = long(match.group(1), 16)
@@ -94,7 +105,6 @@
if name.startswith('pypy_g_'):
name = '\xb7' + name[7:]
symbols[addr] = name
- g.close()
print '%d symbols found' % (len(symbols),)
return symbols
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -74,6 +74,7 @@
OS_LLONG_UGE = 91
OS_LLONG_URSHIFT = 92
OS_LLONG_FROM_UINT = 93
+ OS_LLONG_U_TO_FLOAT = 94
#
OS_MATH_SQRT = 100
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -440,6 +440,7 @@
rewrite_op_ullong_mod_zer = _do_builtin_call
rewrite_op_gc_identityhash = _do_builtin_call
rewrite_op_gc_id = _do_builtin_call
+ rewrite_op_uint_mod = _do_builtin_call
# ----------
# getfield/setfield/mallocs etc.
@@ -454,6 +455,23 @@
# the special return value None forces op.result to be considered
# equal to op.args[0]
return [op0, op1, None]
+ if (hints.get('promote_string') and
+ op.args[0].concretetype is not lltype.Void):
+ S = lltype.Ptr(rstr.STR)
+ assert op.args[0].concretetype == S
+ self._register_extra_helper(EffectInfo.OS_STREQ_NONNULL,
+ "str.eq_nonnull",
+ [S, S],
+ lltype.Signed,
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
+ descr, p = self.callcontrol.callinfocollection.callinfo_for_oopspec(
+ EffectInfo.OS_STREQ_NONNULL)
+ # XXX this is fairly ugly way of creating a constant,
+ # however, callinfocollection has no better interface
+ c = Constant(p.adr.ptr, lltype.typeOf(p.adr.ptr))
+ op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr],
+ op.result)
+ return [SpaceOperation('-live-', [], None), op1, None]
else:
log.WARNING('ignoring hint %r at %r' % (hints, self.graph))
@@ -782,8 +800,7 @@
def rewrite_op_cast_ptr_to_int(self, op):
if self._is_gc(op.args[0]):
- #return op
- raise NotImplementedError("cast_ptr_to_int")
+ return op
def rewrite_op_force_cast(self, op):
v_arg = op.args[0]
@@ -883,9 +900,15 @@
v = v_arg
oplist = []
if unsigned1:
- opname = 'cast_uint_to_longlong'
+ if unsigned2:
+ opname = 'cast_uint_to_ulonglong'
+ else:
+ opname = 'cast_uint_to_longlong'
else:
- opname = 'cast_int_to_longlong'
+ if unsigned2:
+ opname = 'cast_int_to_ulonglong'
+ else:
+ opname = 'cast_int_to_longlong'
op2 = self.rewrite_operation(
SpaceOperation(opname, [v], v_result)
)
@@ -995,6 +1018,21 @@
return op2
''' % (_op, _oopspec.lower(), _oopspec, _oopspec)).compile()
+ for _op, _oopspec in [('cast_int_to_ulonglong', 'FROM_INT'),
+ ('cast_uint_to_ulonglong', 'FROM_UINT'),
+ ('cast_float_to_ulonglong', 'FROM_FLOAT'),
+ ('cast_ulonglong_to_float', 'U_TO_FLOAT'),
+ ]:
+ exec py.code.Source('''
+ def rewrite_op_%s(self, op):
+ args = op.args
+ op1 = self.prepare_builtin_call(op, "ullong_%s", args)
+ op2 = self._handle_oopspec_call(op1, args,
+ EffectInfo.OS_LLONG_%s,
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
+ return op2
+ ''' % (_op, _oopspec.lower(), _oopspec)).compile()
+
def _normalize(self, oplist):
if isinstance(oplist, SpaceOperation):
return [oplist]
@@ -1504,6 +1542,10 @@
def rewrite_op_jit_force_virtual(self, op):
return self._do_builtin_call(op)
+ def rewrite_op_jit_is_virtual(self, op):
+ raise Exception, (
+ "'vref.virtual' should not be used from jit-visible code")
+
def rewrite_op_jit_force_virtualizable(self, op):
# this one is for virtualizables
vinfo = self.get_vinfo(op.args[0])
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -315,18 +315,30 @@
def _ll_1_llong_from_int(x):
return r_longlong(intmask(x))
+def _ll_1_ullong_from_int(x):
+ return r_ulonglong(intmask(x))
+
def _ll_1_llong_from_uint(x):
return r_longlong(r_uint(x))
+def _ll_1_ullong_from_uint(x):
+ return r_ulonglong(r_uint(x))
+
def _ll_1_llong_to_int(xll):
return intmask(xll)
def _ll_1_llong_from_float(xf):
return r_longlong(xf)
+def _ll_1_ullong_from_float(xf):
+ return r_ulonglong(xf)
+
def _ll_1_llong_to_float(xll):
return float(rffi.cast(lltype.SignedLongLong, xll))
+def _ll_1_ullong_u_to_float(xull):
+ return float(rffi.cast(lltype.UnsignedLongLong, xull))
+
def _ll_1_llong_abs(xll):
if xll < 0:
@@ -351,20 +363,23 @@
return llop.llong_mod(lltype.SignedLongLong, xll, yll)
def _ll_2_ullong_floordiv(xll, yll):
- return llop.ullong_floordiv(lltype.SignedLongLong, xll, yll)
+ return llop.ullong_floordiv(lltype.UnsignedLongLong, xll, yll)
def _ll_2_ullong_floordiv_zer(xll, yll):
if yll == 0:
raise ZeroDivisionError
- return llop.ullong_floordiv(lltype.SignedLongLong, xll, yll)
+ return llop.ullong_floordiv(lltype.UnsignedLongLong, xll, yll)
def _ll_2_ullong_mod(xll, yll):
- return llop.ullong_mod(lltype.SignedLongLong, xll, yll)
+ return llop.ullong_mod(lltype.UnsignedLongLong, xll, yll)
def _ll_2_ullong_mod_zer(xll, yll):
if yll == 0:
raise ZeroDivisionError
- return llop.ullong_mod(lltype.SignedLongLong, xll, yll)
+ return llop.ullong_mod(lltype.UnsignedLongLong, xll, yll)
+
+def _ll_2_uint_mod(xll, yll):
+ return llop.uint_mod(lltype.Unsigned, xll, yll)
# libffi support
diff --git a/pypy/jit/codewriter/test/test_flatten.py b/pypy/jit/codewriter/test/test_flatten.py
--- a/pypy/jit/codewriter/test/test_flatten.py
+++ b/pypy/jit/codewriter/test/test_flatten.py
@@ -829,14 +829,15 @@
self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
transform=True)
elif TO in (rffi.LONG, rffi.ULONG):
+ if rffi.cast(FROM, -1) < 0:
+ fnname = "llong_from_int"
+ else:
+ fnname = "llong_from_uint"
if TO == rffi.LONG:
TO = rffi.LONGLONG
else:
TO = rffi.ULONGLONG
- if rffi.cast(FROM, -1) < 0:
- fnname = "llong_from_int"
- else:
- fnname = "llong_from_uint"
+ fnname = "u" + fnname
expected.pop() # remove int_return
expected.append(
"residual_call_irf_f $<* fn %s>, <Descr>, I[%s], R[], F[] -> %%f0"
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -1,5 +1,19 @@
import py
import random
+try:
+ from itertools import product
+except ImportError:
+ # Python 2.5, this is taken from the CPython docs, but simplified.
+ def product(*args):
+ # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+ # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+ pools = map(tuple, args)
+ result = [[]]
+ for pool in pools:
+ result = [x+[y] for x in result for y in pool]
+ for prod in result:
+ yield tuple(prod)
+
from pypy.objspace.flow.model import FunctionGraph, Block, Link
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
from pypy.jit.codewriter.jtransform import Transformer
@@ -85,6 +99,12 @@
if i == oopspecindex:
return True
return False
+ def callinfo_for_oopspec(self, oopspecindex):
+ assert oopspecindex == effectinfo.EffectInfo.OS_STREQ_NONNULL
+ class c:
+ class adr:
+ ptr = 1
+ return ('calldescr', c)
class FakeBuiltinCallControl:
def __init__(self):
@@ -105,6 +125,7 @@
EI.OS_STR2UNICODE:([PSTR], PUNICODE),
EI.OS_STR_CONCAT: ([PSTR, PSTR], PSTR),
EI.OS_STR_SLICE: ([PSTR, INT, INT], PSTR),
+ EI.OS_STREQ_NONNULL: ([PSTR, PSTR], INT),
EI.OS_UNI_CONCAT: ([PUNICODE, PUNICODE], PUNICODE),
EI.OS_UNI_SLICE: ([PUNICODE, INT, INT], PUNICODE),
EI.OS_UNI_EQUAL: ([PUNICODE, PUNICODE], lltype.Bool),
@@ -254,26 +275,35 @@
assert op1.result is None
def test_calls():
- for RESTYPE in [lltype.Signed, rclass.OBJECTPTR,
- lltype.Float, lltype.Void]:
- for with_void in [False, True]:
- for with_i in [False, True]:
- for with_r in [False, True]:
- for with_f in [False, True]:
- ARGS = []
- if with_void: ARGS += [lltype.Void, lltype.Void]
- if with_i: ARGS += [lltype.Signed, lltype.Char]
- if with_r: ARGS += [rclass.OBJECTPTR, lltype.Ptr(rstr.STR)]
- if with_f: ARGS += [lltype.Float, lltype.Float]
- random.shuffle(ARGS)
- if RESTYPE == lltype.Float: with_f = True
- if with_f: expectedkind = 'irf' # all kinds
- elif with_i: expectedkind = 'ir' # integers and references
- else: expectedkind = 'r' # only references
- yield residual_call_test, ARGS, RESTYPE, expectedkind
- yield direct_call_test, ARGS, RESTYPE, expectedkind
- yield indirect_residual_call_test, ARGS, RESTYPE, expectedkind
- yield indirect_regular_call_test, ARGS, RESTYPE, expectedkind
+ for RESTYPE, with_void, with_i, with_r, with_f in product(
+ [lltype.Signed, rclass.OBJECTPTR, lltype.Float, lltype.Void],
+ [False, True],
+ [False, True],
+ [False, True],
+ [False, True],
+ ):
+ ARGS = []
+ if with_void:
+ ARGS += [lltype.Void, lltype.Void]
+ if with_i:
+ ARGS += [lltype.Signed, lltype.Char]
+ if with_r:
+ ARGS += [rclass.OBJECTPTR, lltype.Ptr(rstr.STR)]
+ if with_f:
+ ARGS += [lltype.Float, lltype.Float]
+ random.shuffle(ARGS)
+ if RESTYPE == lltype.Float:
+ with_f = True
+ if with_f:
+ expectedkind = 'irf' # all kinds
+ elif with_i:
+ expectedkind = 'ir' # integers and references
+ else:
+ expectedkind = 'r' # only references
+ yield residual_call_test, ARGS, RESTYPE, expectedkind
+ yield direct_call_test, ARGS, RESTYPE, expectedkind
+ yield indirect_residual_call_test, ARGS, RESTYPE, expectedkind
+ yield indirect_regular_call_test, ARGS, RESTYPE, expectedkind
def get_direct_call_op(argtypes, restype):
FUNC = lltype.FuncType(argtypes, restype)
@@ -821,6 +851,21 @@
assert op1.args[2] == ListOfKind('ref', [v1, v2])
assert op1.result == v3
+def test_str_promote():
+ PSTR = lltype.Ptr(rstr.STR)
+ v1 = varoftype(PSTR)
+ v2 = varoftype(PSTR)
+ op = SpaceOperation('hint',
+ [v1, Constant({'promote_string': True}, lltype.Void)],
+ v2)
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+ op0, op1, _ = tr.rewrite_operation(op)
+ assert op1.opname == 'str_guard_value'
+ assert op1.args[0] == v1
+ assert op1.args[2] == 'calldescr'
+ assert op1.result == v2
+ assert op0.opname == '-live-'
+
def test_unicode_concat():
# test that the oopspec is present and correctly transformed
PSTR = lltype.Ptr(rstr.UNICODE)
diff --git a/pypy/jit/codewriter/test/test_longlong.py b/pypy/jit/codewriter/test/test_longlong.py
--- a/pypy/jit/codewriter/test/test_longlong.py
+++ b/pypy/jit/codewriter/test/test_longlong.py
@@ -57,7 +57,8 @@
assert op1.opname == 'residual_call_irf_f'
else:
assert op1.opname == 'residual_call_irf_i'
- gotindex = getattr(EffectInfo, 'OS_' + op1.args[0].value.upper())
+ gotindex = getattr(EffectInfo,
+ 'OS_' + op1.args[0].value.upper().lstrip('U'))
assert gotindex == oopspecindex
assert op1.args[1] == 'calldescr-%d' % oopspecindex
assert list(op1.args[2]) == [v for v in vlist
@@ -192,8 +193,12 @@
[lltype.SignedLongLong], lltype.Signed)
self.do_check('cast_float_to_longlong', EffectInfo.OS_LLONG_FROM_FLOAT,
[lltype.Float], lltype.SignedLongLong)
+ self.do_check('cast_float_to_ulonglong', EffectInfo.OS_LLONG_FROM_FLOAT,
+ [lltype.Float], lltype.UnsignedLongLong)
self.do_check('cast_longlong_to_float', EffectInfo.OS_LLONG_TO_FLOAT,
[lltype.SignedLongLong], lltype.Float)
+ self.do_check('cast_ulonglong_to_float', EffectInfo.OS_LLONG_U_TO_FLOAT,
+ [lltype.UnsignedLongLong], lltype.Float)
for T1 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
for T2 in [lltype.Signed, lltype.Unsigned]:
self.do_check('cast_primitive', EffectInfo.OS_LLONG_TO_INT,
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -2,7 +2,7 @@
from pypy.rlib.rtimer import read_timestamp
from pypy.rlib.rarithmetic import intmask, LONG_BIT, r_uint, ovfcheck
from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.debug import debug_start, debug_stop
+from pypy.rlib.debug import debug_start, debug_stop, ll_assert
from pypy.rlib.debug import make_sure_not_resized
from pypy.rpython.lltypesystem import lltype, llmemory, rclass
from pypy.rpython.lltypesystem.lloperation import llop
@@ -503,6 +503,15 @@
@arguments("r", returns="r")
def bhimpl_cast_opaque_ptr(a):
return a
+ @arguments("r", returns="i")
+ def bhimpl_cast_ptr_to_int(a):
+ i = lltype.cast_ptr_to_int(a)
+ ll_assert((i & 1) == 1, "bhimpl_cast_ptr_to_int: not an odd int")
+ return i
+ @arguments("i", returns="r")
+ def bhimpl_cast_int_to_ptr(i):
+ ll_assert((i & 1) == 1, "bhimpl_cast_int_to_ptr: not an odd int")
+ return lltype.cast_int_to_ptr(llmemory.GCREF, i)
@arguments("i", returns="i")
def bhimpl_int_copy(a):
@@ -523,6 +532,9 @@
@arguments("f")
def bhimpl_float_guard_value(a):
pass
+ @arguments("r", "i", "d", returns="r")
+ def bhimpl_str_guard_value(a, i, d):
+ return a
@arguments("self", "i")
def bhimpl_int_push(self, a):
diff --git a/pypy/jit/metainterp/graphpage.py b/pypy/jit/metainterp/graphpage.py
--- a/pypy/jit/metainterp/graphpage.py
+++ b/pypy/jit/metainterp/graphpage.py
@@ -12,8 +12,8 @@
def get_display_text(self):
return None
-def display_loops(loops, errmsg=None, highlight_loops=()):
- graphs = [(loop, loop in highlight_loops) for loop in loops]
+def display_loops(loops, errmsg=None, highlight_loops={}):
+ graphs = [(loop, highlight_loops.get(loop, 0)) for loop in loops]
for graph, highlight in graphs:
for op in graph.get_operations():
if is_interesting_guard(op):
@@ -65,8 +65,7 @@
def add_graph(self, graph, highlight=False):
graphindex = len(self.graphs)
self.graphs.append(graph)
- if highlight:
- self.highlight_graphs[graph] = True
+ self.highlight_graphs[graph] = highlight
for i, op in enumerate(graph.get_operations()):
self.all_operations[op] = graphindex, i
@@ -126,10 +125,13 @@
self.dotgen.emit('subgraph cluster%d {' % graphindex)
label = graph.get_display_text()
if label is not None:
- if self.highlight_graphs.get(graph):
- fillcolor = '#f084c2'
+ colorindex = self.highlight_graphs.get(graph, 0)
+ if colorindex == 1:
+ fillcolor = '#f084c2' # highlighted graph
+ elif colorindex == 2:
+ fillcolor = '#808080' # invalidated graph
else:
- fillcolor = '#84f0c2'
+ fillcolor = '#84f0c2' # normal color
self.dotgen.emit_node(graphname, shape="octagon",
label=label, fillcolor=fillcolor)
self.pendingedges.append((graphname,
diff --git a/pypy/jit/metainterp/heapcache.py b/pypy/jit/metainterp/heapcache.py
--- a/pypy/jit/metainterp/heapcache.py
+++ b/pypy/jit/metainterp/heapcache.py
@@ -54,9 +54,10 @@
if box in self.new_boxes:
self.new_boxes[box] = False
if box in self.dependencies:
- for dep in self.dependencies[box]:
+ deps = self.dependencies[box]
+ del self.dependencies[box]
+ for dep in deps:
self._escape(dep)
- del self.dependencies[box]
def clear_caches(self, opnum, descr, argboxes):
if opnum == rop.SETFIELD_GC:
diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -9,6 +9,7 @@
from pypy.jit.metainterp.resoperation import ResOperation, rop
from pypy.jit.codewriter import heaptracker, longlong
+from pypy.rlib.objectmodel import compute_identity_hash
# ____________________________________________________________
@@ -104,7 +105,7 @@
getref._annspecialcase_ = 'specialize:arg(1)'
def _get_hash_(self):
- raise NotImplementedError
+ return compute_identity_hash(self)
def clonebox(self):
raise NotImplementedError
@@ -133,6 +134,9 @@
def _get_str(self):
raise NotImplementedError
+ def same_box(self, other):
+ return self is other
+
class AbstractDescr(AbstractValue):
__slots__ = ()
@@ -241,32 +245,15 @@
def constbox(self):
return self
+ def same_box(self, other):
+ return self.same_constant(other)
+
def same_constant(self, other):
raise NotImplementedError
def __repr__(self):
return 'Const(%s)' % self._getrepr_()
- def __eq__(self, other):
- "NOT_RPYTHON"
- # Remember that you should not compare Consts with '==' in RPython.
- # Consts have no special __hash__, in order to force different Consts
- # from being considered as different keys when stored in dicts
- # (as they always are after translation). Use a dict_equal_consts()
- # to get the other behavior (i.e. using this __eq__).
- if self.__class__ is not other.__class__:
- return False
- try:
- return self.value == other.value
- except TypeError:
- if (isinstance(self.value, Symbolic) and
- isinstance(other.value, Symbolic)):
- return self.value is other.value
- raise
-
- def __ne__(self, other):
- return not (self == other)
-
class ConstInt(Const):
type = INT
@@ -688,33 +675,6 @@
# ____________________________________________________________
-def dict_equal_consts():
- "NOT_RPYTHON"
- # Returns a dict in which Consts that compare as equal
- # are identified when used as keys.
- return r_dict(dc_eq, dc_hash)
-
-def dc_eq(c1, c2):
- return c1 == c2
-
-def dc_hash(c):
- "NOT_RPYTHON"
- # This is called during translation only. Avoid using identityhash(),
- # to avoid forcing a hash, at least on lltype objects.
- if not isinstance(c, Const):
- return hash(c)
- if isinstance(c.value, Symbolic):
- return id(c.value)
- try:
- if isinstance(c, ConstPtr):
- p = lltype.normalizeptr(c.value)
- if p is not None:
- return hash(p._obj)
- else:
- return 0
- return c._get_hash_()
- except lltype.DelayedPointer:
- return -2 # xxx risk of changing hash...
def make_hashable_int(i):
from pypy.rpython.lltypesystem.ll2ctypes import NotCtypesAllocatedStructure
@@ -772,6 +732,7 @@
failed_states = None
retraced_count = 0
terminating = False # see TerminatingLoopToken in compile.py
+ invalidated = False
outermost_jitdriver_sd = None
# and more data specified by the backend when the loop is compiled
number = -1
@@ -974,6 +935,7 @@
self.loops = []
self.locations = []
self.aborted_keys = []
+ self.invalidated_token_numbers = set()
def set_history(self, history):
self.operations = history.operations
@@ -1052,7 +1014,12 @@
if loop in loops:
loops.remove(loop)
loops.append(loop)
- display_loops(loops, errmsg, extraloops)
+ highlight_loops = dict.fromkeys(extraloops, 1)
+ for loop in loops:
+ if hasattr(loop, '_looptoken_number') and (
+ loop._looptoken_number in self.invalidated_token_numbers):
+ highlight_loops.setdefault(loop, 2)
+ display_loops(loops, errmsg, highlight_loops)
# ----------------------------------------------------------------
diff --git a/pypy/jit/metainterp/memmgr.py b/pypy/jit/metainterp/memmgr.py
--- a/pypy/jit/metainterp/memmgr.py
+++ b/pypy/jit/metainterp/memmgr.py
@@ -68,7 +68,8 @@
debug_print("Loop tokens before:", oldtotal)
max_generation = self.current_generation - (self.max_age-1)
for looptoken in self.alive_loops.keys():
- if 0 <= looptoken.generation < max_generation:
+ if (0 <= looptoken.generation < max_generation or
+ looptoken.invalidated):
del self.alive_loops[looptoken]
newtotal = len(self.alive_loops)
debug_print("Loop tokens freed: ", oldtotal - newtotal)
diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -7,6 +7,8 @@
from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll, OptInlineShortPreamble
from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
from pypy.jit.metainterp.optimizeopt.simplify import OptSimplify
+from pypy.jit.metainterp.optimizeopt.pure import OptPure
+from pypy.jit.metainterp.optimizeopt.earlyforce import OptEarlyForce
from pypy.rlib.jit import PARAMETERS
from pypy.rlib.unroll import unrolling_iterable
@@ -14,6 +16,8 @@
('rewrite', OptRewrite),
('virtualize', OptVirtualize),
('string', OptString),
+ ('earlyforce', OptEarlyForce),
+ ('pure', OptPure),
('heap', OptHeap),
('ffi', None),
('unroll', None)]
diff --git a/pypy/jit/metainterp/optimizeopt/earlyforce.py b/pypy/jit/metainterp/optimizeopt/earlyforce.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/optimizeopt/earlyforce.py
@@ -0,0 +1,24 @@
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.optimizeopt.vstring import VAbstractStringValue
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+
+class OptEarlyForce(Optimization):
+ def propagate_forward(self, op):
+ opnum = op.getopnum()
+ if (opnum != rop.SETFIELD_GC and
+ opnum != rop.SETARRAYITEM_GC and
+ opnum != rop.QUASIIMMUT_FIELD):
+
+ for arg in op.getarglist():
+ if arg in self.optimizer.values:
+ value = self.getvalue(arg)
+ value.force_box(self)
+ self.emit_operation(op)
+
+ def new(self):
+ return OptEarlyForce()
+
+ def setup(self):
+ self.optimizer.optearlyforce = self
+
+
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -177,10 +177,10 @@
funcinfo.descr is None):
return [op] # cannot optimize
funcsymval = self.getvalue(op.getarg(2))
- arglist = [funcsymval.force_box()]
+ arglist = [funcsymval.get_key_box()]
for push_op in funcinfo.opargs:
argval = self.getvalue(push_op.getarg(2))
- arglist.append(argval.force_box())
+ arglist.append(argval.get_key_box())
newop = ResOperation(rop.CALL_RELEASE_GIL, arglist, op.result,
descr=funcinfo.descr)
self.commit_optimization()
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -50,6 +50,7 @@
if not self._lazy_setfield_registered:
optheap._lazy_setfields_and_arrayitems.append(self)
self._lazy_setfield_registered = True
+
else:
# this is the case where the pending setfield ends up
# storing precisely the value that is already there,
@@ -158,12 +159,17 @@
self._lazy_setfields_and_arrayitems = []
self._remove_guard_not_invalidated = False
self._seen_guard_not_invalidated = False
+ self.posponedop = None
def force_at_end_of_preamble(self):
self.force_all_lazy_setfields_and_arrayitems()
def flush(self):
self.force_all_lazy_setfields_and_arrayitems()
+ if self.posponedop:
+ posponedop = self.posponedop
+ self.posponedop = None
+ self.next_optimization.propagate_forward(posponedop)
def new(self):
return OptHeap()
@@ -211,7 +217,15 @@
def emit_operation(self, op):
self.emitting_operation(op)
- self.next_optimization.propagate_forward(op)
+ if self.posponedop:
+ posponedop = self.posponedop
+ self.posponedop = None
+ self.next_optimization.propagate_forward(posponedop)
+ if (op.is_comparison() or op.getopnum() == rop.CALL_MAY_FORCE
+ or op.is_ovf()):
+ self.posponedop = op
+ else:
+ self.next_optimization.propagate_forward(op)
def emitting_operation(self, op):
if op.has_no_side_effect():
@@ -293,30 +307,6 @@
if indexvalue is None or indexvalue.intbound.contains(idx):
cf.force_lazy_setfield(self, can_cache)
- def fixup_guard_situation(self):
- # hackish: reverse the order of the last two operations if it makes
- # sense to avoid a situation like "int_eq/setfield_gc/guard_true",
- # which the backend (at least the x86 backend) does not handle well.
- newoperations = self.optimizer.newoperations
- if len(newoperations) < 2:
- return
- lastop = newoperations[-1]
- if (lastop.getopnum() != rop.SETFIELD_GC and
- lastop.getopnum() != rop.SETARRAYITEM_GC):
- return
- # - is_comparison() for cases like "int_eq/setfield_gc/guard_true"
- # - CALL_MAY_FORCE: "call_may_force/setfield_gc/guard_not_forced"
- # - is_ovf(): "int_add_ovf/setfield_gc/guard_no_overflow"
- prevop = newoperations[-2]
- opnum = prevop.getopnum()
- if not (prevop.is_comparison() or opnum == rop.CALL_MAY_FORCE
- or prevop.is_ovf()):
- return
- if prevop.result in lastop.getarglist():
- return
- newoperations[-2] = lastop
- newoperations[-1] = prevop
-
def _assert_valid_cf(self, cf):
# check that 'cf' is in cached_fields or cached_arrayitems
if not we_are_translated():
@@ -362,7 +352,6 @@
fieldvalue.get_key_box(), itemindex))
else:
cf.force_lazy_setfield(self)
- self.fixup_guard_situation()
return pendingfields
def optimize_GETFIELD_GC(self, op):
@@ -374,12 +363,22 @@
return
# default case: produce the operation
structvalue.ensure_nonnull()
- ###self.optimizer.optimize_default(op)
self.emit_operation(op)
# then remember the result of reading the field
fieldvalue = self.getvalue(op.result)
cf.remember_field_value(structvalue, fieldvalue, op)
+ def optimize_GETFIELD_GC_PURE(self, op):
+ structvalue = self.getvalue(op.getarg(0))
+ cf = self.field_cache(op.getdescr())
+ fieldvalue = cf.getfield_from_cache(self, structvalue)
+ if fieldvalue is not None:
+ self.make_equal_to(op.result, fieldvalue)
+ return
+ # default case: produce the operation
+ structvalue.ensure_nonnull()
+ self.emit_operation(op)
+
def optimize_SETFIELD_GC(self, op):
if self.has_pure_result(rop.GETFIELD_GC_PURE, [op.getarg(0)],
op.getdescr()):
@@ -389,6 +388,7 @@
#
cf = self.field_cache(op.getdescr())
cf.do_setfield(self, op)
+
def optimize_GETARRAYITEM_GC(self, op):
arrayvalue = self.getvalue(op.getarg(0))
@@ -413,6 +413,25 @@
fieldvalue = self.getvalue(op.result)
cf.remember_field_value(arrayvalue, fieldvalue, op)
+ def optimize_GETARRAYITEM_GC_PURE(self, op):
+ arrayvalue = self.getvalue(op.getarg(0))
+ indexvalue = self.getvalue(op.getarg(1))
+ cf = None
+ if indexvalue.is_constant():
+ arrayvalue.make_len_gt(MODE_ARRAY, op.getdescr(), indexvalue.box.getint())
+ # use the cache on (arraydescr, index), which is a constant
+ cf = self.arrayitem_cache(op.getdescr(), indexvalue.box.getint())
+ fieldvalue = cf.getfield_from_cache(self, arrayvalue)
+ if fieldvalue is not None:
+ self.make_equal_to(op.result, fieldvalue)
+ return
+ else:
+ # variable index, so make sure the lazy setarrayitems are done
+ self.force_lazy_setarrayitem(op.getdescr(), indexvalue=indexvalue)
+ # default case: produce the operation
+ arrayvalue.ensure_nonnull()
+ self.emit_operation(op)
+
def optimize_SETARRAYITEM_GC(self, op):
if self.has_pure_result(rop.GETARRAYITEM_GC_PURE, [op.getarg(0),
op.getarg(1)],
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -31,8 +31,8 @@
class OptValue(object):
__metaclass__ = extendabletype
- _attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound', 'lenbound')
- last_guard_index = -1
+ _attrs_ = ('box', 'known_class', 'last_guard', 'level', 'intbound', 'lenbound')
+ last_guard = None
level = LEVEL_UNKNOWN
known_class = None
@@ -100,7 +100,7 @@
self.make_constant(other.get_key_box())
optimizer.turned_constant(self)
elif other.level == LEVEL_KNOWNCLASS:
- self.make_constant_class(other.known_class, -1)
+ self.make_constant_class(other.known_class, None)
else:
if other.level == LEVEL_NONNULL:
self.ensure_nonnull()
@@ -114,13 +114,13 @@
self.lenbound = other.lenbound.clone()
- def force_box(self):
+ def force_box(self, optforce):
return self.box
def get_key_box(self):
return self.box
- def force_at_end_of_preamble(self, already_forced):
+ def force_at_end_of_preamble(self, already_forced, optforce):
return self
def get_args_for_fail(self, modifier):
@@ -162,16 +162,16 @@
else:
return None
- def make_constant_class(self, classbox, opindex):
+ def make_constant_class(self, classbox, guardop):
assert self.level < LEVEL_KNOWNCLASS
self.known_class = classbox
self.level = LEVEL_KNOWNCLASS
- self.last_guard_index = opindex
+ self.last_guard = guardop
- def make_nonnull(self, opindex):
+ def make_nonnull(self, guardop):
assert self.level < LEVEL_NONNULL
self.level = LEVEL_NONNULL
- self.last_guard_index = opindex
+ self.last_guard = guardop
def is_nonnull(self):
level = self.level
@@ -240,26 +240,12 @@
def __init__(self):
pass # make rpython happy
- def propagate_begin_forward(self):
- if self.next_optimization:
- self.next_optimization.propagate_begin_forward()
-
- def propagate_end_forward(self):
- if self.next_optimization:
- self.next_optimization.propagate_end_forward()
-
def propagate_forward(self, op):
raise NotImplementedError
def emit_operation(self, op):
self.next_optimization.propagate_forward(op)
- def test_emittable(self, op):
- return self.is_emittable(op)
-
- def is_emittable(self, op):
- return self.next_optimization.test_emittable(op)
-
# FIXME: Move some of these here?
def getvalue(self, box):
return self.optimizer.getvalue(box)
@@ -289,19 +275,19 @@
return self.optimizer.new_const_item(arraydescr)
def pure(self, opnum, args, result):
- op = ResOperation(opnum, args, result)
- key = self.optimizer.make_args_key(op)
- if key not in self.optimizer.pure_operations:
- self.optimizer.pure_operations[key] = op
+ if self.optimizer.optpure:
+ self.optimizer.optpure.pure(opnum, args, result)
def has_pure_result(self, opnum, args, descr):
- op = ResOperation(opnum, args, None, descr)
- key = self.optimizer.make_args_key(op)
- op = self.optimizer.pure_operations.get(key, None)
- if op is None:
- return False
- return op.getdescr() is descr
+ if self.optimizer.optpure:
+ return self.optimizer.optpure.has_pure_result(opnum, args, descr)
+ return False
+ def get_pure_result(self, key):
+ if self.optimizer.optpure:
+ return self.optimizer.optpure.get_pure_result(key)
+ return None
+
def setup(self):
pass
@@ -322,6 +308,9 @@
def produce_potential_short_preamble_ops(self, potential_ops):
pass
+ def forget_numberings(self, box):
+ self.optimizer.forget_numberings(box)
+
class Optimizer(Optimization):
def __init__(self, metainterp_sd, loop, optimizations=None, bridge=False):
@@ -333,14 +322,16 @@
self.interned_refs = self.cpu.ts.new_ref_dict()
self.resumedata_memo = resume.ResumeDataLoopMemo(metainterp_sd)
self.bool_boxes = {}
- self.pure_operations = args_dict()
self.producer = {}
self.pendingfields = []
- self.posponedop = None
self.exception_might_have_happened = False
self.quasi_immutable_deps = None
self.opaque_pointers = {}
- self.newoperations = []
+ self.replaces_guard = {}
+ self._newoperations = []
+ self.optimizer = self
+ self.optpure = None
+ self.optearlyforce = None
if loop is not None:
self.call_pure_results = loop.call_pure_results
@@ -369,21 +360,20 @@
def flush(self):
for o in self.optimizations:
o.flush()
- assert self.posponedop is None
def new(self):
new = Optimizer(self.metainterp_sd, self.loop)
return self._new(new)
def _new(self, new):
- assert self.posponedop is None
optimizations = [o.new() for o in self.optimizations]
new.set_optimizations(optimizations)
new.quasi_immutable_deps = self.quasi_immutable_deps
return new
def produce_potential_short_preamble_ops(self, sb):
- raise NotImplementedError('This is implemented in unroll.UnrollableOptimizer')
+ for opt in self.optimizations:
+ opt.produce_potential_short_preamble_ops(sb)
def turned_constant(self, value):
for o in self.optimizations:
@@ -433,6 +423,13 @@
return constbox
return None
+ def get_newoperations(self):
+ self.flush()
+ return self._newoperations
+
+ def clear_newoperations(self):
+ self._newoperations = []
+
def make_equal_to(self, box, value, replace=False):
assert isinstance(value, OptValue)
assert replace or box not in self.values
@@ -481,15 +478,10 @@
def propagate_all_forward(self):
self.exception_might_have_happened = self.bridge
- self.newoperations = []
- self.first_optimization.propagate_begin_forward()
- self.i = 0
- while self.i < len(self.loop.operations):
- op = self.loop.operations[self.i]
+ self.clear_newoperations()
+ for op in self.loop.operations:
self.first_optimization.propagate_forward(op)
- self.i += 1
- self.first_optimization.propagate_end_forward()
- self.loop.operations = self.newoperations
+ self.loop.operations = self.get_newoperations()
self.loop.quasi_immutable_deps = self.quasi_immutable_deps
# accumulate counters
self.resumedata_memo.update_counters(self.metainterp_sd.profiler)
@@ -501,9 +493,6 @@
self.producer[op.result] = op
dispatch_opt(self, op)
- def test_emittable(self, op):
- return True
-
def emit_operation(self, op):
if op.returns_bool_result():
self.bool_boxes[self.getvalue(op.result)] = None
@@ -519,14 +508,30 @@
pass
else:
self.ensure_imported(value)
- op.setarg(i, value.force_box())
+ op.setarg(i, value.force_box(self))
self.metainterp_sd.profiler.count(jitprof.OPT_OPS)
if op.is_guard():
self.metainterp_sd.profiler.count(jitprof.OPT_GUARDS)
- op = self.store_final_boxes_in_guard(op)
+ if self.replaces_guard and op in self.replaces_guard:
+ self.replace_op(self.replaces_guard[op], op)
+ del self.replaces_guard[op]
+ return
+ else:
+ op = self.store_final_boxes_in_guard(op)
elif op.can_raise():
self.exception_might_have_happened = True
- self.newoperations.append(op)
+ self._newoperations.append(op)
+
+ def replace_op(self, old_op, new_op):
+ # XXX: Do we want to cache indexes to prevent search?
+ i = len(self._newoperations)
+ while i > 0:
+ i -= 1
+ if self._newoperations[i] is old_op:
+ self._newoperations[i] = new_op
+ break
+ else:
+ assert False
def store_final_boxes_in_guard(self, op):
descr = op.getdescr()
@@ -574,51 +579,8 @@
args[n + 1] = op.getdescr()
return args
- @specialize.argtype(0)
def optimize_default(self, op):
- canfold = op.is_always_pure()
- if op.is_ovf():
- self.posponedop = op
- return
- if self.posponedop:
- nextop = op
- op = self.posponedop
- self.posponedop = None
- canfold = nextop.getopnum() == rop.GUARD_NO_OVERFLOW
- else:
- nextop = None
-
- if canfold:
- for i in range(op.numargs()):
- if self.get_constant_box(op.getarg(i)) is None:
- break
- else:
- # all constant arguments: constant-fold away
- resbox = self.constant_fold(op)
- # note that INT_xxx_OVF is not done from here, and the
- # overflows in the INT_xxx operations are ignored
- self.make_constant(op.result, resbox)
- return
-
- # did we do the exact same operation already?
- args = self.make_args_key(op)
- oldop = self.pure_operations.get(args, None)
- if oldop is not None and oldop.getdescr() is op.getdescr():
- assert oldop.getopnum() == op.getopnum()
- self.make_equal_to(op.result, self.getvalue(oldop.result),
- True)
- return
- else:
- self.pure_operations[args] = op
- self.remember_emitting_pure(op)
-
- # otherwise, the operation remains
self.emit_operation(op)
- if nextop:
- self.emit_operation(nextop)
-
- def remember_emitting_pure(self, op):
- pass
def constant_fold(self, op):
argboxes = [self.get_constant_box(op.getarg(i))
@@ -636,11 +598,6 @@
def optimize_DEBUG_MERGE_POINT(self, op):
self.emit_operation(op)
- def optimize_CAST_OPAQUE_PTR(self, op):
- value = self.getvalue(op.getarg(0))
- self.opaque_pointers[value] = True
- self.make_equal_to(op.result, value)
-
def optimize_GETARRAYITEM_GC_PURE(self, op):
indexvalue = self.getvalue(op.getarg(1))
if indexvalue.is_constant():
diff --git a/pypy/jit/metainterp/optimizeopt/pure.py b/pypy/jit/metainterp/optimizeopt/pure.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/optimizeopt/pure.py
@@ -0,0 +1,115 @@
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
+ args_dict)
+
+class OptPure(Optimization):
+ def __init__(self):
+ self.posponedop = None
+ self.pure_operations = args_dict()
+ self.emitted_pure_operations = {}
+
+ def propagate_forward(self, op):
+ dispatch_opt(self, op)
+
+ def optimize_default(self, op):
+ canfold = op.is_always_pure()
+ if op.is_ovf():
+ self.posponedop = op
+ return
+ if self.posponedop:
+ nextop = op
+ op = self.posponedop
+ self.posponedop = None
+ canfold = nextop.getopnum() == rop.GUARD_NO_OVERFLOW
+ else:
+ nextop = None
+
+ if canfold:
+ for i in range(op.numargs()):
+ if self.get_constant_box(op.getarg(i)) is None:
+ break
+ else:
+ # all constant arguments: constant-fold away
+ resbox = self.optimizer.constant_fold(op)
+ # note that INT_xxx_OVF is not done from here, and the
+ # overflows in the INT_xxx operations are ignored
+ self.optimizer.make_constant(op.result, resbox)
+ return
+
+ # did we do the exact same operation already?
+ args = self.optimizer.make_args_key(op)
+ oldop = self.pure_operations.get(args, None)
+ if oldop is not None and oldop.getdescr() is op.getdescr():
+ assert oldop.getopnum() == op.getopnum()
+ self.optimizer.make_equal_to(op.result, self.getvalue(oldop.result),
+ True)
+ return
+ else:
+ self.pure_operations[args] = op
+ self.remember_emitting_pure(op)
+
+ # otherwise, the operation remains
+ self.emit_operation(op)
+ if op.returns_bool_result():
+ self.optimizer.bool_boxes[self.getvalue(op.result)] = None
+ if nextop:
+ self.emit_operation(nextop)
+
+ def optimize_CALL_PURE(self, op):
+ args = self.optimizer.make_args_key(op)
+ oldop = self.pure_operations.get(args, None)
+ if oldop is not None and oldop.getdescr() is op.getdescr():
+ assert oldop.getopnum() == op.getopnum()
+ self.make_equal_to(op.result, self.getvalue(oldop.result))
+ return
+ else:
+ self.pure_operations[args] = op
+ self.remember_emitting_pure(op)
+
+ # replace CALL_PURE with just CALL
+ args = op.getarglist()
+ self.emit_operation(ResOperation(rop.CALL, args, op.result,
+ op.getdescr()))
+
+ def flush(self):
+ assert self.posponedop is None
+
+ def new(self):
+ assert self.posponedop is None
+ return OptPure()
+
+ def setup(self):
+ self.optimizer.optpure = self
+
+ def pure(self, opnum, args, result):
+ op = ResOperation(opnum, args, result)
+ key = self.optimizer.make_args_key(op)
+ if key not in self.pure_operations:
+ self.pure_operations[key] = op
+
+ def has_pure_result(self, opnum, args, descr):
+ op = ResOperation(opnum, args, None, descr)
+ key = self.optimizer.make_args_key(op)
+ op = self.pure_operations.get(key, None)
+ if op is None:
+ return False
+ return op.getdescr() is descr
+
+ def get_pure_result(self, key):
+ return self.pure_operations.get(key, None)
+
+ def remember_emitting_pure(self, op):
+ self.emitted_pure_operations[op] = True
+
+ def produce_potential_short_preamble_ops(self, sb):
+ for op in self.emitted_pure_operations:
+ if op.getopnum() == rop.GETARRAYITEM_GC_PURE or \
+ op.getopnum() == rop.STRGETITEM or \
+ op.getopnum() == rop.UNICODEGETITEM:
+ if not self.getvalue(op.getarg(1)).is_constant():
+ continue
+ sb.add_potential(op)
+
+dispatch_opt = make_dispatcher_method(OptPure, 'optimize_',
+ default=OptPure.optimize_default)
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -31,21 +31,8 @@
dispatch_opt(self, op)
- def test_emittable(self, op):
- opnum = op.getopnum()
- for value, cls, func in optimize_guards:
- if opnum == value:
- assert isinstance(op, cls)
- try:
- func(self, op, dryrun=True)
- return self.is_emittable(op)
- except InvalidLoop:
- return False
- return self.is_emittable(op)
-
-
def try_boolinvers(self, op, targs):
- oldop = self.optimizer.pure_operations.get(targs, None)
+ oldop = self.get_pure_result(targs)
if oldop is not None and oldop.getdescr() is op.getdescr():
value = self.getvalue(oldop.result)
if value.is_constant():
@@ -62,32 +49,35 @@
def find_rewritable_bool(self, op, args):
try:
oldopnum = opboolinvers[op.getopnum()]
+ except KeyError:
+ pass
+ else:
targs = self.optimizer.make_args_key(ResOperation(oldopnum, [args[0], args[1]],
None))
if self.try_boolinvers(op, targs):
return True
- except KeyError:
- pass
try:
oldopnum = opboolreflex[op.getopnum()] # FIXME: add INT_ADD, INT_MUL
+ except KeyError:
+ pass
+ else:
targs = self.optimizer.make_args_key(ResOperation(oldopnum, [args[1], args[0]],
None))
- oldop = self.optimizer.pure_operations.get(targs, None)
+ oldop = self.get_pure_result(targs)
if oldop is not None and oldop.getdescr() is op.getdescr():
self.make_equal_to(op.result, self.getvalue(oldop.result))
return True
- except KeyError:
- pass
try:
oldopnum = opboolinvers[opboolreflex[op.getopnum()]]
+ except KeyError:
+ pass
+ else:
targs = self.optimizer.make_args_key(ResOperation(oldopnum, [args[1], args[0]],
None))
if self.try_boolinvers(op, targs):
return True
- except KeyError:
- pass
return False
@@ -116,10 +106,9 @@
self.make_equal_to(op.result, v1)
else:
self.emit_operation(op)
-
- # Synthesize the reverse ops for optimize_default to reuse
- self.pure(rop.INT_ADD, [op.result, op.getarg(1)], op.getarg(0))
- self.pure(rop.INT_SUB, [op.getarg(0), op.result], op.getarg(1))
+ # Synthesize the reverse ops for optimize_default to reuse
+ self.pure(rop.INT_ADD, [op.result, op.getarg(1)], op.getarg(0))
+ self.pure(rop.INT_SUB, [op.getarg(0), op.result], op.getarg(1))
def optimize_INT_ADD(self, op):
v1 = self.getvalue(op.getarg(0))
@@ -132,10 +121,9 @@
self.make_equal_to(op.result, v1)
else:
self.emit_operation(op)
-
- # Synthesize the reverse op for optimize_default to reuse
- self.pure(rop.INT_SUB, [op.result, op.getarg(1)], op.getarg(0))
- self.pure(rop.INT_SUB, [op.result, op.getarg(0)], op.getarg(1))
+ # Synthesize the reverse op for optimize_default to reuse
+ self.pure(rop.INT_SUB, [op.result, op.getarg(1)], op.getarg(0))
+ self.pure(rop.INT_SUB, [op.result, op.getarg(0)], op.getarg(1))
def optimize_INT_MUL(self, op):
v1 = self.getvalue(op.getarg(0))
@@ -151,13 +139,13 @@
self.make_constant_int(op.result, 0)
else:
for lhs, rhs in [(v1, v2), (v2, v1)]:
- # x & (x -1) == 0 is a quick test for power of 2
- if (lhs.is_constant() and
- (lhs.box.getint() & (lhs.box.getint() - 1)) == 0):
- new_rhs = ConstInt(highest_bit(lhs.box.getint()))
- op = op.copy_and_change(rop.INT_LSHIFT, args=[rhs.box, new_rhs])
- break
-
+ if lhs.is_constant():
+ x = lhs.box.getint()
+ # x & (x - 1) == 0 is a quick test for power of 2
+ if x & (x - 1) == 0:
+ new_rhs = ConstInt(highest_bit(lhs.box.getint()))
+ op = op.copy_and_change(rop.INT_LSHIFT, args=[rhs.box, new_rhs])
+ break
self.emit_operation(op)
def optimize_UINT_FLOORDIV(self, op):
@@ -214,40 +202,7 @@
self.emit_operation(op)
self.pure(rop.FLOAT_NEG, [op.result], v1)
- def optimize_CALL_PURE(self, op):
- arg_consts = []
- for i in range(op.numargs()):
- arg = op.getarg(i)
- const = self.get_constant_box(arg)
- if const is None:
- break
- arg_consts.append(const)
- else:
- # all constant arguments: check if we already know the result
- try:
- result = self.optimizer.call_pure_results[arg_consts]
- except KeyError:
- pass
- else:
- self.make_constant(op.result, result)
- return
-
- args = self.optimizer.make_args_key(op)
- oldop = self.optimizer.pure_operations.get(args, None)
- if oldop is not None and oldop.getdescr() is op.getdescr():
- assert oldop.getopnum() == op.getopnum()
- self.make_equal_to(op.result, self.getvalue(oldop.result))
- return
- else:
- self.optimizer.pure_operations[args] = op
- self.optimizer.remember_emitting_pure(op)
-
- # replace CALL_PURE with just CALL
- args = op.getarglist()
- self.emit_operation(ResOperation(rop.CALL, args, op.result,
- op.getdescr()))
-
- def optimize_guard(self, op, constbox, emit_operation=True, dryrun=False):
+ def optimize_guard(self, op, constbox, emit_operation=True):
value = self.getvalue(op.getarg(0))
if value.is_constant():
box = value.box
@@ -255,62 +210,57 @@
if not box.same_constant(constbox):
raise InvalidLoop
return
- if dryrun: return
if emit_operation:
self.emit_operation(op)
value.make_constant(constbox)
self.optimizer.turned_constant(value)
- def optimize_GUARD_ISNULL(self, op, dryrun=False):
+ def optimize_GUARD_ISNULL(self, op):
value = self.getvalue(op.getarg(0))
if value.is_null():
return
elif value.is_nonnull():
raise InvalidLoop
- if dryrun: return
self.emit_operation(op)
value.make_constant(self.optimizer.cpu.ts.CONST_NULL)
- def optimize_GUARD_NONNULL(self, op, dryrun=False):
+ def optimize_GUARD_NONNULL(self, op):
value = self.getvalue(op.getarg(0))
if value.is_nonnull():
return
elif value.is_null():
raise InvalidLoop
- if dryrun: return
self.emit_operation(op)
- value.make_nonnull(len(self.optimizer.newoperations) - 1)
+ value.make_nonnull(op)
- def optimize_GUARD_VALUE(self, op, dryrun=False):
+ def optimize_GUARD_VALUE(self, op):
value = self.getvalue(op.getarg(0))
- emit_operation = True
- if not dryrun and value.last_guard_index != -1:
+ if value.last_guard:
# there already has been a guard_nonnull or guard_class or
# guard_nonnull_class on this value, which is rather silly.
# replace the original guard with a guard_value
- old_guard_op = self.optimizer.newoperations[value.last_guard_index]
- new_guard_op = old_guard_op.copy_and_change(rop.GUARD_VALUE,
- args = [old_guard_op.getarg(0), op.getarg(1)])
- self.optimizer.newoperations[value.last_guard_index] = new_guard_op
+ old_guard_op = value.last_guard
+ op = old_guard_op.copy_and_change(rop.GUARD_VALUE,
+ args = [old_guard_op.getarg(0), op.getarg(1)])
+ self.optimizer.replaces_guard[op] = old_guard_op
# hack hack hack. Change the guard_opnum on
# new_guard_op.getdescr() so that when resuming,
# the operation is not skipped by pyjitpl.py.
- descr = new_guard_op.getdescr()
+ descr = op.getdescr()
assert isinstance(descr, compile.ResumeGuardDescr)
descr.guard_opnum = rop.GUARD_VALUE
- descr.make_a_counter_per_value(new_guard_op)
- emit_operation = False
+ descr.make_a_counter_per_value(op)
constbox = op.getarg(1)
assert isinstance(constbox, Const)
- self.optimize_guard(op, constbox, emit_operation, dryrun)
+ self.optimize_guard(op, constbox)
- def optimize_GUARD_TRUE(self, op, dryrun=False):
- self.optimize_guard(op, CONST_1, dryrun=dryrun)
+ def optimize_GUARD_TRUE(self, op):
+ self.optimize_guard(op, CONST_1)
- def optimize_GUARD_FALSE(self, op, dryrun=False):
- self.optimize_guard(op, CONST_0, dryrun=dryrun)
+ def optimize_GUARD_FALSE(self, op):
+ self.optimize_guard(op, CONST_0)
- def optimize_GUARD_CLASS(self, op, dryrun=False):
+ def optimize_GUARD_CLASS(self, op):
value = self.getvalue(op.getarg(0))
expectedclassbox = op.getarg(1)
assert isinstance(expectedclassbox, Const)
@@ -319,38 +269,32 @@
if realclassbox.same_constant(expectedclassbox):
return
raise InvalidLoop
- if dryrun: return
- emit_operation = True
- if value.last_guard_index != -1:
+ if value.last_guard:
# there already has been a guard_nonnull or guard_class or
# guard_nonnull_class on this value.
- old_guard_op = self.optimizer.newoperations[value.last_guard_index]
+ old_guard_op = value.last_guard
if old_guard_op.getopnum() == rop.GUARD_NONNULL:
# it was a guard_nonnull, which we replace with a
# guard_nonnull_class.
- new_guard_op = old_guard_op.copy_and_change (rop.GUARD_NONNULL_CLASS,
+ op = old_guard_op.copy_and_change (rop.GUARD_NONNULL_CLASS,
args = [old_guard_op.getarg(0), op.getarg(1)])
- self.optimizer.newoperations[value.last_guard_index] = new_guard_op
+ self.optimizer.replaces_guard[op] = old_guard_op
# hack hack hack. Change the guard_opnum on
# new_guard_op.getdescr() so that when resuming,
# the operation is not skipped by pyjitpl.py.
- descr = new_guard_op.getdescr()
+ descr = op.getdescr()
assert isinstance(descr, compile.ResumeGuardDescr)
descr.guard_opnum = rop.GUARD_NONNULL_CLASS
- emit_operation = False
- if emit_operation:
- self.emit_operation(op)
- last_guard_index = len(self.optimizer.newoperations) - 1
- else:
- last_guard_index = value.last_guard_index
- value.make_constant_class(expectedclassbox, last_guard_index)
+ self.emit_operation(op)
+ value.make_constant_class(expectedclassbox, op)
- def optimize_GUARD_NONNULL_CLASS(self, op, dryrun=False):
- self.optimize_GUARD_NONNULL(op, True)
- self.optimize_GUARD_CLASS(op, dryrun)
+ def optimize_GUARD_NONNULL_CLASS(self, op):
+ value = self.getvalue(op.getarg(0))
+ if value.is_null():
+ raise InvalidLoop
+ self.optimize_GUARD_CLASS(op)
- def optimize_GUARD_NO_EXCEPTION(self, op, dryrun=False):
- if dryrun: return
+ def optimize_GUARD_NO_EXCEPTION(self, op):
if not self.optimizer.exception_might_have_happened:
return
self.emit_operation(op)
@@ -470,7 +414,7 @@
newop = ResOperation(rop.SETARRAYITEM_GC,
[op.getarg(2),
ConstInt(index + dest_start),
- val.force_box()], None,
+ val.get_key_box()], None,
descr=source_value.arraydescr)
self.emit_operation(newop)
return True
@@ -478,6 +422,25 @@
return True # 0-length arraycopy
return False
+ def optimize_CALL_PURE(self, op):
+ arg_consts = []
+ for i in range(op.numargs()):
+ arg = op.getarg(i)
+ const = self.get_constant_box(arg)
+ if const is None:
+ break
+ arg_consts.append(const)
+ else:
+ # all constant arguments: check if we already know the result
+ try:
+ result = self.optimizer.call_pure_results[arg_consts]
+ except KeyError:
+ pass
+ else:
+ self.make_constant(op.result, result)
+ return
+ self.emit_operation(op)
+
def optimize_INT_FLOORDIV(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
@@ -492,6 +455,18 @@
args = [op.getarg(0), ConstInt(highest_bit(val))])
self.emit_operation(op)
+ def optimize_CAST_OPAQUE_PTR(self, op):
+ value = self.getvalue(op.getarg(0))
+ self.optimizer.opaque_pointers[value] = True
+ self.make_equal_to(op.result, value)
+
+ def optimize_CAST_PTR_TO_INT(self, op):
+ self.pure(rop.CAST_INT_TO_PTR, [op.result], op.getarg(0))
+ self.emit_operation(op)
+
+ def optimize_CAST_INT_TO_PTR(self, op):
+ self.pure(rop.CAST_PTR_TO_INT, [op.result], op.getarg(0))
+ self.emit_operation(op)
dispatch_opt = make_dispatcher_method(OptRewrite, 'optimize_',
default=OptRewrite.emit_operation)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -39,8 +39,9 @@
def test_sharing_field_lists_of_virtual():
class FakeOptimizer(object):
- class cpu(object):
- pass
+ class optimizer(object):
+ class cpu(object):
+ pass
opt = FakeOptimizer()
virt1 = virtualize.AbstractVirtualStructValue(opt, None)
lst1 = virt1._get_field_descr_list()
@@ -69,7 +70,7 @@
class FakeVirtualValue(virtualize.AbstractVirtualValue):
def _make_virtual(self, *args):
return FakeVInfo()
- v1 = FakeVirtualValue(None, None, None)
+ v1 = FakeVirtualValue(None, None)
vinfo1 = v1.make_virtual_info(None, [1, 2, 4])
vinfo2 = v1.make_virtual_info(None, [1, 2, 4])
assert vinfo1 is vinfo2
@@ -111,7 +112,7 @@
class BaseTestBasic(BaseTest):
- enable_opts = "intbounds:rewrite:virtualize:string:heap"
+ enable_opts = "intbounds:rewrite:virtualize:string:earlyforce:pure:heap"
def optimize_loop(self, ops, optops, call_pure_results=None):
@@ -651,8 +652,8 @@
i3 = getfield_gc(p3, descr=valuedescr)
escape(i3)
p1 = new_with_vtable(ConstClass(node_vtable))
+ p1sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p1, i1, descr=valuedescr)
- p1sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p1sub, i1, descr=valuedescr)
setfield_gc(p1, p1sub, descr=nextdescr)
jump(i1, p1, p2)
@@ -667,10 +668,10 @@
p3sub = getfield_gc(p3, descr=nextdescr)
i3 = getfield_gc(p3sub, descr=valuedescr)
escape(i3)
+ p1 = new_with_vtable(ConstClass(node_vtable))
p2sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p2sub, i1, descr=valuedescr)
setfield_gc(p2, p2sub, descr=nextdescr)
- p1 = new_with_vtable(ConstClass(node_vtable))
jump(i1, p1, p2)
"""
# The same as test_p123_simple, but in the end the "old" p2 contains
@@ -2328,7 +2329,7 @@
def _variables_equal(box, varname, strict):
if varname not in virtuals:
if strict:
- assert box == oparse.getvar(varname)
+ assert box.same_box(oparse.getvar(varname))
else:
assert box.value == oparse.getvar(varname).value
else:
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
@@ -36,7 +36,7 @@
class TestFfiCall(BaseTestBasic, LLtypeMixin):
- enable_opts = "intbounds:rewrite:virtualize:string:heap:ffi"
+ enable_opts = "intbounds:rewrite:virtualize:string:pure:earlyforce:heap:ffi"
class namespace:
cpu = LLtypeMixin.cpu
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -68,7 +68,7 @@
class BaseTestWithUnroll(BaseTest):
- enable_opts = "intbounds:rewrite:virtualize:string:heap:unroll"
+ enable_opts = "intbounds:rewrite:virtualize:string:earlyforce:pure:heap:unroll"
def optimize_loop(self, ops, expected, expected_preamble=None,
call_pure_results=None, expected_short=None):
@@ -234,6 +234,30 @@
""" % expected_value
self.optimize_loop(ops, expected)
+ def test_reverse_of_cast(self):
+ ops = """
+ [i0]
+ p0 = cast_int_to_ptr(i0)
+ i1 = cast_ptr_to_int(p0)
+ jump(i1)
+ """
+ expected = """
+ [i0]
+ jump(i0)
+ """
+ self.optimize_loop(ops, expected)
+ ops = """
+ [p0]
+ i1 = cast_ptr_to_int(p0)
+ p1 = cast_int_to_ptr(i1)
+ jump(p1)
+ """
+ expected = """
+ [p0]
+ jump(p0)
+ """
+ self.optimize_loop(ops, expected)
+
# ----------
def test_remove_guard_class_1(self):
@@ -825,8 +849,8 @@
i3 = getfield_gc(p2, descr=valuedescr)
escape(i3)
p4 = new_with_vtable(ConstClass(node_vtable))
+ p1sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p4, i1, descr=valuedescr)
- p1sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p1sub, i1, descr=valuedescr)
setfield_gc(p4, p1sub, descr=nextdescr)
jump(i1, p4)
@@ -865,13 +889,7 @@
p3sub = new_with_vtable(ConstClass(node_vtable2))
setfield_gc(p3sub, i1, descr=valuedescr)
setfield_gc(p1, p3sub, descr=nextdescr)
- # XXX: We get two extra operations here because the setfield
- # above is the result of forcing p1 and thus not
- # registered with the heap optimizer. I've makred tests
- # below with VIRTUALHEAP if they suffer from this issue
- p3sub2 = getfield_gc(p1, descr=nextdescr)
- guard_nonnull_class(p3sub2, ConstClass(node_vtable2)) []
- jump(i1, p1, p3sub2)
+ jump(i1, p1, p3sub)
"""
self.optimize_loop(ops, expected, preamble)
@@ -902,9 +920,7 @@
guard_true(i2b) []
p3 = new_with_vtable(ConstClass(node_vtable))
setfield_gc(p3, i2, descr=nextdescr)
- # XXX: VIRTUALHEAP (see above)
- i3 = getfield_gc(p3, descr=nextdescr)
- jump(p3, i3)
+ jump(p3, i2)
"""
self.optimize_loop(ops, expected, preamble)
@@ -1219,7 +1235,15 @@
setfield_gc(p3, p30, descr=valuedescr)
jump(i29, p30, p3)
"""
- expected = preamble
+ expected = """
+ [i0, p1, p3]
+ i28 = int_add(i0, 1)
+ i29 = int_add(i28, 1)
+ p30 = new_with_vtable(ConstClass(node_vtable))
+ setfield_gc(p3, p30, descr=valuedescr)
+ setfield_gc(p30, i28, descr=nextdescr)
+ jump(i29, p30, p3)
+ """
self.optimize_loop(ops, expected, preamble)
def test_nonvirtual_1(self):
@@ -2408,8 +2432,8 @@
guard_class(p2, ConstClass(node_vtable)) []
p3 = getfield_gc(p1, descr=otherdescr)
guard_class(p3, ConstClass(node_vtable)) []
+ p3a = new_with_vtable(ConstClass(node_vtable))
setfield_gc(p3, p2, descr=otherdescr)
- p3a = new_with_vtable(ConstClass(node_vtable))
escape(p3a)
jump(p3a)
"""
@@ -2421,9 +2445,9 @@
# setfield_gc(p3, p2, descr=otherdescr) # p3a.other = p2a
# p1a = new_with_vtable(ConstClass(node_vtable2))
# p2a = new_with_vtable(ConstClass(node_vtable))
+ p3anew = new_with_vtable(ConstClass(node_vtable))
p2 = new_with_vtable(ConstClass(node_vtable))
setfield_gc(p3a, p2, descr=otherdescr) # p3a.other = p2a
- p3anew = new_with_vtable(ConstClass(node_vtable))
escape(p3anew)
jump(p3anew)
"""
@@ -2458,9 +2482,9 @@
p3 = getfield_gc(p1, descr=otherdescr)
guard_class(p3, ConstClass(node_vtable)) []
# p1a = new_with_vtable(ConstClass(node_vtable2))
+ p3a = new_with_vtable(ConstClass(node_vtable))
p2a = new_with_vtable(ConstClass(node_vtable))
setfield_gc(p3, p2a, descr=otherdescr)
- p3a = new_with_vtable(ConstClass(node_vtable))
escape(p3a)
# setfield_gc(p1a, p2a, descr=nextdescr)
# setfield_gc(p1a, p3a, descr=otherdescr)
@@ -2468,9 +2492,9 @@
"""
expected = """
[p2, p3]
+ p3a = new_with_vtable(ConstClass(node_vtable))
p2a = new_with_vtable(ConstClass(node_vtable))
setfield_gc(p3, p2a, descr=otherdescr)
- p3a = new_with_vtable(ConstClass(node_vtable))
escape(p3a)
jump(p2a, p3a)
"""
@@ -2790,7 +2814,6 @@
self.optimize_loop(ops, expected, preamble)
def test_remove_duplicate_pure_op_ovf_with_lazy_setfield(self):
- py.test.skip('this optimization is not yet supprted')
ops = """
[i1, p1]
i3 = int_add_ovf(i1, 1)
@@ -5961,13 +5984,18 @@
escape(i0)
jump(p1)
"""
- expected = """
+ preamble = """
[p1]
i0 = ptr_eq(p1, NULL)
escape(i0)
- jump(p1)
- """
- self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
+ jump(p1, i0)
+ """
+ expected = """
+ [p1, i0]
+ escape(i0)
+ jump(p1, i0)
+ """
+ self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
def test_str_equal_none2(self):
ops = """
@@ -5976,13 +6004,18 @@
escape(i0)
jump(p1)
"""
- expected = """
+ preamble = """
[p1]
i0 = ptr_eq(p1, NULL)
escape(i0)
- jump(p1)
- """
- self.optimize_strunicode_loop_extradescrs(ops, expected, expected)
+ jump(p1, i0)
+ """
+ expected = """
+ [p1, i0]
+ escape(i0)
+ jump(p1, i0)
+ """
+ self.optimize_strunicode_loop_extradescrs(ops, expected, preamble)
def test_str_equal_nonnull1(self):
ops = """
@@ -7220,6 +7253,60 @@
"""
self.optimize_loop(ops, expected)
+ def test_heap_cache_forced_virtuals(self):
+ ops = """
+ [i1, i2, p0]
+ p1 = new(descr=ssize)
+ setfield_gc(p1, i1, descr=adescr)
+ setfield_gc(p1, i2, descr=bdescr)
+ call(p0, p1, descr=writeadescr)
+ i3 = getfield_gc(p1, descr=adescr)
+ i4 = getfield_gc(p1, descr=bdescr)
+ jump(i3, i4, p0)
+ """
+ expected = """
+ [i1, i2, p0]
+ p1 = new(descr=ssize)
+ setfield_gc(p1, i1, descr=adescr)
+ call(p0, p1, descr=writeadescr)
+ i3 = getfield_gc(p1, descr=adescr)
+ setfield_gc(p1, i2, descr=bdescr)
+ jump(i3, i2, p0)
+ """
+ self.optimize_loop(ops, expected)
+
+
+ def test_setarrayitem_followed_by_arraycopy(self):
+ ops = """
+ [p1, p2]
+ setarrayitem_gc(p1, 2, 10, descr=arraydescr)
+ setarrayitem_gc(p2, 3, 13, descr=arraydescr)
+ call(0, p1, p2, 0, 0, 10, descr=arraycopydescr)
+ jump(p1, p2)
+ """
+ self.optimize_loop(ops, ops)
+
+ def test_heap_cache_virtuals_forced_by_delayed_setfield(self):
+ py.test.skip('not yet supoprted')
+ ops = """
+ [i1, p0]
+ p1 = new(descr=ssize)
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p0, p1, descr=adescr)
+ call(p0, descr=writeadescr)
+ i2 = getfield_gc(p1, descr=valuedescr)
+ jump(i2, p0)
+ """
+ expected = """
+ [i1, p0]
+ p1 = new(descr=ssize)
+ setfield_gc(p1, i1, descr=valuedescr)
+ setfield_gc(p0, p1, descr=adescr)
+ call(p0, descr=writeadescr)
+ jump(i1, p0)
+ """
+ self.optimize_loop(ops, expected)
+
class TestLLtype(OptimizeOptTest, LLtypeMixin):
pass
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -182,7 +182,8 @@
EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE,
can_invalidate=True))
arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_ARRAYCOPY))
+ EffectInfo([], [arraydescr], [], [arraydescr],
+ oopspecindex=EffectInfo.OS_ARRAYCOPY))
for _name, _os in [
('strconcatdescr', 'OS_STR_CONCAT'),
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -75,7 +75,6 @@
self.importable_values = {}
self.emitting_dissabled = False
self.emitted_guards = 0
- self.emitted_pure_operations = {}
def ensure_imported(self, value):
if not self.emitting_dissabled and value in self.importable_values:
@@ -96,21 +95,6 @@
new = UnrollableOptimizer(self.metainterp_sd, self.loop)
return self._new(new)
- def remember_emitting_pure(self, op):
- self.emitted_pure_operations[op] = True
-
- def produce_potential_short_preamble_ops(self, sb):
- for op in self.emitted_pure_operations:
- if op.getopnum() == rop.GETARRAYITEM_GC_PURE or \
- op.getopnum() == rop.STRGETITEM or \
- op.getopnum() == rop.UNICODEGETITEM:
- if not self.getvalue(op.getarg(1)).is_constant():
- continue
- sb.add_potential(op)
- for opt in self.optimizations:
- opt.produce_potential_short_preamble_ops(sb)
-
-
class UnrollOptimizer(Optimization):
"""Unroll the loop into two iterations. The first one will
@@ -154,7 +138,7 @@
KillHugeIntBounds(self.optimizer).apply()
- loop.preamble.operations = self.optimizer.newoperations
+ loop.preamble.operations = self.optimizer.get_newoperations()
jump_args = [self.getvalue(a).get_key_box() for a in jump_args]
start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable()
@@ -167,8 +151,9 @@
virtual_state = modifier.get_virtual_state(jump_args)
values = [self.getvalue(arg) for arg in jump_args]
- inputargs = virtual_state.make_inputargs(values)
- short_inputargs = virtual_state.make_inputargs(values, keyboxes=True)
+ inputargs = virtual_state.make_inputargs(values, self.optimizer)
+ short_inputargs = virtual_state.make_inputargs(values, self.optimizer,
+ keyboxes=True)
self.constant_inputargs = {}
for box in jump_args:
@@ -197,7 +182,7 @@
# operations needed to setup the proper state of those virtuals
# in the peeled loop
inputarg_setup_ops = []
- preamble_optimizer.newoperations = []
+ preamble_optimizer.clear_newoperations()
seen = {}
for box in inputargs:
if box in seen:
@@ -211,9 +196,8 @@
continue
seen[box] = True
value = preamble_optimizer.getvalue(box)
- value.force_box()
- preamble_optimizer.flush()
- inputarg_setup_ops += preamble_optimizer.newoperations
+ value.force_box(preamble_optimizer)
+ inputarg_setup_ops += preamble_optimizer.get_newoperations()
# Setup the state of the new optimizer by emiting the
# short preamble operations and discarding the result
@@ -244,13 +228,13 @@
virtual_state)
loop.inputargs = inputargs
- args = [preamble_optimizer.getvalue(self.short_boxes.original(a)).force_box()\
+ args = [preamble_optimizer.getvalue(self.short_boxes.original(a)).force_box(preamble_optimizer)\
for a in inputargs]
jmp = ResOperation(rop.JUMP, args, None)
jmp.setdescr(loop.token)
loop.preamble.operations.append(jmp)
- loop.operations = self.optimizer.newoperations
+ loop.operations = self.optimizer.get_newoperations()
maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards
if self.optimizer.emitted_guards > maxguards:
@@ -335,9 +319,10 @@
assert jumpop
original_jumpargs = jumpop.getarglist()[:]
values = [self.getvalue(arg) for arg in jumpop.getarglist()]
- jumpargs = virtual_state.make_inputargs(values)
+ jumpargs = virtual_state.make_inputargs(values, self.optimizer)
jumpop.initarglist(jumpargs)
- jmp_to_short_args = virtual_state.make_inputargs(values, keyboxes=True)
+ jmp_to_short_args = virtual_state.make_inputargs(values, self.optimizer,
+ keyboxes=True)
self.short_inliner = Inliner(short_inputargs, jmp_to_short_args)
for box, const in self.constant_inputargs.items():
@@ -347,11 +332,11 @@
newop = self.short_inliner.inline_op(op)
self.optimizer.send_extra_operation(newop)
- self.optimizer.flush()
+ newoperations = self.optimizer.get_newoperations()
i = j = 0
- while i < len(self.optimizer.newoperations) or j < len(jumpargs):
- if i == len(self.optimizer.newoperations):
+ while i < len(newoperations) or j < len(jumpargs):
+ if i == len(newoperations):
while j < len(jumpargs):
a = jumpargs[j]
if self.optimizer.loop.logops:
@@ -360,7 +345,7 @@
jumpargs, short_seen)
j += 1
else:
- op = self.optimizer.newoperations[i]
+ op = newoperations[i]
self.boxes_created_this_iteration[op.result] = True
args = op.getarglist()
@@ -375,6 +360,7 @@
self.import_box(a, inputargs, short, short_jumpargs,
jumpargs, short_seen)
i += 1
+ newoperations = self.optimizer.get_newoperations()
jumpop.initarglist(jumpargs)
self.optimizer.send_extra_operation(jumpop)
@@ -468,7 +454,7 @@
inputargs.append(box)
box = newresult
if box in self.optimizer.values:
- box = self.optimizer.values[box].force_box()
+ box = self.optimizer.values[box].force_box(self.optimizer)
jumpargs.append(box)
@@ -483,11 +469,6 @@
if op.getopnum() == rop.JUMP:
loop_token = op.getdescr()
assert isinstance(loop_token, LoopToken)
- # FIXME: Use a tree, similar to the tree formed by the full
- # preamble and it's bridges, instead of a list to save time and
- # memory. This should also allow better behaviour in
- # situations that the is_emittable() chain currently cant
- # handle and the inlining fails unexpectedly belwo.
short = loop_token.short_preamble
if short:
args = op.getarglist()
@@ -523,7 +504,7 @@
values = [self.getvalue(arg)
for arg in op.getarglist()]
- args = sh.virtual_state.make_inputargs(values,
+ args = sh.virtual_state.make_inputargs(values, self.optimizer,
keyboxes=True)
inliner = Inliner(sh.inputargs, args)
diff --git a/pypy/jit/metainterp/optimizeopt/util.py b/pypy/jit/metainterp/optimizeopt/util.py
--- a/pypy/jit/metainterp/optimizeopt/util.py
+++ b/pypy/jit/metainterp/optimizeopt/util.py
@@ -90,14 +90,11 @@
for i in range(len(args1)):
arg1 = args1[i]
arg2 = args2[i]
- if isinstance(arg1, history.Const):
- if arg1.__class__ is not arg2.__class__:
+ if arg1 is None:
+ if arg2 is not None:
return False
- if not arg1.same_constant(arg2):
- return False
- else:
- if not arg1 is arg2:
- return False
+ elif not arg1.same_box(arg2):
+ return False
return True
def args_hash(args):
@@ -106,10 +103,8 @@
for arg in args:
if arg is None:
y = 17
- elif isinstance(arg, history.Const):
+ else:
y = arg._get_hash_()
- else:
- y = compute_identity_hash(arg)
res = intmask((1000003 * res) ^ y)
return res
@@ -145,9 +140,12 @@
for i in range(op1.numargs()):
x = op1.getarg(i)
y = op2.getarg(i)
- assert x == remap.get(y, y)
+ assert x.same_box(remap.get(y, y))
if op2.result in remap:
- assert op1.result == remap[op2.result]
+ if op2.result is None:
+ assert op1.result == remap[op2.result]
+ else:
+ assert op1.result.same_box(remap[op2.result])
else:
remap[op2.result] = op1.result
if op1.getopnum() != rop.JUMP: # xxx obscure
@@ -156,11 +154,20 @@
assert len(op1.getfailargs()) == len(op2.getfailargs())
if strict_fail_args:
for x, y in zip(op1.getfailargs(), op2.getfailargs()):
- assert x == remap.get(y, y)
+ if x is None:
+ assert remap.get(y, y) is None
+ else:
+ assert x.same_box(remap.get(y, y))
else:
fail_args1 = set(op1.getfailargs())
fail_args2 = set([remap.get(y, y) for y in op2.getfailargs()])
- assert fail_args1 == fail_args2
+ for x in fail_args1:
+ for y in fail_args2:
+ if x.same_box(y):
+ fail_args2.remove(y)
+ break
+ else:
+ assert False
assert len(oplist1) == len(oplist2)
print '-'*totwidth
return True
diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -10,13 +10,12 @@
class AbstractVirtualValue(optimizer.OptValue):
- _attrs_ = ('optimizer', 'keybox', 'source_op', '_cached_vinfo')
+ _attrs_ = ('keybox', 'source_op', '_cached_vinfo')
box = None
level = optimizer.LEVEL_NONNULL
_cached_vinfo = None
- def __init__(self, optimizer, keybox, source_op=None):
- self.optimizer = optimizer
+ def __init__(self, keybox, source_op=None):
self.keybox = keybox # only used as a key in dictionaries
self.source_op = source_op # the NEW_WITH_VTABLE/NEW_ARRAY operation
# that builds this box
@@ -29,17 +28,17 @@
return self.keybox
return self.box
- def force_box(self):
+ def force_box(self, optforce):
if self.box is None:
- self.optimizer.forget_numberings(self.keybox)
- self._really_force()
+ optforce.forget_numberings(self.keybox)
+ self._really_force(optforce)
return self.box
- def force_at_end_of_preamble(self, already_forced):
+ def force_at_end_of_preamble(self, already_forced, optforce):
value = already_forced.get(self, None)
if value:
return value
- return OptValue(self.force_box())
+ return OptValue(self.force_box(optforce))
def make_virtual_info(self, modifier, fieldnums):
if fieldnums is None:
@@ -55,7 +54,7 @@
def _make_virtual(self, modifier):
raise NotImplementedError("abstract base")
- def _really_force(self):
+ def _really_force(self, optforce):
raise NotImplementedError("abstract base")
def import_from(self, other, optimizer):
@@ -70,10 +69,11 @@
get_fielddescrlist_cache._annspecialcase_ = "specialize:memo"
class AbstractVirtualStructValue(AbstractVirtualValue):
- _attrs_ = ('_fields', '_cached_sorted_fields')
+ _attrs_ = ('_fields', 'cpu', '_cached_sorted_fields')
- def __init__(self, optimizer, keybox, source_op=None):
- AbstractVirtualValue.__init__(self, optimizer, keybox, source_op)
+ def __init__(self, cpu, keybox, source_op=None):
+ AbstractVirtualValue.__init__(self, keybox, source_op)
+ self.cpu = cpu
self._fields = {}
self._cached_sorted_fields = None
@@ -87,45 +87,44 @@
def _get_descr(self):
raise NotImplementedError
- def _is_immutable_and_filled_with_constants(self):
+ def _is_immutable_and_filled_with_constants(self, optforce):
count = self._get_descr().count_fields_if_immutable()
if count != len(self._fields): # always the case if count == -1
return False
for value in self._fields.itervalues():
- subbox = value.force_box()
+ subbox = value.force_box(optforce)
if not isinstance(subbox, Const):
return False
return True
- def force_at_end_of_preamble(self, already_forced):
+ def force_at_end_of_preamble(self, already_forced, optforce):
if self in already_forced:
return self
already_forced[self] = self
if self._fields:
for ofs in self._fields.keys():
- self._fields[ofs] = self._fields[ofs].force_at_end_of_preamble(already_forced)
+ self._fields[ofs] = self._fields[ofs].force_at_end_of_preamble(already_forced, optforce)
return self
- def _really_force(self):
+ def _really_force(self, optforce):
op = self.source_op
assert op is not None
# ^^^ This case should not occur any more (see test_bug_3).
#
if not we_are_translated():
op.name = 'FORCE ' + self.source_op.name
-
- if self._is_immutable_and_filled_with_constants():
- box = self.optimizer.constant_fold(op)
+
+ if self._is_immutable_and_filled_with_constants(optforce):
+ box = optforce.optimizer.constant_fold(op)
self.make_constant(box)
for ofs, value in self._fields.iteritems():
- subbox = value.force_box()
+ subbox = value.force_box(optforce)
assert isinstance(subbox, Const)
- execute(self.optimizer.cpu, None, rop.SETFIELD_GC,
+ execute(optforce.optimizer.cpu, None, rop.SETFIELD_GC,
ofs, box, subbox)
# keep self._fields, because it's all immutable anyway
else:
- newoperations = self.optimizer.newoperations
- newoperations.append(op)
+ optforce.emit_operation(op)
self.box = box = op.result
#
iteritems = self._fields.iteritems()
@@ -135,10 +134,11 @@
for ofs, value in iteritems:
if value.is_null():
continue
- subbox = value.force_box()
+ subbox = value.force_box(optforce)
op = ResOperation(rop.SETFIELD_GC, [box, subbox], None,
descr=ofs)
- newoperations.append(op)
+
+ optforce.emit_operation(op)
def _get_field_descr_list(self):
_cached_sorted_fields = self._cached_sorted_fields
@@ -155,7 +155,7 @@
else:
lst = self._fields.keys()
sort_descrs(lst)
- cache = get_fielddescrlist_cache(self.optimizer.cpu)
+ cache = get_fielddescrlist_cache(self.cpu)
result = cache.get(lst, None)
if result is None:
cache[lst] = lst
@@ -180,8 +180,8 @@
class VirtualValue(AbstractVirtualStructValue):
level = optimizer.LEVEL_KNOWNCLASS
- def __init__(self, optimizer, known_class, keybox, source_op=None):
- AbstractVirtualStructValue.__init__(self, optimizer, keybox, source_op)
+ def __init__(self, cpu, known_class, keybox, source_op=None):
+ AbstractVirtualStructValue.__init__(self, cpu, keybox, source_op)
assert isinstance(known_class, Const)
self.known_class = known_class
@@ -190,7 +190,7 @@
return modifier.make_virtual(self.known_class, fielddescrs)
def _get_descr(self):
- return vtable2descr(self.optimizer.cpu, self.known_class.getint())
+ return vtable2descr(self.cpu, self.known_class.getint())
def __repr__(self):
cls_name = self.known_class.value.adr.ptr._obj._TYPE._name
@@ -201,8 +201,8 @@
class VStructValue(AbstractVirtualStructValue):
- def __init__(self, optimizer, structdescr, keybox, source_op=None):
- AbstractVirtualStructValue.__init__(self, optimizer, keybox, source_op)
+ def __init__(self, cpu, structdescr, keybox, source_op=None):
+ AbstractVirtualStructValue.__init__(self, cpu, keybox, source_op)
self.structdescr = structdescr
def _make_virtual(self, modifier):
@@ -215,10 +215,10 @@
class VArrayValue(AbstractVirtualValue):
- def __init__(self, optimizer, arraydescr, size, keybox, source_op=None):
- AbstractVirtualValue.__init__(self, optimizer, keybox, source_op)
+ def __init__(self, arraydescr, constvalue, size, keybox, source_op=None):
+ AbstractVirtualValue.__init__(self, keybox, source_op)
self.arraydescr = arraydescr
- self.constvalue = optimizer.new_const_item(arraydescr)
+ self.constvalue = constvalue
self._items = [self.constvalue] * size
def getlength(self):
@@ -232,31 +232,30 @@
assert isinstance(itemvalue, optimizer.OptValue)
self._items[index] = itemvalue
- def force_at_end_of_preamble(self, already_forced):
+ def force_at_end_of_preamble(self, already_forced, optforce):
if self in already_forced:
return self
already_forced[self] = self
for index in range(len(self._items)):
- self._items[index] = self._items[index].force_at_end_of_preamble(already_forced)
+ self._items[index] = self._items[index].force_at_end_of_preamble(already_forced, optforce)
return self
- def _really_force(self):
+ def _really_force(self, optforce):
assert self.source_op is not None
if not we_are_translated():
self.source_op.name = 'FORCE ' + self.source_op.name
- newoperations = self.optimizer.newoperations
- newoperations.append(self.source_op)
+ optforce.emit_operation(self.source_op)
self.box = box = self.source_op.result
for index in range(len(self._items)):
subvalue = self._items[index]
if subvalue is not self.constvalue:
if subvalue.is_null():
continue
- subbox = subvalue.force_box()
+ subbox = subvalue.force_box(optforce)
op = ResOperation(rop.SETARRAYITEM_GC,
[box, ConstInt(index), subbox], None,
descr=self.arraydescr)
- newoperations.append(op)
+ optforce.emit_operation(op)
def get_args_for_fail(self, modifier):
if self.box is None and not modifier.already_seen_virtual(self.keybox):
@@ -279,17 +278,18 @@
return OptVirtualize()
def make_virtual(self, known_class, box, source_op=None):
- vvalue = VirtualValue(self.optimizer, known_class, box, source_op)
+ vvalue = VirtualValue(self.optimizer.cpu, known_class, box, source_op)
self.make_equal_to(box, vvalue)
return vvalue
def make_varray(self, arraydescr, size, box, source_op=None):
- vvalue = VArrayValue(self.optimizer, arraydescr, size, box, source_op)
+ constvalue = self.new_const_item(arraydescr)
+ vvalue = VArrayValue(arraydescr, constvalue, size, box, source_op)
self.make_equal_to(box, vvalue)
return vvalue
def make_vstruct(self, structdescr, box, source_op=None):
- vvalue = VStructValue(self.optimizer, structdescr, box, source_op)
+ vvalue = VStructValue(self.optimizer.cpu, structdescr, box, source_op)
self.make_equal_to(box, vvalue)
return vvalue
@@ -362,7 +362,6 @@
self.make_equal_to(op.result, fieldvalue)
else:
value.ensure_nonnull()
- ###self.heap_op_optimizer.optimize_GETFIELD_GC(op, value)
self.emit_operation(op)
# note: the following line does not mean that the two operations are
@@ -377,7 +376,6 @@
value.setfield(op.getdescr(), fieldvalue)
else:
value.ensure_nonnull()
- ###self.heap_op_optimizer.optimize_SETFIELD_GC(op, value, fieldvalue)
self.emit_operation(op)
def optimize_NEW_WITH_VTABLE(self, op):
@@ -417,7 +415,6 @@
self.make_equal_to(op.result, itemvalue)
return
value.ensure_nonnull()
- ###self.heap_op_optimizer.optimize_GETARRAYITEM_GC(op, value)
self.emit_operation(op)
# note: the following line does not mean that the two operations are
@@ -432,7 +429,6 @@
value.setitem(indexbox.getint(), self.getvalue(op.getarg(2)))
return
value.ensure_nonnull()
- ###self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
self.emit_operation(op)
diff --git a/pypy/jit/metainterp/optimizeopt/virtualstate.py b/pypy/jit/metainterp/optimizeopt/virtualstate.py
--- a/pypy/jit/metainterp/optimizeopt/virtualstate.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualstate.py
@@ -30,7 +30,7 @@
def _generate_guards(self, other, box, cpu, extra_guards):
raise InvalidLoop
- def enum_forced_boxes(self, boxes, value):
+ def enum_forced_boxes(self, boxes, value, optimizer):
raise NotImplementedError
def enum(self, virtual_state):
@@ -100,14 +100,14 @@
def _generalization_of(self, other):
raise NotImplementedError
- def enum_forced_boxes(self, boxes, value):
+ def enum_forced_boxes(self, boxes, value, optimizer):
assert isinstance(value, virtualize.AbstractVirtualStructValue)
assert value.is_virtual()
for i in range(len(self.fielddescrs)):
v = value._fields[self.fielddescrs[i]]
s = self.fieldstate[i]
if s.position > self.position:
- s.enum_forced_boxes(boxes, v)
+ s.enum_forced_boxes(boxes, v, optimizer)
def _enum(self, virtual_state):
for s in self.fieldstate:
@@ -177,14 +177,14 @@
return False
return True
- def enum_forced_boxes(self, boxes, value):
+ def enum_forced_boxes(self, boxes, value, optimizer):
assert isinstance(value, virtualize.VArrayValue)
assert value.is_virtual()
for i in range(len(self.fieldstate)):
v = value._items[i]
s = self.fieldstate[i]
if s.position > self.position:
- s.enum_forced_boxes(boxes, v)
+ s.enum_forced_boxes(boxes, v, optimizer)
def _enum(self, virtual_state):
for s in self.fieldstate:
@@ -316,11 +316,11 @@
import pdb; pdb.set_trace()
raise NotImplementedError
- def enum_forced_boxes(self, boxes, value):
+ def enum_forced_boxes(self, boxes, value, optimizer):
if self.level == LEVEL_CONSTANT:
return
assert 0 <= self.position_in_notvirtuals
- boxes[self.position_in_notvirtuals] = value.force_box()
+ boxes[self.position_in_notvirtuals] = value.force_box(optimizer)
def _enum(self, virtual_state):
if self.level == LEVEL_CONSTANT:
@@ -377,11 +377,13 @@
self.state[i].generate_guards(other.state[i], args[i],
cpu, extra_guards, renum)
- def make_inputargs(self, values, keyboxes=False):
+ def make_inputargs(self, values, optimizer, keyboxes=False):
+ if optimizer.optearlyforce:
+ optimizer = optimizer.optearlyforce
assert len(values) == len(self.state)
inputargs = [None] * len(self.notvirtuals)
for i in range(len(values)):
- self.state[i].enum_forced_boxes(inputargs, values[i])
+ self.state[i].enum_forced_boxes(inputargs, values[i], optimizer)
if keyboxes:
for i in range(len(values)):
@@ -434,7 +436,12 @@
def get_virtual_state(self, jump_args):
self.optimizer.force_at_end_of_preamble()
already_forced = {}
- values = [self.getvalue(box).force_at_end_of_preamble(already_forced)
+ if self.optimizer.optearlyforce:
+ opt = self.optimizer.optearlyforce
+ else:
+ opt = self.optimizer
+ values = [self.getvalue(box).force_at_end_of_preamble(already_forced,
+ opt)
for box in jump_args]
for value in values:
diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/vstring.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -43,7 +43,7 @@
class __extend__(optimizer.OptValue):
"""New methods added to the base class OptValue for this file."""
- def getstrlen(self, optimization, mode):
+ def getstrlen(self, string_optimizer, mode):
if mode is mode_string:
s = self.get_constant_string_spec(mode_string)
if s is not None:
@@ -52,12 +52,12 @@
s = self.get_constant_string_spec(mode_unicode)
if s is not None:
return ConstInt(len(s))
- if optimization is None:
+ if string_optimizer is None:
return None
self.ensure_nonnull()
- box = self.force_box()
+ box = self.force_box(string_optimizer)
lengthbox = BoxInt()
- optimization.propagate_forward(ResOperation(mode.STRLEN, [box], lengthbox))
+ string_optimizer.emit_operation(ResOperation(mode.STRLEN, [box], lengthbox))
return lengthbox
@specialize.arg(1)
@@ -68,25 +68,25 @@
else:
return None
- def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
+ def string_copy_parts(self, string_optimizer, targetbox, offsetbox, mode):
# Copies the pointer-to-string 'self' into the target string
# given by 'targetbox', at the specified offset. Returns the offset
# at the end of the copy.
- lengthbox = self.getstrlen(optimizer, mode)
- srcbox = self.force_box()
- return copy_str_content(optimizer, srcbox, targetbox,
+ lengthbox = self.getstrlen(string_optimizer, mode)
+ srcbox = self.force_box(string_optimizer)
+ return copy_str_content(string_optimizer, srcbox, targetbox,
CONST_0, offsetbox, lengthbox, mode)
class VAbstractStringValue(virtualize.AbstractVirtualValue):
_attrs_ = ('mode',)
- def __init__(self, optimizer, keybox, source_op, mode):
- virtualize.AbstractVirtualValue.__init__(self, optimizer, keybox,
+ def __init__(self, keybox, source_op, mode):
+ virtualize.AbstractVirtualValue.__init__(self, keybox,
source_op)
self.mode = mode
- def _really_force(self):
+ def _really_force(self, optforce):
if self.mode is mode_string:
s = self.get_constant_string_spec(mode_string)
if s is not None:
@@ -101,12 +101,12 @@
return
assert self.source_op is not None
self.box = box = self.source_op.result
- lengthbox = self.getstrlen(self.optimizer, self.mode)
+ lengthbox = self.getstrlen(optforce, self.mode)
op = ResOperation(self.mode.NEWSTR, [lengthbox], box)
if not we_are_translated():
op.name = 'FORCE'
- self.optimizer.emit_operation(op)
- self.string_copy_parts(self.optimizer, box, CONST_0, self.mode)
+ optforce.emit_operation(op)
+ self.string_copy_parts(optforce, box, CONST_0, self.mode)
class VStringPlainValue(VAbstractStringValue):
@@ -140,20 +140,20 @@
return mode.emptystr.join([mode.chr(c.box.getint())
for c in self._chars])
- def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
+ def string_copy_parts(self, string_optimizer, targetbox, offsetbox, mode):
if not self.is_virtual() and targetbox is not self.box:
- lengthbox = self.getstrlen(optimizer, mode)
- srcbox = self.force_box()
- return copy_str_content(optimizer, srcbox, targetbox,
+ lengthbox = self.getstrlen(string_optimizer, mode)
+ srcbox = self.force_box(string_optimizer)
+ return copy_str_content(string_optimizer, srcbox, targetbox,
CONST_0, offsetbox, lengthbox, mode)
for i in range(len(self._chars)):
- charbox = self._chars[i].force_box()
+ charbox = self._chars[i].force_box(string_optimizer)
if not (isinstance(charbox, Const) and charbox.same_constant(CONST_0)):
- optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
- offsetbox,
- charbox],
+ string_optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
+ offsetbox,
+ charbox],
None))
- offsetbox = _int_add(optimizer, offsetbox, CONST_1)
+ offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
return offsetbox
def get_args_for_fail(self, modifier):
@@ -187,16 +187,16 @@
self.left = left
self.right = right
- def getstrlen(self, optimizer, mode):
+ def getstrlen(self, string_optimizer, mode):
if self.lengthbox is None:
- len1box = self.left.getstrlen(optimizer, mode)
+ len1box = self.left.getstrlen(string_optimizer, mode)
if len1box is None:
return None
- len2box = self.right.getstrlen(optimizer, mode)
+ len2box = self.right.getstrlen(string_optimizer, mode)
if len2box is None:
return None
- self.lengthbox = _int_add(optimizer, len1box, len2box)
- # ^^^ may still be None, if optimizer is None
+ self.lengthbox = _int_add(string_optimizer, len1box, len2box)
+ # ^^^ may still be None, if string_optimizer is None
return self.lengthbox
@specialize.arg(1)
@@ -209,10 +209,10 @@
return None
return s1 + s2
- def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
- offsetbox = self.left.string_copy_parts(optimizer, targetbox,
+ def string_copy_parts(self, string_optimizer, targetbox, offsetbox, mode):
+ offsetbox = self.left.string_copy_parts(string_optimizer, targetbox,
offsetbox, mode)
- offsetbox = self.right.string_copy_parts(optimizer, targetbox,
+ offsetbox = self.right.string_copy_parts(string_optimizer, targetbox,
offsetbox, mode)
return offsetbox
@@ -251,8 +251,8 @@
self.vstart = vstart
self.vlength = vlength
- def getstrlen(self, _, mode):
- return self.vlength.force_box()
+ def getstrlen(self, optforce, mode):
+ return self.vlength.force_box(optforce)
@specialize.arg(1)
def get_constant_string_spec(self, mode):
@@ -267,11 +267,11 @@
return s1[start : start + length]
return None
- def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
- lengthbox = self.getstrlen(optimizer, mode)
- return copy_str_content(optimizer,
- self.vstr.force_box(), targetbox,
- self.vstart.force_box(), offsetbox,
+ def string_copy_parts(self, string_optimizer, targetbox, offsetbox, mode):
+ lengthbox = self.getstrlen(string_optimizer, mode)
+ return copy_str_content(string_optimizer,
+ self.vstr.force_box(string_optimizer), targetbox,
+ self.vstart.force_box(string_optimizer), offsetbox,
lengthbox, mode)
def get_args_for_fail(self, modifier):
@@ -300,7 +300,7 @@
return modifier.make_vstrslice(self.mode is mode_unicode)
-def copy_str_content(optimizer, srcbox, targetbox,
+def copy_str_content(string_optimizer, srcbox, targetbox,
srcoffsetbox, offsetbox, lengthbox, mode, need_next_offset=True):
if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
M = 5
@@ -310,26 +310,26 @@
# up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM
# instead of just a COPYSTRCONTENT.
for i in range(lengthbox.value):
- charbox = _strgetitem(optimizer, srcbox, srcoffsetbox, mode)
- srcoffsetbox = _int_add(optimizer, srcoffsetbox, CONST_1)
- optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
- offsetbox,
- charbox],
+ charbox = _strgetitem(string_optimizer, srcbox, srcoffsetbox, mode)
+ srcoffsetbox = _int_add(string_optimizer, srcoffsetbox, CONST_1)
+ string_optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
+ offsetbox,
+ charbox],
None))
- offsetbox = _int_add(optimizer, offsetbox, CONST_1)
+ offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
else:
if need_next_offset:
- nextoffsetbox = _int_add(optimizer, offsetbox, lengthbox)
+ nextoffsetbox = _int_add(string_optimizer, offsetbox, lengthbox)
else:
nextoffsetbox = None
op = ResOperation(mode.COPYSTRCONTENT, [srcbox, targetbox,
srcoffsetbox, offsetbox,
lengthbox], None)
- optimizer.emit_operation(op)
+ string_optimizer.emit_operation(op)
offsetbox = nextoffsetbox
return offsetbox
-def _int_add(optimizer, box1, box2):
+def _int_add(string_optimizer, box1, box2):
if isinstance(box1, ConstInt):
if box1.value == 0:
return box2
@@ -337,23 +337,23 @@
return ConstInt(box1.value + box2.value)
elif isinstance(box2, ConstInt) and box2.value == 0:
return box1
- if optimizer is None:
+ if string_optimizer is None:
return None
resbox = BoxInt()
- optimizer.propagate_forward(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+ string_optimizer.emit_operation(ResOperation(rop.INT_ADD, [box1, box2], resbox))
return resbox
-def _int_sub(optimizer, box1, box2):
+def _int_sub(string_optimizer, box1, box2):
if isinstance(box2, ConstInt):
if box2.value == 0:
return box1
if isinstance(box1, ConstInt):
return ConstInt(box1.value - box2.value)
resbox = BoxInt()
- optimizer.propagate_forward(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+ string_optimizer.emit_operation(ResOperation(rop.INT_SUB, [box1, box2], resbox))
return resbox
-def _strgetitem(optimizer, strbox, indexbox, mode):
+def _strgetitem(string_optimizer, strbox, indexbox, mode):
if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
if mode is mode_string:
s = strbox.getref(lltype.Ptr(rstr.STR))
@@ -362,30 +362,28 @@
s = strbox.getref(lltype.Ptr(rstr.UNICODE))
return ConstInt(ord(s.chars[indexbox.getint()]))
resbox = BoxInt()
- optimizer.propagate_forward(ResOperation(mode.STRGETITEM, [strbox, indexbox],
- resbox))
+ string_optimizer.emit_operation(ResOperation(mode.STRGETITEM, [strbox, indexbox],
+ resbox))
return resbox
class OptString(optimizer.Optimization):
"Handling of strings and unicodes."
- enabled = True
-
def new(self):
return OptString()
def make_vstring_plain(self, box, source_op, mode):
- vvalue = VStringPlainValue(self.optimizer, box, source_op, mode)
+ vvalue = VStringPlainValue(box, source_op, mode)
self.make_equal_to(box, vvalue)
return vvalue
def make_vstring_concat(self, box, source_op, mode):
- vvalue = VStringConcatValue(self.optimizer, box, source_op, mode)
+ vvalue = VStringConcatValue(box, source_op, mode)
self.make_equal_to(box, vvalue)
return vvalue
def make_vstring_slice(self, box, source_op, mode):
- vvalue = VStringSliceValue(self.optimizer, box, source_op, mode)
+ vvalue = VStringSliceValue(box, source_op, mode)
self.make_equal_to(box, vvalue)
return vvalue
@@ -435,9 +433,9 @@
value.ensure_nonnull()
#
if value.is_virtual() and isinstance(value, VStringSliceValue):
- fullindexbox = _int_add(self.optimizer,
- value.vstart.force_box(),
- vindex.force_box())
+ fullindexbox = _int_add(self,
+ value.vstart.force_box(self),
+ vindex.force_box(self))
value = value.vstr
vindex = self.getvalue(fullindexbox)
#
@@ -449,7 +447,7 @@
if res is not optimizer.CVAL_UNINITIALIZED_ZERO:
return res
#
- resbox = _strgetitem(self.optimizer, value.force_box(), vindex.force_box(), mode)
+ resbox = _strgetitem(self, value.force_box(self), vindex.force_box(self), mode)
return self.getvalue(resbox)
def optimize_STRLEN(self, op):
@@ -459,7 +457,7 @@
def _optimize_STRLEN(self, op, mode):
value = self.getvalue(op.getarg(0))
- lengthbox = value.getstrlen(self.optimizer, mode)
+ lengthbox = value.getstrlen(self, mode)
self.make_equal_to(op.result, self.getvalue(lengthbox))
def optimize_COPYSTRCONTENT(self, op):
@@ -477,12 +475,12 @@
if length.is_constant() and length.box.getint() == 0:
return
- copy_str_content(self.optimizer,
- src.force_box(),
- dst.force_box(),
- srcstart.force_box(),
- dststart.force_box(),
- length.force_box(),
+ copy_str_content(self,
+ src.force_box(self),
+ dst.force_box(self),
+ srcstart.force_box(self),
+ dststart.force_box(self),
+ length.force_box(self),
mode, need_next_offset=False
)
@@ -508,6 +506,8 @@
return
self.emit_operation(op)
+ optimize_CALL_PURE = optimize_CALL
+
def opt_call_str_STR2UNICODE(self, op):
# Constant-fold unicode("constant string").
# More generally, supporting non-constant but virtual cases is
@@ -547,16 +547,16 @@
return True
#
vstr.ensure_nonnull()
- lengthbox = _int_sub(self.optimizer, vstop.force_box(),
- vstart.force_box())
+ lengthbox = _int_sub(self, vstop.force_box(self),
+ vstart.force_box(self))
#
if isinstance(vstr, VStringSliceValue):
# double slicing s[i:j][k:l]
vintermediate = vstr
vstr = vintermediate.vstr
- startbox = _int_add(self.optimizer,
- vintermediate.vstart.force_box(),
- vstart.force_box())
+ startbox = _int_add(self,
+ vintermediate.vstart.force_box(self),
+ vstart.force_box(self))
vstart = self.getvalue(startbox)
#
value = self.make_vstring_slice(op.result, op, mode)
@@ -587,15 +587,12 @@
return True
#
if v1.is_nonnull() and v2.is_nonnull():
- if l1box is not None and l2box is not None and (
- l1box == l2box or (isinstance(l1box, ConstInt) and
- isinstance(l2box, ConstInt) and
- l1box.value == l2box.value)):
+ if l1box is not None and l2box is not None and l1box.same_box(l2box):
do = EffectInfo.OS_STREQ_LENGTHOK
else:
do = EffectInfo.OS_STREQ_NONNULL
- self.generate_modified_call(do, [v1.force_box(),
- v2.force_box()], op.result, mode)
+ self.generate_modified_call(do, [v1.force_box(self),
+ v2.force_box(self)], op.result, mode)
return True
return False
@@ -603,7 +600,7 @@
l2box = v2.getstrlen(None, mode)
if isinstance(l2box, ConstInt):
if l2box.value == 0:
- lengthbox = v1.getstrlen(self.optimizer, mode)
+ lengthbox = v1.getstrlen(self, mode)
seo = self.optimizer.send_extra_operation
seo(ResOperation(rop.INT_EQ, [lengthbox, CONST_0], resultbox))
return True
@@ -614,17 +611,17 @@
vchar1 = self.strgetitem(v1, optimizer.CVAL_ZERO, mode)
vchar2 = self.strgetitem(v2, optimizer.CVAL_ZERO, mode)
seo = self.optimizer.send_extra_operation
- seo(ResOperation(rop.INT_EQ, [vchar1.force_box(),
- vchar2.force_box()],
+ seo(ResOperation(rop.INT_EQ, [vchar1.force_box(self),
+ vchar2.force_box(self)],
resultbox))
return True
if isinstance(v1, VStringSliceValue):
vchar = self.strgetitem(v2, optimizer.CVAL_ZERO, mode)
do = EffectInfo.OS_STREQ_SLICE_CHAR
- self.generate_modified_call(do, [v1.vstr.force_box(),
- v1.vstart.force_box(),
- v1.vlength.force_box(),
- vchar.force_box()],
+ self.generate_modified_call(do, [v1.vstr.force_box(self),
+ v1.vstart.force_box(self),
+ v1.vlength.force_box(self),
+ vchar.force_box(self)],
resultbox, mode)
return True
#
@@ -635,10 +632,10 @@
if v1.is_null():
self.make_constant(resultbox, CONST_1)
return True
- op = ResOperation(rop.PTR_EQ, [v1.force_box(),
+ op = ResOperation(rop.PTR_EQ, [v1.force_box(self),
llhelper.CONST_NULL],
resultbox)
- self.optimizer.emit_operation(op)
+ self.emit_operation(op)
return True
#
return False
@@ -652,8 +649,8 @@
do = EffectInfo.OS_STREQ_NONNULL_CHAR
else:
do = EffectInfo.OS_STREQ_CHECKNULL_CHAR
- self.generate_modified_call(do, [v1.force_box(),
- vchar.force_box()], resultbox,
+ self.generate_modified_call(do, [v1.force_box(self),
+ vchar.force_box(self)], resultbox,
mode)
return True
#
@@ -662,10 +659,10 @@
do = EffectInfo.OS_STREQ_SLICE_NONNULL
else:
do = EffectInfo.OS_STREQ_SLICE_CHECKNULL
- self.generate_modified_call(do, [v1.vstr.force_box(),
- v1.vstart.force_box(),
- v1.vlength.force_box(),
- v2.force_box()], resultbox, mode)
+ self.generate_modified_call(do, [v1.vstr.force_box(self),
+ v1.vstart.force_box(self),
+ v1.vlength.force_box(self),
+ v2.force_box(self)], resultbox, mode)
return True
return False
@@ -675,16 +672,11 @@
calldescr, func = cic.callinfo_for_oopspec(oopspecindex)
op = ResOperation(rop.CALL, [ConstInt(func)] + args, result,
descr=calldescr)
- self.optimizer.emit_operation(op)
+ self.emit_operation(op)
def propagate_forward(self, op):
- if not self.enabled:
- self.emit_operation(op)
- return
-
dispatch_opt(self, op)
-
dispatch_opt = make_dispatcher_method(OptString, 'optimize_',
default=OptString.emit_operation)
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
@@ -162,15 +162,18 @@
if registers[i] is oldbox:
registers[i] = newbox
if not we_are_translated():
- assert oldbox not in registers[count:]
+ for b in registers[count:]:
+ assert not oldbox.same_box(b)
+
def make_result_of_lastop(self, resultbox):
got_type = resultbox.type
- if not we_are_translated():
- typeof = {'i': history.INT,
- 'r': history.REF,
- 'f': history.FLOAT}
- assert typeof[self.jitcode._resulttypes[self.pc]] == got_type
+ # XXX disabled for now, conflicts with str_guard_value
+ #if not we_are_translated():
+ # typeof = {'i': history.INT,
+ # 'r': history.REF,
+ # 'f': history.FLOAT}
+ # assert typeof[self.jitcode._resulttypes[self.pc]] == got_type
target_index = ord(self.bytecode[self.pc-1])
if got_type == history.INT:
self.registers_i[target_index] = resultbox
@@ -219,6 +222,7 @@
'cast_float_to_int', 'cast_int_to_float',
'cast_float_to_singlefloat', 'cast_singlefloat_to_float',
'float_neg', 'float_abs',
+ 'cast_ptr_to_int', 'cast_int_to_ptr',
]:
exec py.code.Source('''
@arguments("box")
@@ -895,6 +899,21 @@
def _opimpl_guard_value(self, orgpc, box):
self.implement_guard_value(orgpc, box)
+ @arguments("orgpc", "box", "box", "descr")
+ def opimpl_str_guard_value(self, orgpc, box, funcbox, descr):
+ if isinstance(box, Const):
+ return box # no promotion needed, already a Const
+ else:
+ constbox = box.constbox()
+ resbox = self.do_residual_call(funcbox, descr, [box, constbox])
+ promoted_box = resbox.constbox()
+ # This is GUARD_VALUE because GUARD_TRUE assumes the existance
+ # of a label when computing resumepc
+ self.generate_guard(rop.GUARD_VALUE, resbox, [promoted_box],
+ resumepc=orgpc)
+ self.metainterp.replace_box(box, constbox)
+ return constbox
+
opimpl_int_guard_value = _opimpl_guard_value
opimpl_ref_guard_value = _opimpl_guard_value
opimpl_float_guard_value = _opimpl_guard_value
diff --git a/pypy/jit/metainterp/quasiimmut.py b/pypy/jit/metainterp/quasiimmut.py
--- a/pypy/jit/metainterp/quasiimmut.py
+++ b/pypy/jit/metainterp/quasiimmut.py
@@ -2,6 +2,7 @@
from pypy.rpython.lltypesystem import lltype, rclass
from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
from pypy.jit.metainterp.history import AbstractDescr
+from pypy.rlib.objectmodel import we_are_translated
def get_mutate_field_name(fieldname):
@@ -50,13 +51,13 @@
class QuasiImmut(object):
llopaque = True
+ compress_limit = 30
def __init__(self, cpu):
self.cpu = cpu
# list of weakrefs to the LoopTokens that must be invalidated if
# this value ever changes
self.looptokens_wrefs = []
- self.compress_limit = 30
def hide(self):
qmut_ptr = self.cpu.ts.cast_instance_to_base_ref(self)
@@ -75,6 +76,8 @@
def compress_looptokens_list(self):
self.looptokens_wrefs = [wref for wref in self.looptokens_wrefs
if wref() is not None]
+ # NB. we must keep around the looptoken_wrefs that are
+ # already invalidated; see below
self.compress_limit = (len(self.looptokens_wrefs) + 15) * 2
def invalidate(self):
@@ -86,7 +89,16 @@
for wref in wrefs:
looptoken = wref()
if looptoken is not None:
+ looptoken.invalidated = True
self.cpu.invalidate_loop(looptoken)
+ # NB. we must call cpu.invalidate_loop() even if
+ # looptoken.invalidated was already set to True.
+ # It's possible to invalidate several times the
+ # same looptoken; see comments in jit.backend.model
+ # in invalidate_loop().
+ if not we_are_translated():
+ self.cpu.stats.invalidated_token_numbers.add(
+ looptoken.number)
class QuasiImmutDescr(AbstractDescr):
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
@@ -221,7 +221,6 @@
newop.setfailargs(self.getfailargs())
return newop
-
# ============
# arity mixins
# ============
@@ -434,6 +433,8 @@
'INT_INVERT/1',
#
'SAME_AS/1', # gets a Const or a Box, turns it into another Box
+ 'CAST_PTR_TO_INT/1',
+ 'CAST_INT_TO_PTR/1',
#
'PTR_EQ/2b',
'PTR_NE/2b',
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
@@ -13,7 +13,7 @@
from pypy.rlib.jit import (JitDriver, we_are_jitted, hint, dont_look_inside,
loop_invariant, elidable, promote, jit_debug, assert_green,
AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff,
- isconstant, isvirtual)
+ isconstant, isvirtual, promote_string)
from pypy.rlib.rarithmetic import ovfcheck
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.ootypesystem import ootype
@@ -2956,6 +2956,18 @@
assert res == f(32)
self.check_loops(arraylen_gc=2)
+ def test_ulonglong_mod(self):
+ myjitdriver = JitDriver(greens = [], reds = ['n', 'sa', 'i'])
+ def f(n):
+ sa = i = rffi.cast(rffi.ULONGLONG, 1)
+ while i < rffi.cast(rffi.ULONGLONG, n):
+ myjitdriver.jit_merge_point(sa=sa, n=n, i=i)
+ sa += sa % i
+ i += 1
+ res = self.meta_interp(f, [32])
+ assert res == f(32)
+
+
class TestOOtype(BasicTests, OOJitMixin):
def test_oohash(self):
@@ -3408,6 +3420,106 @@
assert res == main(1, 10)
self.check_loops(call=0)
+ def test_setarrayitem_followed_by_arraycopy(self):
+ myjitdriver = JitDriver(greens = [], reds = ['n', 'sa', 'x', 'y'])
+ def f(n):
+ sa = 0
+ x = [1,2,n]
+ y = [1,2,3]
+ while n > 0:
+ myjitdriver.jit_merge_point(sa=sa, n=n, x=x, y=y)
+ y[0] = n
+ x[0:3] = y
+ sa += x[0]
+ n -= 1
+ return sa
+ res = self.meta_interp(f, [16])
+ assert res == f(16)
+
+
class TestLLtype(BaseLLtypeTests, LLJitMixin):
- pass
+ def test_tagged(self):
+ from pypy.rlib.objectmodel import UnboxedValue
+ class Base(object):
+ __slots__ = ()
+
+ class Int(UnboxedValue, Base):
+ __slots__ = ["a"]
+
+ def is_pos(self):
+ return self.a > 0
+
+ def dec(self):
+ return Int(self.a - 1)
+
+
+ class Float(Base):
+ def __init__(self, a):
+ self.a = a
+
+ def is_pos(self):
+ return self.a > 0
+
+ def dec(self):
+ return Float(self.a - 1)
+
+ driver = JitDriver(greens=['pc', 's'], reds=['o'])
+
+ def main(fl, n, s):
+ if s:
+ s = "--j"
+ else:
+ s = "---j"
+ if fl:
+ o = Float(float(n))
+ else:
+ o = Int(n)
+ pc = 0
+ while True:
+ driver.jit_merge_point(s=s, pc=pc, o=o)
+ c = s[pc]
+ if c == "j":
+ driver.can_enter_jit(s=s, pc=pc, o=o)
+ if o.is_pos():
+ pc = 0
+ continue
+ else:
+ break
+ elif c == "-":
+ o = o.dec()
+ pc += 1
+ return pc
+ res = self.meta_interp(main, [False, 100, True], taggedpointers=True)
+
+ def test_rerased(self):
+ from pypy.rlib.rerased import erase_int, unerase_int, new_erasing_pair
+ eraseX, uneraseX = new_erasing_pair("X")
+ #
+ class X:
+ def __init__(self, a, b):
+ self.a = a
+ self.b = b
+ #
+ def f(i, j):
+ # 'j' should be 0 or 1, not other values
+ if j > 0:
+ e = eraseX(X(i, j))
+ else:
+ try:
+ e = erase_int(i)
+ except OverflowError:
+ return -42
+ if j & 1:
+ x = uneraseX(e)
+ return x.a - x.b
+ else:
+ return unerase_int(e)
+ #
+ x = self.interp_operations(f, [-128, 0], taggedpointers=True)
+ assert x == -128
+ bigint = sys.maxint//2 + 1
+ x = self.interp_operations(f, [bigint, 0], taggedpointers=True)
+ assert x == -42
+ x = self.interp_operations(f, [1000, 1], taggedpointers=True)
+ assert x == 999
diff --git a/pypy/jit/metainterp/test/test_heapcache.py b/pypy/jit/metainterp/test/test_heapcache.py
--- a/pypy/jit/metainterp/test/test_heapcache.py
+++ b/pypy/jit/metainterp/test/test_heapcache.py
@@ -355,6 +355,14 @@
assert not h.is_unescaped(box1)
assert not h.is_unescaped(box2)
+ def test_circular_virtuals(self):
+ h = HeapCache()
+ h.new(box1)
+ h.new(box2)
+ h.invalidate_caches(rop.SETFIELD_GC, None, [box1, box2])
+ h.invalidate_caches(rop.SETFIELD_GC, None, [box2, box1])
+ h.invalidate_caches(rop.SETFIELD_GC, None, [box3, box1]) # does not crash
+
def test_unescaped_array(self):
h = HeapCache()
h.new_array(box1, lengthbox1)
@@ -362,4 +370,4 @@
h.invalidate_caches(rop.SETARRAYITEM_GC, None, [box1, index1, box2])
assert h.is_unescaped(box1)
h.invalidate_caches(rop.SETARRAYITEM_GC, None, [box2, index1, box1])
- assert not h.is_unescaped(box1)
\ No newline at end of file
+ assert not h.is_unescaped(box1)
diff --git a/pypy/jit/metainterp/test/test_longlong.py b/pypy/jit/metainterp/test/test_longlong.py
--- a/pypy/jit/metainterp/test/test_longlong.py
+++ b/pypy/jit/metainterp/test/test_longlong.py
@@ -118,6 +118,26 @@
res = self.interp_operations(f, [1000000000])
assert res == 123500000000.0
+ def test_floats_negative(self):
+ def f(i):
+ # i == 1000000000
+ f = i * -123.5
+ n = r_longlong(f)
+ compare(n, -29, 1054051584)
+ return float(n)
+ res = self.interp_operations(f, [1000000000])
+ assert res == -123500000000.0
+
+ def test_floats_ulonglong(self):
+ def f(i):
+ # i == 1000000000
+ f = i * 12350000000.0
+ n = r_ulonglong(f)
+ compare(n, -1419508847, 538116096)
+ return float(n)
+ res = self.interp_operations(f, [1000000000])
+ assert res == 12350000000000000000.0
+
def test_unsigned_compare_ops(self):
def f(n1, n2):
# n == 30002000000000
diff --git a/pypy/jit/metainterp/test/test_memmgr.py b/pypy/jit/metainterp/test/test_memmgr.py
--- a/pypy/jit/metainterp/test/test_memmgr.py
+++ b/pypy/jit/metainterp/test/test_memmgr.py
@@ -18,6 +18,7 @@
class FakeLoopToken:
generation = 0
+ invalidated = False
class _TestMemoryManager:
diff --git a/pypy/jit/metainterp/test/test_quasiimmut.py b/pypy/jit/metainterp/test/test_quasiimmut.py
--- a/pypy/jit/metainterp/test/test_quasiimmut.py
+++ b/pypy/jit/metainterp/test/test_quasiimmut.py
@@ -48,6 +48,13 @@
class QuasiImmutTests(object):
+ def setup_method(self, meth):
+ self.prev_compress_limit = QuasiImmut.compress_limit
+ QuasiImmut.compress_limit = 1
+
+ def teardown_method(self, meth):
+ QuasiImmut.compress_limit = self.prev_compress_limit
+
def test_simple_1(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
class Foo:
@@ -289,7 +296,7 @@
return total
res = self.meta_interp(main, [])
- self.check_loop_count(9)
+ self.check_tree_loop_count(6)
assert res == main()
def test_change_during_running(self):
@@ -317,7 +324,7 @@
assert f(100, 15) == 3009
res = self.meta_interp(f, [100, 15])
assert res == 3009
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
+ self.check_loops(guard_not_invalidated=4, getfield_gc=0,
call_may_force=0, guard_not_forced=0)
def test_list_simple_1(self):
@@ -453,10 +460,30 @@
assert f(100, 15) == 3009
res = self.meta_interp(f, [100, 15])
assert res == 3009
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
+ self.check_loops(guard_not_invalidated=4, getfield_gc=0,
getarrayitem_gc=0, getarrayitem_gc_pure=0,
call_may_force=0, guard_not_forced=0)
+ def test_invalidated_loop_is_not_used_any_more_as_target(self):
+ myjitdriver = JitDriver(greens=['foo'], reds=['x'])
+ class Foo:
+ _immutable_fields_ = ['step?']
+ @dont_look_inside
+ def residual(x, foo):
+ if x == 20:
+ foo.step = 1
+ def f(x):
+ foo = Foo()
+ foo.step = 2
+ while x > 0:
+ myjitdriver.jit_merge_point(foo=foo, x=x)
+ residual(x, foo)
+ x -= foo.step
+ return foo.step
+ res = self.meta_interp(f, [60])
+ assert res == 1
+ self.check_tree_loop_count(4) # at least not 2 like before
+
class TestLLtypeGreenFieldsTests(QuasiImmutTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py
--- a/pypy/jit/metainterp/test/test_resume.py
+++ b/pypy/jit/metainterp/test/test_resume.py
@@ -1,3 +1,4 @@
+from __future__ import with_statement
import py
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.jit.metainterp.optimizeopt.optimizer import OptValue
@@ -180,22 +181,27 @@
reader.consume_boxes(info, bi, br, bf)
b1s = reader.liveboxes[0]
b2s = reader.liveboxes[1]
- assert bi == [b1s, ConstInt(111), b1s]
- assert br == [ConstPtr(gcrefnull), b2s]
+ assert_same(bi, [b1s, ConstInt(111), b1s])
+ assert_same(br, [ConstPtr(gcrefnull), b2s])
bi, br, bf = [None]*2, [None]*0, [None]*0
info = MyBlackholeInterp([lltype.Signed,
lltype.Signed]).get_current_position_info()
reader.consume_boxes(info, bi, br, bf)
- assert bi == [ConstInt(222), ConstInt(333)]
+ assert_same(bi, [ConstInt(222), ConstInt(333)])
bi, br, bf = [None]*2, [None]*1, [None]*0
info = MyBlackholeInterp([lltype.Signed, llmemory.GCREF,
lltype.Signed]).get_current_position_info()
reader.consume_boxes(info, bi, br, bf)
b3s = reader.liveboxes[2]
- assert bi == [b1s, b3s]
- assert br == [b2s]
+ assert_same(bi, [b1s, b3s])
+ assert_same(br, [b2s])
#
+def assert_same(list1, list2):
+ assert len(list1) == len(list2)
+ for b1, b2 in zip(list1, list2):
+ assert b1.same_box(b2)
+
def test_simple_read_tagged_ints():
storage = Storage()
storage.rd_consts = []
@@ -576,8 +582,9 @@
class FakeOptimizer_VirtualValue(object):
- class cpu:
- pass
+ class optimizer:
+ class cpu:
+ pass
fakeoptimizer = FakeOptimizer_VirtualValue()
def ConstAddr(addr, cpu): # compatibility
@@ -1022,11 +1029,11 @@
metainterp = MyMetaInterp()
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
lst = reader.consume_boxes()
- assert lst == [b1t, b1t, b3t]
+ assert_same(lst, [b1t, b1t, b3t])
lst = reader.consume_boxes()
- assert lst == [ConstInt(2), ConstInt(3)]
+ assert_same(lst, [ConstInt(2), ConstInt(3)])
lst = reader.consume_boxes()
- assert lst == [b1t, ConstInt(1), b1t, b1t]
+ assert_same(lst, [b1t, ConstInt(1), b1t, b1t])
assert metainterp.trace == []
@@ -1043,11 +1050,11 @@
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
lst = reader.consume_boxes()
c1t = ConstInt(111)
- assert lst == [c1t, b2t, b3t]
+ assert_same(lst, [c1t, b2t, b3t])
lst = reader.consume_boxes()
- assert lst == [ConstInt(2), ConstInt(3)]
+ assert_same(lst, [ConstInt(2), ConstInt(3)])
lst = reader.consume_boxes()
- assert lst == [c1t, ConstInt(1), c1t, b2t]
+ assert_same(lst, [c1t, ConstInt(1), c1t, b2t])
assert metainterp.trace == []
@@ -1113,9 +1120,11 @@
# check that we get the operations in 'expected', in a possibly different
# order.
assert len(trace) == len(expected)
- for x in trace:
- assert x in expected
- expected.remove(x)
+ with CompareableConsts():
+ for x in trace:
+ assert x in expected
+ expected.remove(x)
+
ptr = b2t.value._obj.container._as_ptr()
assert lltype.typeOf(ptr) == lltype.Ptr(LLtypeMixin.NODE)
assert ptr.value == 111
@@ -1125,6 +1134,18 @@
assert ptr2.parent.value == 33
assert ptr2.parent.next == ptr
+class CompareableConsts(object):
+ def __init__(self):
+ self.oldeq = None
+
+ def __enter__(self):
+ assert self.oldeq is None
+ self.oldeq = Const.__eq__
+ Const.__eq__ = Const.same_box
+
+ def __exit__(self, type, value, traceback):
+ Const.__eq__ = self.oldeq
+
def test_virtual_adder_make_varray():
b2s, b4s = [BoxPtr(), BoxInt(4)]
c1s = ConstInt(111)
@@ -1135,12 +1156,7 @@
modifier.liveboxes = {}
modifier.vfieldboxes = {}
- class FakeOptimizer(object):
- class cpu:
- pass
- def new_const_item(self, descr):
- return None
- v2 = VArrayValue(FakeOptimizer(), LLtypeMixin.arraydescr, 2, b2s)
+ v2 = VArrayValue(LLtypeMixin.arraydescr, None, 2, b2s)
v2._items = [b4s, c1s]
modifier.register_virtual_fields(b2s, [b4s, c1s])
liveboxes = []
@@ -1167,8 +1183,9 @@
(rop.SETARRAYITEM_GC, [b2t,ConstInt(1), c1s], None,
LLtypeMixin.arraydescr),
]
- for x, y in zip(expected, trace):
- assert x == y
+ with CompareableConsts():
+ for x, y in zip(expected, trace):
+ assert x == y
#
ptr = b2t.value._obj.container._as_ptr()
assert lltype.typeOf(ptr) == lltype.Ptr(lltype.GcArray(lltype.Signed))
@@ -1211,8 +1228,9 @@
(rop.SETFIELD_GC, [b2t, c1s], None, LLtypeMixin.adescr),
(rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.bdescr),
]
- for x, y in zip(expected, trace):
- assert x == y
+ with CompareableConsts():
+ for x, y in zip(expected, trace):
+ assert x == y
#
ptr = b2t.value._obj.container._as_ptr()
assert lltype.typeOf(ptr) == lltype.Ptr(LLtypeMixin.S)
diff --git a/pypy/jit/metainterp/test/test_string.py b/pypy/jit/metainterp/test/test_string.py
--- a/pypy/jit/metainterp/test/test_string.py
+++ b/pypy/jit/metainterp/test/test_string.py
@@ -3,7 +3,8 @@
from pypy.jit.codewriter.policy import StopAtXPolicy
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
from pypy.rlib.debug import debug_print
-from pypy.rlib.jit import JitDriver, dont_look_inside, we_are_jitted
+from pypy.rlib.jit import JitDriver, dont_look_inside, we_are_jitted,\
+ promote_string
from pypy.rlib.rstring import StringBuilder
from pypy.rpython.ootypesystem import ootype
@@ -484,6 +485,42 @@
return len(sa.val)
assert self.meta_interp(f, ['a']) == f('a')
+ def test_string_comepare_quasiimmutable(self):
+ class Sys(object):
+ _immutable_fields_ = ["defaultencoding?"]
+ def __init__(self, s):
+ self.defaultencoding = s
+ _str = self._str
+ sys = Sys(_str('ascii'))
+ mydriver = JitDriver(reds = ['n', 'sa'], greens = [])
+ def f(n):
+ sa = 0
+ sys.defaultencoding = _str('ascii')
+ while n:
+ mydriver.jit_merge_point(n=n, sa=sa)
+ if sys.defaultencoding == _str('ascii'):
+ sa += 1
+ n -= 1
+ sys.defaultencoding = _str('utf-8')
+ return sa
+ assert self.meta_interp(f, [8]) == f(8)
+ self.check_loops({'int_add': 1, 'guard_true': 1, 'int_sub': 1,
+ 'jump': 1, 'int_is_true': 1,
+ 'guard_not_invalidated': 1})
+
+ def test_promote_string(self):
+ driver = JitDriver(greens = [], reds = ['n'])
+
+ def f(n):
+ while n < 21:
+ driver.jit_merge_point(n=n)
+ promote_string(str(n % 3))
+ n += 1
+ return 0
+
+ self.meta_interp(f, [0])
+ self.check_loops(call=3 + 1) # one for int2str
+
#class TestOOtype(StringTests, OOJitMixin):
# CALL = "oosend"
# CALL_PURE = "oosend_pure"
diff --git a/pypy/jit/metainterp/test/test_virtualref.py b/pypy/jit/metainterp/test/test_virtualref.py
--- a/pypy/jit/metainterp/test/test_virtualref.py
+++ b/pypy/jit/metainterp/test/test_virtualref.py
@@ -3,6 +3,7 @@
from pypy.rpython.llinterp import LLException
from pypy.rlib.jit import JitDriver, dont_look_inside, vref_None
from pypy.rlib.jit import virtual_ref, virtual_ref_finish, InvalidVirtualRef
+from pypy.rlib.jit import non_virtual_ref
from pypy.rlib.objectmodel import compute_unique_id
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin, _get_jitcodes
from pypy.jit.metainterp.resoperation import rop
@@ -595,6 +596,65 @@
res = self.meta_interp(fn, [10])
assert res == 6
+ def test_is_virtual(self):
+ myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+ class X:
+ pass
+ @dont_look_inside
+ def residual(vref):
+ return vref.virtual
+ #
+ def f(n):
+ res1 = -42
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, res1=res1)
+ x = X()
+ vref = virtual_ref(x)
+ res1 = residual(vref)
+ virtual_ref_finish(vref, x)
+ n -= 1
+ return res1
+ #
+ res = self.meta_interp(f, [10])
+ assert res == 1
+
+ def test_is_not_virtual_none(self):
+ myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+ @dont_look_inside
+ def residual(vref):
+ return vref.virtual
+ #
+ def f(n):
+ res1 = -42
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, res1=res1)
+ res1 = residual(vref_None)
+ n -= 1
+ return res1
+ #
+ res = self.meta_interp(f, [10])
+ assert res == 0
+
+ def test_is_not_virtual_non_none(self):
+ myjitdriver = JitDriver(greens=[], reds=['n', 'res1'])
+ class X:
+ pass
+ @dont_look_inside
+ def residual(vref):
+ return vref.virtual
+ #
+ def f(n):
+ res1 = -42
+ while n > 0:
+ myjitdriver.jit_merge_point(n=n, res1=res1)
+ x = X()
+ res1 = residual(non_virtual_ref(x))
+ n -= 1
+ return res1
+ #
+ res = self.meta_interp(f, [10])
+ assert res == 0
+
class TestLLtype(VRefTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_virtualstate.py b/pypy/jit/metainterp/test/test_virtualstate.py
--- a/pypy/jit/metainterp/test/test_virtualstate.py
+++ b/pypy/jit/metainterp/test/test_virtualstate.py
@@ -431,7 +431,7 @@
class BaseTestBridges(BaseTest):
- enable_opts = "intbounds:rewrite:virtualize:string:heap:unroll"
+ enable_opts = "intbounds:rewrite:virtualize:string:pure:heap:unroll"
def _do_optimize_bridge(self, bridge, call_pure_results):
from pypy.jit.metainterp.optimizeopt import optimize_bridge_1, build_opt_chain
diff --git a/pypy/jit/metainterp/virtualref.py b/pypy/jit/metainterp/virtualref.py
--- a/pypy/jit/metainterp/virtualref.py
+++ b/pypy/jit/metainterp/virtualref.py
@@ -39,6 +39,7 @@
def replace_force_virtual_with_call(self, graphs):
# similar to rvirtualizable2.replace_force_virtualizable_with_call().
c_force_virtual_ptr = None
+ c_is_virtual_ptr = None
force_virtual_count = 0
for graph in graphs:
for block in graph.iterblocks():
@@ -52,6 +53,13 @@
op.opname = 'direct_call'
op.args = [c_force_virtual_ptr, op.args[0]]
force_virtual_count += 1
+ #
+ if op.opname == 'jit_is_virtual':
+ if c_is_virtual_ptr is None:
+ c_is_virtual_ptr = self.get_is_virtual_fnptr()
+ #
+ op.opname = 'direct_call'
+ op.args = [c_is_virtual_ptr, op.args[0]]
#
if c_force_virtual_ptr is not None:
log("replaced %d 'jit_force_virtual' with %r" % (force_virtual_count,
@@ -129,6 +137,17 @@
force_virtual_if_necessary)
return inputconst(lltype.typeOf(funcptr), funcptr)
+ def get_is_virtual_fnptr(self):
+ #
+ def is_virtual(inst):
+ if not inst:
+ return False
+ return inst.typeptr == self.jit_virtual_ref_vtable
+ #
+ FUNC = lltype.FuncType([rclass.OBJECTPTR], lltype.Bool)
+ funcptr = self.warmrunnerdesc.helper_func(lltype.Ptr(FUNC), is_virtual)
+ return inputconst(lltype.typeOf(funcptr), funcptr)
+
def force_virtual(self, inst):
vref = lltype.cast_pointer(lltype.Ptr(self.JIT_VIRTUAL_REF), inst)
token = vref.virtual_token
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -53,6 +53,8 @@
extraconfigopts = {'translation.list_comprehension_operations': True}
else:
extraconfigopts = {}
+ if kwds.pop("taggedpointers", False):
+ extraconfigopts["translation.taggedpointers"] = True
interp, graph = get_interpreter(function, args,
backendopt=False, # will be done below
type_system=type_system,
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -178,7 +178,7 @@
if self.compiled_merge_points_wref is not None:
for wref in self.compiled_merge_points_wref:
looptoken = wref()
- if looptoken is not None:
+ if looptoken is not None and not looptoken.invalidated:
result.append(looptoken)
return result
diff --git a/pypy/module/__builtin__/__init__.py b/pypy/module/__builtin__/__init__.py
--- a/pypy/module/__builtin__/__init__.py
+++ b/pypy/module/__builtin__/__init__.py
@@ -20,6 +20,10 @@
'any' : 'app_functional.any',
'all' : 'app_functional.all',
'sum' : 'app_functional.sum',
+ 'map' : 'app_functional.map',
+ 'reduce' : 'app_functional.reduce',
+ 'filter' : 'app_functional.filter',
+ 'zip' : 'app_functional.zip',
'vars' : 'app_inspect.vars',
'dir' : 'app_inspect.dir',
@@ -86,11 +90,7 @@
'enumerate' : 'functional.W_Enumerate',
'min' : 'functional.min',
'max' : 'functional.max',
- 'map' : 'functional.map',
- 'zip' : 'functional.zip',
- 'reduce' : 'functional.reduce',
'reversed' : 'functional.reversed',
- 'filter' : 'functional.filter',
'super' : 'descriptor.W_Super',
'staticmethod' : 'descriptor.StaticMethod',
'classmethod' : 'descriptor.ClassMethod',
@@ -119,7 +119,7 @@
builtin = space.interpclass_w(w_builtin)
if isinstance(builtin, module.Module):
return builtin
- # no builtin! make a default one. Given them None, at least.
+ # no builtin! make a default one. Give them None, at least.
builtin = module.Module(space, None)
space.setitem(builtin.w_dict, space.wrap('None'), space.w_None)
return builtin
diff --git a/pypy/module/__builtin__/app_functional.py b/pypy/module/__builtin__/app_functional.py
--- a/pypy/module/__builtin__/app_functional.py
+++ b/pypy/module/__builtin__/app_functional.py
@@ -48,4 +48,135 @@
# Very intentionally *not* +=, that would have different semantics if
# start was a mutable type, such as a list
last = last + x
- return last
\ No newline at end of file
+ return last
+
+def map(func, *collections):
+ """map(function, sequence[, sequence, ...]) -> list
+
+Return a list of the results of applying the function to the items of
+the argument sequence(s). If more than one sequence is given, the
+function is called with an argument list consisting of the corresponding
+item of each sequence, substituting None for missing values when not all
+sequences have the same length. If the function is None, return a list of
+the items of the sequence (or a list of tuples if more than one sequence)."""
+ if not collections:
+ raise TypeError("map() requires at least two arguments")
+ num_collections = len(collections)
+ none_func = func is None
+ if num_collections == 1:
+ if none_func:
+ return list(collections[0])
+ else:
+ # Special case for the really common case of a single collection,
+ # this can be eliminated if we could unroll that loop that creates
+ # `args` based on whether or not len(collections) was constant
+ result = []
+ for item in collections[0]:
+ result.append(func(item))
+ return result
+ result = []
+ # Pair of (iterator, has_finished)
+ iterators = [(iter(seq), False) for seq in collections]
+ while True:
+ cont = False
+ args = []
+ for idx, (iterator, has_finished) in enumerate(iterators):
+ val = None
+ if not has_finished:
+ try:
+ val = next(iterator)
+ except StopIteration:
+ iterators[idx] = (None, True)
+ else:
+ cont = True
+ args.append(val)
+ args = tuple(args)
+ if cont:
+ if none_func:
+ result.append(args)
+ else:
+ result.append(func(*args))
+ else:
+ return result
+
+sentinel = object()
+
+def reduce(func, sequence, initial=sentinel):
+ """reduce(function, sequence[, initial]) -> value
+
+Apply a function of two arguments cumulatively to the items of a sequence,
+from left to right, so as to reduce the sequence to a single value.
+For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
+((((1+2)+3)+4)+5). If initial is present, it is placed before the items
+of the sequence in the calculation, and serves as a default when the
+sequence is empty."""
+ iterator = iter(sequence)
+ if initial is sentinel:
+ try:
+ initial = next(iterator)
+ except StopIteration:
+ raise TypeError("reduce() of empty sequence with no initial value")
+ result = initial
+ for item in iterator:
+ result = func(result, item)
+ return result
+
+def filter(func, seq):
+ """filter(function or None, sequence) -> list, tuple, or string
+
+Return those items of sequence for which function(item) is true. If
+function is None, return the items that are true. If sequence is a tuple
+or string, return the same type, else return a list."""
+ if func is None:
+ func = bool
+ if isinstance(seq, str):
+ return _filter_string(func, seq, str)
+ elif isinstance(seq, unicode):
+ return _filter_string(func, seq, unicode)
+ elif isinstance(seq, tuple):
+ return _filter_tuple(func, seq)
+ result = []
+ for item in seq:
+ if func(item):
+ result.append(item)
+ return result
+
+def _filter_string(func, string, str_type):
+ if func is bool and type(string) is str_type:
+ return string
+ result = []
+ for i in range(len(string)):
+ # You must call __getitem__ on the strings, simply iterating doesn't
+ # work :/
+ item = string[i]
+ if func(item):
+ if not isinstance(item, str_type):
+ raise TypeError("__getitem__ returned a non-string type")
+ result.append(item)
+ return str_type().join(result)
+
+def _filter_tuple(func, seq):
+ result = []
+ for i in range(len(seq)):
+ # Again, must call __getitem__, at least there are tests.
+ item = seq[i]
+ if func(item):
+ result.append(item)
+ return tuple(result)
+
+def zip(*sequences):
+ """zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
+
+Return a list of tuples, where each tuple contains the i-th element
+from each of the argument sequences. The returned list is truncated
+in length to the length of the shortest argument sequence."""
+ if not sequences:
+ return []
+ result = []
+ iterators = [iter(seq) for seq in sequences]
+ while True:
+ try:
+ items = [next(it) for it in iterators]
+ except StopIteration:
+ return result
+ result.append(tuple(items))
diff --git a/pypy/module/__builtin__/app_io.py b/pypy/module/__builtin__/app_io.py
--- a/pypy/module/__builtin__/app_io.py
+++ b/pypy/module/__builtin__/app_io.py
@@ -27,7 +27,20 @@
co = compile(source.rstrip()+"\n", filename, 'exec')
exec co in glob, loc
-def raw_input(prompt=None):
+def _write_prompt(stdout, prompt):
+ print >> stdout, prompt,
+ try:
+ flush = stdout.flush
+ except AttributeError:
+ pass
+ else:
+ flush()
+ try:
+ stdout.softspace = 0
+ except (AttributeError, TypeError):
+ pass
+
+def raw_input(prompt=''):
"""raw_input([prompt]) -> string
Read a string from standard input. The trailing newline is stripped.
@@ -47,18 +60,10 @@
if (hasattr(sys, '__raw_input__') and
isinstance(stdin, file) and stdin.fileno() == 0 and stdin.isatty() and
isinstance(stdout, file) and stdout.fileno() == 1):
- if prompt is None:
- prompt = ''
- return sys.__raw_input__(prompt)
+ _write_prompt(stdout, '')
+ return sys.__raw_input__(str(prompt))
- if prompt is not None:
- stdout.write(prompt)
- try:
- flush = stdout.flush
- except AttributeError:
- pass
- else:
- flush()
+ _write_prompt(stdout, prompt)
line = stdin.readline()
if not line: # inputting an empty line gives line == '\n'
raise EOFError
diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -84,8 +84,8 @@
raise OperationError(space.w_TypeError,
w('eval() arg 1 must be a string or code object'))
- caller = space.getexecutioncontext().gettopframe_nohidden()
if space.is_w(w_globals, space.w_None):
+ caller = space.getexecutioncontext().gettopframe_nohidden()
if caller is None:
w_globals = space.newdict()
if space.is_w(w_locals, space.w_None):
@@ -97,13 +97,9 @@
elif space.is_w(w_locals, space.w_None):
w_locals = w_globals
- try:
- space.getitem(w_globals, space.wrap('__builtins__'))
- except OperationError, e:
- if not e.match(space, space.w_KeyError):
- raise
- if caller is not None:
- w_builtin = space.builtin.pick_builtin(caller.w_globals)
- space.setitem(w_globals, space.wrap('__builtins__'), w_builtin)
+ # xxx removed: adding '__builtins__' to the w_globals dict, if there
+ # is none. This logic was removed as costly (it requires to get at
+ # the gettopframe_nohidden()). I bet no test fails, and it's a really
+ # obscure case.
return codeobj.exec_code(space, w_globals, w_locals)
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -193,229 +193,14 @@
return min_max(space, __args__, "max")
def min(space, __args__):
- """Return the smallest item in a sequence.
+ """min(iterable[, key=func]) -> value
+ min(a, b, c, ...[, key=func]) -> value
- If more than one argument is passed, return the minimum of them.
+ With a single iterable argument, return its smallest item.
+ With two or more arguments, return the smallest argument.
"""
return min_max(space, __args__, "min")
- at unwrap_spec(collections_w="args_w")
-def map(space, w_func, collections_w):
- """does 3 separate things, hence this enormous docstring.
- 1. if function is None, return a list of tuples, each with one
- item from each collection. If the collections have different
- lengths, shorter ones are padded with None.
-
- 2. if function is not None, and there is only one collection,
- apply function to every item in the collection and return a
- list of the results.
-
- 3. if function is not None, and there are several collections,
- repeatedly call the function with one argument from each
- collection. If the collections have different lengths,
- shorter ones are padded with None
- """
- if not collections_w:
- msg = "map() requires at least two arguments"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- none_func = space.is_w(w_func, space.w_None)
- if len(collections_w) == 1:
- w_collection = collections_w[0]
- if none_func:
- result_w = space.unpackiterable(w_collection)
- else:
- result_w = map_single_collection(space, w_func, w_collection)
- else:
- result_w = map_multiple_collections(space, w_func, collections_w,
- none_func)
- return space.newlist(result_w)
-
-def map_single_collection(space, w_func, w_collection):
- """Special case for 'map(func, coll)', where 'func' is not None and there
- is only one 'coll' argument."""
- w_iter = space.iter(w_collection)
- # xxx special hacks for speed
- from pypy.interpreter import function, pycode
- if isinstance(w_func, function.Function):
- # xxx compatibility issue: what if func_code is modified in the
- # middle of running map()?? That's far too obscure for me to care...
- code = w_func.getcode()
- fast_natural_arity = code.fast_natural_arity
- if fast_natural_arity == (1|pycode.PyCode.FLATPYCALL):
- assert isinstance(code, pycode.PyCode)
- return map_single_user_function(code, w_func, w_iter)
- # /xxx end of special hacks
- return map_single_other_callable(space, w_func, w_iter)
-
-def map_single_other_callable(space, w_func, w_iter):
- result_w = []
- while True:
- try:
- w_item = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- result_w.append(space.call_function(w_func, w_item))
- return result_w
-map_single_other_callable._dont_inline_ = True
-
-from pypy.rlib.jit import JitDriver
-mapjitdriver = JitDriver(greens = ['code'],
- reds = ['w_func', 'w_iter', 'result_w'])
-def map_single_user_function(code, w_func, w_iter):
- result_w = []
- while True:
- mapjitdriver.can_enter_jit(code=code, w_func=w_func,
- w_iter=w_iter, result_w=result_w)
- mapjitdriver.jit_merge_point(code=code, w_func=w_func,
- w_iter=w_iter, result_w=result_w)
- space = w_func.space
- try:
- w_item = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- new_frame = space.createframe(code, w_func.w_func_globals,
- w_func)
- new_frame.locals_stack_w[0] = w_item
- w_res = new_frame.run()
- result_w.append(w_res)
- return result_w
-
-def map_multiple_collections(space, w_func, collections_w, none_func):
- result_w = []
- iterators_w = [space.iter(w_seq) for w_seq in collections_w]
- num_iterators = len(iterators_w)
- while True:
- cont = False
- args_w = [space.w_None] * num_iterators
- for i in range(num_iterators):
- if iterators_w[i] is not None:
- try:
- args_w[i] = space.next(iterators_w[i])
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- iterators_w[i] = None
- else:
- cont = True
- if not cont:
- break
- w_args = space.newtuple(args_w)
- if none_func:
- w_res = w_args
- else:
- w_res = space.call(w_func, w_args)
- result_w.append(w_res)
- return result_w
-
- at unwrap_spec(sequences_w="args_w")
-def zip(space, sequences_w):
- """Return a list of tuples, where the nth tuple contains every nth item of
- each collection.
-
- If the collections have different lengths, zip returns a list as long as the
- shortest collection, ignoring the trailing items in the other collections.
- """
- if not sequences_w:
- return space.newlist([])
- result_w = []
- iterators_w = [space.iter(w_seq) for w_seq in sequences_w]
- while True:
- try:
- items_w = [space.next(w_it) for w_it in iterators_w]
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- return space.newlist(result_w)
- result_w.append(space.newtuple(items_w))
-
-def reduce(space, w_func, w_sequence, w_initial=NoneNotWrapped):
- """ Apply function of two arguments cumulatively to the items of sequence,
- from left to right, so as to reduce the sequence to a single value.
- Optionally begin with an initial value.
- """
- w_iter = space.iter(w_sequence)
- if w_initial is None:
- try:
- w_initial = space.next(w_iter)
- except OperationError, e:
- if e.match(space, space.w_StopIteration):
- msg = "reduce() of empty sequence with no initial value"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- raise
- w_result = w_initial
- while True:
- try:
- w_next = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- w_result = space.call_function(w_func, w_result, w_next)
- return w_result
-
-def filter(space, w_func, w_seq):
- """construct a list of those elements of collection for which function
- is True. If function is None, then return the items in the sequence
- which are True.
- """
- if space.is_true(space.isinstance(w_seq, space.w_str)):
- return _filter_string(space, w_func, w_seq, space.w_str)
- if space.is_true(space.isinstance(w_seq, space.w_unicode)):
- return _filter_string(space, w_func, w_seq, space.w_unicode)
- if space.is_true(space.isinstance(w_seq, space.w_tuple)):
- return _filter_tuple(space, w_func, w_seq)
- w_iter = space.iter(w_seq)
- result_w = []
- none_func = space.is_w(w_func, space.w_None)
- while True:
- try:
- w_next = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- if none_func:
- w_keep = w_next
- else:
- w_keep = space.call_function(w_func, w_next)
- if space.is_true(w_keep):
- result_w.append(w_next)
- return space.newlist(result_w)
-
-def _filter_tuple(space, w_func, w_tuple):
- none_func = space.is_w(w_func, space.w_None)
- length = space.len_w(w_tuple)
- result_w = []
- for i in range(length):
- w_item = space.getitem(w_tuple, space.wrap(i))
- if none_func:
- w_keep = w_item
- else:
- w_keep = space.call_function(w_func, w_item)
- if space.is_true(w_keep):
- result_w.append(w_item)
- return space.newtuple(result_w)
-
-def _filter_string(space, w_func, w_string, w_str_type):
- none_func = space.is_w(w_func, space.w_None)
- if none_func and space.is_w(space.type(w_string), w_str_type):
- return w_string
- length = space.len_w(w_string)
- result_w = []
- for i in range(length):
- w_item = space.getitem(w_string, space.wrap(i))
- if none_func or space.is_true(space.call_function(w_func, w_item)):
- if not space.is_true(space.isinstance(w_item, w_str_type)):
- msg = "__getitem__ returned a non-string type"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- result_w.append(w_item)
- w_empty = space.call_function(w_str_type)
- return space.call_method(w_empty, "join", space.newlist(result_w))
-
class W_Enumerate(Wrappable):
def __init__(self, w_iter, w_start):
diff --git a/pypy/module/__builtin__/test/test_functional.py b/pypy/module/__builtin__/test/test_functional.py
--- a/pypy/module/__builtin__/test/test_functional.py
+++ b/pypy/module/__builtin__/test/test_functional.py
@@ -147,7 +147,7 @@
assert list(xrange(A())) == [0, 1, 2, 3, 4]
assert list(xrange(0, A())) == [0, 1, 2, 3, 4]
assert list(xrange(0, 10, A())) == [0, 5]
-
+
def test_xrange_float(self):
assert list(xrange(0.1, 2.0, 1.1)) == [0, 1]
diff --git a/pypy/module/__builtin__/test/test_rawinput.py b/pypy/module/__builtin__/test/test_rawinput.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/__builtin__/test/test_rawinput.py
@@ -0,0 +1,77 @@
+import autopath
+
+
+class AppTestRawInput():
+
+ def test_raw_input(self):
+ import sys, StringIO
+ for prompt, expected in [("def:", "abc/ def:/ghi\n"),
+ ("", "abc/ /ghi\n"),
+ (42, "abc/ 42/ghi\n"),
+ (None, "abc/ None/ghi\n"),
+ (Ellipsis, "abc/ /ghi\n")]:
+ save = sys.stdin, sys.stdout
+ try:
+ sys.stdin = StringIO.StringIO("foo\nbar\n")
+ out = sys.stdout = StringIO.StringIO()
+ print "abc", # softspace = 1
+ out.write('/')
+ if prompt is Ellipsis:
+ got = raw_input()
+ else:
+ got = raw_input(prompt)
+ out.write('/')
+ print "ghi"
+ finally:
+ sys.stdin, sys.stdout = save
+ assert out.getvalue() == expected
+ assert got == "foo"
+
+ def test_softspace(self):
+ import sys
+ import StringIO
+ fin = StringIO.StringIO()
+ fout = StringIO.StringIO()
+
+ fin.write("Coconuts\n")
+ fin.seek(0)
+
+ sys_stdin_orig = sys.stdin
+ sys_stdout_orig = sys.stdout
+
+ sys.stdin = fin
+ sys.stdout = fout
+
+ print "test",
+ raw_input("test")
+
+ sys.stdin = sys_stdin_orig
+ sys.stdout = sys_stdout_orig
+
+ fout.seek(0)
+ assert fout.read() == "test test"
+
+ def test_softspace_carryover(self):
+ import sys
+ import StringIO
+ fin = StringIO.StringIO()
+ fout = StringIO.StringIO()
+
+ fin.write("Coconuts\n")
+ fin.seek(0)
+
+ sys_stdin_orig = sys.stdin
+ sys_stdout_orig = sys.stdout
+
+ sys.stdin = fin
+ sys.stdout = fout
+
+ print "test",
+ raw_input("test")
+ print "test",
+
+ sys.stdin = sys_stdin_orig
+ sys.stdout = sys_stdout_orig
+
+ fout.seek(0)
+ assert fout.read() == "test testtest"
diff --git a/pypy/module/__pypy__/interp_builders.py b/pypy/module/__pypy__/interp_builders.py
--- a/pypy/module/__pypy__/interp_builders.py
+++ b/pypy/module/__pypy__/interp_builders.py
@@ -16,7 +16,8 @@
def _check_done(self, space):
if self.builder is None:
- raise OperationError(space.w_ValueError, space.wrap("Can't operate on a done builder"))
+ raise OperationError(space.w_ValueError, space.wrap(
+ "Can't operate on a built builder"))
@unwrap_spec(size=int)
def descr__new__(space, w_subtype, size=-1):
@@ -31,7 +32,8 @@
def descr_append_slice(self, space, s, start, end):
self._check_done(space)
if not 0 <= start <= end <= len(s):
- raise OperationError(space.w_ValueError, space.wrap("bad start/stop"))
+ raise OperationError(space.w_ValueError, space.wrap(
+ "bad start/stop"))
self.builder.append_slice(s, start, end)
def descr_build(self, space):
@@ -40,6 +42,12 @@
self.builder = None
return w_s
+ def descr_len(self, space):
+ if self.builder is None:
+ raise OperationError(space.w_ValueError, space.wrap(
+ "no length of built builder"))
+ return space.wrap(self.builder.getlength())
+
W_Builder.__name__ = "W_%s" % name
W_Builder.typedef = TypeDef(name,
__new__ = interp2app(func_with_new_name(
@@ -48,6 +56,7 @@
append = interp2app(W_Builder.descr_append),
append_slice = interp2app(W_Builder.descr_append_slice),
build = interp2app(W_Builder.descr_build),
+ __len__ = interp2app(W_Builder.descr_len),
)
W_Builder.typedef.acceptable_as_base_class = False
return W_Builder
diff --git a/pypy/module/__pypy__/test/test_builders.py b/pypy/module/__pypy__/test/test_builders.py
--- a/pypy/module/__pypy__/test/test_builders.py
+++ b/pypy/module/__pypy__/test/test_builders.py
@@ -38,7 +38,9 @@
b = StringBuilder()
b.append("abc")
b.append("123")
+ assert len(b) == 6
b.append("you and me")
s = b.build()
+ raises(ValueError, len, b)
assert s == "abc123you and me"
- raises(ValueError, b.build)
\ No newline at end of file
+ raises(ValueError, b.build)
diff --git a/pypy/module/_continuation/interp_continuation.py b/pypy/module/_continuation/interp_continuation.py
--- a/pypy/module/_continuation/interp_continuation.py
+++ b/pypy/module/_continuation/interp_continuation.py
@@ -19,6 +19,11 @@
# - normal: self.sthread != None, not is_empty_handle(self.h)
# - finished: self.sthread != None, is_empty_handle(self.h)
+ def __del__(self):
+ sthread = self.sthread
+ if sthread is not None and not sthread.is_empty_handle(self.h):
+ sthread.destroy(self.h)
+
def check_sthread(self):
ec = self.space.getexecutioncontext()
if ec.stacklet_thread is not self.sthread:
@@ -28,6 +33,8 @@
def descr_init(self, w_callable, __args__):
if self.sthread is not None:
raise geterror(self.space, "continulet already __init__ialized")
+ sthread = build_sthread(self.space)
+ workaround_disable_jit(sthread)
#
# hackish: build the frame "by hand", passing it the correct arguments
space = self.space
@@ -41,7 +48,6 @@
self.bottomframe = bottomframe
#
global_state.origin = self
- sthread = build_sthread(self.space)
self.sthread = sthread
h = sthread.new(new_stacklet_callback)
post_switch(sthread, h)
@@ -71,6 +77,7 @@
global_state.clear()
raise geterror(self.space, "continulet already finished")
self.check_sthread()
+ workaround_disable_jit(self.sthread)
#
global_state.origin = self
if to is None:
@@ -259,6 +266,16 @@
sthread = ec.stacklet_thread = SThread(space, ec)
return sthread
+def workaround_disable_jit(sthread):
+ # A bad workaround to kill the JIT anywhere in this thread.
+ # This forces all the frames. It's a bad workaround because
+ # it takes O(depth) time, and it will cause some "abort:
+ # vable escape" in the JIT. The goal is to prevent any frame
+ # from being still virtuals, because the JIT generates code
+ # to un-virtualizable them "on demand" by loading values based
+ # on FORCE_TOKEN, which is an address in the stack.
+ sthread.ec.force_all_frames()
+
# ____________________________________________________________
def permute(space, args_w):
diff --git a/pypy/module/_file/test/test_file.py b/pypy/module/_file/test/test_file.py
--- a/pypy/module/_file/test/test_file.py
+++ b/pypy/module/_file/test/test_file.py
@@ -1,5 +1,5 @@
from __future__ import with_statement
-import py
+import py, os, errno
from pypy.conftest import gettestobjspace, option
@@ -257,6 +257,39 @@
assert self.temppath in g.getvalue()
+class AppTestNonblocking(object):
+ def setup_class(cls):
+ from pypy.module._file.interp_file import W_File
+
+ cls.old_read = os.read
+
+ if option.runappdirect:
+ py.test.skip("works with internals of _file impl on py.py")
+
+ state = [0]
+ def read(fd, n=None):
+ if fd != 42:
+ return cls.old_read(fd, n)
+ if state[0] == 0:
+ state[0] += 1
+ return "xyz"
+ if state[0] < 3:
+ state[0] += 1
+ raise OSError(errno.EAGAIN, "xyz")
+ return ''
+ os.read = read
+ stdin = W_File(cls.space)
+ stdin.file_fdopen(42, "r", 1)
+ stdin.name = '<stdin>'
+ cls.w_stream = stdin
+
+ def teardown_class(cls):
+ os.read = cls.old_read
+
+ def test_nonblocking_file(self):
+ res = self.stream.read()
+ assert res == 'xyz'
+
class AppTestConcurrency(object):
# these tests only really make sense on top of a translated pypy-c,
# because on top of py.py the inner calls to os.write() don't
@@ -374,24 +407,24 @@
with self.file(self.temppath, 'w') as f:
f.write('foo')
assert f.closed
-
+
with self.file(self.temppath, 'r') as f:
s = f.readline()
assert s == "foo"
assert f.closed
-
+
def test_subclass_with(self):
file = self.file
class C(file):
def __init__(self, *args, **kwargs):
self.subclass_closed = False
file.__init__(self, *args, **kwargs)
-
+
def close(self):
self.subclass_closed = True
file.close(self)
-
+
with C(self.temppath, 'w') as f:
pass
assert f.subclass_closed
diff --git a/pypy/module/_multiprocessing/interp_connection.py b/pypy/module/_multiprocessing/interp_connection.py
--- a/pypy/module/_multiprocessing/interp_connection.py
+++ b/pypy/module/_multiprocessing/interp_connection.py
@@ -225,7 +225,9 @@
except OSError:
pass
- def __init__(self, fd, flags):
+ def __init__(self, space, fd, flags):
+ if fd == self.INVALID_HANDLE_VALUE or fd < 0:
+ raise OperationError(space.w_IOError, space.wrap("invalid handle %d" % fd))
W_BaseConnection.__init__(self, flags)
self.fd = fd
@@ -234,7 +236,7 @@
flags = (readable and READABLE) | (writable and WRITABLE)
self = space.allocate_instance(W_FileConnection, w_subtype)
- W_FileConnection.__init__(self, fd, flags)
+ W_FileConnection.__init__(self, space, fd, flags)
return space.wrap(self)
def fileno(self, space):
diff --git a/pypy/module/_multiprocessing/interp_semaphore.py b/pypy/module/_multiprocessing/interp_semaphore.py
--- a/pypy/module/_multiprocessing/interp_semaphore.py
+++ b/pypy/module/_multiprocessing/interp_semaphore.py
@@ -468,6 +468,9 @@
self.count -= 1
+ def after_fork(self):
+ self.count = 0
+
@unwrap_spec(kind=int, maxvalue=int)
def rebuild(space, w_cls, w_handle, kind, maxvalue):
self = space.allocate_instance(W_SemLock, w_cls)
@@ -512,6 +515,7 @@
acquire = interp2app(W_SemLock.acquire),
release = interp2app(W_SemLock.release),
_rebuild = interp2app(W_SemLock.rebuild.im_func, as_classmethod=True),
+ _after_fork = interp2app(W_SemLock.after_fork),
__enter__=interp2app(W_SemLock.enter),
__exit__=interp2app(W_SemLock.exit),
SEM_VALUE_MAX=SEM_VALUE_MAX,
diff --git a/pypy/module/_multiprocessing/test/test_connection.py b/pypy/module/_multiprocessing/test/test_connection.py
--- a/pypy/module/_multiprocessing/test/test_connection.py
+++ b/pypy/module/_multiprocessing/test/test_connection.py
@@ -145,3 +145,9 @@
else:
c.close()
space.delslice(w_connections, space.wrap(0), space.wrap(100))
+
+ def test_bad_fd(self):
+ import _multiprocessing
+
+ raises(IOError, _multiprocessing.Connection, -1)
+ raises(IOError, _multiprocessing.Connection, -15)
\ No newline at end of file
diff --git a/pypy/module/_multiprocessing/test/test_semaphore.py b/pypy/module/_multiprocessing/test/test_semaphore.py
--- a/pypy/module/_multiprocessing/test/test_semaphore.py
+++ b/pypy/module/_multiprocessing/test/test_semaphore.py
@@ -39,6 +39,10 @@
sem.release()
assert sem._count() == 0
+ sem.acquire()
+ sem._after_fork()
+ assert sem._count() == 0
+
def test_recursive(self):
from _multiprocessing import SemLock
kind = self.RECURSIVE
diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py
--- a/pypy/module/_sre/interp_sre.py
+++ b/pypy/module/_sre/interp_sre.py
@@ -99,6 +99,7 @@
# SRE_Pattern class
class W_SRE_Pattern(Wrappable):
+ _immutable_fields_ = ["code", "flags"]
def cannot_copy_w(self):
space = self.space
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -57,6 +57,9 @@
compile_extra=['-DPy_BUILD_CORE'],
)
+class CConfig2:
+ _compilation_info_ = CConfig._compilation_info_
+
class CConfig_constants:
_compilation_info_ = CConfig._compilation_info_
@@ -300,9 +303,13 @@
return unwrapper_raise # used in 'normal' RPython code.
return decorate
-def cpython_struct(name, fields, forward=None):
+def cpython_struct(name, fields, forward=None, level=1):
configname = name.replace(' ', '__')
- setattr(CConfig, configname, rffi_platform.Struct(name, fields))
+ if level == 1:
+ config = CConfig
+ else:
+ config = CConfig2
+ setattr(config, configname, rffi_platform.Struct(name, fields))
if forward is None:
forward = lltype.ForwardReference()
TYPES[configname] = forward
@@ -445,9 +452,10 @@
# 'int*': rffi.INTP}
def configure_types():
- for name, TYPE in rffi_platform.configure(CConfig).iteritems():
- if name in TYPES:
- TYPES[name].become(TYPE)
+ for config in (CConfig, CConfig2):
+ for name, TYPE in rffi_platform.configure(config).iteritems():
+ if name in TYPES:
+ TYPES[name].become(TYPE)
def build_type_checkers(type_name, cls=None):
"""
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -321,6 +321,15 @@
} PyTypeObject;
+typedef struct {
+ PyTypeObject ht_type;
+ PyNumberMethods as_number;
+ PyMappingMethods as_mapping;
+ PySequenceMethods as_sequence;
+ PyBufferProcs as_buffer;
+ PyObject *ht_name, *ht_slots;
+} PyHeapTypeObject;
+
/* Flag bits for printing: */
#define Py_PRINT_RAW 1 /* No string quotes etc. */
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -19,13 +19,42 @@
basestruct = PyObject.TO
def get_dealloc(self, space):
- raise NotImplementedError
+ from pypy.module.cpyext.typeobject import subtype_dealloc
+ return llhelper(
+ subtype_dealloc.api_func.functype,
+ subtype_dealloc.api_func.get_wrapper(space))
+
def allocate(self, space, w_type, itemcount=0):
- raise NotImplementedError
+ # similar to PyType_GenericAlloc?
+ # except that it's not related to any pypy object.
+
+ pytype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_type))
+ # Don't increase refcount for non-heaptypes
+ if pytype:
+ flags = rffi.cast(lltype.Signed, pytype.c_tp_flags)
+ if not flags & Py_TPFLAGS_HEAPTYPE:
+ Py_DecRef(space, w_type)
+
+ if pytype:
+ size = pytype.c_tp_basicsize
+ else:
+ size = rffi.sizeof(self.basestruct)
+ if itemcount:
+ size += itemcount * pytype.c_tp_itemsize
+ buf = lltype.malloc(rffi.VOIDP.TO, size,
+ flavor='raw', zero=True)
+ pyobj = rffi.cast(PyObject, buf)
+ pyobj.c_ob_refcnt = 1
+ pyobj.c_ob_type = pytype
+ return pyobj
+
def attach(self, space, pyobj, w_obj):
- raise NotImplementedError
+ pass
+
def realize(self, space, ref):
- raise NotImplementedError
+ # For most types, a reference cannot exist without
+ # a real interpreter object
+ raise InvalidPointerException(str(ref))
typedescr_cache = {}
@@ -40,6 +69,7 @@
"""
tp_basestruct = kw.pop('basestruct', PyObject.TO)
+ tp_alloc = kw.pop('alloc', None)
tp_attach = kw.pop('attach', None)
tp_realize = kw.pop('realize', None)
tp_dealloc = kw.pop('dealloc', None)
@@ -49,58 +79,24 @@
class CpyTypedescr(BaseCpyTypedescr):
basestruct = tp_basestruct
- realize = tp_realize
- def get_dealloc(self, space):
- if tp_dealloc:
+ if tp_alloc:
+ def allocate(self, space, w_type, itemcount=0):
+ return tp_alloc(space, w_type)
+
+ if tp_dealloc:
+ def get_dealloc(self, space):
return llhelper(
tp_dealloc.api_func.functype,
tp_dealloc.api_func.get_wrapper(space))
- else:
- from pypy.module.cpyext.typeobject import subtype_dealloc
- return llhelper(
- subtype_dealloc.api_func.functype,
- subtype_dealloc.api_func.get_wrapper(space))
-
- def allocate(self, space, w_type, itemcount=0):
- # similar to PyType_GenericAlloc?
- # except that it's not related to any pypy object.
-
- pytype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_type))
- # Don't increase refcount for non-heaptypes
- if pytype:
- flags = rffi.cast(lltype.Signed, pytype.c_tp_flags)
- if not flags & Py_TPFLAGS_HEAPTYPE:
- Py_DecRef(space, w_type)
-
- if pytype:
- size = pytype.c_tp_basicsize
- else:
- size = rffi.sizeof(tp_basestruct)
- if itemcount:
- size += itemcount * pytype.c_tp_itemsize
- buf = lltype.malloc(rffi.VOIDP.TO, size,
- flavor='raw', zero=True)
- pyobj = rffi.cast(PyObject, buf)
- pyobj.c_ob_refcnt = 1
- pyobj.c_ob_type = pytype
- return pyobj
if tp_attach:
def attach(self, space, pyobj, w_obj):
tp_attach(space, pyobj, w_obj)
- else:
- def attach(self, space, pyobj, w_obj):
- pass
if tp_realize:
def realize(self, space, ref):
return tp_realize(space, ref)
- else:
- def realize(self, space, ref):
- # For most types, a reference cannot exist without
- # a real interpreter object
- raise InvalidPointerException(str(ref))
if typedef:
CpyTypedescr.__name__ = "CpyTypedescr_%s" % (typedef.name,)
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -268,6 +268,21 @@
assert type(obj) is foo.Custom
assert type(foo.Custom) is foo.MetaType
+ def test_heaptype(self):
+ module = self.import_extension('foo', [
+ ("name_by_heaptype", "METH_O",
+ '''
+ PyHeapTypeObject *heaptype = (PyHeapTypeObject *)args;
+ Py_INCREF(heaptype->ht_name);
+ return heaptype->ht_name;
+ '''
+ )
+ ])
+ class C(object):
+ pass
+ assert module.name_by_heaptype(C) == "C"
+
+
class TestTypes(BaseApiTest):
def test_type_attributes(self, space, api):
w_class = space.appexec([], """():
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -11,7 +11,7 @@
generic_cpy_call, Py_TPFLAGS_READY, Py_TPFLAGS_READYING,
Py_TPFLAGS_HEAPTYPE, METH_VARARGS, METH_KEYWORDS, CANNOT_FAIL,
Py_TPFLAGS_HAVE_GETCHARBUFFER,
- build_type_checkers)
+ build_type_checkers, PyObjectFields)
from pypy.module.cpyext.pyobject import (
PyObject, make_ref, create_ref, from_ref, get_typedescr, make_typedescr,
track_reference, RefcountState, borrow_from)
@@ -25,7 +25,7 @@
from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne
from pypy.module.cpyext.typeobjectdefs import (
PyTypeObjectPtr, PyTypeObject, PyGetSetDef, PyMemberDef, newfunc,
- PyNumberMethods, PySequenceMethods, PyBufferProcs)
+ PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs)
from pypy.module.cpyext.slotdefs import (
slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function)
from pypy.interpreter.error import OperationError
@@ -39,6 +39,19 @@
PyType_Check, PyType_CheckExact = build_type_checkers("Type", "w_type")
+PyHeapTypeObjectStruct = lltype.ForwardReference()
+PyHeapTypeObject = lltype.Ptr(PyHeapTypeObjectStruct)
+PyHeapTypeObjectFields = (
+ ("ht_type", PyTypeObject),
+ ("ht_name", PyObject),
+ ("as_number", PyNumberMethods),
+ ("as_mapping", PyMappingMethods),
+ ("as_sequence", PySequenceMethods),
+ ("as_buffer", PyBufferProcs),
+ )
+cpython_struct("PyHeapTypeObject", PyHeapTypeObjectFields, PyHeapTypeObjectStruct,
+ level=2)
+
class W_GetSetPropertyEx(GetSetProperty):
def __init__(self, getset, w_type):
self.getset = getset
@@ -136,6 +149,8 @@
assert len(slot_names) == 2
struct = getattr(pto, slot_names[0])
if not struct:
+ assert not space.config.translating
+ assert not pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE
if slot_names[0] == 'c_tp_as_number':
STRUCT_TYPE = PyNumberMethods
elif slot_names[0] == 'c_tp_as_sequence':
@@ -301,6 +316,7 @@
make_typedescr(space.w_type.instancetypedef,
basestruct=PyTypeObject,
+ alloc=type_alloc,
attach=type_attach,
realize=type_realize,
dealloc=type_dealloc)
@@ -319,11 +335,13 @@
track_reference(space, lltype.nullptr(PyObject.TO), space.w_type)
track_reference(space, lltype.nullptr(PyObject.TO), space.w_object)
track_reference(space, lltype.nullptr(PyObject.TO), space.w_tuple)
+ track_reference(space, lltype.nullptr(PyObject.TO), space.w_str)
# create the objects
py_type = create_ref(space, space.w_type)
py_object = create_ref(space, space.w_object)
py_tuple = create_ref(space, space.w_tuple)
+ py_str = create_ref(space, space.w_str)
# form cycles
pto_type = rffi.cast(PyTypeObjectPtr, py_type)
@@ -340,10 +358,15 @@
pto_object.c_tp_bases.c_ob_type = pto_tuple
pto_tuple.c_tp_bases.c_ob_type = pto_tuple
+ for typ in (py_type, py_object, py_tuple, py_str):
+ heaptype = rffi.cast(PyHeapTypeObject, typ)
+ heaptype.c_ht_name.c_ob_type = pto_type
+
# Restore the mapping
track_reference(space, py_type, space.w_type, replace=True)
track_reference(space, py_object, space.w_object, replace=True)
track_reference(space, py_tuple, space.w_tuple, replace=True)
+ track_reference(space, py_str, space.w_str, replace=True)
@cpython_api([PyObject], lltype.Void, external=False)
@@ -416,17 +439,34 @@
Py_DecRef(space, obj_pto.c_tp_cache) # let's do it like cpython
Py_DecRef(space, obj_pto.c_tp_dict)
if obj_pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
- if obj_pto.c_tp_as_buffer:
- lltype.free(obj_pto.c_tp_as_buffer, flavor='raw')
- if obj_pto.c_tp_as_number:
- lltype.free(obj_pto.c_tp_as_number, flavor='raw')
- if obj_pto.c_tp_as_sequence:
- lltype.free(obj_pto.c_tp_as_sequence, flavor='raw')
+ heaptype = rffi.cast(PyHeapTypeObject, obj)
+ Py_DecRef(space, heaptype.c_ht_name)
Py_DecRef(space, base_pyo)
- rffi.free_charp(obj_pto.c_tp_name)
PyObject_dealloc(space, obj)
+def type_alloc(space, w_metatype):
+ size = rffi.sizeof(PyHeapTypeObject)
+ metatype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_metatype))
+ # Don't increase refcount for non-heaptypes
+ if metatype:
+ flags = rffi.cast(lltype.Signed, metatype.c_tp_flags)
+ if not flags & Py_TPFLAGS_HEAPTYPE:
+ Py_DecRef(space, w_metatype)
+
+ heaptype = lltype.malloc(PyHeapTypeObject.TO,
+ flavor='raw', zero=True)
+ pto = heaptype.c_ht_type
+ pto.c_ob_refcnt = 1
+ pto.c_ob_type = metatype
+ pto.c_tp_flags |= Py_TPFLAGS_HEAPTYPE
+ pto.c_tp_as_number = heaptype.c_as_number
+ pto.c_tp_as_sequence = heaptype.c_as_sequence
+ pto.c_tp_as_mapping = heaptype.c_as_mapping
+ pto.c_tp_as_buffer = heaptype.c_as_buffer
+
+ return rffi.cast(PyObject, heaptype)
+
def type_attach(space, py_obj, w_type):
"""
Fills a newly allocated PyTypeObject from an existing type.
@@ -445,12 +485,18 @@
if space.is_w(w_type, space.w_str):
setup_string_buffer_procs(space, pto)
- pto.c_tp_flags |= Py_TPFLAGS_HEAPTYPE
pto.c_tp_free = llhelper(PyObject_Del.api_func.functype,
PyObject_Del.api_func.get_wrapper(space))
pto.c_tp_alloc = llhelper(PyType_GenericAlloc.api_func.functype,
PyType_GenericAlloc.api_func.get_wrapper(space))
- pto.c_tp_name = rffi.str2charp(w_type.getname(space))
+ if pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
+ w_typename = space.getattr(w_type, space.wrap('__name__'))
+ heaptype = rffi.cast(PyHeapTypeObject, pto)
+ heaptype.c_ht_name = make_ref(space, w_typename)
+ from pypy.module.cpyext.stringobject import PyString_AsString
+ pto.c_tp_name = PyString_AsString(space, heaptype.c_ht_name)
+ else:
+ pto.c_tp_name = rffi.str2charp(w_type.getname(space))
pto.c_tp_basicsize = -1 # hopefully this makes malloc bail out
pto.c_tp_itemsize = 0
# uninitialized fields:
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
@@ -23,6 +23,8 @@
("arccos", "arccos"),
("arcsin", "arcsin"),
("arctan", "arctan"),
+ ("arcsinh", "arcsinh"),
+ ("arctanh", "arctanh"),
("copysign", "copysign"),
("cos", "cos"),
("divide", "divide"),
@@ -50,4 +52,6 @@
appleveldefs = {
'average': 'app_numpy.average',
'mean': 'app_numpy.mean',
+ 'inf': 'app_numpy.inf',
+ 'e': 'app_numpy.e',
}
diff --git a/pypy/module/micronumpy/app_numpy.py b/pypy/module/micronumpy/app_numpy.py
--- a/pypy/module/micronumpy/app_numpy.py
+++ b/pypy/module/micronumpy/app_numpy.py
@@ -1,5 +1,11 @@
+import math
+
import numpy
+
+inf = float("inf")
+e = math.e
+
def average(a):
# This implements a weighted average, for now we don't implement the
# weighting, just the average part!
@@ -8,4 +14,4 @@
def mean(a):
if not hasattr(a, "mean"):
a = numpy.array(a)
- return a.mean()
\ No newline at end of file
+ return a.mean()
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -7,13 +7,14 @@
from pypy.interpreter.typedef import TypeDef, interp_attrproperty, GetSetProperty
from pypy.module.micronumpy import signature
from pypy.objspace.std.floatobject import float2string
-from pypy.rlib import rfloat
-from pypy.rlib.rarithmetic import widen
+from pypy.rlib import rarithmetic, rfloat
+from pypy.rlib.rarithmetic import LONG_BIT, widen
from pypy.rlib.objectmodel import specialize, enforceargs
from pypy.rlib.unroll import unrolling_iterable
from pypy.rpython.lltypesystem import lltype, rffi
+UNSIGNEDLTR = "u"
SIGNEDLTR = "i"
BOOLLTR = "b"
FLOATINGLTR = "f"
@@ -61,7 +62,10 @@
self.val = val
def wrap(self, space):
- return space.wrap(self.val)
+ val = self.val
+ if valtype is rarithmetic.r_singlefloat:
+ val = float(val)
+ return space.wrap(val)
def convert_to(self, dtype):
return dtype.adapt_val(self.val)
@@ -145,7 +149,7 @@
return self.adapt_val(func(self, self.for_computation(self.unbox(v))))
return impl
-class ArithmaticTypeMixin(object):
+class ArithmeticTypeMixin(object):
_mixin_ = True
@binop
@@ -157,9 +161,6 @@
@binop
def mul(self, v1, v2):
return v1 * v2
- @binop
- def div(self, v1, v2):
- return v1 / v2
@unaryop
def pos(self, v):
@@ -200,12 +201,26 @@
return v1 >= v2
-class FloatArithmeticDtype(ArithmaticTypeMixin):
+class FloatArithmeticDtype(ArithmeticTypeMixin):
_mixin_ = True
+ def unwrap(self, space, w_item):
+ return self.adapt_val(space.float_w(space.float(w_item)))
+
def for_computation(self, v):
- return v
+ return float(v)
+ def str_format(self, item):
+ return float2string(self.for_computation(self.unbox(item)), 'g', rfloat.DTSF_STR_PRECISION)
+
+ @binop
+ def div(self, v1, v2):
+ try:
+ return v1 / v2
+ except ZeroDivisionError:
+ if v1 == v2 == 0.0:
+ return rfloat.NAN
+ return rfloat.copysign(rfloat.INFINITY, v1 * v2)
@binop
def mod(self, v1, v2):
return math.fmod(v1, v2)
@@ -250,19 +265,29 @@
return math.tan(v)
@unaryop
def arcsin(self, v):
- if v < -1.0 or v > 1.0:
+ if not -1.0 <= v <= 1.0:
return rfloat.NAN
return math.asin(v)
@unaryop
def arccos(self, v):
- if v < -1.0 or v > 1.0:
+ if not -1.0 <= v <= 1.0:
return rfloat.NAN
return math.acos(v)
@unaryop
def arctan(self, v):
return math.atan(v)
+ @unaryop
+ def arcsinh(self, v):
+ return math.asinh(v)
+ @unaryop
+ def arctanh(self, v):
+ if v == 1.0 or v == -1.0:
+ return math.copysign(rfloat.INFINITY, v)
+ if not -1.0 < v < 1.0:
+ return rfloat.NAN
+ return math.atanh(v)
-class IntegerArithmeticDtype(ArithmaticTypeMixin):
+class IntegerArithmeticDtype(ArithmeticTypeMixin):
_mixin_ = True
def unwrap(self, space, w_item):
@@ -271,10 +296,21 @@
def for_computation(self, v):
return widen(v)
+ def str_format(self, item):
+ return str(widen(self.unbox(item)))
+
+ @binop
+ def div(self, v1, v2):
+ if v2 == 0:
+ return 0
+ return v1 / v2
@binop
def mod(self, v1, v2):
return v1 % v2
+class SignedIntegerArithmeticDtype(IntegerArithmeticDtype):
+ _mixin_ = True
+
@unaryop
def sign(self, v):
if v > 0:
@@ -285,17 +321,22 @@
assert v == 0
return 0
- def str_format(self, item):
- return str(widen(self.unbox(item)))
+class UnsignedIntegerArithmeticDtype(IntegerArithmeticDtype):
+ _mixin_ = True
+
+ @unaryop
+ def sign(self, v):
+ return int(v != 0)
+
W_BoolDtype = create_low_level_dtype(
num = 0, kind = BOOLLTR, name = "bool",
- aliases = ["?"],
+ aliases = ["?", "bool", "bool8"],
applevel_types = ["bool"],
T = lltype.Bool,
valtype = bool,
)
-class W_BoolDtype(IntegerArithmeticDtype, W_BoolDtype):
+class W_BoolDtype(SignedIntegerArithmeticDtype, W_BoolDtype):
def unwrap(self, space, w_item):
return self.adapt_val(space.is_true(w_item))
@@ -308,67 +349,138 @@
W_Int8Dtype = create_low_level_dtype(
num = 1, kind = SIGNEDLTR, name = "int8",
- aliases = ["int8"],
+ aliases = ["b", "int8", "i1"],
applevel_types = [],
T = rffi.SIGNEDCHAR,
valtype = rffi.SIGNEDCHAR._type,
expected_size = 1,
)
-class W_Int8Dtype(IntegerArithmeticDtype, W_Int8Dtype):
+class W_Int8Dtype(SignedIntegerArithmeticDtype, W_Int8Dtype):
+ pass
+
+W_UInt8Dtype = create_low_level_dtype(
+ num = 2, kind = UNSIGNEDLTR, name = "uint8",
+ aliases = ["B", "uint8", "I1"],
+ applevel_types = [],
+ T = rffi.UCHAR,
+ valtype = rffi.UCHAR._type,
+ expected_size = 1,
+)
+class W_UInt8Dtype(UnsignedIntegerArithmeticDtype, W_UInt8Dtype):
pass
W_Int16Dtype = create_low_level_dtype(
num = 3, kind = SIGNEDLTR, name = "int16",
- aliases = ["int16"],
+ aliases = ["h", "int16", "i2"],
applevel_types = [],
T = rffi.SHORT,
valtype = rffi.SHORT._type,
expected_size = 2,
)
-class W_Int16Dtype(IntegerArithmeticDtype, W_Int16Dtype):
+class W_Int16Dtype(SignedIntegerArithmeticDtype, W_Int16Dtype):
+ pass
+
+W_UInt16Dtype = create_low_level_dtype(
+ num = 4, kind = UNSIGNEDLTR, name = "uint16",
+ aliases = ["H", "uint16", "I2"],
+ applevel_types = [],
+ T = rffi.USHORT,
+ valtype = rffi.USHORT._type,
+ expected_size = 2,
+)
+class W_UInt16Dtype(UnsignedIntegerArithmeticDtype, W_UInt16Dtype):
pass
W_Int32Dtype = create_low_level_dtype(
num = 5, kind = SIGNEDLTR, name = "int32",
- aliases = ["i"],
+ aliases = ["i", "int32", "i4"],
applevel_types = [],
T = rffi.INT,
valtype = rffi.INT._type,
expected_size = 4,
)
-class W_Int32Dtype(IntegerArithmeticDtype, W_Int32Dtype):
+class W_Int32Dtype(SignedIntegerArithmeticDtype, W_Int32Dtype):
+ pass
+
+W_UInt32Dtype = create_low_level_dtype(
+ num = 6, kind = UNSIGNEDLTR, name = "uint32",
+ aliases = ["I", "uint32", "I4"],
+ applevel_types = [],
+ T = rffi.UINT,
+ valtype = rffi.UINT._type,
+ expected_size = 4,
+)
+class W_UInt32Dtype(UnsignedIntegerArithmeticDtype, W_UInt32Dtype):
pass
W_Int64Dtype = create_low_level_dtype(
num = 9, kind = SIGNEDLTR, name = "int64",
- aliases = [],
+ aliases = ["q", "int64", "i8"],
applevel_types = ["long"],
T = rffi.LONGLONG,
valtype = rffi.LONGLONG._type,
expected_size = 8,
)
-class W_Int64Dtype(IntegerArithmeticDtype, W_Int64Dtype):
+class W_Int64Dtype(SignedIntegerArithmeticDtype, W_Int64Dtype):
+ pass
+
+W_UInt64Dtype = create_low_level_dtype(
+ num = 10, kind = UNSIGNEDLTR, name = "uint64",
+ aliases = ["Q", "uint64", "I8"],
+ applevel_types = [],
+ T = rffi.ULONGLONG,
+ valtype = rffi.ULONGLONG._type,
+ expected_size = 8,
+)
+class W_UInt64Dtype(UnsignedIntegerArithmeticDtype, W_UInt64Dtype):
+ pass
+
+if LONG_BIT == 32:
+ long_dtype = W_Int32Dtype
+ ulong_dtype = W_UInt32Dtype
+elif LONG_BIT == 64:
+ long_dtype = W_Int64Dtype
+ ulong_dtype = W_UInt64Dtype
+else:
+ assert False
+
+class W_LongDtype(long_dtype):
+ num = 7
+ aliases = ["l"]
+ applevel_types = ["int"]
+
+class W_ULongDtype(ulong_dtype):
+ num = 8
+ aliases = ["L"]
+
+W_Float32Dtype = create_low_level_dtype(
+ num = 11, kind = FLOATINGLTR, name = "float32",
+ aliases = ["f", "float32", "f4"],
+ applevel_types = [],
+ T = lltype.SingleFloat,
+ valtype = rarithmetic.r_singlefloat,
+ expected_size = 4,
+)
+class W_Float32Dtype(FloatArithmeticDtype, W_Float32Dtype):
pass
W_Float64Dtype = create_low_level_dtype(
num = 12, kind = FLOATINGLTR, name = "float64",
- aliases = [],
+ aliases = ["d", "float64", "f8"],
applevel_types = ["float"],
T = lltype.Float,
valtype = float,
expected_size = 8,
)
class W_Float64Dtype(FloatArithmeticDtype, W_Float64Dtype):
- def unwrap(self, space, w_item):
- return self.adapt_val(space.float_w(space.float(w_item)))
-
- def str_format(self, item):
- return float2string(self.unbox(item), 'g', rfloat.DTSF_STR_PRECISION)
+ pass
ALL_DTYPES = [
W_BoolDtype,
- W_Int8Dtype, W_Int16Dtype, W_Int32Dtype, W_Int64Dtype,
- W_Float64Dtype
+ W_Int8Dtype, W_UInt8Dtype, W_Int16Dtype, W_UInt16Dtype,
+ W_Int32Dtype, W_UInt32Dtype, W_LongDtype, W_ULongDtype,
+ W_Int64Dtype, W_UInt64Dtype,
+ W_Float32Dtype, W_Float64Dtype,
]
dtypes_by_alias = unrolling_iterable([
@@ -395,6 +507,7 @@
num = interp_attrproperty("num", cls=W_Dtype),
kind = interp_attrproperty("kind", cls=W_Dtype),
+ itemsize = interp_attrproperty("num_bytes", cls=W_Dtype),
shape = GetSetProperty(W_Dtype.descr_get_shape),
)
W_Dtype.typedef.acceptable_as_base_class = False
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
@@ -4,6 +4,7 @@
from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
from pypy.module.micronumpy import interp_dtype, signature
from pypy.rlib import jit
+from pypy.rlib.rarithmetic import LONG_BIT
from pypy.tool.sourcetools import func_with_new_name
@@ -180,23 +181,56 @@
# Everything promotes to float, and bool promotes to everything.
if dt2.kind == interp_dtype.FLOATINGLTR or dt1.kind == interp_dtype.BOOLLTR:
+ # Float32 + 8-bit int = Float64
+ if dt2.num == 11 and dt1.num_bytes >= 4:
+ return space.fromcache(interp_dtype.W_Float64Dtype)
return dt2
- assert False
+ # for now this means mixing signed and unsigned
+ if dt2.kind == interp_dtype.SIGNEDLTR:
+ # if dt2 has a greater number of bytes, then just go with it
+ if dt1.num_bytes < dt2.num_bytes:
+ return dt2
+ # we need to promote both dtypes
+ dtypenum = dt2.num + 2
+ else:
+ # increase to the next signed type (or to float)
+ dtypenum = dt2.num + 1
+ # UInt64 + signed = Float64
+ if dt2.num == 10:
+ dtypenum += 1
+ newdtype = interp_dtype.ALL_DTYPES[dtypenum]
+
+ if newdtype.num_bytes > dt2.num_bytes or newdtype.kind == interp_dtype.FLOATINGLTR:
+ return space.fromcache(newdtype)
+ else:
+ # we only promoted to long on 32-bit or to longlong on 64-bit
+ # this is really for dealing with the Long and Ulong dtypes
+ if LONG_BIT == 32:
+ dtypenum += 2
+ else:
+ dtypenum += 3
+ return space.fromcache(interp_dtype.ALL_DTYPES[dtypenum])
def find_unaryop_result_dtype(space, dt, promote_to_float=False,
promote_bools=False, promote_to_largest=False):
if promote_bools and (dt.kind == interp_dtype.BOOLLTR):
return space.fromcache(interp_dtype.W_Int8Dtype)
if promote_to_float:
+ if dt.kind == interp_dtype.FLOATINGLTR:
+ return dt
+ if dt.num >= 5:
+ return space.fromcache(interp_dtype.W_Float64Dtype)
for bytes, dtype in interp_dtype.dtypes_by_num_bytes:
- if dtype.kind == interp_dtype.FLOATINGLTR and dtype.num_bytes >= dt.num_bytes:
+ if dtype.kind == interp_dtype.FLOATINGLTR and dtype.num_bytes > dt.num_bytes:
return space.fromcache(dtype)
if promote_to_largest:
if dt.kind == interp_dtype.BOOLLTR or dt.kind == interp_dtype.SIGNEDLTR:
return space.fromcache(interp_dtype.W_Int64Dtype)
elif dt.kind == interp_dtype.FLOATINGLTR:
return space.fromcache(interp_dtype.W_Float64Dtype)
+ elif dt.kind == interp_dtype.UNSIGNEDLTR:
+ return space.fromcache(interp_dtype.W_UInt64Dtype)
else:
assert False
return dt
@@ -205,15 +239,23 @@
w_type = space.type(w_obj)
bool_dtype = space.fromcache(interp_dtype.W_BoolDtype)
+ long_dtype = space.fromcache(interp_dtype.W_LongDtype)
int64_dtype = space.fromcache(interp_dtype.W_Int64Dtype)
if space.is_w(w_type, space.w_bool):
- if current_guess is None:
+ if current_guess is None or current_guess is bool_dtype:
return bool_dtype
+ return current_guess
elif space.is_w(w_type, space.w_int):
if (current_guess is None or current_guess is bool_dtype or
- current_guess is int64_dtype):
+ current_guess is long_dtype):
+ return long_dtype
+ return current_guess
+ elif space.is_w(w_type, space.w_long):
+ if (current_guess is None or current_guess is bool_dtype or
+ current_guess is long_dtype or current_guess is int64_dtype):
return int64_dtype
+ return current_guess
return space.fromcache(interp_dtype.W_Float64Dtype)
@@ -225,7 +267,9 @@
def impl(res_dtype, lvalue, rvalue):
res = getattr(res_dtype, op_name)(lvalue, rvalue)
if comparison_func:
- res = space.fromcache(interp_dtype.W_BoolDtype).box(res)
+ booldtype = space.fromcache(interp_dtype.W_BoolDtype)
+ assert isinstance(booldtype, interp_dtype.W_BoolDtype)
+ res = booldtype.box(res)
return res
return func_with_new_name(impl, ufunc_name)
@@ -268,6 +312,8 @@
("arcsin", "arcsin", 1, {"promote_to_float": True}),
("arccos", "arccos", 1, {"promote_to_float": True}),
("arctan", "arctan", 1, {"promote_to_float": True}),
+ ("arcsinh", "arcsinh", 1, {"promote_to_float": True}),
+ ("arctanh", "arctanh", 1, {"promote_to_float": True}),
]:
self.add_ufunc(space, *ufunc_def)
@@ -277,7 +323,7 @@
identity = extra_kwargs.get("identity")
if identity is not None:
- identity = space.fromcache(interp_dtype.W_Int64Dtype).adapt_val(identity)
+ identity = space.fromcache(interp_dtype.W_LongDtype).adapt_val(identity)
extra_kwargs["identity"] = identity
func = ufunc_dtype_caller(space, ufunc_name, op_name, argcount,
@@ -290,4 +336,4 @@
setattr(self, ufunc_name, ufunc)
def get(space):
- return space.fromcache(UfuncState)
\ No newline at end of file
+ return space.fromcache(UfuncState)
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
--- a/pypy/module/micronumpy/test/test_base.py
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -64,18 +64,46 @@
def test_unaryops(self, space):
bool_dtype = space.fromcache(interp_dtype.W_BoolDtype)
int8_dtype = space.fromcache(interp_dtype.W_Int8Dtype)
+ uint8_dtype = space.fromcache(interp_dtype.W_UInt8Dtype)
+ int16_dtype = space.fromcache(interp_dtype.W_Int16Dtype)
+ uint16_dtype = space.fromcache(interp_dtype.W_UInt16Dtype)
int32_dtype = space.fromcache(interp_dtype.W_Int32Dtype)
+ uint32_dtype = space.fromcache(interp_dtype.W_UInt32Dtype)
+ long_dtype = space.fromcache(interp_dtype.W_LongDtype)
+ ulong_dtype = space.fromcache(interp_dtype.W_ULongDtype)
+ int64_dtype = space.fromcache(interp_dtype.W_Int64Dtype)
+ uint64_dtype = space.fromcache(interp_dtype.W_UInt64Dtype)
+ float32_dtype = space.fromcache(interp_dtype.W_Float32Dtype)
float64_dtype = space.fromcache(interp_dtype.W_Float64Dtype)
- # Normal rules, everythign returns itself
+ # Normal rules, everything returns itself
assert find_unaryop_result_dtype(space, bool_dtype) is bool_dtype
assert find_unaryop_result_dtype(space, int8_dtype) is int8_dtype
+ assert find_unaryop_result_dtype(space, uint8_dtype) is uint8_dtype
+ assert find_unaryop_result_dtype(space, int16_dtype) is int16_dtype
+ assert find_unaryop_result_dtype(space, uint16_dtype) is uint16_dtype
assert find_unaryop_result_dtype(space, int32_dtype) is int32_dtype
+ assert find_unaryop_result_dtype(space, uint32_dtype) is uint32_dtype
+ assert find_unaryop_result_dtype(space, long_dtype) is long_dtype
+ assert find_unaryop_result_dtype(space, ulong_dtype) is ulong_dtype
+ assert find_unaryop_result_dtype(space, int64_dtype) is int64_dtype
+ assert find_unaryop_result_dtype(space, uint64_dtype) is uint64_dtype
+ assert find_unaryop_result_dtype(space, float32_dtype) is float32_dtype
assert find_unaryop_result_dtype(space, float64_dtype) is float64_dtype
# Coerce to floats, some of these will eventually be float16, or
# whatever our smallest float type is.
- assert find_unaryop_result_dtype(space, bool_dtype, promote_to_float=True) is float64_dtype
- assert find_unaryop_result_dtype(space, int8_dtype, promote_to_float=True) is float64_dtype
+ assert find_unaryop_result_dtype(space, bool_dtype, promote_to_float=True) is float32_dtype # will be float16 if we ever put that in
+ assert find_unaryop_result_dtype(space, int8_dtype, promote_to_float=True) is float32_dtype # will be float16 if we ever put that in
+ assert find_unaryop_result_dtype(space, uint8_dtype, promote_to_float=True) is float32_dtype # will be float16 if we ever put that in
+ assert find_unaryop_result_dtype(space, int16_dtype, promote_to_float=True) is float32_dtype
+ assert find_unaryop_result_dtype(space, uint16_dtype, promote_to_float=True) is float32_dtype
assert find_unaryop_result_dtype(space, int32_dtype, promote_to_float=True) is float64_dtype
- assert find_unaryop_result_dtype(space, float64_dtype, promote_to_float=True) is float64_dtype
\ No newline at end of file
+ assert find_unaryop_result_dtype(space, uint32_dtype, promote_to_float=True) is float64_dtype
+ assert find_unaryop_result_dtype(space, int64_dtype, promote_to_float=True) is float64_dtype
+ assert find_unaryop_result_dtype(space, uint64_dtype, promote_to_float=True) is float64_dtype
+ assert find_unaryop_result_dtype(space, float32_dtype, promote_to_float=True) is float32_dtype
+ assert find_unaryop_result_dtype(space, float64_dtype, promote_to_float=True) is float64_dtype
+
+ # promote bools, happens with sign ufunc
+ assert find_unaryop_result_dtype(space, bool_dtype, promote_bools=True) is int8_dtype
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -17,6 +17,7 @@
from numpy import dtype
assert dtype(bool).num == 0
+ assert dtype(int).num == 7
assert dtype(long).num == 9
assert dtype(float).num == 12
@@ -81,6 +82,48 @@
assert isinstance(a[i], (int, long))
assert a[1] == 1
+ def test_overflow(self):
+ from numpy import array, dtype
+ assert array([128], 'b')[0] == -128
+ assert array([256], 'B')[0] == 0
+ assert array([32768], 'h')[0] == -32768
+ assert array([65536], 'H')[0] == 0
+ if dtype('l').itemsize == 4: # 32-bit
+ raises(OverflowError, "array([2**32/2], 'i')")
+ raises(OverflowError, "array([2**32], 'I')")
+ raises(OverflowError, "array([2**64/2], 'q')")
+ raises(OverflowError, "array([2**64], 'Q')")
+
+ def test_bool_binop_types(self):
+ from numpy import array, dtype
+ types = ('?','b','B','h','H','i','I','l','L','q','Q','f','d')
+ N = len(types)
+ a = array([True], '?')
+ for t in types:
+ assert (a + array([0], t)).dtype is dtype(t)
+
+ def test_binop_types(self):
+ from numpy import array, dtype
+ tests = [('b','B','h'), ('b','h','h'), ('b','H','i'), ('b','i','i'),
+ ('b','l','l'), ('b','q','q'), ('b','Q','d'), ('B','h','h'),
+ ('B','H','H'), ('B','i','i'), ('B','I','I'), ('B','l','l'),
+ ('B','L','L'), ('B','q','q'), ('B','Q','Q'), ('h','H','i'),
+ ('h','i','i'), ('h','l','l'), ('h','q','q'), ('h','Q','d'),
+ ('H','i','i'), ('H','I','I'), ('H','l','l'), ('H','L','L'),
+ ('H','q','q'), ('H','Q','Q'), ('i','l','l'), ('i','q','q'),
+ ('i','Q','d'), ('I','L','L'), ('I','q','q'), ('I','Q','Q'),
+ ('q','Q','d'), ('b','f','f'), ('B','f','f'), ('h','f','f'),
+ ('H','f','f'), ('i','f','d'), ('I','f','d'), ('l','f','d'),
+ ('L','f','d'), ('q','f','d'), ('Q','f','d'), ('q','d','d')]
+ if dtype('i').itemsize == dtype('l').itemsize: # 32-bit
+ tests.extend([('b','I','q'), ('b','L','q'), ('h','I','q'),
+ ('h','L','q'), ('i','I','q'), ('i','L','q')])
+ else:
+ tests.extend([('b','I','l'), ('b','L','d'), ('h','I','l'),
+ ('h','L','d'), ('i','I','l'), ('i','L','d')])
+ for d1, d2, dout in tests:
+ assert (array([1], d1) + array([1], d2)).dtype is dtype(dout)
+
def test_add_int8(self):
from numpy import array, dtype
@@ -99,6 +142,15 @@
for i in range(5):
assert b[i] == i * 2
+ def test_add_uint32(self):
+ from numpy import array, dtype
+
+ a = array(range(5), dtype="I")
+ b = a + a
+ assert b.dtype is dtype("I")
+ for i in range(5):
+ assert b[i] == i * 2
+
def test_shape(self):
from numpy import dtype
diff --git a/pypy/module/micronumpy/test/test_module.py b/pypy/module/micronumpy/test/test_module.py
--- a/pypy/module/micronumpy/test/test_module.py
+++ b/pypy/module/micronumpy/test/test_module.py
@@ -10,4 +10,12 @@
def test_average(self):
from numpy import array, average
assert average(range(10)) == 4.5
- assert average(array(range(10))) == 4.5
\ No newline at end of file
+ assert average(array(range(10))) == 4.5
+
+ def test_constants(self):
+ import math
+ from numpy import inf, e
+ assert type(inf) is float
+ assert inf == float("inf")
+ assert e == math.e
+ assert type(e) is float
\ No newline at end of file
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
@@ -285,7 +285,9 @@
assert b[i] == i * 5
def test_div(self):
- from numpy import array, dtype
+ from math import isnan
+ from numpy import array, dtype, inf
+
a = array(range(1, 6))
b = a / a
for i in range(5):
@@ -297,6 +299,24 @@
for i in range(5):
assert b[i] == 1
+ a = array([-1, 0, 1])
+ b = array([0, 0, 0])
+ c = a / b
+ assert (c == [0, 0, 0]).all()
+
+ a = array([-1.0, 0.0, 1.0])
+ b = array([0.0, 0.0, 0.0])
+ c = a / b
+ assert c[0] == -inf
+ assert isnan(c[1])
+ assert c[2] == inf
+
+ b = array([-0.0, -0.0, -0.0])
+ c = a / b
+ assert c[0] == inf
+ assert isnan(c[1])
+ assert c[2] == -inf
+
def test_div_other(self):
from numpy import array
a = array(range(5))
@@ -551,8 +571,10 @@
from numpy import array, dtype
assert array([True]).dtype is dtype(bool)
- assert array([True, 1]).dtype is dtype(long)
- assert array([1, 2, 3]).dtype is dtype(long)
+ assert array([True, False]).dtype is dtype(bool)
+ assert array([True, 1]).dtype is dtype(int)
+ assert array([1, 2, 3]).dtype is dtype(int)
+ assert array([1L, 2, 3]).dtype is dtype(long)
assert array([1.2, True]).dtype is dtype(float)
assert array([1.2, 5]).dtype is dtype(float)
assert array([]).dtype is dtype(float)
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
@@ -234,7 +234,7 @@
assert b[i] == math.sin(a[i])
a = sin(array([True, False], dtype=bool))
- assert a[0] == sin(1)
+ assert abs(a[0] - sin(1)) < 1e-7 # a[0] will be less precise
assert a[1] == 0.0
def test_cos(self):
@@ -298,6 +298,25 @@
b = arctan(a)
assert math.isnan(b[0])
+ def test_arcsinh(self):
+ import math
+ from numpy import arcsinh, inf
+
+ for v in [inf, -inf, 1.0, math.e]:
+ assert math.asinh(v) == arcsinh(v)
+ assert math.isnan(arcsinh(float("nan")))
+
+ def test_arctanh(self):
+ import math
+ from numpy import arctanh
+
+ for v in [.99, .5, 0, -.5, -.99]:
+ assert math.atanh(v) == arctanh(v)
+ for v in [2.0, -2.0]:
+ assert math.isnan(arctanh(v))
+ for v in [1.0, -1.0]:
+ assert arctanh(v) == math.copysign(float("inf"), v)
+
def test_reduce_errors(self):
from numpy import sin, add
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
@@ -1,20 +1,24 @@
from pypy.jit.metainterp.test.support import LLJitMixin
from pypy.module.micronumpy import interp_ufuncs, signature
from pypy.module.micronumpy.compile import (numpy_compile, FakeSpace,
- FloatObject)
-from pypy.module.micronumpy.interp_dtype import W_Float64Dtype, W_Int64Dtype
+ FloatObject, IntObject)
+from pypy.module.micronumpy.interp_dtype import W_Int32Dtype, W_Float64Dtype, W_Int64Dtype, W_UInt64Dtype
from pypy.module.micronumpy.interp_numarray import (BaseArray, SingleDimArray,
SingleDimSlice, scalar_w)
from pypy.rlib.nonconst import NonConstant
from pypy.rpython.annlowlevel import llstr
from pypy.rpython.test.test_llinterp import interpret
+import py
+
class TestNumpyJIt(LLJitMixin):
def setup_class(cls):
cls.space = FakeSpace()
cls.float64_dtype = cls.space.fromcache(W_Float64Dtype)
cls.int64_dtype = cls.space.fromcache(W_Int64Dtype)
+ cls.uint64_dtype = cls.space.fromcache(W_UInt64Dtype)
+ cls.int32_dtype = cls.space.fromcache(W_Int32Dtype)
def test_add(self):
def f(i):
@@ -303,6 +307,31 @@
'int_lt': 1, 'guard_true': 1, 'jump': 1})
assert result == 11.0
+ def test_int32_sum(self):
+ py.test.skip("pypy/jit/backend/llimpl.py needs to be changed to "
+ "deal correctly with int dtypes for this test to "
+ "work. skip for now until someone feels up to the task")
+ space = self.space
+ float64_dtype = self.float64_dtype
+ int32_dtype = self.int32_dtype
+
+ def f(n):
+ if NonConstant(False):
+ dtype = float64_dtype
+ else:
+ dtype = int32_dtype
+ ar = SingleDimArray(n, dtype=dtype)
+ i = 0
+ while i < n:
+ ar.get_concrete().setitem(i, int32_dtype.box(7))
+ i += 1
+ v = ar.descr_add(space, ar).descr_sum(space)
+ assert isinstance(v, IntObject)
+ return v.intval
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ assert result == f(5)
+
class TestTranslation(object):
def test_compile(self):
x = numpy_compile('aa+f*f/a-', 10)
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
@@ -10,6 +10,7 @@
from pypy.rpython.lltypesystem import rffi, lltype
from pypy.rpython.tool import rffi_platform
from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.module.sys.interp_encoding import getfilesystemencoding
import os, sys
_WIN = sys.platform == 'win32'
@@ -32,17 +33,19 @@
raise OperationError(space.w_OverflowError,
space.wrap("integer out of range"))
+def fsencode_w(space, w_obj):
+ 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)
+
class FileEncoder(object):
def __init__(self, space, w_obj):
self.space = space
self.w_obj = w_obj
def as_bytes(self):
- from pypy.module.sys.interp_encoding import getfilesystemencoding
- space = self.space
- w_bytes = space.call_method(self.w_obj, 'encode',
- getfilesystemencoding(space))
- return space.str_w(w_bytes)
+ return fsencode_w(self.space, self.w_obj)
def as_unicode(self):
return self.space.unicode_w(self.w_obj)
@@ -56,7 +59,6 @@
return self.space.str_w(self.w_obj)
def as_unicode(self):
- from pypy.module.sys.interp_encoding import getfilesystemencoding
space = self.space
w_unicode = space.call_method(self.w_obj, 'decode',
getfilesystemencoding(space))
@@ -536,7 +538,6 @@
The list is in arbitrary order. It does not include the special
entries '.' and '..' even if they are present in the directory."""
- from pypy.module.sys.interp_encoding import getfilesystemencoding
try:
if space.isinstance_w(w_dirname, space.w_unicode):
dirname = FileEncoder(space, w_dirname)
@@ -734,8 +735,7 @@
def _exit(space, status):
os._exit(status)
- at unwrap_spec(command=str)
-def execv(space, command, w_args):
+def execv(space, w_command, w_args):
""" execv(path, args)
Execute an executable path with arguments, replacing current process.
@@ -743,12 +743,13 @@
path: path of executable file
args: iterable of strings
"""
+ command = fsencode_w(space, w_command)
try:
args_w = space.unpackiterable(w_args)
if len(args_w) < 1:
w_msg = space.wrap("execv() must have at least one argument")
raise OperationError(space.w_ValueError, w_msg)
- args = [space.str_w(w_arg) for w_arg in args_w]
+ args = [fsencode_w(space, w_arg) for w_arg in args_w]
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
@@ -759,8 +760,7 @@
except OSError, e:
raise wrap_oserror(space, e)
- at unwrap_spec(command=str)
-def execve(space, command, w_args, w_env):
+def execve(space, w_command, w_args, w_env):
""" execve(path, args, env)
Execute a path with arguments and environment, replacing current process.
@@ -769,7 +769,8 @@
args: iterable of arguments
env: dictionary of strings mapping to strings
"""
- args = [space.str_w(w_arg) for w_arg in space.unpackiterable(w_args)]
+ command = fsencode_w(space, w_command)
+ args = [fsencode_w(space, w_arg) for w_arg in space.unpackiterable(w_args)]
env = {}
w_keys = space.call_method(w_env, 'keys')
for w_key in space.unpackiterable(w_keys):
diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -415,6 +415,23 @@
else:
py.test.fail("didn't raise")
+ def test_execv_unicode(self):
+ os = self.posix
+ import sys
+ if not hasattr(os, "fork"):
+ skip("Need fork() to test execv()")
+ try:
+ output = u"caf\xe9 \u1234\n".encode(sys.getfilesystemencoding())
+ except UnicodeEncodeError:
+ skip("encoding not good enough")
+ pid = os.fork()
+ if pid == 0:
+ os.execv(u"/bin/sh", ["sh", "-c",
+ u"echo caf\xe9 \u1234 > onefile"])
+ os.waitpid(pid, 0)
+ assert open("onefile").read() == output
+ os.unlink("onefile")
+
def test_execve(self):
os = self.posix
if not hasattr(os, "fork"):
@@ -425,6 +442,24 @@
os.waitpid(pid, 0)
assert open("onefile").read() == "xxx"
os.unlink("onefile")
+
+ def test_execve_unicode(self):
+ os = self.posix
+ import sys
+ if not hasattr(os, "fork"):
+ skip("Need fork() to test execve()")
+ try:
+ output = u"caf\xe9 \u1234\n".encode(sys.getfilesystemencoding())
+ except UnicodeEncodeError:
+ skip("encoding not good enough")
+ pid = os.fork()
+ if pid == 0:
+ os.execve(u"/bin/sh", ["sh", "-c",
+ u"echo caf\xe9 \u1234 > onefile"],
+ {'ddd': 'xxx'})
+ os.waitpid(pid, 0)
+ assert open("onefile").read() == output
+ os.unlink("onefile")
pass # <- please, inspect.getsource(), don't crash
if hasattr(__import__(os.name), "spawnv"):
diff --git a/pypy/module/pyexpat/__init__.py b/pypy/module/pyexpat/__init__.py
--- a/pypy/module/pyexpat/__init__.py
+++ b/pypy/module/pyexpat/__init__.py
@@ -4,12 +4,8 @@
class ErrorsModule(MixedModule):
"Definition of pyexpat.errors module."
-
- appleveldefs = {
- }
-
- interpleveldefs = {
- }
+ appleveldefs = {}
+ interpleveldefs = {}
def setup_after_space_initialization(self):
from pypy.module.pyexpat import interp_pyexpat
@@ -18,6 +14,18 @@
interp_pyexpat.ErrorString(self.space,
getattr(interp_pyexpat, name)))
+class ModelModule(MixedModule):
+ "Definition of pyexpat.model module."
+ appleveldefs = {}
+ interpleveldefs = {}
+
+ def setup_after_space_initialization(self):
+ from pypy.module.pyexpat import interp_pyexpat
+ space = self.space
+ for name in interp_pyexpat.xml_model_list:
+ value = getattr(interp_pyexpat, name)
+ space.setattr(self, space.wrap(name), space.wrap(value))
+
class Module(MixedModule):
"Python wrapper for Expat parser."
@@ -39,6 +47,7 @@
submodules = {
'errors': ErrorsModule,
+ 'model': ModelModule,
}
for name in ['XML_PARAM_ENTITY_PARSING_NEVER',
diff --git a/pypy/module/pyexpat/interp_pyexpat.py b/pypy/module/pyexpat/interp_pyexpat.py
--- a/pypy/module/pyexpat/interp_pyexpat.py
+++ b/pypy/module/pyexpat/interp_pyexpat.py
@@ -76,6 +76,18 @@
"XML_ERROR_FINISHED",
"XML_ERROR_SUSPEND_PE",
]
+xml_model_list = [
+ "XML_CTYPE_EMPTY",
+ "XML_CTYPE_ANY",
+ "XML_CTYPE_MIXED",
+ "XML_CTYPE_NAME",
+ "XML_CTYPE_CHOICE",
+ "XML_CTYPE_SEQ",
+ "XML_CQUANT_NONE",
+ "XML_CQUANT_OPT",
+ "XML_CQUANT_REP",
+ "XML_CQUANT_PLUS",
+ ]
class CConfigure:
_compilation_info_ = eci
@@ -104,6 +116,8 @@
for name in xml_error_list:
locals()[name] = rffi_platform.ConstantInteger(name)
+ for name in xml_model_list:
+ locals()[name] = rffi_platform.ConstantInteger(name)
for k, v in rffi_platform.configure(CConfigure).items():
globals()[k] = v
diff --git a/pypy/module/pyexpat/test/test_parser.py b/pypy/module/pyexpat/test/test_parser.py
--- a/pypy/module/pyexpat/test/test_parser.py
+++ b/pypy/module/pyexpat/test/test_parser.py
@@ -131,3 +131,7 @@
'encoding specified in XML declaration is incorrect')
assert (pyexpat.errors.XML_ERROR_XML_DECL ==
'XML declaration not well-formed')
+
+ def test_model(self):
+ import pyexpat
+ assert isinstance(pyexpat.model.XML_CTYPE_EMPTY, int)
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -137,20 +137,15 @@
def jump_absolute(self, jumpto, _, ec=None):
if we_are_jitted():
- # Normally, the tick counter is decremented by 100 for every
- # Python opcode. Here, to better support JIT compilation of
- # small loops, we decrement it by a possibly smaller constant.
- # We get the maximum 100 when the (unoptimized) trace length
- # is at least 3200 (a bit randomly).
- trace_length = r_uint(current_trace_length())
- decr_by = trace_length // 32
- if decr_by < 1:
- decr_by = 1
- elif decr_by > 100: # also if current_trace_length() returned -1
- decr_by = 100
+ #
More information about the pypy-commit
mailing list