[Vm-dev] [VM-dev] Can we be a JavaScript engine?

Eliot Miranda eliot.miranda at gmail.com
Tue Feb 20 20:51:16 UTC 2018


Hi Denis,

On Tue, Feb 20, 2018 at 6:56 AM, Denis Kudriashov <dionisiydk at gmail.com>
wrote:

>
> Hi.
>
> It is just a question from the space :)
>
> I wonder, can we be a JavaScript engine? Like replacing nodeJs with
> Pharo/Squeak.
> I imagine that JS prototypes can be easily supported by anonymous classes.
> What is missing?
>

Well, Vassili Bykov and I did a JavaScript implementation above VisualWorks
in 2005 is.  One can do a very good job.  There are three strong ideas one
can use.  The first two are Vassili's, the last is Claus Gittinger's.

Vassili's ideas were to
a) analyse constructors and map the accessors in constructors to inst var
offsets.  If one does inst var access by property lookup (an object is a
dictionary from field name to field) performance will be very poor.  If one
can analyze a constructor and map each field to an inst var offset
performance can be much better.

b) the first field of any object is the prototype slot which points to the
prototype or nil.  An inst var holding nil means "unbound".  Therefore a
new JavaScript object will have all its fields unbound.  To access an
object's field Vassili minted special accessors, one for each offset.  They
can't be written directly in Smalltalk but can be expressed using Smalltalk
bytecodes.  They look like this, where instVarAt: is actually a bytecode
that fetches that inst var from the receiver

    | current value |
    current := self.
    [current isNil ifTrue: [self error: 'field not found'].
     value := current instVarAt: N. "N is the offset of the inst var this
accessor should return"
     value ~~ nil ifTrue: [^value].
     current := current instVarAt: 0] repeat

or in bytecode:

pushReceiver
popIntoTemp: 0 "set current to be the receiver"
L1:
pushTemp: N inVectorAt: 0.  "fetch Nth field of current"
dup
pushConstant: nil  "if Nth field is non-nil (is bound) answer it"
send: ~~
jumpFalseTo: L2
returnTop
L2:
pop  "discard nil"
pushTemp: 0 inVectorAt: 0.  "fetch prototype field of current"
dup
pushTemp: 0 "if prototype is nil, slot is not found"
pushConstant: nil
send: ==
jumpFalseTo: L3
self
send: errorFieldNotFound
pop
L3:
popIntoTemp: 0 "set current to be the current's prototype"
jumpTo: L1
returnSelf


We used a class per constructor, and were able to get performance about 4x
of Netscape's VM at the time.

Claus' idea is to create a circular instance, an object whose class is the
object itself.   The idea follows from the fact that if one has the
changeClass/adoptInstance primitive one can create a prototype by creating
an Array whose first three fields are laid out like a Behavior's fields,
superclass/prototype, methodDictionsry, format, and whose subsequent fields
are the inst vars of the object.  One then uses adoptInstance/change class
primitive to change the class of the Array to itself, so that it is an
instance of itself; it has its own methods in its methodDictionary, its
superclass field is now the prototype.  The format field must specify a
pointer object with N inst vars.  If using this scheme to implement
JavaScript objects, then slot 4 (immediately after format) is probably the
sot for dynamically added properties.  And of course one would implement
prototype creation in a special primitive rather than using the standard
adoptInstance: primitive.

Using tricks like these one can get reasonably high performance.  In fact,
at Cadence we have a JavaScript implementation which is has evolved from
Vassili's two ideas.  The implementation is in Newspeak running above the
64-bit Spur VM.


_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20180220/0ccff1d5/attachment-0001.html>


More information about the Vm-dev mailing list