Mac OS X Essentials • 1:02:57
WebKit and the JavaScript Core framework support bridging between JavaScript and C. Learn practical techniques for extending your application's functionality by using JavaScript attributes and functions to enable internal scripting.
Speakers: Geoff Garen, Brady Eidson, Alice Liu
Unlisted on Apple Developer site
Transcript
This transcript has potential transcription errors. We are working on an improved version.
a >> Welcome to Extending Your Application with JavaScript. Introducing JavaScript Core. A new framework on Mac OS X. This is Session 157. W I'm Geoff Garen I am an engineer here at Apple and I am fluent in JavaScript, as well as Klingon.
( Laughter )
( Applause )
Increasingly, developers are using internal scripting in their applications. This is where you use script to implement essential features and components of application logic. One application that's been in the blog sphere lately that you may have heard of for doing this is Adobe Lightroom. Lightroom uses a scripting language to implement all or nearly all of its user interface. So if you click on a button in Lightroom a little bit of Lua script runs in response. Of course Lightroom is not the first application to use this technique. And Lua is not the first language to be used for it.
Computer game designers, for example, have been using internal scripting for some time now to control the behavior of non-player characters and also the animations of parts of the scenery. And many of the them invent their own languages. Quake invented Quake-C, which you may be familiar with. They're a bunch of others. Unreal and Unreal Tournament invented Unreal Script for themselves.
Conquest of the Aegean uses GameMonkey script, Age of Empires and Age of Mythology invented a custom version of Lisp, and Never Winter Nights uses NW Scripts. There are a bunch more. Another series of applications -- another category using internal scripting is scientific modelling environments. These are applications that have an underlying model engine and then they expose the engine through script. And you write your models in the scripting language. And you can use these to model a lot of different scientific phenomenon. Evolution, economics, global warming. Many of the hippie sciences that we enjoy studying here in San Francisco.
There are a bunch of these languages. There's Star Logo, which is a custom version of Logo, with the turtles that you may remember from kindergarten. There's Net Logo, which believe it or not is a custom version of Star Logo. Stella, they like at MIT, I suspect because it has a woman's name, and they don't see that much there.
( Laughter )
Graphical modelling environments are doing this too, so you see auto cat invented all the o lisp. Macromedia Director which we use here at Apple, using Lingo. Maya, another really famous 3-D rendering engine has it's own scripting language called the Maya embedded language. And finally, last but not least, even dinky little IRC clients are wanting to make themselves scriptable these days and having to invent their own languages. And MIRC, for example, invented MIRC script.
Now, these are all great applications. And it's great that they're using internal scripting. I think that they're better applications for it. Individually, these are all great scripting languages as well. I have programmed in a number of them, I've each worked on the underlying interpreters for some of them. But taken as a whole, this plethora of scripting languages is growing into a minor disaster.
Think of all the engineering time that could have been spent making these great applications even greater that was instead spent in inventing a new programming language, and then having to implement the underlying interpreter. And I can tell you from experience that is not easy. That's why I get paid the big bucks.
Thunk about all the scripting time that could have been spent writing interesting scripts for these applications that was instead spent learning the hot new scripting language of the day. Or not spent at all -- because somebody got fed up having to learn new scripting languages. And finally, consider all the bandwidth and processing power that was wasted having to download all of these interpreters along side their applications, launch them at the launch time of the application, and keep them all resident in memory.
And new scripting languages are being invented all the time to suit different people's interest, or the ideologies of the day. Here's one that I found recently on the Internet. It's called LOLCODE, and it was invented for the inevitable take over of the world by Internet-addicted kittens. So you can see how LOLCODE from the Filezorz example program. You begin with the manically Ha! There's a Please Open File command.
So you to have to be polite in LOLCODE, lest you be scratched. Awesome. Thanks. More politeness. There's an O Noes Clause to catch any errors. LOLCODE programmers love that. That's a great feature. And finally the final Okay, thanks, bye. And these guys even have the requisite O'Reilly Book.
( Applause )
Meow! So in our own software development at Apple we use internal scripting, and we needed to solve this problem of a plethora of languages. And we came up with a solution that's worked out really well for us. JavaScript. So that's really what this presentation is about.
You'll learn how we use JavaScript to give you some ideas for your own applications. Of the benefits that we've seen, both from internal scripting, and from using JavaScript for our internal scripting. And finally, you'll learn about JavaScript Core. A new framework that we're making available on Mac OS X so that you can imbed JavaScript in your own applications, if you like.
Here's how JavaScript Core fits in with the overall Mac OS X technologies. You can use it stand-alone, like so, and get just a JavaScript interpreter in your application. You can also use the WebKit framework, which has been around for some time now. And that's the full Web browser in a box. So it brings JavaScript Core along with it, and provides it's own high level Objective-C interface to the JavaScript language.
New with JavaScript Core being a stand-alone framework with its own API, if you have a WebKit programmer, you can now talk directly to the JavaScript engine to do things that were not previously possible through that high level interface in WebKit. So that's how this presentation will flow. I will talk about using JavaScript Core stand alone, but if you are a WebKit application developer or a widget plug-in developer, don't sleep through that part of the presentation, as Tim Hatcher likes to say, because it will be relevant to you. And I'll show you how to use it in your WebKit applications and the WebKit plug-ins at the end.
Okay. So let's get started with how we use JavaScript. Probably the place most people are most familiar with us using JavaScript for internal scripting is Dashboard. Dashboard widgets are mini applications, and the entire application control flow and UI response are written in JavaScript. Now you might think it's obvious that we might use JavaScript here, because we're using the Web stack. We're using HTML and CSS to render these mini applications as well.
But we get requests all the time for why didn't you choose Python as the programming language in Dashboard. That's my favorite language. How about Ruby Cocoa, an then I could use Interface Builder and App Kit in Dashboard. I invented a new custom version of Haskel and I would like to discuss it under NDA, because your widgets will be nothing with without it -- meow, meow, meow, meow.
( Laughter )
But we went with JavaScript and it's turned out to be a great success for us. Quartz Composer is another application using JavaScript. So Dashboard uses JavaScript to control application control flow like you see in Adobe Lightroom. Quartz Composer uses it in a different way to enable end-user programming in a modelling environment much like you see in Stella and Steve and Net Logo and Maya.
And what you do in Quartz Composer is you compose bits of graphics and logic into larger scale animations like the ones you see here. Quartz Composer shifts with hundreds of predefined interesting bits that you can plug in together. But ultimately, you want to describe arbitrary transformations. So that's what we needed a scripting language for, and we JavaScript for that. If you've been down to the graphics and media lab you can see some of the great Quartz Composer animations that look even better when you put them on, like.
27 L CD displays next to each other. But even if you haven't seen that you have probably seen some of the applications where you are using Quartz Composer. So, iChat uses Quartz Composer to animate the fake backgrounds behind you. And Time Machine also animates all the way back to the beginning of time the big magnets itself using Quartz Composer and JavaScript.
iDVD uses JavaScript for its menu themes. When you pop a DVD into a DVD player you see an animated menu. And redescribe those animations with JavaScript so we can dump in new animations and see how they look pretty easily. And so here, this pretty interesting. We're using JavaScript as a kind of an active data format, where rather than reading in a bunch of data about how the animation should work, we just load the animation in it, and it executes itself. This is a lot like game designers where they load an unclear character into memory and it executes itself and the scenery runs itself along side.
The installer uses JavaScript throughout installer packages, and the one that I'll call your attention to is that a package decides for itself whether it is capable of installing on the volumes of your system, as you see here. And it's a JavaScript that runs to make that determination. So once again here we're kind of using JavaScript as kind of an active data format, where, you know, instead of trying to read in complex registry keys or any of that other kind of craziness to decide for you whether your package can install on a certain volume. We just ask your package, it runs a script and gives us a yes or no answer.
Finally, iSync uses JavaScript as a full-blown plug-in architecture. iSync has default behavior for syncing devices with your Macintosh. But if you release a new device that maybe has iTunes play list, or new kinds of context or notes or things like that. You want to write your own plug-in syncing logic. So you write that in JavaScript and you use the underlying syncing framework to do the heavy lifting for you. And those are just the uses of JavaScript that I can tell you about without having to kill you.
( Laughter )
So we have a demo for you of another application using JavaScript to try to show you that it's not just us at Apple who have drunk the JavaScript punch. Third party applications are also integrating JavaScript, and a great example of that is Colloquy, an IRC client that we use on the WebKit team.
In contrast to MIRC that had to spend all that time inventing its own scripting language, Colloquy just builds on the existing JavaScript capabilities of Mac OS X. And it was a bit of a pioneer here because it using the WebKit framework, which has been around for some time now in order to do that. So without further ado, demo machine, please. Here's Brady Eidson.
( Applause )
Thank you, Jeff. As Jeff just mentioned, Colloquy is an IRC client that the Safari and WebKit team uses to interact with the open search community in open pound kit in free node, but we also use it on a private IRC server because we're spread out over many offices, many towns, even. So it helps us get our work done.
We need to finish up Leopard, we need to talk about new app features, and most importantly we need to coordinate and make sure we go to lunch and afternoon coffee at the right time. So I'm going to go ahead and hop in there really quick. This is our super-seeker panel. I am going to go ahead and -- uh-oh.
Little hiccup here. Bear with me. Relaunch. Just go -- okay. This is supposed to look a little better. This is where we would talk normally. I would say hi here, but to ping everyone in the room I would normally have to add their name and then say, hi, hi, hi, to 15 different people. But it's a lot easier if I can just type one command to greet everyone and say, hey, let's do lunch after of the session. So this is the slash announce. After relaunch. I will go ahead and relaunch and make sure everyone can see.
It's going, it's going, it's going. Bear with us here just a second. Okay. Here it goes. So let me try this one more time. Hi. Let's do lunch afterwards. Oh dear. Okay. So as you can see this quick command easily appended everyone's name in the room. And then added my message. Now slash announce is not an IRC command. It is not built into Colloquy. It is something I wrote myself in JavaScript -- I wrote myself in JavaScript to augment Colloquy's behavior. So let's go ahead and look at the code here.
As you can see it's pretty simple. Colloquy calls this process user command whenever anyone types a slash, blah-dee-blah-dah thing. It's about just under 20 lines of JavaScript code. Now there's a few interesting things to notice here. Primarily, Colloquy is not a Web browser. There is no window here, there is no document here, I am not using DOS, I am not using the frame.
Instead I am using Colloquy's own custom object model. View here is JV chat view, connection is an MV chat connection. And you can see down here I am actually dynamically creating a JV mutable chat message with the text I have compiled together, and then I can send it out to the channel and then I can echo it on my local display.
So in under 20 lines of JavaScript I have added a very useful command to my IRC experience. I've written a much more involved plug-in that does some pretty cool things. And despite our experience with what we did last year I am going to try it again to show you this new plug-in. I am going to hop on Pound WebKit from up on stage. And I can already see that someone's already messaged me. That's exciting.
Okay. And here I am. In Pound WebKit. People are talking. Fist post, interesting. Interestingness aside. Okay. Oh my gosh, I am on stage. Yes. So a lot of the time in Pound WebKit there's so much going on -- like now it's starting to go -- that it's hard to see exactly who you want to talk to. So let me go ahead and show you my plug-in.
I am going to customize Colloquy's tool bar here, and you can see I'm actually adding a tool bar item. And I'm doing this all in JavaScript. And I'm going to go ahead and turn filters on. Filter is active. Okay, this is a little bit better. It's still going to be hard to talk to exactly who I want to talk to, so I can go ahead and change the way my filter works a little.
Now everyone's just completely blocked. Another thing I did all in JavaScript. It's kind of hard to see, but there's actually a tool tip here if I want to see what the message actually was. And I am going go ahead and talk to Sam. So let me go ahead and specifically unblock him. And now I think I can talk to Sam. Ah. There we go. Ready to do all the work on Safari SJ promised This is very important. I need to ask him this.
And he's going to reply. Okay. Awesome. Okay, so this is pretty cool. Hopefully I have given you a pretty good idea of some pretty cool things you can do using -- uh-oh. Well, let me go ahead and -- no, never mind. So it's a pretty cool example of what you can do in JavaScript as a plug-in architecture. One last thing I am going to do before I hand the stage back over to Jeff is go ahead and use this one.
( Applause )
Thanks Brady. So that was the Colloquy IRC client. And we showed you that it uses JavaScript as a full-blown plug-in architecture. It provides a custom object model. So in JavaScript, inside Colloquy you're interacting with a chat application and chat messages rather than the JavaScript that you may be used to where you're interacting with a window or a document. And it's a third party application. So not only Apple is cuckoo for JavaScript.
Here are the benefits that we've seen from internal scripting. The big one is high level thinking. Take the example of a non-player character in a game of Halo. Its behavior is fairly high level. You know, it hides behind a rock, waits for you to show up, jumps out from the rock, goes ah, ah, ah, ah, shoots at you, hides behind the rock again. So it's nice to be able to describe that sort of high level logic in a high level program.
You don't want to have to worry about, you know, the distance that I jump out from behind the rock is that a 32-bit imager or a 64-bit imager, and is it stored on the stack or on the heap. And if it's on the heap did I create it through malloc, or through new, or through a CF allocator, and which CF allocator did I use, and do I need to release it after I create it. Who's retaining it, if I did it through malloc, how do I know when it's going to be deleted and how long I can use it -- et cetera.
( Laughter )
When Al Gore goes out to commission a study on global warming he wants to hire a climate expert, not a C++ expert. And very few people are both. So it's nice to be able to provide an environment where non-experts can program. We've seen this in Dashboard too. There are a lot of non-experts making really interesting Dashboard widgets for better or for worse.
You know?
( Laughter )
- Quick and easy iteration, many of the things we've talked about like the behavior of an unclear character or the animation of a scene, it's not easy to tell in advance what's going to look good or feel good. You know, you want to be able to play around, make this player a little bit more aggressive and see whether it kills you right away or see if it's a little more interesting to play against -
- make this animation faster, make it slower, use sign instead of could he sign. It's nice to be able to experiment with, you know, being afraid of having to recompile potentially thousands of different files or use a render form or anything like that.
Finally, end user modifications. In an application like Quartz Composer, end user modifications are a requirement. That's the whole point of the application. But even in your applications, if they're not designed for end user modification, end user modification can be a really great value add. So, in Colloquy, for example, it's nice for expert programmers to be able to do what they like with your application, but they can also develop a community where they up load their plug-ins on line, people talk about them and share them.
People love to do this with computer games where they'll design new AI's to play against, they'll trade them on the Internet, they will have tournaments for them. And a community can develop around your application, and your application can continue to growing into the future long after you yourself have stopped working on it.
For internal scripting, here are the benefits that we've seen for using JavaScript as a language. The big one is everybody knows it. JavaScript is the language that powers the Internet. So there are literally according to Google billions of applications that exist that were written with JavaScript. The sad truth of it is, you can get on line on any sort of message board and find thousands and thousands of people who haven't really mastered basic punctuation, grammar, or even politeness. But they are experts in JavaScript and they can do your scripting for you.
Even if you don't know JavaScript, you know JavaScript. Because you know C, or C++, or Java, and all the basic control flow primitives and object access behave the same way. So you can write your own interesting scripts even if you don't know the advanced prototype based inheritance. You can write your own interesting scripts without having much of an introduction to JavaScript at all.
JavaScript was designed for internal scripting on the Internet. It was designed to script the internal object model of the Web browser. So there are some things built into the language to support that in your applications. It's dynamic and extensible. So if you have a function that you all today that takes two arguments and somebody writes a plug-in for that function. And tomorrow you want to upgrade your application, add some capabilities and start passing three arguments, that's okay. The old plug-in isn't going to crash. It isn't going to have to recompile. It will just work -- and ignore that extra argument.
The dinism and extensibility includes the very object model in JavaScript, prototype-based inheritance means that the objects themselves and the interfaces are dynamic. So there are frameworks on the Internet like Dojo, Prototype, Scriptalicious, that extend the underlying object model of the Web browser. Now every time we release a new version of the Web browser we're extending the underlying object model as well to improve compatibility and add features for Web developers. And these two sets of extensions coexist peacefully. We don't have to recompile the Internet or anything like that every time we release a new Web browser. And the same is true of your own applications.
The language is hermetically sealed. So by default, JavaScript can talk to nobody but itself. In that way it's a lot like many programmers.
( Laughter )
It can't talk to the file system. It can't talk to the network, it can't allocate arbitrary memory, it can't invoke arbitrary symbols in your binary, it can't run through your NS View hierarchy looking for a specific UI configuration that it wants to push buttons in. And this is really important for future proofing your plug-in architecture.
We have a plug-in architecture in the Web browser, and we say, okay, plug-in developers, this is how your plug-ins should behave. But ultimately the plug-ins are written in lower level languages, so they can do whatever they want. And they do. So when we change the underlying user interface in our application or the binary code that can get us into trouble where certain plug-ins start crashing because the language they were written in allowed them to do things we didn't want them to do. Well, in JavaScript you are only responsible for the APIs that you specifically export to the script. It doesn't have any native access to anything else.
The language is garbage collected. So you can use your data until you don't need it any more, an then it goes away. And it bridged efficiently with native code. This is really important. A lot of other scripting languages bridging to native code was added on top of that. So that bridge can be pretty expensive. JavaScript was designed for bridging from the very beginning, so it can often be as cheap as a single C function call -- which is about as cheap as you can get.
Now , a lot of people when they brag about their scripting languages and their speed, if you'll permit me to use the Conan O'Brien nerd voice for a moment, they like to say things like, my scripting language can compute 563 digits of pi in under 63 seconds -- well, you're not using script to compute 573 digits of pi. You're using script to communicate with your applications. So the essential feature of scripting performance is how fast it can do that. And JavaScript can do it fast.
Finally, JavaScript supports inline object notation. We call this JSON -- JavaScript object notation. If you don't know what this is, that's okay. Forget about it. If you do know what it is, for example, if you're a game designer and you like to use rule sets and lists and tables to describe the behavior of characters in different situations, JavaScript has a rich syntactic support for those kinds of lists and tables inline and the language.
Now that we're introducing JavaScript Core there are even more benefits to using JavaScript in your application on Mac OS X. And the big one is that it's a part of Mac OS X. So if your end users are using Safari, or if they're using the installer, or Dashboard, or iChat, or Time Machine, or any of these other applications, the cost to you for firing up the JavaScript interpreter is zero. Because it's all ready in memory. It's a shared framework.
Similarly, because JavaScript Core is such an essential component of our operating system and is now available on many platforms, such a component of a lot of our development, we put a lot of development effort into it. So as JavaScript Core gets better, your applications get better. Go to sleep, two months later, your application is faster. How did that happen? Because we weren't sleeping -- ever.
It's fast. We showed you in the key note that JavaScript Core powers the fastest Web browser in the world according to iBench. So I mentioned before that the language is designed for efficient bridging. But of course you need implementation that takes advantage of that design, and we have one.
And finally, it's open source. So there are a few features that you think would help out in your application, you don't have to go off and invent your own language and write your own interpreter. You can if you like just implement the features that you think would be helpful in the open source project, and then they become a part of Mac OS X going forward.
So that's the sales pitch. And the rest of this presentation is all about teaching you how to use the JavaScript Core framework in your own applications. JavaScript Core is a C API in the style of the Core APIs on Mac OS X. So it is the lowest interface to your JavaScript engine.
It's complete. Everything that you find in the E C M A standard and some things that you don't find there but we've found useful you can do through this API. And it's light weight. It's just the JavaScript and none of the rest of the Web stacks, it is the JavaScript, the whole JavaScript, and nothing but the JavaScript. It's thread safe, so you can run your scripts on a secondary thread in order to keep the UI thread responsive.
And it requires Tiger with the Safari 3 O update installed -- or greater. So we've put a lot of work into making sure that this new technology works on our older operating system as well so you can get a lot of value by adding it to your application right away. Widget plug-ins, however, will require Leopard or greater.
To begin, before I go into the API itself, I'd like to give you a brief overview of the JavaScript language. I am not going to teach you JavaScript, there are better resources for that. But I do need to introduce you to some core concepts so that you can understand how the API works, because it reflects those concepts.
And there is no better place to start than the classic hello world that you'll find in any JavaScript textbook. This will bring up an alert in a Web browser that says, hello world. But I am here today to tell you that all of these text books are dirty rotten liars. Alert is not a part of the JavaScript language. It has nothing to do with JavaScript and everything to do with the capabilities of a Web browser to bring up a window. So the real JavaScript hello world would look like this.
JavaScript can create the string hello world. It has a rich library for manipulating those strings, for processing them with regular expressions and things like that. But ultimately what JavaScript does is it hands the string off to your application, and then your application decides what to do with it.
So I've drawn you a bit of a key note stick figure to show you exactly what I mean. This is little Jimmy JavaScript. Then this is the JavaScript language, just by himself. Pretty svelte language, just the basic control flow and those built in concepts like functions. What we showed you earlier in Colloquy was Jimmy JavaScript dressed up as the Colloquy application. It absorbed the Colloquy object model that was reflected in the script that we used. Now what people usually mean when they say JavaScript is actually Jimmy JavaScript looking like this. Where he has absorbed the whole thousands of APIs that we make available for all Web programming.
What's important to remember is that those of a part of the Web browser and they're not a part of the JavaScript language. So I have a table here to show you exactly where the dividing line is. This is what's part of the JavaScript language. Those basic concepts, objects, arrays, regular expressions -- anything that you would find in this other descent programming or scripting language.
What's a part of the Web browser, which is separate from JavaScript itself, is the window. That window that you see -- that's a Web browser, that's not necessarily in your application. So things that hang off the window are also not a part of the JavaScript language. The document, for example.
The set time out function that's a part of that window interface, and XML JPG request, just to name a couple. So we have a JavaScript engine that is just the JavaScript language. And if you're an application like Quartz Composer, you don't want those Web browser things. You don't want people making their own time outs, because you have your own animation timer. You don't want people making synchronous requests over the network in the middle of an animation -- and you don't need it. So we have your stand alone Java library for applications like that.
If you're looking at the slide and you say to yourself, boy, I don't know what I would do in my life without XML HJPG requests -- that's a good sign that you actually do want the full Web browser in a box in your application. And that's what the WebKit framework is for. We have a demo for you of the most basic JavaScript interpreter that you can build. It's just a few lines of code. And for that I will invite to the stage my friend and colleague Alice Liu. Could we have the demo machine please.
( Applause )
Thanks, Jeff. So I will be showing you a JavaScript interpreter. And in this app we will be using the newly available JavaScript Core to execute the statements. So keeping in mine with what Jeff has just mentioned about what statements are supported by the JavaScript language as opposed to the window-related functions that you're so used to when you're experimenting with JavaScript code in the browser. I am going execute some statements that will work and some that won't.
So here's our JavaScript interpreter. And very simple, here's we'll insert the statements and we'll get our results down here. Let's try some really simple statements. Let try a date object first. And this statement is going to give us I string that gives us the exact date and time right now.
That's pretty simple, let's do some things a little bit different. Let's execute a statement. And this is called what is today. And it's going to use that date object and give us back just the date string. But if I execute this statement here it will give us just the definition of the function -- which it does. So that's good. And if I run it, then it will give us just the date string. Which is what I expect. Okay. So those all work. And they're simple enough. But just to drive home what Jeff was just talking about, about hello world.
The classic alert, hello world, that everyone does -- and it really is true that this is not part of the JavaScript language. So although my examples were very simple, surely you can envision the great benefit of leveraging the power of the language in your application. And doing so is very simple. And I'll show you how sample it is by opening up the Xcode project.
So this Xcode project does some really basic things. The very first thing you need to do is you need to link JavaScript Core as a framework, which we do here. The next thing you need to do is actually use it, right? So in this function this is where we use it, called evaluate script.
And in evaluate script we do just four simple things. We first need to create a string to house the command. And next we evaluate that command. When -- after we do so, we'll get a result and we save it in a string. And lastly, we need to clean up after ourselves, which this line does right here. And it's really as simple as that. Back to you, Jeff.
( Applause )
Thank you, Alice. That was the basic JavaScript interpreter. It shows you JavaScript in the buff. Jimmy JavaScript dressed up with no clothes at all. Just the language itself. It's implemented in very few lines of code. So we do try to make the easy things easy for you, and the difficult things possible. And we showed that execution can fail. You're running arbitrary scripts. They may be malformed, so you do need to check for failure and exceptions.
Here is the code. Once again, and I'll step through it with you so you'll understand what we're doing. At the top we have a JavaScript in a JS string ref. So the JavaScript library has its own strings. We don't use CF string. And this is because JavaScript uses strings in very different ways that you use in a regular application. So everything that we do in this API we do for performance.
And one of those things is we use our own internal representation of strings. So once you have a string what you do is you call JS Global context to create, to create a JavaScript execution context. You call JS Evaluate Script to evaluate your script in that execution context.
And then you get a result back. And if you got a result, you can use it. If execution failed, if there was an exception thrown, then the exception will be null. And you do need to check for null. Because unless an API specifically says that you can pass null in certain circumstances, you can't.
Finally, we call JS Global Context Release to release that execution context and all of the resources associated with it. Now this result that we get back is a JS Value Ref. So what is that? Well there are only two JavaScript data types in this API. JavaScript's a pretty simple, straight forward language. So the base type in JS Value Ref. And that means a JavaScript value. It could be any kind of JavaScript data in the JavaScript language.
The derived type is JS Object Ref. That's a JavaScript object. And according to the JavaScript language, all objects are treated equally. So we just have one object class. So the derived type of object can be used anywhere you use a JS value without any casting. With a JS value you can only perform primitive operations. So, converting it to a string, for example.
However, it may be an object, in which case you can ask it if it is an object. And if it is, you can cast it to a JS Object Ref. And then you can perform object operations, like getting and setting properties or calling it as a function. I mentioned that all objects are created equally in JavaScript, so you can even take a regular expression and call it as a function. The API will let you do that. Of course the regular expression will just turn around and say, I am not a function. And throw a JavaScript exception. But you know, it's safe to do that sort of thing. Just kind of silly.
That's all that you need to understand in order to evaluate a script in your application but what you really want to do is interact with the script, and enable the script to interact with your application. And in order to describe how to do that I am going to develop a real world example for you. I am going to write the A I for a video game in JavaScript. I love doing this with Age of Empire, I became a little bit addicted to it. So I'm doing it now in my own application.
And keeping in mind the theme of WWDC going back to the beginning of time on the desktop. I am going back to the beginning of time in video games with the original and still the greatest video game -- Pong. So on the right we have an AI that's going to control the behavior of the non player character. And that A I is all written in JavaScript. On the left is the human character who will inevitably be crushed by our superior logic and processing power.
And the first question we have to ask is how does the A I script community its decisions to our application, and how does the application communicate the state of the game -- of the underlying model of its shapes -- to the A I. The answer is simple. It's the global object.
All JavaScript execution occurs in the context of a global object. If you create a global variable in JavaScript it becomes a property of the global object. If you create a function, a global function in JavaScript, also a property of the global object. So the most basic form of communication between an application and a script is that global object where you can get and set properties and so can the script.
I have drawn you a little diagram here of how that works. We have the Pong application in red. That's my native code. I chose Objective-C because I love Objective-C. Very easy. And the first thing that we do is we allocate the JavaScript execution context which brings with it a global object in green. So next we're going to set up some constants to ease communication just like you would in any API. And we're going to set up and down as global properties so that the script it tell us which way it wants to move.
Next, we'll evaluate the JavaScript AI. And it will add its own properties to the global object, like an X move function. And that's the function we're going to call every time it's the AI turn to move. So here we go. Our timer fires and it's time to update and we call next move.
We pass it two arguments. A paddle and a ball. So it can judge the position of everything and decide how it wants to move. And now, I don't know what this script is going to do. That'd the point. It can do whatever it wants. It may be storing its old moves in order to adapt to past losses.
It may be storing your old moves in order to predict where you're going to go. It may be storing the last position of the ball and making judgments that way to try and see how the ball is moving. But it comes up with a decision and it returns me a result. And it says, okay, I want to go up.
Let me show you the code required for that. This is how to set the global up constant. First we create that string in the JavaScript API with the name of the constant. Then we use JS context get global object to get the global object in the execution context. We make our JavaScript number, that's the numeric constant for up.
And we use JS object set property now to set it as a variable inside that global object. Finally, we call JS string release to release that string. If you were doing this over and over again you would probably want to hang on to that string instead of allocating it every time.
Here's the code for calling a global function. That next move function. Once again we need the name of the function we want to call. Which is next move. We use JS object get property to get the next move function from the global object. And then we need to do a little bit of type checking.
We need to make sure we got something back, for example. JavaScript supports getter functions. So when you get a property, a function may run and it can throw an exception, and you will get back null. So we checked null. We also need to check that we got back an object.
Because the script could be malformed. It could have created a variable called next move, and set it to the number 1. We can't call the number 1 as a function. So we need to make sure that we have an object, and then we can cast this value to a JS object ref.
Now that we have an object we prepare our array of arguments, the paddle and the ball, and we use JS object cause function to call the function next move, passing the paddle and the ball. You'll notice in this example I haven't actually done anything with the result of this function call in. In the real application we take that result and decide how to move the paddle based on it.
So if we look back at the diagram one thing I haven't talked about yet is the paddle and the ball. Those are JavaScript objects. So let's zoom in on them and get rid of everything else. And in order to explain how these work I am going to bring back our old friend Jimmy JavaScript. So JavaScript objects here are just thin wrappers around my Pong application, just like they are in the Web browser or in Colloquy.
And they have the same properties of my underlying model of shapes in the application. The ball and the paddle are just shapes. So there are these properties like left and right in the shape. There are others that I haven't shown here like top and bottom. And when a script asks for left, we just turn around and we forward the request to the underlying model in your application. Office why I said before, communicating with your application can be as cheap as a simple C function call. You asked for left, let me go get left. You asked for right, let me go get right.
Here are the data types that you need to describe those objects. 99 percent of the time you can use a static table to describe the properties that you want to make available to JavaScript. And we support two kinds of tables. There's a static value table, which is the most generic. It can support any kind of JavaScript data. And then we provide a static function table, which is specifically tailored for making it easy for you to describe member functions in your objects. We haven't used any member functions in Pong, but rest assured, they're there.
The rest of the time you can also register for optional dynamic call backs that are invoked any time your object is used in a script. The two you will use most often are initialize and finalize. These are called when your object is first created, and when it's finally destroyed by the garbage collector. And you use these to retain and release the underlying model in your application. The lifetime of a garbage collected object is unpredictable. So you do need to retain and release any data that your JavaScript objects are going to use.
We have a get property call back that you can use to dynamically describe the properties on your objects if you can't do it statically. An example of this is if you have an object that you want to behave like an array. You don't know, necessarily, what properties it has. So when I ask for the property at index 100, you need to do a dynamic look up to see if the array is that big, and if there is a property there, then respond dynamically, then you can implement this call back to do that.
This call has function which is invoked if your object is used as a function. And there are a bunch of others. Not very interesting, but they're there. And they're all in JS Object Ref dot H. Here's a little bit of code that describes -- here's an example of a static value table to describe one of these shapes. So here's the property left. What we do first is we provide the name of the property. Which is left. Next we provide a getter function. JS shape get left.
And that will be invoked when the script asks for the property left. Next, we provide a setter function and a set of attributes. And here we've made this property read only. And so the setter function is null. It doesn't really make sense to have a setter function for a property that can't be set.
So this is a design decision that I made in Pong. I don't want your API scripts to be able to arbitrarily set the state of the model and move the paddle anywhere they want to the screen and whack the ball back at the user. That's called cheating. So my model is read only. And then you just decide which way you want to go. In your own applications your models might not be read only. In the Web browser virtually nothing is read only. But you can do that if you want. I just haven't done it here.
So we have another demo for you of that full blown Pong application in action, and I will invite back to the stage by particular demand Alice Liu. Could we have the demo machine, please?
( Applause )
Sorry. Hi again. So I am going to be showing you the JS Pong application which uses JavaScript Core. And also look into the performance of it, with Xray.
I didn't quit. Okay. So here's the Pong game. And it's a pretty neat little app. Currently, we're running with a logic that makes the paddle over here, the opponents paddle, follow the direction of the ball exactly. The moment is being determined by the JavaScript function which we can see over here, called next move.
The logic is very simple. Basically the paddle's next move will be the same as where the ball has just moved. The only thing I want to show you is this line here. Last ball Y. Basically, we're keeping track of the ball's last Y position so that we can remember it for next time. A script -- what you should take from this is us scripts can keep static in between invocations.
So one of the great benefits of using JavaScript in your application is that you don't have to recompile code when you want to try something different. And this is -- a benefit to you if you need -- if your application is one in which you need to try different code quickly to see how something will behavior. Like experimentation. So this will facilitate experimentation. And because Pong is an application like that, let's try some different -- let's try some different A I models. I have another one here called run away.
So run away was designed it the intention to move in the exact opposite direction of the ball and hopefully lose as quickly as possible. But when we run it we see that due to the geometry of the game field it actually ends up intercepting the ball anyway -- which is expected. But kind of interesting. So if you wanted to run away from the ball, you know, try to and it didn't work. Well, actually that time it worked. So let's try something different. Since that didn't produce what we actually wanted to do. Let's do something different.
Here's one called semi- random. So this introduces randomness into the position of the paddle. And it produces a pretty decent opponent, actually. So randomness is often used in AI because it makes for a more unpredictable opponent, which will make it harder to master the game. So in your Pong game you can see that the introduction of random numbers will cause the opponents paddle to kind of jiggle up and down.
And this will cause the ball to -- this will cause the ball to be hit in more interesting ways. And so we just saw the game -- the opponent lose. Okay. So Jeff is -- mentioned and also Steve Jobs has mentioned how fast JavaScript Core is by JavaScript and JavaScript Core. So let's look at an Xray sample and we'll see just how fast it is.
Xray is a new tool for Leopard. And -- okay. So here we have a sampled 30-second run of the Pong application. And we took this earlier so that we didn't have to wait for it. But what I want to show you is this line here, Pong AI, next move. So Pong AI next move is taking just 0.2 percent of overall runtime. And as Jeff told you earlier, Pong A next move is that function that invokes JavaScript. So we're taking just 0.2 percent of overall runtime.
And another thing I want to show you is Pong controller update. This function is the function responsible for updating everything. The position of the player, the position of the ball, and the position of the opponent. And this one takes just 0.9 percent of overall runtime. So the function that invokes JavaScript -- JavaScript Core -- is using less than one-quarter of the time it takes to update everything. And that's a pretty performance PC. Okay. Thank you very much.
( Applause )
Thanks, Alice.
That was the full Pong application. And we showed you JavaScript controlling a non-player character. We showed you quick and easy iterations so you can experiment with different strategies. You saw our strategies still lose even if the player is doing nothing. So maybe as an exercise for the reader you can implement a better strategy.
I have a theory that you could implement an AI that would calculate the full trajectory of the ball and literally be unbeatable. I hope someone can do that. And we also showed you a miniscule sample count inside JavaScript Core. Only 0.2 percent of overall execution time. Now there are a number of factors that contribute to that miniscule sample count.
One is the JavaScript Core is efficient and its fast. Another is that we're using script judiciously. We're using it to implement high level application control flow logic. So this model scales. Imagine if we had a hundred paddles zooming around the screen instead of just one. Well, your sample count inside JavaScript Core would still be just about 0.2 percent, because you would spend all that extra time drawing all the panels. So scripting in your application use judiciously, also scales.
We have a couple of styles that we recommend for interacting with scripts in your applications. We only showed you one possibility inside Pong. But there are a couple. And I'm going list them from simplest to most complicated and interesting. The simplest thing you can do is execute a one-shot script. And the API for that is JS Evaluate Script.
So if you encounter a script tag in a Web page, that's what the Web page does. One shot script and then it ignores the script after that. And that's also what we did on the simple JavaScript interpreter. We just evaluated the script and did nothing more with it after that. Not very interesting, but it's the simplest thing to do.
Getting a little bit more interesting, you can take a snitch yet of JavaScript and treat it as the body of a function. So your script author just writes a little bit of JavaScript, and then compile that into a function and call that function with whatever arguments you want to provide. So the API for that is J A object make function, where you pass in the function -- the script you want to turn into a function. And the advantage here is that you can now call it multiple times. And you can pass up arguments.
Getting a little bit more interesting, you can have a conically named entry point. That's what we did in point where you have a conically named next move function that we look for in your script and then we call that over and over again. So the API we use there is JS object get property, where you ask for the next move function from the global object. And the advantage here is that now your scripts can start saving their own state.
And finally, you can implement arbitrarily named entry points. And the API name for that is -- well, there is none. The point is that the script decides what the entry points are. So you provide a function or a property that the script can set or pass a value to.
And that's how it let's you know what its entry points are. We do this in the Web browser where you can set a property, window dot onload, and set it to the function you want called when your page fully loads. Or you can call add event listener, and pass your entry point function as an argument.
I'm sorry -- the advantage to those arbitrarily named entry points is that then you don't have to pollute the name space for your script author. If you have a lot of different entry points you don't want to require them all to have very predefined names. We have a couple of best practices for you that we've learned over the years.
Evaluate a script only once. When you evaluate a script we have to compile it and turn it into an abstract syntax tree in memory. That can be expensive if you do it over and over again. So if you want a simple bit of script that you run over and over again, we recommend that you compile that into a function with JS Object Make Function and call your function over and over again. Then you don't have to start the hamster running every single time you execute the code.
Use a model for your application data. In Pong we have a model of shapes to represent everything on the screen. And that means that your JavaScript binding code should be mindless. You asked for left. Let me go get left. We learned this the hard way in the Web browser. Where we would have bugs in the underlying object model of the Web browser and we would fix them by fiddling with the JavaScript binding code.
So our bindings became huge and really complicated, and our bugs were only half fixed. Because if you accessed the model in a different way it was still broken. So we've done a lot of work in Safari 3 to move that logic out of the binding code to make the bindings mindless, and to implement our application logic inside our application model.
In fact, your bindings should be so mindless that you can consider writing a script to auto generate them. This has been a boon to us in WebKit because we have so many JavaScript APIs. In your own application, in Pong, for example, we don't have that many APIs, so we write them by hand because that's easier. And create convenience types and functions. You know, it's a CAPI. If you like C, you're weird. It's there. But if you like C++ I would recommend, for example, boost auto pointer and shared pointer to handle reference counting. Same things exist in Objective-C, et cetera.
Now I have two more subtle topics for you. So get a little -- get a little -- shake it out. Get ready. And the first is memory management. Okay? I mentioned that the JavaScript language is garbage collected. We expose that fact in our API for performance. If every time we had to pass through a JavaScript value you had to fiddle with a garbage collector that would be slow. And so we garbage collect in the API itself.
JS Value Ref and JS Object Ref, the two JavaScript data types are garbage collected. They are handled mostly automatically, you do not need to release them. And the mnemonic for remembering that is that you get these values from functions with the words get, make, and to in the name. They are handled mostly automatically. You do not need to release them.
I said mostly -- automatically. Here's exactly what that means. From the script author's perspective, garbage collection is completely automatic. You use your data in a script until you don't need it any more, an then it goes away. From the script embedders perspective, local variables are automatic. So if you have a JS Value Ref or a JS Object Ref and a local variable, the garbage collection knows you're using it and doesn't delete it. That means that global variables are not automatic, and neither is heap data. So if you move a JS Value Ref into a global or into a structure on the heap you need to let the garbage collector know you're still using it.
We have a simple API for that. JS Value Protect and Unprotect. These work just like retain and release. You protect any number of times, unprotect an equal number of times, and your value becomes eligible for garbage collection again. Here's an example. We have a global count that's a JS Value Ref. We unprotect the old count when setting a new count. And we protect the new count, and finally perform the assignment.
The rest of the data types in the API correspond to the lower level details of the engine, and not the JavaScript language itself. And so we reference count these for performance. And the mnemonic to remember that you've gotten a reference counted piece of data is that it comes from a function with the word copy or create in the name. You don't have to know these intimately, really. You just need to remember that they're reference-counted because you got them from copy and create -- you need to release them.
The second more subtle topic is threading. Which is always subtle. JavaScript thread safe is not thread optimized so you do not get symmetric multiprocessing if you run two JavaScript threads simultaneously. You can run them, if that fits your programming model, does it doesn't help you. Your finalizers may be called on any thread, so they need to be thread safe.
Don't move JavaScript Core types between threads. You can share them, as long as the type is still live on the thread that created it. But if you move it away, the garbage collector no longer knows you're using it, and will delete it. We apply this to everything in the API, not just the JavaScript data types, because we may start garbage collecting more things in the future if it makes us faster.
Finally, don't use the JS Global context ref on multiple threads. This is more of a no-brainer than a subtlety. The JavaScript execution context is just like the JavaScript stack. So just as you wouldn't execute two C threads on the same stack at the same time because you would explode, the same goes for JavaScript.
That's it for the JavaScript Core API. We do have a brief nugget left for you which is how to use JavaScript in a WebKit application. We have a little bit of API for that. If you have a Web frame you can call global context to get the JavaScript execution context corresponding to that frame. And if you have a Web script object, which is the WebKit wrapper around a JavaScript Core object you can call JS object to get back the underlying JavaScript object.
We also have a new delegate. Did clear window object for frame. If you are familiar with WebKit programming you know that you should do your JavaScript programming in the window script object available call back. Now, window script object available only gives you a Web view. But you need a Web frame to do JavaScript Core programming.
So we have a new delegate call back here to supplant the old one. It fires at the exact same time. And you can also implement both of them. In which case we will only call the new one. So you can implement both, do the old thing on Tiger, do the new thing with the new WebKit.
Here's an example of that. You implement , did clear window object for frame. You call global context to get the JavaScript execution context. JS object to get that window object as a JavaScript Core object. And then you go to town. This is an object that you got back from WebKit. So it's a real window object that includes the whole Web stack like set time out, XML H T P request. And you can do anything you want. You can add your own custom constructor functions, et cetera.
A final note about using JavaScript Core with WebKit is threading. JavaScript Core is thread safe. WebKit is not thread safe. So do not be tempted by JavaScript Core to take a JavaScript object that you got from WebKit and start using it on a secondary thread. Remember, these are only thin wrappers around the underlying model into WebKit. That model is not thread safe. So if you use those objects on a secondary thread, kittens will go flying, babies will be bounced out of the boat -- and worst of all, your application will crash randomly. Use WebKit objects on the main thread only.
For more information about the WebKit open source project we have WebKit dot org, and we also have our IRC channel which I think you all joined just recently. I mentioned that we support a rich object notation called JSON, and you can read more about that at J-S-on-dot-org. Raise your hand if you're still writing anything down. Okay. We have a Safari and WebKit lab at 3:30 today in Lab D, as in dog.
Okay. So we went through a lot of API, a lot of interesting stuff. Thank you for paying attention the whole time. You don't need to memorize all that. The slides are available after WWDC. Of the sample code is already on line. The headers are copiously documented. But these are the high level ideas I want you to hang on to, to consider when you're making your own applications.
Consider JavaScript for internal scripting. It's popular. It powers the Web; everybody knows it. It's fast. It powers the fastest Web browser in the world. And it's a part of Mac OS X. For your JavaScript needs we have the JavaScript Core framework. It's light weight, none of the rest of the Web stack. It's complete. Everything in the UCMA standard, and it's thread safe.