<NME> test_immut_cache.py
<BEF> from testing.test_interpreter import BaseTestInterpreter


class TestFunctionCache(BaseTestInterpreter):

    def test_declare_function_call(self):
        output = self.run('''
        function myf2197123($a, $b) { return $a + $b; }
        echo myf2197123(10, 20);
        ''')
        assert self.space.int_w(output[0]) == 30
        cell = self.space.global_function_cache.get_cell('myf2197123')
        assert cell.constant_value_is_currently_declared
        assert cell.constant_value is cell.currently_declared
        #
        output2 = self.run('''
        function myf2197123($a, $b) { return $a - $b; }
        echo myf2197123(100, 20);
        ''')
        assert self.space.int_w(output2[0]) == 80
        cell2 = self.space.global_function_cache.get_cell('myf2197123')
        assert cell2 is cell
        assert not cell2.constant_value_is_currently_declared
        assert cell2.constant_value is not cell2.currently_declared

    def test_has_definition(self):
        output = self.run('''
        define('fooBAR', 42);
        ''')
        assert self.space.global_constant_cache.has_definition('fooBAR')
        assert not self.space.global_constant_cache.has_definition('foobar')

<MSG> Merge pull request #61 from hippyvm/immut_cache_opt

Optimise immut cache for cache misses.
<DFF> @@ -1,5 +1,6 @@
 from testing.test_interpreter import BaseTestInterpreter
 
+import uuid
 
 class TestFunctionCache(BaseTestInterpreter):
 
@@ -9,7 +10,7 @@ class TestFunctionCache(BaseTestInterpreter):
         echo myf2197123(10, 20);
         ''')
         assert self.space.int_w(output[0]) == 30
-        cell = self.space.global_function_cache.get_cell('myf2197123')
+        cell = self.space.global_function_cache.get_cell('myf2197123', object())
         assert cell.constant_value_is_currently_declared
         assert cell.constant_value is cell.currently_declared
         #
@@ -18,7 +19,24 @@ class TestFunctionCache(BaseTestInterpreter):
         echo myf2197123(100, 20);
         ''')
         assert self.space.int_w(output2[0]) == 80
-        cell2 = self.space.global_function_cache.get_cell('myf2197123')
+        cell2 = self.space.global_function_cache.get_cell('myf2197123', object())
+        assert cell2 is cell
+        assert not cell2.constant_value_is_currently_declared
+        assert cell2.constant_value is not cell2.currently_declared
+
+    def test_declare_class(self):
+        class_name = "MyClass%s" % uuid.uuid4().hex
+        class_cache = self.space.global_class_cache
+
+        self.run("class %s { function f() { return 666;} };" % class_name)
+        cell = class_cache.get_cell(class_name, class_cache.version)
+
+        assert cell.constant_value_is_currently_declared
+        assert cell.constant_value is cell.currently_declared
+
+        self.run("class %s { function f() { return 667;} };" % class_name)
+        cell2 = class_cache.get_cell(class_name, class_cache.version)
+
         assert cell2 is cell
         assert not cell2.constant_value_is_currently_declared
         assert cell2.constant_value is not cell2.currently_declared
