Placeholder Image

字幕表 動画を再生する

  • Wow.

  • I mean, thank you by the way to JSConf EU for having me here.

  • It's an absolute privilege to be at the last JSConf EU of its time.

  • Can you guys give it up for JSConf EU?

  • I can't believe it.

  • [Applause].

  • I cannot believe it.

  • So, you know, biggest JavaScript conference in Europe, I think biggest JavaScript conference

  • in the world.

  • Going to be talking about building a JS engine.

  • No pressure at all!

  • It will be completely fine!

  • I should probably introduce myself.

  • I'm Jason Williams.

  • I'm a senior software engineer at Bloomberg in the UK in London, dare I say Europe?

  • We're still clinging on.

  • I used to work at the BBC as well.

  • Are there BBC people?

  • Here?

  • Yes, wow!

  • I've also been involved in sort of TC39, so I've been working on the proposal over the

  • past year which allows you to get on board all of your promises, regardless of the state

  • whether fulfilled or rejected, and you have control then to do what you want with them.

  • On the Rust side, I've also been involved in Rust as well.

  • I used to be the maintainer of the Sublime Text Rust plugin.

  • I've since moved into the Rust DevTools team, and they focus on sort of ID plugins, tooling,

  • and then the Rust language server as well which is pretty cool which is a single process

  • that handles things like debugging, hinting, and all the editors can make use of that.

  • So I thought doing some specifications with JavaScript, really interesting, you know,

  • working a little bit on Rust as well, my two favourite things, how can I marry these two

  • things together?

  • My two languages that I love so much?

  • How can I bring these together?

  • And by the way, the biggest JavaScript conference in the world, I know it's a ballsy move talking

  • about Rust, but I'm going to go with it.

  • Bear with me.

  • I thought why not build a JavaScript engine written in Rust?

  • Take the fun and accessibility of JavaScript and then power that with Rust, and then see

  • what we can get?

  • It was a bit ambitious.

  • It was quite checking.

  • I actually started off by looking for a place where I could contribute to this.

  • Nowhere existed, like no-one had built one in Rust before, at least two years ago, anyway.

  • There was one single project called js.rs, and the maintainer abandoned it.

  • It was six years old.

  • I tried to build it, and there were 650 errors.

  • It was basically because he made it at a time when Rust wasn't 1.0 and so the syntax had

  • changed and moved on, and it was faster to build one from scratch than it was to try

  • and get this working.

  • That's exactly what I did.

  • I'm not an expert by the way in parsing, or writing compilers, or any of that.

  • I mean, I even struggle to read CSS in the morning, so I'm not an expert in parsing source

  • code, so I thought let's start from somewhere.

  • I thought it was a jump.

  • Doing small things in Rust, and going from like "hello, world" to full-on JavaScript

  • interpreter was probably a bit of a jump.

  • I probably should have done something in between like a to-do MVC, or something like that.

  • I knew I was on to something here that I had a good language, something to work with.

  • There were a couple of reasons that I wanted to use Rust to build this engine.

  • One was control.

  • One of the things Rust says to you we're going to give you the control you can have plus

  • all the safety.

  • And so I can utilise a sort of close to the metalness of Rust and do the things I need

  • to do, and then the compile time means I don't fall over myself.

  • It was quite fast, so it is built on top of LOVM which means the years of the LOVM compiler

  • they've done, Rust can tap into that and make use of it.

  • Sometimes, you can get faster CodeGen which produces C. There's memory safety as well.

  • Rust has an ownership system which is quite unique.

  • Basically, if you were passing a value into a function, the function is now the owner

  • of that value, so there can only be one owner to a value.

  • That means when Rust wants to get rid of data it can do so with 100 per cent confidence

  • because nothing else is pointing to that thing.

  • You don't have dangling pointers, or anything like that.

  • Currency, I might want to make use of this at some point.

  • Again, because of the ownership model, I won't have to worry too much about multiple threads,

  • trying to access the same value, and then what I need to worry about data races.

  • All of that is taken care of at compile time which means at run time, I'm not penalised

  • on performance.

  • So, yes I thought it's a good start.

  • Let's get going.

  • So I started.

  • About two years ago, I started, I made a Git Repo, an engine called Boa.

  • Someone asked me why I called that?

  • An evil boa in a zoo in Australia.

  • I thought I'm naming my engine after that.

  • I'm going to start with a high-level view here.

  • So the architecture that I'm going to go with for now is going to be a few things missing,

  • for those of you who are familiar with compilers, but the view that I'm going to start with

  • is taking some source code, and that can be from, say, a network, or in this case reading

  • from a file, basically.

  • Taking the source code, bringing it into as a string buffer, so just imagine a big string

  • of JavaScript.

  • And I'm going to use the scanner that I've made to break that down into some various

  • tokens.

  • And these tokens, they're basically like groups of characters that are bunched together to

  • have some sort of semantic meaning.

  • In this case, we've got function as a tone, or fu, or the opening parenthesis here.

  • We are going to send them on to the parser operation.

  • These expressions are different.

  • Expressions are hold other for example s.

  • If you've got the opening of a function, you've got function declaration.

  • The block inside that function, we've got some variable declaration, so the variable

  • declarations would be children to that function.

  • That's generally the idea.

  • Let's just step into that a little bit closer.

  • I'm going to take that line of JS here.

  • I showed this to someone earlier, and they were like, "You should have used const!"

  • Sorry, I forgot.

  • I'm going to start with Let.

  • Imagine you're scanning this, so the thing that reads the file has done its job now and

  • it's going to pass this through.

  • The first thing we are sitting on is the Let.

  • We will produce a token from that.

  • The token in this case is our object.

  • We've got the value.

  • We want to keep hold of the value.

  • That's useful.

  • We want to make a category for the tone.

  • In this case, it's a key word.

  • The reason I know that it is a key word is because I took a look on the spec, grabbed

  • key words and codified against that.

  • So, when I take a string like let that's got no quotes at the beginning, I know it's going

  • to be either an identifier, or maybe even a token, so what I start off by doing is checking

  • off against my list of key words, and I know that this one is a key word.

  • Conf does the same thing but it doesn't exist as a key word so we assume it's an identifier.

  • We are keeping track of the position.

  • The compiler doesn't really need that per se, but it's good if you're debugging and

  • you can output that there's a problem on this line, or if there is a token we're not expecting,

  • we can say there was an issue here, and it's on this column, this line number.

  • Punctuation gets its own category.

  • Again with, we want to keep the value and where we are and so on.

  • So here we've got.

  • Some punctuation, a string, and an identifier, and a key word.

  • We're going to place this into an array, and then pass it on to our parser.

  • This makes it easier for us to deal with when we're traversing through the source code.

  • If you want to look at Rust, don't worry about understanding the syntax or anything like

  • that, but I make use of Rust pattern-matching.

  • Think of Switch Case.

  • It is similar but more powerful.

  • The idea is that you can take a character, or anything, actually, and then the pattern-matching

  • allows you to do some destructuring on that.

  • You can say is it this or this?

  • You can do bindings which I don't have an example about here, or we can say is it a

  • digit, or is it alphabetic?

  • This this case, we're on let, so it is alphabetic, so we go into a state of trying to parse and

  • an identifier.

  • And then we take this, put it in a loop, and of through each character in our source code

  • and you do the same thing.

  • With each character you land on, that's a state.

  • It's a state machine going on.

  • If you're in a quote, it's a state of parsing a string.

  • If you're in a character, it's parsing an identifier and a key word, and so on.

  • I've got a demo.

  • This is something I did earlier.

  • I was going to do it live, but Paul Irish is around, so I just wanted to make sure that

  • he's not going to get interference, or anything!

  • This is a quick writing conf, equals JSConf.

  • We will output some tones.

  • Very similar to what I showed you earlier.

  • We have key words, an identifier, and our punctuation, et cetera.

  • This is now ready to go.

  • We're Aldershot Town done in this area.

  • With parsing, we're going to sort of take that array of tokens, and we are going to

  • try to build a tree structure now.

  • So we are going to start building expressions from these tokens.

  • The way we do that is similar to how we were iterating for the tokens as well.

  • With the characters, we had some pattern-matching going on.

  • We're going to do the same thing.

  • This time, though, we're able to pattern-match on tokens.

  • Now we can say we are sitting on a token that is representing a key word, or representing

  • an identifier.

  • If it is a key word, we can say this is the key word that we are currently sitting on.

  • A quick side note about pattern-matching.

  • It's been so successful in Rust, it's had a lot of success that there's been, there's

  • been a proposal in stage one to bring pattern-matching across into JavaScript.

  • Keep an eye on that.

  • In this case here, we are sitting on top of an "if", and we know our next token should

  • be an open parenthesis.

  • I've made a utility function here called expect punc, expect to have a premises or error out

  • at that point.

  • We can then parse our condition.

  • This is between the parenthesis, and we can make an expression there, and we can do the

  • same thing with the "if" body, and that is the expression underneath.

  • "Next" there chemicals it if the token is "else", which is optional.

  • We may or may not have angles.

  • If we do, we parse that as well.

  • After that, we've got three expressions: the condition, the body, and then potentially

  • the else, and we add those expressions underneath the if "if" expression, so you can start to

  • see the free structure we've got.

  • Don't worry about the syntax of this, but this is implemented using an enum.

  • The expressions are what they call variants.

  • Each variant can hold another expression.

  • You get the recursive structure, and the if variant holds an expression which is a condition

  • which is another expression that might hold other things.

  • The if body, and we wrap the "else" in an option which basically means we may or may

  • not have a value there.

  • It's basically a way of way of us checking to say is there angles?

  • If not, we move on and we get a non-value.

  • The box you can see as well, so these are like the types and the box is basically saying

  • that these are going to live on the heap.

  • And it will manage that for us.

  • So, take a look at this.

  • I've added a bit more code now.

  • We had a let const.

  • I've added more around that.

  • If we started with our function, we get a function declaration expression.

  • And as we work our way down, we get a let const, and that generates is a "let" generation

  • expression underneath the function, and the const expression is just a little, so that

  • is basically saying it's a value that we don't need to evaluate, it's already there.

  • That is also a child of the let declaration.

  • The reason for that is that you can declare variables without defining what they are.

  • In this case, we are defining it as well.

  • So we go down to our "if", and we have our if expression which holds an operation, a

  • condition, a then, and angles.

  • As I said earlier, the else was wrapped in an option, and we didn't use else in this

  • case so we just return as non.

  • Then we go into our if body which is the block expression, and we have another let declaration,

  • and that also has a concept underneath that as well.

  • The last thing is the return which gives us the return expression.

  • So the source code that you see on the left is what would generate the tree we have on

  • the right.

  • Again, a demo, so, hope you can run this.

  • And then there we go.

  • Yes.

  • And so there is a lot more going on there, and that is because I can only cover one more

  • on one slide, but there are more expressions.

  • In this case, there's a block expression at the top, and that represents the global scope,

  • and you've got the function declaration underneath.

  • But you kind of get the idea.

  • So, now we are going to try and evaluate the tree that we've generated.

  • And there are a couple of things that we need to do and bear in mind.

  • The idea is that we want to walk