From hpk at codespeak.net Fri Aug 1 01:07:03 2003 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 1 Aug 2003 01:07:03 +0200 (MEST) Subject: [pypy-svn] rev 1252 - in pypy/trunk/doc: . devel objspace sprintinfo Message-ID: <20030731230703.598515A8DD@thoth.codespeak.net> Author: hpk Date: Fri Aug 1 01:07:00 2003 New Revision: 1252 Added: pypy/trunk/doc/sprintinfo/Hildesheim.txt Removed: pypy/trunk/doc/devel/builtins.txt pypy/trunk/doc/devel/using-testsupport.txt pypy/trunk/doc/sprintinfo/hildesheim.txt pypy/trunk/doc/sprintinfo/llnsprint-brainstorming.txt Modified: pypy/trunk/doc/devel/coding-style.txt pypy/trunk/doc/devel/testdesign.txt pypy/trunk/doc/objspace/abstractobjspace.txt pypy/trunk/doc/objspace/annotateobjspace.txt pypy/trunk/doc/objspace/stdobjspace.txt pypy/trunk/doc/objspace/trivialobjspace.txt pypy/trunk/doc/readme.txt pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt pypy/trunk/doc/sprintinfo/sprint-planning.txt Log: - fixed various documentation issues - removed 'builtins.txt' because it's horribly outdated and i doubt that anybody consults it instead of pypy/module/builtin.py - changed the focus of 'sprint-planning' to the spurious past. - removed bogus 'using-testsupport' "documentation". - removed llnsprint-brainstorming.txt into Hildesheim.txt where it belonged (it's an old brainstorm :-) Deleted: /pypy/trunk/doc/devel/builtins.txt ============================================================================== --- /pypy/trunk/doc/devel/builtins.txt Fri Aug 1 01:07:00 2003 +++ (empty file) @@ -1,152 +0,0 @@ -# this is just a list of the builtins from 2.2.3c1 -# we expect to annotate the file with comments about ease of -# implementation & implemented-ness of each builtin - -annotations: status/difficulty/strategy/urgency - -d == done -p == written, but the are problems -w == it works, but has not the full functionality -n == need to be done - -e == easy -h == hard - -o == essentially implemented by the objectspace -p == just write it in python -v == variable -t == type -m == messy - -u == urgent -i == important -n == normal -d == really don't care -r == resolved, done -U == unsupported - -For now, all exception names are synonyms (until inheritance happens). - -+ ArithmeticError -+ AssertionError -+ AttributeError -++DeprecationWarning -+ EOFError -+ EnvironmentError -+ Exception -+ FloatingPointError -+ IOError -+ ImportError -+ IndentationError -+ IndexError -+ KeyError -+ KeyboardInterrupt -+ LookupError -+ MemoryError -+ NameError -+ NotImplementedError -+ OSError -+ OverflowError -++OverflowWarning -+ ReferenceError -+ RuntimeError -++RuntimeWarning -+ StandardError -+ StopIteration -+ SyntaxError -++SyntaxWarning -+ SystemError -+ SystemExit -+ TabError -+ TypeError -+ UnboundLocalError -+ UnicodeError -++UserWarning -+ ValueError -++Warning -+ ZeroDivisionError - -Ellipsis -False -None -NotImplemented -True - - -- all singletons. - -ev __debug__ -ev __doc__ - - -whmu __import__ -nevd __name__ -deon abs -deon apply -n ti bool -nhtd buffer -nhpn callable -deon chr -nhtn classmethod -n pn cmp -nhmd coerce -nemn compile -- will need to become hard later -n pn complex -nepv copyright -nepv credits -deon delattr -n tn dict -nhpn dir -nepn divmod -- maybe should do more later -n pi eval -n pn execfile -n v exit -nhtu file -- ouch. maybe use Guido's sio stuff? - -- also, can probably escape to the host impl. for now -n pn filter -n tn float -deoi getattr -nepn globals -depn hasattr -- err, maybe not easy if all exceptions are the same -deon hash -nepn help -deon hex -deon id -nepd input -n t int -n md intern -dhpi isinstance -phoi issubclass -p oi iter -deoi len -n v license -n ti list -nemn locals -- maybe will become harder later -n t long -d pn map -n pn max -n pn min -n t object -neon oct -nhtu open -- see file, they're the same -deon ord -- chr -deon pow -n t property -n v quit -- (see exit) -d pi range -nepn raw_input -n pn reduce -nhpn reload -deon repr -nepn round -deoi setattr -n t slice -n t staticmethod -d t str -n t super -n t tuple -n t type -neon unichr -n t unicode -nepn vars -n t xrange -- alias to range for now? -nepn zip Modified: pypy/trunk/doc/devel/coding-style.txt ============================================================================== --- pypy/trunk/doc/devel/coding-style.txt (original) +++ pypy/trunk/doc/devel/coding-style.txt Fri Aug 1 01:07:00 2003 @@ -97,18 +97,10 @@ - every non-test file is forbidden to start with "t" -- each test directory needs a copy of testsupport.py. see using-testsupport_ for more information. +- each test directory needs a copy of pypy/tool/autopath.py. -- see information at test-design_ - -Miscellaneous stuff ---------------------- - -To edit wiki pages do: - -``svn co http://codespeak.net/svn/pypy/trunk/www/moininstance/data/text wiki-pages`` +- see some more information about tests at the test-design_ document. ------------------------------------------------------------------------------------------ .. _test-design: ../devel/testdesign.html -.. _using-testsupport: ../devel/using-testsupport.html \ No newline at end of file Modified: pypy/trunk/doc/devel/testdesign.txt ============================================================================== --- pypy/trunk/doc/devel/testdesign.txt (original) +++ pypy/trunk/doc/devel/testdesign.txt Fri Aug 1 01:07:00 2003 @@ -2,26 +2,46 @@ Test Design ============= -Our tests are based on the unittest framework. Tests for modules usually reside in a subdirectory called "test". +Our tests are based on the unittest framework. All tests of modules in a directory usually reside +in a subdirectory **test**. There are basically two types of unit tests: -If you write a test, then you usually have the following lines at the start of your test:: +- **Interpreter Level tests**. They run at the same level as PyPy's interpreter. + +- **Application Level tests**. They run at application level which means + that they look like straight python code but they are interpreted by PyPy. + +Both types of tests need an object-space they can run with (the interpreter +dispatches operations on objects to an objectspace). If you run a test you +can usually give the '-S', '-T' or '-A' switch for using the Standard, Trivial +or Ann-ObjectSpace respectively. + + +Writing a test +-------------- + +The best reference is to go to some test files and look how they are done. +Almost all tests start with the following lines:: import autopath from pypy.tool import test -For the time being, please refer to a pypy-dev mail_ for more info. +of which the first line determines the package path automatically by searching +for 'pypy' among the parents of the test-file's directory. The second +line imports PyPy's test-hook of which ''test.main()'' is the most important +and is usually the last line of the testfile. +For more info please refer to a pypy-dev mail_ where the current testing +framework was introduced. Command line tool test_all --------------------------- -PyPy's current testing framework (since revision 831) is driven by the command line src/pypy/test_all.py tool. Simply change to the pypy root directory and issue:: - +You can run almost all of PyPy's tests by invoking:: python test_all.py - -which will run all tests against the Trivial Object Space. If you want to test against the StandardObjectSpace then issue:: +which will run all tests against the Trivial Object Space. If you want +to test against the StandardObjectSpace then issue:: python test_all.py -S Deleted: /pypy/trunk/doc/devel/using-testsupport.txt ============================================================================== --- /pypy/trunk/doc/devel/using-testsupport.txt Fri Aug 1 01:07:00 2003 +++ (empty file) @@ -1,23 +0,0 @@ -======================= -Using testsupport -======================= - -file ``testsupport.py`` should be copied into every test subdirectory. -The main copy lives in ``pypy/testsupport.py`` Use:: - - import testsupport - -to fix the path so that all 'import pypy.something' should work. - -Module testsupport also exposes the following: -+ main a function just like unittest.main -+ TestCase a class just like unittest.TestCase but with extra methods -+ objspace the object space class to be used in this test - -``testsupport.objspace`` is normally the trivial object space, but you -can set it (via an environment variable set before running Python) -to be the standard object space instead. Specifically:: - - set OBJSPACE=pypy.objspace.std.objspace.StdObjSpace - -(or equivalent syntax depending on your shell) does the job. \ No newline at end of file Modified: pypy/trunk/doc/objspace/abstractobjspace.txt ============================================================================== --- pypy/trunk/doc/objspace/abstractobjspace.txt (original) +++ pypy/trunk/doc/objspace/abstractobjspace.txt Fri Aug 1 01:07:00 2003 @@ -2,10 +2,8 @@ AbstractObjectSpace ======================= -User History ------------------------- - -This is an example of abstract interpretation, i.e. following the bytecode instructions of a program like an interpreter does but with abstract objects instead of concrete ones. Remember that in PyPy this is done by using alternate object spaces with the same interpreter main loop. +We currently don't really have an AbstractObjSpace but an AnnotateObjSpace_ (or AnnSpace for short) +which comes close to it. Abstract Interpretation means to the bytecode instructions of a program like an interpreter does but with abstract objects instead of concrete ones. Remember that in PyPy this is done by using alternate object spaces with the same interpreter main loop. The most abstract object space is the one manipulating the most abstract objects that you could imagine: they are all equivalent, because we have abstracted away any information about the object. There is actually only one of them left, and we can call it "the object". In Python terms, our AbstractObjectSpace uses None for all its wrapped objects. Any operation between wrapped objects gives None again as the wrapped result -- there is nothing else it could give anyway. So when you have said that the add method of AbstractObjectSpace takes None and None and returns None you have said everything. @@ -14,3 +12,5 @@ Typically, however, abstract object spaces are a (little) bit less abstract, still maintaining a minimal amount of information about the objects. For example, a wrapped object could be represented by its type. You then define the object space's add to return int when the two arguments are int and int. That way, you abstractedly call a function with the input argument's types and what the interpreter will do is a type inference. (Here also there are subtle problems, even besides the remark that integer operations can overflow and actually return longs in a real Python implementation.) As an example of more abstract object spaces you have the ones with finite domain, i.e. with a finite number of different possible wrapped objects. For example, you can use True and False as wrapped values to denote the fact that the object is, respectively, a non-negative integer or anything else. In this way you are doing another kind of type inference that just tells you which variables will only ever contain non-negative integers. + +.. _AnnotatedObjSpace: annotateobjspace.html Modified: pypy/trunk/doc/objspace/annotateobjspace.txt ============================================================================== --- pypy/trunk/doc/objspace/annotateobjspace.txt (original) +++ pypy/trunk/doc/objspace/annotateobjspace.txt Fri Aug 1 01:07:00 2003 @@ -4,10 +4,7 @@ was TranslateObjectSpace ------------------------- -This has been renamed and is currently being re-written. Old information follows: - -User History ------------------- +This has been renamed and is currently being re-written. Older information follows: This is an example of an ObjectSpace that differs a lot from StandardObjectSpace_. Modified: pypy/trunk/doc/objspace/stdobjspace.txt ============================================================================== --- pypy/trunk/doc/objspace/stdobjspace.txt (original) +++ pypy/trunk/doc/objspace/stdobjspace.txt Fri Aug 1 01:07:00 2003 @@ -2,9 +2,6 @@ StandardObjectSpace ========================= -User History ---------------- - The StandardObjectSpace is the direct equivalent of CPython's object library (the "Objects/" subdirectory in the distribution). It is an implementation of the common Python types in a lower-level language. The StandardObjectSpace defines an abstract parent class, W_Object, and a bunch of subclasses like W_IntObject, W_ListObject, and so on. A wrapped object (a "black box" for the interpreter main loop) is thus an instance of one of these classes. When the main loop invokes an operation, say the addition, between two wrapped objects w1 and w2, the StandardObjectSpace does some internal dispatching (similar to "Object/ abstract.c" in CPython) and invokes a method of the proper W_XyzObject class that can do the operation. The operation itself is done with the primitives allowed by RestrictedPython. The result is constructed as a wrapped object again. For example, compare the following implementation of integer addition with the function "int_add()" in "Object/intobject.c": :: @@ -28,4 +25,4 @@ --------------------------------------------------------------------------- .. _StandardObjectSpace: stdobjspace.html -.. _MultiMethod: multimethod.html \ No newline at end of file +.. _MultiMethod: multimethod.html Modified: pypy/trunk/doc/objspace/trivialobjspace.txt ============================================================================== --- pypy/trunk/doc/objspace/trivialobjspace.txt (original) +++ pypy/trunk/doc/objspace/trivialobjspace.txt Fri Aug 1 01:07:00 2003 @@ -2,9 +2,6 @@ PyPython Trivial Object Space ================================= -User History ----------------- - The easiest way to implement an Object Space is to represent a Python object with itself. A PyPy interpreter using the Trivial Object Space is an interpreter with its own main loop (written in Python), but this main loop manipulates real Python objects and all operations are done directly on the Python objects. For example, "1" really means "1" and when the interpreter encounters the BINARY_ADD bytecode instructions the TrivialObjectSpace will just add two real Python objects together using Python's "+". The same for lists, dictionaries, classes... We just use Python's own. Modified: pypy/trunk/doc/readme.txt ============================================================================== --- pypy/trunk/doc/readme.txt (original) +++ pypy/trunk/doc/readme.txt Fri Aug 1 01:07:00 2003 @@ -2,19 +2,21 @@ PyPy Documentation ===================== -PyPy documentation generally is generated from reST textfiles in the /doc directory of +PyPy documentation is generated from reST textfiles in the /doc directory of our pypy-subversion repository. On the pypy home page you'll find a "doc" link that shows you a list of recently modified documents. While in "doc-view" you also have a navigation area on the left side which maps all documentation. -Please add new or updated documentation by checking it in to the appropriate directory in subversion, usually under http://codespeak.net/svn/pypy/trunk/doc/. +Please add new or updated documentation by checking it in to the appropriate +directory in subversion, usually under http://codespeak.net/svn/pypy/trunk/doc/. + Remember to run ``svn up`` **before** doing any commit. + All filenames should be lowercase, and documentation should be .txt files. + Mark-up the documentation with reST so it can generate a pretty html version. + On the server side a commit on the doc-subtree will immediately update the webpage. -*Note* If you don't markup the textfile, it'll still be checked in, but when docutils runs the parser, it'll look ugly on the website. So run docutils yourself before you commit it. +*Note* If you don't markup the textfile, it'll still be checked in, but when docutils +runs the parser, it'll look ugly on the website. So run docutils yourself before you commit it. Some reST basics: ------------------ Added: pypy/trunk/doc/sprintinfo/Hildesheim.txt ============================================================================== --- (empty file) +++ pypy/trunk/doc/sprintinfo/Hildesheim.txt Fri Aug 1 01:07:00 2003 @@ -0,0 +1,113 @@ +Hildesheim Sprint Report +======================== + +The Hildesheim Sprint provided a chance to meet and decide several crucial design considerations. +A #pypy irc channel provided communication among participants between sprints. The sourcecode +was loaded into subversion and participants given commit rights. + +At the Sprint: +Some folks did lots of handwaving and created really kewl concepts. Other pairs concentrated on coding, testing, builtin functions etc etc. We gathered for goalsetting meetings several times during the sprint, then split up to work on tasks. Half of the work was done by pair programming. Pairs were informal, developing and changing as tasks were discovered and completed. Sprints varied in amount of "discuss as a group" and "just do it" time. We spent lots of intense time together, not just coding but also social time, (meals, spending a day playing tourist, etc), which enhanced the building of relationships and understanding among sprinters. + +Some discoveries: Plan on the first morning for hardware setup and fixing system issues, (wireless is great!) Built-in private time is necessary for the sprint. Whiteboards and projectors are both necessary, as is coffee and tea. Bringing in/providing food is fine but getting people away for lunch is good to clear their minds. Leadership varied throughout the sprints and throughout the day. + + + +Brainstorming about what PyPy might be +-------------------------------------- + +The following was written down at the first Sprint to understand +each other's motivations and ideas. It's not very sorted but +might still be interesting to skim. + +- Python interpreter written in python + - loads bytecode + - delegates/dispatches to ObjectSpaces to implement operations + on the objects + - there can be more than one ObjectSpace + - for example: BorrowingObjectSpace (from CPython) + - define/implement a class that emulates the Python + Execution Frame + +- use the main-loop of the interpreter to do a lot of + things (e.g. do type inference during running the bytecode + or not even run the bytecodes, but interpret various attributes of the code) + +- working together, producing something real + +- saving interpreter state to an image (following the smalltalk model) + process-migration / persistence + +- looking at the entire code base (living in an image), browsing + objects interactively + +- interactive environment, trying code snippets, introspection + +- deploying python made easy, integrate version control systems + +- integrate the various technologies on the web site, issue tracking, + Wiki... + +- seperate exception handling from the mainline code, avoid peppering + your code with try :-), put exception handling into objects. + +- import python code from the version control store directly, give + imported code also a time dimension + +- combining python interpreters from multiple machines (cluster) into a + virtual sandbox (agent space?) + +- get a smaller (maybe faster) python with very few C-code + +- (hoping for Psyc) to render fast code from Python code (instead of + hard-c) + +- go to a higher level python core (and write out/generate interpreters + in different languages), e.g. the former P-to-C resolved the evalframe-loop + but still called into the Python-C-library which is statically coded + +- very far fetched: PyPython becomes a/the reference implementation + +- have enough flexibility to make a separate stackless obsolete + +- have a language that is high-level/easy enough to program + but with the same performance as statically compiled languages + (e.g. C++) + + +what is the difference between a compiler and an interpreter +------------------------------------------------------------ + +f = bytecode interpreter +p = program +a = arguments + +c = compiler + +assert f(p, a) == c(p)(a) == r + + +- architecture overview + * byte code interp loop + plan how the interp loop should look like from a hi level + map that structure in descriptions that can be used to generate interpreters/compilers + define the frame structure + + * define a object/type model that maps into python data structures + wrap cpython objects into the new object model so we can continue + to use cpython modules + + * rewrite c python modules and the builtin object library in python + optimzation for a potential global python optimizer, until that + exists it will be slower than the corresponding cpython implementation + +- import the cpython distribution so we can use parts of it in our + repository, make it easy to follow the cpython development + +- finish the python to byte code compiler in python project (this is + already part of the cpython distribution, needs a python lexer) + +- doing other things than interpreting byte code from the python interp + loop, for example generate C code, implement other object spaces in our + terminlogy other far fetched things with execution + +- how to enter c ysystem calls into the python object space (ctypes) Modified: pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt ============================================================================== --- pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt (original) +++ pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt Fri Aug 1 01:07:00 2003 @@ -63,7 +63,7 @@ - bugfixes, refactorings and adding tests all over the place (everybody) - + --------------------------------------------------------------- .. _boolobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000381.html Deleted: /pypy/trunk/doc/sprintinfo/hildesheim.txt ============================================================================== --- /pypy/trunk/doc/sprintinfo/hildesheim.txt Fri Aug 1 01:07:00 2003 +++ (empty file) @@ -1,11 +0,0 @@ -Hildesheim Sprint Report -======================== - -The Hildesheim Sprint provided a chance to meet and decide several crucial design considerations. -A #pypy irc channel provided communication among participants between sprints. The sourcecode -was loaded into subversion and participants given commit rights. - -At the Sprint: -Some folks did lots of handwaving and created really kewl concepts. Other pairs concentrated on coding, testing, builtin functions etc etc. We gathered for goalsetting meetings several times during the sprint, then split up to work on tasks. Half of the work was done by pair programming. Pairs were informal, developing and changing as tasks were discovered and completed. Sprints varied in amount of "discuss as a group" and "just do it" time. We spent lots of intense time together, not just coding but also social time, (meals, spending a day playing tourist, etc), which enhanced the building of relationships and understanding among sprinters. - -Some discoveries: Plan on the first morning for hardware setup and fixing system issues, (wireless is great!) Built-in private time is necessary for the sprint. Whiteboards and projectors are both necessary, as is coffee and tea. Bringing in/providing food is fine but getting people away for lunch is good to clear their minds. Leadership varied throughout the sprints and throughout the day. Deleted: /pypy/trunk/doc/sprintinfo/llnsprint-brainstorming.txt ============================================================================== --- /pypy/trunk/doc/sprintinfo/llnsprint-brainstorming.txt Fri Aug 1 01:07:00 2003 +++ (empty file) @@ -1,95 +0,0 @@ - -General ideas of what PyPy should achieve for us - -- Python interpreter written in python - - loads bytecode - - delegates/dispatches to ObjectSpaces to implement operations - on the objects - - there can be more than one ObjectSpace - - for example: BorrowingObjectSpace (from CPython) - - define/implement a class that emulates the Python - Execution Frame - -- use the main-loop of the interpreter to do a lot of - things (e.g. do type inference during running the bytecode - or not even run the bytecodes, but interpret various attributes of the code) - -- working together, producing something real - -- saving interpreter state to an image (following the smalltalk model) - process-migration / persistence - -- looking at the entire code base (living in an image), browsing - objects interactively - -- interactive environment, trying code snippets, introspection - -- deploying python made easy, integrate version control systems - -- integrate the various technologies on the web site, issue tracking, - Wiki... - -- seperate exception handling from the mainline code, avoid peppering - your code with try :-), put exception handling into objects. - -- import python code from the version control store directly, give - imported code also a time dimension - -- combining python interpreters from multiple machines (cluster) into a - virtual sandbox (agent space?) - -- get a smaller (maybe faster) python with very few C-code - -- (hoping for Psyc) to render fast code from Python code (instead of - hard-c) - -- go to a higher level python core (and write out/generate interpreters - in different languages), e.g. the former P-to-C resolved the evalframe-loop - but still called into the Python-C-library which is statically coded - -- very far fetched: PyPython becomes a/the reference implementation - -- have enough flexibility to make a separate stackless obsolete - -- have a language that is high-level/easy enough to program - but with the same performance as statically compiled languages - (e.g. C++) - - -what is the difference between a compiler and an interpreter ------------------------------------------------------------- - -f = bytecode interpreter -p = program -a = arguments - -c = compiler - -assert f(p, a) == c(p)(a) == r - - -- architecture overview - * byte code interp loop - plan how the interp loop should look like from a hi level - map that structure in descriptions that can be used to generate interpreters/compilers - define the frame structure - - * define a object/type model that maps into python data structures - wrap cpython objects into the new object model so we can continue - to use cpython modules - - * rewrite c python modules and the builtin object library in python - optimzation for a potential global python optimizer, until that - exists it will be slower than the corresponding cpython implementation - -- import the cpython distribution so we can use parts of it in our - repository, make it easy to follow the cpython development - -- finish the python to byte code compiler in python project (this is - already part of the cpython distribution, needs a python lexer) - -- doing other things than interpreting byte code from the python interp - loop, for example generate C code, implement other object spaces in our - terminlogy other far fetched things with execution - -- how to enter c ysystem calls into the python object space (ctypes) Modified: pypy/trunk/doc/sprintinfo/sprint-planning.txt ============================================================================== --- pypy/trunk/doc/sprintinfo/sprint-planning.txt (original) +++ pypy/trunk/doc/sprintinfo/sprint-planning.txt Fri Aug 1 01:07:00 2003 @@ -1,77 +1,30 @@ -LouvainLaNeuveSprint planning +Sprint Planning +--------------- ---> join this screen: ssh codespeak.net "and" screen -x hpk/hpk +Here is a list of things we might want to do at one of the next sprints. +Currently it's roughly what is left over from the last sprints. -eternal goals: - do more tests (eternal goal) -- Fix XXX-marked things + +- Fix XXX-marked things (eternal goal) - enhance StdObjSpace, define goals and achieve them http://codespeak.net/svn/pypy/trunk/src/goals/ - - Being able to run main.py dis.dis(dis.dis) - - Unbound methods. - support the objects we see falling back to CPython. - more builtins. - more things from sys. - dict object/type - Hash table based implementation of dictionaries? - list object/type - - check all other type implementation and document their state: - - alex, christian - -- boolobject review done boolobject_ -- cpythonobject review done cpythonobj_ -- instmethobject review done instmethod_ -- longobject review done removed long_ -- sliceobject review done sliceobject_ -- userobject review done userobject_ -- dictobject review done dictobject_ -- iterobject review done iterobject_ -- tupleobject -- listobject -- intobject inprocess intobject_ -- floatobject - - tomek, holger, guenter - - moduleobject review done - stringobject - noneobject - - write a small tool that checks a type's methods of CPython against PyPy (Jacob, Laura) done -- improve "main.py" tool and rename it to "py.py" :-) - with a subset of the options of "python". This - should allow executing commands (-c), going - interactive (-i) and executing files (argv[1]) - - - Fix the way an OBJECTSPACE is selected. ? - - main.py doesn't handle exceptions very well. - - (michael) - -- move design documentation from wiki to subversion - (doc/design) to make it locally editable (instead - of html-textfields) and keep everyone up-to-date - maybe rearrange the doc-directory to be into src/pypy - - (Anna, Laura) - - go through the wiki and clean up "stale" or old pages -- implement AnnotationObjSpace and the beginnings - of a C code generator. the basic idea is "abstract - interpretation".... - - - getting the translator to understand RPython, handling the - constructions it can and delegating the things it doesn't - understand to CPython (Mimicking Python2C) - - (Armin, Guido) +- implement the beginnings of a C code generator. the basic idea + is "abstract interpretation".... - discuss funding and future organization issues From hpk at codespeak.net Fri Aug 1 01:13:13 2003 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 1 Aug 2003 01:13:13 +0200 (MEST) Subject: [pypy-svn] rev 1253 - pypy/trunk/doc/objspace Message-ID: <20030731231313.3D7C55A8DD@thoth.codespeak.net> Author: hpk Date: Fri Aug 1 01:13:12 2003 New Revision: 1253 Modified: pypy/trunk/doc/objspace/abstractobjspace.txt pypy/trunk/doc/objspace/annotateobjspace.txt Log: some reST fixes. Modified: pypy/trunk/doc/objspace/abstractobjspace.txt ============================================================================== --- pypy/trunk/doc/objspace/abstractobjspace.txt (original) +++ pypy/trunk/doc/objspace/abstractobjspace.txt Fri Aug 1 01:13:12 2003 @@ -13,4 +13,4 @@ As an example of more abstract object spaces you have the ones with finite domain, i.e. with a finite number of different possible wrapped objects. For example, you can use True and False as wrapped values to denote the fact that the object is, respectively, a non-negative integer or anything else. In this way you are doing another kind of type inference that just tells you which variables will only ever contain non-negative integers. -.. _AnnotatedObjSpace: annotateobjspace.html +.. _AnnotateObjSpace: annotateobjspace.html Modified: pypy/trunk/doc/objspace/annotateobjspace.txt ============================================================================== --- pypy/trunk/doc/objspace/annotateobjspace.txt (original) +++ pypy/trunk/doc/objspace/annotateobjspace.txt Fri Aug 1 01:13:12 2003 @@ -1,5 +1,5 @@ ========================= -Annotated Object Space +Annotate Object Space ========================= was TranslateObjectSpace From jum at codespeak.net Wed Aug 6 02:31:43 2003 From: jum at codespeak.net (jum at codespeak.net) Date: Wed, 6 Aug 2003 02:31:43 +0200 (MEST) Subject: [pypy-svn] rev 1260 - pypy/trunk/doc/devel Message-ID: <20030806003143.B98235AAE5@thoth.codespeak.net> Author: jum Date: Wed Aug 6 02:31:42 2003 New Revision: 1260 Modified: pypy/trunk/doc/devel/howtosvn.txt Log: The windows installer finally arrived. Modified: pypy/trunk/doc/devel/howtosvn.txt ============================================================================== --- pypy/trunk/doc/devel/howtosvn.txt (original) +++ pypy/trunk/doc/devel/howtosvn.txt Wed Aug 6 02:31:42 2003 @@ -110,7 +110,7 @@ -------------------------------------------------------------------------------- -.. _commandline: http://codespeak.net/~jum/svn-0.24.2-setup.exe +.. _commandline: http://codespeak.net/~jum/svn-0.26.0-setup.exe .. _website: http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B259403 .. _GUI: http://codespeak.net/~jum/TortoiseSVN-0.15.1-UNICODE.msi .. _Win: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=4B6140F9-2D36-4977-8FA1-6F8A0F5DCA8F From arigo at codespeak.net Sat Aug 9 21:21:03 2003 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 9 Aug 2003 21:21:03 +0200 (MEST) Subject: [pypy-svn] rev 1264 - pypy/branch/builtinrefactor/pypy/interpreter Message-ID: <20030809192103.276415A544@thoth.codespeak.net> Author: arigo Date: Sat Aug 9 21:21:02 2003 New Revision: 1264 Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py pypy/branch/builtinrefactor/pypy/interpreter/eval.py pypy/branch/builtinrefactor/pypy/interpreter/function.py pypy/branch/builtinrefactor/pypy/interpreter/pycode.py pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py Log: More intermediate check-ins, after discussion with hpk... Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py Sat Aug 9 21:21:02 2003 @@ -130,11 +130,8 @@ if expected_length is not None and tuple_length != expected_length: raise ValueError, "got a tuple of length %d instead of %d" % ( tuple_length, expected_length) - items = [] - for i in range(tuple_length): - w_i = self.wrap(i) - w_item = self.getitem(w_tuple, w_i) - items.append(w_item) + items = [ + self.getitem(w_tuple, self.wrap(i)) for i in range(tuple_length)] return items def exception_match(self, w_exc_type, w_check_class): @@ -160,6 +157,10 @@ w_kw = self.newdict([(self.wrap(k), w_v) for k, w_v in kw_w.iteritems()]) return self.call(w_func, self.newtuple(list(args_w)), w_kw) + def call_method(self, w_obj, methname, *arg_w, **kw_w): + w_meth = self.getattr(w_obj, self.wrap(methname)) + return self.call_function(w_meth, *arg_w, **kw_w) + def isinstance(self, w_obj, w_type): w_objtype = self.type(w_obj) return self.issubtype(w_objtype, w_type) Modified: pypy/branch/builtinrefactor/pypy/interpreter/eval.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/eval.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/eval.py Sat Aug 9 21:21:02 2003 @@ -11,16 +11,14 @@ def __init__(self, co_name): self.co_name = co_name - def create_frame(self, space): + def create_frame(self, space, w_globals, closure=None): "Create an empty frame object suitable for evaluation of this code." raise TypeError, "abstract" def exec_code(self, space, w_globals, w_locals): "Implements the 'exec' statement." - frame = self.create_frame(space) - frame.setglobaldict(w_globals) - frame.setlocaldict(w_locals) - frame.setclosure(None) + frame = self.create_frame(space, w_globals) + frame.setdictscope(w_locals) return frame.run() def signature(self): @@ -59,10 +57,10 @@ """A frame is an environment supporting the execution of a code object. Abstract base class.""" - def __init__(self, space, code): + def __init__(self, space, code, w_globals): self.space = space self.code = code # Code instance - self.w_globals = None # wrapped dict of globals + self.w_globals = w_globals # wrapped dict of globals self.w_locals = None # wrapped dict of locals def run(self): @@ -79,40 +77,22 @@ "Abstract method to override." raise TypeError, "abstract" - def getglobaldict(self): - return self.w_globals - - def setglobaldict(self): - self.w_globals = w_globals - - def getlocaldict(self): + def getdictscope(self): "Overriden by subclasses with another representation for locals." return self.w_locals - def setlocaldict(self, w_locals): + def setdictscope(self, w_locals): """Initialize the locals from a dictionary. Overriden by subclasses with another representation for locals.""" self.w_locals = w_locals - def getlocalvar(self, index): - """Get the value of the indexth local variable - where numbering is according to self.code.signature(). - Default implementation, to be overridden.""" - varname = self.code.getlocalvarname(index) - return self.space.getitem(w_locals, self.space.wrap(varname)) - - def setlocalvar(self, index, w_value): - """Set the value of the indexth local variable, - where numbering is according to self.code.signature(). + def setfastscope(self, scope_w): + """Initialize the locals from a list of values, + where the order is according to self.code.signature(). Default implementation, to be overridden.""" + space = self.space if self.w_locals is None: - self.w_locals = self.space.newdict([]) - varname = self.code.getlocalvarname(index) - self.space.setitem(self.w_locals, self.space.wrap(varname), w_value) - - def setclosure(self, closure): - """Initialize the closure from the given data, which should be None or - a list of Cells for PyFrame. This should be called after setlocalvar() - or setlocaldict() is used to initialize the frame.""" - if closure: - raise TypeError, "%s instance expects no closure" % self.__class__ + self.w_locals = space.newdict([]) + for i in range(len(scope_w)): + varname = self.code.getlocalvarname(i) + space.setitem(self.w_locals, space.wrap(varname), scope_w[i]) Modified: pypy/branch/builtinrefactor/pypy/interpreter/function.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/function.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/function.py Sat Aug 9 21:21:02 2003 @@ -15,120 +15,96 @@ self.space = space self.func_code = code # Code instance self.w_globals = w_globals # the globals dictionary - self.w_defs = w_defs # wrapped sequence of default args or None self.closure = closure # normally, list of Cell instances or None + if w_defs is None: + self.defs_w = [] + else: + self.defs_w = space.unpackiterable(w_defs) # list of w_default's + + def call(self, w_args, w_kwds=None): + scope_w = self.parse_args(w_args, w_kwds) + frame = self.func_code.create_frame(self.space, self.w_globals) + frame.setfastscope(scope_w) + return frame.run() - def parse_args(self, frame, w_args, w_kwargs=None): + def parse_args(self, w_args, w_kwds=None): """ parse args and kwargs to initialize the frame. """ space = self.space signature = self.func_code.signature() argnames, varargname, kwargname = signature - # test for 'simple case': - if (varargname is None and kwargname is None and # no */** - (w_kwargs is None or not space.is_true(w_kwargs)) and # no kwargs - self.unwrap(self.len(w_args)) == len(argnames)): # correct #args - for index in range(len(argnames)): - w_index = self.wrap(index) - w_argument = self.getitem(w_args, w_index) - frame.setlocalvar(index, w_argument) - else: - #print "complicated case of arguments for", self.func_code.co_name - if w_kwargs is None: - w_kwargs = space.w_None - self.parse_args_complex(frame, w_args, w_kwargs, - space.wrap(signature)) - frame.setclosure(self.closure) - - def app_parse_args_complex(self, frame, args, kws, signature): - """ app-level helper for the complex case of parse_args(). - """ - # ===== ATTENTION ===== - # - # This code is pretty fundamental to pypy and great care must be taken - # to avoid infinite recursion. In particular: - # - # - All calls here must be "easy", i.e. not involve default or keyword - # arguments. For example, all range() calls need three arguments. # - # - You cannot *catch* any exceptions (raising is fine). - # - # Assumptions: - # frame = empty frame to be initialized - # args = sequence of the normal actual parameters - # kws = dictionary of keyword parameters or None - # self.defs = sequence of defaults + # w_args = wrapped sequence of the normal actual parameters + # args_w = the same, as a list of wrapped actual parameters + # w_kwds = wrapped dictionary of keyword parameters or a real None + # argnames = list of formal parameter names + # scope_w = resulting list of wrapped values # # We try to give error messages following CPython's, which are # very informative. - - argnames, varargname, kwargname = signature - input_argcount = len(args) - co_argcount = len(argnames) - deffirst = co_argcount - len(self.defs) - if kws: - kwargs = kws.copy() + # + if w_kwds is None: + w_kwargs = space.newdict([]) else: - kwargs = {} + w_kwargs = space.call_method(w_kwds, "copy") - # fetch all arguments left-to-right - for i in range(0, co_argcount, 1): - argname = argnames[i] - if i < input_argcount: - value = args[i] - # check that no keyword argument also gives a value here - if argname in kwargs: - raise TypeError, self.argerr_multiple_values(argname) - elif argname in kwargs: - # positional arguments exhausted, - # complete with keyword arguments - value = kwargs[argname] - del kwargs[argname] - elif i >= deffirst: - # no matching keyword argument, but there is a default value - value = self.defs[i - deffirst] - else: - raise TypeError, self.argerr(signature, args, kws, False) - frame.setlocalvar(i, value) + co_argcount = len(argnames) # expected formal arguments, without */** + # put as many positional input arguments into place as available + args_w = space.unpacktuple(w_args) + scope_w = args_w[:co_argcount] + + # check that no keyword argument conflicts with these + for name in argnames[:len(scope_w)]: + w_name = space.wrap(name) + if space.is_true(space.contains(w_kwargs, w_name)): + self.raise_argerr_multiple_values(name) + + if len(scope_w) < co_argcount: + # not enough args, fill in kwargs or defaults if exists + def_first = co_argcount - len(self.defs_w) + for i in range(input_argcount, co_argcount): + w_name = space.wrap(argnames[i]) + if space.is_true(space.contains(w_kwargs, w_name)): + scope_w.append(space.getitem(w_kwargs, w_name)) + space.delitem(w_kwargs, w_name) + elif i >= def_first: + scope_w.append(self.defs_w[i-def_first]) + else: + self.raise_argerr(w_args, w_kwds, False) + # collect extra positional arguments into the *vararg - specialarg = co_argcount if varargname is not None: - var_tuple = args[co_argcount:] - frame.setlocalvar(specialarg, var_tuple) - specialarg += 1 - elif input_argcount > co_argcount: - # cannot do anything with these extra positional arguments - raise TypeError, self.argerr(signature, args, kws, True) + scope_w.append(space.newtuple(args_w[co_argcount:])) + elif len(args_w) > co_argcount: + self.raise_argerr(w_args, w_kwds, True) # collect extra keyword arguments into the **kwarg if kwargname is not None: # XXX this doesn't check that the keys of kwargs are strings - frame.setlocalvar(specialarg, kwargs) - specialarg += 1 - elif kwargs: - # cannot do anything with these extra keyword arguments - raise TypeError, self.argerr_unknown_kwds(kwargs) - parse_args_complex = app2interp(app_parse_args_complex) + scope_w.append(w_kwargs) + elif space.is_true(w_kwargs): + self.raise_unknown_kwds(w_kwds) + return scope_w # helper functions to build error message for the above - def app_argerr(self, signature, args, kws, too_many): - argnames, varargname, kwargname = signature + def raise_argerr(self, w_args, w_kwds, too_many): + argnames, varargname, kwargname = self.func_code.signature() + nargs = self.space.unwrap(self.space.len(w_args)) n = len(argnames) if n == 0: - n = len(args) if kwargname is not None: msg2 = "non-keyword " else: msg2 = "" - n += len(kws) - return "%s() takes no %sargument (%d given)" % ( + nargs += self.space.unwrap(self.space.len(w_kwds)) + msg = "%s() takes no %sargument (%d given)" % ( self.func_code.co_name, msg2, - n) + nargs) else: - defcount = len(self.defs) + defcount = len(self.defs_w) if defcount == 0: msg1 = "exactly" elif too_many: @@ -144,25 +120,31 @@ plural = "" else: plural = "s" - return "%s() takes %s %d %sargument%s (%d given)" % ( + msg = "%s() takes %s %d %sargument%s (%d given)" % ( self.func_code.co_name, msg1, n, msg2, plural, len(args)) + raise OperationError(self.space.w_TypeError, msg) - def app_argerr_multiple_values(self, argname): - return "%s() got multiple values for keyword argument %s" % ( + def raise_argerr_multiple_values(self, argname): + msg = "%s() got multiple values for keyword argument %s" % ( self.func_code.co_name, argname) + raise OperationError(self.space.w_TypeError, msg) - def app_argerr_unknown_kwds(self, kws): - if len(kws) == 1: - return "%s() got an unexpected keyword argument '%s'" % ( + def raise_argerr_unknown_kwds(self, w_kwds): + nkwds = self.space.unwrap(self.space.len(w_kwds)) + if nkwds == 1: + w_iter = self.space.iter(w_kwds) + w_key = self.space.next(w_iter) + msg = "%s() got an unexpected keyword argument '%s'" % ( self.func_code.co_name, - kws.keys()[0]) + self.space.unwrap(w_key)) else: - return "%s() got %d unexpected keyword arguments" % ( + msg = "%s() got %d unexpected keyword arguments" % ( self.func_code.co_name, - len(kws)) + nkwds) + raise OperationError(self.space.w_TypeError, msg) Modified: pypy/branch/builtinrefactor/pypy/interpreter/pycode.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pycode.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pycode.py Sat Aug 9 21:21:02 2003 @@ -62,7 +62,7 @@ newconsts = newconsts + (const,) self.co_consts = newconsts - def create_frame(self, space): + def create_frame(self, space, w_globals, closure=None): "Create an empty PyFrame suitable for this code object." # select the appropriate kind of frame; see below if self.co_cellvars or self.co_freevars: @@ -71,7 +71,7 @@ frameclass = PyFastScopeFrame else: frameclass = PyOperationalFrame - return frameclass(space, self) + return frameclass(space, self, w_globals, closure) def signature(self): "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." Modified: pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py Sat Aug 9 21:21:02 2003 @@ -12,26 +12,23 @@ # of values accessed by index (by the LOAD_FAST, STORE_FAST and # DELETE_FAST opcodes). - def __init__(self, space, code): - PyOperationalFrame.__init__(self, space, code) + def __init__(self, space, code, w_globals, closure): + PyOperationalFrame.__init__(self, space, code, w_globals, closure) self.locals_w = [UNDEFINED] * code.co_nlocals def getlocalvarname(self, index): return self.bytecode.co_varnames[index] - def getlocaldict(self): + def getdictlocals(self): self.fast2locals() return self.w_locals - def setlocaldict(self, w_locals): + def setdictlocals(self, w_locals): self.w_locals = w_locals self.locals2fast() - def getlocalvar(self, index): - return self.locals_w[index] - - def setlocalvar(self, index, w_value): - self.locals_w[index] = w_value + def setfastlocals(self, scope_w): + self.locals_w[:len(scope_w)] = scope_w def fast2locals(self): # Copy values from self.locals_w to self.w_locals Modified: pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py Sat Aug 9 21:21:02 2003 @@ -22,8 +22,8 @@ * 'valuestack', 'blockstack', 'next_instr' control the interpretation """ - def __init__(self, space, code): - eval.Frame.__init__(self, space, code) + def __init__(self, space, code, w_globals, closure): + eval.Frame.__init__(self, space, code, w_globals) self.bytecode = code # Misnomer; this is really like a code object self.valuestack = Stack() self.blockstack = Stack() Modified: pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py Sat Aug 9 21:21:02 2003 @@ -40,11 +40,17 @@ """This class enhances a standard frame with nested scope abilities, i.e. handling of cell/free variables.""" - def __init__(self, space, code): - PyFastScopeFrame.__init__(self, space, code) + # Cell Vars: + # my local variables that are exposed to my inner functions + # Free Vars: + # variables coming from a parent function in which i'm nested + # 'closure' is a list of Cell instances: the received free vars. + + def __init__(self, space, code, w_globals, closure): + PyFastScopeFrame.__init__(self, space, code, w_globals, closure) ncellvars = len(code.co_cellvars) nfreevars = len(code.co_freevars) - self.cells = [None] * (ncellvars + nfreevars) + self.cells = [Cell() for i in range(ncellvars)] + closure def fast2locals(self): PyFastScopeFrame.fast2locals(self) @@ -71,44 +77,26 @@ else: cell.set(w_value) - def setclosure(self, closure): - # Cell Vars: - # my local variables that are exposed to my inner functions - # Free Vars: - # variables coming from a parent function in which i'm nested - # 'closure' is a list of Cell instances: the received free vars. - code = self.bytecode - ncellvars = len(code.co_cellvars) - nfreevars = len(code.co_freevars) - if ncellvars: + def setfastscope(self, scope_w): + PyFastScopeFrame.setfastscope(scope_w) + if self.bytecode.co_cellvars: # the first few cell vars could shadow already-set arguments, # in the same order as they appear in co_varnames - nargvars = code.getargcount() + code = self.bytecode argvars = code.co_varnames cellvars = code.co_cellvars next = 0 nextname = cellvars[0] - for i in range(nargvars): + for i in range(len(scope_w)): if argvars[i] == nextname: # argument i has the same name as the next cell var - w_value = self.locals_w[i] + w_value = scope_w[i] self.cells[next] = Cell(w_value) next += 1 try: nextname = cellvars[next] except IndexError: break # all cell vars initialized this way - else: - # the remaining cell vars are empty - for i in range(next, ncellvars): - self.cells[i] = Cell() - # following the cell vars are the free vars, copied from 'closure' - if closure is None: - closure = [] - if len(closure) != nfreevars: - raise TypeError, ("%d free variables expected, got %d" % - (nfreevars, len(closure))) # internal error - self.cells[ncellvars:] = closure def getfreevarname(self, index): freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars @@ -123,7 +111,6 @@ def LOAD_CLOSURE(f, varindex): # nested scopes: access the cell object cell = f.cells[varindex] - assert cell is not None, "setclosure() was not called" w_value = f.space.wrap(cell) f.valuestack.push(w_value) From arigo at codespeak.net Sat Aug 9 15:48:31 2003 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 9 Aug 2003 15:48:31 +0200 (MEST) Subject: [pypy-svn] rev 1263 - pypy/branch/builtinrefactor/pypy/interpreter Message-ID: <20030809134831.EEB835A8EB@thoth.codespeak.net> Author: arigo Date: Sat Aug 9 15:48:30 2003 New Revision: 1263 Added: pypy/branch/builtinrefactor/pypy/interpreter/error.py (contents, props changed) - copied, changed from rev 1262, pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py pypy/branch/builtinrefactor/pypy/interpreter/eval.py pypy/branch/builtinrefactor/pypy/interpreter/function.py pypy/branch/builtinrefactor/pypy/interpreter/pycode.py pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py (contents, props changed) - copied, changed from rev 1262, pypy/branch/builtinrefactor/pypy/interpreter/opcode.py Removed: pypy/branch/builtinrefactor/pypy/interpreter/opcode.py Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py pypy/branch/builtinrefactor/pypy/interpreter/gateway.py pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py Log: This is a middle-work check-in. Nothing works. A interpreter/eval.py New abstract base classes Code and Frame A interpreter/pycode.py M interpreter/pyframe.py Concrete subclasses of Code and Frame D interpreter/opcode.py AM interpreter/pyopcode.py A interpreter/pyfastscope.py A interpreter/pynestedscope.py Opcodes moved into three seperate files defining a subclass of PyFrame each M interpreter/gateway.py M interpreter/baseobjspace.py Various changes here, work in progress A interpreter/function.py The one and unique Function class, with argument parsing M interpreter/executioncontext.py AM interpreter/error.py OperationError moved to its own module Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py Sat Aug 9 15:48:30 2003 @@ -1,18 +1,14 @@ -from executioncontext import ExecutionContext, OperationError, NoValue -import threadlocals +from pypy.interpreter.executioncontext import ExecutionContext, Stack +from pypy.interpreter.error import OperationError +from pypy.interpreter import threadlocals -__all__ = ['ObjSpace', 'OperationError', 'NoValue', 'PyPyError'] +__all__ = ['ObjSpace', 'OperationError', 'NoValue'] + + +class NoValue(Exception): + """Raised to signal absence of value, e.g. in the iterator accessing + method 'op.next()' of object spaces.""" -class PyPyError(Exception): - "Raise this when you encounter an exceptional situation in PyPy itself." - def __init__(self, space, operationerr): - self.space = space - self.operationerr = operationerr - -class Null: - """ marker object for Null values within the interpreter - (not visible at app-level) - """ class ObjSpace: """Base class for the interpreter-level implementations of object spaces. @@ -146,31 +142,19 @@ check_list = [w_check_class] while check_list: w_item = check_list.pop() - #Test within iterables (i.e. tuples) + # Match identical items. + if self.is_true(self.is_(w_exc_type, w_item)): + return True + # Test within iterables (i.e. tuples) try: exclst = self.unpackiterable(w_item) check_list.extend(exclst) - except KeyboardInterrupt: - raise - except: - #w_check_class is not iterable - pass - #w_item should now be an Exception (or string?) - #Match identical items. - w_rv = self.is_(w_exc_type, w_item) - if self.is_true(w_rv): - return w_rv - #Match subclasses. - try: - w_rv = self.issubtype(w_exc_type, w_item) - except KeyboardInterrupt: - raise - except: - pass - else: - if self.is_true(w_rv): - return w_rv - return self.w_False + except OperationError: + # w_item is not iterable; it should then be an Exception. + # Match subclasses. + if self.is_true(self.issubtype(w_exc_type, w_item)): + return True + return False def call_function(self, w_func, *args_w, **kw_w): w_kw = self.newdict([(self.wrap(k), w_v) for k, w_v in kw_w.iteritems()]) Copied: pypy/branch/builtinrefactor/pypy/interpreter/error.py (from rev 1262, pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py) ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/error.py Sat Aug 9 15:48:30 2003 @@ -1,53 +1,11 @@ import sys -import threadlocals -class ExecutionContext: - def __init__(self, space): +class PyPyError(Exception): + "Raise this when you encounter an exceptional situation in PyPy itself." + def __init__(self, space, operationerr): self.space = space - self.framestack = Stack() - - def eval_frame(self, frame): - locals = threadlocals.getlocals() - self.framestack.push(frame) - previous_ec = locals.executioncontext - locals.executioncontext = self - try: - result = frame.eval(self) - finally: - locals.executioncontext = previous_ec - self.framestack.pop() - return result - - def get_w_builtins(self): - if self.framestack.empty(): - return self.space.w_builtins - else: - return self.framestack.top().w_builtins - - def make_standard_w_globals(self): - "Create a new empty 'globals' dictionary." - w_key = self.space.wrap("__builtins__") - w_value = self.get_w_builtins() - w_globals = self.space.newdict([(w_key, w_value)]) - return w_globals - - def bytecode_trace(self, frame): - "Trace function called before each bytecode." - - def exception_trace(self, operationerr): - "Trace function called upon OperationError." - operationerr.record_interpreter_traceback() - #operationerr.print_detailed_traceback(self.space) - - def sys_exc_info(self): - """Implements sys.exc_info(). - Return an OperationError instance or None.""" - for i in range(self.framestack.depth()): - frame = self.framestack.top(i) - if frame.last_exception is not None: - return frame.last_exception - return None + self.operationerr = operationerr class OperationError(Exception): @@ -71,7 +29,7 @@ def match(self, space, w_check_class): "Check if this application-level exception matches 'w_check_class'." - return space.is_true(space.exception_match(self.w_type, w_check_class)) + return space.exception_match(self.w_type, w_check_class) def __str__(self): "Convenience for tracebacks." @@ -155,11 +113,6 @@ print >> file, exc_typename+':', exc_value -class NoValue(Exception): - """Raised to signal absence of value, e.g. in the iterator accessing - method 'op.next()' of object spaces.""" - - # Utilities def inlinecompile(source, space, symbol='exec'): @@ -203,40 +156,6 @@ if self.linestart: self.file.write('\n') -class Stack: - """Utility class implementing a stack.""" - - def __init__(self): - self.items = [] - - def clone(self): - s = self.__class__() - for item in self.items: - try: - item = item.clone() - except AttributeError: - pass - s.push(item) - return s - - - def push(self, item): - self.items.append(item) - - def pop(self): - return self.items.pop() - - def top(self, position=0): - """'position' is 0 for the top of the stack, 1 for the item below, - and so on. It must not be negative.""" - return self.items[~position] - - def depth(self): - return len(self.items) - - def empty(self): - return not self.items - # installing the excepthook for OperationErrors def operr_excepthook(exctype, value, traceback): if issubclass(exctype, OperationError): Added: pypy/branch/builtinrefactor/pypy/interpreter/eval.py ============================================================================== --- (empty file) +++ pypy/branch/builtinrefactor/pypy/interpreter/eval.py Sat Aug 9 15:48:30 2003 @@ -0,0 +1,118 @@ +""" +This module defines the abstract base classes that support execution: +Code and Frame. +""" + + +class Code(object): + """A code is a compiled version of some source code. + Abstract base class.""" + + def __init__(self, co_name): + self.co_name = co_name + + def create_frame(self, space): + "Create an empty frame object suitable for evaluation of this code." + raise TypeError, "abstract" + + def exec_code(self, space, w_globals, w_locals): + "Implements the 'exec' statement." + frame = self.create_frame(space) + frame.setglobaldict(w_globals) + frame.setlocaldict(w_locals) + frame.setclosure(None) + return frame.run() + + def signature(self): + "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." + return [], None, None + + def getargcount(self): + "Number of arguments including * and **." + argnames, varargname, kwargname = self.signature() + count = len(argnames) + if varargname is not None: + count += 1 + if kwargname is not None: + count += 1 + return count + + def getlocalvarname(self, index): + "Default implementation, can be overridden." + argnames, varargname, kwargname = self.signature() + try: + return argnames[index] + except IndexError: + index -= len(argnames) + if varargname is not None: + if index == 0: + return varargname + index -= 1 + if kwargname is not None: + if index == 0: + return kwargname + index -= 1 + raise IndexError, "local variable index out of bounds" + + +class Frame(object): + """A frame is an environment supporting the execution of a code object. + Abstract base class.""" + + def __init__(self, space, code): + self.space = space + self.code = code # Code instance + self.w_globals = None # wrapped dict of globals + self.w_locals = None # wrapped dict of locals + + def run(self): + "Run the frame." + executioncontext = self.space.getexecutioncontext() + previous = executioncontext.enter(self) + try: + result = self.eval(executioncontext) + finally: + executioncontext.leave(previous) + return result + + def eval(self, executioncontext): + "Abstract method to override." + raise TypeError, "abstract" + + def getglobaldict(self): + return self.w_globals + + def setglobaldict(self): + self.w_globals = w_globals + + def getlocaldict(self): + "Overriden by subclasses with another representation for locals." + return self.w_locals + + def setlocaldict(self, w_locals): + """Initialize the locals from a dictionary. + Overriden by subclasses with another representation for locals.""" + self.w_locals = w_locals + + def getlocalvar(self, index): + """Get the value of the indexth local variable + where numbering is according to self.code.signature(). + Default implementation, to be overridden.""" + varname = self.code.getlocalvarname(index) + return self.space.getitem(w_locals, self.space.wrap(varname)) + + def setlocalvar(self, index, w_value): + """Set the value of the indexth local variable, + where numbering is according to self.code.signature(). + Default implementation, to be overridden.""" + if self.w_locals is None: + self.w_locals = self.space.newdict([]) + varname = self.code.getlocalvarname(index) + self.space.setitem(self.w_locals, self.space.wrap(varname), w_value) + + def setclosure(self, closure): + """Initialize the closure from the given data, which should be None or + a list of Cells for PyFrame. This should be called after setlocalvar() + or setlocaldict() is used to initialize the frame.""" + if closure: + raise TypeError, "%s instance expects no closure" % self.__class__ Modified: pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/executioncontext.py Sat Aug 9 15:48:30 2003 @@ -1,23 +1,24 @@ -import sys -import threadlocals +from pypy.interpreter import threadlocals class ExecutionContext: - + """An ExecutionContext holds the state of an execution thread + in the Python interpreter.""" + def __init__(self, space): + # Note that self.framestack only contains PyFrames self.space = space self.framestack = Stack() - def eval_frame(self, frame): + def enter(self, frame): locals = threadlocals.getlocals() self.framestack.push(frame) previous_ec = locals.executioncontext locals.executioncontext = self - try: - result = frame.eval(self) - finally: - locals.executioncontext = previous_ec - self.framestack.pop() - return result + return previous_ec + + def leave(self, previous_ec): + locals.executioncontext = previous_ec + self.framestack.pop() def get_w_builtins(self): if self.framestack.empty(): @@ -50,159 +51,6 @@ return None -class OperationError(Exception): - """Interpreter-level exception that signals an exception that should be - sent to the application level. - - OperationError instances have three public attributes (and no .args), - w_type, w_value and application_traceback, which contain the wrapped - type and value describing the exception, and the unwrapped list of - (frame, instruction_position) making the application-level traceback. - """ - - def __init__(self, w_type, w_value): - self.w_type = w_type - self.w_value = w_value - self.application_traceback = [] - self.debug_tbs = [] - - def record_application_traceback(self, frame, last_instruction): - self.application_traceback.append((frame, last_instruction)) - - def match(self, space, w_check_class): - "Check if this application-level exception matches 'w_check_class'." - return space.is_true(space.exception_match(self.w_type, w_check_class)) - - def __str__(self): - "Convenience for tracebacks." - return '[%s: %s]' % (self.w_type, self.w_value) - - def errorstr(self, space): - "The exception class and value, as a string." - exc_type = space.unwrap( - space.getattr(self.w_type, space.wrap('__name__'))) - exc_value = space.unwrap(space.str(self.w_value)) - return '%s: %s' % (exc_type, exc_value) - - def getframe(self): - "The frame this exception was raised in, or None." - if self.application_traceback: - frame, last_instruction = self.application_traceback[0] - return frame - else: - return None - - def record_interpreter_traceback(self): - """Records the current traceback inside the interpreter. - This traceback is only useful to debug the interpreter, not the - application.""" - self.debug_tbs.append(sys.exc_info()[2]) - - def print_application_traceback(self, space, file=None): - "Dump a standard application-level traceback." - if file is None: file = sys.stderr - self.print_app_tb_only(file) - print >> file, self.errorstr(space) - - def print_app_tb_only(self, file): - tb = self.application_traceback[:] - if tb: - import linecache - tb.reverse() - print >> file, "Traceback (application-level):" - for f, i in tb: - co = f.bytecode - lineno = offset2lineno(co, i) - fname = co.co_filename - if fname.startswith('\n'): - lines = fname.split('\n') - fname = lines[0].strip() - try: - l = lines[lineno] - except IndexError: - l = '' - else: - l = linecache.getline(fname, lineno) - print >> file, " File \"%s\"," % fname, - print >> file, "line", lineno, "in", co.co_name - if l: - if l.endswith('\n'): - l = l[:-1] - print >> file, l - - def print_detailed_traceback(self, space=None, file=None): - """Dump a nice detailed interpreter- and application-level traceback, - useful to debug the interpreter.""" - if file is None: file = sys.stderr - for i in range(len(self.debug_tbs)-1, -1, -1): - import traceback - interpr_file = LinePrefixer(file, '||') - print >> interpr_file, "Traceback (interpreter-level):" - traceback.print_tb(self.debug_tbs[i], file=interpr_file) - self.print_app_tb_only(file) - if space is None: - exc_typename = str(self.w_type) - exc_value = self.w_value - else: - w = space.wrap - exc_typename = space.unwrap( - space.getattr(self.w_type, w('__name__'))) - exc_value = space.unwrap(space.str(self.w_value)) - print >> file, '(application-level)', - if exc_value is None: - print >> file, exc_typename - else: - print >> file, exc_typename+':', exc_value - - -class NoValue(Exception): - """Raised to signal absence of value, e.g. in the iterator accessing - method 'op.next()' of object spaces.""" - - -# Utilities - -def inlinecompile(source, space, symbol='exec'): - """Compile the given 'source' string. - This function differs from the built-in compile() because it abuses - co_filename to store a copy of the complete source code. - This lets OperationError.print_application_traceback() print the - actual source line in the traceback.""" - compile = space.builtin.compile - w = space.wrap - return compile(w(source), w('\n%s'%source), w(symbol), w(0), w(0)) - - -def offset2lineno(c, stopat): - tab = c.co_lnotab - line = c.co_firstlineno - addr = 0 - for i in range(0, len(tab), 2): - addr = addr + ord(tab[i]) - if addr > stopat: - break - line = line + ord(tab[i+1]) - return line - -class LinePrefixer: - """File-like class that inserts a prefix string - at the beginning of each line it prints.""" - def __init__(self, file, prefix): - self.file = file - self.prefix = prefix - self.linestart = True - def write(self, data): - if self.linestart: - self.file.write(self.prefix) - if data.endswith('\n'): - data = data[:-1] - self.linestart = True - else: - self.linestart = False - self.file.write(data.replace('\n', '\n'+self.prefix)) - if self.linestart: - self.file.write('\n') - class Stack: """Utility class implementing a stack.""" @@ -219,7 +67,6 @@ s.push(item) return s - def push(self, item): self.items.append(item) @@ -236,13 +83,3 @@ def empty(self): return not self.items - -# installing the excepthook for OperationErrors -def operr_excepthook(exctype, value, traceback): - if issubclass(exctype, OperationError): - value.debug_tbs.append(traceback) - value.print_detailed_traceback() - else: - old_excepthook(exctype, value, traceback) -old_excepthook = sys.excepthook -sys.excepthook = operr_excepthook Added: pypy/branch/builtinrefactor/pypy/interpreter/function.py ============================================================================== --- (empty file) +++ pypy/branch/builtinrefactor/pypy/interpreter/function.py Sat Aug 9 15:48:30 2003 @@ -0,0 +1,168 @@ +""" +Function objects. + +In PyPy there is no difference between built-in and user-defined function +objects; the difference lies in the code object found in their func_code +attribute. +""" + +class Function: + """A function is a code object captured with some environment: + an object space, a dictionary of globals, default arguments, + and an arbitrary 'closure' passed to the code object.""" + + def __init__(self, space, code, w_globals, w_defs=None, closure=None): + self.space = space + self.func_code = code # Code instance + self.w_globals = w_globals # the globals dictionary + self.w_defs = w_defs # wrapped sequence of default args or None + self.closure = closure # normally, list of Cell instances or None + + def parse_args(self, frame, w_args, w_kwargs=None): + """ parse args and kwargs to initialize the frame. + """ + space = self.space + signature = self.func_code.signature() + argnames, varargname, kwargname = signature + # test for 'simple case': + if (varargname is None and kwargname is None and # no */** + (w_kwargs is None or not space.is_true(w_kwargs)) and # no kwargs + self.unwrap(self.len(w_args)) == len(argnames)): # correct #args + for index in range(len(argnames)): + w_index = self.wrap(index) + w_argument = self.getitem(w_args, w_index) + frame.setlocalvar(index, w_argument) + else: + #print "complicated case of arguments for", self.func_code.co_name + if w_kwargs is None: + w_kwargs = space.w_None + self.parse_args_complex(frame, w_args, w_kwargs, + space.wrap(signature)) + frame.setclosure(self.closure) + + def app_parse_args_complex(self, frame, args, kws, signature): + """ app-level helper for the complex case of parse_args(). + """ + # ===== ATTENTION ===== + # + # This code is pretty fundamental to pypy and great care must be taken + # to avoid infinite recursion. In particular: + # + # - All calls here must be "easy", i.e. not involve default or keyword + # arguments. For example, all range() calls need three arguments. + # + # - You cannot *catch* any exceptions (raising is fine). + # + # Assumptions: + # frame = empty frame to be initialized + # args = sequence of the normal actual parameters + # kws = dictionary of keyword parameters or None + # self.defs = sequence of defaults + # + # We try to give error messages following CPython's, which are + # very informative. + + argnames, varargname, kwargname = signature + input_argcount = len(args) + co_argcount = len(argnames) + deffirst = co_argcount - len(self.defs) + if kws: + kwargs = kws.copy() + else: + kwargs = {} + + # fetch all arguments left-to-right + for i in range(0, co_argcount, 1): + argname = argnames[i] + if i < input_argcount: + value = args[i] + # check that no keyword argument also gives a value here + if argname in kwargs: + raise TypeError, self.argerr_multiple_values(argname) + elif argname in kwargs: + # positional arguments exhausted, + # complete with keyword arguments + value = kwargs[argname] + del kwargs[argname] + elif i >= deffirst: + # no matching keyword argument, but there is a default value + value = self.defs[i - deffirst] + else: + raise TypeError, self.argerr(signature, args, kws, False) + frame.setlocalvar(i, value) + + # collect extra positional arguments into the *vararg + specialarg = co_argcount + if varargname is not None: + var_tuple = args[co_argcount:] + frame.setlocalvar(specialarg, var_tuple) + specialarg += 1 + elif input_argcount > co_argcount: + # cannot do anything with these extra positional arguments + raise TypeError, self.argerr(signature, args, kws, True) + + # collect extra keyword arguments into the **kwarg + if kwargname is not None: + # XXX this doesn't check that the keys of kwargs are strings + frame.setlocalvar(specialarg, kwargs) + specialarg += 1 + elif kwargs: + # cannot do anything with these extra keyword arguments + raise TypeError, self.argerr_unknown_kwds(kwargs) + parse_args_complex = app2interp(app_parse_args_complex) + + # helper functions to build error message for the above + + def app_argerr(self, signature, args, kws, too_many): + argnames, varargname, kwargname = signature + n = len(argnames) + if n == 0: + n = len(args) + if kwargname is not None: + msg2 = "non-keyword " + else: + msg2 = "" + n += len(kws) + return "%s() takes no %sargument (%d given)" % ( + self.func_code.co_name, + msg2, + n) + else: + defcount = len(self.defs) + if defcount == 0: + msg1 = "exactly" + elif too_many: + msg1 = "at most" + else: + msg1 = "at least" + n -= defcount + if kws: + msg2 = "non-keyword " + else: + msg2 = "" + if n == 1: + plural = "" + else: + plural = "s" + return "%s() takes %s %d %sargument%s (%d given)" % ( + self.func_code.co_name, + msg1, + n, + msg2, + plural, + len(args)) + + def app_argerr_multiple_values(self, argname): + return "%s() got multiple values for keyword argument %s" % ( + self.func_code.co_name, + argname) + + def app_argerr_unknown_kwds(self, kws): + if len(kws) == 1: + return "%s() got an unexpected keyword argument '%s'" % ( + self.func_code.co_name, + kws.keys()[0]) + else: + return "%s() got %d unexpected keyword arguments" % ( + self.func_code.co_name, + len(kws)) Modified: pypy/branch/builtinrefactor/pypy/interpreter/gateway.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/gateway.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/gateway.py Sat Aug 9 15:48:30 2003 @@ -1,10 +1,23 @@ """ -gateway between app-level and interpreter-level +Gateway between app-level and interpreter-level: +the Wrappable base class. """ -import inspect -CO_VARARGS, CO_VARKEYWORDS = 0x4, 0x8 + +class Wrappable(object): + """A subclass of Wrappable is an internal, interpreter-level class + that can nevertheless be exposed at application-level, + via space.wrap(). + + The methods and attributes that a class wants to expose are defined + with a naming convension: 'app_xxx' is exposed as 'xxx'. + In general, 'app_xxx' should be a gateway (see below).""" + +# XXX most code from some classes below have been "stolen" in +# XXX new classes in other modules. Will be removed. + + class ScopedCode(object): """ a code object within a certain global and closure scope. Deleted: /pypy/branch/builtinrefactor/pypy/interpreter/opcode.py ============================================================================== --- /pypy/branch/builtinrefactor/pypy/interpreter/opcode.py Sat Aug 9 15:48:30 2003 +++ (empty file) @@ -1,758 +0,0 @@ -from appfile import AppFile -from pypy.interpreter.baseobjspace import OperationError, NoValue -from pypy.interpreter.pyframe import _NULL -import dis -from pypy.interpreter import pyframe, baseobjspace -from pypy.interpreter.gateway import InterpretedFunction, ScopedCode - - -# dynamically loaded application-space utilities -appfile = AppFile(__name__, ["interpreter"]) - - -class unaryoperation: - def __init__(self, operationname): - self.operationname = operationname - def __call__(self, f): - operation = getattr(f.space, self.operationname) - w_1 = f.valuestack.pop() - w_result = operation(w_1) - f.valuestack.push(w_result) - -class binaryoperation: - def __init__(self, operationname): - self.operationname = operationname - def __call__(self, f): - operation = getattr(f.space, self.operationname) - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - w_result = operation(w_1, w_2) - f.valuestack.push(w_result) - - -################################################################ -## Implementation of the opcodes -## - -def LOAD_FAST(f, varindex): - # access a local variable through its cell object - w_value = f.locals_w[varindex] - if w_value is _NULL: - varname = f.getlocalvarname(varindex) - message = "local variable '%s' referenced before assignment" % varname - raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) - f.valuestack.push(w_value) - -def LOAD_CONST(f, constindex): - w_const = f.space.wrap(f.getconstant(constindex)) - f.valuestack.push(w_const) - -def STORE_FAST(f, varindex): - try: - w_newvalue = f.valuestack.pop() - f.locals_w[varindex] = w_newvalue - except: - print "exception: got index error" - print " varindex:", varindex - print " len(locals_w)", len(f.locals_w) - import dis - print dis.dis(f.bytecode) - print "co_varnames", f.bytecode.co_varnames - print "co_nlocals", f.bytecode.co_nlocals - raise - - -def POP_TOP(f): - f.valuestack.pop() - -def ROT_TWO(f): - w_1 = f.valuestack.pop() - w_2 = f.valuestack.pop() - f.valuestack.push(w_1) - f.valuestack.push(w_2) - -def ROT_THREE(f): - w_1 = f.valuestack.pop() - w_2 = f.valuestack.pop() - w_3 = f.valuestack.pop() - f.valuestack.push(w_1) - f.valuestack.push(w_3) - f.valuestack.push(w_2) - -def ROT_FOUR(f): - w_1 = f.valuestack.pop() - w_2 = f.valuestack.pop() - w_3 = f.valuestack.pop() - w_4 = f.valuestack.pop() - f.valuestack.push(w_1) - f.valuestack.push(w_4) - f.valuestack.push(w_3) - f.valuestack.push(w_2) - -def DUP_TOP(f): - w_1 = f.valuestack.top() - f.valuestack.push(w_1) - -def DUP_TOPX(f, itemcount): - assert 1 <= itemcount <= 5, "limitation of the current interpreter" - for i in range(itemcount): - w_1 = f.valuestack.top(itemcount-1) - f.valuestack.push(w_1) - -UNARY_POSITIVE = unaryoperation("pos") -UNARY_NEGATIVE = unaryoperation("neg") -UNARY_NOT = unaryoperation("not_") -UNARY_CONVERT = unaryoperation("repr") -UNARY_INVERT = unaryoperation("invert") - -def BINARY_POWER(f): - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - w_result = f.space.pow(w_1, w_2, f.space.w_None) - f.valuestack.push(w_result) - -BINARY_MULTIPLY = binaryoperation("mul") -BINARY_TRUE_DIVIDE = binaryoperation("truediv") -BINARY_FLOOR_DIVIDE = binaryoperation("floordiv") -BINARY_DIVIDE = binaryoperation("div") -BINARY_MODULO = binaryoperation("mod") -BINARY_ADD = binaryoperation("add") -BINARY_SUBTRACT = binaryoperation("sub") -BINARY_SUBSCR = binaryoperation("getitem") -BINARY_LSHIFT = binaryoperation("lshift") -BINARY_RSHIFT = binaryoperation("rshift") -BINARY_AND = binaryoperation("and_") -BINARY_XOR = binaryoperation("xor") -BINARY_OR = binaryoperation("or_") - -def INPLACE_POWER(f): - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - w_result = f.space.inplace_pow(w_1, w_2, f.space.w_None) - f.valuestack.push(w_result) - -INPLACE_MULTIPLY = binaryoperation("inplace_mul") -INPLACE_TRUE_DIVIDE = binaryoperation("inplace_truediv") -INPLACE_FLOOR_DIVIDE = binaryoperation("inplace_floordiv") -INPLACE_DIVIDE = binaryoperation("inplace_div") -INPLACE_MODULO = binaryoperation("inplace_mod") -INPLACE_ADD = binaryoperation("inplace_add") -INPLACE_SUBTRACT = binaryoperation("inplace_sub") -INPLACE_LSHIFT = binaryoperation("inplace_lshift") -INPLACE_RSHIFT = binaryoperation("inplace_rshift") -INPLACE_AND = binaryoperation("inplace_and") -INPLACE_XOR = binaryoperation("inplace_xor") -INPLACE_OR = binaryoperation("inplace_or") - -def slice(f, w_start, w_end): - w_slice = f.space.newslice(w_start, w_end, None) - w_obj = f.valuestack.pop() - w_result = f.space.getitem(w_obj, w_slice) - f.valuestack.push(w_result) - -def SLICE_0(f): - slice(f, None, None) - -def SLICE_1(f): - w_start = f.valuestack.pop() - slice(f, w_start, None) - -def SLICE_2(f): - w_end = f.valuestack.pop() - slice(f, None, w_end) - -def SLICE_3(f): - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - slice(f, w_start, w_end) - -def storeslice(f, w_start, w_end): - w_slice = f.space.newslice(w_start, w_end, None) - w_obj = f.valuestack.pop() - w_newvalue = f.valuestack.pop() - f.space.setitem(w_obj, w_slice, w_newvalue) - -def STORE_SLICE_0(f): - storeslice(f, None, None) - -def STORE_SLICE_1(f): - w_start = f.valuestack.pop() - storeslice(f, w_start, None) - -def STORE_SLICE_2(f): - w_end = f.valuestack.pop() - storeslice(f, None, w_end) - -def STORE_SLICE_3(f): - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - storeslice(f, w_start, w_end) - -def deleteslice(f, w_start, w_end): - w_slice = f.space.newslice(w_start, w_end, None) - w_obj = f.valuestack.pop() - f.space.delitem(w_obj, w_slice) - -def DELETE_SLICE_0(f): - deleteslice(f, None, None) - -def DELETE_SLICE_1(f): - w_start = f.valuestack.pop() - deleteslice(f, w_start, None) - -def DELETE_SLICE_2(f): - w_end = f.valuestack.pop() - deleteslice(f, None, w_end) - -def DELETE_SLICE_3(f): - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - deleteslice(f, w_start, w_end) - -def STORE_SUBSCR(f): - "obj[subscr] = newvalue" - w_subscr = f.valuestack.pop() - w_obj = f.valuestack.pop() - w_newvalue = f.valuestack.pop() - f.space.setitem(w_obj, w_subscr, w_newvalue) - -def DELETE_SUBSCR(f): - "del obj[subscr]" - w_subscr = f.valuestack.pop() - w_obj = f.valuestack.pop() - f.space.delitem(w_obj, w_subscr) - -def PRINT_EXPR(f): - w_expr = f.valuestack.pop() - #print f.space.unwrap(w_expr) - f.space.gethelper(appfile).call("print_expr", [w_expr]) - -def PRINT_ITEM_TO(f): - w_stream = f.valuestack.pop() - w_item = f.valuestack.pop() - f.space.gethelper(appfile).call("print_item_to", [w_item, w_stream]) - -def PRINT_ITEM(f): - w_item = f.valuestack.pop() - f.space.gethelper(appfile).call("print_item", [w_item]) - -def PRINT_NEWLINE_TO(f): - w_stream = f.valuestack.pop() - f.space.gethelper(appfile).call("print_newline_to", [w_stream]) - -def PRINT_NEWLINE(f): - f.space.gethelper(appfile).call("print_newline", []) - -def BREAK_LOOP(f): - raise pyframe.SBreakLoop - -def CONTINUE_LOOP(f, startofloop): - raise pyframe.SContinueLoop(startofloop) - -def RAISE_VARARGS(f, nbargs): - # we use the .app.py file to prepare the exception/value/traceback - # but not to actually raise it, because we cannot use the 'raise' - # statement to implement RAISE_VARARGS - if nbargs == 0: - w_resulttuple = f.space.gethelper(appfile).call("prepare_raise0", []) - elif nbargs == 1: - w_type = f.valuestack.pop() - w_resulttuple = f.space.gethelper(appfile).call( - "prepare_raise", [w_type, f.space.w_None, f.space.w_None]) - elif nbargs == 2: - w_value = f.valuestack.pop() - w_type = f.valuestack.pop() - w_resulttuple = f.space.gethelper(appfile).call( - "prepare_raise", [w_type, w_value, f.space.w_None]) - elif nbargs == 3: - w_traceback = f.valuestack.pop() - w_value = f.valuestack.pop() - w_type = f.valuestack.pop() - w_resulttuple = f.space.gethelper(appfile).call( - "prepare_raise", [w_type, w_value, w_traceback]) - else: - raise pyframe.BytecodeCorruption, "bad RAISE_VARARGS oparg" - w_type, w_value, w_traceback = f.space.unpacktuple(w_resulttuple) - # XXX the three-arguments 'raise' is not supported yet - raise OperationError(w_type, w_value) - -def LOAD_LOCALS(f): - f.valuestack.push(f.w_locals) - -def RETURN_VALUE(f): - w_returnvalue = f.valuestack.pop() - raise pyframe.SReturnValue(w_returnvalue) - -def YIELD_VALUE(f): - w_yieldedvalue = f.valuestack.pop() - raise pyframe.SYieldValue(w_yieldedvalue) -YIELD_STMT = YIELD_VALUE # misnamed in dis.opname - -def EXEC_STMT(f): - w_locals = f.valuestack.pop() - w_globals = f.valuestack.pop() - w_prog = f.valuestack.pop() - w_tuple = f.space.gethelper(appfile).call("exec_statement", - [w_prog, w_globals, w_locals, - f.w_builtins, f.w_globals, f.w_locals]) - w_prog = f.space.getitem(w_tuple,f.space.wrap(0)) - w_globals = f.space.getitem(w_tuple,f.space.wrap(1)) - w_locals = f.space.getitem(w_tuple,f.space.wrap(2)) - - plain = (w_locals is f.w_locals) - if plain: - f.fast2locals() - scopedcode = ScopedCode(f.space, f.space.unwrap(w_prog), w_globals) - scopedcode.eval_frame(w_locals) - #f.space.unwrap(w_prog).eval_code(f.space, w_globals, w_locals) - if plain: - f.locals2fast() - -def POP_BLOCK(f): - block = f.blockstack.pop() - block.cleanup(f) # the block knows how to clean up the value stack - -def END_FINALLY(f): - # unlike CPython, when we reach this opcode the value stack has - # always been set up as follows (topmost first): - # [exception type or None] - # [exception value or None] - # [wrapped stack unroller ] - f.valuestack.pop() # ignore the exception type - f.valuestack.pop() # ignore the exception value - unroller = f.space.unwrap(f.valuestack.pop()) - if unroller is not None: - raise unroller # re-raise the unroller, if any - -def BUILD_CLASS(f): - w_methodsdict = f.valuestack.pop() - w_bases = f.valuestack.pop() - w_name = f.valuestack.pop() - # XXX it would be best to have all opcodes on a class that has a 'space' attribute - # then the following initialization could be done at init-time. - build_class = InterpretedFunction(f.space.gethelperspace(), app_build_class) - w_newclass = build_class(w_name, w_bases, w_methodsdict, f.w_globals) - f.valuestack.push(w_newclass) - -def app_build_class(name, bases, namespace, globals): - if '__metaclass__' in namespace: - metaclass = namespace['__metaclass__'] - elif len(bases) > 0: - base = bases[0] - if hasattr(base, '__class__'): - metaclass = base.__class__ - else: - metaclass = type(base) - elif '__metaclass__' in globals: - metaclass = globals['__metaclass__'] - else: - metaclass = type - return metaclass(name, bases, namespace) - -def STORE_NAME(f, varindex): - varname = f.getname(varindex) - w_varname = f.space.wrap(varname) - w_newvalue = f.valuestack.pop() - f.space.setitem(f.w_locals, w_varname, w_newvalue) - -def DELETE_NAME(f, varindex): - varname = f.getname(varindex) - w_varname = f.space.wrap(varname) - try: - f.space.delitem(f.w_locals, w_varname) - except OperationError, e: - # catch KeyErrors and turn them into NameErrors - if not e.match(f.space, f.space.w_KeyError): - raise - message = "name '%s' is not defined" % varname - raise OperationError(f.space.w_NameError, f.space.wrap(message)) - -def UNPACK_SEQUENCE(f, itemcount): - w_iterable = f.valuestack.pop() - try: - items = f.space.unpackiterable(w_iterable, itemcount) - except ValueError, e: - raise OperationError(f.space.w_ValueError, f.space.wrap(str(e))) - items.reverse() - for item in items: - f.valuestack.push(item) - -def STORE_ATTR(f, nameindex): - "obj.attributename = newvalue" - attributename = f.getname(nameindex) - w_attributename = f.space.wrap(attributename) - w_obj = f.valuestack.pop() - w_newvalue = f.valuestack.pop() - f.space.setattr(w_obj, w_attributename, w_newvalue) - -def DELETE_ATTR(f, nameindex): - "del obj.attributename" - attributename = f.getname(nameindex) - w_attributename = f.space.wrap(attributename) - w_obj = f.valuestack.pop() - f.space.delattr(w_obj, w_attributename) - -def STORE_GLOBAL(f, nameindex): - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - w_newvalue = f.valuestack.pop() - f.space.setitem(f.w_globals, w_varname, w_newvalue) - -def DELETE_GLOBAL(f, nameindex): - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - f.space.delitem(f.w_globals, w_varname) - -def LOAD_NAME(f, nameindex): - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - try: - w_value = f.space.getitem(f.w_locals, w_varname) - except OperationError, e: - if not e.match(f.space, f.space.w_KeyError): - raise - try: - w_value = f.space.getitem(f.w_globals, w_varname) - except OperationError, e: - if not e.match(f.space, f.space.w_KeyError): - raise - try: - w_value = f.space.getitem(f.w_builtins, w_varname) - except OperationError, e: - if not e.match(f.space, f.space.w_KeyError): - raise - message = "global name '%s' is not defined" % varname - w_exc_type = f.space.w_NameError - w_exc_value = f.space.wrap(message) - raise OperationError(w_exc_type, w_exc_value) - f.valuestack.push(w_value) - # XXX the implementation can be pushed back into app-space as an - # when exception handling begins to behave itself. For now, it - # was getting on my nerves -- mwh -# w_value = f.space.gethelper(appfile).call( -# "load_name", [w_varname, f.w_locals, f.w_globals, f.w_builtins]) -# f.valuestack.push(w_value) - -def LOAD_GLOBAL(f, nameindex): - assert f.w_globals is not None - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - try: - w_value = f.space.getitem(f.w_globals, w_varname) - except OperationError, e: - # catch KeyErrors - if not e.match(f.space, f.space.w_KeyError): - raise - # we got a KeyError, now look in the built-ins - try: - w_value = f.space.getitem(f.w_builtins, w_varname) - except OperationError, e: - # catch KeyErrors again - if not e.match(f.space, f.space.w_KeyError): - raise - message = "global name '%s' is not defined" % varname - w_exc_type = f.space.w_NameError - w_exc_value = f.space.wrap(message) - raise OperationError(w_exc_type, w_exc_value) - f.valuestack.push(w_value) - -def DELETE_FAST(f, varindex): - w_value = f.locals_w[varindex] - if f.locals_w[varindex] is _NULL: - varname = f.getlocalvarname(varindex) - message = "local variable '%s' referenced before assignment" % varname - raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) - f.locals_w[varindex] = _NULL - -def LOAD_CLOSURE(f, varindex): - # nested scopes: access the cell object - cell = f.closure_w[varindex] - w_value = f.space.wrap(cell) - f.valuestack.push(w_value) - -def LOAD_DEREF(f, varindex): - # nested scopes: access a variable through its cell object - cell = f.closure_w[varindex] - try: - w_value = cell.get() - except ValueError: - varname = f.getfreevarname(varindex) - if f.iscellvar(varindex): - message = "local variable '%s' referenced before assignment" - w_exc_type = f.space.w_UnboundLocalError - else: - message = ("free variable '%s' referenced before assignment" - " in enclosing scope") - w_exc_type = f.space.w_NameError - raise OperationError(w_exc_type, f.space.wrap(message % varname)) - f.valuestack.push(w_value) - -def STORE_DEREF(f, varindex): - # nested scopes: access a variable through its cell object - w_newvalue = f.valuestack.pop() - try: - cell = f.closure_w[varindex] - except IndexError: - #import pdb; pdb.set_trace() - raise - cell.set(w_newvalue) - -def BUILD_TUPLE(f, itemcount): - items = [f.valuestack.pop() for i in range(itemcount)] - items.reverse() - w_tuple = f.space.newtuple(items) - f.valuestack.push(w_tuple) - -def BUILD_LIST(f, itemcount): - items = [f.valuestack.pop() for i in range(itemcount)] - items.reverse() - w_list = f.space.newlist(items) - f.valuestack.push(w_list) - -def BUILD_MAP(f, zero): - if zero != 0: - raise pyframe.BytecodeCorruption - w_dict = f.space.newdict([]) - f.valuestack.push(w_dict) - -def LOAD_ATTR(f, nameindex): - "obj.attributename" - attributename = f.getname(nameindex) - w_attributename = f.space.wrap(attributename) - w_obj = f.valuestack.pop() - w_value = f.space.getattr(w_obj, w_attributename) - f.valuestack.push(w_value) - -def cmp_lt(f, w_1, w_2): return f.space.lt(w_1, w_2) -def cmp_le(f, w_1, w_2): return f.space.le(w_1, w_2) -def cmp_eq(f, w_1, w_2): return f.space.eq(w_1, w_2) -def cmp_ne(f, w_1, w_2): return f.space.ne(w_1, w_2) -def cmp_gt(f, w_1, w_2): return f.space.gt(w_1, w_2) -def cmp_ge(f, w_1, w_2): return f.space.ge(w_1, w_2) - -def cmp_in(f, w_1, w_2): - return f.space.contains(w_2, w_1) -def cmp_not_in(f, w_1, w_2): - return f.space.not_(f.space.contains(w_2, w_1)) -def cmp_is(f, w_1, w_2): - return f.space.is_(w_1, w_2) -def cmp_is_not(f, w_1, w_2): - return f.space.not_(f.space.is_(w_1, w_2)) -def cmp_exc_match(f, w_1, w_2): - return f.space.exception_match(w_1, w_2) - -compare_dispatch_table = { - 0: cmp_lt, # "<" - 1: cmp_le, # "<=" - 2: cmp_eq, # "==" - 3: cmp_ne, # "!=" - 4: cmp_gt, # ">" - 5: cmp_ge, # ">=" - 6: cmp_in, - 7: cmp_not_in, - 8: cmp_is, - 9: cmp_is_not, - 10: cmp_exc_match, - } -def COMPARE_OP(f, testnum): - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - try: - testfn = compare_dispatch_table[testnum] - except KeyError: - raise pyframe.BytecodeCorruption, "bad COMPARE_OP oparg" - w_result = testfn(f, w_1, w_2) - f.valuestack.push(w_result) - -def IMPORT_NAME(f, nameindex): - modulename = f.getname(nameindex) - w_modulename = f.space.wrap(modulename) - w_fromlist = f.valuestack.pop() - w_obj = f.space.gethelper(appfile).call( - "import_name", [f.w_builtins, - w_modulename, f.w_globals, f.w_locals, w_fromlist]) - f.valuestack.push(w_obj) - -def IMPORT_STAR(f): - w_module = f.valuestack.pop() - f.space.gethelper(appfile).call("import_all_from", [w_module, f.w_locals]) - -def IMPORT_FROM(f, nameindex): - name = f.getname(nameindex) - w_name = f.space.wrap(name) - w_module = f.valuestack.top() - w_obj = f.space.gethelper(appfile).call("import_from", [w_module, w_name]) - f.valuestack.push(w_obj) - -def JUMP_FORWARD(f, stepby): - f.next_instr += stepby - -def JUMP_IF_FALSE(f, stepby): - w_cond = f.valuestack.top() - if not f.space.is_true(w_cond): - f.next_instr += stepby - -def JUMP_IF_TRUE(f, stepby): - w_cond = f.valuestack.top() - if f.space.is_true(w_cond): - f.next_instr += stepby - -def JUMP_ABSOLUTE(f, jumpto): - f.next_instr = jumpto - -def GET_ITER(f): - w_iterable = f.valuestack.pop() - w_iterator = f.space.iter(w_iterable) - f.valuestack.push(w_iterator) - -def FOR_ITER(f, jumpby): - w_iterator = f.valuestack.top() - try: - w_nextitem = f.space.next(w_iterator) - except NoValue: - # iterator exhausted - f.valuestack.pop() - f.next_instr += jumpby - else: - f.valuestack.push(w_nextitem) - -def FOR_LOOP(f, oparg): - raise pyframe.BytecodeCorruption, "old opcode, no longer in use" - -def SETUP_LOOP(f, offsettoend): - block = pyframe.LoopBlock(f, f.next_instr + offsettoend) - f.blockstack.push(block) - -def SETUP_EXCEPT(f, offsettoend): - block = pyframe.ExceptBlock(f, f.next_instr + offsettoend) - f.blockstack.push(block) - -def SETUP_FINALLY(f, offsettoend): - block = pyframe.FinallyBlock(f, f.next_instr + offsettoend) - f.blockstack.push(block) - -def call_function_extra(f, oparg, with_varargs, with_varkw): - n_arguments = oparg & 0xff - n_keywords = (oparg>>8) & 0xff - if with_varkw: - w_varkw = f.valuestack.pop() - if with_varargs: - w_varargs = f.valuestack.pop() - keywords = [] - for i in range(n_keywords): - w_value = f.valuestack.pop() - w_key = f.valuestack.pop() - keywords.append((w_key, w_value)) - arguments = [f.valuestack.pop() for i in range(n_arguments)] - arguments.reverse() - w_function = f.valuestack.pop() - w_arguments = f.space.newtuple(arguments) - w_keywords = f.space.newdict(keywords) - if with_varargs: - w_arguments = f.space.gethelper(appfile).call("concatenate_arguments", - [w_arguments, w_varargs]) - if with_varkw: - w_keywords = f.space.gethelper(appfile).call("concatenate_keywords", - [w_keywords, w_varkw]) - w_result = f.space.call(w_function, w_arguments, w_keywords) - f.valuestack.push(w_result) - -def CALL_FUNCTION(f, oparg): - call_function_extra(f, oparg, False, False) - -def CALL_FUNCTION_VAR(f, oparg): - call_function_extra(f, oparg, True, False) - -def CALL_FUNCTION_KW(f, oparg): - call_function_extra(f, oparg, False, True) - -def CALL_FUNCTION_VAR_KW(f, oparg): - call_function_extra(f, oparg, True, True) - -def MAKE_FUNCTION(f, numdefaults): - w_codeobj = f.valuestack.pop() - defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] - defaultarguments.reverse() - w_defaultarguments = f.space.newtuple(defaultarguments) - w_func = f.space.newfunction(f.space.unwrap(w_codeobj), - f.w_globals, w_defaultarguments) - f.valuestack.push(w_func) - -def MAKE_CLOSURE(f, numdefaults): - w_codeobj = f.valuestack.pop() - codeobj = f.space.unwrap(w_codeobj) - nfreevars = len(codeobj.co_freevars) - freevars = [f.valuestack.pop() for i in range(nfreevars)] - freevars.reverse() - w_freevars = f.space.newtuple(freevars) - defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] - defaultarguments.reverse() - w_defaultarguments = f.space.newtuple(defaultarguments) - w_func = f.space.newfunction(f.space.unwrap(w_codeobj), - f.w_globals, w_defaultarguments, w_freevars) - f.valuestack.push(w_func) - -def BUILD_SLICE(f, numargs): - if numargs == 3: - w_step = f.valuestack.pop() - elif numargs == 2: - w_step = None - else: - raise pyframe.BytecodeCorruption - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - w_slice = f.space.newslice(w_start, w_end, w_step) - f.valuestack.push(w_slice) - -def SET_LINENO(f, lineno): - pass - -def EXTENDED_ARG(f, oparg): - opcode = f.nextop() - oparg = oparg<<16 | f.nextarg() - dispatch_arg(f, oparg) - -def MISSING_OPCODE(f, oparg=None): - raise pyframe.BytecodeCorruption, "unknown opcode" - - -################################################################ - -dispatch_table = [] -for i in range(256): - opname = dis.opname[i].replace('+', '_') - fn = MISSING_OPCODE - if opname in globals(): - fn = globals()[opname] - elif not opname.startswith('<') and i>0: - #import warnings - #warnings.warn("* Warning, missing opcode %s" % opname) - pass - dispatch_table.append(fn) - - -def name(thing): - try: - return thing.operationname - except AttributeError: - return thing.__name__ - -def has_arg(opcode): - return opcode >= dis.HAVE_ARGUMENT - -def dispatch_noarg(f, opcode): - try: - fn = dispatch_table[opcode] -# print name(fn) - except KeyError: - raise KeyError, "missing opcode %s" % dis.opname[opcode] - fn(f) - -def dispatch_arg(f, opcode, oparg): - assert oparg >= 0 - try: - fn = dispatch_table[opcode] -# print name(fn) - except KeyError: - raise KeyError, "missing opcode %s" % dis.opname[opcode] - fn(f, oparg) Added: pypy/branch/builtinrefactor/pypy/interpreter/pycode.py ============================================================================== --- (empty file) +++ pypy/branch/builtinrefactor/pypy/interpreter/pycode.py Sat Aug 9 15:48:30 2003 @@ -0,0 +1,105 @@ +""" +Python-style code objects. +PyCode instances have the same co_xxx arguments as CPython code objects. +The bytecode interpreter itself is implemented by the PyFrame class. +""" + +from pypy.interpreter import eval +from pypy.interpreter.pyopcode import PyOperationalFrame +from pypy.interpreter.pyfastscope import PyFastScopeFrame +from pypy.interpreter.pynestedscope import PyNestedScopeFrame + + +# code object contants, for co_flags below +CO_OPTIMIZED = 0x0001 +CO_NEWLOCALS = 0x0002 +CO_VARARGS = 0x0004 +CO_VARKEYWORDS = 0x0008 +CO_NESTED = 0x0010 +CO_GENERATOR = 0x0020 + + +class PyCode(eval.Code): + "CPython-style code objects." + + def __init__(self, co_name): + eval.Code.__init__(self, co_name) + self.co_argcount = 0 # #arguments, except *vararg and **kwarg + self.co_nlocals = 0 # #local variables + self.co_stacksize = 0 # #entries needed for evaluation stack + self.co_flags = 0 # CO_..., see above + self.co_code = None # string: instruction opcodes + self.co_consts = () # tuple: constants used + self.co_names = () # tuple of strings: names (for attrs,...) + self.co_varnames = () # tuple of strings: local variable names + self.co_freevars = () # tuple of strings: free variable names + self.co_cellvars = () # tuple of strings: cell variable names + # The rest doesn't count for hash/cmp + self.co_filename = "" # string: where it was loaded from + #self.co_name (in base class)# string: name, for reference + self.co_firstlineno = 0 # first source line number + self.co_lnotab = "" # string: encoding addr<->lineno mapping + + def _from_code(self, code): + """ Initialize the code object from a real (CPython) one. + This is just a hack, until we have our own compile. + At the moment, we just fake this. + This method is called by our compile builtin function. + """ + import types + assert isinstance(code, types.CodeType) + # simply try to suck in all attributes we know of + for name in self.__dict__.keys(): + value = getattr(code, name) + setattr(self, name, value) + newconsts = () + for const in code.co_consts: + if isinstance(const, types.CodeType): + newc = PyCode(const.co_name) + newc._from_code(const) + newconsts = newconsts + (newc,) + else: + newconsts = newconsts + (const,) + self.co_consts = newconsts + + def create_frame(self, space): + "Create an empty PyFrame suitable for this code object." + # select the appropriate kind of frame; see below + if self.co_cellvars or self.co_freevars: + frameclass = PyNestedScopeFrame + elif self.co_nlocals: + frameclass = PyFastScopeFrame + else: + frameclass = PyOperationalFrame + return frameclass(space, self) + + def signature(self): + "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." + argcount = self.co_argcount + argnames = self.co_varnames[:argcount] + if self.co_flags & CO_VARARGS: + varargname = self.co_varnames[argcount] + argcount += 1 + else: + varargname = None + if self.co_flags & CO_VARKEYWORDS: + kwargname = self.co_varnames[argcount] + argcount += 1 + else: + kwargname = None + return argnames, varargname, kwargname + + def getargcount(self): + count = self.co_argcount + if self.co_flags & CO_VARARGS: + count += 1 + if self.co_flags & CO_VARKEYWORDS: + count += 1 + return count + + def getlocalvarname(self, index): + # nb. this is duplicated in PyFastScopeFrame.getlocalvarname() + return self.co_varnames[index] + + def is_generator(self): + return self.co_flags & CO_GENERATOR Added: pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py ============================================================================== --- (empty file) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py Sat Aug 9 15:48:30 2003 @@ -0,0 +1,91 @@ +from pypy.interpreter.pyopcode import PyOperationalFrame + + +UNDEFINED = object() # marker for undefined local variables + + +class PyFastScopeFrame(PyOperationalFrame): + "A PyFrame that knows about fast scopes." + + # this is the class that knows about "fast locals", i.e. + # the fact that local variables are better represented as an array + # of values accessed by index (by the LOAD_FAST, STORE_FAST and + # DELETE_FAST opcodes). + + def __init__(self, space, code): + PyOperationalFrame.__init__(self, space, code) + self.locals_w = [UNDEFINED] * code.co_nlocals + + def getlocalvarname(self, index): + return self.bytecode.co_varnames[index] + + def getlocaldict(self): + self.fast2locals() + return self.w_locals + + def setlocaldict(self, w_locals): + self.w_locals = w_locals + self.locals2fast() + + def getlocalvar(self, index): + return self.locals_w[index] + + def setlocalvar(self, index, w_value): + self.locals_w[index] = w_value + + def fast2locals(self): + # Copy values from self.locals_w to self.w_locals + if self.w_locals is None: + self.w_locals = self.space.newdict([]) + for name, w_value in zip(self.bytecode.co_varnames, self.locals_w): + if w_value is not UNDEFINED: + w_name = self.space.wrap(name) + self.space.setitem(self.w_locals, w_name, w_value) + + def locals2fast(self): + # Copy values from self.w_locals to self.locals_w + for i in range(self.bytecode.co_nlocals): + w_name = self.space.wrap(self.bytecode.co_varnames[i]) + try: + w_value = self.space.getitem(self.w_locals, w_name) + except OperationError, e: + if not e.match(self.space, self.space.w_KeyError): + raise + else: + self.locals_w[i] = w_value + + ### extra opcodes ### + + def LOAD_FAST(f, varindex): + # access a local variable directly + w_value = f.locals_w[varindex] + if w_value is UNDEFINED: + varname = f.getlocalvarname(varindex) + message = "local variable '%s' referenced before assignment" % varname + raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) + f.valuestack.push(w_value) + + def STORE_FAST(f, varindex): + try: + w_newvalue = f.valuestack.pop() + f.locals_w[varindex] = w_newvalue + except: + print "exception: got index error" + print " varindex:", varindex + print " len(locals_w)", len(f.locals_w) + import dis + print dis.dis(f.bytecode) + print "co_varnames", f.bytecode.co_varnames + print "co_nlocals", f.bytecode.co_nlocals + raise + + def DELETE_FAST(f, varindex): + w_value = f.locals_w[varindex] + if f.locals_w[varindex] is UNDEFINED: + varname = f.getlocalvarname(varindex) + message = "local variable '%s' referenced before assignment" % varname + raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) + f.locals_w[varindex] = UNDEFINED + + +PyFastScopeFrame.setup_dispatch_table() Modified: pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py Sat Aug 9 15:48:30 2003 @@ -1,61 +1,53 @@ """ PyFrame class implementation with the interpreter main loop. """ -from pypy.interpreter.executioncontext import OperationError, Stack, NoValue -from pypy.interpreter.gateway import app2interp, Cell, _NULL +from pypy.interpreter.executioncontext import Stack +from pypy.interpreter.error import OperationError +from pypy.interpreter.gateway import app2interp +from pypy.interpreter import eval, baseobjspace -class PyFrame: +class PyFrame(eval.Frame): """Represents a frame for a regular Python function that needs to be interpreted. + See also pyopcode.PyStandardFrame and pynestedscope.PyNestedScopeFrame. + Public fields: * 'space' is the object space this frame is running in + * 'bytecode' is the PyCode object this frame runs * 'w_locals' is the locals dictionary to use * 'w_globals' is the attached globals dictionary * 'w_builtins' is the attached built-ins dictionary * 'valuestack', 'blockstack', 'next_instr' control the interpretation """ - def initialize(self, scopedcode): - self.space = scopedcode.space - self.bytecode = scopedcode.cpycode - self.w_globals = scopedcode.w_globals - self.closure_w = scopedcode.closure_w or () + def __init__(self, space, code): + eval.Frame.__init__(self, space, code) + self.bytecode = code # Misnomer; this is really like a code object self.valuestack = Stack() self.blockstack = Stack() self.last_exception = None self.next_instr = 0 self.w_builtins = self.space.w_builtins - def setdictscope(self, w_locals): - self.w_locals = w_locals - self.locals_w = [_NULL] * self.bytecode.co_nlocals - self.locals2fast() - - def setfastscope(self, locals_w): - self.locals_w = locals_w - self.w_locals = self.space.newdict([]) - self.fast2locals() - - def XXXclone(self): - f = self.__class__() - f.space = self.space - f.bytecode = self.bytecode - f.w_globals = self.w_globals - f.w_locals = self.w_locals - f.w_builtins = self.w_builtins - f.valuestack = self.valuestack.clone() - f.blockstack = self.blockstack.clone() - f.localcells = [x.clone() for x in self.localcells] - f.nestedcells = [x.clone() for x in self.nestedcells] - f.last_exception = self.last_exception - f.next_instr = self.next_instr - return f +## def XXXclone(self): +## f = self.__class__() +## f.space = self.space +## f.bytecode = self.bytecode +## f.w_globals = self.w_globals +## f.w_locals = self.w_locals +## f.w_builtins = self.w_builtins +## f.valuestack = self.valuestack.clone() +## f.blockstack = self.blockstack.clone() +## f.localcells = [x.clone() for x in self.localcells] +## f.nestedcells = [x.clone() for x in self.nestedcells] +## f.last_exception = self.last_exception +## f.next_instr = self.next_instr +## return f def eval(self, executioncontext): "Interpreter main loop!" - from pypy.interpreter import opcode try: while True: try: @@ -63,12 +55,8 @@ last_instr = self.next_instr try: # fetch and dispatch the next opcode - op = self.nextop() - if opcode.has_arg(op): - oparg = self.nextarg() - opcode.dispatch_arg(self, op, oparg) - else: - opcode.dispatch_noarg(self, op) + # dispatch() is abstract, see pyopcode. + self.dispatch() except OperationError, e: #import traceback #traceback.print_exc() @@ -93,7 +81,20 @@ w_exitvalue = e.args[0] return w_exitvalue - ### accessor functions ### + ### opcode dispatch ### + + # 'dispatch_table' is a class attribute: a list of functions. + # Currently, it is always created by setup_dispatch_table in pyopcode.py + # but it could be a custom table. + + def dispatch(self): + opcode = self.nextop() + fn = self.dispatch_table[opcode] + if fn.has_arg: + oparg = self.nextarg() + fn(self, oparg) + else: + fn(self) def nextop(self): c = self.bytecode.co_code[self.next_instr] @@ -105,45 +106,6 @@ hi = self.nextop() return (hi<<8) + lo - def getconstant(self, index): - return self.bytecode.co_consts[index] - - def getlocalvarname(self, index): - return self.bytecode.co_varnames[index] - - def getname(self, index): - return self.bytecode.co_names[index] - - def getfreevarname(self, index): - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars - return freevarnames[index] - - def iscellvar(self, index): - # is the variable given by index a cell or a free var? - return index < len(self.bytecode.co_cellvars) - - def fast2locals(self): - # Copy values from self.localcells to self.w_locals - for name, w_value in zip(self.bytecode.co_varnames, self.locals_w): - w_name = self.space.wrap(name) - if w_value is not _NULL: - self.space.setitem(self.w_locals, w_name, w_value) - - def locals2fast(self): - # Copy values from self.w_locals to self.localcells - for i in range(self.bytecode.co_nlocals): - w_name = self.space.wrap(self.bytecode.co_varnames[i]) - try: - w_value = self.space.getitem(self.w_locals, w_name) - except OperationError, e: - if not e.match(self.space, self.space.w_KeyError): - raise - else: - self.locals_w[i] = w_value - - ### frame initialization ### - - ### exception stack ### def clean_exceptionstack(self): @@ -240,7 +202,7 @@ evalue = (evalue,) evalue = etype(*evalue) else: - raise Exception, "?!" + raise Exception, "?!" # XXX return etype, evalue normalize_exception = app2interp(app_normalize_exception) @@ -324,8 +286,8 @@ """Signals a 'return' statement. Argument is the wrapped object to return.""" def emptystack(self, frame): - if frame.bytecode.co_flags & 0x0020:#CO_GENERATOR: - raise NoValue + if frame.bytecode.is_generator(): + raise baseobjspace.NoValue w_returnvalue = self.args[0] raise ExitFrame(w_returnvalue) Added: pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py ============================================================================== --- (empty file) +++ pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py Sat Aug 9 15:48:30 2003 @@ -0,0 +1,173 @@ +from pypy.interpreter.pyfastscope import PyFastScopeFrame, UNDEFINED + + +class Cell(object): + "A simple container for a wrapped value." + + def __init__(self, w_value=UNDEFINED): + self.w_value = w_value + + def clone(self): + return self.__class__(self.w_value) + + def empty(self): + return self.w_value is UNDEFINED + + def get(self): + if self.w_value is UNDEFINED: + raise ValueError, "get() from an empty cell" + return self.w_value + + def set(self, w_value): + self.w_value = w_value + + def delete(self): + if self.w_value is UNDEFINED: + raise ValueError, "delete() on an empty cell" + self.w_value = UNDEFINED + + def __repr__(self): + """ representation for debugging purposes """ + if self.w_value is UNDEFINED: + content = "" + else: + content = repr(self.w_value) + return "<%s(%s) at 0x%x>" % (self.__class__.__name__, + content, id(self)) + + +class PyNestedScopeFrame(PyFastScopeFrame): + """This class enhances a standard frame with nested scope abilities, + i.e. handling of cell/free variables.""" + + def __init__(self, space, code): + PyFastScopeFrame.__init__(self, space, code) + ncellvars = len(code.co_cellvars) + nfreevars = len(code.co_freevars) + self.cells = [None] * (ncellvars + nfreevars) + + def fast2locals(self): + PyFastScopeFrame.fast2locals(self) + freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars + for name, cell in zip(freevarnames, self.cells): + try: + w_value = cell.get() + except ValueError: + pass + else: + w_name = self.space.wrap(name) + self.space.setitem(self.w_locals, w_name, w_value) + + def locals2fast(self): + PyFastScopeFrame.locals2fast(self) + freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars + for name, cell in zip(freevarnames, self.cells): + w_name = self.space.wrap(name) + try: + w_value = self.space.getitem(self.w_locals, w_name) + except OperationError, e: + if not e.match(self.space, self.space.w_KeyError): + raise + else: + cell.set(w_value) + + def setclosure(self, closure): + # Cell Vars: + # my local variables that are exposed to my inner functions + # Free Vars: + # variables coming from a parent function in which i'm nested + # 'closure' is a list of Cell instances: the received free vars. + code = self.bytecode + ncellvars = len(code.co_cellvars) + nfreevars = len(code.co_freevars) + if ncellvars: + # the first few cell vars could shadow already-set arguments, + # in the same order as they appear in co_varnames + nargvars = code.getargcount() + argvars = code.co_varnames + cellvars = code.co_cellvars + next = 0 + nextname = cellvars[0] + for i in range(nargvars): + if argvars[i] == nextname: + # argument i has the same name as the next cell var + w_value = self.locals_w[i] + self.cells[next] = Cell(w_value) + next += 1 + try: + nextname = cellvars[next] + except IndexError: + break # all cell vars initialized this way + else: + # the remaining cell vars are empty + for i in range(next, ncellvars): + self.cells[i] = Cell() + # following the cell vars are the free vars, copied from 'closure' + if closure is None: + closure = [] + if len(closure) != nfreevars: + raise TypeError, ("%d free variables expected, got %d" % + (nfreevars, len(closure))) # internal error + self.cells[ncellvars:] = closure + + def getfreevarname(self, index): + freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars + return freevarnames[index] + + def iscellvar(self, index): + # is the variable given by index a cell or a free var? + return index < len(self.bytecode.co_cellvars) + + ### extra opcodes ### + + def LOAD_CLOSURE(f, varindex): + # nested scopes: access the cell object + cell = f.cells[varindex] + assert cell is not None, "setclosure() was not called" + w_value = f.space.wrap(cell) + f.valuestack.push(w_value) + + def LOAD_DEREF(f, varindex): + # nested scopes: access a variable through its cell object + cell = f.cells[varindex] + try: + w_value = cell.get() + except ValueError: + varname = f.getfreevarname(varindex) + if f.iscellvar(varindex): + message = "local variable '%s' referenced before assignment" + w_exc_type = f.space.w_UnboundLocalError + else: + message = ("free variable '%s' referenced before assignment" + " in enclosing scope") + w_exc_type = f.space.w_NameError + raise OperationError(w_exc_type, f.space.wrap(message % varname)) + else: + f.valuestack.push(w_value) + + def STORE_DEREF(f, varindex): + # nested scopes: access a variable through its cell object + w_newvalue = f.valuestack.pop() + #try: + cell = f.cells[varindex] + #except IndexError: + # import pdb; pdb.set_trace() + # raise + cell.set(w_newvalue) + + def MAKE_CLOSURE(f, numdefaults): + w_codeobj = f.valuestack.pop() + codeobj = f.space.unwrap(w_codeobj) + nfreevars = len(codeobj.co_freevars) + freevars = [f.valuestack.pop() for i in range(nfreevars)] + freevars.reverse() + w_freevars = f.space.newtuple(freevars) + defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] + defaultarguments.reverse() + w_defaultarguments = f.space.newtuple(defaultarguments) + w_func = f.space.newfunction(f.space.unwrap(w_codeobj), + f.w_globals, w_defaultarguments, w_freevars) + f.valuestack.push(w_func) + + +PyNestedScopeFrame.setup_dispatch_table() Copied: pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py (from rev 1262, pypy/branch/builtinrefactor/pypy/interpreter/opcode.py) ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/opcode.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py Sat Aug 9 15:48:30 2003 @@ -1,13 +1,13 @@ -from appfile import AppFile +""" +Implementation of a part of the standard Python opcodes. +The rest, dealing with variables in optimized ways, is in +pyfastscope.py and pynestedscope.py. +""" + from pypy.interpreter.baseobjspace import OperationError, NoValue -from pypy.interpreter.pyframe import _NULL import dis -from pypy.interpreter import pyframe, baseobjspace -from pypy.interpreter.gateway import InterpretedFunction, ScopedCode - - -# dynamically loaded application-space utilities -appfile = AppFile(__name__, ["interpreter"]) +from pypy.interpreter import baseobjspace +from pypy.interpreter.pyframe import PyFrame class unaryoperation: @@ -30,729 +30,648 @@ f.valuestack.push(w_result) -################################################################ -## Implementation of the opcodes -## - -def LOAD_FAST(f, varindex): - # access a local variable through its cell object - w_value = f.locals_w[varindex] - if w_value is _NULL: - varname = f.getlocalvarname(varindex) - message = "local variable '%s' referenced before assignment" % varname - raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) - f.valuestack.push(w_value) - -def LOAD_CONST(f, constindex): - w_const = f.space.wrap(f.getconstant(constindex)) - f.valuestack.push(w_const) +class PyOperationalFrame(PyFrame): + """A PyFrame that knows about all operational Python opcodes. + It does not know about 'fast variables' nor 'nested scopes'.""" + + ### accessor functions ### + + def getconstant(self, index): + return self.bytecode.co_consts[index] + + def getname(self, index): + return self.bytecode.co_names[index] + + ################################################################ + ## Implementation of the "operational" opcodes + ## See also pyfastscope.py and pynestedscope.py for the rest. + ## + + # the 'self' argument of opcode implementations is called 'f' + # for historical reasons -def STORE_FAST(f, varindex): - try: - w_newvalue = f.valuestack.pop() - f.locals_w[varindex] = w_newvalue - except: - print "exception: got index error" - print " varindex:", varindex - print " len(locals_w)", len(f.locals_w) - import dis - print dis.dis(f.bytecode) - print "co_varnames", f.bytecode.co_varnames - print "co_nlocals", f.bytecode.co_nlocals - raise - - -def POP_TOP(f): - f.valuestack.pop() - -def ROT_TWO(f): - w_1 = f.valuestack.pop() - w_2 = f.valuestack.pop() - f.valuestack.push(w_1) - f.valuestack.push(w_2) - -def ROT_THREE(f): - w_1 = f.valuestack.pop() - w_2 = f.valuestack.pop() - w_3 = f.valuestack.pop() - f.valuestack.push(w_1) - f.valuestack.push(w_3) - f.valuestack.push(w_2) - -def ROT_FOUR(f): - w_1 = f.valuestack.pop() - w_2 = f.valuestack.pop() - w_3 = f.valuestack.pop() - w_4 = f.valuestack.pop() - f.valuestack.push(w_1) - f.valuestack.push(w_4) - f.valuestack.push(w_3) - f.valuestack.push(w_2) - -def DUP_TOP(f): - w_1 = f.valuestack.top() - f.valuestack.push(w_1) - -def DUP_TOPX(f, itemcount): - assert 1 <= itemcount <= 5, "limitation of the current interpreter" - for i in range(itemcount): - w_1 = f.valuestack.top(itemcount-1) + def LOAD_CONST(f, constindex): + w_const = f.space.wrap(f.getconstant(constindex)) + f.valuestack.push(w_const) + + def POP_TOP(f): + f.valuestack.pop() + + def ROT_TWO(f): + w_1 = f.valuestack.pop() + w_2 = f.valuestack.pop() f.valuestack.push(w_1) + f.valuestack.push(w_2) -UNARY_POSITIVE = unaryoperation("pos") -UNARY_NEGATIVE = unaryoperation("neg") -UNARY_NOT = unaryoperation("not_") -UNARY_CONVERT = unaryoperation("repr") -UNARY_INVERT = unaryoperation("invert") - -def BINARY_POWER(f): - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - w_result = f.space.pow(w_1, w_2, f.space.w_None) - f.valuestack.push(w_result) - -BINARY_MULTIPLY = binaryoperation("mul") -BINARY_TRUE_DIVIDE = binaryoperation("truediv") -BINARY_FLOOR_DIVIDE = binaryoperation("floordiv") -BINARY_DIVIDE = binaryoperation("div") -BINARY_MODULO = binaryoperation("mod") -BINARY_ADD = binaryoperation("add") -BINARY_SUBTRACT = binaryoperation("sub") -BINARY_SUBSCR = binaryoperation("getitem") -BINARY_LSHIFT = binaryoperation("lshift") -BINARY_RSHIFT = binaryoperation("rshift") -BINARY_AND = binaryoperation("and_") -BINARY_XOR = binaryoperation("xor") -BINARY_OR = binaryoperation("or_") - -def INPLACE_POWER(f): - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - w_result = f.space.inplace_pow(w_1, w_2, f.space.w_None) - f.valuestack.push(w_result) - -INPLACE_MULTIPLY = binaryoperation("inplace_mul") -INPLACE_TRUE_DIVIDE = binaryoperation("inplace_truediv") -INPLACE_FLOOR_DIVIDE = binaryoperation("inplace_floordiv") -INPLACE_DIVIDE = binaryoperation("inplace_div") -INPLACE_MODULO = binaryoperation("inplace_mod") -INPLACE_ADD = binaryoperation("inplace_add") -INPLACE_SUBTRACT = binaryoperation("inplace_sub") -INPLACE_LSHIFT = binaryoperation("inplace_lshift") -INPLACE_RSHIFT = binaryoperation("inplace_rshift") -INPLACE_AND = binaryoperation("inplace_and") -INPLACE_XOR = binaryoperation("inplace_xor") -INPLACE_OR = binaryoperation("inplace_or") - -def slice(f, w_start, w_end): - w_slice = f.space.newslice(w_start, w_end, None) - w_obj = f.valuestack.pop() - w_result = f.space.getitem(w_obj, w_slice) - f.valuestack.push(w_result) - -def SLICE_0(f): - slice(f, None, None) - -def SLICE_1(f): - w_start = f.valuestack.pop() - slice(f, w_start, None) - -def SLICE_2(f): - w_end = f.valuestack.pop() - slice(f, None, w_end) - -def SLICE_3(f): - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - slice(f, w_start, w_end) - -def storeslice(f, w_start, w_end): - w_slice = f.space.newslice(w_start, w_end, None) - w_obj = f.valuestack.pop() - w_newvalue = f.valuestack.pop() - f.space.setitem(w_obj, w_slice, w_newvalue) - -def STORE_SLICE_0(f): - storeslice(f, None, None) - -def STORE_SLICE_1(f): - w_start = f.valuestack.pop() - storeslice(f, w_start, None) - -def STORE_SLICE_2(f): - w_end = f.valuestack.pop() - storeslice(f, None, w_end) - -def STORE_SLICE_3(f): - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - storeslice(f, w_start, w_end) - -def deleteslice(f, w_start, w_end): - w_slice = f.space.newslice(w_start, w_end, None) - w_obj = f.valuestack.pop() - f.space.delitem(w_obj, w_slice) - -def DELETE_SLICE_0(f): - deleteslice(f, None, None) - -def DELETE_SLICE_1(f): - w_start = f.valuestack.pop() - deleteslice(f, w_start, None) - -def DELETE_SLICE_2(f): - w_end = f.valuestack.pop() - deleteslice(f, None, w_end) - -def DELETE_SLICE_3(f): - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - deleteslice(f, w_start, w_end) - -def STORE_SUBSCR(f): - "obj[subscr] = newvalue" - w_subscr = f.valuestack.pop() - w_obj = f.valuestack.pop() - w_newvalue = f.valuestack.pop() - f.space.setitem(w_obj, w_subscr, w_newvalue) - -def DELETE_SUBSCR(f): - "del obj[subscr]" - w_subscr = f.valuestack.pop() - w_obj = f.valuestack.pop() - f.space.delitem(w_obj, w_subscr) - -def PRINT_EXPR(f): - w_expr = f.valuestack.pop() - #print f.space.unwrap(w_expr) - f.space.gethelper(appfile).call("print_expr", [w_expr]) - -def PRINT_ITEM_TO(f): - w_stream = f.valuestack.pop() - w_item = f.valuestack.pop() - f.space.gethelper(appfile).call("print_item_to", [w_item, w_stream]) - -def PRINT_ITEM(f): - w_item = f.valuestack.pop() - f.space.gethelper(appfile).call("print_item", [w_item]) - -def PRINT_NEWLINE_TO(f): - w_stream = f.valuestack.pop() - f.space.gethelper(appfile).call("print_newline_to", [w_stream]) - -def PRINT_NEWLINE(f): - f.space.gethelper(appfile).call("print_newline", []) - -def BREAK_LOOP(f): - raise pyframe.SBreakLoop - -def CONTINUE_LOOP(f, startofloop): - raise pyframe.SContinueLoop(startofloop) - -def RAISE_VARARGS(f, nbargs): - # we use the .app.py file to prepare the exception/value/traceback - # but not to actually raise it, because we cannot use the 'raise' - # statement to implement RAISE_VARARGS - if nbargs == 0: - w_resulttuple = f.space.gethelper(appfile).call("prepare_raise0", []) - elif nbargs == 1: - w_type = f.valuestack.pop() - w_resulttuple = f.space.gethelper(appfile).call( - "prepare_raise", [w_type, f.space.w_None, f.space.w_None]) - elif nbargs == 2: - w_value = f.valuestack.pop() - w_type = f.valuestack.pop() - w_resulttuple = f.space.gethelper(appfile).call( - "prepare_raise", [w_type, w_value, f.space.w_None]) - elif nbargs == 3: - w_traceback = f.valuestack.pop() - w_value = f.valuestack.pop() - w_type = f.valuestack.pop() - w_resulttuple = f.space.gethelper(appfile).call( - "prepare_raise", [w_type, w_value, w_traceback]) - else: - raise pyframe.BytecodeCorruption, "bad RAISE_VARARGS oparg" - w_type, w_value, w_traceback = f.space.unpacktuple(w_resulttuple) - # XXX the three-arguments 'raise' is not supported yet - raise OperationError(w_type, w_value) - -def LOAD_LOCALS(f): - f.valuestack.push(f.w_locals) - -def RETURN_VALUE(f): - w_returnvalue = f.valuestack.pop() - raise pyframe.SReturnValue(w_returnvalue) - -def YIELD_VALUE(f): - w_yieldedvalue = f.valuestack.pop() - raise pyframe.SYieldValue(w_yieldedvalue) -YIELD_STMT = YIELD_VALUE # misnamed in dis.opname - -def EXEC_STMT(f): - w_locals = f.valuestack.pop() - w_globals = f.valuestack.pop() - w_prog = f.valuestack.pop() - w_tuple = f.space.gethelper(appfile).call("exec_statement", - [w_prog, w_globals, w_locals, - f.w_builtins, f.w_globals, f.w_locals]) - w_prog = f.space.getitem(w_tuple,f.space.wrap(0)) - w_globals = f.space.getitem(w_tuple,f.space.wrap(1)) - w_locals = f.space.getitem(w_tuple,f.space.wrap(2)) - - plain = (w_locals is f.w_locals) - if plain: - f.fast2locals() - scopedcode = ScopedCode(f.space, f.space.unwrap(w_prog), w_globals) - scopedcode.eval_frame(w_locals) - #f.space.unwrap(w_prog).eval_code(f.space, w_globals, w_locals) - if plain: - f.locals2fast() - -def POP_BLOCK(f): - block = f.blockstack.pop() - block.cleanup(f) # the block knows how to clean up the value stack - -def END_FINALLY(f): - # unlike CPython, when we reach this opcode the value stack has - # always been set up as follows (topmost first): - # [exception type or None] - # [exception value or None] - # [wrapped stack unroller ] - f.valuestack.pop() # ignore the exception type - f.valuestack.pop() # ignore the exception value - unroller = f.space.unwrap(f.valuestack.pop()) - if unroller is not None: - raise unroller # re-raise the unroller, if any - -def BUILD_CLASS(f): - w_methodsdict = f.valuestack.pop() - w_bases = f.valuestack.pop() - w_name = f.valuestack.pop() - # XXX it would be best to have all opcodes on a class that has a 'space' attribute - # then the following initialization could be done at init-time. - build_class = InterpretedFunction(f.space.gethelperspace(), app_build_class) - w_newclass = build_class(w_name, w_bases, w_methodsdict, f.w_globals) - f.valuestack.push(w_newclass) - -def app_build_class(name, bases, namespace, globals): - if '__metaclass__' in namespace: - metaclass = namespace['__metaclass__'] - elif len(bases) > 0: - base = bases[0] - if hasattr(base, '__class__'): - metaclass = base.__class__ + def ROT_THREE(f): + w_1 = f.valuestack.pop() + w_2 = f.valuestack.pop() + w_3 = f.valuestack.pop() + f.valuestack.push(w_1) + f.valuestack.push(w_3) + f.valuestack.push(w_2) + + def ROT_FOUR(f): + w_1 = f.valuestack.pop() + w_2 = f.valuestack.pop() + w_3 = f.valuestack.pop() + w_4 = f.valuestack.pop() + f.valuestack.push(w_1) + f.valuestack.push(w_4) + f.valuestack.push(w_3) + f.valuestack.push(w_2) + + def DUP_TOP(f): + w_1 = f.valuestack.top() + f.valuestack.push(w_1) + + def DUP_TOPX(f, itemcount): + assert 1 <= itemcount <= 5, "limitation of the current interpreter" + for i in range(itemcount): + w_1 = f.valuestack.top(itemcount-1) + f.valuestack.push(w_1) + + UNARY_POSITIVE = unaryoperation("pos") + UNARY_NEGATIVE = unaryoperation("neg") + UNARY_NOT = unaryoperation("not_") + UNARY_CONVERT = unaryoperation("repr") + UNARY_INVERT = unaryoperation("invert") + + def BINARY_POWER(f): + w_2 = f.valuestack.pop() + w_1 = f.valuestack.pop() + w_result = f.space.pow(w_1, w_2, f.space.w_None) + f.valuestack.push(w_result) + + BINARY_MULTIPLY = binaryoperation("mul") + BINARY_TRUE_DIVIDE = binaryoperation("truediv") + BINARY_FLOOR_DIVIDE = binaryoperation("floordiv") + BINARY_DIVIDE = binaryoperation("div") + BINARY_MODULO = binaryoperation("mod") + BINARY_ADD = binaryoperation("add") + BINARY_SUBTRACT = binaryoperation("sub") + BINARY_SUBSCR = binaryoperation("getitem") + BINARY_LSHIFT = binaryoperation("lshift") + BINARY_RSHIFT = binaryoperation("rshift") + BINARY_AND = binaryoperation("and_") + BINARY_XOR = binaryoperation("xor") + BINARY_OR = binaryoperation("or_") + + def INPLACE_POWER(f): + w_2 = f.valuestack.pop() + w_1 = f.valuestack.pop() + w_result = f.space.inplace_pow(w_1, w_2, f.space.w_None) + f.valuestack.push(w_result) + + INPLACE_MULTIPLY = binaryoperation("inplace_mul") + INPLACE_TRUE_DIVIDE = binaryoperation("inplace_truediv") + INPLACE_FLOOR_DIVIDE = binaryoperation("inplace_floordiv") + INPLACE_DIVIDE = binaryoperation("inplace_div") + INPLACE_MODULO = binaryoperation("inplace_mod") + INPLACE_ADD = binaryoperation("inplace_add") + INPLACE_SUBTRACT = binaryoperation("inplace_sub") + INPLACE_LSHIFT = binaryoperation("inplace_lshift") + INPLACE_RSHIFT = binaryoperation("inplace_rshift") + INPLACE_AND = binaryoperation("inplace_and") + INPLACE_XOR = binaryoperation("inplace_xor") + INPLACE_OR = binaryoperation("inplace_or") + + def slice(f, w_start, w_end): + w_slice = f.space.newslice(w_start, w_end, None) + w_obj = f.valuestack.pop() + w_result = f.space.getitem(w_obj, w_slice) + f.valuestack.push(w_result) + + def SLICE_0(f): + slice(f, None, None) + + def SLICE_1(f): + w_start = f.valuestack.pop() + slice(f, w_start, None) + + def SLICE_2(f): + w_end = f.valuestack.pop() + slice(f, None, w_end) + + def SLICE_3(f): + w_end = f.valuestack.pop() + w_start = f.valuestack.pop() + slice(f, w_start, w_end) + + def storeslice(f, w_start, w_end): + w_slice = f.space.newslice(w_start, w_end, None) + w_obj = f.valuestack.pop() + w_newvalue = f.valuestack.pop() + f.space.setitem(w_obj, w_slice, w_newvalue) + + def STORE_SLICE_0(f): + storeslice(f, None, None) + + def STORE_SLICE_1(f): + w_start = f.valuestack.pop() + storeslice(f, w_start, None) + + def STORE_SLICE_2(f): + w_end = f.valuestack.pop() + storeslice(f, None, w_end) + + def STORE_SLICE_3(f): + w_end = f.valuestack.pop() + w_start = f.valuestack.pop() + storeslice(f, w_start, w_end) + + def deleteslice(f, w_start, w_end): + w_slice = f.space.newslice(w_start, w_end, None) + w_obj = f.valuestack.pop() + f.space.delitem(w_obj, w_slice) + + def DELETE_SLICE_0(f): + deleteslice(f, None, None) + + def DELETE_SLICE_1(f): + w_start = f.valuestack.pop() + deleteslice(f, w_start, None) + + def DELETE_SLICE_2(f): + w_end = f.valuestack.pop() + deleteslice(f, None, w_end) + + def DELETE_SLICE_3(f): + w_end = f.valuestack.pop() + w_start = f.valuestack.pop() + deleteslice(f, w_start, w_end) + + def STORE_SUBSCR(f): + "obj[subscr] = newvalue" + w_subscr = f.valuestack.pop() + w_obj = f.valuestack.pop() + w_newvalue = f.valuestack.pop() + f.space.setitem(w_obj, w_subscr, w_newvalue) + + def DELETE_SUBSCR(f): + "del obj[subscr]" + w_subscr = f.valuestack.pop() + w_obj = f.valuestack.pop() + f.space.delitem(w_obj, w_subscr) + + def PRINT_EXPR(f): + w_expr = f.valuestack.pop() + #print f.space.unwrap(w_expr) + f.space.gethelper(appfile).call("print_expr", [w_expr]) + + def PRINT_ITEM_TO(f): + w_stream = f.valuestack.pop() + w_item = f.valuestack.pop() + f.space.gethelper(appfile).call("print_item_to", [w_item, w_stream]) + + def PRINT_ITEM(f): + w_item = f.valuestack.pop() + f.space.gethelper(appfile).call("print_item", [w_item]) + + def PRINT_NEWLINE_TO(f): + w_stream = f.valuestack.pop() + f.space.gethelper(appfile).call("print_newline_to", [w_stream]) + + def PRINT_NEWLINE(f): + f.space.gethelper(appfile).call("print_newline", []) + + def BREAK_LOOP(f): + raise pyframe.SBreakLoop + + def CONTINUE_LOOP(f, startofloop): + raise pyframe.SContinueLoop(startofloop) + + def RAISE_VARARGS(f, nbargs): + # we use the .app.py file to prepare the exception/value/traceback + # but not to actually raise it, because we cannot use the 'raise' + # statement to implement RAISE_VARARGS + if nbargs == 0: + w_resulttuple = f.space.gethelper(appfile).call("prepare_raise0", []) + elif nbargs == 1: + w_type = f.valuestack.pop() + w_resulttuple = f.space.gethelper(appfile).call( + "prepare_raise", [w_type, f.space.w_None, f.space.w_None]) + elif nbargs == 2: + w_value = f.valuestack.pop() + w_type = f.valuestack.pop() + w_resulttuple = f.space.gethelper(appfile).call( + "prepare_raise", [w_type, w_value, f.space.w_None]) + elif nbargs == 3: + w_traceback = f.valuestack.pop() + w_value = f.valuestack.pop() + w_type = f.valuestack.pop() + w_resulttuple = f.space.gethelper(appfile).call( + "prepare_raise", [w_type, w_value, w_traceback]) + else: + raise pyframe.BytecodeCorruption, "bad RAISE_VARARGS oparg" + w_type, w_value, w_traceback = f.space.unpacktuple(w_resulttuple) + # XXX the three-arguments 'raise' is not supported yet + raise OperationError(w_type, w_value) + + def LOAD_LOCALS(f): + f.valuestack.push(f.w_locals) + + def RETURN_VALUE(f): + w_returnvalue = f.valuestack.pop() + raise pyframe.SReturnValue(w_returnvalue) + + def YIELD_VALUE(f): + w_yieldedvalue = f.valuestack.pop() + raise pyframe.SYieldValue(w_yieldedvalue) + YIELD_STMT = YIELD_VALUE # misnamed in dis.opname + + def EXEC_STMT(f): + w_locals = f.valuestack.pop() + w_globals = f.valuestack.pop() + w_prog = f.valuestack.pop() + w_tuple = f.space.gethelper(appfile).call("exec_statement", + [w_prog, w_globals, w_locals, + f.w_builtins, f.w_globals, f.w_locals]) + w_prog = f.space.getitem(w_tuple,f.space.wrap(0)) + w_globals = f.space.getitem(w_tuple,f.space.wrap(1)) + w_locals = f.space.getitem(w_tuple,f.space.wrap(2)) + + #plain = ... ... w_locals = f.getlocaldict() XXX XXX + + scopedcode = ScopedCode(f.space, f.space.unwrap(w_prog), w_globals) + scopedcode.eval_frame(w_locals) + #f.space.unwrap(w_prog).eval_code(f.space, w_globals, w_locals) + if plain: + f.setlocaldict(w_locals) + + def POP_BLOCK(f): + block = f.blockstack.pop() + block.cleanup(f) # the block knows how to clean up the value stack + + def END_FINALLY(f): + # unlike CPython, when we reach this opcode the value stack has + # always been set up as follows (topmost first): + # [exception type or None] + # [exception value or None] + # [wrapped stack unroller ] + f.valuestack.pop() # ignore the exception type + f.valuestack.pop() # ignore the exception value + unroller = f.space.unwrap(f.valuestack.pop()) + if unroller is not None: + raise unroller # re-raise the unroller, if any + + def BUILD_CLASS(f): + w_methodsdict = f.valuestack.pop() + w_bases = f.valuestack.pop() + w_name = f.valuestack.pop() + # XXX it would be best to have all opcodes on a class that has a 'space' attribute + # then the following initialization could be done at init-time. + build_class = InterpretedFunction(f.space.gethelperspace(), app_build_class) + w_newclass = build_class(w_name, w_bases, w_methodsdict, f.w_globals) + f.valuestack.push(w_newclass) + + def app_build_class(name, bases, namespace, globals): + if '__metaclass__' in namespace: + metaclass = namespace['__metaclass__'] + elif len(bases) > 0: + base = bases[0] + if hasattr(base, '__class__'): + metaclass = base.__class__ + else: + metaclass = type(base) + elif '__metaclass__' in globals: + metaclass = globals['__metaclass__'] else: - metaclass = type(base) - elif '__metaclass__' in globals: - metaclass = globals['__metaclass__'] - else: - metaclass = type - return metaclass(name, bases, namespace) - -def STORE_NAME(f, varindex): - varname = f.getname(varindex) - w_varname = f.space.wrap(varname) - w_newvalue = f.valuestack.pop() - f.space.setitem(f.w_locals, w_varname, w_newvalue) - -def DELETE_NAME(f, varindex): - varname = f.getname(varindex) - w_varname = f.space.wrap(varname) - try: - f.space.delitem(f.w_locals, w_varname) - except OperationError, e: - # catch KeyErrors and turn them into NameErrors - if not e.match(f.space, f.space.w_KeyError): - raise - message = "name '%s' is not defined" % varname - raise OperationError(f.space.w_NameError, f.space.wrap(message)) - -def UNPACK_SEQUENCE(f, itemcount): - w_iterable = f.valuestack.pop() - try: - items = f.space.unpackiterable(w_iterable, itemcount) - except ValueError, e: - raise OperationError(f.space.w_ValueError, f.space.wrap(str(e))) - items.reverse() - for item in items: - f.valuestack.push(item) - -def STORE_ATTR(f, nameindex): - "obj.attributename = newvalue" - attributename = f.getname(nameindex) - w_attributename = f.space.wrap(attributename) - w_obj = f.valuestack.pop() - w_newvalue = f.valuestack.pop() - f.space.setattr(w_obj, w_attributename, w_newvalue) - -def DELETE_ATTR(f, nameindex): - "del obj.attributename" - attributename = f.getname(nameindex) - w_attributename = f.space.wrap(attributename) - w_obj = f.valuestack.pop() - f.space.delattr(w_obj, w_attributename) - -def STORE_GLOBAL(f, nameindex): - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - w_newvalue = f.valuestack.pop() - f.space.setitem(f.w_globals, w_varname, w_newvalue) - -def DELETE_GLOBAL(f, nameindex): - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - f.space.delitem(f.w_globals, w_varname) - -def LOAD_NAME(f, nameindex): - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - try: - w_value = f.space.getitem(f.w_locals, w_varname) - except OperationError, e: - if not e.match(f.space, f.space.w_KeyError): - raise + metaclass = type + return metaclass(name, bases, namespace) + + def STORE_NAME(f, varindex): + varname = f.getname(varindex) + w_varname = f.space.wrap(varname) + w_newvalue = f.valuestack.pop() + f.space.setitem(f.w_locals, w_varname, w_newvalue) + + def DELETE_NAME(f, varindex): + varname = f.getname(varindex) + w_varname = f.space.wrap(varname) + try: + f.space.delitem(f.w_locals, w_varname) + except OperationError, e: + # catch KeyErrors and turn them into NameErrors + if not e.match(f.space, f.space.w_KeyError): + raise + message = "name '%s' is not defined" % varname + raise OperationError(f.space.w_NameError, f.space.wrap(message)) + + def UNPACK_SEQUENCE(f, itemcount): + w_iterable = f.valuestack.pop() + try: + items = f.space.unpackiterable(w_iterable, itemcount) + except ValueError, e: + raise OperationError(f.space.w_ValueError, f.space.wrap(str(e))) + items.reverse() + for item in items: + f.valuestack.push(item) + + def STORE_ATTR(f, nameindex): + "obj.attributename = newvalue" + attributename = f.getname(nameindex) + w_attributename = f.space.wrap(attributename) + w_obj = f.valuestack.pop() + w_newvalue = f.valuestack.pop() + f.space.setattr(w_obj, w_attributename, w_newvalue) + + def DELETE_ATTR(f, nameindex): + "del obj.attributename" + attributename = f.getname(nameindex) + w_attributename = f.space.wrap(attributename) + w_obj = f.valuestack.pop() + f.space.delattr(w_obj, w_attributename) + + def STORE_GLOBAL(f, nameindex): + varname = f.getname(nameindex) + w_varname = f.space.wrap(varname) + w_newvalue = f.valuestack.pop() + f.space.setitem(f.w_globals, w_varname, w_newvalue) + + def DELETE_GLOBAL(f, nameindex): + varname = f.getname(nameindex) + w_varname = f.space.wrap(varname) + f.space.delitem(f.w_globals, w_varname) + + def LOAD_NAME(f, nameindex): + varname = f.getname(nameindex) + w_varname = f.space.wrap(varname) + try: + w_value = f.space.getitem(f.w_locals, w_varname) + except OperationError, e: + if not e.match(f.space, f.space.w_KeyError): + raise + try: + w_value = f.space.getitem(f.w_globals, w_varname) + except OperationError, e: + if not e.match(f.space, f.space.w_KeyError): + raise + try: + w_value = f.space.getitem(f.w_builtins, w_varname) + except OperationError, e: + if not e.match(f.space, f.space.w_KeyError): + raise + message = "global name '%s' is not defined" % varname + w_exc_type = f.space.w_NameError + w_exc_value = f.space.wrap(message) + raise OperationError(w_exc_type, w_exc_value) + f.valuestack.push(w_value) + # XXX the implementation can be pushed back into app-space as an + # when exception handling begins to behave itself. For now, it + # was getting on my nerves -- mwh + # w_value = f.space.gethelper(appfile).call( + # "load_name", [w_varname, f.w_locals, f.w_globals, f.w_builtins]) + # f.valuestack.push(w_value) + + def LOAD_GLOBAL(f, nameindex): + assert f.w_globals is not None + varname = f.getname(nameindex) + w_varname = f.space.wrap(varname) try: w_value = f.space.getitem(f.w_globals, w_varname) except OperationError, e: + # catch KeyErrors if not e.match(f.space, f.space.w_KeyError): raise + # we got a KeyError, now look in the built-ins try: w_value = f.space.getitem(f.w_builtins, w_varname) except OperationError, e: + # catch KeyErrors again if not e.match(f.space, f.space.w_KeyError): raise message = "global name '%s' is not defined" % varname w_exc_type = f.space.w_NameError w_exc_value = f.space.wrap(message) raise OperationError(w_exc_type, w_exc_value) - f.valuestack.push(w_value) - # XXX the implementation can be pushed back into app-space as an - # when exception handling begins to behave itself. For now, it - # was getting on my nerves -- mwh -# w_value = f.space.gethelper(appfile).call( -# "load_name", [w_varname, f.w_locals, f.w_globals, f.w_builtins]) -# f.valuestack.push(w_value) - -def LOAD_GLOBAL(f, nameindex): - assert f.w_globals is not None - varname = f.getname(nameindex) - w_varname = f.space.wrap(varname) - try: - w_value = f.space.getitem(f.w_globals, w_varname) - except OperationError, e: - # catch KeyErrors - if not e.match(f.space, f.space.w_KeyError): - raise - # we got a KeyError, now look in the built-ins + f.valuestack.push(w_value) + + def BUILD_TUPLE(f, itemcount): + items = [f.valuestack.pop() for i in range(itemcount)] + items.reverse() + w_tuple = f.space.newtuple(items) + f.valuestack.push(w_tuple) + + def BUILD_LIST(f, itemcount): + items = [f.valuestack.pop() for i in range(itemcount)] + items.reverse() + w_list = f.space.newlist(items) + f.valuestack.push(w_list) + + def BUILD_MAP(f, zero): + if zero != 0: + raise pyframe.BytecodeCorruption + w_dict = f.space.newdict([]) + f.valuestack.push(w_dict) + + def LOAD_ATTR(f, nameindex): + "obj.attributename" + attributename = f.getname(nameindex) + w_attributename = f.space.wrap(attributename) + w_obj = f.valuestack.pop() + w_value = f.space.getattr(w_obj, w_attributename) + f.valuestack.push(w_value) + + def cmp_lt(f, w_1, w_2): return f.space.lt(w_1, w_2) + def cmp_le(f, w_1, w_2): return f.space.le(w_1, w_2) + def cmp_eq(f, w_1, w_2): return f.space.eq(w_1, w_2) + def cmp_ne(f, w_1, w_2): return f.space.ne(w_1, w_2) + def cmp_gt(f, w_1, w_2): return f.space.gt(w_1, w_2) + def cmp_ge(f, w_1, w_2): return f.space.ge(w_1, w_2) + + def cmp_in(f, w_1, w_2): + return f.space.contains(w_2, w_1) + def cmp_not_in(f, w_1, w_2): + return f.space.not_(f.space.contains(w_2, w_1)) + def cmp_is(f, w_1, w_2): + return f.space.is_(w_1, w_2) + def cmp_is_not(f, w_1, w_2): + return f.space.not_(f.space.is_(w_1, w_2)) + def cmp_exc_match(f, w_1, w_2): + return f.space.newbool(f.space.exception_match(w_1, w_2)) + + compare_dispatch_table = { + 0: cmp_lt, # "<" + 1: cmp_le, # "<=" + 2: cmp_eq, # "==" + 3: cmp_ne, # "!=" + 4: cmp_gt, # ">" + 5: cmp_ge, # ">=" + 6: cmp_in, + 7: cmp_not_in, + 8: cmp_is, + 9: cmp_is_not, + 10: cmp_exc_match, + } + def COMPARE_OP(f, testnum): + w_2 = f.valuestack.pop() + w_1 = f.valuestack.pop() try: - w_value = f.space.getitem(f.w_builtins, w_varname) - except OperationError, e: - # catch KeyErrors again - if not e.match(f.space, f.space.w_KeyError): - raise - message = "global name '%s' is not defined" % varname - w_exc_type = f.space.w_NameError - w_exc_value = f.space.wrap(message) - raise OperationError(w_exc_type, w_exc_value) - f.valuestack.push(w_value) - -def DELETE_FAST(f, varindex): - w_value = f.locals_w[varindex] - if f.locals_w[varindex] is _NULL: - varname = f.getlocalvarname(varindex) - message = "local variable '%s' referenced before assignment" % varname - raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) - f.locals_w[varindex] = _NULL - -def LOAD_CLOSURE(f, varindex): - # nested scopes: access the cell object - cell = f.closure_w[varindex] - w_value = f.space.wrap(cell) - f.valuestack.push(w_value) - -def LOAD_DEREF(f, varindex): - # nested scopes: access a variable through its cell object - cell = f.closure_w[varindex] - try: - w_value = cell.get() - except ValueError: - varname = f.getfreevarname(varindex) - if f.iscellvar(varindex): - message = "local variable '%s' referenced before assignment" - w_exc_type = f.space.w_UnboundLocalError - else: - message = ("free variable '%s' referenced before assignment" - " in enclosing scope") - w_exc_type = f.space.w_NameError - raise OperationError(w_exc_type, f.space.wrap(message % varname)) - f.valuestack.push(w_value) - -def STORE_DEREF(f, varindex): - # nested scopes: access a variable through its cell object - w_newvalue = f.valuestack.pop() - try: - cell = f.closure_w[varindex] - except IndexError: - #import pdb; pdb.set_trace() - raise - cell.set(w_newvalue) - -def BUILD_TUPLE(f, itemcount): - items = [f.valuestack.pop() for i in range(itemcount)] - items.reverse() - w_tuple = f.space.newtuple(items) - f.valuestack.push(w_tuple) - -def BUILD_LIST(f, itemcount): - items = [f.valuestack.pop() for i in range(itemcount)] - items.reverse() - w_list = f.space.newlist(items) - f.valuestack.push(w_list) - -def BUILD_MAP(f, zero): - if zero != 0: - raise pyframe.BytecodeCorruption - w_dict = f.space.newdict([]) - f.valuestack.push(w_dict) - -def LOAD_ATTR(f, nameindex): - "obj.attributename" - attributename = f.getname(nameindex) - w_attributename = f.space.wrap(attributename) - w_obj = f.valuestack.pop() - w_value = f.space.getattr(w_obj, w_attributename) - f.valuestack.push(w_value) - -def cmp_lt(f, w_1, w_2): return f.space.lt(w_1, w_2) -def cmp_le(f, w_1, w_2): return f.space.le(w_1, w_2) -def cmp_eq(f, w_1, w_2): return f.space.eq(w_1, w_2) -def cmp_ne(f, w_1, w_2): return f.space.ne(w_1, w_2) -def cmp_gt(f, w_1, w_2): return f.space.gt(w_1, w_2) -def cmp_ge(f, w_1, w_2): return f.space.ge(w_1, w_2) - -def cmp_in(f, w_1, w_2): - return f.space.contains(w_2, w_1) -def cmp_not_in(f, w_1, w_2): - return f.space.not_(f.space.contains(w_2, w_1)) -def cmp_is(f, w_1, w_2): - return f.space.is_(w_1, w_2) -def cmp_is_not(f, w_1, w_2): - return f.space.not_(f.space.is_(w_1, w_2)) -def cmp_exc_match(f, w_1, w_2): - return f.space.exception_match(w_1, w_2) - -compare_dispatch_table = { - 0: cmp_lt, # "<" - 1: cmp_le, # "<=" - 2: cmp_eq, # "==" - 3: cmp_ne, # "!=" - 4: cmp_gt, # ">" - 5: cmp_ge, # ">=" - 6: cmp_in, - 7: cmp_not_in, - 8: cmp_is, - 9: cmp_is_not, - 10: cmp_exc_match, - } -def COMPARE_OP(f, testnum): - w_2 = f.valuestack.pop() - w_1 = f.valuestack.pop() - try: - testfn = compare_dispatch_table[testnum] - except KeyError: - raise pyframe.BytecodeCorruption, "bad COMPARE_OP oparg" - w_result = testfn(f, w_1, w_2) - f.valuestack.push(w_result) - -def IMPORT_NAME(f, nameindex): - modulename = f.getname(nameindex) - w_modulename = f.space.wrap(modulename) - w_fromlist = f.valuestack.pop() - w_obj = f.space.gethelper(appfile).call( - "import_name", [f.w_builtins, - w_modulename, f.w_globals, f.w_locals, w_fromlist]) - f.valuestack.push(w_obj) - -def IMPORT_STAR(f): - w_module = f.valuestack.pop() - f.space.gethelper(appfile).call("import_all_from", [w_module, f.w_locals]) - -def IMPORT_FROM(f, nameindex): - name = f.getname(nameindex) - w_name = f.space.wrap(name) - w_module = f.valuestack.top() - w_obj = f.space.gethelper(appfile).call("import_from", [w_module, w_name]) - f.valuestack.push(w_obj) - -def JUMP_FORWARD(f, stepby): - f.next_instr += stepby - -def JUMP_IF_FALSE(f, stepby): - w_cond = f.valuestack.top() - if not f.space.is_true(w_cond): - f.next_instr += stepby + testfn = f.compare_dispatch_table[testnum] + except KeyError: + raise pyframe.BytecodeCorruption, "bad COMPARE_OP oparg" + w_result = testfn(f, w_1, w_2) + f.valuestack.push(w_result) + + def IMPORT_NAME(f, nameindex): + modulename = f.getname(nameindex) + w_modulename = f.space.wrap(modulename) + w_fromlist = f.valuestack.pop() + w_obj = f.space.gethelper(appfile).call( + "import_name", [f.w_builtins, + w_modulename, f.w_globals, f.w_locals, w_fromlist]) + f.valuestack.push(w_obj) + + def IMPORT_STAR(f): + w_module = f.valuestack.pop() + f.space.gethelper(appfile).call("import_all_from", [w_module, f.w_locals]) + + def IMPORT_FROM(f, nameindex): + name = f.getname(nameindex) + w_name = f.space.wrap(name) + w_module = f.valuestack.top() + w_obj = f.space.gethelper(appfile).call("import_from", [w_module, w_name]) + f.valuestack.push(w_obj) -def JUMP_IF_TRUE(f, stepby): - w_cond = f.valuestack.top() - if f.space.is_true(w_cond): + def JUMP_FORWARD(f, stepby): f.next_instr += stepby -def JUMP_ABSOLUTE(f, jumpto): - f.next_instr = jumpto + def JUMP_IF_FALSE(f, stepby): + w_cond = f.valuestack.top() + if not f.space.is_true(w_cond): + f.next_instr += stepby + + def JUMP_IF_TRUE(f, stepby): + w_cond = f.valuestack.top() + if f.space.is_true(w_cond): + f.next_instr += stepby + + def JUMP_ABSOLUTE(f, jumpto): + f.next_instr = jumpto + + def GET_ITER(f): + w_iterable = f.valuestack.pop() + w_iterator = f.space.iter(w_iterable) + f.valuestack.push(w_iterator) -def GET_ITER(f): - w_iterable = f.valuestack.pop() - w_iterator = f.space.iter(w_iterable) - f.valuestack.push(w_iterator) - -def FOR_ITER(f, jumpby): - w_iterator = f.valuestack.top() - try: - w_nextitem = f.space.next(w_iterator) - except NoValue: - # iterator exhausted - f.valuestack.pop() - f.next_instr += jumpby - else: - f.valuestack.push(w_nextitem) - -def FOR_LOOP(f, oparg): - raise pyframe.BytecodeCorruption, "old opcode, no longer in use" - -def SETUP_LOOP(f, offsettoend): - block = pyframe.LoopBlock(f, f.next_instr + offsettoend) - f.blockstack.push(block) - -def SETUP_EXCEPT(f, offsettoend): - block = pyframe.ExceptBlock(f, f.next_instr + offsettoend) - f.blockstack.push(block) - -def SETUP_FINALLY(f, offsettoend): - block = pyframe.FinallyBlock(f, f.next_instr + offsettoend) - f.blockstack.push(block) - -def call_function_extra(f, oparg, with_varargs, with_varkw): - n_arguments = oparg & 0xff - n_keywords = (oparg>>8) & 0xff - if with_varkw: - w_varkw = f.valuestack.pop() - if with_varargs: - w_varargs = f.valuestack.pop() - keywords = [] - for i in range(n_keywords): - w_value = f.valuestack.pop() - w_key = f.valuestack.pop() - keywords.append((w_key, w_value)) - arguments = [f.valuestack.pop() for i in range(n_arguments)] - arguments.reverse() - w_function = f.valuestack.pop() - w_arguments = f.space.newtuple(arguments) - w_keywords = f.space.newdict(keywords) - if with_varargs: - w_arguments = f.space.gethelper(appfile).call("concatenate_arguments", - [w_arguments, w_varargs]) - if with_varkw: - w_keywords = f.space.gethelper(appfile).call("concatenate_keywords", - [w_keywords, w_varkw]) - w_result = f.space.call(w_function, w_arguments, w_keywords) - f.valuestack.push(w_result) - -def CALL_FUNCTION(f, oparg): - call_function_extra(f, oparg, False, False) - -def CALL_FUNCTION_VAR(f, oparg): - call_function_extra(f, oparg, True, False) - -def CALL_FUNCTION_KW(f, oparg): - call_function_extra(f, oparg, False, True) - -def CALL_FUNCTION_VAR_KW(f, oparg): - call_function_extra(f, oparg, True, True) - -def MAKE_FUNCTION(f, numdefaults): - w_codeobj = f.valuestack.pop() - defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] - defaultarguments.reverse() - w_defaultarguments = f.space.newtuple(defaultarguments) - w_func = f.space.newfunction(f.space.unwrap(w_codeobj), - f.w_globals, w_defaultarguments) - f.valuestack.push(w_func) - -def MAKE_CLOSURE(f, numdefaults): - w_codeobj = f.valuestack.pop() - codeobj = f.space.unwrap(w_codeobj) - nfreevars = len(codeobj.co_freevars) - freevars = [f.valuestack.pop() for i in range(nfreevars)] - freevars.reverse() - w_freevars = f.space.newtuple(freevars) - defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] - defaultarguments.reverse() - w_defaultarguments = f.space.newtuple(defaultarguments) - w_func = f.space.newfunction(f.space.unwrap(w_codeobj), - f.w_globals, w_defaultarguments, w_freevars) - f.valuestack.push(w_func) - -def BUILD_SLICE(f, numargs): - if numargs == 3: - w_step = f.valuestack.pop() - elif numargs == 2: - w_step = None - else: - raise pyframe.BytecodeCorruption - w_end = f.valuestack.pop() - w_start = f.valuestack.pop() - w_slice = f.space.newslice(w_start, w_end, w_step) - f.valuestack.push(w_slice) - -def SET_LINENO(f, lineno): - pass - -def EXTENDED_ARG(f, oparg): - opcode = f.nextop() - oparg = oparg<<16 | f.nextarg() - dispatch_arg(f, oparg) - -def MISSING_OPCODE(f, oparg=None): - raise pyframe.BytecodeCorruption, "unknown opcode" - - -################################################################ - -dispatch_table = [] -for i in range(256): - opname = dis.opname[i].replace('+', '_') - fn = MISSING_OPCODE - if opname in globals(): - fn = globals()[opname] - elif not opname.startswith('<') and i>0: - #import warnings - #warnings.warn("* Warning, missing opcode %s" % opname) + def FOR_ITER(f, jumpby): + w_iterator = f.valuestack.top() + try: + w_nextitem = f.space.next(w_iterator) + except NoValue: + # iterator exhausted + f.valuestack.pop() + f.next_instr += jumpby + else: + f.valuestack.push(w_nextitem) + + def FOR_LOOP(f, oparg): + raise pyframe.BytecodeCorruption, "old opcode, no longer in use" + + def SETUP_LOOP(f, offsettoend): + block = pyframe.LoopBlock(f, f.next_instr + offsettoend) + f.blockstack.push(block) + + def SETUP_EXCEPT(f, offsettoend): + block = pyframe.ExceptBlock(f, f.next_instr + offsettoend) + f.blockstack.push(block) + + def SETUP_FINALLY(f, offsettoend): + block = pyframe.FinallyBlock(f, f.next_instr + offsettoend) + f.blockstack.push(block) + + def call_function_extra(f, oparg, with_varargs, with_varkw): + n_arguments = oparg & 0xff + n_keywords = (oparg>>8) & 0xff + if with_varkw: + w_varkw = f.valuestack.pop() + if with_varargs: + w_varargs = f.valuestack.pop() + keywords = [] + for i in range(n_keywords): + w_value = f.valuestack.pop() + w_key = f.valuestack.pop() + keywords.append((w_key, w_value)) + arguments = [f.valuestack.pop() for i in range(n_arguments)] + arguments.reverse() + w_function = f.valuestack.pop() + w_arguments = f.space.newtuple(arguments) + w_keywords = f.space.newdict(keywords) + if with_varargs: + w_arguments = f.space.gethelper(appfile).call("concatenate_arguments", + [w_arguments, w_varargs]) + if with_varkw: + w_keywords = f.space.gethelper(appfile).call("concatenate_keywords", + [w_keywords, w_varkw]) + w_result = f.space.call(w_function, w_arguments, w_keywords) + f.valuestack.push(w_result) + + def CALL_FUNCTION(f, oparg): + f.call_function_extra(oparg, False, False) + + def CALL_FUNCTION_VAR(f, oparg): + f.call_function_extra(oparg, True, False) + + def CALL_FUNCTION_KW(f, oparg): + f.call_function_extra(oparg, False, True) + + def CALL_FUNCTION_VAR_KW(f, oparg): + f.call_function_extra(oparg, True, True) + + def MAKE_FUNCTION(f, numdefaults): + w_codeobj = f.valuestack.pop() + defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] + defaultarguments.reverse() + w_defaultarguments = f.space.newtuple(defaultarguments) + w_func = f.space.newfunction(f.space.unwrap(w_codeobj), + f.w_globals, w_defaultarguments) + f.valuestack.push(w_func) + + def BUILD_SLICE(f, numargs): + if numargs == 3: + w_step = f.valuestack.pop() + elif numargs == 2: + w_step = None + else: + raise pyframe.BytecodeCorruption + w_end = f.valuestack.pop() + w_start = f.valuestack.pop() + w_slice = f.space.newslice(w_start, w_end, w_step) + f.valuestack.push(w_slice) + + def SET_LINENO(f, lineno): pass - dispatch_table.append(fn) + + def EXTENDED_ARG(f, oparg): + opcode = f.nextop() + oparg = oparg<<16 | f.nextarg() + fn = self.dispatch_table[opcode] + if not fn.has_arg: + raise pyframe.BytecodeCorruption + fn(f, oparg) + + def MISSING_OPCODE(f, oparg=None): + raise pyframe.BytecodeCorruption, "unknown opcode" + + ################################################################ + + # 'dispatch_table' is a class attribute: a list of functions + # it is created by 'cls.setup_dispatch_table()'. + + def setup_dispatch_table(cls): + # create the 'cls.dispatch_table' attribute + dispatch_table = [] + missing_opcode = cls.MISSING_OPCODE + for i in range(256): + opname = dis.opname[i].replace('+', '_') + fn = getattr(cls, opname, missing_opcode) + #if fn is missing_opcode and not opname.startswith('<') and i>0: + # import warnings + # warnings.warn("* Warning, missing opcode %s" % opname) + dispatch_table.append(fn) + cls.dispatch_table = dispatch_table + setup_dispatch_table = classmethod(setup_dispatch_table) -def name(thing): - try: - return thing.operationname - except AttributeError: - return thing.__name__ - -def has_arg(opcode): - return opcode >= dis.HAVE_ARGUMENT - -def dispatch_noarg(f, opcode): - try: - fn = dispatch_table[opcode] -# print name(fn) - except KeyError: - raise KeyError, "missing opcode %s" % dis.opname[opcode] - fn(f) - -def dispatch_arg(f, opcode, oparg): - assert oparg >= 0 - try: - fn = dispatch_table[opcode] -# print name(fn) - except KeyError: - raise KeyError, "missing opcode %s" % dis.opname[opcode] - fn(f, oparg) +PyOperationalFrame.setup_dispatch_table() From arigo at codespeak.net Sun Aug 10 21:34:27 2003 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 10 Aug 2003 21:34:27 +0200 (MEST) Subject: [pypy-svn] rev 1265 - pypy/branch/builtinrefactor/pypy/interpreter Message-ID: <20030810193427.ACFDC5A8EF@thoth.codespeak.net> Author: arigo Date: Sun Aug 10 21:34:26 2003 New Revision: 1265 Added: pypy/branch/builtinrefactor/pypy/interpreter/nestedscope.py - copied, changed from rev 1264, pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py Removed: pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py pypy/branch/builtinrefactor/pypy/interpreter/eval.py pypy/branch/builtinrefactor/pypy/interpreter/gateway.py pypy/branch/builtinrefactor/pypy/interpreter/pycode.py pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py pypy/branch/builtinrefactor/pypy/interpreter/unittest_w.py Log: Still nothing that works, far from it, but progress nevertheless (I think). Modified: pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/baseobjspace.py Sun Aug 10 21:34:26 2003 @@ -5,6 +5,11 @@ __all__ = ['ObjSpace', 'OperationError', 'NoValue'] +class Wrappable(object): + """A subclass of Wrappable is an internal, interpreter-level class + that can nevertheless be exposed at application-level by space.wrap().""" + + class NoValue(Exception): """Raised to signal absence of value, e.g. in the iterator accessing method 'op.next()' of object spaces.""" Modified: pypy/branch/builtinrefactor/pypy/interpreter/eval.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/eval.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/eval.py Sun Aug 10 21:34:26 2003 @@ -25,43 +25,31 @@ "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." return [], None, None - def getargcount(self): - "Number of arguments including * and **." + def getvarnames(self): + """List of names including the arguments, vararg and kwarg, + and possibly more locals.""" argnames, varargname, kwargname = self.signature() - count = len(argnames) if varargname is not None: - count += 1 + argnames.append(argname) if kwargname is not None: - count += 1 - return count + argnames.append(kwargname) + return argnames - def getlocalvarname(self, index): - "Default implementation, can be overridden." - argnames, varargname, kwargname = self.signature() - try: - return argnames[index] - except IndexError: - index -= len(argnames) - if varargname is not None: - if index == 0: - return varargname - index -= 1 - if kwargname is not None: - if index == 0: - return kwargname - index -= 1 - raise IndexError, "local variable index out of bounds" + +UNDEFINED = object() # marker for undefined local variables class Frame(object): """A frame is an environment supporting the execution of a code object. Abstract base class.""" - def __init__(self, space, code, w_globals): + def __init__(self, space, code, w_globals, numlocals=0): self.space = space self.code = code # Code instance self.w_globals = w_globals # wrapped dict of globals self.w_locals = None # wrapped dict of locals + # flat list of wrapped locals + self.fastlocals_w = [UNDEFINED]*numlocals def run(self): "Run the frame." @@ -78,21 +66,45 @@ raise TypeError, "abstract" def getdictscope(self): - "Overriden by subclasses with another representation for locals." + "Get the locals as a dictionary." + self.fast2locals() return self.w_locals def setdictscope(self, w_locals): - """Initialize the locals from a dictionary. - Overriden by subclasses with another representation for locals.""" + "Initialize the locals from a dictionary." self.w_locals = w_locals + self.locals2fast() + + def getfastscope(self): + "Get the fast locals as a list." + return self.fastlocals_w def setfastscope(self, scope_w): - """Initialize the locals from a list of values, - where the order is according to self.code.signature(). - Default implementation, to be overridden.""" - space = self.space + """Initialize the fast locals from a list of values, + where the order is according to self.code.signature().""" + if len(scope_w) > len(self.fastlocals_w): + raise ValueError, "too many fastlocals" + self.fastlocals_w[:len(scope_w)] = scope_w + + def fast2locals(self): + # Copy values from self.fastlocals_w to self.w_locals if self.w_locals is None: - self.w_locals = space.newdict([]) - for i in range(len(scope_w)): - varname = self.code.getlocalvarname(i) - space.setitem(self.w_locals, space.wrap(varname), scope_w[i]) + self.w_locals = self.space.newdict([]) + varnames = self.code.getvarnames() + for name, w_value in zip(varnames, self.fastlocals_w): + if w_value is not UNDEFINED: + w_name = self.space.wrap(name) + self.space.setitem(self.w_locals, w_name, w_value) + + def locals2fast(self): + # Copy values from self.w_locals to self.fastlocals_w + varnames = self.code.getvarnames() + for name, i in zip(varnames, range(len(self.fastlocals_w))): + w_name = self.space.wrap(varnames[i]) + try: + w_value = self.space.getitem(self.w_locals, w_name) + except OperationError, e: + if not e.match(self.space, self.space.w_KeyError): + raise + else: + self.fastlocals_w[i] = w_value Modified: pypy/branch/builtinrefactor/pypy/interpreter/gateway.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/gateway.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/gateway.py Sun Aug 10 21:34:26 2003 @@ -1,331 +1,416 @@ """ Gateway between app-level and interpreter-level: -the Wrappable base class. +* BuiltinCode (calling interp-level code from app-level) +* code2interp (embedding a code object into an interpreter-level callable) +* app2interp (embedding an app-level function's code object in the same way) """ -class Wrappable(object): - """A subclass of Wrappable is an internal, interpreter-level class - that can nevertheless be exposed at application-level, - via space.wrap(). - - The methods and attributes that a class wants to expose are defined - with a naming convension: 'app_xxx' is exposed as 'xxx'. - In general, 'app_xxx' should be a gateway (see below).""" - -# XXX most code from some classes below have been "stolen" in -# XXX new classes in other modules. Will be removed. - - - -class ScopedCode(object): - """ a code object within a certain global and closure scope. - (the local scope is given when you call 'eval_frame') - """ - def __init__(self, space, cpycode, w_globals=None, closure_w=()): - self.space = space - self.cpycode = cpycode - self.w_code = space.wrap(cpycode) - self.closure_w = closure_w - if w_globals is None: - w_globals = space.newdict([]) - self.w_globals = w_globals - - def create_frame(self, w_locals=None): - """ return result of executing code object within a frame""" - from pyframe import PyFrame - frame = PyFrame() - frame.initialize(self) - if w_locals is None: - w_locals = self.w_globals - frame.setdictscope(w_locals) - return frame - - def eval_frame(self, *args, **kwargs): - frame = self.create_frame(*args, **kwargs) - return self.space.getexecutioncontext().eval_frame(frame) - -class app2interp(object): - """ this class exposes an app-level method at interpreter-level. - - Note that the wrapped method must *NOT* use a 'self' argument. - Assumption: the instance on which this method is bound to has a - 'space' attribute. - """ - def __init__(self, appfunc): - self.appfunc = appfunc - - def __get__(self, instance, cls=None): - return InterpretedFunction(instance.space, self.appfunc) - -class InterpretedFunctionFromCode(ScopedCode): - def __init__(self, space, cpycode, w_defs, w_globals=None, closure_w=()): - ScopedCode.__init__(self, space, cpycode, w_globals, closure_w) - self.w_defs = w_defs - self.simple = cpycode.co_flags & (CO_VARARGS|CO_VARKEYWORDS)==0 - self.func_code = cpycode - - def parse_args(self, frame, w_args, w_kwargs): - """ parse args and kwargs and set fast scope of frame. - """ - space = self.space - loc_w = None - if self.simple and (w_kwargs is None or not space.is_true(w_kwargs)): - try: - loc_w = space.unpacktuple(w_args, self.cpycode.co_argcount) - except ValueError: - pass - if loc_w is None: - #print "complicated case of arguments for", self.cpycode.co_name, "simple=", self.simple - w_loc = self.parse_args_complex(self.w_code, w_args, w_kwargs, self.w_defs) - loc_w = space.unpacktuple(w_loc) - loc_w.extend([_NULL] * (self.cpycode.co_nlocals - len(loc_w))) - - # make nested cells - if self.cpycode.co_cellvars: - varnames = list(self.cpycode.co_varnames) - for name in self.cpycode.co_cellvars: - i = varnames.index(name) - w_value = loc_w[i] - loc_w[i] = _NULL - frame.closure_w += (Cell(w_value),) - - assert len(loc_w) == self.cpycode.co_nlocals, "local arguments not prepared correctly" - frame.setfastscope(loc_w) - - def create_frame(self, w_args, w_kwargs): - """ parse arguments and execute frame """ - from pyframe import PyFrame - frame = PyFrame() - frame.initialize(self) - self.parse_args(frame, w_args, w_kwargs) - return frame - - def app_parse_args_complex(cpycode, args, kwargs, defs): - """ return list of initial local values parsed from - 'args', 'kwargs' and defaults. - """ - #if cpycode.co_name == 'failUnlessRaises': - # print "co_name", cpycode.co_name - # print "co_argcount", cpycode.co_argcount - # print "co_nlocals", cpycode.co_nlocals - # print "co_varnames", cpycode.co_varnames - # print "args", args - # print "kwargs", kwargs - # print "defs", defs - - CO_VARARGS, CO_VARKEYWORDS = 0x4, 0x8 - - # co_argcount number of expected positional arguments - # (elipsis args like *args and **kwargs do not count) - co_argcount = cpycode.co_argcount - - # construct list of positional args - positional_args = list(args[:co_argcount]) - - len_args = len(args) - len_defs = len(defs) - - if len_args < co_argcount: - # not enough args, fill in kwargs or defaults if exists - i = len_args - while i < co_argcount: - name = cpycode.co_varnames[i] - if name in kwargs: - positional_args.append(kwargs[name]) - del kwargs[name] - else: - if i + len_defs < co_argcount: - raise TypeError, "Not enough arguments" - positional_args.append(defs[i-co_argcount]) - i+=1 - if cpycode.co_flags & CO_VARARGS: - positional_args.append(tuple(args[co_argcount:])) - elif len_args > co_argcount: - raise TypeError, "Too many arguments" - - # we only do the next loop for determining multiple kw-values - i = 0 - while i < len_args and i < co_argcount: - name = cpycode.co_varnames[i] - if name in kwargs: - raise TypeError, "got multiple values for argument %r" % name - i+=1 - - if cpycode.co_flags & CO_VARKEYWORDS: - positional_args.append(kwargs) - elif kwargs: - raise TypeError, "got unexpected keyword argument(s) %s" % repr(kwargs.keys()[0]) - - return positional_args - parse_args_complex = app2interp(app_parse_args_complex) - - def __call__(self, *args_w, **kwargs_w): - """ execute function and take arguments with - native interp-level parameter passing convention """ - w_args = self.space.newtuple(args_w) - w = self.space.wrap - w_kwargs = self.space.newdict([]) - for name, w_value in kwargs_w.items(): - self.space.setitem(w_kwargs, w(name), w_value) - return self.eval_frame(w_args, w_kwargs) - -class InterpretedFunction(InterpretedFunctionFromCode): - """ a function which executes at app-level (by interpreting bytecode - and dispatching operations on an objectspace). - """ - - def __init__(self, space, cpyfunc, w_globals=None, closure_w=()): - """ initialization similar to base class but it also wraps - some function-specific stuff (like defaults). - """ - assert not hasattr(cpyfunc, 'im_self') - InterpretedFunctionFromCode.__init__(self, space, - cpyfunc.func_code, - space.wrap(cpyfunc.func_defaults or ()), - w_globals, closure_w) - -class InterpretedMethod(InterpretedFunction): - """ an InterpretedFunction with 'self' spice. - - XXX hpk: i think we want to eliminate all uses for this class - as bound/unbound methods should be done in objspace?! - - """ - - def __init__(self, *args): - InterpretedFunction.__init__(self, *args) - - def parse_args(self, frame, w_args, w_kwargs): - """ fills in "self" arg and dispatch to InterpreterFunction. - """ - space = self.space - args_w = space.unpacktuple(w_args) - args_w = [space.wrap(self)] + args_w - w_args = space.newtuple(args_w) - return InterpretedFunction.parse_args(self, frame, w_args, w_kwargs) - -class AppVisibleModule: - """ app-level visible Module defined at interpreter-level. - - Inherit from this class if you want to have a module that accesses - the PyPy interpreter (e.g. builtins like 'locals()' require accessing the - frame). You can mix in application-level code by prefixing your method - with 'app_'. Both non-underscore methods and app-level methods will - be available on app-level with their respective name. - - Note that app-level functions don't get a 'self' argument because it doesn't - make sense and we really only need the function (there is no notion of beeing - 'bound' or 'unbound' for them). - - """ - def __init__(self, space): - self.space = space - - space = self.space - modname = self.__class__.__name__ - self.w___name__ = space.wrap(modname) - self._wrapped = _wrapped = space.newmodule(self.w___name__) - - # go through all items in the module class instance - for name in dir(self): - # skip spurious info and internal methods - if name == '__module__' or name.startswith('_') and not name.endswith('_'): - #print modname, "skipping", name - continue - obj = getattr(self, name) - # see if we just need to expose an already wrapped value - if name.startswith('w_'): - space.setattr(_wrapped, space.wrap(name[2:]), obj) - - # see if have something defined at app-level - elif name.startswith('app_'): - obj = self.__class__.__dict__.get(name) - name = name[4:] - w_res = wrap_applevel(space, name, obj) - # nope then we must expose interpreter-level to app-level - else: - w_res = wrap_interplevel(space, name, obj) - setattr(self, 'w_'+name, w_res) - w_name = space.wrap(name) - space.setattr(_wrapped, w_name, w_res) - -def wrap_applevel(space, name, obj): - """ wrap an app-level style object which was compiled at interp-level. """ - if hasattr(obj, 'func_code'): - return space.wrap(InterpretedFunction(space, obj)) - elif inspect.isclass(obj): - # XXX currently (rev 1020) unused, but it may be useful - # to define builtin app-level classes at interp-level. - return wrap_applevel_class(space, name, obj) - else: - raise ValueError, "cannot wrap %s, %s" % (name, obj) - -def wrap_applevel_class(space, name, obj): - """ construct an app-level class by reproducing the - source definition and running it through the interpreter. - It's a bit ugly but i don't know a better way (holger). - """ - assert 1!=1, "Oh you want to use this function?" - l = ['class %s:' % name] - indent = ' ' - for key, value in vars(obj).items(): - if hasattr(value, 'func_code'): - s = inspect.getsource(value) - l.append(s) - indent = " " * (len(s) - len(s.lstrip())) - - if getattr(obj, '__doc__', None): - l.insert(1, indent + obj.__doc__) - - for key, value in vars(obj).items(): - if not key in ('__module__', '__doc__'): - if isinstance(value, (str, int, float, tuple, list)): - l.append('%s%s = %r' % (indent, key, value)) - - s = "\n".join(l) - code = compile(s, s, 'exec') - scopedcode = ScopedCode(space, code, None) - scopedcode.eval_frame() - w_name = space.wrap(name) - w_res = space.getitem(scopedcode.w_globals, w_name) - return w_res - - -def wrap_interplevel(space, name, obj): - """ make an interp-level object accessible on app-level. """ - return space.wrap(obj) - -## Cells (used for nested scopes only) ## - -_NULL = object() # Marker object - -class Cell: - def __init__(self, w_value=_NULL): - self.w_value = w_value - - def clone(self): - return self.__class__(self.w_value) - - def get(self): - if self.w_value is _NULL: - raise ValueError, "get() from an empty cell" - return self.w_value - - def set(self, w_value): - self.w_value = w_value - - def delete(self): - if self.w_value is _NULL: - raise ValueError, "make_empty() on an empty cell" - self.w_value = _NULL - - def __repr__(self): - """ representation for debugging purposes """ - if self.w_value is _NULL: - return "%s()" % self.__class__.__name__ - else: - return "%s(%s)" % (self.__class__.__name__, self.w_value) +from pypy.interpreter import eval, pycode +from pypy.interpreter.baseobjspace import Wrappable + +class BuiltinCode(eval.Code): + "The code object implementing a built-in (interpreter-level) hook." + # When a BuiltinCode is stored in a Function object, + # you get the functionality of CPython's built-in function type. + + def __init__(self, func): + # 'implfunc' is the interpreter-level function. + # note that this uses a lot of (construction-time) introspection. + eval.Code.__init__(self, func.__name__) + self.func = func + # extract the signature from the (CPython-level) code object + tmp = pycode.PyCode(None) + tmp._from_code(func.func_code) + self.sig = tmp.signature() + self.nargs = len(self.getvarnames()) + + def create_frame(self, space, w_globals, closure=None): + return BuiltinFrame(space, self, w_globals, numlocals=self.nargs) + + def signature(self): + return self.sig + + +class BuiltinFrame(eval.Frame): + "Frame emulation for BuiltinCode." + # This is essentially just a delegation to the 'func' of the BuiltinCode. + # Initialization of locals is already done by the time run() is called, + # via the interface defined in eval.Frame. + + def run(self): + return call_with_prepared_arguments(self.space, self.code.func, + self.fastlocals_w) + + +def call_with_prepared_arguments(space, function, argarray): + """Call the given function. 'argarray' is a correctly pre-formatted + list of values for the formal parameters, including one for * and one + for **.""" + # XXX there is no clean way to do this in Python, + # we have to hack back an arguments tuple and keywords dict. + # This algorithm is put in its own well-isolated function so that + # you don't need to look at it :-) + keywords = {} + co = function.func_code + if co.flags & 8: # CO_VARKEYWORDS + w_kwds = argarray[-1] + for w_key in space.unpackiterable(w_kwds): + keywords[space.unwrap(w_key)] = space.getitem(w_kwds, w_key) + argarray = argarray[:-1] + if co.flags & 4: # CO_VARARGS + w_varargs = argarray[-1] + argarray = argarray[:-1] + space.unpacktuple(w_varargs) + return function(*argarray, **keywords) + + +class code2interp(object): + # General-purpose utility for the interpreter-level to create callables + # that transparently invoke code objects (and thus possibly interpreted + # app-level code). + + def __init__(self, code, staticglobals, staticdefaults=[]): + self.code = code + self.staticglobals = staticglobals # a StaticGlobals instance + self.staticdefaults = staticdefaults + + def make_function(self, space): + assert self.staticglobals.is_frozen(), ( + "gateway not callable before the StaticGlobals is frozen") + w_globals = space.wrap(self.staticglobals) + defs_w = [space.wrap(def_value) for def_value in self.staticdefaults] + return Function(space, self.code, w_globals, defs_w) + + def __call__(self, space, *args, **kwds): + wrap = space.wrap + w_args = [wrap(arg) for arg in args] + w_kwds = space.newdict([(wrap(key), wrap(value)) + for key, value in kwds.items()]) + fn = self.make_function(space) + return fn.call(w_args, w_kwds) + + +class StaticGlobals(Wrappable): + # This class captures a part of the content of an interpreter module + # or of a class definition, to be exposed at app-level with a read-only + # dict-like interface. + + def __init__(self, content=None): + self.content = None + if content is not None: + self.freeze(content) + + def freeze(self, content): + # Freeze the object to the value given by 'content': + # either a dictionary or a (new-style) class + assert self.content is None, "%r already frozen" % self + if isinstance(content, dict): + content = content.copy() + else: + mro = list(content.__mro__) + mro.reverse() + content = {} + for c in mro: + content.update(c.__dict__) + self.content = content + + def is_frozen(self): + return self.content is not None + + def app2interp(self, app_f): + "Build a code2interp gateway that calls 'app_f' at app-level." + code = pycode.PyCode(None) + code._from_code(app_f.func_code) + return code2interp(code, self, list(app_f.func_defaults or ())) + + def __getitem__(self, key): + # XXX is only present for today's stdobjspace.cpythonobject wrapper + return self.content[key] + +noglobals = StaticGlobals({}) + + +##class app2interp(object): +## """ this class exposes an app-level method at interpreter-level. + +## Note that the wrapped method must *NOT* use a 'self' argument. +## Assumption: the instance on which this method is bound to has a +## 'space' attribute. +## """ +## def __init__(self, appfunc): +## self.appfunc = appfunc + +## def __get__(self, instance, cls=None): +## return InterpretedFunction(instance.space, self.appfunc) + +##class InterpretedFunctionFromCode(ScopedCode): +## def __init__(self, space, cpycode, w_defs, w_globals=None, closure_w=()): +## ScopedCode.__init__(self, space, cpycode, w_globals, closure_w) +## self.w_defs = w_defs +## self.simple = cpycode.co_flags & (CO_VARARGS|CO_VARKEYWORDS)==0 +## self.func_code = cpycode + +## def parse_args(self, frame, w_args, w_kwargs): +## """ parse args and kwargs and set fast scope of frame. +## """ +## space = self.space +## loc_w = None +## if self.simple and (w_kwargs is None or not space.is_true(w_kwargs)): +## try: +## loc_w = space.unpacktuple(w_args, self.cpycode.co_argcount) +## except ValueError: +## pass +## if loc_w is None: +## #print "complicated case of arguments for", self.cpycode.co_name, "simple=", self.simple +## w_loc = self.parse_args_complex(self.w_code, w_args, w_kwargs, self.w_defs) +## loc_w = space.unpacktuple(w_loc) +## loc_w.extend([_NULL] * (self.cpycode.co_nlocals - len(loc_w))) + +## # make nested cells +## if self.cpycode.co_cellvars: +## varnames = list(self.cpycode.co_varnames) +## for name in self.cpycode.co_cellvars: +## i = varnames.index(name) +## w_value = loc_w[i] +## loc_w[i] = _NULL +## frame.closure_w += (Cell(w_value),) + +## assert len(loc_w) == self.cpycode.co_nlocals, "local arguments not prepared correctly" +## frame.setfastscope(loc_w) + +## def create_frame(self, w_args, w_kwargs): +## """ parse arguments and execute frame """ +## from pyframe import PyFrame +## frame = PyFrame() +## frame.initialize(self) +## self.parse_args(frame, w_args, w_kwargs) +## return frame + +## def app_parse_args_complex(cpycode, args, kwargs, defs): +## """ return list of initial local values parsed from +## 'args', 'kwargs' and defaults. +## """ +## #if cpycode.co_name == 'failUnlessRaises': +## # print "co_name", cpycode.co_name +## # print "co_argcount", cpycode.co_argcount +## # print "co_nlocals", cpycode.co_nlocals +## # print "co_varnames", cpycode.co_varnames +## # print "args", args +## # print "kwargs", kwargs +## # print "defs", defs + +## CO_VARARGS, CO_VARKEYWORDS = 0x4, 0x8 + +## # co_argcount number of expected positional arguments +## # (elipsis args like *args and **kwargs do not count) +## co_argcount = cpycode.co_argcount + +## # construct list of positional args +## positional_args = list(args[:co_argcount]) + +## len_args = len(args) +## len_defs = len(defs) + +## if len_args < co_argcount: +## # not enough args, fill in kwargs or defaults if exists +## i = len_args +## while i < co_argcount: +## name = cpycode.co_varnames[i] +## if name in kwargs: +## positional_args.append(kwargs[name]) +## del kwargs[name] +## else: +## if i + len_defs < co_argcount: +## raise TypeError, "Not enough arguments" +## positional_args.append(defs[i-co_argcount]) +## i+=1 +## if cpycode.co_flags & CO_VARARGS: +## positional_args.append(tuple(args[co_argcount:])) +## elif len_args > co_argcount: +## raise TypeError, "Too many arguments" + +## # we only do the next loop for determining multiple kw-values +## i = 0 +## while i < len_args and i < co_argcount: +## name = cpycode.co_varnames[i] +## if name in kwargs: +## raise TypeError, "got multiple values for argument %r" % name +## i+=1 + +## if cpycode.co_flags & CO_VARKEYWORDS: +## positional_args.append(kwargs) +## elif kwargs: +## raise TypeError, "got unexpected keyword argument(s) %s" % repr(kwargs.keys()[0]) + +## return positional_args +## parse_args_complex = app2interp(app_parse_args_complex) + +## def __call__(self, *args_w, **kwargs_w): +## """ execute function and take arguments with +## native interp-level parameter passing convention """ +## w_args = self.space.newtuple(args_w) +## w = self.space.wrap +## w_kwargs = self.space.newdict([]) +## for name, w_value in kwargs_w.items(): +## self.space.setitem(w_kwargs, w(name), w_value) +## return self.eval_frame(w_args, w_kwargs) + +##class InterpretedFunction(InterpretedFunctionFromCode): +## """ a function which executes at app-level (by interpreting bytecode +## and dispatching operations on an objectspace). +## """ + +## def __init__(self, space, cpyfunc, w_globals=None, closure_w=()): +## """ initialization similar to base class but it also wraps +## some function-specific stuff (like defaults). +## """ +## assert not hasattr(cpyfunc, 'im_self') +## InterpretedFunctionFromCode.__init__(self, space, +## cpyfunc.func_code, +## space.wrap(cpyfunc.func_defaults or ()), +## w_globals, closure_w) + +##class InterpretedMethod(InterpretedFunction): +## """ an InterpretedFunction with 'self' spice. + +## XXX hpk: i think we want to eliminate all uses for this class +## as bound/unbound methods should be done in objspace?! + +## """ + +## def __init__(self, *args): +## InterpretedFunction.__init__(self, *args) + +## def parse_args(self, frame, w_args, w_kwargs): +## """ fills in "self" arg and dispatch to InterpreterFunction. +## """ +## space = self.space +## args_w = space.unpacktuple(w_args) +## args_w = [space.wrap(self)] + args_w +## w_args = space.newtuple(args_w) +## return InterpretedFunction.parse_args(self, frame, w_args, w_kwargs) + +##class AppVisibleModule: +## """ app-level visible Module defined at interpreter-level. + +## Inherit from this class if you want to have a module that accesses +## the PyPy interpreter (e.g. builtins like 'locals()' require accessing the +## frame). You can mix in application-level code by prefixing your method +## with 'app_'. Both non-underscore methods and app-level methods will +## be available on app-level with their respective name. + +## Note that app-level functions don't get a 'self' argument because it doesn't +## make sense and we really only need the function (there is no notion of beeing +## 'bound' or 'unbound' for them). + +## """ +## def __init__(self, space): +## self.space = space + +## space = self.space +## modname = self.__class__.__name__ +## self.w___name__ = space.wrap(modname) +## self._wrapped = _wrapped = space.newmodule(self.w___name__) + +## # go through all items in the module class instance +## for name in dir(self): +## # skip spurious info and internal methods +## if name == '__module__' or name.startswith('_') and not name.endswith('_'): +## #print modname, "skipping", name +## continue +## obj = getattr(self, name) +## # see if we just need to expose an already wrapped value +## if name.startswith('w_'): +## space.setattr(_wrapped, space.wrap(name[2:]), obj) + +## # see if have something defined at app-level +## elif name.startswith('app_'): +## obj = self.__class__.__dict__.get(name) +## name = name[4:] +## w_res = wrap_applevel(space, name, obj) +## # nope then we must expose interpreter-level to app-level +## else: +## w_res = wrap_interplevel(space, name, obj) +## setattr(self, 'w_'+name, w_res) +## w_name = space.wrap(name) +## space.setattr(_wrapped, w_name, w_res) + +##def wrap_applevel(space, name, obj): +## """ wrap an app-level style object which was compiled at interp-level. """ +## if hasattr(obj, 'func_code'): +## return space.wrap(InterpretedFunction(space, obj)) +## elif inspect.isclass(obj): +## # XXX currently (rev 1020) unused, but it may be useful +## # to define builtin app-level classes at interp-level. +## return wrap_applevel_class(space, name, obj) +## else: +## raise ValueError, "cannot wrap %s, %s" % (name, obj) + +##def wrap_applevel_class(space, name, obj): +## """ construct an app-level class by reproducing the +## source definition and running it through the interpreter. +## It's a bit ugly but i don't know a better way (holger). +## """ +## assert 1!=1, "Oh you want to use this function?" +## l = ['class %s:' % name] +## indent = ' ' +## for key, value in vars(obj).items(): +## if hasattr(value, 'func_code'): +## s = inspect.getsource(value) +## l.append(s) +## indent = " " * (len(s) - len(s.lstrip())) + +## if getattr(obj, '__doc__', None): +## l.insert(1, indent + obj.__doc__) + +## for key, value in vars(obj).items(): +## if not key in ('__module__', '__doc__'): +## if isinstance(value, (str, int, float, tuple, list)): +## l.append('%s%s = %r' % (indent, key, value)) + +## s = "\n".join(l) +## code = compile(s, s, 'exec') +## scopedcode = ScopedCode(space, code, None) +## scopedcode.eval_frame() +## w_name = space.wrap(name) +## w_res = space.getitem(scopedcode.w_globals, w_name) +## return w_res + + +##def wrap_interplevel(space, name, obj): +## """ make an interp-level object accessible on app-level. """ +## return space.wrap(obj) + +#### Cells (used for nested scopes only) ## + +##_NULL = object() # Marker object + +##class Cell: +## def __init__(self, w_value=_NULL): +## self.w_value = w_value + +## def clone(self): +## return self.__class__(self.w_value) + +## def get(self): +## if self.w_value is _NULL: +## raise ValueError, "get() from an empty cell" +## return self.w_value + +## def set(self, w_value): +## self.w_value = w_value + +## def delete(self): +## if self.w_value is _NULL: +## raise ValueError, "make_empty() on an empty cell" +## self.w_value = _NULL + +## def __repr__(self): +## """ representation for debugging purposes """ +## if self.w_value is _NULL: +## return "%s()" % self.__class__.__name__ +## else: +## return "%s(%s)" % (self.__class__.__name__, self.w_value) Copied: pypy/branch/builtinrefactor/pypy/interpreter/nestedscope.py (from rev 1264, pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py) ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/nestedscope.py Sun Aug 10 21:34:26 2003 @@ -1,4 +1,5 @@ -from pypy.interpreter.pyfastscope import PyFastScopeFrame, UNDEFINED +from pypy.interpreter.eval import UNDEFINED +from pypy.interpreter.pyopcode import PyInterpFrame class Cell(object): @@ -36,7 +37,7 @@ content, id(self)) -class PyNestedScopeFrame(PyFastScopeFrame): +class PyNestedScopeFrame(PyInterpFrame): """This class enhances a standard frame with nested scope abilities, i.e. handling of cell/free variables.""" @@ -47,14 +48,23 @@ # 'closure' is a list of Cell instances: the received free vars. def __init__(self, space, code, w_globals, closure): - PyFastScopeFrame.__init__(self, space, code, w_globals, closure) + PyInterpFrame.__init__(self, space, code, w_globals, closure) ncellvars = len(code.co_cellvars) nfreevars = len(code.co_freevars) + if closure is None: + if nfreevars: + raise OperationError(space.w_TypeError, + "directly executed code object " + "may not contain free variables") + else: + if len(closure) != nfreevars: + raise ValueError("code object received a closure with " + "an unexpected number of free variables") self.cells = [Cell() for i in range(ncellvars)] + closure def fast2locals(self): - PyFastScopeFrame.fast2locals(self) - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars + PyInterpFrame.fast2locals(self) + freevarnames = self.code.co_cellvars + self.code.co_freevars for name, cell in zip(freevarnames, self.cells): try: w_value = cell.get() @@ -65,8 +75,8 @@ self.space.setitem(self.w_locals, w_name, w_value) def locals2fast(self): - PyFastScopeFrame.locals2fast(self) - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars + PyInterpFrame.locals2fast(self) + freevarnames = self.code.co_cellvars + self.code.co_freevars for name, cell in zip(freevarnames, self.cells): w_name = self.space.wrap(name) try: @@ -78,11 +88,11 @@ cell.set(w_value) def setfastscope(self, scope_w): - PyFastScopeFrame.setfastscope(scope_w) - if self.bytecode.co_cellvars: + PyInterpFrame.setfastscope(scope_w) + if self.code.co_cellvars: # the first few cell vars could shadow already-set arguments, # in the same order as they appear in co_varnames - code = self.bytecode + code = self.code argvars = code.co_varnames cellvars = code.co_cellvars next = 0 @@ -99,12 +109,12 @@ break # all cell vars initialized this way def getfreevarname(self, index): - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars + freevarnames = self.code.co_cellvars + self.code.co_freevars return freevarnames[index] def iscellvar(self, index): # is the variable given by index a cell or a free var? - return index < len(self.bytecode.co_cellvars) + return index < len(self.code.co_cellvars) ### extra opcodes ### Modified: pypy/branch/builtinrefactor/pypy/interpreter/pycode.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pycode.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pycode.py Sun Aug 10 21:34:26 2003 @@ -5,9 +5,6 @@ """ from pypy.interpreter import eval -from pypy.interpreter.pyopcode import PyOperationalFrame -from pypy.interpreter.pyfastscope import PyFastScopeFrame -from pypy.interpreter.pynestedscope import PyNestedScopeFrame # code object contants, for co_flags below @@ -64,14 +61,12 @@ def create_frame(self, space, w_globals, closure=None): "Create an empty PyFrame suitable for this code object." - # select the appropriate kind of frame; see below + # select the appropriate kind of frame if self.co_cellvars or self.co_freevars: - frameclass = PyNestedScopeFrame - elif self.co_nlocals: - frameclass = PyFastScopeFrame + from pypy.interpreter.nestedscope import PyNestedScopeFrame as F else: - frameclass = PyOperationalFrame - return frameclass(space, self, w_globals, closure) + from pypy.interpreter.pyopcode import PyInterpFrame as F + return F(space, self, w_globals, closure) def signature(self): "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." @@ -89,17 +84,8 @@ kwargname = None return argnames, varargname, kwargname - def getargcount(self): - count = self.co_argcount - if self.co_flags & CO_VARARGS: - count += 1 - if self.co_flags & CO_VARKEYWORDS: - count += 1 - return count - - def getlocalvarname(self, index): - # nb. this is duplicated in PyFastScopeFrame.getlocalvarname() - return self.co_varnames[index] + def getvarnames(self): + return self.co_varnames def is_generator(self): return self.co_flags & CO_GENERATOR Deleted: /pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py ============================================================================== --- /pypy/branch/builtinrefactor/pypy/interpreter/pyfastscope.py Sun Aug 10 21:34:26 2003 +++ (empty file) @@ -1,88 +0,0 @@ -from pypy.interpreter.pyopcode import PyOperationalFrame - - -UNDEFINED = object() # marker for undefined local variables - - -class PyFastScopeFrame(PyOperationalFrame): - "A PyFrame that knows about fast scopes." - - # this is the class that knows about "fast locals", i.e. - # the fact that local variables are better represented as an array - # of values accessed by index (by the LOAD_FAST, STORE_FAST and - # DELETE_FAST opcodes). - - def __init__(self, space, code, w_globals, closure): - PyOperationalFrame.__init__(self, space, code, w_globals, closure) - self.locals_w = [UNDEFINED] * code.co_nlocals - - def getlocalvarname(self, index): - return self.bytecode.co_varnames[index] - - def getdictlocals(self): - self.fast2locals() - return self.w_locals - - def setdictlocals(self, w_locals): - self.w_locals = w_locals - self.locals2fast() - - def setfastlocals(self, scope_w): - self.locals_w[:len(scope_w)] = scope_w - - def fast2locals(self): - # Copy values from self.locals_w to self.w_locals - if self.w_locals is None: - self.w_locals = self.space.newdict([]) - for name, w_value in zip(self.bytecode.co_varnames, self.locals_w): - if w_value is not UNDEFINED: - w_name = self.space.wrap(name) - self.space.setitem(self.w_locals, w_name, w_value) - - def locals2fast(self): - # Copy values from self.w_locals to self.locals_w - for i in range(self.bytecode.co_nlocals): - w_name = self.space.wrap(self.bytecode.co_varnames[i]) - try: - w_value = self.space.getitem(self.w_locals, w_name) - except OperationError, e: - if not e.match(self.space, self.space.w_KeyError): - raise - else: - self.locals_w[i] = w_value - - ### extra opcodes ### - - def LOAD_FAST(f, varindex): - # access a local variable directly - w_value = f.locals_w[varindex] - if w_value is UNDEFINED: - varname = f.getlocalvarname(varindex) - message = "local variable '%s' referenced before assignment" % varname - raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) - f.valuestack.push(w_value) - - def STORE_FAST(f, varindex): - try: - w_newvalue = f.valuestack.pop() - f.locals_w[varindex] = w_newvalue - except: - print "exception: got index error" - print " varindex:", varindex - print " len(locals_w)", len(f.locals_w) - import dis - print dis.dis(f.bytecode) - print "co_varnames", f.bytecode.co_varnames - print "co_nlocals", f.bytecode.co_nlocals - raise - - def DELETE_FAST(f, varindex): - w_value = f.locals_w[varindex] - if f.locals_w[varindex] is UNDEFINED: - varname = f.getlocalvarname(varindex) - message = "local variable '%s' referenced before assignment" % varname - raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) - f.locals_w[varindex] = UNDEFINED - - -PyFastScopeFrame.setup_dispatch_table() Modified: pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyframe.py Sun Aug 10 21:34:26 2003 @@ -3,8 +3,8 @@ from pypy.interpreter.executioncontext import Stack from pypy.interpreter.error import OperationError -from pypy.interpreter.gateway import app2interp from pypy.interpreter import eval, baseobjspace +from pypy.interpreter.gateway import noglobals class PyFrame(eval.Frame): @@ -15,7 +15,7 @@ Public fields: * 'space' is the object space this frame is running in - * 'bytecode' is the PyCode object this frame runs + * 'code' is the PyCode object this frame runs * 'w_locals' is the locals dictionary to use * 'w_globals' is the attached globals dictionary * 'w_builtins' is the attached built-ins dictionary @@ -24,7 +24,6 @@ def __init__(self, space, code, w_globals, closure): eval.Frame.__init__(self, space, code, w_globals) - self.bytecode = code # Misnomer; this is really like a code object self.valuestack = Stack() self.blockstack = Stack() self.last_exception = None @@ -81,31 +80,6 @@ w_exitvalue = e.args[0] return w_exitvalue - ### opcode dispatch ### - - # 'dispatch_table' is a class attribute: a list of functions. - # Currently, it is always created by setup_dispatch_table in pyopcode.py - # but it could be a custom table. - - def dispatch(self): - opcode = self.nextop() - fn = self.dispatch_table[opcode] - if fn.has_arg: - oparg = self.nextarg() - fn(self, oparg) - else: - fn(self) - - def nextop(self): - c = self.bytecode.co_code[self.next_instr] - self.next_instr += 1 - return ord(c) - - def nextarg(self): - lo = self.nextop() - hi = self.nextop() - return (hi<<8) + lo - ### exception stack ### def clean_exceptionstack(self): @@ -171,40 +145,37 @@ # push the exception to the value stack for inspection by the # exception handler (the code after the except:) operationerr = unroller.args[0] + operationerr.normalize(frame.space) # the stack setup is slightly different than in CPython: # instead of the traceback, we store the unroller object, # wrapped. frame.valuestack.push(frame.space.wrap(unroller)) - - s = frame.space - w_value = operationerr.w_value - w_type = operationerr.w_type -## import pdb -## pdb.set_trace() -## print w_type, `w_value`, frame.bytecode.co_name - self.space = s # needed for the following call - w_res = self.normalize_exception(w_type, w_value) - w_value = s.getitem(w_res, s.wrap(1)) - - frame.valuestack.push(w_value) - frame.valuestack.push(w_type) + frame.valuestack.push(operationerr.w_value) + frame.valuestack.push(operationerr.w_type) frame.next_instr = self.handlerposition # jump to the handler raise StopUnrolling - def app_normalize_exception(etype, evalue): - # mistakes here usually show up as infinite recursion, which is fun. - if isinstance(evalue, etype): - return etype, evalue - if isinstance(etype, type) and issubclass(etype, Exception): - if evalue is None: - evalue = () - elif not isinstance(evalue, tuple): - evalue = (evalue,) - evalue = etype(*evalue) - else: - raise Exception, "?!" # XXX +def app_normalize_exception(etype, evalue): + # XXX should really be defined as a method on OperationError, + # but this is not so easy because OperationError cannot be + # at the same time an old-style subclass of Exception and a + # new-style subclass of Wrappable :-( + # moreover, try importing gateway from errors.py and you'll see :-( + + # mistakes here usually show up as infinite recursion, which is fun. + if isinstance(evalue, etype): return etype, evalue - normalize_exception = app2interp(app_normalize_exception) + if isinstance(etype, type) and issubclass(etype, Exception): + if evalue is None: + evalue = () + elif not isinstance(evalue, tuple): + evalue = (evalue,) + evalue = etype(*evalue) + else: + raise Exception, "?!" # XXX + return etype, evalue +normalize_exception = noglobals.app2interp(app_normalize_exception) + class FinallyBlock(FrameBlock): """A try:finally: block. Stores the position of the exception handler.""" @@ -286,7 +257,7 @@ """Signals a 'return' statement. Argument is the wrapped object to return.""" def emptystack(self, frame): - if frame.bytecode.is_generator(): + if frame.code.is_generator(): raise baseobjspace.NoValue w_returnvalue = self.args[0] raise ExitFrame(w_returnvalue) Deleted: /pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py ============================================================================== --- /pypy/branch/builtinrefactor/pypy/interpreter/pynestedscope.py Sun Aug 10 21:34:26 2003 +++ (empty file) @@ -1,160 +0,0 @@ -from pypy.interpreter.pyfastscope import PyFastScopeFrame, UNDEFINED - - -class Cell(object): - "A simple container for a wrapped value." - - def __init__(self, w_value=UNDEFINED): - self.w_value = w_value - - def clone(self): - return self.__class__(self.w_value) - - def empty(self): - return self.w_value is UNDEFINED - - def get(self): - if self.w_value is UNDEFINED: - raise ValueError, "get() from an empty cell" - return self.w_value - - def set(self, w_value): - self.w_value = w_value - - def delete(self): - if self.w_value is UNDEFINED: - raise ValueError, "delete() on an empty cell" - self.w_value = UNDEFINED - - def __repr__(self): - """ representation for debugging purposes """ - if self.w_value is UNDEFINED: - content = "" - else: - content = repr(self.w_value) - return "<%s(%s) at 0x%x>" % (self.__class__.__name__, - content, id(self)) - - -class PyNestedScopeFrame(PyFastScopeFrame): - """This class enhances a standard frame with nested scope abilities, - i.e. handling of cell/free variables.""" - - # Cell Vars: - # my local variables that are exposed to my inner functions - # Free Vars: - # variables coming from a parent function in which i'm nested - # 'closure' is a list of Cell instances: the received free vars. - - def __init__(self, space, code, w_globals, closure): - PyFastScopeFrame.__init__(self, space, code, w_globals, closure) - ncellvars = len(code.co_cellvars) - nfreevars = len(code.co_freevars) - self.cells = [Cell() for i in range(ncellvars)] + closure - - def fast2locals(self): - PyFastScopeFrame.fast2locals(self) - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars - for name, cell in zip(freevarnames, self.cells): - try: - w_value = cell.get() - except ValueError: - pass - else: - w_name = self.space.wrap(name) - self.space.setitem(self.w_locals, w_name, w_value) - - def locals2fast(self): - PyFastScopeFrame.locals2fast(self) - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars - for name, cell in zip(freevarnames, self.cells): - w_name = self.space.wrap(name) - try: - w_value = self.space.getitem(self.w_locals, w_name) - except OperationError, e: - if not e.match(self.space, self.space.w_KeyError): - raise - else: - cell.set(w_value) - - def setfastscope(self, scope_w): - PyFastScopeFrame.setfastscope(scope_w) - if self.bytecode.co_cellvars: - # the first few cell vars could shadow already-set arguments, - # in the same order as they appear in co_varnames - code = self.bytecode - argvars = code.co_varnames - cellvars = code.co_cellvars - next = 0 - nextname = cellvars[0] - for i in range(len(scope_w)): - if argvars[i] == nextname: - # argument i has the same name as the next cell var - w_value = scope_w[i] - self.cells[next] = Cell(w_value) - next += 1 - try: - nextname = cellvars[next] - except IndexError: - break # all cell vars initialized this way - - def getfreevarname(self, index): - freevarnames = self.bytecode.co_cellvars + self.bytecode.co_freevars - return freevarnames[index] - - def iscellvar(self, index): - # is the variable given by index a cell or a free var? - return index < len(self.bytecode.co_cellvars) - - ### extra opcodes ### - - def LOAD_CLOSURE(f, varindex): - # nested scopes: access the cell object - cell = f.cells[varindex] - w_value = f.space.wrap(cell) - f.valuestack.push(w_value) - - def LOAD_DEREF(f, varindex): - # nested scopes: access a variable through its cell object - cell = f.cells[varindex] - try: - w_value = cell.get() - except ValueError: - varname = f.getfreevarname(varindex) - if f.iscellvar(varindex): - message = "local variable '%s' referenced before assignment" - w_exc_type = f.space.w_UnboundLocalError - else: - message = ("free variable '%s' referenced before assignment" - " in enclosing scope") - w_exc_type = f.space.w_NameError - raise OperationError(w_exc_type, f.space.wrap(message % varname)) - else: - f.valuestack.push(w_value) - - def STORE_DEREF(f, varindex): - # nested scopes: access a variable through its cell object - w_newvalue = f.valuestack.pop() - #try: - cell = f.cells[varindex] - #except IndexError: - # import pdb; pdb.set_trace() - # raise - cell.set(w_newvalue) - - def MAKE_CLOSURE(f, numdefaults): - w_codeobj = f.valuestack.pop() - codeobj = f.space.unwrap(w_codeobj) - nfreevars = len(codeobj.co_freevars) - freevars = [f.valuestack.pop() for i in range(nfreevars)] - freevars.reverse() - w_freevars = f.space.newtuple(freevars) - defaultarguments = [f.valuestack.pop() for i in range(numdefaults)] - defaultarguments.reverse() - w_defaultarguments = f.space.newtuple(defaultarguments) - w_func = f.space.newfunction(f.space.unwrap(w_codeobj), - f.w_globals, w_defaultarguments, w_freevars) - f.valuestack.push(w_func) - - -PyNestedScopeFrame.setup_dispatch_table() Modified: pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/pyopcode.py Sun Aug 10 21:34:26 2003 @@ -30,17 +30,49 @@ f.valuestack.push(w_result) -class PyOperationalFrame(PyFrame): - """A PyFrame that knows about all operational Python opcodes. - It does not know about 'fast variables' nor 'nested scopes'.""" +class PyInterpFrame(PyFrame): + """A PyFrame that knows about interpretation of standard Python opcodes, + with the exception of nested scopes.""" + + def __init__(self, space, code, w_globals, closure): + PyFrame.__init__(self, space, code, w_globals, closure, + code.co_nlocals) # size of fastlocals_w array + + ### opcode dispatch ### + + # 'dispatch_table' is a class attribute: a list of functions. + # Currently, it is always created by setup_dispatch_table in pyopcode.py + # but it could be a custom table. + + def dispatch(self): + opcode = self.nextop() + fn = self.dispatch_table[opcode] + if fn.has_arg: + oparg = self.nextarg() + fn(self, oparg) + else: + fn(self) + + def nextop(self): + c = self.code.co_code[self.next_instr] + self.next_instr += 1 + return ord(c) + + def nextarg(self): + lo = self.nextop() + hi = self.nextop() + return (hi<<8) + lo ### accessor functions ### + def getlocalvarname(self, index): + return self.code.co_varnames[index] + def getconstant(self, index): - return self.bytecode.co_consts[index] + return self.code.co_consts[index] def getname(self, index): - return self.bytecode.co_names[index] + return self.code.co_names[index] ################################################################ ## Implementation of the "operational" opcodes @@ -50,10 +82,32 @@ # the 'self' argument of opcode implementations is called 'f' # for historical reasons + def LOAD_FAST(f, varindex): + # access a local variable directly + w_value = f.fastlocals_w[varindex] + if w_value is UNDEFINED: + varname = f.getlocalvarname(varindex) + message = "local variable '%s' referenced before assignment" % varname + raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) + f.valuestack.push(w_value) + def LOAD_CONST(f, constindex): w_const = f.space.wrap(f.getconstant(constindex)) f.valuestack.push(w_const) + def STORE_FAST(f, varindex): + w_newvalue = f.valuestack.pop() + f.fastlocals_w[varindex] = w_newvalue + #except: + # print "exception: got index error" + # print " varindex:", varindex + # print " len(locals_w)", len(f.locals_w) + # import dis + # print dis.dis(f.code) + # print "co_varnames", f.code.co_varnames + # print "co_nlocals", f.code.co_nlocals + # raise + def POP_TOP(f): f.valuestack.pop() @@ -447,6 +501,13 @@ raise OperationError(w_exc_type, w_exc_value) f.valuestack.push(w_value) + def DELETE_FAST(f, varindex): + if f.fastlocals_w[varindex] is UNDEFINED: + varname = f.getlocalvarname(varindex) + message = "local variable '%s' referenced before assignment" % varname + raise OperationError(f.space.w_UnboundLocalError, f.space.wrap(message)) + f.fastlocals_w[varindex] = UNDEFINED + def BUILD_TUPLE(f, itemcount): items = [f.valuestack.pop() for i in range(itemcount)] items.reverse() @@ -674,4 +735,4 @@ setup_dispatch_table = classmethod(setup_dispatch_table) -PyOperationalFrame.setup_dispatch_table() +PyInterpFrame.setup_dispatch_table() Modified: pypy/branch/builtinrefactor/pypy/interpreter/unittest_w.py ============================================================================== --- pypy/branch/builtinrefactor/pypy/interpreter/unittest_w.py (original) +++ pypy/branch/builtinrefactor/pypy/interpreter/unittest_w.py Sun Aug 10 21:34:26 2003 @@ -2,7 +2,8 @@ import sys, os import unittest -from pypy.interpreter.gateway import InterpretedMethod, InterpretedFunction +#from pypy.interpreter.gateway import InterpretedMethod, InterpretedFunction +# FIX ME FIX ME FIX ME def make_testcase_class(space, tc_w): # XXX this is all a bit insane (but it works) From jum at codespeak.net Mon Aug 25 19:52:45 2003 From: jum at codespeak.net (jum at codespeak.net) Date: Mon, 25 Aug 2003 19:52:45 +0200 (MEST) Subject: [pypy-svn] rev 1269 - pypy/trunk/doc/devel Message-ID: <20030825175245.5E3E95A5AB@thoth.codespeak.net> Author: jum Date: Mon Aug 25 19:52:43 2003 New Revision: 1269 Modified: pypy/trunk/doc/devel/howtosvn.txt Log: Updated for the latest Windows command line binaries. Modified: pypy/trunk/doc/devel/howtosvn.txt ============================================================================== --- pypy/trunk/doc/devel/howtosvn.txt (original) +++ pypy/trunk/doc/devel/howtosvn.txt Mon Aug 25 19:52:43 2003 @@ -110,13 +110,13 @@ -------------------------------------------------------------------------------- -.. _commandline: http://codespeak.net/~jum/svn-0.26.0-setup.exe +.. _commandline: http://codespeak.net/~jum/svn-0.27.0-setup.exe .. _website: http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B259403 -.. _GUI: http://codespeak.net/~jum/TortoiseSVN-0.15.1-UNICODE.msi +.. _GUI: http://codespeak.net/~jum/TortoiseSVN-0.16-UNICODE.msi .. _Win: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=4B6140F9-2D36-4977-8FA1-6F8A0F5DCA8F -.. _MacOS: http://codespeak.net/~jum/svn-0.26.0-darwin-ppc.tar.gz +.. _MacOS: http://codespeak.net/~jum/svn-0.27.0-darwin-ppc.tar.gz .. _iconv: http://codespeak.net/~jum/iconv-darwin-ppc.tar.gz -.. _tarball: http://codespeak.net/~jum/subversion-0.26.0.tar.gz +.. _tarball: http://codespeak.net/~jum/subversion-0.27.0.tar.gz .. _guide: http://svnbook.red-bean.com/book.html#svn-ch-1 .. _archives: http://codespeak.net/pipermail/pypy-svn/