Skip to content

Confused by Scope API #4519

@rix0rrr

Description

@rix0rrr

Just to dabble, I'm trying to build my own bytecode VM on top of the Boa parser (to skip past the annoying things and get to the good stuff!).

I did expressions and at this point I'm getting to doing variables. It feels like I should be using the Scope API for that, but it's a little unclear to me how it works.

I've parsed the AST and I'm now traversing it, querying Scope objects when I'm encountering identifiers.

Is this even the right API?

First off, it feels to weird to me that I'm getting Syms (interned strings) out of the AST, but then have to pass JsString objects into the Scope API rather than those same interned strings. Seems a bit weird to have to reverse the interned string and then build a new class out of it, if all we need is a unique name that we already have access to?

Can't get to the bindings

I would like to access a Scope's bindings for example, or its unique_id but none of those are exposed, so I can't.

I don't understand the numbering rules

Right, as I'm traversing the AST, I'm keeping a stack of Scopes that I push and pop whenever I'm entering/leaving a scope. For example:

            Statement::Block(block) => {
                self.enter_scope(block.scope().unwrap().clone());

                self.compile_statement_list(block.statement_list())?;

                self.leave_scope();

                Ok(())
            }

Then, when I encounter a variable I call get_identifier_reference on the closest scope to resolve that identifier:

                let locator = self
                    .current_scope()
                    .scope
                    .get_identifier_reference(ident.sym().to_js_string(&self.interner));

Now I need to know in which of the scopes on my stack that variable was found in, so that I know where on my stack my local variable slot will live(*).

(*) Haven't gotten to closures yet, I know my model will have to change a little once I start doing that.

If I could get the unique_id out of a scope I could check my stack to find it, but as we already saw that's not accessible. Instead, I can get the BindingLocatorScope (which is based off of of the self.scope member) which seems to do something similar, but it seems to have weird values.

If I send in the following script:

var x = 3;
{
  let y = 4;
  {
    let z = 1;
    x * y *z
  }
}

And I call get_identifier_reference on every variable declaration and print it, I see the following:

IdentifierReference { locator: BindingLocator { name: "x", scope: 0, binding_index: 0, unique_scope_id: 0 }, lexical: false, escapes: true }
IdentifierReference { locator: BindingLocator { name: "y", scope: 1, binding_index: 0, unique_scope_id: 1 }, lexical: true, escapes: false }
IdentifierReference { locator: BindingLocator { name: "z", scope: 1, binding_index: 0, unique_scope_id: 2 }, lexical: true, escapes: false }

Looking at the Locator:

  • scope: 0 I understand, x is found in the global scope.
  • But according to these values both y and z are in scope 1 (mapping toBindingLocatorScope::GlobalDeclarative, whatever that may be). Even though it seems like they should be in 2 successive nested scopes.
  • It's also not the case that Boa thinks those nested scopes should just share a memory space, because both variables have binding_index: 0, which means they would end up sharing a stack slot in that scope, which also cannot be the intent.

At this point, I'm just utterly confused by what I should be using or looking at.

Can anyone give me any pointers on how I should be thinking about this, and how I should map an identifier to a scope?

Much appreciated, thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions