User-provided scripts

16 views
Skip to first unread message

rod...@codecaster.es

unread,
Mar 26, 2014, 10:38:53 AM3/26/14
to therub...@googlegroups.com
Hi all,

I am developing a custom CMS and I want users to be able to provide their own scripts.
A solution would be to use therubyracer as a sandbox, but for performance reasons it would be nice to know if scripts can be precompiled, stored and executed later without further parsing, and if this would make a real impact in performance or if it's enough to just eval() them.

I am using V8::C::ScriptData::PreCompile and I think its return value is what I need to store for later execution, but I don't know how to add data to that when I need to execute it.

A typical example would be to precompile an user-provided function (via exports probably) with ScriptData::PreCompile and then call it passing some environment data.

How can I accomplish that?

If you think that there is a better approach for this matter, please tell me. I could use another language or even a DSL as long as they are fast enough and secure.

Thanks in advance,
Rodrigo.

Cal Heldenbrand

unread,
May 20, 2014, 7:30:53 AM5/20/14
to therub...@googlegroups.com
Hi Rodrigo,

I needed a very similar solution.  I have a custom scripting language with bits of embedded javascript, and I wanted to precompile the js code for faster execution.  I dug into therubyracer tests, studied the code, and came up with the two functions below.  You'll want to run some speed tests though, because for very small scripts V8::Context::eval() seems to be just as fast as using a precompiled script.

(Note that @context is initialized in my class constructor using just  @context = V8::Context.new )

  # Precompile a javascript source.  This is like a first pass
  # phase compile.  The second pass compile is bound to the running
  # context instance.
  def precompile_js(source)
    source = source.encode("UTF-8")
    script_data = false
    @context.enter do
      # Precompile the source and return a ScriptData object
      data = V8::C::ScriptData::PreCompile(source, source.length)
      # Check for syntax/compile errors
      if data.HasError() == false
        # Binary data is in here -- store for later use
        # when creating a new Script object
        script_data = data.Data
      end
    end
    script_data
  end

  # Given a javascript source text, and precopmiled script data,
  # execute the code.  Results from the script are returned
  def exec_js(source, script_data)
    result = false
    # Holds a lock on the context, creates a handle, and
    # enters the context inside this block
    @context.enter do
      # Create a new script with a dummy name.  Filenames
      # could be reference here
      name = V8::C::String::New("<eval>")
      origin = V8::C::ScriptOrigin.new(name)
      # Create a new ScriptData object using our raw script_data
      data = V8::C::ScriptData::New(script_data, script_data.length)
      if data.HasError() == false
        # Creating a new Script will run the second-pass compile
        # that is bound to our currently running context instance
        script = V8::C::Script::New(source, origin, data)

        # Run it!
        result = script.Run()
      end
    end
    result
  end

Reply all
Reply to author
Forward
0 new messages