JS++ 0.9.0: Efficient Compile Time Analysis of Out-of-Bounds Errors

I promised a breakthrough for our next release.

We are proud to announce JS++ efficiently analyzes and prevents out-of-bounds errors. An out-of-bounds error occurs when you attempt to access a container element that doesn’t exist in the container. For example, if an array has only three elements, accessing the tenth element is a runtime error.

In C, you risk buffer overflows. In C++, you risk buffer overflows and exceptions. In Java and C#, you get an exception at runtime. If exceptions are uncaught, the application terminates. If segmentation faults occur, the application terminates. In the case of buffer overflows, you open your application to a variety of exploits.

As we will show, we can perform out-of-bounds analysis with only a ±1-2ms (milliseconds) overhead on complex projects. There is virtually no effect on compile times with our invention.

Out-of-bounds errors have plagued computer science and programming for decades. Detecting these errors at compile time has ranged from slow to impossible, depending on the language design. With that said, let’s first explore the problems which influenced the design.

Problems

Basic Cases to Handle

In all of the following cases, you cannot predict the value at compile time:

import System;

int[] arr = [ 1, 2, 3 ];

Console.log(arr[Math.random(1, 100)]);
Console.log(arr[getUserInput()]);
Console.log(arr[getValueFromFile()]);
Console.log(arr[API.getTweetLimit()]);

JS++ doesn’t stop at array indexes. Array indexes are limited to numeric values. What about arbitrary string keys on System.Dictionary<T>? Yes, we handle these too:

import System;

auto dict = new Dictionary<string>();

Console.log(dict[Math.random(1, 100).toString()]);
Console.log(dict[getUserInput()]);
Console.log(dict[getTextFromFile()]);
Console.log(dict[API.getTwitterUsername()]);

These are the basic cases. It gets more complex with branching logic:

import System;

Dictionary<string> dict = {
    "1":  "a",
    "10": "b"
};

bool yes() {
    return Math.random(0, 100) > 50;
}

if (yes()) {
    dict["20"] = "c";
}

string key = Math.random(0, 100).toString();
if (dict.contains(key)) {
    Console.log(dict[key]);
}
else {
    Console.log(dict[key + "0"]);
}

These are the very basic cases. There are more… a lot more. All the corner cases you need to explore are outside the scope of this announcement.

Compile Times Must Be Fast

Efficiency is the key. We can’t announce 30% faster compile times in the previous release and simultaneously promise a breakthrough that will cause compile times to explode exponentially.

Clearly, following every branch, virtual function call, external function call, and then some would not be a realistic proposal.

First, let’s look at a basic benchmark so we know what we’re comparing against. In the last release, 0.8.10, I measured “Hello World” compile times. With all of the analyses we added in 0.9.0 (the latest release), how much did it increase compile times for “Hello World”? A little under two (2) milliseconds:

Version Total Time
JS++ 0.8.10 72.6ms
JS++ 0.9.0 74.2ms
(Lower is better)

The test system is the exact same as the one we used to measure compile times for 0.8.10:

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

However, “Hello World” is not a perfect benchmark. How long does it take to compile real-world projects with thousands of lines of code that make lots of array and dictionary accesses? Here are three projects before we introduced compile-time analysis of out-of-bounds errors:

Compile times for 0.8.10 – before out-of-bounds checking
Line Count Source Files Count Total Time
1,137 lines 27 files 124.8ms
4,210 lines 42 files 164.4ms
6,019 lines 72 files 224.6ms
(Lower is better)

Here are compile times after we introduced analysis of out-of-bounds errors:

Compile times for 0.9.0 – detection of out-of-bounds at compile time
Line Count Source Files Count Total Time
1,140 lines 27 files 124.4ms
4,148 lines 41 files 165.4ms
5,942 lines 71 files 224.2ms
(Lower is better)

There’s a slight change in line and file counts due to the inclusion of a ‘Base64’ library, which – during the 0.9.0 refactoring – I removed and replaced with the Standard Library’s System.Encoding.Base64. (The code is the exact same.)

The above projects include both frontend and backend code. They include lots of modules, classes, arrays, dictionaries, and other complexities. I’ve included source file counts to account for disk I/O.

It can be observed that there is virtually no performance penalty for dealing with out-of-bounds errors at compile time. The results are within ±1ms (milliseconds).

Nullable Types are a Problem

Expressing nullability is important in computer programming. For example, a file might have a creation date and last access time. For a new file, there may never have been a “last access time”; thus, it might be ideal to use a nullable data type in this case.

Nullable types are a solved problem in other languages. We considered having Array<T> return T?, but there would be issues with that as presented by Anton Rapetov, our lead compiler engineer:

int[] intArr = [ 1, 2 ];
int? intEl2 = intArr[2];
if (intEl2 == null) {
    Console.log("Definitely out of bounds");
}

int?[] nullIntArr = [ 1, null ];
int? nullIntEl2 = nullIntArr[2];
if (nullIntEl2 == null) {
    Console.log("Might be out of bounds, might just be an access of a null element");
}

Usability

Even if returning nullable types worked, there would be significant usability issues. For example, the following is common code:

int[] arr = [ 1, 2, 3 ];
for (int i = 0, len = arr.length; i < len; ++i) {
    arr[i]++;
    // or
    arr[i] += 1;
}

In the above code, it's clear an out-of-bounds access can never occur. Nonetheless, if an array access returns T?, type conversions would be necessary before the ++ or += 1 operations can occur so we aren't adding to a null value. We need a way to avoid making the user do this for common operations. In fact, for common operations, we want you to be able to write the code exactly as you would above.

Exceptions

In a statically-typed programming language, exceptions allow us to return T for an Array<T> without compromising correctness on an out-of-bounds access. For example, if we declare an Array<int>, a 'pop' method can only return a value of type int or throw an exception. If an exception is thrown, it is none of the concern of the type checker. At compile time, it would not be possible to determine whether or not the exception will be thrown. Yet, an uncaught exception will result in premature program termination at runtime.

Here's an example of how exceptions might be implemented for a container in JS++:

class Array<T>
{
    var data = [];

    T pop() {
        if (this.data.length > 0) {
            return this.data[this.data.length - 1];
        }
        else {
            throw new OutOfBoundsException("Array is empty.");
        }
    }
}

By using exceptions, we never sacrifice type checker performance and compile times. Bounds checking is still performed, but the dark side of exceptions is that it can terminate the application if uncaught.

If we avoid throwing exceptions, and just let JavaScript return undefined, we'd be walking into TypeScript territory and just letting our type system become unsound because it would be "practical." While you might convert undefined to int as zero, there aren't always sensible default values for all JS++ types (e.g. a callback type or a non-nullable class Foo with no default constructor). Speaking of default values...

Default Initialization

Facebook discovered a problem with C++ maps and default initialization in their code that can lead to bugs:

std::unordered_map<std::string, int> settings{};

// ...

std::cout << "Timeout: " << settings["timeout"] << std::endl;

In the above code, simply printing the value of "timeout" can cause it to be zero-initialized. This led us to conclude that default initialization of missing keys would not be a solution. Default initialization of a map of word counts to zero for missing words is innocuous, but an accidental initialization of timeout or price values to zero can lead to substantially different bug severities.

The Breakthrough: null vs undefined

We wanted to have nullable types in the language. We wanted programmers to be able to express the following:

class Person
{
    string firstName = "";
    string? middleName = null;
    string lastName = "";
}

As of the latest release (0.9.0), the above code will work because we've introduced nullable types.

However, I want to revisit an example on nullable types earlier. When we decided to move forward with nullable types, a suggestion was brought up to return T? from array accesses. This example was given as a counter-argument:

int[] intArr = [ 1, 2 ];
int? intEl2 = intArr[2];
if (intEl2 == null) {
    Console.log("Definitely out of bounds");
}

int?[] nullIntArr = [ 1, null ];
int? nullIntEl2 = nullIntArr[2];
if (nullIntEl2 == null) {
    Console.log("Might be out of bounds, might just be an access of a null element");
}

We keep a record of all our meetings. While we didn't explicitly discuss undefined at all, I was in a hurry and summarized our meeting as:

>>>> * There's a problem differentiating between 'null' and
>>>> 'undefined':
>>>>
>>>> ```
>>>> Foo?[] arr = [new Foo(), null];
>>>> auto el1 = arr[1];
>>>> auto el2 = arr[2];
>>>> ```
>>>>
>>>> el1 has type Foo?
>>>> el2 has type Foo?
>>>>
>>>> el1 has value null // el1 exists but is null
>>>> el2 has value null // el2 does NOT exist but is also null
>>>>
>>>> To deal with this, we can add a `hasIndex(int i)` method to
>>>> containers.

Subconsciously, this led to the realization that all we had to do was differentiate between null and undefined in our type system.

Introducing Existent Types

In JavaScript, null means that a value exists but is an "empty value," and undefined means no value exists at all. A basic example is here:

var x = null;
var y;

console.log(x); // null
console.log(y); // undefined

This illustrates the basic concept; unfortunately, JavaScript is inconsistent:

var x = null;
var y;
var z = undefined;

console.log(x); // null
console.log(y); // undefined
console.log(z); // undefined

JS++ has different semantics. First of all, in JS++, all variables must be initialized; therefore, you can't have a variable reference return undefined... ever. Secondly, null means "empty value," but undefined in JS++ means "out-of-bounds error."

JS++ introduces existent types, which uses the + syntax, to describe container accesses:

int[] arr = [ 7, 8, 9 ];

int+ x = arr[0];
int+ y = arr[1000];

We can think of existent types as the "bounds-checked type." I'm a big believer in simplicity. Rather than trying to calculate whether the container access is within-bounds or out-of-bounds at compile time, we delay this check to runtime via the code generator. Existent types are not purely a type checking innovation. The type provides guidance to the code generator to generate code such as the following:

int[] arr = [ 7, 8, 9 ];

int+ x = 0 < arr.length ? arr[0] : undefined;
int+ y = 1000 < arr.length ? arr[1000] : undefined;

We don't actually generate code this way, but it helps illustrate the concept for developers coming from backgrounds in C, C++, C#, Java, etc.

By default, int+ and int are not compatible types. I'll start by introducing the "safe default operator":

int[] arr = [ 7, 8, 9 ];

int+ x = arr[0];
int+ y = arr[1000];

int a = x ?? 0;
int b = y ?? 1;

The "safe default operator" will check if the left-hand side is undefined. If the value is undefined, the evaluated value of the right-hand side of the ?? operator is returned. Otherwise, the left-hand side is returned. In the case of the example above, 'a' will have the value of 7 because 'x' was within-bounds. 'b' will have the value of '1' because 'y' was out-of-bounds, and, thus, the alternative value provided to the ?? operator was used.

T+ cannot be the element type

The problem with JavaScript is that you can have an array of undefined values:

var arr = [ undefined, undefined, undefined ];

In the above case, JavaScript would not be able to differentiate between a within-bounds undefined and an out-of-bounds undefined. In JS++, an existent type cannot be the element type of an array or other container:

int+[] arr = []; // ERROR

[ ERROR ] JSPPE5204: Existent type `int+' cannot be used as the element type for arrays

Therefore, the invention of existent types cannot be retroactively applied to JavaScript.

If you want to represent an array element as having an "empty" value, you have to use nullable types...

Nullable Types + Existent Types

The following describes the basic syntax for the nullable and existent types being introduced in version 0.9.0:

int a = 1;  // 'int' only
int? b = 1; // 'int' or 'null'
int+ c = 1; // 'int' or 'undefined'

However, sometimes we want an array element to contain the "empty" value. In this case, we can combine nullable types with existent types using the following syntax:

int?+ d = 1; // 'int' or 'null' or 'undefined'

In this way, JS++ doesn't have the ambiguity of an undefined value that can be a within-bounds access and also an out-of-bounds access.

Usage with Dictionaries

Existent types can also be used with System.Dictionary<T>. We just introduced how nullable and existent types can be combined so let's use the combination:

import System;

Dictionary<bool?> inviteeDecisions = {
    "Roger": true,
    "Anton": true,
    "James": null, // James is undecided
    "Qin": false
};

bool?+ isJamesAttending = inviteeDecisions["James"]; // 'null'
bool?+ isBryceAttending = inviteeDecisions["Bryce"]; // 'undefined'

In the above code, we use the ?+ syntax to combine nullable and existent types. We're throwing a party, and we want to keep track of the decisions of our invitees. If the invitee's decision is true, he's coming to the party. If the invitee's decision is false, he won't be attending. If the invitee's decision is null, he is undecided. Finally, if the invitee's decision evaluates to undefined, he was not actually invited.

Naturally, the operators that apply to nullable types and existent types (such as the ?? safe default operator) will also apply to the combined ?+ type as well. Code will just be generated to check for null and undefined when using the combined ?+ type.

Beyond arrays and dictionaries, existent types can be applied to the other Standard Library containers (such as Stack<T> and Queue<T>) and even user-defined containers.

Safe Navigation Operator

Besides not being able to differentiate from a within-bounds undefined from an out-of-bounds undefined, JavaScript suffers from another problem:

var arr = [ 1 ];
console.log( arr[1000].toString() );
console.log( "This will never get logged." );

The above code will never reach line 3. The reason is because arr[1000] evaluated to undefined, and you can't call the toString() method on undefined so you'll get a runtime 'TypeError'. In JS++, this isn't a problem because the compiler will detect your attempt to use the . operator and suggest for you to use the ?. safe navigation operator instead:

import System;

int[] arr = [ 1 ];
Console.log( arr[1000].toString() );
Console.log( "This will eventually get logged." );

[ ERROR ] JSPPE5200: The '.' operator cannot be used for nullable and existent types (`int+'). Please use the '?.' safe navigation operator instead at line 4 char 13

If we refactor, we'll discover that, unlike the ?? safe default operator, ?. can return undefined and evaluates to an existent type T+:

import System;

int[] arr = [ 1 ];
Console.log( arr[1000]?.toString() );
Console.log( "This will eventually get logged." );

[ ERROR ] JSPPE5024: No overload for `System.Console.log' matching signature `System.Console.log(string+)' at line 4 char 0

So one possible fix is to provide a default value:

import System;

int[] arr = [ 1 ];
Console.log( arr[1000]?.toString() ?? "out of bounds" );
Console.log( "This will eventually get logged." );

It finally compiles, and we get the following output:

out of bounds
This will eventually get logged.

No crashes and no exceptions can occur.

Inspecting 'undefined'

Oftentimes, when you encounter an out-of-bounds error, you might want to skip to the next iteration over the container or return from a function. Essentially, you want to "skip" code that was written for within-bounds accesses. In JS++, it's as simple as comparing against the undefined value:

import System;

int[] arr = [ 1 ];

for (int i = 0; i < 10; ++i) {
    int+ element = arr[i];
    if (element == undefined) {
        continue;
    }

    int x = (int) element;

    Console.log(x + 1);
    Console.log(x + 2);
    Console.log(x + 3);
}

The C-style cast to int is safe because we already checked for and skipped out-of-bounds accesses. We can also use the safe default operator instead in the code above.

Finally, our output:

2
3
4

This allows us to elegantly write large chunks of code for within-bounds accesses while skipping, returning, or just ignoring out-of-bounds accesses. We can even log the out-of-bounds error to stderr by using System.Console.error.

Downloads

We're providing download links for the latest release (0.9.0) and the previous version (0.8.10). We want you to be able to verify our claims and benchmarks.

JS++ 0.9.0 (latest) – includes out-of-bounds checking
Platform Download Link
Windows Download (32- and 64-bit)
Mac OS X Download (32- and 64-bit)
Linux Download (32-bit)
Download (64-bit)

JS++ 0.8.10 – before out-of-bounds checking
Platform Download Link
Windows Download (32- and 64-bit)
Mac OS X Download (64-bit)
Linux Download (32-bit)
Download (64-bit)

What's Next?

Our first priority is to manage engineering complexity. We have to refactor our tests, and none of this will show up for you, the user. As I write this, I don't know what to expect. Existent types can bring demand for JS++, but we don't have the resources to manage this demand. Instead, we have to stay disciplined in sticking to our own internal schedules to ensure the long-term success of JS++. We listen to user input, but we don't (and can't) follow hype and trends. JS++ over the next 25 years will be more important than JS++ over the next 25 days. I point to Haskell as an example: it's a programming language that is well thought-out and has persisted for 29 years.

