[pypy-svn] r27497 - in pypy/dist/pypy/translator/js2: . test

fijal at codespeak.net fijal at codespeak.net
Sat May 20 12:13:19 CEST 2006


Author: fijal
Date: Sat May 20 12:13:10 2006
New Revision: 27497

Added:
   pypy/dist/pypy/translator/js2/
   pypy/dist/pypy/translator/js2/__init__.py
   pypy/dist/pypy/translator/js2/asmgen.py
   pypy/dist/pypy/translator/js2/function.py
   pypy/dist/pypy/translator/js2/jts.py
   pypy/dist/pypy/translator/js2/log.py
   pypy/dist/pypy/translator/js2/opcodes.py
   pypy/dist/pypy/translator/js2/support.py
   pypy/dist/pypy/translator/js2/test/
   pypy/dist/pypy/translator/js2/test/__init__.py
   pypy/dist/pypy/translator/js2/test/browsertest.py
   pypy/dist/pypy/translator/js2/test/runtest.py
   pypy/dist/pypy/translator/js2/test/test_runtest.py
Log:
Added preliminary version of genjs with ootypesystem support. Supports basic if/while detection.


Added: pypy/dist/pypy/translator/js2/__init__.py
==============================================================================

Added: pypy/dist/pypy/translator/js2/asmgen.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/asmgen.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,153 @@
+
+""" backend generator routines
+"""
+
+from pypy.translator.js2.log import log
+
+from pypy.translator.squeak.node import LoopFinder
+
+from StringIO import StringIO
+
+class CodeGenerator(object):
+    def __init__(self, out, indentstep = 4, startblock = '{', endblock = '}'):
+        self._out = out
+        self._indent = 0
+        self._bol = True # begin of line
+        self._indentstep = indentstep
+        self._startblock = startblock
+        self._endblock = endblock
+    
+    def write(self, s, indent = 0):
+        indent = self._indent + (indent * self._indentstep)
+        
+        if self._bol:
+            self._out.write(' ' * indent)
+
+        self._out.write(s)
+        self._bol = (s and s[-1] == '\n')
+
+    def writeline(self, s=''):
+        self.write(s)
+        self.write('\n')
+
+    def openblock(self):
+        self.writeline(self._startblock)
+        self._indent += self._indentstep
+
+    def closeblock(self):
+        self._indent -= self._indentstep
+        self.writeline(self._endblock)
+
+class Queue ( object ):
+    def __init__ ( self , l , subst_table ):
+        self . l = l [:]
+        self . subst_table = subst_table
+    
+    def pop ( self ):
+        el = self . l . pop ()
+        return self . subst_table . get ( el , el )
+    
+    def __getattr__ ( self , attr ):
+        return getattr ( self . l , attr )
+    
+    def __len__ ( self ):
+        return len(self.l)
+
+class AsmGen ( object ):
+    """ JS 'assembler' generator routines
+    """
+    def __init__ ( self , outfile , name ):
+        self . outfile = outfile
+        self . name = name
+        self . subst_table = {}
+        self . right_hand = Queue ( [] , self . subst_table )
+        self . codegenerator = CodeGenerator ( outfile )
+    
+    def show_const ( self ):
+        return False
+
+    def begin_function ( self , name, arglist, returntype, is_entrypoint = False, *args ):
+        args = "," . join ( [ i [ 1 ] for i in arglist ] )
+        self . codegenerator . write ( "function %s (%s) " % ( name , args ) )
+        self . codegenerator . openblock ()
+    
+    def end_function ( self ):
+        self . codegenerator . closeblock ()
+    
+    def locals ( self , loc ):
+        self . codegenerator . writeline ( "var " + "," . join ( [ i [ 1 ] for i in loc ] ) + ";" )
+    
+    def load_arg ( self , v ):
+        self . right_hand . append ( v.name )
+    
+    def store_local ( self , v ):
+        name = self . subst_table . get ( v . name , v . name )
+        element = self . right_hand . pop ()
+        if element != name:
+            self . codegenerator . writeline ( "%s = %s;" % ( name , element ) )
+    
+    def load_local ( self , v ):
+        self . right_hand . append ( v . name )
+    
+    def load_const ( self , _type , v ):
+        self . right_hand . append ( str(v) )
+    
+    def ret ( self ):
+        self . codegenerator . writeline ( "return ( %s );" % self . right_hand . pop () )
+    
+    def begin_namespace ( self , namespace ):
+        pass
+    
+    def end_namespace ( self ):
+        pass
+    
+    def begin_class ( self , cl ):
+        pass
+    
+    def end_class ( self ):
+        pass
+
+    def emit ( self , opcode , *args ):
+        v1 = self . right_hand . pop ()
+        v2 = self . right_hand . pop ()
+        self . right_hand . append ( "(%s%s%s)" % ( v2 , opcode , v1 ) )
+
+    def call ( self , func ):
+        func_name,args = func
+        real_args = "," . join ( [ self . right_hand . pop () for i in xrange(len(args)) ] )
+        self . right_hand . append ( "%s ( %s )" % ( func_name , real_args ) )
+
+    def branch_if ( self , arg , exitcase ):
+        arg_name = self . subst_table . get ( arg . name , arg . name )
+        self . codegenerator . write ( "if ( %s == %s )" % ( arg_name , exitcase ) )
+        self . codegenerator . openblock ()
+    
+    def branch_while ( self , arg , exitcase ):
+        arg_name = self . subst_table . get ( arg . name , arg . name )
+        self . codegenerator . write ( "while ( %s == %s )" % ( arg_name , exitcase ) )
+        self . codegenerator . openblock ()
+    
+    def branch_else ( self ):
+        self . codegenerator . closeblock ()
+        self . codegenerator . write ( "else" )
+        self . codegenerator . openblock ()
+    
+    def close_branch ( self ):
+        self . codegenerator . closeblock ()
+
+    def label ( self , *args ):
+        self . codegenerator . openblock ()
+
+    def branch ( self , *args ):
+        #self . codegenerator . closeblock ()
+        pass
+    
+    def change_name ( self , from_name , to_name ):
+        self . subst_table [ from_name.name ] = to_name.name
+        #pass
+    
+    def cast_floor ( self ):
+        self . right_hand . append ( "Math.floor ( %s )" % self . right_hand . pop() )
+    
+    #def finish ( self ):
+    #    self . outfile . write ( "%r" % self . right_hand )

