Anonymous avatar Anonymous committed 81d2fad

Fixes global handling of "function foo() {}"

JavaScript acts a bit odd in this case in that the body of 'foo' is passed to
JS_Class.add_property but not JS_Class.set_property. For now I added an add_prop
method that special cases when the passed in value is a function and delegates
to set_prop.

While I was at it, I went ahead and added support for deleting global properties
as well. If your global handler has __delitem__ defined, it will now get called
for the JavaScript statement "delete foo;"

Thanks to Riccardo Pelizzi for the bug report.

Comments (0)

Files changed (4)

     * Reported bug in Context.max_time
     * Better test for test_exceeds_time
     * Reported missing pkg-config requirement
+
+Riccardo Pelizzi
+    * Bug report for global handlers and "function foo() {}" syntax.
+

spidermonkey/context.c

 #include <jscntxt.h>
 
 JSBool
+add_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval)
+{
+    JSObject* obj = NULL;
+
+    if(!JSVAL_IS_OBJECT(*rval)) return JS_TRUE;
+
+    obj = JSVAL_TO_OBJECT(*rval);
+    if(JS_ObjectIsFunction(jscx, obj)) return set_prop(jscx, jsobj, key, rval);
+    return JS_TRUE;
+}
+
+JSBool
+del_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval)
+{
+    Context* pycx = NULL;
+    PyObject* pykey = NULL;
+    PyObject* pyval = NULL;
+    JSBool ret = JS_FALSE;
+
+    pycx = (Context*) JS_GetContextPrivate(jscx);
+    if(pycx == NULL)
+    {
+        JS_ReportError(jscx, "Failed to get Python context.");
+        goto done;
+    }
+
+    // Bail if there's no registered global handler.
+    if(pycx->global == NULL)
+    {
+        ret = JS_TRUE;
+        goto done;
+    }
+
+    // Bail if the global doesn't have a __delitem__
+    if(!PyObject_HasAttrString(pycx->global, "__delitem__"))
+    {
+        ret = JS_TRUE;
+        goto done;
+    }
+
+    pykey = js2py(pycx, key);
+    if(pykey == NULL) goto done;
+
+    if(PyObject_DelItem(pycx->global, pykey) < 0) goto done;
+
+    ret = JS_TRUE;
+
+done:
+    Py_XDECREF(pykey);
+    Py_XDECREF(pyval);
+    return ret;
+}
+
+JSBool
 get_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval)
 {
     Context* pycx = NULL;
 js_global_class = {
     "JSGlobalObjectClass",
     JSCLASS_GLOBAL_FLAGS,
-    JS_PropertyStub,
-    JS_PropertyStub,
+    add_prop,
+    del_prop,
     get_prop,
     set_prop,
     JS_EnumerateStub,
     assert a == b, "%r != %r" % (a, b)
 
 def ne(a, b):
+    print "%r" % a
+    print "%r" % b
     assert a != b, "%r == %r" % (a, b)
 
 def lt(a, b):
         raise AssertionError("Function %s did not raise %s" % (
             func_name, exctype.__name__))
 
+def is_js_object(obj):
+    assert isinstance(obj, spidermonkey.Object), \
+            "%r is not an instance of spdermonkey.Object." % obj

tests/test-global.py

     t.eq(cx.execute("foo;"), 71);
     t.eq(glbl["foo"], 71)
 
+class FunctionTest(object):
+    def __init__(self):
+        self.data = {}
+    def __getitem__(self, key):
+        return self.data[key]
+    def __setitem__(self, key, value):
+        self.data[key] = value
+
+@t.rt()
+def test_py_set_function_global(rt):
+    glbl = FunctionTest()
+    cx = rt.new_context(glbl)
+    cx.execute("function foo() {};")
+    t.is_js_object(glbl["foo"])
+
 class ActiveGlobal(object):
     def __init__(self):
         self.data = {}
         self.data[key] = value * 2
 
 @t.rt()
+def test_py_no_del_item(rt):
+    glbl = ActiveGlobal()
+    cx = rt.new_context(glbl)
+    cx.execute('foo = 4;')
+    t.eq(glbl.data["foo"], 8)
+    cx.execute("delete foo;")
+    t.isin("foo", glbl.data)
+
+class ActiveGlobalWithDel(ActiveGlobal):
+    def __delitem__(self, key):
+        del self.data[key]
+
+@t.rt()
+def test_py_del_global(rt):
+    glbl = ActiveGlobalWithDel()
+    cx = rt.new_context(glbl)
+    cx.execute("foo = 4;")
+    t.eq(glbl.data["foo"], 8)
+    cx.execute("delete foo;")
+    t.isnotin("foo", glbl.data)
+
+@t.rt()
 def test_py_with_active_global(rt):
     glbl = ActiveGlobal()
     cx = rt.new_context(glbl)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.