21.12.09

ECMA-262 ed. 5 backwards-incompatible change coming to SpiderMonkey and to Gecko-based browsers

(preemptive clarification: coming in Firefox 3.7 and not Firefox 3.6, which is to say, a good half year away from now rather than Real Soon Now)

ES5 and compatibility

The fifth edition of ECMA-262, the next iteration of the JavaScript language, is broadly backwards-compatible with existing JavaScript code. Generally, code that worked in past browsers that implemented the specification will continue to work in new browsers as they implement the new edition of the specification. However, there are a few exceptions. The most obvious one is ES5’s strict mode, where specially-tagged scripts and functions will cause parsing and execution of their contents to occur under stricter requirements than in the past. For example, this code would have executed “as intended” in ES3, but in ES5 the definition of a variable named “arguments” inside a function in strict mode is a syntax error (none of the code even executes):

function strictModeError()
{
  "use strict";
  var arguments = 17; // stupid, but permissible, in ES3
  return arguments;
}
if (strictModeError() !== 17)
  throw new Error("up is down");

The above isn’t more than a theoretical problem as it is expected old code wouldn’t have accidentally opted into strict mode. Not all of ES5’s incompatible changes, however, are so benign.

ES5 compatibility with ES3 extensions

One unusual area of compatibility concerns not ES3, but extensions to ES3. One of the more profound changes in ES5 is the introduction of getters and setters, in which what appears syntactically to be a property when used actually will invoke function calls “under the hood”. Most major JS engines support this extension to ES3:

var o =
  {
    get field() { return this._field; },
    set field(f)
    {
      if (typeof f != "number")
        throw new Error("not a number");
      this._field = f;
    },
    _field: 0
  };
print(o.field); // 0
o.field = 5;
print(o.field); // 5
try { o.field = "0"; } catch (e) { /* throws: not a number */ }
print(o.field); // 5
o.field = 17;
print(o.field); // 17

This syntax is in ES5, partly because it addresses a need in a reasonable way but mostly because many developers will already be familiar with it. (Getters and setters were also available programmatically; ES5’s solution is different but more flexible.) However, not all aspects of getters and setters are present in ES5 in the same way they were in extensions to ES3 engines.

Assigning to getter-only properties in ES5

Consider the previous example, slightly tweaked:

var o =
  {
    get field() { return this._field; },
    _field: 17
  };
print(o.field); // 17
o.field = 5;  // ???
print(o.field); // ???

In this case the field property is read-only: you could analogize it to element.childNodes.length, which has a value which it makes no sense to attempt to change. What should happen, then, if you attempt to change it? Current browsers throw a TypeError when you try this. ES5, however, chooses to remain (arguably) more faithful to ES3 and instead makes setting a property that only has a getter do nothing — except in strict mode, where a TypeError exception will be thrown.

SpiderMonkey, the JavaScript engine embedded in Gecko browsers, has just made the switch from its previous always-throw behavior to ES5’s only-throw-if-in-strict-mode behavior when an attempt is made to set a property that only has a getter. (This will also apply to DOM properties like the aforementioned element.childNodes.length.) In the future, if you try to set a property that only has a getter, no exception will be thrown unless you’ve opted into strict mode. This change is now in trunk builds of Firefox; download a nightly build from nightly.mozilla.org and test out the change for yourself (use the profile manager if you want to keep your current Firefox settings and install untouched). If your site relies on an exception being thrown, this change could break it, and we’re hoping that an extended period of time to test the change will help developers iron out any reliance on this non-standard behavior. This change will first appear in Firefox 3.7, which probably won’t be released until the second half of 2010 or so. Firefox 3.6 preserves current behavior where an exception is always thrown, so you should have plenty of time to update your site in response to this change.

The bottom line

Firefox 3.6 and earlier throw an exception whenever you attempt to set a property represented only by a getter (this includes DOM properties defined as readonly). Firefox 3.7 will only throw a TypeError when assigning to a property represented by only a getter if the assignment occurs in ES5 strict mode code. This change will also apply to attempts to set readonly DOM properties like element.childNodes.length. If you’re relying on an exception being thrown in either case, change the assignment location code so that it works when no TypeError exception is thrown.

08.12.09

An exercise in XPCOM programming, redux

The Exercise

Last time our hero was engaged in solving this posed streams problem:

Suppose you wish to complete one conceptually simple task in stream programming: copying a stream, i.e. reading all data from one stream and writing it all into another, where both streams are nonblocking. (Such a copier might buffer data read before it can be immediately written; assume this is a requirement for the purposes of this exercise.) Suppose for the moment that there is no readily available implementation of the nsIAsyncStreamCopier interface, so you have to roll your own stream copier. In what situation is it necessary to asynchronously wait with flags = WAIT_CLOSURE_ONLY to efficiently implement stream copying?

(Refer to the original post for full background if you’re not familiar with streams.)

The Answer

Copying from one nonblocking stream (the source) to another (the sink) involves waiting for the source to be able to provide data, reading in that data, waiting for the sink to be ready to accept data, and writing out buffered data. In the simple case there’s always data available to read and always space to write it. Let’s break down the cases where these aren’t the case:

There’s no data available to read from the source
Just wait for data to be available (assuming the sink hasn’t hit an error, if it has the copy’s done)
There’s no space in the sink to write data
There’s data available to write to the sink
Wait for that amount of data, or a fraction of it, to be writable
There’s no data available to write to the sink
???

What should happen in the final alternation? Suppose you waited for some amount of data to be writable, we’ll say 1 byte. What happens when the sink becomes that far unblocked? You’d have to be notified, and if there’s still no data to write you’re back where you started. Maybe you can bump up the amount you wait for, but how far should you bump? Increase arithmetically? Double? Any amount you bump to might result in more notifications when there’s nothing you can do.

There’s a further problem with waiting for some amount of data, one you’d only know if you were familiar with the async copying interfaces: the amount you specify when calling asyncWait to request notification when the stream’s unblocked again is only a hint. That is, the implementation is free to notify whenever it wants, so long as it’s not notifying when the state of the stream is unchanged from being previously blocked and unclosed. A stream might notify whenever any data is available, even if you bump up the amount. Therefore, if you just wait for an amount of data, and wait again if you have no data to write, you’ll be notified almost immediately (after any pending tasks in the thread event loop). Repeat this a few times and suddenly you’re spinning doing nothing, which is clearly inefficient. The main async stream classes in the tree ignore the requested count precisely as described here, so this isn’t simply an academic problem that we could ignore.

Here’s where you need WAIT_CLOSURE_ONLY. Until you have data to write, you don’t care about how much can be written to the sink. What you really care about is knowing if the sink closes (or gets in an error state), so you can stop copying immediately when that happens, rather than wait (perhaps indefinitely) until you have data to write and only determine when writing it that the sink’s closed (or in error). Using WAIT_CLOSURE_ONLY whenever you haven’t hit errors but don’t have any data to write neatly solves the problem of efficiently learning if the sink dies.

01.12.09

An exercise in XPCOM stream programming

If you’ve done any programming with XPCOM, at some time you’ve probably had to work with streams. A little background in case you haven’t, then a small thought exercise:

Streams

A stream is an object from which you read data or to which you write data. In XPCOM an input stream stream is a stream from which you read data; an output stream is a stream to which you write data. In an ideal world a stream is either open (indicating data may be read or written to it) or closed (indicating that the stream is no longer readable, or that no more data can be written to it), and that’s all there is to it. File objects in Python function very much like ideal streams.

In the real world, truly useful streams have further limitations (or characteristics). How much data can be read from an input stream right now? Can a given amount of data be written to an output stream right now? Should reading or writing proceed until completion when right now isn’t possible but sometime later might be, or should it halt immediately with an error indicating that reading or writing would block program execution? One might ignore these concerns in simplistic scenarios such as those which short Python scripts might be used to address. In complex applications, particularly those which must remain responsive to user input, these concerns may be quite important. You can’t display a useful progress bar if the stream you’re reading from represents the download of a 3GB file over a slow network and reading from the stream blocks program execution.

Streams which immediately halt with an error when reading or writing would block execution are nonblocking streams. Efficient use of such streams requires a way to wait until the desired amount of data can be written to or read from a stream. XPCOM efficiently supports nonblocking streams through an asyncWait method which will notify at some later time when the desired amount of data can be written to or read from the stream, without blocking. At the moment there are two flavors of asynchronous waiting: waiting until the desired amount can be read or written, and waiting until the given stream has been closed. At the interface level, the former is indicated by flags = 0, while the latter is indicated by flags = WAIT_CLOSURE_ONLY.

