JS++ 0.8.10: Faster Compile Times, Stacks/Queues, Unicode, Base64, and More

The next version of JS++ (not this one) will be a breakthrough. It is on par with ‘external’ in its importance to JS++. Stay with us, and stay tuned.

In this latest release of JS++, we’ve done a lot: we’ve improved on our compile times which already lead the competition by an order of magnitude (with room for more improvement), we’ve substantially expanded the Standard Library, made a UX improvement to generics (without breaking any existing code), and we’ve fixed a lot of bugs (which are mostly minor at this point after years of engineering).

Faster Compile Times

import System;

Console.log("Hello World");

On the Core i7-4790k:

Version Total Time
JS++ 0.8.5 96.2ms
JS++ 0.8.10 72.6ms
(Lower is better)

System Specifications:

  • Intel Core i7-4790k
  • 32gb DDR3 RAM
  • Samsung 960 EVO M.2 SSD

As evidenced in the table above, the latest version is now compiling “Hello World” 32.51% faster. There is still room for more improvement to compile times, but it is not our current priority. We improved compile times in this release by pre-parsing and caching the JS++ Standard Library. There is more to Standard Library compilation than this single step, but we wanted to address this problem as it was the largest performance regression in our profiling. Practically, this allows us to substantially expand the JS++ Standard Library with an O(log N) cost to compile times versus the previous O(n) cost of adding new libraries.

And because we’re able to add substantially more libraries… we’ve done exactly that.

System.Stack<T> and System.Queue<T>

JS++ now provides stack (LIFO) and queue (FIFO) data structures. Stacks are an abstraction over JavaScript arrays and are very fast.

import System;

auto stack = new Stack<int>();
stack.push(1);
stack.push(2);
Console.log(stack.pop()); // 2

In the generated code, the ‘push’ call is a direct ‘push’ call on the internal array representation behind the stack so there is no performance loss in using the stack abstraction instead of arrays.

Next, we also have queues via System.Queue. It’s not as simple as an abstraction over JavaScript arrays like stacks because re-indexing an array would be an O(n) operation. Instead, to guarantee O(1) pop operations, we use a ring buffer. (Credit goes to Anton, our lead engineer.)

import System;

auto queue = new Queue<int>();
queue.push(1);
queue.push(2);
Console.log(queue.pop()); // 1

At a micro-optimization level, you might question our decision on the ring buffer backing store. However, computer science is important. If you take a look at my JS++ stringset library, you’ll see this benchmark over a dictionary of ~49,000 terms:

StringSet : 70ms     (00.07 seconds)
string[]  : 82299ms  (82.29 seconds)

Clearly, the performance difference here is substantial, but it should be no surprise to anyone that understands data structures: an array has O(n) lookups, and a set has O(1) lookups.

Documentation:

System.Encoding

This module introduces a lot of useful new features, but I’m going to break it down.

Base64 Encoding and Decoding

A common operation in web development is Base64 encoding and decoding. For example, the HBase REST API requires Base64 encoding/decoding. You can also use Base64 to encode binary data and files as ASCII, such as converting canvas image data into data URIs.

Wouldn’t it be nice to have all of this functionality readily available in the language you use?

import System;
import System.Encoding;

string quote = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";

string encoded = Base64.encode(quote);
Console.log(encoded);

string decoded = Base64.decode(encoded);
Console.log(decoded);

The more you master JS++ and know how to use the Standard Library, the more efficiently you can get work done compared to JavaScript.

Documentation: System.Encoding.Base64

UTF-8, UTF-16, and UTF-32

Dealing with Unicode is a key aspect of writing world-ready software. UTF-8, UTF-16, and UTF-32 are encoding schemes defined in the Unicode Standard, and JS++ now supports encoding and decoding of all of these in the System.Encoding module.

Here’s an example of UTF-8 encoding:

import System;
import System.Encoding;

byte[] encoded = UTF8.encode("€");

string toHex = "";
foreach(byte b in encoded) {
    toHex += "\\x" + b.toHex().toUpperCase();
}

Console.log(toHex); // "\xE2\x82\xAC"

We just want to take a moment to remind you the importance of the JS++ type system in scenarios like this and how it naturally interoperates with so many areas of computing. In this case, the ‘byte’ data type is a natural fit for dealing with standard Unicode encoding schemes.

Documentation:

URI Encoding and Decoding

This module provides the ECMAScript 3 encodeURI, encodeURIComponent, decodeURI, and decodeURIComponent functions.

Documentation: System.Encoding.URI

Superior Documentation

Documentation is one of the strengths of JS++. We have over 600+ pages of handwritten documentation.

With the release of the System.Encoding.URI module, we wanted to expand on this. Have you ever wondered about the difference between encodeURI and encodeURIComponent in JavaScript? This unfortunate naming scheme, dating back to ECMAScript 3, is a source of confusion. Developers often cite the Mozilla Developer Network (MDN) for documentation, but their explanation is equally as confusing as the naming scheme and lacks useful information:

encodeURIComponent

Stack Overflow wasn’t tremendously more helpful either, and most answers once again just go over which characters get converted and which do not. This doesn’t help the practicing developer learn or memorize which function to use.

Fortunately, JS++ has you covered. We explain the difference between encodeURI and encodeURIComponent clearly, and we also provide an explanation of best practices to help you navigate the confusion:

https://docs.onux.com/en-US/Developers/JavaScript-PP/Language-Guide/encodeuri-vs-encodeuricomponent

Improving Generic Programming UX: Default Constraint

We’ve improved the user experience (UX) for generic programming in JS++.

Previously, the default constraint for generic type parameters was System.Object. However, for performance reasons, the default constraint did not allow primitive types. For example:

class Foo<T> // same as 'Foo<T: System.Object>'
{
}

auto foo = new Foo<string>(); // ERROR (previously)

Instead, you needed to specify the “wildcard constraint” (see the docs) if you wanted to allow primitive types as arguments, as System.Array and System.Dictionary do.

However, this was not the most useful default. All your old code will still work, but we just made the defaults more useful. Starting from version 0.8.10, all your generic classes will have the wildcard constraint as the default. You can declare a plain generic class and instantiate it with primitive types as type arguments.

‘this’ Semantics

First and foremost, before we announce this change, we have to say all semantics relating to this are subject to change.

We’ve been well aware of how JavaScript’s this rules differ from the semantics that users from other programming languages are familiar with. However, previous versions of JS++ raised a ‘0000’ (unimplemented) error here, and the fix was non-obvious:

class Foo
{
    string message = "Test";
    void bar() {
        $(document).click(void() {
            $("#button").text(this.message); // JSPPE0000 error
        });
    }
}

You had to manually capture the ‘this’ value for closures inside classes:

class Foo
{
    string message = "Test";
    void bar() {
        Foo _this = this;
        $(document).click(void() {
            $("#button").text(_this.message); // OK
        });
    }
}

First of all, I apologize for the cryptic error message. We work very hard to make sure our error messages are easy to understand, and we always try to suggest the fix in the error message itself where possible. In this case, we always thought the ‘this’ semantics would be settled within a reasonable time and thought the “unimplemented” error would only be temporary. We were wrong. Thus, it’s important to know we may change the rules and semantics again prior to JS++ 1.0.

In the current release, the this keyword – when used inside classes – refers to the class instance by default. There are cases where you might want it to retain JavaScript this semantics, and, at least for closures inside classes declared with the function return type, you can cast it to external:

class Foo
{
    string message = "Test";
    void attachEvents() {
        $("#button").click(function() {
            var $this = (external) this;
            $this.text(this.message); // OK
        });
    }
}

We are open to input on this functionality.

You can review our plans on the this documentation page under the header, “‘this’ Casting inside Classes”.

Bug Fixes

  • Disable auto creation of the arguments object
  • File extensions are case-insensitive
  • Fix crash during access to a property with undeclared type (reported by @lorveg in JS++ chat)
  • Fix contravariance check in foreach loop
  • Incorrect scoping and code generation for ‘external’
  • Segfault for interface with generic variants
  • Fix missing class name for inheritance in error message
Roger PoonRoger Poon
JS++ Designer and Project Lead. Follow me on Twitter or GitHub.