03.06.10

Dear Lazyweb

I’ll be traveling outside the United States for the Mozilla Summit, and I have a couple other distant vacation ideas which might involve leaving the country. When you spend overseas, you spend the local country’s currency, not your home country’s currency, exchanging one for the other when needed. The common wisdom seems to be that the most efficient and generally least expensive way to exchange currency for spending (when possible) is through a credit card. Most credit cards impose a “foreign transaction fee” on such transactions: a few percent, usually. I’m aware of one credit card that currently imposes no such fee: Capital One. (The Schwab Invest First Visa also charged no fee, according to reports, but that card’s been discontinued.) What other credit cards should I be aware of that charge no foreign currency fee? Does anyone have a recommendation for a good credit card primarily to be used for overseas purchases?

Most ideal would be if the card had no foreign transaction fee and no minimum usage requirements, but I can make some perfunctory use of it in order to avoid fees if necessary.

20.05.10

A brief note on web video formats, Mozilla, Theora, and H.264 in light of WebM

In a dispassionate comparison of the two major web video formats as of a few days ago, H.264 is basically stronger than Theora. H.264 has numerous and varied hardware decoders. Theora does not. H.264 has numerous software decoders. Theora has this too. Windows 7 and OS X natively support H.264. Neither natively supports Theora. Flash supports H.264. It does not support Theora. Numerous video cameras natively generate H.264. The same is not true of Theora. H.264 produces high quality for its compression. It’s disputable whether this is true of Theora. Licensing H.264 is burdensome but reasonably risk-free. Licensing Theora is painless but disputably risky.

Still, H.264 has weaknesses. H.264 is quite complex. Theora is relatively simple. H.264 is subject to actively enforced patents. Theora is, from all public appearances and from inferred verdicts of legal reviews by numerous organizations using or implementing Theora, subject to none. To use H.264, you must be sufficiently small as to not be worth the trouble of monetizing, you must find someone willing to acquire a license permitting them to extend to you the privileges you desire, or you must pay the piper his demand. To use, modify, or redistribute Theora requires no permission.

Network effects, unusually strong in technology, ceded the advantage to H.264. The smart money was always on H.264 enjoying continued success and Theora enjoying comparative irrelevance. But for Mozilla “logic” could not be dispositive. The loss of freedom of redistribution and grant of near-unlimited reuse and modification was a dealbreaker for H.264, absent a situation where refusing to deal with the devil (metaphorically speaking) caused greater harm than doing so.

Where faith in a fact can help create the fact, that would be an insane logic which should say that faith running ahead of scientific evidence is the ‘lowest kind of immorality’ into which a thinking being can fall. Yet such is the logic by which our scientific absolutists pretend to regulate our lives!

William James, The Will to Believe

Mozilla (and Opera, I should note) believed it was crucial for implementing, producing, and consuming video on the web to be legal and free to all without restriction. We had faith that it was possible to solve the problem of video on the web in the same way other problems at the foundation of the web were solved: through freely usable standards as happened with TCP/IP, HTTP, DNS, HTML, CSS, ECMAScript, PNG, SVG, JPEG, and many others. Absent this faith and a willingness to translate it into action by not implementing H.264, I doubt even a plausible free solution would have materialized. Google’s motivations surely are not exactly those of Mozilla or Opera, but I believe our actions strongly motivated Google to spend over $130 million to give away a video format.

Google’s release of VP8 and WebM cannot, I think, be attributed to logic alone; it must also be attributed to faith that freely usable video could be reality while evidence counseled otherwise.

07.05.10

SpiderMonkey change du jour: the special __parent__ property has been removed

tl;dr

The special __parent__ property has been removed from SpiderMonkey and is no longer in nightly builds. If you don’t use __parent__ or don’t know what the property does, you didn’t miss much.

If you use __parent__, you have a couple replacements. If you were using it to determine the global object for another object, use Components.utils.getGlobalForObject instead. If you were using it only to test its value against an expected value, use nsIDOMWindowUtils.getParent instead (but do note that its semantics are not absolutely identical to those of __parent__). If you were using it from unprivileged web scripts as a potential vector for security exploits, I feel your pain and will take no steps to assuage it. If you were using it some other way, comment and we’ll figure something out for your use case.