Added: pypy/dist/pypy/translator/js2/function.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/function.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,443 @@
+try:
+    set
+except NameError:
+    from sets import Set as set
+
+from pypy.objspace.flow import model as flowmodel
+from pypy.rpython.lltypesystem.lltype import Signed, Unsigned, Void, Bool, Float
+from pypy.rpython.lltypesystem.lltype import SignedLongLong, UnsignedLongLong
+from pypy.rpython.ootypesystem import ootype
+from pypy.translator.cli.option import getoption
+from pypy.translator.cli.cts import CTS
+from pypy.translator.cli.opcodes import opcodes
+from pypy.translator.cli.metavm import Generator,InstructionList
+from pypy.translator.cli.node import Node
+from pypy.translator.cli.class_ import Class
+
+from pypy.tool.ansi_print import ansi_log
+import py
+log = py.log.Producer("cli") 
+py.log.setconsumer("cli", ansi_log) 
+
+class LoopFinder:
+
+    def __init__(self, startblock):
+        self.loops = {}
+        self.parents = {startblock: startblock}
+        self.temps = {}
+        self.seen = set ()
+        self.block_seeing_order = {}
+        self.visit_Block(startblock)
+   
+    def visit_Block(self, block, switches=[]):
+        #self.temps.has_key()
+        self.seen.add(block)
+        self.block_seeing_order[block] = []
+        if block.exitswitch:
+            switches.append(block)
+            self.parents[block] = block
+        for link in block.exits:
+            self.block_seeing_order[block].append(link)
+            self.visit_Link(link, switches) 
+
+    def visit_Link(self, link, switches):
+        if link.target in switches:
+            if len(self.block_seeing_order[link.target]) == 1:
+                self.loops[link.target] = self.block_seeing_order[link.target][0].exitcase
+            else:
+                self.loops[link.target] = self.block_seeing_order[link.target][1].exitcase
+            
+        if not link.target in self.seen:
+            self.parents[link.target] = self.parents[link.prevblock]
+            self.visit_Block(link.target, switches)
+
+class Function(Node, Generator):
+    def __init__(self, db, graph, name = None, is_method = False, is_entrypoint = False ):
+        self.db = db
+        self.cts = db.type_system_class(db)
+        self.graph = graph
+        self.name = name or graph.name
+        self.is_method = is_method
+        self.is_entrypoint = is_entrypoint
+        self.blocknum = {}
+        self._set_args()
+        self._set_locals()
+
+    def get_name(self):
+        return self.name
+
+    def __hash__(self):
+        return hash(self.graph)
+
+    def __eq__(self, other):
+        return self.graph == other.graph
+
+    def _is_return_block(self, block):
+        return (not block.exits) and len(block.inputargs) == 1
+
+    def _is_raise_block(self, block):
+        return (not block.exits) and len(block.inputargs) == 2        
+
+    def render_block ( self , block ):
+        for op in block.operations:
+            self._render_op(op)
+    
+    def render_block ( self , block , stop_block = None ):
+        if block is stop_block:
+            return
+        
+        for op in block.operations:
+            self._render_op(op)
+        
+        if len ( block . exits ) == 0:
+            # return block
+            return_var = block.inputargs[0]
+            self . load ( return_var )
+            self . ilasm . ret ()
+        elif block . exitswitch is None:
+            # single exit block
+            assert ( len(block.exits) == 1 )
+            link = block . exits [ 0 ]
+            self . _setup_link ( link )
+            self . render_block ( link . target , stop_block )
+        elif block . exitswitch is flowmodel.c_last_exception:
+            raise NotImplementedError ( "Exception handling" )
+        else:
+            if self . loops . has_key ( block ):
+                # we've got loop
+                self . ilasm . branch_while ( block . exitswitch , self . loops [ block ] )
+                exit_case = block . exits [ self . loops [ block ] ]
+                self . _setup_link ( exit_case )
+                self . render_block ( exit_case . target , block )
+                for op in block . operations:
+                    self._render_op ( op )
+                self . ilasm . close_branch ()
+                exit_case = block . exits [ not self . loops [ block ] ]
+                self . _setup_link ( exit_case )
+                #log (  )
+                self . render_block ( exit_case . target , block )
+                #raise NotImplementedError ( "loop" )
+            else:
+                # just a simple if
+                assert ( len ( block . exits ) == 2 )
+                self . ilasm . branch_if ( block . exitswitch , True )
+                self . _setup_link ( block . exits [ True ] )
+                self . render_block ( block . exits [ True ] . target , stop_block )
+                self . ilasm . branch_else ()
+                self . _setup_link ( block . exits [ False ] )
+                self . render_block ( block . exits [ False ] . target , stop_block )
+                self . ilasm . close_branch ()
+
+    def render ( self , ilasm ):
+        if self . db . graph_name ( self . graph ) is not None and not self . is_method:
+            return # already rendered
+        
+        self . ilasm = ilasm
+        
+        self . loops = LoopFinder ( self . graph . startblock ) . loops
+        self . ilasm . begin_function ( self . name , self . args , None , None , None )
+
+        self . render_block ( self . graph . startblock )
+##            if self._is_return_block(block):
+##                return_blocks.append(block)
+##                continue
+##            
+##            for op in block.operations:
+##                self._render_op(op)
+##            
+##            for link in block.exits:
+##                target_label = self._get_block_name(link.target)
+##                if link.exitcase is None:
+##                    pass
+##                    self.ilasm.branch(target_label)
+##                else:
+##                    assert type(link.exitcase is bool)
+##                    assert block.exitswitch is not None
+##                    self.ilasm.branch_if( block.exitswitch, link.exitcase, target_label)
+##                self._setup_link(link)
+
+##        for block in return_blocks:
+##            return_var = block.inputargs[0]
+##            if return_var.concretetype is not Void:
+##                self.load(return_var)
+##            self.ilasm.ret()
+
+        self.ilasm.end_function()
+        if self.is_method:
+            pass # TODO
+        else:
+            self.db.record_function(self.graph, self.name)
+
+    def oldrender(self, ilasm):
+        if self.db.graph_name(self.graph) is not None and not self.is_method:
+            return # already rendered
+
+        loop_finder = LoopFinder ( self . graph . startblock )
+        # Work out it here
+
+        self.ilasm = ilasm
+        graph = self.graph
+        returntype, returnvar = self.cts.llvar_to_cts(graph.getreturnvar())
+
+        self.ilasm.begin_function(self.name, self.args, returntype, None , None )
+        self.ilasm.locals(self.locals)
+
+        return_blocks = []
+        for block in graph.iterblocks():
+            if self._is_return_block(block):
+                return_blocks.append(block)
+                continue
+                
+
+            #self.ilasm.label(self._get_block_name(block))
+
+            handle_exc = (block.exitswitch == flowmodel.c_last_exception)
+            if handle_exc:
+                self.ilasm.begin_try()
+
+            for op in block.operations:
+                #self._search_for_classes(op)
+                self._render_op(op)
+
+            if self._is_raise_block(block):
+                exc = block.inputargs[1]
+                self.load(exc)
+                self.ilasm.throw()
+
+            if handle_exc:
+                # search for the "default" block to be executed when no exception is raised
+                for link in block.exits:
+                    if link.exitcase is None:
+                        self._setup_link(link)
+                        target_label = self._get_block_name(link.target)
+                        self.ilasm.leave(target_label)
+                self.ilasm.end_try()
+
+                # catch the exception and dispatch to the appropriate block
+                for link in block.exits:
+                    if link.exitcase is None:
+                        continue # see above
+
+                    assert issubclass(link.exitcase, Exception)
+                    #cts_exc = self.cts.pyexception_to_cts(link.exitcase)
+                    #cts_exc = str(link.exitcase) # TODO: is it a bit hackish?
+                    ll_meta_exc = link.llexitcase
+                    self.db.record_const(ll_meta_exc)
+                    ll_exc = ll_meta_exc._inst.class_._INSTANCE
+                    cts_exc = self.cts.lltype_to_cts(ll_exc, False)
+                    self.ilasm.begin_catch(cts_exc)
+
+                    target = link.target
+                    if self._is_raise_block(target):
+                        # the exception value is on the stack, use it as the 2nd target arg
+                        assert len(link.args) == 2
+                        assert len(target.inputargs) == 2
+                        self.store(link.target.inputargs[1])
+                    else:
+                        # pop the unused exception value
+                        self.ilasm.pop()
+                        self._setup_link(link)
+                    
+                    target_label = self._get_block_name(target)
+                    self.ilasm.leave(target_label)
+                    self.ilasm.end_catch()
+
+            else:
+                # no exception handling, follow block links
+                for link in block.exits:
+                    target_label = self._get_block_name(link.target)
+                    if link.exitcase is None:
+                        pass
+                        #self.ilasm.branch(target_label)
+                    else:
+                        assert type(link.exitcase is bool)
+                        assert block.exitswitch is not None
+                        self.ilasm.branch_if( block.exitswitch, link.exitcase, target_label)
+                    self._setup_link(link)
+                    self.ilasm.close_branch()
+
+        # render return blocks at the end just to please the .NET
+        # runtime that seems to need a return statement at the end of
+        # the function
+        for block in return_blocks:
+            #self.ilasm.label(self._get_block_name(block))
+            return_var = block.inputargs[0]
+            if return_var.concretetype is not Void:
+                self.load(return_var)
+            self.ilasm.ret()
+
+        self.ilasm.end_function()
+        if self.is_method:
+            pass # TODO
+        else:
+            self.db.record_function(self.graph, self.name)
+
+    def _setup_link(self, link):
+        target = link.target
+        for to_load, to_store in zip(link.args, target.inputargs):
+            if to_load.concretetype is not Void:
+                self.load(to_load)
+                self.store(to_store)
+
+
+    def _set_locals(self):
+        # this code is partly borrowed from pypy.translator.c.funcgen.FunctionCodeGenerator
+        # TODO: refactoring to avoid code duplication
+
+        graph = self.graph
+        mix = [graph.getreturnvar()]
+        for block in graph.iterblocks():
+            self.blocknum[block] = len(self.blocknum)
+            mix.extend(block.inputargs)
+
+            for op in block.operations:
+                mix.extend(op.args)
+                mix.append(op.result)
+                if getattr(op, "cleanup", None) is not None:
+                    cleanup_finally, cleanup_except = op.cleanup
+                    for cleanupop in cleanup_finally + cleanup_except:
+                        mix.extend(cleanupop.args)
+                        mix.append(cleanupop.result)
+            for link in block.exits:
+                mix.extend(link.getextravars())
+                mix.extend(link.args)
+
+        # filter only locals variables, i.e.:
+        #  - must be variables
+        #  - must appear only once
+        #  - must not be function parameters
+        #  - must not have 'void' type
+
+        args = {}
+        for ctstype, name in self.args:
+            args[name] = True
+        
+        locals = []
+        seen = {}
+        for v in mix:
+            is_var = isinstance(v, flowmodel.Variable)
+            if id(v) not in seen and is_var and v.name not in args and v.concretetype is not Void:
+                locals.append(self.cts.llvar_to_cts(v))
+                seen[id(v)] = True
+
+        self.locals = locals
+
+    def _set_args(self):
+        args = [arg for arg in self.graph.getargs() if arg.concretetype is not Void]
+        self.args = map(self.cts.llvar_to_cts, args)
+        self.argset = set([argname for argtype, argname in self.args])
+
+    def _get_block_name(self, block):
+        return 'block%s' % self.blocknum[block]
+
+    def _search_for_classes(self, op):
+        for arg in op.args:
+            lltype = None
+            if isinstance(arg, flowmodel.Variable):
+                lltype = arg.concretetype
+            elif isinstance(arg, flowmodel.Constant):
+                lltype = arg.value
+
+            if isinstance(lltype, ootype._view) and isinstance(lltype._inst, ootype._instance):
+                lltype = lltype._inst._TYPE
+
+            if isinstance(lltype, ootype.Instance):
+                self.db.pending_class(lltype)
+
+    def _render_op(self, op):
+        # FIXME: what to do here?
+        instr_list = self.db.opcode_dict.get(op.opname, None)
+        if instr_list is not None:
+            #assert isinstance(instr_list, InstructionList)
+            instr_list.render(self, op)
+        else:
+            if getoption('nostop'):
+                log.WARNING('Unknown opcode: %s ' % op)
+                self.ilasm.opcode(str(op))
+            else:
+                assert False, 'Unknown opcode: %s ' % op
+
+    def field_name(self, obj, field):
+        class_, type_ = obj._lookup_field(field)
+        assert type_ is not None, 'Cannot find the field %s in the object %s' % (field, obj)
+        
+        class_name = self.class_name(class_)
+        field_type = self.cts.lltype_to_cts(type_)
+        return (field_type, class_name, field)
+
+    # following methods belongs to the Generator interface
+
+    def function_signature(self, graph):
+        return self.cts.graph_to_signature(graph, False)
+
+    def class_name(self, ooinstance):
+        return ooinstance._name
+
+    def emit(self, instr, *args):
+        self.ilasm.emit(instr, *args)
+
+    def call_graph(self, graph):
+        self.db.pending_function(graph)
+        func_sig = self.function_signature(graph)        
+        self.ilasm.call(func_sig)
+
+    def call_signature(self, signature):
+        self.ilasm.call(signature)
+
+    def cast_to(self, lltype):
+        cts_type = self.cts.lltype_to_cts(lltype, False)
+        self.ilasm.castclass(cts_type)
+        
+    def new(self, obj):
+        self.ilasm.new(self.cts.ctor_name(obj))
+
+    def set_field(self, obj, name):
+        self.ilasm.set_field(self.field_name(obj,name))
+
+    def get_field(self, obj, name):
+        self.ilasm.get_field(self.field_name(obj,name))
+        
+    def call_method(self, obj, name):
+        # TODO: use callvirt only when strictly necessary
+        signature, virtual = self.cts.method_signature(obj, name)
+        self.ilasm.call_method(signature, virtual)
+
+    def load(self, v):
+        if isinstance(v, flowmodel.Variable):
+            if v.name in self.argset:
+                selftype, selfname = self.args[0]
+                if self.is_method and v.name == selfname:
+                    self.ilasm.load_self()
+                else:
+                    self.ilasm.load_arg(v)
+            else:
+                self.ilasm.load_local(v)
+
+        elif isinstance(v, flowmodel.Constant):
+            self._load_const(v)
+        else:
+            assert False
+
+    def _load_const(self, const):        
+        type_ = const.concretetype
+        if type_ in [ Void , Bool , Float , Signed , Unsigned , SignedLongLong , UnsignedLongLong ]:
+            self.ilasm.load_const(type_,const.value)
+        else:
+            name = self.db.record_const(const.value)
+            cts_type = self.cts.lltype_to_cts(type_)
+            self.ilasm.load_set_field(cts_type,name)
+            #assert False, 'Unknown constant %s' % const
+
+    def store(self, v):
+        if isinstance(v, flowmodel.Variable):
+            if v.concretetype is not Void:
+                self.ilasm.store_local(v)
+        else:
+            assert False
+    
+    def change_name(self, name, to_name):
+        self.ilasm.change_name(name, to_name)
+    
+    def cast_floor(self):
+        self.ilasm.cast_floor()
+

