JS++ 0.7.0: JavaScript Fully Implemented

JS++ 0.7.0 has arrived. Everything that is possible to do in JavaScript is now possible to do in JS++. JS++ has fully implemented JavaScript and the JS++ Standard Library is now fully documented.

This means that every JavaScript feature you expect is now available in JS++: arrays (0.7.0 introduces the generic Array<T> class), functions, date/time, regular expressions, loops, bitwise operators, etc. This is in addition to several JS++-only features: foreach, modules, classes, imports, enums, virtual functions, integer types, optimized auto-boxing, function overloading, dead code elimination (DCE), and so on.

You can find all the current Standard Library documentation by clicking here. Every documented class, method, and field (with examples) should be available and work in 0.7.0.

System.Array<T>

The major missing feature for a while now has been arrays. JS++ 0.7.0 brings the generic System.Array<T> class to JS++. With this addition, everything you can do in JavaScript is possible in JS++.

The full documentation for System.Array<T> is available here.

Additionally, JS++ adds more features to the Array API that aren’t available in JavaScript such as clear, contains, remove, first, last, count, and more. These methods are all optimized; in other words, there is no performance overhead in using them. In addition, we benchmark each method to make sure we are always providing the fastest implementation for an abstraction you want.

System.Array.sort and IComparable<T>

It comes as no surprise that JavaScript has strange behavior. Consider this JavaScript code:

var arr = [ 1, 2, 10, 9 ];
arr.sort();
console.log(arr); // [ 1, 10, 2, 9 ]

As you can see, JavaScript did not sort the numeric array correctly. The reason for this behavior is because JavaScript performs a string sort regardless of the types in the array. Since JS++ is statically-typed, we can generate code for a correct sort with zero overhead. In JS++, the System.IComparable<T> interface provides exactly this behavior. If a class implements System.IComparable<T>, it can provide custom sorting behavior.

All Standard Library wrapper classes implement System.IComparable<T>. Thus, all JS++ numeric types will be sorted numerically when used inside an array – as you would expect from a modern, well-designed language. Here’s an example:

import System;

int[] arr = [ 1, 2, 10, 9 ]; // make sure you use an internal type like 'int[]' and not an external type like 'var'
arr.sort();

And here’s the generated code:

// Compiled with JS++ v.0.7.0
! function() {
    ! function() {
        var arr = [1, 2, 10, 9];
        arr.sort(function(a, b) {
            return (a - b);
        });
    }();
}();

As you can see, there is zero overhead – despite the complex inheritance hierarchy (int[] being auto-boxed by System.Array<int>, System.Array<int> providing a custom sort based on generic type constraints, and System.Integer32 implementing IComparable<T>).

ECMAScript 5 Array Methods

ECMAScript 5 (ES5) added several array methods:

  • indexOf
  • lastIndexOf
  • every
  • some
  • filter
  • map
  • forEach
  • reduce
  • reduceRight

However, these methods are not supported in older web browsers. JS++ aims for enterprise support and legacy web application support. However, in order to polyfill these methods, it would result in hundreds of lines of code. Thus, in the spirit of JS++ dead code elimination (DCE), the above methods are only polyfilled for incompatible web browsers if and only if the individual method is used.

System.Object

One peculiar Standard Library class you might notice is System.Object. Specifically, it does not implement the JavaScript API at all. This is because JS++ does not use prototypical inheritance like JavaScript does. JS++ uses class-based inheritance; thus, System.Object is minimal and does not provide methods that are only useful for a prototype-based language – such as hasOwnProperty and isPrototypeOf.