The Exercise

Suppose you wish to complete one conceptually simple task in stream programming: copying a stream, i.e. reading all data from one stream and writing it all into another, where both streams are nonblocking. (Such a copier might buffer data read before it can be immediately written; assume this is a requirement for the purposes of this exercise.) Suppose for the moment that there is no readily available implementation of the nsIAsyncStreamCopier interface, so you have to roll your own stream copier. In what situation is it necessary to asynchronously wait with flags = WAIT_CLOSURE_ONLY to efficiently implement stream copying?

Hints

If you want a hint (arguably the answer, if you can interpret the code), take a look at the uses of WAIT_CLOSURE_ONLY in xpcom/io/nsStreamUtils.cpp. You may perhaps find further hints in bug 513854, the bug which brought this somewhat quirky need for flags = WAIT_CLOSURE_ONLY to my attention.

Questions?

I come to this problem with more experience and familiarity with streams than most people will have. If anything in the above description is unclear, ask questions in the comment section — I did the best I could to make the problem and its background understandable, but I may easily have done so less well than intended.

16.10.09

Working on the JS engine, redux

In a continuation of a topic started by mrbkap, I present you this gem of a gdb command I needed to use today:

cond 8 \
  (*$9 && \
   ((*$9)->id&7) == 4 && \
   (*(jschar**)((uintptr_t) (*$9)->id + 4))[0] == 'o' && \
   (*(jschar**)((uintptr_t) (*$9)->id + 4))[1] == 'f' && \
   (*(jschar**)((uintptr_t) (*$9)->id + 4))[2] == 'f')

For minimal background, breakpoint 8 was the result of watch *$9, and of course $9 = (JSScopeProperty **) 0x7ffff2f08038.

By the way, did you know the gdb command line supports line continuations? I didn’t, before I had to think about how the above command would display without any. 🙂 This is the above command as I originally wrote it:

cond 8 (*$9 && ((*$9)->id&7) == 4 && (*(jschar**)((uintptr_t) (*$9)->id + 4))[0] == 'o' && (*(jschar**)((uintptr_t) (*$9)->id + 4))[1] == 'f' && (*(jschar**)((uintptr_t) (*$9)->id + 4))[2] == 'f')

15.10.09

My distro can beat up your distro’s honor student. Or something like that. (Or: setting up ccache-powered Firefox builds in Fedora)

Tags: , , , , , , — Jeff @ 22:23

dholbert makes a recent post (well, recent only in planet.mozilla.org‘s little mind, no idea why a post from September 2008 is being displayed as new!) discussing how to build Firefox with ccache on Ubuntu, saving compilation time on close to null-program rebuilds. Cool beans. However:

If you’re on Fedora 11 (conceivably earlier too, I regretfully haven’t regularly used Fedora since Fedora 6, until recently), the basic developer tools package combo includes ccache, and caching Just Works in Firefox builds with no extra work needed at all.

[jwalden@the-great-waldo-search dbg]$ \
> ls -la `which g++` `which c++` `which gcc` /usr/bin/ccache
-rwxr-xr-x. 1 root root 43584 2009-02-23 23:42 /usr/bin/ccache
lrwxrwxrwx. 1 root root    16 2009-10-02 21:29 /usr/lib64/ccache/c++ -> ../../bin/ccache
lrwxrwxrwx. 1 root root    16 2009-10-02 21:29 /usr/lib64/ccache/g++ -> ../../bin/ccache
lrwxrwxrwx. 1 root root    16 2009-10-02 21:29 /usr/lib64/ccache/gcc -> ../../bin/ccache
[jwalden@the-great-waldo-search dbg]$ du -hs ~/.ccache
883M	/home/jwalden/.ccache

Anyway, use whichever distro you want, with ccache or without, whatever satisfies your preferences and utility curve. (The semi-troll title is completely gratuitous, but my sense of humor mandated I use it. 🙂 ) As for me: I am an absolute sucker for convenience. I’ve known of ccache for years and never used it before due to the activation energy needed to do so; had using ccache required equivalent effort in Fedora I strongly doubt I’d ever have used it. Score one for making the right choice for the user rather than requiring him to make it himself.

« NewerOlder »