This page is generated from commits to the ease.js repository. This news may also be viewed offline the NEWS file. If you would like to subscribe to release notes, please subscribe to the info-easejs mailing list. For release notes on all GNU software (including ease.js), please subscribe to info-gnu.

Miscellaneous performance enhancements

Mike Gerwitz
17 Apr 2014
    These are the beginning of some smaller performance optimizations brought on
    by the v8 profiler. This includes removal or movement of over-reaching
    try/catch blocks and more disciplined argument handling, neither of which
    can be compiled into machine code (permanently, at least). This also removes
    some unneeded code, adds some baseline performance test cases, and begins
    generic performance test output and HTML generation which will be used in
    the future for more detailed analysis.
    
    This is just a starting point; there's more to come, guided by profiling.
    The trait implementation needs some love and, since its development is not
    yet complete, that will be optimized in the near future. Further, there are
    additional optimizations that can be made when ease.js recognizes that
    certain visibility layers are unneeded, allowing it to create more
    lightweight classes.
    
    Performance enhancements will also introduce the ability to generate a
    ``compiled'' class, which will generate a prototype that can be immediately
    run without the overhead of processing keywords, etc. This will also have
    the benefit of generating code that can be understood by static analysis
    tools and, consequently, optimizers.
    
    All in good time.

Copyright assignment to the FSF

Mike Gerwitz
9 Apr 2014
    Copyright for the GNU ease.js project has been assigned to the Free Software
    Foundation. This allows the FSF to enforce the project licenses, which is
    something that I lack the time and money to do. It further ensures, through
    the contract I signed with the FSF, that all distrbutions of GNU ease.js,
    and all derivatives, will always ``be on terms that explicitly and
    perpetually permit anyone possessing a copy of the work to which the terms
    apply, and possessing accurate notice of these terms, to redistribute copies
    of the work to anyone on the same terms'' and that the project ``shall be
    offered in the form of machine-readable source code''.
    
    Consequently, any contributors to the project (aside from changes deemed to
    be trivial) will be required to assign copyright to the FSF; this puts GNU
    ease.js on a firm legal ground to prevent complicating enforcement.
    Contributors can rest assured that the code they contribute will always
    remain free (as in freedom).
    
    I thank Donald Robertson III of the FSF for his help and guidance during
    this process.

Private methods are no longer wrapped

Mike Gerwitz
20 Mar 2014
    This is an exciting performance optimization that seems to have eluded me
    for a surprisingly long time, given that the realization was quite random.
    ease.js accomplishes much of its work through a method wrapper---each and
    every method definition (well, until now) was wrapped in a closure that
    performed a number of steps, depending on the type of wrapper involved:
    
      1. All wrappers perform a context lookup, binding to the instance's private
         member object of the class that defined that particular method. (See
         "Implementation Details" in the manual for more information.)
      2. This context is restored upon returning from the call: if a method
         returns `this', it is instead converted back to the context in which
         the method was invoked, which prevents the private member object from
         leaking out of a public interface.
      3. In the event of an override, this.__super is set up (and torn down).
    
    There are other details (e.g. the method wrapper used for method proxies),
    but for the sake of this particular commit, those are the only ones that
    really matter. There are a couple of important details to notice:
    
      - Private members are only ever accessible from within the context of the
        private member object, which is always the context when executing a
        method.
      - Private methods cannot be overridden, as they cannot be inherited.
    
    Consequently:
    
      1. We do not need to perform a context lookup: we are already in the proper
         context.
      2. We do not need to restore the context, as we never needed to change it
         to begin with.
      3. this.__super is never applicable.
    
    Method wrappers are therefore never necessary for private methods; they have
    therefore been removed.
    
    This has some interesting performance implications. While in most cases the
    overhead of method wrapping is not a bottleneck, it can have a strong impact
    in the event of frequent method calls or heavily recursive algorithms. There
    was one particular problem that ease.js suffered from, which is mentioned in
    the manual: recursive calls to methods in ease.js were not recommended
    because it
    
      (a) made two function calls for each method call, effectively halving the
          remaining call stack size, and
      (b) tail call optimization could not be performed, because recursion
          invoked the wrapper, *not* the function that was wrapped.
    
    By removing the method wrapper on private methods, we solve both of these
    problems; now, heavily recursive algorithms need only use private methods
    (which could always be exposed through a protected or public API) when
    recursing to entirely avoid any performance penalty by using ease.js.
    
    Running the test cases on my system (your results may vary) before and after
    the patch, we have:
    
      BEFORE:
      0.170s (x1000 = 0.0001700000s each): Declare 1000 anonymous classes with
        private members
      0.021s (x500000 = 0.0000000420s each): Invoke private methods internally
    
      AFTER:
      0.151s (x1000 = 0.0001510000s each): Declare 1000 anonymous classes with
        private members
      0.004s (x500000 = 0.0000000080s each): Invoke private methods internally
    
    This is all the more motivation to use private members, which enforces
    encapsulation; keep in mind that, because use of private members is the
    ideal in well-encapsulated and well-factored code, ease.js has been designed
    to perform best under those circumstances.

Preliminary support for traits as mixins

Mike Gerwitz
15 Mar 2014
    This has turned out to be a very large addition to the project---indeed,
    with this release, its comprehensiveness remains elusive, but this is a huge
    step in the right direction.
    
    Traits allow for powerful methods of code reuse by defining components that
    can be ``mixed into'' classes, almost as if the code were copied and pasted
    directly into the class definition. Mixins, as they are so called, carry
    with them the type of the trait, just as implementing an interface carries
    with it the type of the interface; this means that they integrate into
    ease.js' type system such that, given some trait T that mixes into class C
    and an instance of C, it will be true that Class.isA( T, inst ).
    
    The trait implementation for GNU ease.js is motivated heavily by Scala's
    implementation of mixins using traits. Notable features include:
    
      1. Traits may be mixed in either prior to or following a class definition;
         this allows coupling traits tightly with a class or allowing them to be
         used in a decorator-style manner prior to instantiation.
    
      2. By mixing in a trait prior to the class definition, the class may
         override methods of the trait:
    
           Class( 'Foo' ).use( T ).extend( { /*...*/ } )
    
         If a trait is mixed in after a class definition, then the trait may
         instead override the functionality of a class:
    
           Class( 'Foo', { /*...*/ } ).use( T )
    
      3. Traits are stackable: By using the `abstract override' keyword
         combination, a trait can override the concrete definition of its
         parent, provided that the abstract definition is implemented by the
         trait (e.g. by implementing a common interface). This allows overrides
         to be mixed in any order. For example, consider some class Buffer that
         defines an `add' method, accepting a string. Now consider two traits
         Dup and Upper:
    
           Buffer.use( Dup ).use( Upper )().add( "foo" )
    
         This would result in the string "FooFoo" being added to the buffer.
         On the other hand:
    
           Buffer.use( Reverse ).use( Dup )().add( "foo" )
    
         would add the string "Foofoo".
    
      4. A trait may maintain its own private state and API completely disjoint
         from the class that it is mixed into---a class has access only to
         public and protected members of a trait and vice versa. This further
         allows a class and trait to pass messages between one-another without
         having their communications exposed via a public API. A trait may even
         communicate with with other traits mixed into the same class (or its
         parents/children), given the proper overrides.
    
    Traits provide a powerful system of code reuse that solves the multiple
    inheritance problems of languages like C++, without introducing the burden
    and code duplication concerns of Java's interfaces (note that GNU ease.js
    does support interfaces, but not multiple inheritance). However, traits also
    run the risk of encouraging overly rich APIs and complicated inheritance
    trees that produce a maintenance nightmare: it is important to keep concerns
    separated, creating classes (and traits) that do one thing and do it well.
    Users should understand the implications of mixing in traits prior to the
    class definition, and should understand how decorating an API using mixins
    after a class definition tightly couples the trait with all objects derived
    from the generated class (as opposed to the flexibility provided by the
    composition-based decorator pattern). These issues will be detailed in the
    manual once the trait implementation is complete.
    
    The trait implementation is still under development; outstanding tasks are
    detailed in `README.traits`. In the meantime, note that the implementation
    *is* stable and can be used in the production environment. While
    documentation is not yet available in the manual, comprehensive examples and
    rationale may be found in the trait test cases.
    
    Happy hacking!

Support for stacked mixins

Mike Gerwitz
6 Mar 2014
    The concept of stacked traits already existed in previous commits, but until
    now, mixins could not be stacked without some ugly errors. This also allows
    mixins to be stacked atop of themselves, duplicating their effect. This
    would naturally have limited use, but it's there.
    
    This differs slightly from Scala. For example, consider this ease.js mixin:
    
      C.use( T ).use( T )()
    
    This is perfectly valid---it has the effect of stacking T twice. In reality,
    ease.js is doing this:
    
      - C' = C.use( T );
      - new C'.use( T );
    
    That is, it each call to `use' creates another class with T mixed in.
    
    Scala, on the other hand, complains in this situation:
    
      new C with T with T
    
    will produce an error stating that "trait T is inherited twice". You can
    work around this, however, by doing this:
    
      class Ca extends T
      new Ca with T
    
    In fact, this is precisely what ease.js is doing, as mentioned above; the
    "use.use" syntax is merely shorthand for this:
    
      new C.use( T ).extend( {} ).use( T )
    
    Just keep that in mind.