We have users that have followed us for years, and we thank all of them for giving us the motivation to persist. If you're willing to be patient and watch JS++ evolve, I urge you to join our email list. The sign-up form for our email list can be found by scrolling to the bottom of this page.

Final Words

Existent types were co-invented by me and Anton Rapetov (lead compiler engineer for JS++).

We solved compile-time analysis of out-of-bounds errors via traditional nominal typing. Thus, there is no performance difference for JS++ checking whether int can be assigned to string or whether int+ can be assigned to string. This explains the ± 1ms compile time difference for compile time out-of-bounds analysis.

We place heavy emphasis on compile times because we know long compile times hurt developer productivity.

When existent types are used correctly, you should never get premature or unexpected program termination.

There is a full tutorial on nullable and existent types available here.

JS++ 0.8.5: Bug Fix Release

This is a small bug fix release.

The following minor issues have been fixed:

  1. Dictionary expression of empty arrays now has the correct type inferred
  2. Labels for foreach statements
  3. Type inference for System.Array.reduce
  4. Function inlining for System.String methods: padRight(int), padLeft(int), startsWith, endsWith

Issue #1:

Dictionary<unsigned int[]> x = {
    "foo": (unsigned int[]) [],
    "bar": (unsigned int[]) []
};

The casts were previously required in 0.8.4, but, in the latest release, casting of empty arrays is no longer necessary.

Issue #2:

outerLoop: foreach (var row in json) {
    // ...

    foreach(var cell in row.Cell) {
        // ...
        continue outerLoop;
    }
}

Issue #3:

string[] keys = dict.keys();
int x = keys.reduce(
    int(string previous /* type of 'previous' param */, string current) {
    },
    0 // and this type
);

In the above case, the two types must match. However, the type checker was inferring ‘external’ instead. This has been fixed in the latest release.

JS++ 0.8.4: Advanced Generics and System.String Expansion

We have significantly expanded the Standard Library with this release. In particular, System.String has undergone significant expansion.

System.String Highlights

between

string quotedWords = '"duck" "swan" "crab"';
// 'between' is smart enough to allow the same string to be used as a start and end delimiter
string[] words = quotedWords.between('"', '"');
Console.log(words); // [ "duck", "swan", "crab" ]

Documentation page: click here

format: C-like printf

"%s is %d years old".format("Joe", 10) // Joe is 10 years old

Documentation page: click here

escape

"a\r\nb".escape() // a\\r\\nb

Documentation page: click here

truncate

string text = "The quick brown fox jumped over the lazy dog.";
 
Console.log(text.truncate(9)); // "The quick..."

Documentation page: click here

repeat

"*".repeat(5) // *****

Documentation page: click here

count

"foobar".count("foo") // 1
"FOOBAR".icount("foo") // 1

Documentation (count): click here
Documentation (icount): click here

contains

"abc".contains("b") // true
"ABC".icontains("b") // true

Documentation (contains): click here
Documentation (icontains): click here

New System.String Methods

Here are all the new methods available for strings in JS++:

  • between – Gets substrings between two delimiters (does not use regex)
  • compact – Removes whitespace globally
  • contains/icontains
  • count/icount
  • countLines
  • countNonEmptyLines
  • startsWith/endsWith
  • escape/unescape – Escape the escape sequence characters (e.g. \n -> \\n)
  • escapeQuotes/unescapeQuotes
  • format – Similar to C’s printf
  • insert/append/prepend
  • isEmpty – uses .length === 0 rather than str1 === “” for performance, not everyone has time to benchmark every detail
  • isLowerCase
  • isUpperCase
  • isWhitespace
  • joinLines – collapses a string composed of multiple lines into a single line
  • joinNonEmptyLines
  • padLeft/padRight – remember the NPM debacle?
  • quote/quoteSingle – wraps the string in quotes
  • unquote – removes quote pairs
  • repeat – “*”.repeat(3) == “***”
  • reverse
  • splitLines – splits a string into a string[] (array) based on newlines
  • trim, trimLeft, trimRight, trimMulti, trimMultiLeft, trimMultiRight
  • truncate – Cuts off the string at the specified length (with support for custom ellipsis)

There are close to 50 new string methods (48 including overloads, 39 otherwise), and these methods should cover most application-level usages. With documentation, this resulted in +1400 new lines of code to System.String. I’m happy to announce we actually still have more methods (for System.String and others) on the way.

Every single method is documented. All documentation is online and available at the System.String index page.

We avoided regular expressions as much as possible to avoid runtime FSM construction, which takes time and space. Therefore, prefer JS++ methods such as "abc".endsWith("c") over the traditional regex/JavaScript /c$/.test("abc").

The best thing about JS++ is that it’s a compiled language. This gives you performance benefits that a JavaScript library with string utilities can never give you. For example:

if ("abc".isEmpty());

becomes:

if ("abc".length===0);

and

"abc".quote()

becomes:

'"'+"abc"+'"'

The astute observer will notice that both the above methods can be further optimized to reach “perfect” optimization. However, there is no optimizing compiler inside JS++ yet, and inserting branching logic into the code generator will result in technical debt.