Added: pypy/dist/pypy/translator/js2/jts.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/jts.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,52 @@
+
+""" JavaScript type system
+"""
+
+from pypy.rpython.ootypesystem import ootype
+
+class JTS ( object ):
+    """ Class implementing JavaScript type system
+    calls with mapping similiar to cts
+    """
+    def __init__ ( self , db ):
+        self . db = db
+    
+    def llvar_to_cts ( self , var ):
+        return 'var ',var.name
+    
+    def graph_to_signature(self, graph, is_method = False, func_name = None):
+        ret_type, ret_var = self.llvar_to_cts(graph.getreturnvar())
+        func_name = func_name or graph.name
+        
+        args = [arg for arg in graph.getargs() if arg.concretetype is not ootype.Void]
+        if is_method:
+            args = args[1:]
+
+        #arg_types = [self.lltype_to_cts(arg.concretetype) for arg in args]
+        #arg_list = ', '.join(arg_types)
+
+        return func_name,args
+    
+    def lltype_to_cts(self, t, include_class=True):
+        return 'var'
+##        if isinstance(t, ootype.Instance):
+##            self.db.pending_class(t)
+##            return self.__class(t._name, include_class)
+##        elif isinstance(t, ootype.Record):
+##            name = self.db.pending_record(t)
+##            return self.__class(name, include_class)
+##        elif isinstance(t, ootype.StaticMethod):
+##            return 'void' # TODO: is it correct to ignore StaticMethod?
+##        elif isinstance(t, ootype.List):
+##            item_type = self.lltype_to_cts(t._ITEMTYPE)
+##            return self.__class(PYPY_LIST % item_type, include_class)
+##        elif isinstance(t, ootype.Dict):
+##            key_type = self.lltype_to_cts(t._KEYTYPE)
+##            value_type = self.lltype_to_cts(t._VALUETYPE)
+##            return self.__class(PYPY_DICT % (key_type, value_type), include_class)
+##        elif isinstance(t, ootype.DictItemsIterator):
+##            key_type = self.lltype_to_cts(t._KEYTYPE)
+##            value_type = self.lltype_to_cts(t._VALUETYPE)
+##            return self.__class(PYPY_DICT_ITEMS_ITERATOR % (key_type, value_type), include_class)
+##
+##        return _get_from_dict(_lltype_to_cts, t, 'Unknown type %s' % t)

