1
0
Fork 0
Commit Graph

40 Commits (74b4525f00b3d79866bd6f0a3964abbc0f9745aa)

Author SHA1 Message Date
Mike Gerwitz c835641dcb Private methods are no longer wrapped
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.
2014-03-20 23:43:24 -04:00
Mike Gerwitz 744696b1a7
[copyright] Copyright update 2014-03-15 23:56:47 -04:00
Mike Gerwitz 1b323ed80b Validation warnings now stored in state object
This will allow for additional processing before actually triggering the
warnings. For the sake of this commit, though, we just keep with existing
functionality.
2014-03-15 21:16:26 -04:00
Mike Gerwitz dac4b9b3a1 The `weak' keyword can now apply to overrides
Well, technically anything, but we're leaving that behavior as undefined for
now (the documentation will say the same thing).
2014-03-07 00:47:43 -05:00
Mike Gerwitz 548c38503f Added support for weak abstract methods
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.
2014-03-07 00:47:43 -05:00
Mike Gerwitz 97fbbd5bb9 [no-copyright] Modified headers to reduce GPL license notice width 2014-01-15 23:56:00 -05:00
Mike Gerwitz 8b83add95f ease.js is now GNU ease.js.
On Sun, Dec 22, 2013 at 03:31:08AM -0500, Richard Stallman wrote:
> I hereby dub ease.js a GNU package, and you its maintainer.
>
> Please don't forget to mention prominently in the README file and
> other suitable documentation places that it is a GNU program.
2013-12-23 00:27:18 -05:00
Mike Gerwitz 26dffce00a [copyright] Copyright update after adding --follow to copyright script 2013-12-22 22:50:29 -05:00
Mike Gerwitz 13ca9cd852
[copyright] Copyright update after relicensing 2013-12-20 01:11:39 -05:00
Mike Gerwitz 9050c4e4ac
Relicensed under the GPLv3+
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.
2013-12-20 01:10:05 -05:00
Mike Gerwitz 2a76be2461
[copyright] Copyright update 2013-12-20 00:50:54 -05:00
Mike Gerwitz e67c14e8c3
Added support for static proxy methods
When the static keyword is provided, the proxy will use the static accessor
method to look up the requested member.
2012-05-03 14:13:47 -04:00
Mike Gerwitz d84b86b21b
Added `proxy' keyword support
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.
2012-05-03 09:49:22 -04:00
Mike Gerwitz cdbcada4d2 Copyright year update 2011-12-23 00:09:11 -05:00
Mike Gerwitz d1b1d2691a Fixed initial warnings provided by Closure Compiler
Getting ready for release means that we need to rest assured that everything is
operating as it should. Tests do an excellent job at aiding in this, but they
cannot cover everything. For example, a simple missing comma in a variable
declaration list could have terrible, global consequences.
2011-12-10 11:18:41 -05:00
Mike Gerwitz e0254f6441 Removed invalid @package tags
Not a valid tag in jsdoc
2011-12-06 20:19:31 -05:00
Mike Gerwitz 94419742c0 Resolved IE8 test failures
- Additional checks for its buggy defineProperty(), etc implementation
2011-11-18 08:57:37 -05:00
Mike Gerwitz 1fa92d44a1 [#25] Added Getter/Setter validator call tests for MemberBuilder 2011-11-05 09:40:58 -04:00
Mike Gerwitz cb6c4af763 [#25] Refactored common MemberBuilder validator call assertion logic into a common module 2011-11-05 08:52:19 -04:00
Mike Gerwitz e809c10dfe [#25] Added MemberBuilder/PropTest for validator call 2011-11-04 23:08:41 -04:00
Mike Gerwitz fda002d252 [#25] Added tests to ensure proper data is passed to validateMethod() 2011-11-03 23:20:45 -04:00
Mike Gerwitz 4e2af2333d [#25] Now injecting MemberBuilderValidator into MemberBuilder 2011-11-02 23:28:23 -04:00
Mike Gerwitz de78a472f0 [#25] MemberBuilder/MethodTest - removed unnecessary test 2011-11-02 19:12:15 -04:00
Mike Gerwitz 3c676de55d [#25] Combined buildGetter() and buildSetter()
This helped to get rid of some unnecessary duplicate code and should also help
to improve performance slightly for getter/setter definitions.
2011-10-29 08:25:51 -04:00
Mike Gerwitz ad0343fb9b [#25] Moved getter/setter validation logic into MemberBuilderValidator
- Tests have not yet been moved
2011-10-28 00:08:22 -04:00
Mike Gerwitz 05df0b485c [#25] Moved single access modifier getter/setter test to VisibilityTest 2011-10-27 20:46:30 -04:00
Mike Gerwitz f19a62e733 [#25] Moved public default getter/setter test to new location 2011-10-27 20:43:56 -04:00
Mike Gerwitz 5959956a27 [#25] this => _self replacements in MemberBuilder/VisibilityTest 2011-10-27 19:52:43 -04:00
Mike Gerwitz 11020a9d2a [#25] Minor typo fix in MemberBuilder/VisibilityTest 2011-10-27 19:51:28 -04:00
Mike Gerwitz 1ba160e51c [#25] Added getter/setter vis test to MemberBuilder/VisibilityTest 2011-10-27 19:50:13 -04:00
Mike Gerwitz ce7853965e [#25] Combined separate property and method vis test 2011-10-27 19:28:36 -04:00
Mike Gerwitz 6d1cc06c27 [#25] Finished refactoring MemberBuilder/MethodTest and removed inc-member_builder-common (no longer needed)
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.
2011-10-26 23:39:03 -04:00
Mike Gerwitz 88cff48599 [#25] Moved remaining tests in test-member_builder into MemberBuilder/VisibilityTest 2011-10-26 22:12:28 -04:00
Mike Gerwitz e90699c805 [#25] Added test to MemberBuilder/Visibility test to ensure members will be declared public by default (if no access modifier is given) 2011-10-25 23:47:06 -04:00
Mike Gerwitz f4b8eb3589 [#25] Added test in MemberBuilder/VisibilityTest to ensure multiple access modifiers are not used 2011-10-25 23:30:57 -04:00
Mike Gerwitz 9b629b8b61 [#25] Removed tests from MemberBuilder/MethodTest that have been refactored into MemberBuilderValidator/MethodTest 2011-10-23 00:06:22 -04:00
Mike Gerwitz aeff796332 [#25] [#25] Added member builder tests for private and protected members 2011-10-21 16:04:24 -04:00
Mike Gerwitz f7700f93e5 [#25] Refactored MemberBuilder/VisibilityTest basic tests into reusable functions for upcoming tests in other access levels 2011-10-21 12:10:49 -04:00
Mike Gerwitz bb9eb16fd3 [#25] Began adding MemberBuilder/VisibilityTest to test MemberBuilder directly
As mentioned in a prior commit blog-like entry, many of the tests evolved into more of an integration or system-level type of test. Let's get away from that.
2011-10-21 12:09:00 -04:00
Mike Gerwitz c1207eb3d5 Moved MemberBuilder-MethodTest into MemberBuilder subdir
- Altered combine script to support subdirs (note that the dir names aren't included in the combined file)
2011-10-20 23:40:30 -04:00