Our goal with the Standard Library is to make it easier than ever to write applications compared to JavaScript. Side effects of our work on the JS++ Standard Library are performance, size, and correctness. JS++ dead code elimination means we can add hundreds of methods to System.String, but you only pay for the methods you actually use. For performance, not every team can afford to hire a JavaScript performance expert. Even if you have the performance expert, he can’t be expected to micro-optimize and benchmark every method.

Finally, with the JS++ Standard Library, we can fully avoid the NPM left-pad debacle.

import System;

Console.log("1".padLeft(4, "0")); // "0001"

fromString

Previously, to convert a string to number in JS++, it was a little unintuitive. For example:

int x = +"1000"; // use the unary + operator

For all numeric types, we’ve introduced the fromString, fromStringOr, and fromStringOrThrow static methods. The above example can be re-written to use Integer32.fromString:

int x = Integer32.fromString("1000");

Advanced Generics

JS++ 0.8.4 introduces covariant and contravariant generic types (including upcasting and downcasting for types with variants). Covariance and contravariance are based on use-site variance. At this time, we are not introducing declaration-site variance at all; we have higher priorities. In addition, we’ve introduced generic constraints (subtype constraints, multiple constraints, wildcard constraints, and more).

Finally, we have support for generic functions and generic static methods.

Everything from basic to advanced generic programming in JS++ is covered in our generic programming documentation.

When we released version 0.8.0, we introduced only basic generics. In today’s 0.8.4 release, you can consider generics fully implemented.

I highly encourage reading the generic programming documentation. To put it all together, here’s generic covariance and contravariance together with use-site variance:

import System;
 
abstract class Animal {}
class Tiger : Animal {}
 
abstract class Pet : Animal {}
class Dog : Pet {}
class Cat : Pet {}
 
class PetCollection
{
    Pet[] data = [];
 
    void insert(descend Pet[] pets) {
        foreach(Pet pet in pets) {
            this.data.push(pet);
        }
    }
 
    ascend Pet[] get() {
        return this.data;
    }
}
 
auto myPets = new PetCollection();
 
// Write operations (descend, covariance)
myPets.insert([ new Dog, new Cat ]);
// myPets.insert([ new Tiger ]); // not allowed
 
// Read operations (ascend, contravariance)
Pet[] getPets = [];
Animal[] getAnimals = [];
ascend Pet[] tmp = myPets.get(); // read here
foreach(Pet pet in tmp) { // but we still need to put them back into our "result" arrays
    getPets.push(pet);
    getAnimals.push(pet);
}
 
// Now we can modify the arrays we read into above
getPets.push(new Dog);
getAnimals.push(new Dog);
getAnimals.push(new Tiger);
// getPets.push(new Tiger); // ERROR

Other Changes

  • Fix return types for System.String.charAt and System.String.charCodeAt
  • Fix type promotion to ‘double’. We now handle this better than languages like Java and C#. Thanks to our lead engineer, Anton, for the idea.
  • isEven() and isOdd(). You might think this is fizz buzz, but if you’re using the modulus operator, it’ll be slower. We use bitwise operations, and you might be interested in reading this article on how we took advantage of overflow behavior to improve performance while preserving correctness.
  • Fix System.Array.map and System.Array.reduce to support wildcard generic types.
  • Type inference of generic parameters for function calls. This is needed for System.Array.map and System.Array.reduce, but it’s also available for user-side code.
  • Fix System.Console.error when no console is available
  • Fixed error message with incorrect type for setters defined with no accompanying getters.
  • Fixed private access modifier for modules in a multi-file setting.
  • Fix callback types as generic arguments
  • Fix enum bitwise operations to reduce explicit casting

JS++ 0.8.1: auto, catch-all, and Time

JS++ 0.8.1 fixes two major bug fixes that appeared when we introduced generics (0.8.0) and introduces some very useful new features. If you come from a C++ background, you might appreciate some of the new syntax :-).

‘auto’ Keyword

With the introduction of generic programming in 0.8.0, we need a more concise syntax to instantiate generic classes. The auto keyword solves this problem.

Now, instead of the following:

VeryLongClassName<string> foo = new VeryLongClassName<string>();

You can instead instantiate classes using auto:

auto foo = new VeryLongClassName<string>();

auto can be used for more than just generic classes though:

class Foo {}

auto foo = new Foo();

The auto keyword can only be used for variable declarations, and it reduces the redundancy of having to specify the type on the left-hand side only to repeat yourself on the right-hand side. This is known as local-variable type inference (to use the Java 10 terminology). For fans of C++, you may recognize this syntax.

Catch-all Clauses

This is another syntax that comes from C++.

Sometimes, you’ll want to catch an exception, but you don’t care to do anything with the exception object. In other cases, you may want to catch a specific type of exception but ignore all other exceptions. The “catch-all” clause can help you:

import System;

try {
    throw new System.Exception();
}
catch(System.Exception e) {
    Console.log(e.getMessage());
}
catch(...) {
    Console.log("The catch-all clause provides no exception object but will catch all exceptions - whether the type of the exception is internal or external.");
}
import Externals.JS;

try {
    throw new ReferenceError();
}
catch(...) {
    Console.log("Do nothing");
}

System.Time.TimeUnits

JS++ has an emphasis on readability. Have you ever seen code that looks like this?

setInterval(function() { /* ... */ }, 3600000);

The System.Time module has been introduced for dealing with time. Specifically, we’ve introduced a TimeUnits module to directly deal with the above case:

import System.Time;
external setInterval;

setInterval(void() { /* ... */ }, TimeUnits.hours(1));

Or, more succinctly:

import System.Time.TimeUnits;
external setInterval;

