JS-Interpreter
JS-Interpreter copied to clipboard
Proposal: conveniance functions
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