If you think you understood the __parent__ property (you probably don’t), or if you’re interested in the nitty-gritty details of JavaScript semantics, read on for the details of this esoteric property and the reasons for its removal.

Scoping in JavaScript

In the following example code, when the function g is invoked, what stores the v variable?

function f(a)
{
  var v = a;
  function g() { return v; }
  return g;
}

var fun = f(2);
fun();

The variable is accessed in an enclosing scope, created when the enclosing function was invoked. In the ECMAScript standard, the location of such variables is an object, stored as an internal [[Scope]] property of the function being called. In ES3 this object was a standard JavaScript object; ES5 tightens semantics slightly and uses a simpler structure, but the idea’s the same.

Meet the __parent__

It’s not possible to access the object stored as [[Scope]] in ECMAScript proper, but it has been possible to access it in SpiderMonkey and Mozilla-based browsers. The magical __parent__ property can often provide access to this value:

this.toString = function() { return "global"; }
var q = 17;
function foo() { return q; }
print(foo.__parent__); // prints "global"

“Often”? When does __parent__ not expose this value?

__parent__ doesn’t always reflect [[Scope]] because ECMAScript requires that certain objects not be made available to scripts. Among such objects are what SpiderMonkey refers to as With objects, Call objects, and Block objects.

With objects

SpiderMonkey creates With objects to handle the esoteric, non-lexical name lookup required by the semantics of the with statement in JavaScript. These semantics require that a name, depending on the runtime object used in the with, refer to a property of that object or to a variable in an enclosing scope. It’s impossible to know in general which will be the case before runtime, and therefore it’s impossible to speed up a lot of script that runs inside a with. (Incidentally, this is why you should never ever ever [ever] use with in performance-critical code. Use a two-letter abbreviation variable to save typing, if verbosity is your concern.) We can’t simply use the with block’s object as the scope, because if we miss on a lookup there we want to fall back to the normal scope; therefore we introduce a With object. Here’s an example of a situation where a With object is created:

with ({ toString: function() { return "with object"; } })
{
  print(function() { return toString; }.__parent__); // prints "with object"
}

__parent__ doesn’t actually give you the With object, because it’s a carefully-tuned internal value. With objects have behavior and functionality optimized for their particular purpose, and if we simply exposed them to scripts it would probably be possible to do Very Bad Things. Consequently, if you try to access __parent__ in a situation where you “should” get a With object, we instead give you back the object where the With object begins its search: the object used for the with block. This may be what a developer superficially familiar with __parent__ might expect, but it is nevertheless a lie.

Call objects

SpiderMonkey’s Call objects represent the local variables of a function call. Therefore, Call objects correspond to the [[Scope]] of functions. Returning to the original example, reproduced below, v is stored in a Call object, which is the [[Scope]] of the function g:

function f(a)
{
  var v = a;
  function g() { return v; }
  return g;
}

var fun = f(2);
print(fun.__parent__);

When you attempt to access g‘s __parent__ property, SpiderMonkey censors the [[Scope]] and returns null, because ECMAScript requires that Call objects not be exposed. This case, which is far more common than the with case, completely prevents access to [[Scope]] at all (rather than exposing a half-representation of the value). This situation is even more pervasive in light of the modern JavaScript encapsulation practice of enclosing libraries in closures for pseudo-namespacing purposes. In many libraries __parent__ on many functions of interest gives no value whatsoever.

Block objects

One of the first things every JavaScript developer learns is that JavaScript variables are not scoped to blocks. Language tutorials usually emphasize this point, because it differs from most other languages (and in particular, it differs from the C-ish family of languages, the syntax of which JavaScript borrows to a fair degree). The conventional wisdom is that names in functions are always scoped to enclosing functions or to the global scope. If you want a locally-defined name, you have to wrap it up in a function.

The conventional wisdom is wrong.

Here’s proof: what does this example print?

var v = "global";
function test(frob)
{
  try
  {
    if (frob === 0)
      return v + " try";
    if (frob === 1)
      throw "local";
  }
  catch (v)
  {
    return v + " catch";
  }
  finally
  {
    if (frob === 2)
      return v + " finally";
  }
  return v + " after finally";
}

print("not throwing, in try:          " + test(0));
print("throwing, in catch:            " + test(1));
print("not throwing, in finally:      " + test(2));
print("not throwing, after finally:   " + test(3));