Added: pypy/dist/pypy/translator/js2/log.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/log.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,4 @@
+import py
+from pypy.tool.ansi_print import ansi_log
+log = py.log.Producer("js2")
+py.log.setconsumer("js2", ansi_log)

Added: pypy/dist/pypy/translator/js2/opcodes.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/opcodes.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,108 @@
+
+""" opcode definitions
+"""
+
+from pypy.translator.cli.metavm import PushArg, PushAllArgs, StoreResult,\
+     InstructionList, New, SetField, GetField, CallMethod, RuntimeNew, Call, MicroInstruction
+
+DoNothing = [PushAllArgs]
+
+class _SameAs ( MicroInstruction ):
+    def render ( self , generator , op ):
+        generator.change_name(op.result,op.args[0])
+        
+class _CastFloor ( MicroInstruction ):
+    def render ( self , generator , op ):
+        generator.cast_floor()
+
+CastFloor = [ PushAllArgs , _CastFloor() ]
+CopyName = [ PushAllArgs , _SameAs () ]
+SameAs = CopyName
+
+opcodes = {'int_mul': '*',
+    'int_add': '+',
+    'int_sub': '-',
+    'int_floordiv': '/',
+    'int_mod': '%',
+    'int_and': '&',
+    'int_or': '|',
+    'int_xor': '^',
+    'int_lshift': '<<',
+    'int_rshift': '>>',
+    'int_lt': '<',
+    'int_le': '<=',
+    'int_eq': '==',
+    'int_ne': '!=',
+    'int_ge': '>=',
+    'int_gt': '>',
+
+    'uint_mul': '*',
+    'uint_add': '+',
+    'uint_sub': '-',
+    'uint_floordiv': '/',
+    'uint_mod': '%',
+    'uint_and': '&',
+    'uint_or': '|',
+    'uint_xor': '^',
+    'uint_lshift': '<<',
+    'uint_rshift': '>>',
+    'uint_lt': '<',
+    'uint_le': '<=',
+    'uint_eq': '==',
+    'uint_ne': '!=',
+    'uint_ge': '>=',
+    'uint_gt': '>',
+
+    'unichar_lt': '<',
+    'unichar_le': '<=',
+    'unichar_eq': '==',
+    'unichar_ne': '!=',
+    'unichar_ge': '>=',
+    'unichar_gt': '>',
+
+    'float_mul': '*',
+    'float_add': '+',
+    'float_sub': '-',
+    'float_truediv': '/',
+    'float_mod': '%',
+    'float_lt': '<',
+    'float_le': '<=',
+    'float_eq': '==',
+    'float_ne': '!=',
+    'float_ge': '>=',
+    'float_gt': '>',
+
+    'ptr_eq': '==',
+    'ptr_ne': '!=',
+        
+    'direct_call' : [Call],
+    'same_as' : SameAs,
+    # when casting from bool we want that every truth value is casted
+    # to 1: we can't simply DoNothing, because the CLI stack could
+    # contains a truth value not equal to 1, so we should use the !=0
+    # trick.
+    'cast_bool_to_int':         CopyName,
+    'cast_bool_to_uint':        CopyName,
+    'cast_bool_to_float':       CopyName,
+    'cast_char_to_int':         CopyName,
+    'cast_unichar_to_int':      CopyName,
+    'cast_int_to_char':         CopyName,
+    'cast_int_to_unichar':      CopyName,
+    'cast_int_to_uint':         CopyName,
+    'cast_int_to_float':        CopyName,
+    'cast_int_to_longlong':     CopyName,
+    'cast_uint_to_int':         CopyName,
+    'cast_float_to_int':        CastFloor,
+    'cast_float_to_uint':       CastFloor,
+    'truncate_longlong_to_int': CopyName,
+}
+
+for key, value in opcodes.iteritems():
+    if type(value) is str:
+        value = InstructionList([PushAllArgs, value, StoreResult])
+    elif value is not None:
+        if StoreResult not in value:
+            value.append(StoreResult)
+        value = InstructionList(value)
+
+    opcodes[key] = value

