JS-Interpreter icon indicating copy to clipboard operation
JS-Interpreter copied to clipboard

Proposal: conveniance functions

Open BrownBear2 opened this issue 10 years ago • 0 comments

Hi, I want to propose to add the following functions to the interpreter. They allow users to use the interpreter more easily. Excuse the coffee-script, you can use js2.coffee to convert them to JS if necessary.

# convert a value to pseudo values (for use inside the sandbox)
Interpreter::convertToPseudo = (value) ->
  if typeof value == "function"
    ast = acorn.parse "$ = " + value.toString()

    func = @createObject @FUNCTION
    func.node = ast.body[0].expression.right
    func.parentScope = @scope

    @setProperty func, 'length', @createPrimitive(func.node.params.length), true
    return func

  if typeof value != "object" or value == null
    return @createPrimitive value

  if value instanceof Array
    pseudoArray = @createObject @ARRAY
    for item, i in value
      @setProperty pseudoArray, i, @convertToPseudo item

    return pseudoArray

  pseudoObject = @createObject @OBJECT
  for key, val of value
    @setProperty pseudoObject, key, @convertToPseudo val

  return pseudoObject

# convert pseudo objects from the sandbox into real objects
Interpreter::convertToNative = (value) ->
  return value.data if value.isPrimitive

  if value.length? # array
    newArray = []
    for i in [0...value.length]
      newArray.push @convertToNative value.properties[i]

    return newArray

  if value.type == "function"
    return value

  newObject = {}
  for key, val of value.properties
    newObject[key] = @convertToNative val

  return newObject

# convert a list of arguments from pseudo to native (see convertToNative)
Interpreter::convertArgsToNative = (args...) ->
  nativeArgs = []
  for arg in args
    nativeArgs.push @convertToNative arg

  return nativeArgs

# fully wrap a native function to be used inside the interpreter
# parent: scope of the function to be added to
# name: name of the function in said scope
# fn: the native function
# thisObj: the `this` object the function should be called by
Interpreter::wrapNativeFn = (parent, name, fn, thisObj) ->
  thisIP = @
  @setProperty parent, name, @createNativeFunction (args...) ->
    thisObj ?= @ if [email protected] # don't convert window
    thisIP.convertToPseudo fn.apply thisObj, thisIP.convertArgsToNative args...
  return

# fully wrap an asynchronous native function, see wrapNativeFn
Interpreter::wrapNativeAsyncFn = (parent, name, fn, thisObj) ->
  thisIP = @
  @setProperty parent, name, @createAsyncFunction (args..., callback) ->
    thisObj ?= @ if [email protected] # don't convert window
    nativeArgs = thisIP.convertArgsToNative args...
    nativeArgs.unshift (result) -> callback thisIP.convertToPseudo(result), true
    fn.apply thisObj, nativeArgs
  return

# wrap a whole class, see wrapNativeFn (doesn't work with async functions)
# scope: the scope for the class to be added to
# name: name of the class in said scope
# $class: the native class instance
# fns: optional, list of names of functions to be wrapped
Interpreter::wrapClass = (scope, name, $class, fns) ->
  obj = @createObject @OBJECT
  @setProperty scope, name, obj

  if !fns?
    fns = []
    for key, fn of $class
      fns.push key if typeof fn == "function"

  for fn in fns
    @wrapNativeFn obj, fn, $class[fn], $class

  return

# transfer object from the sandbox to the outside by name
Interpreter::retrieveObject = (scope, name) ->
  return @convertToNative @getProperty scope, name

# transfer object from the outside into the sandbox by name
Interpreter::transferObject = (scope, name, obj) ->
  @setProperty scope, name, @convertToPseudo obj
  return

BrownBear2 avatar Sep 01 '15 09:09 BrownBear2