Added support for weak abstract methods

Mike Gerwitz
26 Jan 2014
    This adds the `weak' keyword and permits abstract method definitions to
    appear in the same definition object as the concrete implementation. This
    should never be used with hand-written code---it is intended for code
    generators (e.g. traits) that do not know if a concrete implementation will
    be provided, and would waste cycles duplicating the property parsing that
    ease.js will already be doing. It also allows for more concise code
    generator code.

Began implementing composition-based traits

Mike Gerwitz
23 Jan 2014
    As described in <https://savannah.gnu.org/task/index.php#comment3>.
    
    The benefit of this approach over definition object merging is primarily
    simplicitly---we're re-using much of the existing system. We may provide
    more tight integration eventually for performance reasons (this is a
    proof-of-concept), but this is an interesting start.
    
    This also allows us to study and reason about traits by building off of
    existing knowledge of composition; the documentation will make mention of
    this to explain design considerations and issues of tight coupling
    introduced by mixing in of traits.

GNU ease.js

Mike Gerwitz
20 Jan 2014
    ease.js is now part of the GNU project; this merges in changes that led up
    to the submission being accepted and additional cleanup thereafter. More
    information will be included in the release announcement (which will be
    included in the 0.2.0 tag), and relicensing rationale is included in the
    commit that introduced the license change. The release announcement will
    also include a small essay I have been authoring with the help and input of
    RMS about the importance of making JavaScript code free.
    
    Happy GNU year!

Altered license templates for combined files with section 7 exception

Mike Gerwitz
6 Jan 2014
    As suggested by RMS in The JavaScript Trap:
      <https://gnu.org/philosophy/javascript-trap.html>
    
    This does increase the size of the minified file a bit---the header is now
    about 1kB of uncompressed text (which will hopefully comprses nicely with
    the rest of the minified file). That said, ease.js will be continuing to
    grow in size, bandwidth is becoming less and less important, and the license
    is very important, especially in our goal to spread the philosophy of
    software freedom.

Relicensed under the GPLv3+

Mike Gerwitz
20 Dec 2013
    This project was originally LGPLv+-licensed to encourage its use in a community
    that is largely copyleft-phobic. After further reflection, that was a mistake,
    as adoption is not the important factor here---software freedom is.
    
    When submitting ease.js to the GNU project, it was asked if I would be willing
    to relicense it under the GPLv3+; I agreed happily, because there is no reason
    why we should provide proprietary software any sort of edge. Indeed, proprietary
    JavaScript is a huge problem since it is automatically downloaded on the user's
    PC generally without them even knowing, and is a current focus for the FSF. As
    such, to remain firm in our stance against proprietary JavaScript, relicensing
    made the most sense for GNU.
    
    This is likely to upset current users of ease.js. I am not sure of their
    number---I have only seen download counts periodically on npmjs.org---but I know
    there are at least a small number. These users are free to continue using the
    previous LGPL'd releases, but with the understanding that there will be no
    further maintenance (not even bug fixes). If possible, users should use the
    GPL-licensed versions and release their software as free software.
    
    Here comes GNU ease.js.

'this' now properly binds to the private member object of the instance for getters/setters

Mike Gerwitz
19 Jan 2013
    Getters/setters did not get much attention during the initial development of
    ease.js, simply because there was such a strong focus on pre-ES5
    compatibility---ease.js was created for a project that strongly required it.
    Given that, getters/setters were not used, since those are ES5 features. As
    such, I find that two things have happened:
    
      1. There was little incentive to provide a proper implementation; even though
         I noticed the issues during the initial development, they were left
         unresolved and were then forgotten about as the project lay dormant for a
         while.
      2. The project was dormant because it was working as intended (sure, there
         are still things on the TODO-list feature-wise). Since getters/setters were
         unused in the project for which ease.js was created, the bug was never
         found and so never addressed.
    
    That said, I now am using getters/setters in a project with ease.js and noticed
    a very odd bug that could not be explained by that project's implementation.
    Sure enough, it was an ease.js issue and this commit resolves it.
    
    Now, there is more to be said about this commit. Mainly, it should be noted that
    MemberBuilder.buildGetterSetter, when compared with its method counterpart
    (buildMethod) is incomplete---it does not properly address overrides, the
    abstract keyword, proxies or the possibility of method hiding. This is certainly
    something that I will get to, but I want to get this fix out as soon as I can.
    Since overriding ES5 getters/setters (rather than explicit methods) is more
    likely to be a rarity, and since a partial fix is better than no fix, this will
    likely be tagged immediately and a further fix will follow in the (hopefully
    near) future.
    
    (This is an interesting example of how glaring bugs manage to slip through the
    cracks, even when the developer is initially aware of them.)

Added `proxy' keyword support