Added: pypy/dist/pypy/translator/js2/support.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/support.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,61 @@
+from pypy.translator.gensupp import NameManager
+#from pypy.translator.js2.optimize import is_optimized_function
+
+class JavascriptNameManager(NameManager):
+    def __init__(self, js):
+        NameManager.__init__(self)
+        self.js = js
+        self.reserved = {}
+
+        #http://javascript.about.com/library/blreserved.htm
+        reserved_words = '''
+            abstract as boolean break byte case catch
+            char class continue const debugger default delete
+            do double else enum export extends false
+            final finally float for function goto if implements
+            import in instanceof int interface is long
+            namespace native new null package private protected
+            public return short static super switch synchronized
+            this throw throws transient true try typeof
+            use var void volatile while with
+            '''
+        for name in reserved_words.split():
+            self.reserved[name] = True
+
+        #http://javascript.about.com/library/blclassobj.htm
+        predefined_classes_and_objects = '''
+            Anchor anchors Applet applets Area Array Body
+            Button Checkbox Date document Error EvalError FileUpload
+            Form forms frame frames Function Hidden History
+            history Image images Link links location Math
+            MimeType mimetypes navigator Number Object Option options
+            Password Plugin plugins Radio RangeError ReferenceError RegExp
+            Reset screen Script Select String Style StyleSheet
+            Submit SyntaxError Text Textarea TypeError URIError window
+            '''
+        for name in predefined_classes_and_objects.split():
+            self.reserved[name] = True
+
+        #http://javascript.about.com/library/blglobal.htm
+        global_properties_and_methods = '''
+            _content closed Components controllers crypto defaultstatus directories
+            document frames history innerHeight innerWidth length location
+            locationbar menubar name navigator opener outerHeight outerWidth
+            pageXOffset pageYOffset parent personalbar pkcs11 prompter screen
+            screenX screenY scrollbars scrollX scrollY self statusbar
+            toolbar top window
+            '''
+        for name in global_properties_and_methods.split():
+            self.reserved[name] = True
+
+        self.make_reserved_names(' '.join(self.reserved))
+
+    def uniquename(self, name):
+        #if self.js.compress and name != self.js.functions[0].func_name and is_optimized_function(name) and name.startswith("ll_issubclass__object_vtablePtr_object_vtablePtr"):
+        #    name = 'f'
+        return NameManager.uniquename(self, name)
+
+    def ensure_non_reserved(self, name):
+        while name in self.reserved:
+            name += '_'
+        return name

