changeset 11:665f69f61716

Some refactorings
author Atul Varma <varmaa@toolness.com>
date Thu, 10 Sep 2009 13:51:04 -0700
parents 97f3b2242505
children e4978bd08bfa
files pydertron.py
diffstat 1 files changed, 80 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/pydertron.py	Thu Sep 10 11:09:14 2009 -0700
+++ b/pydertron.py	Thu Sep 10 13:51:04 2009 -0700
@@ -162,7 +162,7 @@
         obj = cx.call_function(this, jsobject, tuple(arglist))
         return self._wrap_to_python(obj)
 
-def format_stack(js_stack):
+def format_stack(js_stack, open=open):
     """
     Returns a formatted Python-esque stack traceback of the given
     JS stack.
@@ -235,7 +235,7 @@
     loading and executing scripts.
     """
 
-    def __init__(self, watchdog=watchdog):
+    def __init__(self, fs, watchdog=watchdog):
         rt = pydermonkey.Runtime()
         cx = rt.new_context()
         root_proto = cx.new_object()
@@ -246,16 +246,23 @@
         cx.set_throw_hook(self._throwhook)
         watchdog.add_context(cx)
 
+        self.fs = fs
         self.rt = rt
         self.cx = cx
         self.curr_exc = None
         self.py_stack = None
         self.js_stack = None
+        self.__modules = {}
         self.__py_to_js = {}
         self.__type_protos = {}
-        self.root_proto = root_proto
+        self.__globals = {}
+        self.__root_proto = root_proto
         self.root = self.wrap_jsobject(root, root)
 