Behavior depends on whether the binding for v as introduced by the catch is scoped to the function or to something else. JavaScript specifies that v, referring to the value potentially thrown while executing the try block, is scoped only to the catch block. Thus v in the catch refers to the thrown value; all other uses of v are outside the catch block and refer to the global variable v. Therefore the output is this:

not throwing, in try:          global try
throwing, in catch:            local catch
not throwing, in finally:      global finally
not throwing, after finally:   global after finally

Any function which referred to v inside the test function would have a Call object as its [[Scope]] — except for a function defined inside the catch block. Such a function at that location would have to capture the thrown value, so there must be something else on the scope chain before the Call object. SpiderMonkey refers to these objects as Block objects, because they implement traditional block-level scoping.

Block objects have the same issues as Call objects, so SpiderMonkey censors them to null as well. For optimization purposes we’d like to “boil them away” whenever possible, so as not to create an obnoxious little one-property object when we catch an exception and define a function that captures it. Exposing such an object directly prevents these optimizations, and would likely expose some security vulnerabilities.

But __parent__‘s issues don’t stop merely at those induced by complex language semantics. Even perfectly simple code — simpler even than nested functions — has potential for unpredictability.

If certain optimizations are implemented, __parent__ may be a lie

Consider this script in the global scope:

function fun() { return 17; }

print(fun.__parent__);

Does fun‘s [[Scope]] need to be the global object?

It’s OK to cheat if you never get caught.

Smalltalk implementer maxim

ECMAScript provides no way for script to access [[Scope]]. It’s a construct used to ease specification, and it need not even exist in implementations. fun never uses any values from its enclosing scope, so there’s no reason that [[Scope]] must be global object. An implementation that carried around [[Scope]] with each function might simply make [[Scope]] be null, which could potentially speed up some garbage collection algorithms. Similarly, a function which refers only to never-modified variables in enclosing scopes might simply copy those variables’ values and, again, make [[Scope]] lie. Should a specification-artifact internal property constrain implementations looking to improve performance, or code size, or any number of other measures of desirability? Given where JavaScript use is heading now, the answer must be no.

For certain types of functions, there are very good reasons, even in simple cases, to make [[Scope]] a lie. SpiderMonkey implements some optimizations along these lines, but those optimizations don’t affect the value we store for [[Scope]] at the present. If it were advantageous to change that, we would do so in a heartbeat. Therefore, while __parent__ may have a reliable value now, it’s possible it would not in the future.

__parent__ is available even where you think it isn’t

Oddly enough, __parent__, despite its representing [[Scope]], isn’t just applicable to function objects. Instead, its value is generalized to all objects, in a way even more difficult to describe than that above. (Very roughly, __parent__ on non-function objects corresponds to the object which would be the __parent__ of a function created in the same context. I think. Mostly the value is used for security checks; improper setting of __parent__ is a common cause of implementation-caused XSS vulnerabilities.) This generalization and its semantics have even less support in the specification, so its meaning is even less clear than for functions.

__parent__ doesn’t expose anything useful

__parent__, beyond having unintuitive behavior, doesn’t tell the developer anything he’d care to know. Why would you need to access the enclosing scope of a function, as an object, in the first place? The very definition of the problem is obscure; it is almost a prerequisite to have read the ECMA-262 specification to be able to ask the question.

In the searching of various codebases that we’ve done, we’ve found only these use cases for __parent__:

Testing
A small number of Mozilla tests use __parent__ to verify proper scoping of objects. Since the value as we use it has security implications, this use case is reasonable — but it has extremely limited applicability. This use case can be supported by creating an alternate means of accessing an object’s parent, a means exposed through XPCOM, not through JavaScript itself, and certainly not by polluting every object with the functionality. We have implemented nsIDOMWindowUtils.getParent(obj) to support this testing-oriented use case. Beware: this method doesn’t censor like __parent__ does! Tread cautiously when using it where a With, Call, or Block object might be exposed, and don’t use the returned object except in immediate strict equality (===) checks. (NB: we’ve implemented it this way out of expediency; if it turns out to be too great a responsibility for typical testers, we’ll likely change it to censor.)
Determining the global context (global object) where an object was created
Since __parent__ often (for any function not nested in another function) is simply the global object, some people have used __parent__ to retrieve the global object associated with an arbitrary object. By walking from __parent__ to __parent__ you can often reach the global object this way. However, since the __parent__ value is sometimes a lie, it’s not as simple as var global = obj; while (global.__parent__) global = global.__parent__;. Instead, you have to be careful to start from an object with a known __parent__, one guaranteed not to be a nested function or similar. The easiest way to do this is to first walk up the prototype chain to eventually bottom out at Object.prototype, then to walk the parent chain. This method is baroque and non-obvious, and it requires careful tiptoeing around incorrect __parent__ values. If you know the object whose global you want to retrieve is a DOM node, the fully-standard node.ownerDocument.defaultView property also provides global access. Otherwise, the use case would be better addressed by exposing a method somewhere to directly retrieve the corresponding global object. We have recently added such a method to support this use case: Components.utils.getGlobalForObject(obj).
Being evil to Firefox
A fair number of security bugs reported against SpiderMonkey in recent years have worked by successfully confusing the engine into assigning a bad parent to an object. This incorrect security context can then be used as a vector to run code in the context of another website (resulting in an XSS hole, possibly usable against any website), or in some cases, in the context of the browser “chrome” itself (providing the capability for arbitrary code execution — the worst-case scenario as far as exploits are concerned). __parent__ is immutable and can’t itself be used to confuse the engine (at least in the absence of bugs, and we have no way to demonstrate such). However, it provides greater visibility into the engine, and it can sometimes expose values to script which wouldn’t (and shouldn’t) be accessible any other way.
Being evil to sandboxing mechanisms
__parent__ exposes security holes in some websites as well as in the browser directly. Such websites are those which purport to “safely” execute scripts provided by other users, through sandboxing or other mechanisms. For example, Facebook uses FBJS, a script-rewriting plus runtime-check mechanism, to allow applications to include interactivity. Google‘s Caja system provides a rigorous capabilities-providing system (also through script-rewriting plus runtime checks) to do the same, and it too is used in public sites these days. One requirement of such systems is that the global object must never be directly exposed: if you have the global object, you have unfettered access to the document, you have eval, you have Function, and you are utterly and thoroughly hosed. __parent__ provides easy access to this, so all these systems must censor access to __parent__, both statically (obj.__parent__) and dynamically (var p = "__parent__"; obj[p]). Not everyone knows, or remembers, that this is necessary. Removing __parent__ reduces attack surface in JavaScript sandboxes.

The former two use cases are better addressed in other ways. The latter two are holes best removed entirely.

__parent__ is being removed

__parent__ is being removed from SpiderMonkey. The value it purports to expose is esoteric and has semantics defined by a specification which JavaScript developers shouldn’t really have to read to understand it. The identification of that value is non-intuitive. Sometimes the value it exposes is a lie. There are plausible reasons why it might be made to lie if potential performance-improving ideas were implemented. It’s exposed even in places where it makes little sense. The use cases for it can be and are better served in other ways — or explicitly not served, when it serves as a vector for security vulnerabilities. In sum __parent__ doesn’t pass muster, so it’s being removed.

You can experiment with a version of Firefox without support for __parent__ by downloading a nightly from nightly.mozilla.org. (Don’t forget to use the profile manager if you want to keep the settings you use with your primary Firefox installation pristine.) The next release is many months away, which should provide plenty of time for extension developers to update their extensions to not use __parent__, if by chance they had managed to discover it and use it.

05.05.10

New Mozilla developer feature: Components.utils.getGlobalForObject(obj)

Suppose you’re an extension developer implementing some sort of event listener-like interface corresponding to browser windows. You’d like listeners to stick around as long as the original browser window is open, so when the browser window’s unload event fires when the browser window is closed, you want to remove the listener. Further, for simplicity, you’d like to be able to reuse the same interface as the DOM uses: EventTarget.addEventListener(eventName, listener, bubbles). But if you do that, how do you know what browser window to associate with a listener? One possibility is that you could associate event listeners with windows if you could determine the global object that corresponded to the event listener in question. (Assume arguendo that you control all use of listeners, so every listener is straightforwardly created in the window with which it’s associated — no shenanigans passing listeners across windows.) This isn’t the only situation where one might wish to know the global object, but it does happen to be a somewhat common one.