Added: pypy/dist/pypy/translator/js2/test/__init__.py
==============================================================================

Added: pypy/dist/pypy/translator/js2/test/browsertest.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/test/browsertest.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,149 @@
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+import py
+from os   import system
+from cgi  import parse_qs
+from sys  import platform
+from time import sleep
+from webbrowser import open as webbrowser_open
+from pypy.translator.js2.log import log
+log = log.browsertest
+
+
+class config:
+    http_port = 10001
+
+    html_page = """<html>
+<head>
+<script type="text/javascript">
+%(jscode)s
+// code for running the unittest...
+
+function runTest() {
+    var result = undefined;
+    try {
+        result = %(jstestcase)s;
+    } catch (e) {
+        try {
+            result = "throw '" + e.toSource() + "'";
+        } catch (dummy) {
+            result = "throw 'unknown javascript exception'";
+        }
+    }
+
+    if (result != undefined || !in_browser) {  // if valid result (no timeout)
+        handle_result(result);
+    }
+};
+
+function handle_result(result) {
+    var resultform = document.forms['resultform'];
+    if (typeof(result) == typeof({})) {
+        result = result.chars;  //assume it's a rpystring
+    }
+    resultform.result.value = result;
+    resultform.submit();
+};
+</script>
+</head>
+<body onload="runTest()">
+    %(jsfilename)s
+    <form method="post" name="resultform" id="resultform">
+        <input name="result" type="hidden" value="UNKNOWN" />
+    </form>
+    <div id="logdiv"></div>
+</body>
+</html>"""
+
+    refresh_page = """<html>
+<head>
+<meta http-equiv="refresh" content="0">
+</head>
+<body>
+<pre>
+// testcase: %(jstestcase)s
+%(jscode)s
+</pre>
+</body>
+</html>"""
+
+
+class TestCase(object):
+    def __init__(self, jsfilename, jstestcase):
+        self.jsfilename = jsfilename
+        self.jscode     = open(jsfilename).read()
+        self.jstestcase = jstestcase
+        self.result     = None
+
+
+class TestHandler(BaseHTTPRequestHandler):
+    """The HTTP handler class that provides the tests and handles results"""
+
+    def do_GET(self):
+        global do_status
+        if self.path != "/test.html":
+            self.send_error(404, "File not found")
+            return
+        jsfilename = jstest.jsfilename
+        jstestcase = jstest.jstestcase
+        jscode     = jstest.jscode
+        html_page  = config.html_page % locals()
+        open("html_page.html", "w").write(html_page)
+        self.serve_data('text/html', html_page)
+        do_status = 'do_GET'
+
+    def do_POST(self):
+        global do_status
+        if self.path != "/test.html":
+            self.send_error(404, "File not found")
+            return
+        form = parse_qs(self.rfile.read(int(self.headers['content-length'])))
+        jstest.result = form['result'][0]
+
+        #we force a page refresh here because of two reason:
+        # 1. we don't have the next testcase ready yet
+        # 2. browser should ask again when we do have a test
+        jsfilename = jstest.jsfilename
+        jstestcase = jstest.jstestcase
+        jscode     = jstest.jscode
+        refresh_page = config.refresh_page % locals()
+        self.serve_data('text/html', refresh_page)
+        do_status = 'do_POST'
+
+    def serve_data(self, content_type, data):
+        self.send_response(200)
+        self.send_header("Content-type", content_type)
+        self.send_header("Content-length", len(data))
+        self.end_headers()
+        self.wfile.write(data)
+
+
+class BrowserTest(object):
+    """The browser driver"""
+
+    def start_server(self, port):
+        server_address = ('', port)
+        self.httpd = HTTPServer(server_address, TestHandler)
+
+    def get_result(self):
+        global do_status
+        do_status = None
+        while do_status != 'do_GET':
+            self.httpd.handle_request()
+        while do_status != 'do_POST':
+            self.httpd.handle_request()
+        return jstest.result
+
+
+def jstest(jsfilename, jstestcase):
+    global driver, jstest
+    jstest = TestCase(str(jsfilename), str(jstestcase))
+
+    try:
+        driver
+    except:
+        driver = BrowserTest()
+        driver.start_server(config.http_port)
+        webbrowser_open('http://localhost:%d/test.html' % config.http_port)
+
+    result = driver.get_result()
+    return result