@@ -29,3 +47,25 @@ class TestFunctionCache(BaseTestInterpreter):
         ''')
         assert self.space.global_constant_cache.has_definition('fooBAR')
         assert not self.space.global_constant_cache.has_definition('foobar')
+
+    def test_nonexistent_constant(self):
+        class_name = "MyClass%s" % uuid.uuid4().hex
+
+        class_cache = self.space.global_class_cache
+
+        cell = class_cache.get_cell(class_name, class_cache.version)
+        assert cell is None
+
+    def test_nonexistent_constant_then_defined_later(self):
+        class_name = "MyClass%s" % uuid.uuid4().hex
+
+        class_cache = self.space.global_class_cache
+
+        cell = class_cache.get_cell(class_name, class_cache.version)
+        assert cell is None
+
+        self.run("class %s { function f() { return 666;} };" % class_name)
+        cell = class_cache.get_cell(class_name, class_cache.version)
+
+        assert cell.constant_value_is_currently_declared
+        assert cell.constant_value is cell.currently_declared

{'commit': 'diff --git a/hippy/builtin.py b/hippy/builtin.py\nindex 69e5af6f..5bdea4ce 100644\n--- a/hippy/builtin.py\n+++ b/hippy/builtin.py\n@@ -986,9 +986,7 @@ def microtime(space,   is_float=False):\n \n @wrap([\'interp\', str])\n def function_exists(interp, funcname):\n-    try:\n-        interp.lookup_function(funcname)\n-    except KeyError:\n+    if interp.lookup_function(funcname) is None:\n         return interp.space.w_False\n     return interp.space.w_True\n \ndiff --git a/hippy/debugger.py b/hippy/debugger.py\nindex 2580163f..be7d57b9 100644\n--- a/hippy/debugger.py\n+++ b/hippy/debugger.py\n@@ -242,11 +242,10 @@ class Debugger(object):\n                 continue\n             if msg.command == "breakpoint":\n                 fname = msg.args[0].lower()\n-                try:\n-                    interp.lookup_function(fname)\n+                if interp.lookup_function(fname) is not None:\n                     self.breakpoints[fname] = True\n                     self.send_and_wait("echo", ["Breakpoint set %s" % fname])\n-                except KeyError:\n+                else:\n                     self.send_and_wait("warn",\n                                      ["error function %s not found" % fname])\n             elif msg.command == "force_breakpoint":\ndiff --git a/hippy/immut_cache.py b/hippy/immut_cache.py\nindex f9ee3525..7ad6e400 100644\n--- a/hippy/immut_cache.py\n+++ b/hippy/immut_cache.py\n@@ -14,6 +14,7 @@ class ImmutCell(object):\n     _class_key = (None, None, None)\n \n     def __init__(self, constant_value, is_builtin=False):\n+        assert constant_value is not None\n         self.constant_value = constant_value\n         self.currently_declared = constant_value\n         self.constant_value_is_currently_declared = True\n@@ -23,13 +24,14 @@ class ImmutCell(object):\n         if self.is_builtin or self.constant_value_is_currently_declared:\n             return self.constant_value     # constant-folded\n         else:\n-            result = self.currently_declared\n-            if result is None:\n-                raise KeyError\n-            return result\n+            return self.currently_declared\n+\n+\n+class GlobalImmutCacheVersion(object): pass\n \n \n class GlobalImmutCache(object):\n+    _immutable_fields_ = [\'version?\']\n \n     def __init__(self, space, initdict={}, force_lowcase=True):\n         self.space = space\n@@ -37,48 +39,52 @@ class GlobalImmutCache(object):\n         self.force_lowcase = force_lowcase\n         for key, value in initdict.items():\n             self.set_builtin(key, value)\n+        self.version = GlobalImmutCacheVersion()\n \n     def set_builtin(self, name, value):\n         self.set_cell(name, ImmutCell(value, is_builtin=True))\n \n     def reset(self):\n         # un-declare every non-builtin value\n+        # Note: we don\'t need to reset the cache version here, as we\'re not\n+        # deleting cell objects, merely emptying their contents.\n         for cell in self.all_cells.itervalues():\n             if not cell.is_builtin:\n                 cell.currently_declared = None\n                 cell.constant_value_is_currently_declared = False\n \n     @jit.elidable\n-    def get_cell(self, name):\n+    def get_cell(self, name, version):\n         if self.force_lowcase:\n             name = name.lower()\n-        return self.all_cells[name]\n+        try:\n+            return self.all_cells[name]\n+        except KeyError:\n+            return None\n \n     def set_cell(self, name, newcell):\n         if self.force_lowcase:\n             name = name.lower()\n         assert name not in self.all_cells\n         self.all_cells[name] = newcell\n+        self.version = GlobalImmutCacheVersion()\n \n     def has_definition(self, name):\n-        if self.force_lowcase:\n-            name = name.lower()\n-        try:\n-            cell = self.get_cell(name)\n-            return cell.currently_declared is not None\n-        except KeyError:\n+        cell = self.get_cell(name, jit.promote(self.version))\n+        if cell is None:\n             return False\n+        return cell.currently_declared is not None\n \n     def locate(self, name):\n-        # return the currently declared object, or raises KeyError\n-        cell = self.get_cell(name)\n+        cell = self.get_cell(name, jit.promote(self.version))\n+        if cell is None:\n+            return None\n         return cell.get_current_value()\n \n     def declare_new(self, name, value):\n         assert value is not None\n-        try:\n-            cell = self.get_cell(name)\n-        except KeyError:\n+        cell = self.get_cell(name, jit.promote(self.version))\n+        if cell is None:\n             cell = ImmutCell(value)\n             self.set_cell(name, cell)\n         else:\n@@ -91,11 +97,8 @@ class GlobalImmutCache(object):\n \n     def create_class(self, interp, name, decl, key):\n         "Special case for classes"\n-        try:\n-            cell = self.get_cell(name)\n-        except KeyError:\n-            pass\n-        else:\n+        cell = self.get_cell(name, jit.promote(self.version))\n+        if cell is not None:\n             if cell._class_key == key:\n                 cell.currently_declared = cell.constant_value\n                 cell.constant_value_is_currently_declared = True\ndiff --git a/hippy/interpreter.py b/hippy/interpreter.py\nindex 20ad7579..4fbf54cc 100644\n--- a/hippy/interpreter.py\n+++ b/hippy/interpreter.py\n@@ -293,7 +293,7 @@ class Interpreter(object):\n \n     def _class_get(self, class_name):\n         kls = self.space.global_class_cache.locate(class_name)\n-        assert isinstance(kls, ClassBase)\n+        assert kls is None or isinstance(kls, ClassBase)\n         return kls\n \n     def load_static(self, bc, arg):\n@@ -466,29 +466,31 @@ class Interpreter(object):\n         self.constant_names.append(name)\n \n     def locate_constant(self, name, complain=True):\n-        try:\n-            return self.lookup_constant(name)\n-        except KeyError:\n-            if not complain:\n-                return None\n-            self.notice("Use of undefined constant %s - "\n-                    "assumed \'%s\'" % (name, name))\n-            return self.space.wrap(name)\n+        c = self.lookup_constant(name)\n+        if c is not None:\n+            return c\n+        if not complain:\n+            return None\n+        self.notice("Use of undefined constant %s - "\n+                "assumed \'%s\'" % (name, name))\n+        return self.space.wrap(name)\n \n     def lookup_function(self, name):\n         if not name:\n-            raise KeyError\n+            return None\n         if name[0] == \'\\\\\':\n             name = name[1:]\n         func = self.space.global_function_cache.locate(name)\n+        if func is None:\n+            return None\n         assert isinstance(func, AbstractFunction)\n         return func\n \n     def locate_function(self, name):\n-        try:\n-            return self.lookup_function(name)\n-        except KeyError:\n-            self.fatal("Call to undefined function %s()" % name)\n+        func = self.lookup_function(name)\n+        if func is not None:\n+            return func\n+        self.fatal("Call to undefined function %s()" % name)\n \n     def get_this(self, frame):\n         w_this = frame.w_this\n@@ -511,12 +513,13 @@ class Interpreter(object):\n             name = name[1:]\n         if not name:\n             return None\n-        try:\n-            return self._class_get(name)\n-        except KeyError:\n+        kls = self._class_get(name)\n+        if kls is None:\n             if autoload:\n-                return self._autoload(name)\n-            return None\n+                kls = self._autoload(name)\n+            else:\n+                return None\n+        return kls\n \n     def _get_self_class(self):\n         contextclass = self.get_contextclass()\n@@ -575,10 +578,7 @@ class Interpreter(object):\n         if self.autoload_stack:\n             return self._autoload_from_stack(name)\n \n-        try:\n-            autoload_func = self.lookup_function(\'__autoload\')\n-        except KeyError:\n-            return None\n+        autoload_func = self.lookup_function(\'__autoload\')\n         if autoload_func is None:\n             return None\n         self._autoloading[cls_id] = None\n@@ -1024,9 +1024,8 @@ class Interpreter(object):\n         w_name = frame.pop().deref()\n         name = space.str_w(w_name)\n         w_base_name = frame.pop().deref()\n-        try:\n-            const = self.lookup_constant(name)\n-        except KeyError:\n+        const = self.lookup_constant(name)\n+        if const is None:\n             base_name = space.str_w(w_base_name)\n             const = self.locate_constant(base_name)\n         frame.push(const)\n@@ -1345,9 +1344,8 @@ class Interpreter(object):\n         w_name = frame.pop().deref()\n         name = space.str_w(w_name)\n         w_base_name = frame.pop().deref()\n-        try:\n-            func = self.lookup_function(name)\n-        except KeyError:\n+        func = self.lookup_function(name)\n+        if func is None:\n             func = self.getfunc(w_base_name, frame.w_this,\n                     frame.get_contextclass())\n         assert func is not None\n@@ -1556,16 +1554,15 @@ class Interpreter(object):\n     def declare_func(self, func):\n         name = func.name\n         func_id = func.get_identifier()\n-        try:\n-            func = self.lookup_function(func_id)\n+        ldfunc = self.lookup_function(func_id)\n+        if ldfunc is not None:\n+            func = ldfunc\n             if isinstance(func, Function):\n                 extra = \' (previously declared in %s:%d)\' % (\n                     func.bytecode.filename, func.bytecode.startlineno)\n             else:\n                 extra = \'\'\n             self.fatal("Cannot redeclare %s()%s" % (name, extra))\n-        except KeyError:\n-            pass\n         self.space.global_function_cache.declare_new(func_id, func)\n \n     def DECLARE_FUNC(self, bytecode, frame, space, arg, pc):\n@@ -1790,6 +1787,7 @@ class Interpreter(object):\n \n         if self._class_is_defined(name):\n             cls = self._class_get(name)\n+            assert cls is not None\n         else:\n             cls = None\n \ndiff --git a/hippy/module/reflections/function.py b/hippy/module/reflections/function.py\nindex 8f7ed29c..0a94b8e7 100644\n--- a/hippy/module/reflections/function.py\n+++ b/hippy/module/reflections/function.py\n@@ -42,6 +42,7 @@ class NameWrapper(Wrapper):\n         self.space = interp.space\n         self.name = interp.space.str_w(name)\n         self.function = interp.lookup_function(self.name)\n+        assert self.function is not None\n \n     def get_name(self):\n         return self.space.wrap(self.function.get_fullname())\ndiff --git a/hippy/module/reflections/parameter.py b/hippy/module/reflections/parameter.py\nindex b0b96ac0..a642e144 100644\n--- a/hippy/module/reflections/parameter.py\n+++ b/hippy/module/reflections/parameter.py\n@@ -18,6 +18,7 @@ def construct(interp, this, function, parameter):\n     if isinstance(function, W_ConstStringObject):\n         name = interp.space.str_w(function)\n         function  = interp.lookup_function(name)\n+        assert function is not None\n         signature = function.get_signature()\n \n         this.ref_parameter = signature.args[parameter]\ndiff --git a/hippy/module/standard/array/funcs.py b/hippy/module/standard/array/funcs.py\nindex 28aa1ef2..96f37d76 100644\n--- a/hippy/module/standard/array/funcs.py\n+++ b/hippy/module/standard/array/funcs.py\n@@ -1758,6 +1758,7 @@ def natsort(interp, w_ref):\n         return space.w_False\n     pairs = w_arr.as_pair_list(space)\n     w_natsort_func = interp.lookup_function(\'strnatcmp\')\n+    assert w_natsort_func is not None\n     _sort(space, pairs, sort_type=2, elem=VALUE, cmp=w_natsort_func)\n     w_ref.store(space.new_array_from_pairs(pairs))\n     return space.w_True\n@@ -1776,6 +1777,7 @@ def natcasesort(interp, w_ref):\n         return space.w_Null\n     pairs = w_arr.as_pair_list(space)\n     w_natsort_func = interp.lookup_function(\'strnatcasecmp\')\n+    assert w_natsort_func is not None\n     _sort(space, pairs, sort_type=2, elem=VALUE, cmp=w_natsort_func)\n     w_ref.store(space.new_array_from_pairs(pairs))\n     return space.w_True\ndiff --git a/hippy/objspace.py b/hippy/objspace.py\nindex 1402e982..9cb9226f 100644\n--- a/hippy/objspace.py\n+++ b/hippy/objspace.py\n@@ -807,11 +807,11 @@ class ObjSpace(object):\n             clsname = name[:pos]\n             methname = name[pos + 2:]\n             return self._get_callback_from_class(clsname, methname)\n-        try:\n-            return self.ec.interpreter.lookup_function(name)\n-        except KeyError:\n-            raise InvalidCallback("function \'%s\' not found or invalid "\n-                                    "function name" % (name))\n+        func = self.ec.interpreter.lookup_function(name)\n+        if func is not None:\n+            return func\n+        raise InvalidCallback("function \'%s\' not found or invalid "\n+                              "function name" % (name))\n \n     def _get_callback_from_class(self, clsname, methname):\n         interp = self.ec.interpreter\ndiff --git a/hippy/phpcompiler.py b/hippy/phpcompiler.py\nindex 4a372428..9a9e9780 100644\n--- a/hippy/phpcompiler.py\n+++ b/hippy/phpcompiler.py\n@@ -116,9 +116,7 @@ class PHPLexerWrapper(LexerWrapper):\n         #\n         # hack: copies the end position to a constant\n         if self.interp is not None:\n-            try:\n-                self.interp.lookup_constant(\'__COMPILER_HALT_OFFSET__\')\n-            except KeyError:\n+            if self.interp.lookup_constant(\'__COMPILER_HALT_OFFSET__\') is None:\n                 if self.mode == MODE_LITERAL:\n                     endpos = self.startindex\n                 else:\ndiff --git a/testing/test_immut_cache.py b/testing/test_immut_cache.py\nindex 10b6afb3..fc0a0d7c 100644\n--- a/testing/test_immut_cache.py\n+++ b/testing/test_immut_cache.py\n@@ -1,5 +1,6 @@\n from testing.test_interpreter import BaseTestInterpreter\n \n+import uuid\n \n class TestFunctionCache(BaseTestInterpreter):\n \n@@ -9,7 +10,7 @@ class TestFunctionCache(BaseTestInterpreter):\n         echo myf2197123(10, 20);\n         \'\'\')\n         assert self.space.int_w(output[0]) == 30\n-        cell = self.space.global_function_cache.get_cell(\'myf2197123\')\n+        cell = self.space.global_function_cache.get_cell(\'myf2197123\', object())\n         assert cell.constant_value_is_currently_declared\n         assert cell.constant_value is cell.currently_declared\n         #\n@@ -18,7 +19,24 @@ class TestFunctionCache(BaseTestInterpreter):\n         echo myf2197123(100, 20);\n         \'\'\')\n         assert self.space.int_w(output2[0]) == 80\n-        cell2 = self.space.global_function_cache.get_cell(\'myf2197123\')\n+        cell2 = self.space.global_function_cache.get_cell(\'myf2197123\', object())\n+        assert cell2 is cell\n+        assert not cell2.constant_value_is_currently_declared\n+        assert cell2.constant_value is not cell2.currently_declared\n+\n+    def test_declare_class(self):\n+        class_name = "MyClass%s" % uuid.uuid4().hex\n+        class_cache = self.space.global_class_cache\n+\n+        self.run("class %s { function f() { return 666;} };" % class_name)\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+\n+        assert cell.constant_value_is_currently_declared\n+        assert cell.constant_value is cell.currently_declared\n+\n+        self.run("class %s { function f() { return 667;} };" % class_name)\n+        cell2 = class_cache.get_cell(class_name, class_cache.version)\n+\n         assert cell2 is cell\n         assert not cell2.constant_value_is_currently_declared\n         assert cell2.constant_value is not cell2.currently_declared\n@@ -29,3 +47,25 @@ class TestFunctionCache(BaseTestInterpreter):\n         \'\'\')\n         assert self.space.global_constant_cache.has_definition(\'fooBAR\')\n         assert not self.space.global_constant_cache.has_definition(\'foobar\')\n+\n+    def test_nonexistent_constant(self):\n+        class_name = "MyClass%s" % uuid.uuid4().hex\n+\n+        class_cache = self.space.global_class_cache\n+\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+        assert cell is None\n+\n+    def test_nonexistent_constant_then_defined_later(self):\n+        class_name = "MyClass%s" % uuid.uuid4().hex\n+\n+        class_cache = self.space.global_class_cache\n+\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+        assert cell is None\n+\n+        self.run("class %s { function f() { return 666;} };" % class_name)\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+\n+        assert cell.constant_value_is_currently_declared\n+        assert cell.constant_value is cell.currently_declared\n', 'commit_subject': 'Merge pull request #61 from hippyvm/immut_cache_opt', 'repo_name': 'hippyvm/hippyvm', 'committer': {'name': 'Carl Friedrich Bolz', 'email': '7341442bb50cf86d06eae82082467ad4c6611e5a@gmx.de', 'time_sec': '1409674117', 'tz_offset': '120', 'date': {'seconds': '1409674117'}}, 'commit_tree': 'e6e6dee8048b821add65d7500f24abc3f05fe89f', 'commit_message': 'Merge pull request #61 from hippyvm/immut_cache_opt\n\nOptimise immut cache for cache misses.', 'language_name': 'PHP', 'license': 'mit', 'hunks': '@@ -1,5 +1,6 @@\n from testing.test_interpreter import BaseTestInterpreter\n \n+import uuid\n \n class TestFunctionCache(BaseTestInterpreter):\n \n@@ -9,7 +10,7 @@ class TestFunctionCache(BaseTestInterpreter):\n         echo myf2197123(10, 20);\n         \'\'\')\n         assert self.space.int_w(output[0]) == 30\n-        cell = self.space.global_function_cache.get_cell(\'myf2197123\')\n+        cell = self.space.global_function_cache.get_cell(\'myf2197123\', object())\n         assert cell.constant_value_is_currently_declared\n         assert cell.constant_value is cell.currently_declared\n         #\n@@ -18,7 +19,24 @@ class TestFunctionCache(BaseTestInterpreter):\n         echo myf2197123(100, 20);\n         \'\'\')\n         assert self.space.int_w(output2[0]) == 80\n-        cell2 = self.space.global_function_cache.get_cell(\'myf2197123\')\n+        cell2 = self.space.global_function_cache.get_cell(\'myf2197123\', object())\n+        assert cell2 is cell\n+        assert not cell2.constant_value_is_currently_declared\n+        assert cell2.constant_value is not cell2.currently_declared\n+\n+    def test_declare_class(self):\n+        class_name = "MyClass%s" % uuid.uuid4().hex\n+        class_cache = self.space.global_class_cache\n+\n+        self.run("class %s { function f() { return 666;} };" % class_name)\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+\n+        assert cell.constant_value_is_currently_declared\n+        assert cell.constant_value is cell.currently_declared\n+\n+        self.run("class %s { function f() { return 667;} };" % class_name)\n+        cell2 = class_cache.get_cell(class_name, class_cache.version)\n+\n         assert cell2 is cell\n         assert not cell2.constant_value_is_currently_declared\n         assert cell2.constant_value is not cell2.currently_declared\n@@ -29,3 +47,25 @@ class TestFunctionCache(BaseTestInterpreter):\n         \'\'\')\n         assert self.space.global_constant_cache.has_definition(\'fooBAR\')\n         assert not self.space.global_constant_cache.has_definition(\'foobar\')\n+\n+    def test_nonexistent_constant(self):\n+        class_name = "MyClass%s" % uuid.uuid4().hex\n+\n+        class_cache = self.space.global_class_cache\n+\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+        assert cell is None\n+\n+    def test_nonexistent_constant_then_defined_later(self):\n+        class_name = "MyClass%s" % uuid.uuid4().hex\n+\n+        class_cache = self.space.global_class_cache\n+\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+        assert cell is None\n+\n+        self.run("class %s { function f() { return 666;} };" % class_name)\n+        cell = class_cache.get_cell(class_name, class_cache.version)\n+\n+        assert cell.constant_value_is_currently_declared\n+        assert cell.constant_value is cell.currently_declared\n', 'addition_count': 42, 'deletion_count': 2, 'src_file': 'a/testing/test_immut_cache.py', 'tgt_file': 'b/testing/test_immut_cache.py', 'file_extension': '.py', 'before_file': "from testing.test_interpreter import BaseTestInterpreter\n\n\nclass TestFunctionCache(BaseTestInterpreter):\n\n    def test_declare_function_call(self):\n        output = self.run('''\n        function myf2197123($a, $b) { return $a + $b; }\n        echo myf2197123(10, 20);\n        ''')\n        assert self.space.int_w(output[0]) == 30\n        cell = self.space.global_function_cache.get_cell('myf2197123')\n        assert cell.constant_value_is_currently_declared\n        assert cell.constant_value is cell.currently_declared\n        #\n        output2 = self.run('''\n        function myf2197123($a, $b) { return $a - $b; }\n        echo myf2197123(100, 20);\n        ''')\n        assert self.space.int_w(output2[0]) == 80\n        cell2 = self.space.global_function_cache.get_cell('myf2197123')\n        assert cell2 is cell\n        assert not cell2.constant_value_is_currently_declared\n        assert cell2.constant_value is not cell2.currently_declared\n\n    def test_has_definition(self):\n        output = self.run('''\n        define('fooBAR', 42);\n        ''')\n        assert self.space.global_constant_cache.has_definition('fooBAR')\n        assert not self.space.global_constant_cache.has_definition('foobar')\n", 'message': 'Merge pull request #61 from hippyvm/immut_cache_opt\n\nOptimise immut cache for cache misses.', 'author_name': 'Carl Friedrich Bolz', 'lang': 'py'}