JS++ uses a “Unified Type System” (like Java/C#) with System.Object at the top of the inheritance hierarchy. Thus, System.Object represents all “internal types.” If you don’t know what that means, please fully read the “Getting Started” guide.

If you want to use JavaScript objects, you still have to declare external types until System.Dictionary<T> arrives:

var obj = {
    "a": 1,
    "b": 2
};

In addition, if you want the JavaScript Object prototype methods, use the Externals.JS module:

import System;
import Externals.JS;

Console.log(typeof Externals.JS.Object.prototype.hasOwnProperty == "function"); // true

Prefer the Standard Library

There are 300+ pages of Standard Library documentation (not including all the documentation for individual method overloads). This is in addition to 300+ pages of handwritten documentation, bringing the JS++ documentation to over 600 pages.

As always, I recommend that you prefer the JS++ Standard Library to writing your own JavaScript implementation. Consider clearing an array. In JS++, the code is:

arr.clear();

You might be tempted to avoid the function call overhead and try to roll your own JavaScript:

arr = [];

First of all, this can create a memory leak (e.g. if other references to the original array are being held). The JS++ Standard Library provides high-quality, fast, and well-tested functions to you. Use it.

Secondly, what looks like function call overhead on the surface is actually inlined, correct code in the final output:

arr.length = 0;

Arrays have incredibly low overhead. Here’s the full code:

import System;

int[] arr = [1,2,3];
arr.clear();

Here’s the generated output:

// Compiled with JS++ v.0.7.0

!function(){!function(){var arr=[1,2,3];arr.length=0;}();}();

As you can see, there is zero overhead. Thus, use the Standard Library. Don’t try to be fancy and write JavaScript rather than JS++ code. Your users’ garbage collectors will thank you for it in the future when you’re not leaking 500mb of RAM, and your team will thank you for it because .clear() is A LOT more readable than .length = 0 and A LOT more correct than arr = [];

Code Readability

One of the major driving changes for JS++ is code readability. We invest heavily into this from cutting-edge parsers to Standard Library design. The JS++ Standard Library allows you to write high-performance, readable code.

Consider this JavaScript code:

var abc = [ "a", "b", "c" ];
abc.splice(1, 1);

Without evaluating it, what do you think it does?

Here’s the equivalent JS++ code:

string[] abc = [ "a", "b", "c" ];
abc.remove(1);

Same code. Same performance. Many times more readable when written in JS++.

Looking Ahead

We’re going to continue to expand the JS++ Standard Library to provide useful functions and give you a “batteries included” experience. Additionally, we’ll continue to expand the language. The following features are still not implemented:

  • User-defined Generic Classes
  • Reflection API
  • System.Dictionary (hash maps)
  • Block Scoping
  • Nullable Types
  • final variables (but final classes and methods are done)

If you have a pressing need for any of these features, you may want to wait. However, everything that is possible to do in JavaScript is now possible to do in JS++.

Roger PoonRoger Poon
JS++ Designer and Project Lead. Follow me on Twitter or GitHub.
  • Nice! In pratice, only libraries should access the global object and use externals. It should be possible to polyfill Dictionary, however no generics yet.

    • Roger Poon

      Yup, you catch on quickly :-).

      > In pratice, only libraries should access the global object and use externals.

      This is a best practice that we’ve only discussed internally. I don’t think I ever explained it publicly.

      • I always (after my jr. kid experience) find it a bad pratice to avoid locals. :v

    • Kirill Berezin

      Well i didn’t get it yet. Is it execute inside web browsers? Then who links the code? Server? How does he know is polyfill is required?

      • Roger Poon

        > Is it execute inside web browsers?

        It can be.

        > Then who links the code?

        The JS++ compiler also handles linking. The linking is static.

        > How does he know is polyfill is required?

        JS++ analyzes your code to see which modules, classes, and functions were used/unused. If a method that requires a polyfill was used, it will be polyfilled. If a method that requires a polyfill is never used, the polyfill for that method will not be generated.

        • Kirill Berezin

          I just don’t get it. Imagine me wrote app on JS++. This apps runs on IE, Chrome, Opera, etc. This browsers both ES5/ES6 (elsewere you find ES5 this days). How does JS++ knows which browser build support which method. Where polyfill is required and where is not.

          It makes no sense for me.
          – why do you still need ES5
          – how does it vanish unused code from compiled “binary” if target VM requirements is unknown.

          If it compiles JS++ -> JS on target device, it means it require all the poly-fills to be downloaded.

          • It’s clear. E.g., if you don’t use Class#method, then this method’s code won’t be generated into the compiler’s output… otherwise, it’s pollyfilled and put inside the output.

            But I think it doesn’t matter if the function is a class method and so on.

            function abc_compiler()
            {
            // do anything
            int aVariableShz;
            }

            // ~~~~~~~~~~~~ Compiles as ~~~~~~~~~~~~

            // ??? Nothing.

            However, since you only need to support at least ES6,

            We can add a compiler flag for that.

          • Kirill Berezin

            Yeah, i get an idea. Just lurking for specifics. Great definitely should refactor php server into this.

            Keep in touch.

          • Roger Poon

            Hi Kirill,

            Good choice! Let us know how it goes.

            You may also want to consider subscribing to the JS++ mailing list (if you haven’t already done so) because that’s where we send out our latest releases and announcements. We’ve got more coming!

          • how does it vanish unused code from compiled “binary” if target VM requirements is unknown.

            Check this: https://docs.onux.com/en-US/Developers/JavaScript-PP/Language/Reference/Types , this: https://docs.onux.com/en-US/Developers/JavaScript-PP/Language/Reference/Types/Primitive-Types and this, https://docs.onux.com/en-US/Developers/JavaScript-PP/Language/Reference/Types/Primitive-Types/external (external type). After seeing `external`, I would deduce that most of what JS++ provides to ECMAScript 3 is compile-time and the Onux compiler also wraps the global code in a function body (thus making everything local).

            // MrRunner creates a compile-time type and an Object, I believe.
            class MrRunner
            {
            int gravity; // This is a fixture property and known at compile-time
            }

            Can we access a fixture property without the compile time mr_runner.gravity syntax, e.g., mr_runner[‘gravity’]? Not sure.

            class isn’t much like ES6’s class, but as for the static part, I’ve not tested so far. I believe the entire class is a static namespace.

  • Hmmm, “ECMAScript 5 Array Methods”. careful, ES3.

    • Roger Poon

      Yes, we’ve documented this well. We felt these methods were useful so we polyfill on demand. I think this is a better user experience than cramming all the ES5 Array polyfills (whether used or unused) into a .js library as you would have had to do in traditional JavaScript.

      • Yeah, I say, you can’t implement ES5 since JS++ extends ES3. Of course, JS++ can have some ES5 features, or even some from ES4 nah.

  • Kirill Berezin

    I hope it is possible to shrink ES5 support, in purpose of reducing library download size?
    Zero of my customers (according to the GA) are using IE11 etc. They all support ES6.

    • Roger Poon

      Hi Kirill. What do you mean by “shrink ES5 support”? We already have dead code elimination, and we don’t generate polyfills unless the method is used. Do you mean you want to be able to disable polyfills altogether? We can add a compiler flag for that.