Tips & Tricks: fromExternal/toExternal Design Pattern

JS++ provides toString and fromString (one example) methods in the Standard Library. However, it can be argued that the external type is just as important or even more important in JS++ as string.

We introduce a design pattern for converting complex user-defined JS++ types (such as classes) to JavaScript.

toExternal

You can define a toExternal method that enables you to convert an object of an internal type to external:

import System;

class Point
{
    int x;
    int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    function toExternal() {
        return {
            x: x,
            y: y
        };
    }
}

Point p = new Point(2, 3);
var p2 = p.toExternal(); // conversion to 'external'
Console.log(p2.x); // 2
Console.log(p2.y); // 3

fromExternal

Likewise, you can convert incoming JavaScript data to a complex, user-defined JS++ type:

import System;
import System.Assert;

class Point
{
    int x;
    int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    static Point fromExternal(obj) {
        assert(typeof obj == "object", "Expected external 'object' type");
        assert(typeof obj.x == "number", "Expected incoming external to have numeric 'x' property");
        assert(typeof obj.y == "number", "Expected incoming external to have numeric 'y' property");
        return new Point(Integer32.fromString(obj.x), Integer32.fromString(obj.y));
    }
}

Point p1 = Point.fromExternal({ x: 2, y: 3 });
// Point p2 = Point.fromExternal({ x: "x", y: 3 }); // this will fail
// Point p3 = Point.fromExternal({ x: 2, y: "y" }); // this will fail

Protecting References

For functions, you don’t want to send out a direct reference to external JavaScript. Otherwise, external JavaScript code can modify the JS++ reference in unsafe ways. Therefore, you should wrap the function using closures:

class Foo
{
    void bar() {}

    function toExternal() {
        Foo self = this;

        return {
            bar: void() {
                self.bar();
            }
        };
    }
}

Furthermore, you can use the Standard Library’s System.BoxedExternal to handle this case without wrapping in a closure:

import System;

class Foo
{
    BoxedExternal bar;

    Foo() {
        this.bar = new BoxedExternal(void() {
        // ...
        });
    }
 
    function toExternal() {
        return {
            bar: this.bar
        };
    }
}

If the reference to the function accidentally escapes to external, you’ll be alerted by the compiler:

[ ERROR ] JSPPE5000: Cannot convert `System.Dictionary‘ to `external’ at line 14 char 15

However, if you actually intended to allow the function reference to escape to external, you can call the unbox method on System.BoxedExternal:

import System;

class Foo
{
    BoxedExternal bar;

    Foo() {
        this.bar = new BoxedExternal(void() {
            // ...
        });
    }
 
    function toExternal() {
        return {
            bar: this.bar.unbox()
        };
    }
}

The above code will now compile and the bar function can be passed to external code. However, unlike the code where we wrapped the function in a closure, the external code can now modify the reference to the bar function directly so be careful.

For arrays and containers, you can likewise pass a shallow copy or manually clone each element – depending on the level of trust and safety you desire.

Tips & Tricks: Only Fields are ‘private’ by Default

Programmers often complain about the verbosity of Java. Once you specify all the modifiers that must be applied, it’s not difficult to see how it can quickly become verbose:

public static void veryLongNamingConventions() {
    // ...
}

JS++ does this differently. Following the OOP principle of encapsulation, JS++ provides convenient default rules for access modifiers.

By default, only fields (variable members of classes) are private. All other class members – such as methods, getters, setters, and constructors – are public by default.

This makes it very easy to write concise code:

class Point
{
    int x, y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    int getX() { return this.x; }
    int getY() { return this.y; }
}

In the above code, the fields x and y are private. Meanwhile, the constructor and the getX/getY methods are all public. We can be explicit and manually specify the access modifiers, but it’s not necessary in JS++.

Tips & Tricks: Structural Typing (in a Nominal Type System) with ‘auto’ Type Inference

By default, JS++ uses nominal typing over structural typing. However, structural typing can be achieved using auto.

The following is a contrived example but illustrates a consideration in your application when using auto:

class Foo
{
    bool isError() { return false; }
}

auto foo = new Foo();
if (foo.isError()) {
    exit(1);
}

Now, during refactoring:

class Foo
{
    bool isError() { return false; }
}
class Bar
{
    bool isError() { return true; }
}

auto foo = new Bar(); // type changed
if (foo.isError()) { // this effectively becomes static duck typing
    exit(1);
}

Since the variable foo above effectively becomes “duck typed” (or, more precisely, structurally typed), it becomes harder for the compiler to potentially catch your errors.

While the above example is contrived, it’s not difficult to see how this can crop up in more complex applications.

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

Bitwise Operators and Specification-compliant Integer Overflow Optimizations

If you use the upcoming UInteger32.isEven or isOdd methods, you’ll notice that it uses a bitwise AND operation. The reason, as described in a previous post, is because it improves performance.

However, while this is straightforward for all other integer wrapper classes, UInteger32 is an exception. According to ECMAScript 3 11.10:

