You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Current situation
The current public API of Boa, that can be found here for Boa 0.8.0 and can be seen offline running cargo doc --open for the master branch, is far from optimal. We have 3 global functions:
exec(): That creates a new lexer, parser, Realm and interpreter, and isolates the execution of a given JavaScript string, and return a string with the result.
forward(): That will receive an already initialized interpreter and a JavaScript string and will lex, parse and execute this code in the initialized interpreter, and return the result as a string.
forward_val(): That will work the same way as forward() but will return a ResultValue, which will indicate if the execution was successful or not.
Then, we have the following modules public:
syntax: which includes:
ast: the AST implementation (nodes, constants, keywords, builtin types, positions, spans, tokens, operations, punctuators...), with some re-exports to make life easier.
parser: The parser implementation, with its error and a couple of parsers (Script and ScriptBody), which turns a vector of tokens into a StatementList, that can then be executed with an interpreter.
realm, which includes the implementation of a Realm, an interesting helper function to register a global function, and makes public the global object, environment, and the lexical environment. This in theory allows anyone to add stuff to the global environment, but it's a bit cumbersome.
builtins: Where we have the builtin objects such as Array, Number, JSON and more, and also the implementations of Value and object properties. And the initialization function.
environment, which has modules for each of the environments and the global trait, everything exposed because the realm has public members so that they can be modified.
exec: We only contain the Interpreter, with just two public functions: new() and to_string(). The first creates a new interpreter with a given Realm, and the second one converts a Value to a string. It also contains the Executable trait, that allows calling run() on any AST node passing the interpreter so that it runs it.
Finally, we also have some re-exports to make things easier to use.
Problem description
When a user wants to use Boa for their projects, I can imagine the following use cases:
They want to interpret JavaScript from a file or a stored String and get the result.
They want to interpret JavaScript that is coming from a socket with a Read interface and get the result by streaming the bytes.
They want to extend our engine by providing new global functions and objects, so that with the JavaScript they can either perform more rich things in their setup (for example by providing an API for a browser window), or because they want to add JavaScript helpers for common actions in their setup. This also includes things as being the scripting language of a complex platform.
They want to change the global object, for example, because they want it to be the window or a frame.
I cannot think of anything else, but there might be others. In this context, our current API is very poor. It does allow most of this (except for the Read interface, that is being worked on in #432). But, it is very difficult to do it if you are not an expert in Boa.
Proposed solution
I think we should go for a global Boa structure. This structure should have a default() function (implementing the default() trait, that would setup a clean environment for the execution. Then a interpret() function that would receive either a &str or a u8 (Read) stream (we might need two functions) and return a ResultValue. We could then call to_string() on this if we needed to show it, for example, or on the Ok/Err variants.
It should also have a new() or with_global_obj() function that would require a custom global object. Note that we don't need to expose all the Gc stuff here, as we would be owning the global object.
One option is for it to receive the full Realm, but I think it's best if we didn't expose the complexity of a Realm. In any case, that global object should be wrapped, to provide an easy to use API:
It should have a register_global_object() function, that would just receive a name and a NativeObject. The current register_global_func() should be kept (maybe renamining to register_global_function().
The NativeObject should just implement a trait, and should easily be boxable, or we could use dynamic dispatch. This trait should allow adding native methods to the object, and should implement a way of retrieving and setting fields. I want to be vague about this, because #419 should give us a clear path.
Related to #440.
Current situation
The current public API of Boa, that can be found here for Boa 0.8.0 and can be seen offline running
cargo doc --openfor themasterbranch, is far from optimal. We have 3 global functions:exec(): That creates a new lexer, parser, Realm and interpreter, and isolates the execution of a given JavaScript string, and return a string with the result.forward(): That will receive an already initialized interpreter and a JavaScript string and will lex, parse and execute this code in the initialized interpreter, and return the result as a string.forward_val(): That will work the same way asforward()but will return aResultValue, which will indicate if the execution was successful or not.Then, we have the following modules public:
syntax: which includes:ast: the AST implementation (nodes, constants, keywords, builtin types, positions, spans, tokens, operations, punctuators...), with some re-exports to make life easier.lexer: The tokenizer/lexer implementation, that turns a string into a vector of tokens. (this is changing in Started with the new lexer implementation #432)parser: The parser implementation, with its error and a couple of parsers (ScriptandScriptBody), which turns a vector of tokens into aStatementList, that can then be executed with an interpreter.realm, which includes the implementation of aRealm, an interesting helper function to register a global function, and makes public the global object, environment, and the lexical environment. This in theory allows anyone to add stuff to the global environment, but it's a bit cumbersome.builtins: Where we have the builtin objects such asArray,Number,JSONand more, and also the implementations ofValueand object properties. And the initialization function.environment, which has modules for each of the environments and the global trait, everything exposed because therealmhas public members so that they can be modified.exec: We only contain theInterpreter, with just two public functions:new()andto_string(). The first creates a new interpreter with a givenRealm, and the second one converts aValueto a string. It also contains theExecutabletrait, that allows callingrun()on any AST node passing the interpreter so that it runs it.Finally, we also have some re-exports to make things easier to use.
Problem description
When a user wants to use Boa for their projects, I can imagine the following use cases:
Readinterface and get the result by streaming the bytes.globalobject, for example, because they want it to be thewindowor aframe.I cannot think of anything else, but there might be others. In this context, our current API is very poor. It does allow most of this (except for the
Readinterface, that is being worked on in #432). But, it is very difficult to do it if you are not an expert in Boa.Proposed solution
I think we should go for a global
Boastructure. This structure should have adefault()function (implementing thedefault()trait, that would setup a clean environment for the execution. Then ainterpret()function that would receive either a&stror au8(Read) stream (we might need two functions) and return aResultValue. We could then callto_string()on this if we needed to show it, for example, or on theOk/Errvariants.It should also have a
new()orwith_global_obj()function that would require a custom global object. Note that we don't need to expose all the Gc stuff here, as we would be owning the global object.One option is for it to receive the full
Realm, but I think it's best if we didn't expose the complexity of aRealm. In any case, that global object should be wrapped, to provide an easy to use API:register_global_object()function, that would just receive a name and aNativeObject. The currentregister_global_func()should be kept (maybe renamining toregister_global_function().The
NativeObjectshould just implement atrait, and should easily be boxable, or we could use dynamic dispatch. This trait should allow adding native methods to the object, and should implement a way of retrieving and setting fields. I want to be vague about this, because #419 should give us a clear path.So, what do you think? What could we improve?