Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: add method to call sandboxed functions at current state of interpreter #37

Closed
BrownBear2 opened this issue Sep 1, 2015 · 7 comments

Comments

@BrownBear2
Copy link
Contributor

This function will take a sandbox typed function and execute it in a child interpreter. When it's done it will run a given callback function.

The function will create a scope for the pseudo-function, apply all arguments, remove the return statement at the end and execute the function in a new interpreter. Once the interpreter is done it will call back. Note: this needs the callback and convenience patches I submitted earlier.

# call a sandbox function at the current state of the interpreter
# fn: sandbox type function
# args: any native arguments to the function
# done: callback to be run when the function call is done
Interpreter::call = (fn, args..., done) ->
  scope = @createScope fn.node.body, fn.parentScope
  for p, i in fn.node.params
    @setProperty scope, @createPrimitive(p.name), @convertToPseudo(args[i])

  argsList = @createObject @ARRAY
  for arg, i in args
    @setProperty argsList, @createPrimitive(i), @convertToPseudo(arg)

  @setProperty scope, "arguments", argsList

  # remove returns from callbacks
  [..., last] = fn.node.body.body
  if last.type == "ReturnStatement"
    last.type = "ExpressionStatement"
    last.expression = last.argument
    delete last.argument

  funcState =
    node: fn.node.body
    scope: scope
    thisExpression: @stateStack[0].funcThis_

  ip = new Interpreter ""
  ip.stateStack.unshift funcState
  ip.run done
  return
@binarez
Copy link

binarez commented Nov 12, 2015

I'm wondering if it would be useful to provide an option for this feature and permit execution in the current interpreter in addition to running it in a child interpreter. This would allow for a REPL console to change the interpreter's state. I can see that being useful for a debugger, to change a variable's value.

@BrownBear2
Copy link
Contributor Author

I'm not sure if I understand what you mean, but this being run in a child interpreter is a minor detail. It's still using the same variable scope of the parent interpreter. The main difference is that the child interpreter has it's own stack and therefore knows when it's done with executing the given function.

@NeilFraser
Copy link
Owner

I just added a new function that should help. Here's the documentation (not published yet):

Additional JavaScript code may be added at any time (frequently used to interactively call previously defined functions):

  myInterpreter.appendCode('foo();');

After appending code, you can run/step the interpreter more.

@BrownBear2
Copy link
Contributor Author

Hi Neil, nice to see you so active in this project 😄

Does your change run the appended code at the current state and return after it's done? Could you also add another method to allow for added AST instead of code?

Thanks for your effort!

@NeilFraser
Copy link
Owner

This project gets brief periods of activity then prolonged stretches of neglect. The cycle seems to be every six months or so.

The new appendCode does not run the code automatically, it simply adds it to the bottom of the existing tree. As discussed in Issue #39 I don't want to make assumptions on how code is run. You may use run exclusively, but many developers will be single stepping or following other execution models.

That's an interesting question about adding AST rather than string code. What's the use case for that? Currently the interpreter is designed to accept string code at both initialization and appendCode.

@BrownBear2
Copy link
Contributor Author

No worries, I tried to help you out and handle some of the issues while you were distracted.

I use the interpreter to allow for a script interface to my app, that can be programmed synchronously. So the user can call my script API aka bound native a-/synchronous functions and give them callbacks. I then want to call these callbacks from the native functions. For example:

loadUserByName "John", (user) ->
  user.foo = "bar"

The loadUserByName function would be a native function:

# calling this function pauses the interpreter
loadUserByName = (username, callback) ->
  # load the user
  users.findByName username, (user) ->
    # call the user-defined callback
    interpreter.call callback, user, ->
      ...
      # when done, resume the normal program
      interpreter.resume()

@thejohncrafter
Copy link

I implemented a function (queueCall) based on your work in #102

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants