I called it rhino-8 because I started out on the idea of native JS
property access implemented by Google's V8 engine described here:
http://code.google.com/apis/v8/design.html#prop_access
Obviously, the JVM poses some restrictions on what you can do (for
example, to invoke a method or access a field in a class from Java
bytecode, the target interface or class must be "baked" into the local
class so it can't really be generated on the fly). So I went for a
more conservative approach which has the benefit of also being more
compatible with the Rhino script runtime.
What I am currently doing is to generate a Java class implementing
Rhino's Scriptable interface for each individual set of properties,
store property values in a dense object array, and generate switch
statements in the get(), put(), has(), and delete() methods similar to
those created by the IdSwitch tool to directly map the object's
properties into the value array *). When put() is called with an
unknown property id, it throws an UnknownFieldError that is caught by
a wrapper which transparently "morphes" the object to a newly
generated class that includes the new property id.
*) Note: I previously used individual native Java fields to store
property values but changed to the array representation for easier
transition between classes. I may go back to individual fields if it
benefits performance.
In other words, I'm providing a faster implementation of Scriptable by
implementing property access methods using a switch statement for a
well-known set of properties similar to the one used for NativeObject,
NativeString, NativeArray etc. prototypes instead of the generic slot
hashing implemented by ScriptableObject.
The code for this is in the org.mozilla.javascript.adaptive package:
http://github.com/hns/rhino-8/tree/master/src/org/mozilla/javascript/adaptive
You can use the Rhino shell's defineClass() function to play with it:
js> defineClass(org.mozilla.javascript.adaptive.AdaptiveObject);
js> a = new AdaptiveObject();
On initial benchmarks, this yields something like 30% - 50%
improvement in property access time compared to the current
ScriptableObject property implementation (quite a bit when using the
server HotSpot VM, which obviously does its own optimization magic on
ScriptableObject). Of course the approach won't work for all
situations. If you create an object and subsequently add properties to
it, a new Java class is generated in each step, and the object is
"morphed" to the new class in each step. So this isn't a magic pill
that will bring Rhino performance on par with V8 (not even close), but
I think it's an approach worth following. The good thing is that it
should be relatively easy to use different object implementations for
different situations, or switch between them as needed.
For example, one area where this would most probably make immediate
sense is object literals. Object literals have a set of properties
that is known beforehand, and they are often read-only and used over
and over again, so creating an optimized Java class for them should be
worth the overhead most of the time. Other ideas I'm playing with may
be to initially use a hashed object and watch usage patterns to decide
when and if to switch to compiled, "switched" state, or more simply to
delay class generation until the first invocation of get(), which
might work well for many common JS programming patterns.
Unfortunately, a lot of Rhino code currently assumes things to be
ScriptableObject instances, so it's not easy to test other object
implementations "in the wild". For example, Object.defineProperty
assumes not only the target object to be an instance of
ScriptableObject, but also the property descriptor argument. So one of
the next steps will be to factor the "advanced" property access
methods implemented by ScriptableObject into a new sub-interface of
Scriptable (I'll probably call it
org.mozilla.javascript.ExtendedScriptable - any other suggestions?)
and use that wherever ScriptableObject is used. That won't be fun, so
maybe as a short-term hack I'll just extend ScriptableObject in
AdaptiveObject and override the required methods.
I'm looking forward to your feedback and suggestions!
Hannes
This should have read:
... (quite a bit __less__ (performance improvement) when using the
server HotSpot VM, which obviously does its own optimization magic on
ScriptableObject).
Hannes
Just to highlight some other work in the area:
John Rose was experimenting with a MethodHandle (the invokedynamic
stuff slated for Java 7 as per JSR-292) based approach with Rhino, he
forwarded me some of his code doodles for it (I gave them to Norris
since, I'll send them to you too so you can take a look).
I'm of course thinking that next time we'd need to overhaul Rhino
would be when Java 7 is widespread, and actually rewrite it in terms
of my invokedynamic-based linker framework, see slides of the
presentation I gave at JVM Language Summit here: <http://wiki.jvmlangsummit.com/MOP_and_Invokedynamic
>; the slides have a link to the SVN repository too. That'll make it
possible to emit bytecode of the form "invoke method named
dyn:getProp:foo" and have the linker framework resolve it to a fully
optimizable (inlinable) method call on the object of appropriate type
at the call site just before it is invoked.
You can also get a working OpenJDK build with invokedynamic in it for
Mac OS X at <http://www.szegedi.org/mlvm-macosx.html> (for Windows and
Linux, you can just download the latest OpenJDK beta from Sun)
Of course, that's all well and good, but we'll need Java 7 for it to
work :-) (Or, alternatively, Rémi Forax's backport of JSR-292)
As for Rhino internals relying on ScriptableObject, I believe these
should be considered bad practice. All Rhino internals should ideally
only rely on Scriptable interface. I think you can liberally change
code from using ScriptableObject to Scriptable whereever you can.
In any case, I'll make sure to take a look at your effort sometime
when I get a bit of time (maybe over weekend).
Attila.
At least I learned a lot about Java bytecode. Maybe I'll use that to
dig deeper into the rhino optimizer code.
On Nov 3, 2:39 pm, Attila Szegedi <szege...@gmail.com> wrote:
>
> John Rose was experimenting with a MethodHandle (the invokedynamic
> stuff slated for Java 7 as per JSR-292) based approach with Rhino, he
> forwarded me some of his code doodles for it (I gave them to Norris
> since, I'll send them to you too so you can take a look).
That sounds very interesting, I'd love to see this.
> I'm of course thinking that next time we'd need to overhaul Rhino
> would be when Java 7 is widespread, and actually rewrite it in terms
> of my invokedynamic-based linker framework, see slides of the
> presentation I gave at JVM Language Summit here: <http://wiki.jvmlangsummit.com/MOP_and_Invokedynamic
> >; the slides have a link to the SVN repository too. That'll make it
> possible to emit bytecode of the form "invoke method named
> dyn:getProp:foo" and have the linker framework resolve it to a fully
> optimizable (inlinable) method call on the object of appropriate type
> at the call site just before it is invoked.
I must admit I don't know a lot about invokedynamic, but it definitely
sounds like the way to go. I'm reading up on it now.
> You can also get a working OpenJDK build with invokedynamic in it for
> Mac OS X at <http://www.szegedi.org/mlvm-macosx.html> (for Windows and
> Linux, you can just download the latest OpenJDK beta from Sun)
>
> Of course, that's all well and good, but we'll need Java 7 for it to
> work :-) (Or, alternatively, Rémi Forax's backport of JSR-292)
>
> As for Rhino internals relying on ScriptableObject, I believe these
> should be considered bad practice. All Rhino internals should ideally
> only rely on Scriptable interface. I think you can liberally change
> code from using ScriptableObject to Scriptable whereever you can.
The thing is that Scriptable is not enough for most things (getters/
setters, attributes, etc.) so I thought of introducing an extended
scriptable interface for these purposes. Alas, it would be necessary
to touch a lot of code and I'm not really keen on doing that. Maybe we
should start thinking about a release that introduces some non-
backward-compatible changes and API cleanup?
Hannes
> >http://github.com/hns/rhino-8/tree/master/src/org/mozilla/javascript/...