setInterval(void() { /* ... */ }, hours(1));

As always, we’ve had a focus on readable code. Here’s a preview of how well this composes:

import System;
import System.Time.TimeUnits;

Console.log(hours(1) + minutes(5));

Externals.Time

In JavaScript, functions such as setTimeout and setInterval are not part of the ECMAScript standard; they are provided by the host environment. Web browsers and Node.js ship with these functions, but it would not have been correct to add this to the Externals.JS module. Therefore, we’ve introduced the Externals.Time external module so you can more clearly work with timers.

Bug Fixes

  • Fixed methods with variadic parameters passing by reference
  • Fixed implementing of generic interfaces from generic classes
  • MAJOR BUG FIX: Fixed double calls being generated for getters
  • MAJOR BUG FIX: Fix this binding in code generation

JS++ 0.8.0: Altitude MVC, Generics, System.Dictionary, and More

This is a major update that has been in the works for a while.

Altitude MVC

Altitude MVC

Altitude is an MVC/MVP framework for JS++. It follows a “pure MVC” approach. In other words, the user uses the controller to manipulate the model, the model updates the view, and the view is fed back to the user as explained in this diagram from Wikipedia.

The “pure MVC” approach has historically proven to scale. Internally, we have tested Altitude for an application consisting of 3,000+ lines of JS++ code, and it has served us well.

Lastly, Altitude contains fewer than 100 lines of code (if you remove comments and whitespace). In an age when load times influence search rankings and you can get downgraded to a 2G/3G connection at any time, it’s important for libraries to be as lightweight as possible. However, there is one external JavaScript dependency (Handlebars.js), and, optionally, jQuery.

Despite the small size of the library, we’ve managed to scale it for a relatively large and complex internal email application. Examples are included to get you started. As usual, we also spoil you with documentation.

Download it here.

Generics

The major new language feature is generics (parametric polymorphism). You can now define generic classes in JS++:

class Foo<T>
{
    /* ... */
}

Generic programming allows you to specify “type parameters” for a class. This allows you to create classes that can operate on any data type.

In version 0.8.0, JS++ supports the following generic programming features:

  • Multiple generic arguments
  • Multiple generic constraints
  • Inheritance (including generic from generic)

More advanced generic programming features (such as sound covariance/contravariance) are scheduled for upcoming releases.

System.Dictionary<T>

While generics are a major feature, on a day-to-day basis, the official introduction of System.Dictionary<T> may be more important. Dictionaries are documented here and here. Dictionaries are also known as associative arrays, maps, etc.

Dictionaries allow you to create key-value pairs. The keys must be strings, but the values can be any data type.

import System;

Dictionary<unsigned int> ages = {
    "Steve": 25,
    "David": 31
};

Arbitrary key types will be possible in the future as we continue to expand the Standard Library. It’s going to be so much easier than JavaScript objects with non-string keys.

Multi-line Strings

This feature was available from the last release (0.7.0), but it wasn’t properly announced.

You can create multi-line strings with a syntax similar to Python’s:

string html =
    """
    <!DOCTYPE html>
    <head></head>
    <body>
    <div id="notice the quotes are unescaped">some content goes here</div>
    </body>
    </html>
    """;

Multi-line strings are a useful way to create heredocs. One use case is pasting large blocks of HTML text that you don’t want to manually escape.

System.BoxedExternal

I consider this a critical class. If you understand the JS++ type system, you should understand the difference between “internal” types and “external” types. The BoxedExternal class basically allows you to construct an internal value (with an internal type) using any external value that has an external type.

When you box an external, it has an internal type. When you unbox the boxed external, you get a value with an external type. It’s extremely useful and is one way to achieve safe bi-directional communication between JavaScript and JS++.

Furthermore, if you’re using System.Object in your APIs, it becomes absolutely critical. Sometimes, you might want to define a function that can accept internal or external values.

System.Benchmark

This was a user request. The System.Benchmark class makes it easy for you to benchmark your code.

Here’s an example:

import System;
 
Benchmark bench = new Benchmark();
bench.start();

// ... do stuff
for (int i = 0; i < 500; ++i) {
    Math.sqrt(2);
}

bench.stop();
  
Console.log(bench.duration);

It wasn't that this wasn't possible prior to the Benchmark class being introduced. It was just painful and unreadable. Remember: JS++ has an emphasis on code readability.

Dead Code Elimination (DCE) Improvements

In previous versions of JS++, dead code elimination (DCE) would not eliminate inherited methods. Since 0.8.0, the JS++ compiler supports DCE even in inheritance settings. This reduces the code size and footprint of the final output.

.js++ File Extension

Previously, JS++ supported the .jspp and .jpp file extensions. As of version 0.8.0, the JS++ compiler now officially supports the .js++ file extension too. When you input a directory to the compiler, it will automatically recursively search for .jspp, .jpp, and .js++ files now.

In order to support the new .js++ file extension, you may need to update your editor support.

Warnings

This is probably the biggest release if you've been annoyed with the compiler warnings. We've completely suppressed the warnings for variable shadowing. JSPPW0015 and JSPPW0016 are gone. We're going to see how this goes or at least come up with saner rules before raising warnings. We've had applications internally that were raising hundreds of warnings on source code that otherwise compiled OK.

The #1 design principle here is that excessive warnings cause users to ignore warnings. Thus, we're being extra careful here.

