[pypy-commit] lang-js default: re-enabled and fixed virtualizables

stepahn noreply at buildbot.pypy.org
Sun Feb 3 17:38:36 CET 2013


Author: Stephan <stephan at stzal.com>
Branch: 
Changeset: r347:4d28120584cd
Date: 2013-02-01 13:20 +0100
http://bitbucket.org/pypy/lang-js/changeset/4d28120584cd/

Log:	re-enabled and fixed virtualizables

diff --git a/js/completion.py b/js/completion.py
--- a/js/completion.py
+++ b/js/completion.py
@@ -2,6 +2,8 @@
 
 # 8.9
 class Completion(object):
+    _immutable_fields_ = ['value', 'target']
+
     def __init__(self, value=None, target=None):
         self.value = value
         self.target = target
diff --git a/js/environment_record.py b/js/environment_record.py
--- a/js/environment_record.py
+++ b/js/environment_record.py
@@ -83,7 +83,11 @@
         if self._binding_map_.not_found(idx):
             return
 
-        del(self._binding_slots_[idx])
+        assert idx >= 0
+        i = (idx + 1)
+        assert i >= 0
+
+        self._binding_slots_ = self._binding_slots_[:idx] + self._binding_slots_[i:] #len(self._binding_slots_)]
         self._binding_map_ = self._binding_map_.delete(name)
 
     # 10.2.1.1.2
@@ -126,7 +130,7 @@
             return False
         if self._is_deletable_binding(identifier) is False:
             return False
-        self._deletable_bindings_map__ = self._deletable_bindings_map_.delete(identifier)
+        self._deletable_bindings_map_ = self._deletable_bindings_map_.delete(identifier)
         self._mutable_bindings_map_ = self._mutable_bindings_map_.delete(identifier)
         self._del_binding(identifier)
         return False
diff --git a/js/execution_context.py b/js/execution_context.py
--- a/js/execution_context.py
+++ b/js/execution_context.py
@@ -1,12 +1,15 @@
 from js.utils import StackMixin
 from js.object_space import newundefined
+from pypy.rlib import jit
 
 
 class ExecutionContext(StackMixin):
-    _immutable_fields_ = ['_stack_', '_stack_resize_', '_this_binding_', '_lexical_environment_', '_variable_environment_']
-    _refs_resizable_ = True
+    _immutable_fields_ = ['_stack_', '_this_binding_', '_lexical_environment_', '_variable_environment_', '_refs_', '_code_', '_formal_parameters_', '_argument_values_', '_w_func_']  # TODO why are _formal_parameters_, _w_func_ etc. required here?
+    _virtualizable2_ = ['_stack_[*]', '_stack_pointer_', '_refs_[*]']
+    _settled_ = True
 
     def __init__(self, stack_size=1, refs_size=1):
+        self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
         self._lexical_environment_ = None
         self._variable_environment_ = None
         self._this_binding_ = None
@@ -22,6 +25,7 @@
     def stack_top(self):
         return self._stack_top()
 
+    @jit.unroll_safe
     def stack_pop_n(self, n):
         if n < 1:
             return []
@@ -51,12 +55,13 @@
         self._lexical_environment_ = lex_env
 
     # 10.5
+    @jit.unroll_safe
     def declaration_binding_initialization(self):
         from js.object_space import newundefined
 
         env = self._variable_environment_.environment_record
         strict = self._strict_
-        code = self._code_
+        code = jit.promote(self._code_)
 
         if code.is_eval_code():
             configurable_bindings = True
@@ -65,7 +70,7 @@
 
         # 4.
         if code.is_function_code():
-            names = self._formal_parameters_
+            names = code.params() #_formal_parameters_
             n = 0
             args = self._argument_values_
 
@@ -99,7 +104,7 @@
             # TODO get calling W_Function
             func = self._w_func_
             arguments = self._argument_values_
-            names = self._formal_parameters_
+            names = code.params() #_formal_parameters_
             args_obj = W_Arguments(func, names, arguments, env, strict)
 
             if strict is True:
@@ -118,41 +123,60 @@
                 env.set_mutable_binding(dn, newundefined(), False)
 
     def _get_refs(self, index):
-        assert index <= len(self._refs_)
+        assert index < len(self._refs_)
+        assert index >= 0
         return self._refs_[index]
 
     def _set_refs(self, index, value):