The production A : A @B, where @ is one of the bitwise operators in the productions above, is evaluated as follows:

  1. Evaluate A.
  2. Call GetValue(Result(1)).
  3. Evaluate B.
  4. Call GetValue(Result(3)).
  5. Call ToInt32(Result(2)).
  6. Call ToInt32(Result(4)).
  7. Apply the bitwise operator @ to Result(5) and Result(6). The result is a signed 32 bit integer.
  8. Return Result(7).

The operands (and, thus, the result type) for the bitwise AND operation in ECMAScript are converted to 32-bit signed integers. System.UInteger32 represents an unsigned 32-bit integer.

This is inconvenient because we’d obviously have to fall back to the slower modulus operation for isEven/isOdd on UInteger32. Unless…

((Math.pow(2, 32) + 1) >>> 0 | 0) & 1 // true
((Math.pow(2, 32) + 2) >>> 0 | 0) & 1 // false

We can take advantage of overflow behavior. (Note: Since we’re able to get the correct result by leveraging overflow behavior, we actually don’t perform the extraneous zero-fill right shift as illustrated in the example.)

This is completely safe because:

A) Mathematically, in base 2, the last bit will always be 1 for odd numbers and 0 for even numbers… no matter how big the number is.

B) Bitwise AND will compare both bits in equal-length binary forms. Thus, no matter how big the number is, when you AND against 1, it will always be 0000001 (or zero-padded to whatever length is needed). Therefore, all the preceding bits don’t matter because they will always be ANDed against a zero bit. The only bit that matters is the trailing bit; see A for why this will always work.

Standard Library Performance: isEven() and isOdd()

Remember what I always advise: always use the JS++ Standard Library if you can. The methods aren’t just well-tested for validity, but we also test for performance.

Checking if a number is even or odd is the classic fizzbuzz test. Most professional developers can use the modulus operator. However, that’s not always the fastest implementation.

> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i & 1) == 0; console.log(x); new Date - t;
false
87
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i & 1) == 0; console.log(x); new Date - t;
false
90
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i & 1) == 0; console.log(x); new Date - t;
false
86
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i & 1) == 0; console.log(x); new Date - t;
false
86
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i & 1) == 0; console.log(x); new Date - t;
false
86

= 87ms

> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i % 2) == 0; console.log(x); new Date - t;
false
105
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i % 2) == 0; console.log(x); new Date - t;
false
100
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i % 2) == 0; console.log(x); new Date - t;
false
100
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i % 2) == 0; console.log(x); new Date - t;
false
104
> var t = new Date(); var x; for (var i = 0; i < 50000000; ++i) x = (i % 2) == 0; console.log(x); new Date - t;
false
101

= 102ms

Node.js v8.11.1 Linux x64
Core i7-4790k, 32gb RAM
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i & 1) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
1948 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i & 1) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2072 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i & 1) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2086 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i & 1) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2092 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i & 1) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2102

= 2060ms

var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i % 2) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2058 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i % 2) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2082 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i % 2) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2114 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i % 2) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2102 debugger eval code:1:96
var t = new Date(); var x; for (var i = 0; i < 5000000; ++i) x = (i % 2) == 0; console.log(x); console.log(new Date - t);
false debugger eval code:1:80
2104 debugger eval code:1:96

= 2092ms

Firefox 59.0.2, Linux x64
Core i7-4790k, 32gb RAM

While the results are not statistically significant in Firefox (because it's very possible SpiderMonkey is manually optimizing this case via a pattern-matched optimization), you can get a 17% performance gain in Node.js via bitwise AND.

Due to all the layers of abstraction in JavaScript, it's not entirely evident how much faster a bitwise AND for isEven/isOdd can really be. In our benchmarks, we were able to achieve a 17% performance improvement in Node.js. As our lead engineer pointed out via email, according to this table, "for Intel Skylake-X `div` has a latency of 26 (for 32-bit integers), whereas `and` has latency 1 ("reciprocal throughput" has similar difference) so it is an order of magnitude slower, not 20% as in your tests."

Look for isEven() and isOdd() to appear in a future version of the JS++ Standard Library.

You may also be interested in reading Part II of this post which describes how we leveraged overflow behavior to improve performance while preserving correctness for UInteger32.

Google Image Search Broken with TypeErrors

First of all, this is not an April Fools joke. It’s unfortunate that Easter Sunday falls on April Fools Day.

Google Image Search isn’t working with custom image sizes on Easter Sunday. A closer inspection reveals a plethora of runtime TypeErrors:

When you click “Go” above, nothing happens. Form submissions that do nothing are a common problem with JavaScript runtime failures.

It’s important to remember, with JS++ “type guarantees,” it is possible to completely eliminate type errors via a sound type system.

Update:

This bug is still happening as of Monday, April 2. It’s happening as far as Russia. Steps to reproduce:

1. Go to google.com
2. Search “black forest”
3. Navigate to Image Search
4. Under Tools -> Size -> Exactly… Try 1920×1080.

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.