Is it possible to determine the global object corresponding to an arbitrary object? You can through a convoluted sequence of actions involving prototype hopping using Object.getPrototypeOf and walking the “scope chain” for the object (using an obscure Mozilla-specific feature whose details I omit). More simply, if the object in question is a DOM node, you could use node.ownerDocument.defaultView, which, while quite understandable, is still DOM-specific. But wouldn’t it be much better if there were some simpler, universal way to determine this value, rather than skirting the edges of feature intentionality?

Firefox nightlies now implement support for a new method available to extensions and other privileged code: Components.utils.getGlobalForObject(obj). It’s designed to support the specific need of determining the window where an object was created. (XPCOM components of course have a global object that isn’t a window, and that global object will be returned by the method in those circumstances.) Its functionality needs little explanation:

/* in the global scope */
var global = this;
var obj = {};
function foo() { }
assertEq(Components.utils.getGlobalForObject(foo), global);
assertEq(Components.utils.getGlobalForObject(obj), global);

If you’re using the previously-noted method involving prototype- and scope-chain-hopping, you should change your code to use Components.utils.getGlobalForObject(obj) instead. This new method is much simpler and clearer, and upcoming changes mean that the old method will no longer work in future nightlies and releases. The next release is still several months out, so you should have plenty of time to adjust.

As always, you can experiment with a bleeding-edge version of Firefox that supports Components.utils.getGlobalForObject(obj) by downloading a nightly from nightly.mozilla.org. (Don’t forget to use the profile manager if you want to keep the settings you use with your primary Firefox installation pristine.)

A last note: the method as currently implemented in nightlies suffers from a small bug when used with objects from unprivileged code — objects from scripts in web pages, that sort of thing. It’s fixed in the TraceMonkey repository, and the adjustment should make its way into the mozilla-central repository (and thus into nightlies) in short order. If you only use the method on objects from privileged scripts, I don’t believe you’ll encounter any problems.

16.04.10

More SpiderMonkey changes: ancient, esoteric, very rarely used syntax for creating getters and setters is being removed

tl;dr

We’ve removed support for a handful of obsolete getter/setter syntaxes in SpiderMonkey and Mozilla. This does not include { get property() { return "value"; }, set property(v) { } }, which is widely used and which is part of the latest standard. If you don’t get any syntax errors with your code, you don’t need to worry about this. If you do, skip to the end for details on how to adjust your code to cope. But really, you should read it all for the sheer joy of learning about all sorts of awful syntax you didn’t even know existed before it went away. [Or at least empathize with us liberated SpiderMonkey hackers. :-D])

Properties in JavaScript and ECMAScript 3

The fundamental data structure in JavaScript is the object: a container mapping names to values through properties. You can add, remove, or change the value associated with any property, so long as the property may be modified. All user-defined properties are infinitely modifiable in any of these ways; only a few properties defined by ECMAScript (the standard on which JavaScript is based) are not fully modifiable.

var obj1 = {};
obj.property = 17; // add
var obj2 = { property: 42 }; // add from birth
obj2.property = 17; // change it
delete obj2.property; // remove it

Properties which store values are useful, but what if you want properties which can do things when you interact with them? What if you want to have properties which map strings to lazily-computed values? Or what if you want setting a property to have side effects (as, for example, setting an array’s length to 0 removes all elements in it)?

Properties with getters and setters in JavaScript

If you want properties which have functionality beyond just holding a value, you need getters and setters, stored within accessor properties. (Properties which hold values are called data properties.) JavaScript has long included extensions to ECMAScript to create accessor properties, both syntactic:

var o1 =
  {
    get property() { print("gotten!"); return "get"; },
    set property(v) { print("sotten!  " + v); }
  };
var v1 = o1.property; // prints "gotten!", v1 === "get"
o1.property = "new"; // prints "sotten!  new"

…and programmatic:

var o2 = {};
o2.__defineGetter__("property", function() { print("gotten!"); return "get"; });
o2.__defineSetter__("property", function(v) { print("sotten!  " + v); });
var v2 = o2.property; // prints "gotten!", v2 === "get"
o2.property = "new"; // prints "sotten!  new"

Getters and setters are now part of ES5. The syntax demonstrated above is valid ES5; a different API, Object.defineProperty, provides more flexible support for specifying getters and setters dynamically. Developers using the old-school APIs should begin updating to use the new API as browsers make new releases supporting it. Firefox in particular will include support for Object.defineProperty in its next major release, likely to occur in the latter half of the year.