-        assert index <= len(self._refs_)
+        assert index < len(self._refs_)
+        assert index >= 0
         self._refs_[index] = value
 
     def get_ref(self, symbol, index=-1):
         ## TODO pre-bind symbols, work with idndex, does not work, see test_foo19
-        if index == -1:
+        if index < 0:
             lex_env = self.lexical_environment()
             ref = lex_env.get_identifier_reference(symbol)
             return ref
 
-        if self._refs_resizable_ is True and index >= len(self._refs_):
-            self._refs_ += ([None] * (1 + index - len(self._refs_)))
+        ref = self._get_refs(index)
 
-        if self._get_refs(index) is None:
+        if ref is None:
             lex_env = self.lexical_environment()
             ref = lex_env.get_identifier_reference(symbol)
             if ref.is_unresolvable_reference() is True:
                 return ref
             self._set_refs(index, ref)
 
-        return self._get_refs(index)
+        return ref
 
     def forget_ref(self, symbol, index):
         self._set_refs(index, None)
 
 
-class GlobalExecutionContext(ExecutionContext):
+class _DynamicExecutionContext(ExecutionContext):
+    def __init__(self, stack_size):
+        ExecutionContext.__init__(self, stack_size)
+        self._dyn_refs_ = [None]
+
+    def _get_refs(self, index):
+        self._resize_refs(index)
+        return self._dyn_refs_[index]
+
+    def _set_refs(self, index, value):
+        self._resize_refs(index)
+        self._dyn_refs_[index] = value
+
+    def _resize_refs(self, index):
+        if index >= len(self._dyn_refs_):
+            self._dyn_refs_ += ([None] * (1 + index - len(self._dyn_refs_)))
+
+
+class GlobalExecutionContext(_DynamicExecutionContext):
     def __init__(self, code, global_object, strict=False):
         stack_size = code.estimated_stack_size()
 
-        ExecutionContext.__init__(self, stack_size)
+        _DynamicExecutionContext.__init__(self, stack_size)
 
         self._code_ = code
         self._strict_ = strict
@@ -166,11 +190,11 @@
         self.declaration_binding_initialization()
 
 
-class EvalExecutionContext(ExecutionContext):
+class EvalExecutionContext(_DynamicExecutionContext):
     def __init__(self, code, calling_context=None):
         stack_size = code.estimated_stack_size()
 
-        ExecutionContext.__init__(self, stack_size)
+        _DynamicExecutionContext.__init__(self, stack_size)
         self._code_ = code
         self._strict_ = code.strict
 
@@ -191,7 +215,6 @@
 
 class FunctionExecutionContext(ExecutionContext):
     _immutable_fields_ = ['_scope_', '_calling_context_']
-    _refs_resizable_ = False
 
     def __init__(self, code, formal_parameters=[], argv=[], this=newundefined(), strict=False, scope=None, w_func=None):
         from js.jsobj import W_BasicObject
@@ -203,7 +226,6 @@
         ExecutionContext.__init__(self, stack_size, env_size)
 
         self._code_ = code
-        self._formal_parameters_ = formal_parameters
         self._argument_values_ = argv
         self._strict_ = strict
         self._scope_ = scope
@@ -234,9 +256,9 @@
         return self._argument_values_
 
 
-class SubExecutionContext(ExecutionContext):
+class SubExecutionContext(_DynamicExecutionContext):
     def __init__(self, parent):
-        ExecutionContext.__init__(self)
+        _DynamicExecutionContext.__init__(self, 0)
         self._parent_context_ = parent
 
     def stack_append(self, value):
@@ -261,6 +283,7 @@
         self._code_ = code
         self._strict_ = code.strict
         self._expr_obj_ = expr_obj
+        self._dynamic_refs = []
 
         from js.lexical_environment import ObjectEnvironment
         parent_environment = parent_context.lexical_environment()
@@ -273,16 +296,16 @@
         self.declaration_binding_initialization()
 
 
-class CatchExecutionContext(ExecutionContext):
+class CatchExecutionContext(_DynamicExecutionContext):
     def __init__(self, code, catchparam, exception_value, parent_context):
         self._code_ = code
         self._strict_ = code.strict
         self._parent_context_ = parent_context
 
         stack_size = code.estimated_stack_size()
-        env_size = code.env_size() + 1  # neet do add one for the arguments object
+        #env_size = code.env_size() + 1  # neet do add one for the arguments object
 