Miscellaneous

  • System.Date copy constructor added
  • Binding on virtual methods
  • Removed System.Object.hashCode
  • Fixed segmentation fault for [].concat(1, 2, 3)
  • Fix segmentation fault for if(foo) where foo is an object
  • Fix closure capturing variable with name arguments
  • Don't cache .length of an array in for-in/each loops, to address potential array modification inside loops
  • Allow implicit conversion for double[] a = [1];
  • Forbid access modifier changes on override
  • Don't generate prototype chain for non-virtual methods
  • Lowest Common Ancestor (LCA) for expressions. This allows the type checker to deduce the type for arrays and dictionaries when polymorphism is present.

Looking Ahead

Expect Standard Library expansion and modifications.

We made changes to the original JavaScript API in our original Standard Library implementation. (Because whose great idea was it to introduce a String.substring and a String.substr in JavaScript?) We're likely going to restore the JavaScript API, and we're going to add data structures such as sets, maps, and more. We're also thinking about popular cryptography algorithms such as MD5, SHA1, and AES.

Meanwhile, generics will continue to be expanded and improved in future short-term releases.

JS++ 0.5.2: BSD License, Interfaces, Abstract Classes, Virtual Methods, Non-generic Standard Library Classes

You’ve asked and we’ve listened. JS++ is now licensed under the 3-clause BSD License.

When we first announced 0.5.1 back in March, we introduced bare minimum classes. Specifically, I noted the following class features were unavailable:

  • Generic classes
  • Abstract classes
  • Inner classes
  • Interfaces
  • Virtual methods
  • Custom Conversion Rules as defined in my book, “The JS++ Type System”, Chapter 6.2

Every feature in the above list that isn’t crossed out is now available except the last feature (custom conversion rules) which will be arriving next. In addition, today’s release marks the introduction of the Standard Library. I’m going to briefly introduce the new features.

Update to Hello World

The JS++ Hello World program is now written as:

import System;

Console.log("Hello World");

Notice we no longer have to declare console as external. external is used for importing JavaScript libraries, and since we didn’t have a JS++ console implementation yet, we resorted to using the JavaScript console. However, now that we have a Console class in the Standard Library, it’s no longer a problem.

It is always recommended that you use the Standard Library’s Console class over the external JavaScript console. JS++ will detect if a console is available and will not crash if you try to log to an unavailable console. This can be a problem for web browsers like older versions of Internet Explorer, which are still used heavily in enterprise web applications.

Standard Library

The following Standard Library classes are now available:

  • System.Boolean
  • System.Character
  • System.Console
  • System.Date
  • System.Double
  • System.Exception
  • System.Exceptions
  • System.Integer8
  • System.UInteger8
  • System.Integer16
  • System.UInteger16
  • System.Integer32
  • System.UInteger32
  • System.Math
  • System.Object
  • System.RegExp
  • System.String

Many of the above classes, such as System.String and System.Integer32, are wrapper classes for auto-boxing. Currently, these wrapper classes simply provide a type-safe (and sometimes optimized) version of their JavaScript-equivalent methods. For example:

import System;

string s = "my string";
Console.log(s.replace(/^[a-z]/, string(string match){ return match.toUpperCase(); })); // Prints "My string"

The above example provides the exact same functionality as JavaScript’s String.prototype.replace. However, you get safety guarantees that you wouldn’t get with JavaScript. For example, if you try to call System.String.replace using the wrong arguments:

Console.log(s.replace(1));
[  ERROR  ] JSPPE5023: No overload for `System.String.replace' that takes `1' argument(s) at line 4 char 12 at replace.jspp

Optimizations

Always favor using the JS++ Standard Library over “rolling your own” functions. Consider the following code (which you can run yourself with the latest JS++):

import System;

double t = (new Date).getTime();
string z;
for (int i = 0; i < 5000000; ++i) {
	z += Double.POSITIVE_INFINITY.toString();
}
Console.log((new Date).getTime() - t);

And the nearly equivalent JavaScript code:

var t = (new Date).getTime();
var z = "";
for (var i = 0; i < 5000000; ++i) {
	z += Number.POSITIVE_INFINITY.toString();
}
console.log((new Date).getTime() - t);

JS++ average time: 124.4ms
JavaScript average time: 211ms

In this case, JS++ is roughly 70% faster... for nearly identical code.

You may think JS++ adds overhead (based on perceptions of what fast code may look like), but well-written JS++ will be faster than JavaScript. See my other article on optimization for more details.

Typed Exceptions and Multiple Catch Clauses

JS++ 0.5.2 introduces the System.Exception class and enables you to create your own custom exceptions.

Here's an example:

import System;

class CustomException : System.Exception
{
    CustomException() {
        super();
    }
    CustomException(string message) {
        super(message);
    }
}

try {
    throw new CustomException("This is a custom exception object.");
}
catch(CustomException e) {
    Console.log("Caught CustomException");
}
catch(System.Exception e) {
    Console.log("Caught System.Exception");
}

Variadic Parameters

The latest version of JS++ also introduces variadic parameters, which allow you to supply an arbitrary number of arguments to a function:

import System;

void log(Date date, ...string messages, bool silent) {
    if (silent) return;

    foreach(string message in messages) {
        Console.log(date.toString() + ": " + message);
    }
}

log(new Date(), "1", "2", "3", false);

Interfaces

An interface creates a contract. Methods defined in an interface must be implemented by all inheriting classes. Classes can inherit more than one interface.

According to JS++ naming conventions, interfaces should be prefixed with "I" and should be UpperCamelCase.

import System;

interface IWalkable {
	void walk();
}
interface ITalkable {
	void talk();
}