Mike Gerwitz
2 May 2012
    The concept of proxy methods will become an important, core concept in ease.js
    that will provide strong benefits for creating decorators and proxies, removing
    boilerplate code and providing useful metadata to the system. Consider the
    following example:
    
      Class( 'Foo',
      {
          // ...
    
          'public performOperation': function( bar )
          {
              this._doSomethingWith( bar );
              return this;
          },
      } );
    
      Class( 'FooDecorator',
      {
          'private _foo': null,
    
          // ...
    
          'public performOperation': function( bar )
          {
              return this._foo.performOperation( bar );
          },
      } );
    
    In the above example, `FooDecorator` is a decorator for `Foo`. Assume that the
    `getValueOf()` method is undecorated and simply needs to be proxied to its
    component --- an instance of `Foo`. (It is not uncommon that a decorator, proxy,
    or related class will alter certain functionality while leaving much of it
    unchanged.) In order to do so, we can use this generic, boilerplate code
    
      return this.obj.func.apply( this.obj, arguments );
    
    which would need to be repeated again and again for *each method that needs to
    be proxied*. We also have another problem --- `Foo.getValueOf()` returns
    *itself*, which `FooDecorator` *also* returns.  This breaks encapsulation, so we
    instead need to return ourself:
    
      'public performOperation': function( bar )
      {
          this._foo.performOperation( bar );
          return this;
      },
    
    Our boilerplate code then becomes:
    
      var ret = this.obj.func.apply( this.obj, arguments );
      return ( ret === this.obj )
          ? this
          : ret;
    
    Alternatively, we could use the `proxy' keyword:
    
      Class( 'FooDecorator2',
      {
          'private _foo': null,
    
          // ...
    
          'public proxy performOperation': '_foo',
      } );
    
    `FooDecorator2.getValueOf()` and `FooDecorator.getValueOf()` both perform the
    exact same task --- proxy the entire call to another object and return its
    result, unless the result is the component, in which case the decorator itself
    is returned.
    
    Proxies, as of this commit, accomplish the following:
      - All arguments are forwarded to the destination
      - The return value is forwarded to the caller
        - If the destination returns a reference to itself, it will be replaced with
          a reference to the caller's context (`this`).
      - If the call is expected to fail, either because the destination is not an
        object or because the requested method is not a function, a useful error
        will be immediately thrown (rather than the potentially cryptic one that
        would otherwise result, requiring analysis of the stack trace).
    
    N.B. As of this commit, static proxies do not yet function properly.

Added signchk tool

Mike Gerwitz
18 Apr 2012
    This tool can help to ensure that commits have not been falsely authored. For
    example, if you receive an ease.js repository from a friend, there is no way to
    verify that a commit from "Mike Gerwitz" is actually a commit from myself unless
    it has been signed using my private key. This additional check will help to
    ensure the integrity of the repository.
    
    Please note that automated systems should *not* invoke this utility directly
    from this repository, unless it is invoked using a previously trusted commit.
    Otherwise, an attacker need only alter the script to competely evade the check.

Updated package.json to remove deps and correct license

Mike Gerwitz
13 Dec 2011
    Growing much closer to releasing. Hopefully in the next couple of days; I just
    don't want to rush it. Though, at the same time, I've been noticing projects
    popping up with very similar / exact names to this one. A project named "ease"
    was added to the npm repository and another "ease.js" is on GitHub, although
    it's made no progress. As such, I want to ensure I reserve the name in npm.
    
    I've been testing the new library and work and noticed only a couple minor
    issues, primiarly due to misuse of the library. Looking good.

Switched to Closure Compiler

Mike Gerwitz
6 Dec 2011
    This is nothing against uglify. Rather, here's the story on this:
    
    Commit e4cd1e fixed an error that was causing minified files to break in IE.
    This was due to how IE interprets things, not how UglifyJS was minifying them.
    Indeed, Closure Compiler had the exact same problem.
    
    The decision to move to Closure Compiler was due to a variety of factors, which
    came down to primarily feature set and tests. Closure Compiler is well tested
    and maintained. It also includes a number of additional, beneficial features.
    UglifyJS is an excellent project and I would recommend it to anyone, but it is
    not tested (no unit tests; it's tested by ensuring common libraries like jQuery
    run after minification). It is, however, significantly faster.
    
    It's likely that, in the future, once I add autoconf for the build process to
    configure certain settings, that I will add UglifyJS as an option. I'm sure many
    people would prefer that, especially those who dislike Java and do not wish to
    have it installed. Hopefully those that do decide to install Java will go with
    openjdk, not Oracle's proprietary implementation.

Fixed __self assignment for FF

Mike Gerwitz
4 Dec 2011
    This little experience was rather frustrating. Indeed, it would imply that
    the static implementation (at least, accessing protected and private static
    members) was always broken in FF. I should be a bit more diligent in my testing.
    Or perhaps it broke in a more recent version of FF, which is more likely. The
    problem seems to be that we used defineSecureProp() for an assignment to the
    actual class, then later properly assigned it to class.___$$svis$$.
    Of course, defineSecureProp() makes it read-only, so this failed, causing
    an improper assignment for __self, breaking the implementation. As such,
    this probably broke in newer versions of FF and worked properly in older versions.
    
    More concerningly is that the implementations clearly differ between Chromium
    and Firefox. It may be that Firefox checks the prototype chain, whereas Chromium
    (v8, specifically) will simply write to that object, ignoring that the property
    further down the prototype chain is read-only.

[#25] Finished refactoring MemberBuilder/MethodTest and removed inc-member_builder-common (no longer needed)

Mike Gerwitz
26 Oct 2011
    Finally feels like things are starting to come together.
    
    It's rather interesting looking back. Each time I begin writing a piece of
    software, I think to myself, "This is the best way to do it." Well, generally.
    Perhaps the implementation could have been better, but I may not have had the
    time. However, the general concept remains.
    
    Each time I look back months later and find that I disagree with certain
    decisions. I find certain implementations to be messy or poorly constructed. Or
    perhaps I was just being lazy to begin with. Whatever the case, it is
    comforting. It shows that one is continuing to learn and evolve.
    
    Now, in the case of ease.js, we're working with a number of different factors in
    regards to my perception of prior code quality. Primarily, I'm looking at a
    basic implementation (in this case, I'm referring to test cases) that served as
    a foundation that could be later evolved. I didn't have the time to devote to a
    stronger solution. However, since the project has evolved so far past my
    original expectations, a more sophisticated solution is needed in order to
    simplify the overall design. That is what happened here.
    
    Of course, we're also looking at a year's worth of additional, intimate
    experience with a language.
    
    Regardless of the reason, I love to see software evolve. Especially my own. It's
    as if I'm watching my child grow. From that, I can get a great deal of
    satisfaction.
    
    One shouldn't expect perfection. But one should certainly aim for it.

Added very basic formatted output and failure tolerance for test case

Mike Gerwitz
10 Oct 2011
    The one year anniversary of the beginning of the ease.js project is quickly
    approaching. I find myself to be not quite where I had expected many months ago,
    but find that the project has evolved so much further than I had event
    originally anticipated. My main motivation behind the project continues to be
    making my life at work easier, while providing an excellent library that others
    can hopefully benefit from. If anything, it's a fascinating experiment and
    clever hack around JavaScript.
    
    Now I find myself with a newborn child (nearly four weeks old), who demands my
    constant attention (and indeed, it is difficult to find the desire to put my
    attention elsewhere). Still - I am a hacker. Software is my passion. So the
    project must move forward.
    
    I also find myself unwilling to create a blog for ease.js. I feel it's
    inappropriate for a project that's in its (relative) infancy and does not have
    much popularity (it has never been announced to anyone). As such, I feel that
    commit messages will serve my purpose as useful journal entries regarding the
    status of the project. They will also be interesting easter eggs for those who
    would wish to seek them out for additional perspective on the project. (Granted,
    one could easy script the discovery of such entries by examining the absurd
    length of the commit message...perhaps the git log manpages would be useful).
    
    So. Let's get back to the project.
    
    ease.js is currently going through a strong refactoring in order to address
    design issues that have begun to creep up as the project grew. The initial
    design was a very simple one - a "series of modules", as it was originally
    described in a CommonJS sense, that would provide features of a classical
    Object-Oriented system. It would seem ironic that, having a focus on
    classical Object-Oriented development, one would avoid developing the project in
    such a paradigm. Instead, I wished to keep the design simple (because the
    project seemed simple), more natural to JS developers (prototypal) and
    performant (object literals do not have the overhead of instantiation). Well,
    unfortunately, the project scope has increased drastically due to the success of
    the implementation (and my playfulness), the chosen paradigm has become awkward
    in itself and the performance benefit is indeed a micro-optimization when
    compared with the performance of both the rest of the system and the system that
    will implement ease.js as a framework.
    
    You can only put off refactoring for so long before the system begins to trip
    over itself and stop being a pleasure to work with. In fact, it's a slap in the
    face. You develop this intricate and beautiful system (speaking collectively and
    generally, of course) and it begins to feel tainted. In order to prevent it from
    developing into a ball of mud - a truly unmaintainable mess - the act of
    refactoring is inevitable, especially if we want to ensure that the project
    survives and is actively developed for any length of time.
    
    In this case, the glaring problem is that each of the modules are terribly,
    tightly coupled. This reduces the flexibility of the system and forces us to
    resort to a system riddled with conditionals. This becomes increasingly apparent
    when we need to provide slightly different implementations between environments
    (e.g. ES5/pre-ES5, production/development, etc and every combination).
    Therefore, we need to decouple the modules in order to take advantage of
    composition in order to provide more flexible feature sets depending on
    environment.
    
    What does this mean?
    
    We need to move from object literals for the modules to prototypes (class-like,
    but remember that ease.js exists because JS does not have "classes"). A number
    of other prototypes can be extracted from the existing modules and abstracted to
    the point where they can be appropriately injected where necessary. Rather than
    using conditions for features such as fallbacks, we can encapsulate the entire
    system in a facade that contains the features relevant to that particular
    environment. This will also have the consequence that we can once again test
    individual units rather than systems.
    
    At the point of this commit (this entry was written before any work was done),
    the major hurdle is refactoring the test cases so that they do not depend on
    fallback logic and instead simply test specific units and skip the test if the
    unit (the prototype) is not supported by the environment (e.g. proxies in a
    pre-ES5 environment). This will allow us to finish refactoring the fallback and
    environment-specific logic. It will also allow us to cleanly specify a fallback
    implementation (through composition) in an ES5 environment while keeping ES5
    detection mechanisms separate.
    
    The remaining refactorings will likely be progressive. This all stemmed out of
    the desire to add the method hiding feature, whose implementation varies
    depending on environment. I want to get back to developing that feature so I can
    get the first release (v0.1.0) out. Refactoring can continue after that point.
    This project needs a version number so it can be used reliably.