Examining antediluvian accessor syntax

Unbeknownst to the vast majority of web developers, extension developers, and even Mozilla developers, in the past JavaScript has included other getter and setter syntaxes.

Named ES5-like getters and setters

If you look up the function that acts as the getter given ES5-standard getter syntax, what’s the name of that function?

var o = { get property() { return "get"; } };
print(Object.getOwnPropertyDescriptor(o, "property").get.name);

There are a couple plausible answers here: “anonymous“, “property” (the property name), or “” (the empty string) might be reasonable. JavaScript and ES5 arbitrarily create the getter or setter as a function whose name is the empty string. What if you wanted to name that function? (Bear with me for a moment and pretend this is a compelling need, and that adding a named getter or setter programmatically is absolutely unacceptable.)

Solely by accident of implementation, in the past SpiderMonkey has parsed the following syntax to assign names to getter and setter functions:

var o = { get property getter() { return "get"; } };
// Prints "Name: getter" in past versions of SpiderMonkey (or would if
// Object.getOwnPropertyDescriptor existed; __lookupGetter__ is a
// simple workaround); previous line is syntax error elsewhere
print("Name: " + Object.getOwnPropertyDescriptor(o, "property").get.name);

SpiderMonkey internally implemented the parsing of literal getters and setters by parsing them as though the start of a function expression had just been parsed:

// Faked-up parser state when parsing normal getters/setters
var o = { get property () { } };
                       ↑
function () { }
         ↑

Function expressions may be named or unnamed, but this wasn’t originally considered, so in the above example getter is treated as the name of the function created to correspond to the getter:

// Faked-up parser state when parsing named-getter-function syntax
var o = { get property getter() { } };
                       ↑
function getter() { }
         ↑

No other JS engine accepts this unintentional accessor-method name token.

Getters and setters in object literals

Possibly the best-known additional syntax is for specifying getters and setters in object literals. This syntax was the original Netscape invention for getters and setters; in practice it was superseded by the newer, more function-looking syntax. SpiderMonkey is again the only engine to implement it.

function g() { print("gotten!"); return "get"; }
var o1 =
  {
    property getter: g,
    property setter: function(v) { print("sotten!  " + v); }
  };
var v1 = o1.property; // prints "gotten!", v1 === "get"
o1.property = "new"; // prints "sotten!"

This accessor property syntax has one large advantage over the more-common syntax previously demonstrated (and even over the unintentional named-accessor mistake shown in the previous section). Where you see property in the object literal above, you could instead see a numeric literal, or a string literal — just as you might see either in any object literal without getters or setters, e.g. { 1: "value", "o": "hai" }. Historically, in get property() { ... }, property was required to be an identifier, thus excluding numbers and non-identifier accessor properties from representation. The syntax here had the further advantage of allowing serialization to “source” (more accurately, a reasonable but not always equivalent facsimile) of objects containing non-identifier-named accessor properties, through another Netscape extension in JavaScript.

This syntax also has a few disadvantages. Since the getter and setter contextual keywords follow the property name, the eye must scan past the property name to determine whether a portion of a literal represents a data property or an accessor property. This special-case check also complicates parsing, because now the parser has to check for something beyond just a colon at such locations. (To be sure, this problem exists with get foo() { }, but it’s restricted to the single leading token get, not to all leading tokens.) Since the value assigned to the getter is parsed as an arbitrary expression, there’s no guarantee the value must be a function — that must be checked at runtime.

Assigning getters and setters to properties

This accessor syntax provides the same functionality as Object.defineProperty(obj, propname, { get: fun, enumerable: true, configurable: true }) (mutatis mutandis for setters), except as part of the language syntax rather than as part of its standard library. Again, no other engine has implemented this syntax.

var o = {};
o.property getter = function() { print("gotten!"); return "get" };
o.property setter = function() { print("sotten!"); };
var v = o.property; // prints "gotten!", v === "get"
o.property = "new"; // prints "sotten!"

This syntax is also obscure: outside SpiderMonkey source and test files, only a single file in the Mozilla source code uses it. Strangely, a trawl through AMO shows half a dozen extensions have managed to discover this syntax, despite its near-complete disuse in Mozilla itself.

Assigning getters and setters to names rather than properties