-        ExecutionContext.__init__(self, stack_size, env_size)
+        _DynamicExecutionContext.__init__(self, stack_size)
 
         parent_env = parent_context.lexical_environment()
 
diff --git a/js/functions.py b/js/functions.py
--- a/js/functions.py
+++ b/js/functions.py
@@ -2,6 +2,7 @@
 
 
 class JsBaseFunction(object):
+    _settled_ = True
     eval_code = False
     function_code = False
     configurable_bindings = False
@@ -63,6 +64,7 @@
 
         args = ctx.argv()
         this = ctx.this_binding()
+        assert isinstance(self, JsNativeFunction)
         res = self._function_(this, args)
         w_res = _w(res)
         compl = ReturnCompletion(value=w_res)
@@ -98,6 +100,7 @@
         from js.jscode import JsCode
         assert isinstance(js_code, JsCode)
         self._js_code_ = js_code
+        self._js_code_.compile()
         self._stack_size_ = js_code.estimated_stack_size()
         self._symbol_size_ = js_code.symbol_size()
 
@@ -122,8 +125,10 @@
         return code.variables()
 
     def functions(self):
+        # XXX tuning
         code = self.get_js_code()
-        return code.functions()
+        functions = code.functions()
+        return functions
 
     def params(self):
         code = self.get_js_code()
diff --git a/js/jscode.py b/js/jscode.py
--- a/js/jscode.py
+++ b/js/jscode.py
@@ -16,8 +16,7 @@
     else:
         return '%d: %s' % (pc, 'end of opcodes')
 
-#jitdriver = JitDriver(greens=['pc', 'self'], reds=['ctx'], get_printable_location = get_printable_location, virtualizables=['ctx'])
-jitdriver = JitDriver(greens=['pc', 'debug', 'self'], reds=['result', 'ctx'], get_printable_location=get_printable_location)
+jitdriver = JitDriver(greens=['pc', 'debug', 'self'], reds=['result', 'ctx'], get_printable_location=get_printable_location, virtualizables=['ctx'])
 
 
 def ast_to_bytecode(ast, symbol_map):
@@ -32,7 +31,7 @@
 
 
 class JsCode(object):
-    _immutable_fields_ = ['_oppcodes_', '_symbols_']
+    _immutable_fields_ = ['compiled_opcodes[*]', '_symbols', 'parameters[*]']
 
     """ That object stands for code of a single javascript function
     """
@@ -46,6 +45,7 @@
         self.updatelooplabel = []
         self._estimated_stack_size = -1
         self._symbols = symbol_map
+        self.parameters = symbol_map.parameters[:]
 
     def variables(self):
         return self._symbols.variables
@@ -62,8 +62,9 @@
     def symbol_for_index(self, index):
         return self._symbols.get_symbol(index)
 
+    @jit.unroll_safe
     def params(self):
-        return self._symbols.parameters
+        return [p for p in self.parameters]
 
     #@jit.elidable
     def estimated_stack_size(self):
@@ -71,7 +72,7 @@
         if self._estimated_stack_size == -1:
             max_size = 0
             moving_size = 0
-            for opcode in self.opcodes:
+            for opcode in self.compiled_opcodes:
                 moving_size += opcode.stack_change()
                 max_size = max(moving_size, max_size)
             assert max_size >= 0
@@ -167,7 +168,9 @@
         if self.has_labels:
             self.remove_labels()
 
+    def compile(self):
         self.unlabel()