class Person : IWalkable, ITalkable
{
	void talk() {
		Console.log("Talking...");
	}
	void walk() {
		Console.log("Walking...");
	}
}

Person person = new Person();
person.talk();
person.walk();

Callback Type Parameter Names

Callback types can have parameters. Previously, you could only specify the parameter types for a callback/function type. However, you can now add names for these parameters. While these names cannot be used and have no meaningful effect on the executed code, they improve the readability of the code.

import System;

class Bird
{
	void fly() {
		Console.log("Flying...");
	}
}

void(Bird bird) fly = void(Bird bird) {
	bird.fly();
};
Bird bird = new Bird();
fly(bird);

Removal of 'Convert' Module

We have removed from the Convert module from the latest release. It was always used as a stopgap until we implemented the Standard Library wrapper classes, which provide toString() and other methods.

Bug fix: 'typeof' for internal types

For non-external types, typeof will always return the string "internal".

import System;

int x;
Console.log(typeof x); // "internal"

Virtual Methods

JS++ 0.5.2 introduces the virtual keyword and the override keyword to enable virtual methods on classes.

Virtual methods enable late binding and runtime polymorphism.

class Shape
{
    public virtual double area() {
        return 0;
    }
}
 
class Rectangle : Shape
{
    private int length, width;
 
    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }
 
    public override double area() {
        return length * width;
    }
}
 
class Triangle : Shape
{
    private int base, height;
 
    public Triangle(int base, int height) {
        this.base = base;
        this.height = height;
    }
 
    public override double area() {
        return (base * height) / 2;
    }
}

Abstract Classes

Use the abstract modifier to create abstract classes and methods.

abstract class Shape
{
    public abstract int area();
}
class Rectangle : Shape
{
    private int length, width;
 
    public override int area() {
        return length * width;
    }
}

Enumerations

Enumerations (enums) can be used to restrict values and write type-safe code:

enum Importance { None, Regular, Critical }
 
Importance errorLevel = Importance.Critical;

The one missing feature...

Sadly, there is still one major missing feature from JS++. The Standard Library does not support System.Array yet because it is a generic class, and generics have not yet been implemented. In the meantime, you can resort to declaring your arrays as var:

var arr = [ 1, 2, 3 ];

BSD License

Last, but not least, JS++ 0.5.2 is the first version of JS++ licensed under the 3-clause BSD License.

The download for JS++ 0.5.2 is available from our home page.

Enjoy JS++!

JS++ 0.5.1: ‘foreach’

We just released JS++ 0.5.1, which features the foreach keyword.

Basic usage is as follows:

external console;

int[] arr = [ 50, 40, 30 ];

foreach (int value in arr) {
    console.log(value);
}

// Output:
// 50
// 40
// 30

More complex usage, such as arrays of objects, are also possible:

external console;

class Foo
{
	string bar() {
		return "bar";
	}
}

Foo[] foo = [ new Foo(), new Foo(), new Foo() ];

foreach (Foo value in foo) {
    console.log(value.bar());
}

// Output:
// "bar"
// "bar"
// "bar"

Additionally, the for-in keyword has been implemented. It is a counterpart to the foreach keyword in the sense that it enumerates keys rather than values.

JS++ 0.5.0: Basic Classes

JS++ now supports the ‘class’ keyword. As promised, we would have classes available by Q1 2017. Notably, we are providing “basic” classes, so the following features are supported:

  • ‘class’ keyword
  • Inheritance
  • Constructors (including private/protected constructors that limit instantiation/inheritance)
  • Static Constructors
  • Instantiation
  • Fields
  • Methods
  • ‘this’ keyword
  • ‘super’ keyword
  • Method Overloading
  • Constructor Overloading
  • Getters and Setters (via ‘property’ keyword)
  • Type system support
  • Debugger support for classes (via source maps)

What did we consider to be outside the scope of basic classes? The following features are currently not available yet:

As an example of what you can do with JS++ classes, we included an example with the Tiled Map Editor:

Tiled Map Editor

The included sample for Tiled can be found in the ‘Samples/Classes/tiled-loader’ directory with JS++ 0.5.0. Currently, it will load all maps exported to JSON with orthogonal tiles. It’s just a small but powerful example of what you can start doing with JS++ for early adopters.

I am also happy to inform that the backend for the JS++ website is written completely in JS++, and it has now run stable for one week without a single crash or error (other than 404 errors).

Finally, we have made the decision to not include the Mac/Linux installer… yet.

Be sure to check out the documentation on the ‘class’ keyword to get up to speed on JS++ classes.

Download JS++ 0.5.0 from our home page.

Mac/Linux Installer Coming for JS++ 0.5

The next version of JS++ will feature the ‘class’ keyword and is expected for Q1 2017.

In addition, Mac and Linux users will benefit from a JS++ installer:


The installer is completely optional. However, it will be useful if you don’t want to manually install JS++ and various editor plugins. We auto-detect which editors are installed on your system, and, if a plugin is available, we will attempt to install it for you.

JS++ for Mac OS X Updated to Latest Version

During our Christmas release, we had to release one version down (0.4.2.3) for Mac OS X. I’m happy to announce that – now that our staff are back from their breaks – JS++ for Mac OS X has been updated to the latest version and is in sync with JS++ for Windows and Linux.

Please note that, as of 0.4.2.4 for Mac OS X, JS++ defaults to the 32-bit binary. The 64-bit binary is available as jspp-x64.

You can download JS++ for Mac OS X from our homepage.