Syntactically, this is just a different flavor of the previous example:

varname getter = function() { return "get"; };
var q = varname; // "get"

Semantically, however, it’s a rather different beast. The problem is that not all names are alike in SpiderMonkey. While ECMAScript specifies all name accesses in terms of objects (pure-JS objects in ES3, tighter spec-internal artifacts in ES5), most if not all JS engines out there optimize name access based on the type of the name. Local and enclosing variable access may be some number of pointer jumps, comparisons, and an offset, rather than some sort of hash table lookup in a more general case. Global variable access can in many circumstances skip lookups in enclosing scopes, going to the global object directly. (Last and certainly least, variable access inside with almost necessarily must be essentially unoptimized and dog-slow. Friends don’t let friends use with!) These sorts of optimizations rely on names always being plain old values, not accessors (except in the global case, where the type of optimizations implemented are qualitatively quite different). Slowing down local or enclosing variable accesses just to support this very rare case would be insane.

SpiderMonkey actually hasn’t supported this syntax for awhile. I only mention it because SpiderMonkey includes code specifically to exclude it. If this syntax is seen and varname can be resolved to a var, it’s a compile-time syntax error. Otherwise, if varname resolves to a var at runtime (possible in the presence of with or eval), it’s a runtime TypeError. Last, if it doesn’t, it “works” — and you are most likely Jesse, combining syntax and features in obscure and evil ways solely to make SpiderMonkey developers’ lives hard. 😉 In sufficiently old versions of Firefox where these restrictions weren’t in place, it’s entirely conceivable this syntax may have resulted in security vulnerabilities (one large factor in its removal from SpiderMonkey).

Prefixed function expressions

Perhaps the most bizarre getter/setter syntax is a modification of the syntax for function expressions and statements. As with all the others, this syntax has only been implemented by SpiderMonkey.

getter function foo() { return "foo getter"; };
var v = foo; // "foo getter"
var q = setter function bar(v) { };

When the prefixed function is a statement in the global scope, the syntax is equivalent to Object.defineProperty(globalObject, "foo", { get: function foo() { /* ... */ }, enumerable: true, configurable: true }) (mutatis mutandis for setter). If it’s a statement in a function scope or an expression that’s not a statement, the prefix serves no purpose that I can discern, except that it affects Function.prototype.toString()‘s behavior by including the prefix in the returned string.

None of these old getter/setter syntaxes provide value

Now that ES5 has codified The One True Syntax and The One True Programmatic API, these older syntaxes bring little to the table.

  • The mistaken ES5-like named accessor get property funname() { } syntax doesn’t satisfy a compelling need.
  • property getter: in object literals provides one compelling feature: the ability to have non-identifier-named properties. As ES5’s get property() { } syntax includes these further extensions beyond what engines have already implemented, this advantage no longer exists:
    var o =
      {
        get name() { return "names valid"; },
        set break() { this.x = "keywords too"; },
        set 1() { this.y = "numeric literals also accepted"; },
        get "custom string"() { return "arbitrary string literals too!"; }
      };
    

    (property getter: has a final advantage with respect to an ancient Netscape extension, but given that extension’s dubious future I will omit the details. Suffice it to say the use case is highly esoteric, and reasonably graceful degradation is possible without property getter:.)

  • getter = and getter function are fully subsumed by Object.defineProperty.
  • varname getter = was already gone.

In sum: these syntaxes make some things slightly easier, but they don’t provide anything you can’t do with ES5’s standardized accessor support.

These syntaxes were the source of numerous bugs

In addition to not being particularly useful, these syntaxes imposed notable costs on development. Supporting so many different getter and setter syntaxes isn’t easy, and the relevant code paths are quite complicated, attempting to decide when which syntax is correct and when not (particularly as far as object serialization is concerned). This has resulted in a multitude of accessor bugs usually found by Jesse‘s fuzz-testing and almost never by real-world scripts: bugs which, in C or C++, can often lead to memory-unsafety and, in the extreme, arbitrary code execution. By my count SpiderMonkey has sixteen separate tests (corresponding to the same number of bugs) dedicated to edge cases and corner behaviors with these syntaxes: syntaxes no one uses, syntaxes superseded by newer and better ones, and syntaxes which no other JS engine currently supports, nor ever will support.