+        self.compiled_opcodes = [o for o in self.opcodes]
 
     def remove_labels(self):
         """ Basic optimization to remove all labels and change
@@ -192,10 +195,11 @@
     @jit.elidable
     def _get_opcode(self, pc):
         assert pc >= 0
-        return self.opcodes[pc]
+        return self.compiled_opcodes[pc]
 
+    @jit.elidable
     def _opcode_count(self):
-        return len(self.opcodes)
+        return len(self.compiled_opcodes)
 
     def run(self, ctx):
         from js.object_space import object_space
@@ -203,8 +207,6 @@
         from js.completion import NormalCompletion, is_completion, is_return_completion, is_empty_completion
         from js.opcodes import BaseJump
 
-        self.unlabel()
-
         if self._opcode_count() == 0:
             return NormalCompletion()
 
diff --git a/js/jsobj.py b/js/jsobj.py
--- a/js/jsobj.py
+++ b/js/jsobj.py
@@ -179,7 +179,7 @@
     _type_ = 'object'
     _class_ = 'Object'
     _extensible_ = True
-    _immutable_fields_ = ['_type_', '_class_', '_extensible_']
+    _immutable_fields_ = ['_type_', '_class_']  # TODO why need _primitive_value_ here???
 
     def __init__(self):
         from js.object_space import newnull
@@ -532,6 +532,8 @@
 
 
 class W__PrimitiveObject(W_BasicObject):
+    _immutable_fields_ = ['_primitive_value_']
+
     def __init__(self, primitive_value):
         W_BasicObject.__init__(self)
         self.set_primitive_value(primitive_value)
@@ -832,7 +834,7 @@
 
 
 class W__Function(W_BasicFunction):
-    _immutable_fields_ = ['_type_', '_class_', '_extensible_', '_scope_', '_params_', '_strict_', '_function_']
+    _immutable_fields_ = ['_type_', '_class_', '_extensible_', '_scope_', '_params_[*]', '_strict_', '_function_']
 
     def __init__(self, function_body, formal_parameter_list=[], scope=None, strict=False):
         W_BasicFunction.__init__(self)
@@ -874,12 +876,11 @@
         from js.completion import Completion
 
         code = self.code()
-        argn = self.formal_parameters()
+        jit.promote(code)
         strict = self._strict_
         scope = self.scope()
 
         ctx = FunctionExecutionContext(code,
-                                       formal_parameters=argn,
                                        argv=args,
                                        this=this,
                                        strict=strict,
@@ -910,6 +911,7 @@
 class W_Arguments(W__Object):
     _class_ = 'Arguments'
 
+    @jit.unroll_safe
     def __init__(self, func, names, args, env, strict=False):
         from js.object_space import _w
         W__Object.__init__(self)
@@ -919,22 +921,22 @@
 
         from js.object_space import object_space
         _map = object_space.new_obj()
-        mapped_names = []
+        mapped_names = _new_map()
         indx = _len - 1
         while indx >= 0:
             val = args[indx]
             put_property(self, unicode(str(indx)), val, writable=True, enumerable=True, configurable=True)
             if indx < len(names):
                 name = names[indx]
-                if strict is False and name not in mapped_names:
-                    mapped_names.append(name)
+                if strict is False and not mapped_names.contains(name):
+                    mapped_names = mapped_names.add(name)
                     g = make_arg_getter(name, env)
                     p = make_arg_setter(name, env)
                     desc = PropertyDescriptor(setter=p, getter=g, configurable=True)
                     _map.define_own_property(unicode(str(indx)), desc, False)
             indx = indx - 1
 
-        if len(mapped_names) > 0:
+        if not mapped_names.empty():
             self._paramenter_map_ = _map
 
         if strict is False:
diff --git a/js/lexical_environment.py b/js/lexical_environment.py
--- a/js/lexical_environment.py
+++ b/js/lexical_environment.py
@@ -11,7 +11,6 @@
     exists = envRec.has_binding(identifier)
     if exists:
         ref = Reference(base_env=envRec, referenced=identifier, strict=strict)
-        jit.promote(ref)
         return ref
     else:
         outer = lex.outer_environment
diff --git a/js/object_map.py b/js/object_map.py
--- a/js/object_map.py
+++ b/js/object_map.py
@@ -3,7 +3,7 @@
 
 class Map(object):
     NOT_FOUND = -1
-    _immutable_fields_ = ['index', 'back', 'name', 'forward_pointers']
+    _immutable_fields_ = ['index', 'back', 'name']
 
     def __init__(self):
         self.index = self.NOT_FOUND
@@ -40,6 +40,12 @@
     def _key(self):
         return (self.name)
 
+    def empty(self):
+        return True
+
+    def len(self):
+        return self.index
+
     @jit.elidable
     def add(self, name):
         assert self.lookup(name) == self.NOT_FOUND
@@ -84,6 +90,9 @@
             n = self.back.delete(name)
             return n.add(self.name)
 
+    def empty(self):
+        return False
+
 
 ROOT_MAP = MapRoot()
 
diff --git a/js/opcodes.py b/js/opcodes.py
--- a/js/opcodes.py
+++ b/js/opcodes.py
@@ -5,11 +5,12 @@
 from pypy.rlib.rarithmetic import intmask
 
 from js.jsobj import put_property
+from pypy.rlib import jit
 
 
 class Opcode(object):
     _settled_ = True
-    _immutable_fields_ = ['_stack_change']
+    _immutable_fields_ = ['_stack_change', 'funcobj']
     _stack_change = 1
 
     def __init__(self):
@@ -171,6 +172,7 @@
     def __init__(self, counter):
         self.counter = counter
 
+    @jit.unroll_safe
     def eval(self, ctx):
         from js.object_space import object_space
         array = object_space.new_array()
@@ -206,7 +208,7 @@
 
 
 class LOAD_FUNCTION(Opcode):
-    #_immutable_fields_ = ['funcobj']
+    _immutable_fields_ = ['funcobj']
 
     def __init__(self, funcobj):
         self.funcobj = funcobj
@@ -233,11 +235,13 @@
     def __init__(self, counter):
         self.counter = counter
 
+    @jit.unroll_safe
     def eval(self, ctx):
         from js.object_space import object_space
         w_obj = object_space.new_obj()
         for _ in range(self.counter):
-            name = ctx.stack_pop().to_string()
+            top = ctx.stack_pop()
+            name = top.to_string()
             w_elem = ctx.stack_pop()
             put_property(w_obj, name, w_elem, writable=True, configurable=True, enumerable=True)
         ctx.stack_append(w_obj)
@@ -307,7 +311,7 @@
 
     def eval(self, ctx):
         from js.object_space import newstring
-        ref = ctx.get_ref(self.name)
+        ref = ctx.get_ref(self.name, self.index)
         if ref.is_unresolvable_reference():
             var_type = u'undefined'
         else:
@@ -800,14 +804,9 @@
 class LOAD_ITERATOR(Opcode):
     _stack_change = 0
 
-    def eval(self, ctx):
-        exper_value = ctx.stack_pop()
-        obj = exper_value.ToObject()
+    # separate function because jit should trace eval but not iterator creation.
+    def _make_iterator(self, obj):
         props = []
-
-        from js.jsobj import W_BasicObject
-        assert isinstance(obj, W_BasicObject)
-
         properties = obj.named_properties()
         TimSort(properties).sort()
 
@@ -817,8 +816,19 @@
                 props.append(_w(key))
 
         props.reverse()
+
         from js.jsobj import W_Iterator
         iterator = W_Iterator(props)
+        return iterator
+
+    def eval(self, ctx):
+        exper_value = ctx.stack_pop()
+        obj = exper_value.ToObject()
+
+        from js.jsobj import W_BasicObject
+        assert isinstance(obj, W_BasicObject)
+
+        iterator = self._make_iterator(obj)
 
         ctx.stack_append(iterator)
 
diff --git a/test/test_environment_record.py b/test/test_environment_record.py
--- a/test/test_environment_record.py
+++ b/test/test_environment_record.py
@@ -4,12 +4,12 @@
 
 class TestDeclarativeEnvironmentRecord(object):
     def test_create_mutable_binding(self):
-        env_rec = DeclarativeEnvironmentRecord()
+        env_rec = DeclarativeEnvironmentRecord(size=1)
         env_rec.create_mutuable_binding(u'foo', True)
         assert env_rec.has_binding(u'foo') is True
 
     def test_set_and_get_mutable_binding(self):
-        env_rec = DeclarativeEnvironmentRecord()
+        env_rec = DeclarativeEnvironmentRecord(size=1)
         env_rec.create_mutuable_binding(u'foo', True)
         env_rec.set_mutable_binding(u'foo', 42, False)
         assert env_rec.get_binding_value(u'foo') == 42
diff --git a/test/test_jsfunction.py b/test/test_jsfunction.py
--- a/test/test_jsfunction.py
+++ b/test/test_jsfunction.py
@@ -50,11 +50,12 @@
         var_idx = symbol_map.add_variable(u'a')
 
         code = JsCode(symbol_map)
+        code.parameters = [u'a']
         code.emit('LOAD_VARIABLE', var_idx, u'a')
         code.emit('RETURN')
 
         f = JsFunction(u'foo', code)
-        ctx = FunctionExecutionContext(f, argv=[_w(42)], formal_parameters=[u'a'])
+        ctx = FunctionExecutionContext(f, argv=[_w(42)])
 
         res = f.run(ctx)
         assert res.value == _w(42)


More information about the pypy-commit mailing list