+    def set_globals(self, **globals):
+        self.__globals.update(globals)
+        self._install_globals(self.root)
+
     def finish(self):
         """
         Cleans up all resources used by the sandbox, breaking any reference
@@ -443,13 +450,45 @@
             obj[name] = contents[name]
         return obj
 
-    def evaluate(self, code, filename='<string>', lineno=1):
-        retval = self.cx.evaluate_script(self.root.wrapped_jsobject,
-                                         code, filename, lineno)
-        return self.wrap_jsobject(retval)
+    def get_calling_script(self):
+        frame = self.cx.get_stack()['caller']
+        curr_script = None
+        while frame and curr_script is None:
+            if frame['function'] and frame['function'].filename:
+                curr_script = frame['function'].filename
+            elif frame['script']:
+                curr_script = frame['script'].filename
+            frame = frame['caller']
+
+        if curr_script is None:
+            raise RuntimeError("Can't find calling script")
+        return curr_script
+
+    def _install_globals(self, object):
+        for name in self.__globals:
+            object[name] = self.__globals[name]
+        object['require'] = self._require
+
+    @jsexposed(name='require')
+    def _require(self, path):
+        filename = self.fs.find_module(self.get_calling_script(), path)
+        if not filename:
+            raise pydermonkey.error('Module not found: %s' % path)
+
+        if not filename in self.__modules:
+            cx = self.cx
+            module = cx.new_object(None, self.__root_proto)
+            cx.init_standard_classes(module)
+            exports = cx.new_object()
+            cx.define_property(module, 'exports', exports)
+            self._install_globals(self.wrap_jsobject(module))
+            self.__modules[filename] = self.wrap_jsobject(exports)
+            contents = self.fs.open(filename).read()
+            cx.evaluate_script(module, contents, filename, 1)
+        return self.__modules[filename]
 
     def run_script(self, contents, filename='<string>', lineno=1,
-                   callback=None):
+                   callback=None, stderr=sys.stderr):
         """
         Runs the given JS script, returning 0 on success, -1 on failure.
         """
@@ -463,82 +502,42 @@
                 callback(self.wrap_jsobject(result))
             retval = 0
         except pydermonkey.error, e:
-            print format_stack(self.js_stack)
-            print e.args[1]
+            params = dict(
+                stack_trace = format_stack(self.js_stack, self.fs.open),
+                error = e.args[1]
+                )
+            stderr.write("%(stack_trace)s\n%(error)s\n" % params)
         except InternalError, e:
-            print "An internal error occurred."
-            traceback.print_tb(e.exc_info[2])
-            print e.exc_info[1]
+            stderr.write("An internal error occurred.\n")
+            traceback.print_tb(e.exc_info[2], None, stderr)
+            stderr.write("%s\n" % e.exc_info[1])
         return retval
 
-class SecurableModuleLoader(object):
-    """
-    Loader for CommonJS SecurableModules.
-    """
-
-    def __init__(self, sandbox, root_dir, globals):
+class SandboxedFileSystem(object):
+    def __init__(self, root_dir):
         self.root_dir = root_dir
-        self.sandbox = sandbox
-        self.globals = globals
-        self.modules = {}
-        self._install_globals(sandbox.root)
 
-    def _install_globals(self, object):
-        for name in self.globals:
-            object[name] = self.globals[name]
-        object['require'] = self.require
-
-    def _get_calling_script(self):
-        frame = self.sandbox.cx.get_stack()['caller']
-        curr_script = None
-        while frame and curr_script is None:
-            if frame['function'] and frame['function'].filename:
-                curr_script = frame['function'].filename
-            elif frame['script']:
-                curr_script = frame['script'].filename
-            frame = frame['caller']
-
-        if curr_script is None:
-            raise RuntimeError("Can't find calling script")
-        return curr_script
+    def find_module(self, curr_script, path):
+        if path.startswith("."):
+            base_dir = os.path.dirname(curr_script)
+        else:
+            base_dir = self.root_dir
 
-    def _load_module(self, filename):
-        sandbox = self.sandbox
-        cx = sandbox.cx
-
-        module = cx.new_object(None, sandbox.root_proto)
-        cx.init_standard_classes(module)
-        exports = cx.new_object()
-        cx.define_property(module, 'exports', exports)
-        self._install_globals(sandbox.wrap_jsobject(module))
-        self.modules[filename] = sandbox.wrap_jsobject(exports)
-
-        contents = open(filename).read()
-        cx.evaluate_script(module, contents, filename, 1)
+        ospath = path.replace('/', os.path.sep)
+        filename = os.path.join(base_dir, "%s.js" % ospath)
+        filename = os.path.normpath(filename)
+        if (filename.startswith(self.root_dir) and
+            (os.path.exists(filename) and
+             not os.path.isdir(filename))):
+            return filename
+        else:
+            return None
 
-    @jsexposed
-    def require(self, path):
-        curr_script = self._get_calling_script()
-        curr_dir = os.path.split(curr_script)[0]
-
-        if (not path.startswith(".") or
-            not curr_dir.startswith(self.root_dir)):
-            curr_dir = self.root_dir
-        ospath = path.replace('/', os.path.sep)
-        filename = os.path.join(curr_dir, "%s.js" % ospath)
-        filename = os.path.normpath(filename)
-        if (not filename.startswith(self.root_dir) or
-            not (os.path.exists(filename) and
-                 not os.path.isdir(filename))):
-            raise pydermonkey.error('Module not found: %s' % path)
-
-        if filename not in self.modules:
-            self._load_module(filename)
-        return self.modules[filename]
+    def open(self, filename):
+        return open(filename, 'r')
 
 def run_test(name, libpath):
-    print name
-    sandbox = JsSandbox()
+    sandbox = JsSandbox(SandboxedFileSystem(libpath))
 
     stats = [0, 0]
 
@@ -550,11 +549,11 @@
             stats[1] += 1
         print "%s %s" % (message, label)
 
-    system = sandbox.new_object()
-    system['print'] = jsprint
-    globals = {'sys': system,
-               'environment': sandbox.new_object()}
-    loader = SecurableModuleLoader(sandbox, libpath, globals)
+    sandbox.set_globals(
+        sys = sandbox.new_object(**{'print': jsprint}),
+        environment = sandbox.new_object()
+        )
+
     retval = sandbox.run_script("require('program')")
     sandbox.finish()
     print
@@ -591,6 +590,6 @@
     import gc
     gc.collect()
     if pydermonkey.get_debug_info()['runtime_count']:
-        print "WARNING: JS runtime was not destroyed."
+        sys.stderr.write("WARNING: JS runtime was not destroyed.\n")
 
     sys.exit(totals[1])