These syntaxes continue to impose costs on development. Not all related bugs have been fixed, and changes to nearby code do have to take account of this syntax. We have had at least one long-standing (but believed “mostly harmless”, in that a sanity-check fails but surrounding defensive code completely contains the problem) bug involving this syntax, which due to its relative harmlessness has gone unfixed for nearly three years (and, almost as bad, undiscovered for two of them). Recent implementation work on ES5’s strict mode support required adjustments to the area of parsing object literals (for ES5’s strict mode rejection of duplicate property names), adjustments required to work around support for these syntaxes.

In short, TANSTAAFL. We’ve paid a large cost to keep these syntaxes around, and we continue to pay to keep them around — sometimes directly, sometimes indirectly, but unavoidably if support is worthwhile.

Support for all non-ES5 accessor syntaxes has been removed from SpiderMonkey

But for the many reasons previously given, support for these obsolete syntaxes is not worthwhile, so we have removed them from SpiderMonkey. get property funname() { } was an error from the start that no one will miss. SpiderMonkey has recently implemented support for ES5 numeric- and string-literal accessor property names (support for keyword names already exists), so the remaining important use case for property getter: has been eliminated. The getter = and getter function syntaxes never provided extra value, so they too have been removed without qualms.

To give an idea of the complexity eliminated by removing these syntaxes, the patch to remove them added 116 lines of code but removed 313 lines of code. Outside of code changes (that is, adjusting or removing tests which used these features), it added 133 lines but removed 1213 lines. It’s always great deleting code like this. 🙂

Updating existing code to adapt to these removals

One nice feature of removing syntax is that the failure mode when that syntax is encountered is blindingly obvious: the script will fail to parse. Parse errors show up in the JavaScript console, so it’s easy to tell when this is the problem; SpiderMonkey’s excellent error messages should point directly at the offending location.

If by chance you do actually use any of these syntaxes, the necessary fixes are simple. Suppose the existence of these helper functions:

function accessorDescriptor(field, fun)
{
  var desc = { enumerable: true, configurable: true };
  desc[field] = fun;
  return desc;
}

function defineGetter(obj, prop, get)
{
  if (Object.defineProperty)
    return Object.defineProperty(obj, prop, accessorDescriptor("get", get));
  if (Object.prototype.__defineGetter__)
    return obj.__defineGetter__(prop, get);

  throw new Error("browser does not support getters");
}

function defineSetter(obj, prop, set)
{
  if (Object.defineProperty)
    return Object.defineProperty(obj, prop, accessorDescriptor("set", set));
  if (Object.prototype.__defineSetter__)
    return obj.__defineSetter__(prop, set);

  throw new Error("browser does not support setters");
}

Here’s how you can update each old syntax to work again:

get property funname() { }
var o = defineGetter({}, "property", function funname() { });
property setter: fun
var o = defineSetter({}, "property", fun);
obj.prop getter = fun
defineGetter(obj, "prop", fun);
setter function prop() { } (when at global scope; otherwise just remove the setter prefix)
defineSetter(obj, "prop", fun);

You can experiment with a version of Firefox with support for these obsolete syntaxes removed by downloading a nightly from nightly.mozilla.org. (Don’t forget to use the profile manager if you want to keep the settings you use with your primary Firefox installation pristine.)

A brief word on __defineGetter__ and __defineSetter__

As you may have noticed, all examples here use Object.defineProperty in preference to either __defineGetter__ or __defineSetter__, using the latter two only as fallback when the former is absent. While many browsers support these methods, not all do. Object.defineProperty is the future, and it is the standard; Microsoft has even gone on the record to say that they will not implement __defineGetter__ or __defineSetter__ in IE given the existence of the standardized method (props to them for that choice, by the way). For greatest forward compatibility with all browsers, you should use Object.defineProperty if it exists, and only fall back to __define{G,S}etter__ if it does not.

In a distant future we would like to remove support for __defineGetter__ and __defineSetter__, after ES5 adoption has taken off, so as not to distract from the standardized support. The less new web developers have to know about legacy extensions superseded by standardized alternatives, the better. This action is at least several years in the future, likely longer; being able to make the change will require preparation and adjustment in anticipation of that time. Given upcoming releases of browsers supporting ES5 functionality, there’s no better time than the present to start gradually, and gracefully, adopting standardized methods over legacy alternatives.

« NewerOlder »