Added: pypy/dist/pypy/translator/js2/test/runtest.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/test/runtest.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,81 @@
+'''
+    Skipped tests should still be fixed. (or only run with py.test --browser)
+    Sests with DONT in front of them will probably not be fixed for the time being.
+'''
+
+import py, os
+from pypy.translator.translator import TranslationContext
+from pypy.translator.backendopt.all import backend_optimizations
+from pypy.translator.js2.js import JS
+from pypy.translator.js2.test.browsertest import jstest
+from pypy.translator.js import conftest
+from pypy.translator.js2.log import log
+from pypy.conftest import option
+log = log.runtest
+use_browsertest = conftest.option.jsbrowser
+
+def _CLI_is_on_path():
+    try:
+        py.path.local.sysfind('js') #we recommend Spidermonkey
+    except py.error.ENOENT:
+        return False
+    return True
+
+class compile_function(object):
+    def __init__(self, function, annotation, stackless=False, view=False):
+        if not use_browsertest and not _CLI_is_on_path():
+            py.test.skip('Javascript CLI (js) not found')
+
+        t = TranslationContext()
+        t.buildannotator().build_types(function, annotation)
+
+        t.buildrtyper(type_system="ootype").specialize() 
+        #print t.rtyper
+
+        #backend_optimizations(t, raisingop2direct_call_all=True, inline_threshold=0, mallocs=False)
+        #backend_optimizations(t)
+        if view or option.view:
+            t.view()
+        #self.js = JS(t, [function, callback_function], stackless)
+        self.js = JS(t, [function], stackless)
+        self.js.write_source()
+
+    def _conv(self, v):
+        if isinstance(v, str):
+            return "{hash:0, chars:'%s'}" % v
+        return str(v).lower()
+
+    def __call__(self, *kwds):
+        args = ', '.join([self._conv(kw) for kw in kwds]) #lowerstr for (py)False->(js)false, etc.
+
+        entry_function = self.js.translator.graphs[0].name
+        function_call = "%s(%s)" % (entry_function, args)
+        #if self.js.stackless:
+        #    function_call = "slp_entry_point('%s')" % function_call
+
+        if use_browsertest:
+            output = jstest(self.js.filename, function_call)
+        else:
+            cmd = 'echo "load(\'%s\'); print(%s)" | js 2>&1' % (self.js.filename, function_call)
+            log(cmd)
+            output = os.popen(cmd).read().strip()
+        for s in output.split('\n'):
+            log(s)
+
+        if s == 'false':
+            res = False
+        elif s == 'true':
+            res = True
+        elif s == 'undefined':
+            res = None
+        elif s == 'inf':
+            res = 1e300 * 1e300
+        elif s == 'NaN':
+            res = (1e300 * 1e300) / (1e300 * 1e300)
+        else:
+            log('javascript result:', s)
+            try:
+                res = eval(s)
+            except:
+                res = str(s)
+        return res

