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

Add new sketch verifier to FES #7293

Merged
merged 5 commits into from
Oct 13, 2024
Merged

Conversation

sproutleaf
Copy link

@sproutleaf sproutleaf commented Sep 30, 2024

Addresses #7269

Changes:

  • Add a new sketch_verifier.js file to .../core/friendly_errors, this will replace current sketch_reader.js
  • sketch_verifier.js can now read user's code and extract user-defined variables and fucntions
  • Add a test file that covers all the existing functionalities

Screenshots of the change:

PR Checklist

@sproutleaf sproutleaf marked this pull request as ready for review October 2, 2024 02:29
package.json Outdated
@@ -23,6 +23,7 @@
"version": "1.9.4",
"dependencies": {
"colorjs.io": "^0.5.2",
"espree": "^10.2.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea to go with Espree (and manual traverse of AST) or are there plans to further investigate alternatives?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my own experiments, Espree is consistently faster than Acorn, but I just dug around and it seems that now Espree is built on top of Acorn, and extends Acorn for additional functionalities. I feel like maybe given larger files / over time, Acorn might be more efficient? Since what we require from a parser is pretty basis and not an extended capability.

And I totally forgot that we could traverse in a more efficient way! Regardless of which parser we choose, we can leverage on a traversal utility library, likeacorn-walk for acorn or estraverse for espree.

Let me know what you think! I'm happy to do more formal benchmarking / experiments and have some sort of report / documentation before updating the implementation too.

cc: @davepagurek

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can potentially do a bit of exploration for Espress vs Acorn, but the other things is whether using a linter is possible for this case or would manual AST traversal be the more realistic solution.

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, nice work on the tests so far!

*/
fn.fetchScript = async function (script) {
if (script.src) {
const contents = await fetch(script.src).then((res) => res.text());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a chance this request will fail depending on access control policies of the server, we might want to wrap lines 16/17 in a try/catch. If there's an error, maybe we can just log the error and return an empty string?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah CORS is a potential problem here. I'm thinking would it be possible to catch the error, print a friendly message about it, then degrade into a lower version of the script content grabbing eg. the '' + window.setup technique used in the current sketch reader?

That way there is still some checks going on while advise on enabling CORS on the server will still be available.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a TODO as a future item!

*/
fn.getUserCode = async function () {
const scripts = document.querySelectorAll('script');
const userCodeScript = scripts[scripts.length - 1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's a more robust way to get the user's script, since e.g. some sketches are spread across multiple files, or might import other addon libraries. We definitely don't need to be perfect here, but since multi-file sketches are pretty common on OpenProcessing, maybe we could do something like, filter out all the scripts that look like library code, e.g. because their src attribute matches /p5(\.\w+)\.js/, and then run the parser on the remaining scripts?

We could also exclude srcs that come from a known CDN domain like cdnjs.cloudflare.com or cdn.jsdelivr.net or unpkg.com. This is what OpenProcessing uses for its additional library toggles, so we could exclude more library code that way, although with some false negatives still.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way I was thinking about is to only grab from <script> tag that are in <body> which assumes that library import <script> tags are usually included in the <head> element while user code are included in the <body> tag, but that is mostly convention and not a guarantee so I'm not sure if it would be a good solution.

The other thing is that, if I remember correctly, if any <script> tag is using type=module they shouldn't be grabbed as well because these are scoped automatically.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a TODO!

function traverse(node) {
const { type, declarations, id, init } = node;

switch (type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely less common, but ClassDeclaration nodes might also be something to check

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

src/core/friendly_errors/sketch_verifier.js Outdated Show resolved Hide resolved
const category = init && ['ArrowFunctionExpression', 'FunctionExpression'].includes(init.type)
? 'functions'
: 'variables';
userDefinitions[category].push(id.name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to give a line number too, although just thinking out loud, it looks like espree gives us a character range, so we'd have to count the number of \ns in preceding the start of the range of the node to get the line, which is a little annoying.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used acorn config to get locations for the lines

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Acorn update looks great!

@davepagurek davepagurek merged commit ef1b09c into processing:dev-2.0 Oct 13, 2024
2 checks passed
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

Successfully merging this pull request may close these issues.

3 participants