Added: pypy/dist/pypy/translator/js2/test/test_runtest.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/js2/test/test_runtest.py	Sat May 20 12:13:10 2006
@@ -0,0 +1,53 @@
+import py
+from pypy.translator.js2.test.runtest import compile_function
+
+#test returntypes
+##def test_bool_return():
+##    def bool_return_False():
+##        return False
+##    def bool_return_True():
+##        return True
+##    f_false = compile_function(bool_return_False, [])
+##    assert f_false() == bool_return_False()
+##    f_true  = compile_function(bool_return_True , [])
+##    assert f_true()  == bool_return_True()
+##    
+##
+def test_int_return():
+    def int_return():
+        return 42
+    f = compile_function(int_return, [])
+    assert f() == int_return()
+
+def test_float_return():
+    def float_return():
+        return 42.5
+    f = compile_function(float_return, [])
+    assert f() == float_return()
+
+#test paramtypes
+def test_bool_param():
+    def bool_param(b):
+        return b
+    f = compile_function(bool_param, [bool])
+    assert f(False) == bool_param(False)
+    assert f(True ) == bool_param(True )
+
+def test_int_param():
+    def int_param(n):
+        return n * 2
+    f = compile_function(int_param, [int])
+    assert f(42) == int_param(42)
+
+def test_float_param():
+    def float_param(f):
+        return f * 3.0
+    f = compile_function(float_param, [float])
+    assert f(12.5) == float_param(12.5)
+
+def test_combined_params():
+    def combined_params(b, n, f):
+        return int(int(b) * 5 + n * 2 + f * 3.0)
+    f = compile_function(combined_params, [bool,int,float])
+    assert f(False, 13, 12.5) == combined_params(False, 13, 12.5)
+    assert f(True , 13, 12.5) == combined_params(True , 13, 12.5)



More information about the Pypy-commit mailing list