Bridge 1.12 Released

The Bridge.NET team has been working overtime to ensure version 1.12 is packed full of important new features and fixes. We’re excited to announce the immediate availability of Bridge.NET 1.12.

Please download or install using NuGet.

Install-Package Bridge

For a full list of closed issues, please review in the GitHub Bridge repository. There’s a lot to cover, so let’s jump into Bridge 1.12.

Regex Support

In prior releases, Bridge provided great coverage of the JavaScript Regex API, including the JavaScript native test, search and replace functions. The following sample demonstrates a basic pure JavaScript API only regex block written in C#, and the resulting compiled JavaScript output.

Write C#.
Run JavaScript.
C#
var result = new Regex(“^hello”)
    .Test(“Hello, World”);

Console.WriteLine(result); // true 
JS
var result = new RegExp(“^hello”)
    .test(“Hello, World”);

console.log(result); // true 

While providing JavaScript Regex API coverage was excellent for porting JavaScript code into C#, it wasn’t the best if you were attempting to reuse existing C# Regex code with the Bridge compiler. The API support just wasn’t there and developers were forced to re-write C# code into the simpler JavaScript Regex engine, or just avoid porting altogether.

We have re-focused our efforts and are investing resources into ensuring the API and functionality coverage within Bridge is focused on supporting the C# API first.

Our ultimate goal is that you should be able to take any existing C# code supported by a .NET Portable Class Library and compile it directly to JavaScript using Bridge.NET, without making a single modification to the original C# source.

As of Bridge 1.12 we’ve taken a major step closer to that goal, and we’re thrilled to announce support for the .NET System.Text.RegularExpressions namespace, including the Match class, Named Capture Groups, MatchCollection and GroupCollection classes.

Including .NET Regular Expressions compiler support within Bridge 1.12 required focusing on two goals:

  1. .NET System.Text.RegularExpressions API (classes and methods)
  2. .NET Regular Expressions Engine (logic)

The following sample matches any word that begins with the characters th and is not followed by an o. This sample also demonstrates the use of the Negative Character Group [^] functionality. The example is taken directly from Regular Expression Language – Quick Reference on MSDN.

Example (Live)

C#
// Match any word that starts with ‘th’, but not ‘tho’
string pattern = @”\bth[^o]\w+\b”;

string input = “thought thing though them through thus thorough this”;

foreach (Match match in Regex.Matches(input, pattern))
{
    Console.WriteLine(match.Value);
}
 
 
 
/*
    Matches:
        thing
        them
        through
        thus
        this
*/ 
JS
var $t;
// Match any word that starts with ‘th’, but not ‘tho’
var pattern = “\\bth[^o]\\w+\\b”;

var input = “thought thing though them through thus thorough this”;

$t = Bridge.getEnumerator(Bridge.Text.RegularExpressions.Regex.matches(input, pattern));
while ($t.moveNext()) {
    var match = $t.getCurrent();
    console.log(match.getValue());
}

/*
    Matches:
        thing
        them
        through
        thus
        this
*/ 

The table below details the current level of support for the .NET Regular Expressions Engine within Bridge.

Regex Class or Feature Status Exceptions
Regex  
Capture  
CaptureCollection  
Group  
GroupCollection  
Match  
MatchCollection  
MatchEvaluator  
Character Escapes  
Quantifiers
Substitutions  
RegexOptions
Supported
IgnoreCase and Multiline
Not Supported
Compiled, CultureInvariant, ECMAScript, ExplicitCapture, IgnorePatternWhitespace, RightToLeft, Singleline
Backreference Constructs
Supported
See Table Below
Not Supported
\k (Planned for Bridge 1.13)
Character Classes
Supported
\w and \W only for Latin characters
Not Supported
Unicode categories p{name} and \P{name}
Anchors
Not Supported
\A \Z \z \G
Grouping Constructs
Not Supported
Balancing groups
imnsx options
Lookbehind assertions
Nonbacktracking subexpression
Alternation Constructs  
Miscellaneous Constructs  

The highlight feature is support for Named Captured Groups.

The JavaScript Regex engine does not include named captured group functionality, so this is a big advantage for .NET developers and now with great support within Bridge, a whole new platform of opportunities for existing .NET code has been opened up.

The following table outlines named captured group support within Bridge, as documented by the source MSDN article.

Named Captured Group Support

Status Technique
By using the backreference construct within the regular expression. The matched subexpression is referenced in the same regular expression by using the syntax \number, where number is the ordinal number of the captured subexpression. Named matched subexpressions are numbered consecutively from left to right after matched subexpressions.
By using the ${code} replacement sequence in a Regex.Replace or Match.Result method call, where name is the name of the captured subexpression.
By using the $number replacement sequence in a Regex.Replace or Match.Result method call, where number is the ordinal number of the captured subexpression.
Programmatically, by using the GroupCollection object returned by the Match.Groups property. The member at position zero in the collection represents the entire regular expression match. Each subsequent member represents a matched subexpression. Named captured groups are stored in the collection after numbered captured groups.
Programmatically, by providing the subexpression name to the GroupCollection object’s indexer.
Currently not supported. Planned for Bridge 1.13.
By using the named backreference construct within the regular expression. The matched subexpression is referenced in the same regular expression by using the syntax \k<name>, where name is the name of the captured subexpression.

Let’s have a look at a sample using C# Named Captured Groups.

Example (Live)

C#
[Ready]
public static void Main()
{
    App.ParseFilePath(@”C:\ParentDir\ChildDir\Archive.zip”);

    Console.WriteLine();
    App.ParseFilePath(@”D:\WithoutDir.doc”);

    Console.WriteLine();
    App.ParseFilePath(@”D:\WithoutExtension”);

    Console.WriteLine();
    App.ParseFilePath(@”InvalidPath”);
}

public static void ParseFilePath(string path)
{
    var rgx = new Regex(@”((?<Drive>\w):)(?<DirPath>(\\(?<DirName>[\w\d]+))*)\\(?<FileName>[\w\d]+)(?<FileExt>\.[\w\d]{1,4})?”);
    var match = rgx.Match(path);

    if (!match.Success)
    {
        return;
    }
    else
    {
        var drive = match.Groups[“Drive”].Value;
        var dir = match.Groups[“DirPath”].Value;
        var subDirs = match.Groups[“DirName”].Captures;
        var fileName = match.Groups[“FileName”].Value;
        var fileExt = match.Groups[“FileExt”].Value;

        Console.WriteLine(string.Format(“Drive: \”{0}\””, drive));
        Console.WriteLine(string.Format(“Dir path: \”{0}\””, dir));

        for (int i = 0; i < subDirs.Count; i++)
        {
            Console.WriteLine(string.Format(“Dir #{0}: \”{1}\””, i + 1, subDirs[i]));
        }

        Console.WriteLine(string.Format(“File name: \”{0}\””, fileName));
        Console.WriteLine(string.Format(“File ext: \”{0}\””, fileExt));
    }
}
 
 
 
 
 
 
 
  
JS
(function (globals) {
    “use strict”;

    Bridge.define(‘Demo.App’, {
        statics: {
            config: {
                init: function () {
                    Bridge.ready(this.main);
                }
            },
            main: function () {
                Demo.App.parseFilePath(“C:\\ParentDir\\ChildDir\\Archive.zip”);

                console.log();
                Demo.App.parseFilePath(“D:\\WithoutDir.doc”);

                console.log();
                Demo.App.parseFilePath(“D:\\WithoutExtension”);

                console.log();
                Demo.App.parseFilePath(“InvalidPath”);
            },
            parseFilePath: function (path) {
                var rgx = new Bridge.Text.RegularExpressions.Regex(“constructor”, “((?<Drive>\\w):)(?<DirPath>(\\\\(?<DirName>[\\w\\d]+))*)\\\\(?<FileName>[\\w\\d]+)(?<FileExt>\\.[\\w\\d]{1,4})?”);
                var match = rgx.match(path);

                if (!match.getSuccess()) {
                    return;
                }
                else  {
                    var drive = match.getGroups().getByName(“Drive”).getValue();
                    var dir = match.getGroups().getByName(“DirPath”).getValue();
                    var subDirs = match.getGroups().getByName(“DirName”).getCaptures();
                    var fileName = match.getGroups().getByName(“FileName”).getValue();
                    var fileExt = match.getGroups().getByName(“FileExt”).getValue();

                    console.log(Bridge.String.format(“Drive: \”{0}\””, drive));
                    console.log(Bridge.String.format(“Dir path: \”{0}\””, dir));

                    for (var i = 0; i < subDirs.getCount(); i = (i + 1) | 0) {
                        console.log(Bridge.String.format(“Dir #{0}: \”{1}\””, ((i + 1) | 0), subDirs.get(i)));
                    }

                    console.log(Bridge.String.format(“File name: \”{0}\””, fileName));
                    console.log(Bridge.String.format(“File ext: \”{0}\””, fileExt));
                }
            }
        }
    });

    Bridge.init();
})(this); 

The output from the above is:

Drive: “C”
Dir path: “\ParentDir\ChildDir”
Dir #1: “ParentDir”
Dir #2: “ChildDir”
File name: “Archive”
File ext: “.zip”
Drive: “D”
Dir path: “”
File name: “WithoutDir”
File ext: “.doc”
Drive: “D”
Dir path: “”
File name: “WithoutExtension”
File ext: “” 

The new Regex functionality is also well covered by more than 2200 new unit tests.

C# Regex features yet to be implemented are listed in Issue #1167.

The work for Regex support within Bridge was initiated by Issue #701.

Expanded .NET Regular Expressions support will be added in future releases, and please let us know if there’s something you really need and we’ll do our best to add support.

Long Type Support

Another core piece of .NET functionality that has been added with Bridge 1.12 is support for long and ulong.

We’re hearing whispers from the community of projects using Bridge to compile advanced .NET encryption and arithmetic projects into JavaScript, although lack of big number power was a limiting factor. Well, no more.

The goal was to perfectly mimic the native .NET behaviour, and we’re happy to say that Bridge 1.12 brings full support of long and ulong types (Issue #778). For references: MSDN long and ulong documentation.

Support for the checked and uncheсked keywords has also been included, as well as OverflowMode and CheckForOverflowUnderflow options. You can find thorough details on these features in Issue #1092.

The example below demonstrates some basic long operations.

Example (Live)

C#
long a = 1234567890;
long b = -9876543210;

// Long is quite long
Console.WriteLine(long.MinValue);
Console.WriteLine(long.MaxValue);

// Some long calculations
Console.WriteLine(long.MinValue + long.MaxValue);
Console.WriteLine(a * b);

// Default long value
Console.WriteLine(default(long)); 
JS
var a = Bridge.Long(1234567890);
var b = Bridge.Long([-1286608618,-3]);

// Long is quite long
console.log(Bridge.Long.MinValue.toString());
console.log(Bridge.Long.MaxValue.toString());

// Some long calculations
console.log(Bridge.Long(-1).toString());
console.log(a.mul(b).toString());

// Default long value
console.log(Bridge.getDefaultValue(Bridge.Long).toString()); 

The sample outputs:

-9223372036854775808 // long.MinValue
9223372036854775807  // long.MaxValue
-1                   // long.MinValue + long.MaxValue
6253480962446024716  // 1234567890 * -9876543210
0                    // default(long) 

Do you know a factorial of 13 cannot be calculated using the int type, because the result exceeds int.MaxValue. An int can only calculate a factorial up to 12. The long type lets us go a little further – up to 20.

The following sample demonstrates factorial calculations.

Example (Live)

C#
[Ready]
public static void Main()
{
    Console.WriteLine(“int.MaxValue: ” + int.MaxValue);
    Console.WriteLine(“Factorial of 12: ” + App.Factorial(12));
    Console.WriteLine(“Factorial of 13: ” + App.Factorial(13)); // exceeds int.MaxValue

    Console.WriteLine(“long.MaxValue: ” + long.MaxValue);
    Console.WriteLine(“Factorial of 20: ” + App.Factorial(20));
    Console.WriteLine(“Factorial of 21: ” + App.Factorial(21)); // exceeds long.MaxValue
}

public static long Factorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }

    long result = 1;

    for (int i = 2; i <= n; i++)
    {
        result = result * i;
    }

    return result;
} 
JS
main: function () {
    console.log(“int.MaxValue: ” + 2147483647);
    console.log(“Factorial of 12: ” + Demo.App.factorial(12));
    console.log(“Factorial of 13: ” + Demo.App.factorial(13)); // exceeds int.MaxValue

    console.log(“long.MaxValue: ” + Bridge.Long.MaxValue);
    console.log(“Factorial of 20: ” + Demo.App.factorial(20));
    console.log(“Factorial of 21: ” + Demo.App.factorial(21)); // exceeds long.MaxValue
},
factorial: function (n) {
    if (n <= 1) {
        return Bridge.Long(1);
    }

    var result = Bridge.Long(1);

    for (var i = 2; i <= n; i = (i + 1) | 0) {
        result = result.mul(Bridge.Long(i));
    }

    return result;
}
 
 
 
 
 
  

And outputs the following to the browsers console:

int.MaxValue    : 2147483647
Factorial of 12 : 479001600
Factorial of 13 : 6227020800
long.MaxValue   : 9223372036854775807
Factorial of 20 : 2432902008176640000
Factorial of 21 : -4249290049419214848 

Decimal Support Enhancements

Decimal class support was added to Bridge in the 1.8 release, but it was time for an upgrade.

The underlying Decimal.js library has been improved, so we’ve included the latest v5 release (Issue #1039). Decimal.js CHANGELOG documents a nice list of fixes and enhancements which automatically flowed into Bridge with the upgrade.

One such enhancement is removal of the 15 digits limitation which was reported by our community. Bridge 1.12 fixes this problem and it felt great to inform the Bridge community of the fix.

WebSockets

It is always so exciting to receive pull requests from Bridge Community members, and we’ve had some amazing pull requests sent to us during this last cycle.

This time it was the GitHub user @Dia6lo who contributed support for the HTML5 WebSocket API (Issue #776). The pull request quality from @Dia6lo was very high and it was a pleasure to merge.

To test and demonstrate the WebSocket functionality we have recreated the websocket.org’s Echo Test Demo using Bridge. The full source code for the WebSocket demo is available if you want to explore, or test the demo itself online.

Bridge.NET WebSocket Demo

More WebSocket support is planned for the Bridge 1.13 release. In particular, the implementation of some entities within the System.Net.WebSockets namespace such as ClientWebSocket. Again, contributed by @Dia6lo via a pull request.

C# Random Class Support

Incorporating Random class support in Bridge 1.12 was a genuine proof of the C#-to-JavaScript compiler concept.

Working on the Random class support (Issue #1080) we ate our own dog food, i.e. we used Bridge as a tool to create a JavaScript implementation of C# Random class.

The requirement was to 100% match C# Random class API and the goal has been achieved, because we compiled the actual original C# Core source code into JavaScript.

The native C# Random classes unit tests were also incorporated into Bridge, ensuring 100% .NET API compatibility.

Instructions on how to compile .NET C# source code into high-quality JavaScript libraries with 100% .NET API compatibility is included in Issue #1118.

The following sample generates 1 million random numbers and displays their distribution. Our sample reproduces the original MSDN sample, except it’s now running in JavaScript, in the browser.

Example (Live)

C#
Random rnd = new Random();

int lowerBound = 10;
int upperBound = 11;

int[] range = new int[10];

for (int ctr = 1; ctr <= 1000000; ctr++)
{
    Double value = rnd.NextDouble() * (upperBound – lowerBound) + lowerBound;

    range[(int)Math.Truncate((value – lowerBound) * 10)]++;
}

for (int ctr = 0; ctr <= 9; ctr++)
{
    Double lowerRange = 10 + ctr * .1;

    Console.WriteLine(“{0:N1} to {1:N1}: {2,8:N0}  ({3,7:P2})”,
                        lowerRange, lowerRange + .1, range[ctr],
                        range[ctr] / 1000000.0);
} 
JS
var rnd = new Bridge.Random(“constructor”);

var lowerBound = 10;
var upperBound = 11;

var range = Bridge.Array.init(10, 0);

for (var ctr = 1; ctr <= 1000000; ctr = (ctr + 1) | 0) {
    var value = rnd.nextDouble() * (((upperBound – lowerBound) | 0)) + lowerBound;

    range[Bridge.Int.clip32(Bridge.Int.trunc((value – lowerBound) * 10))] = (range[Bridge.Int.clip32(Bridge.Int.trunc((value – lowerBound) * 10))] + 1) | 0;
}

for (var ctr1 = 0; ctr1 <= 9; ctr1 = (ctr1 + 1) | 0) {
    var lowerRange = 10 + ctr1 * 0.1;

    console.log(Bridge.String.format(“{0:N1} to {1:N1}: {2,8:N0}  ({3,7:P2})”, lowerRange, lowerRange + 0.1, range[ctr1], range[ctr1] / 1000000.0));
}
 
 
 
  

The following output was generated, although will vary each time the sample is run.

10.0 to 10.1:   99,905  ( 9.99 %)
10.1 to 10.2:   99,761  ( 9.98 %)
10.2 to 10.3:   99,849  ( 9.98 %)
10.3 to 10.4:   99,878  ( 9.99 %)
10.4 to 10.5:  100,260  (10.03 %)
10.5 to 10.6:  100,526  (10.05 %)
10.6 to 10.7:  100,411  (10.04 %)
10.7 to 10.8:   99,872  ( 9.99 %)
10.8 to 10.9:  100,021  (10.00 %)
10.9 to 11.0:   99,517  ( 9.95 %) 

Credits to the active Bridge Community member @michaelcheers who submitted a pull request with an initial draft of the Random Class Support implementation. In the end we didn’t merge the pull request, but it pushed us to implement an alternative technique that we had been thinking about for a while.

Custom bridge.json Config Files

It is very important to be able to configure globally as much as possible and we keep adding features in this area. In particular with Bridge 1.12 we’ve added custom bridge.[Release|Debug|Any].json configuration support (Issue #1033).

This now means it is possible to configure custom bridge.json files for specific Build modes.

Build Modes

If your project has the default Release and Debug build modes, you can now define separate bridge.json files for each build mode, as well as a default bridge.json file for common configs of all build modes.

bridge.json Common configurations applied to all build modes.
bridge.Debug.json Configuration only applied during Debug mode compilation. Takes precedence over the same settings if configured in bridge.json.
bridge.Release.json Configuration only applied during Release mode compilation. Takes precedence over the same settings if configured in bridge.json.
The config file name is case-sensitive. For example, if the build mode name is Release, the file name must be bridge.Release.json, and not bridge.release.json.

Let’s consider a scenario using two global bridge.json settings: output and generateDocumentation.

The output setting configures an output folder path for compiled JavaScript. In our scenario we don’t need to target different output folders based on build modes, therefore we can add as a common bridge.json setting.

The generateDocumentation setting is used to manage inclusion of JSDoc compatible documentation. In our scenario, the project requirements call for documentation to be generated with Release builds, therefore we add the setting to bridge.Release.json.

See the Global Configuration Knowledge Base article for all the available global Bridge Compiler settings.

The screenshot below demonstrates how the configuration files should look in our scenario.

Bridge Json

Let’s now build this test case in Debug and Release modes, then compare the output.

C#Demo App Test
using System;
using Bridge.Html5;

namespace Demo
{
    public class App
    {
        /// <summary>
        /// The Main method is marked with a Ready attribute to be executed on page load.
        /// </summary>
        [Ready]
        public static void Main()
        {
            var s = App.ConvertToString(100);
        }

        /// <summary>
        /// Converts the specified number to a string
        /// </summary>
        /// <param name=”number”>The number to be converted to a string
        /// <returns>The result of converting the number to a string
        public static string ConvertToString(int number)
        {
            return number.ToString();
        }
    }
} 

Here the two JavaScript files clearly demonstrate the results from the two different compilation modes.

JSJS Compiled In Debug Mode
(function (globals) {
    “use strict”;

    Bridge.define(‘Demo.App’, {
        statics: {
            config: {
                init: function () {
                    Bridge.ready(this.main);
                }
            },
            main: function () {
                var s = Demo.App.convertToString(100);
            },
            convertToString: function (number) {
                return number.toString();
            }
        }
    });

    Bridge.init();
})(this);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  
JSJS Compiled In Release Mode
(function (globals) {
    “use strict”;

    /** @namespace Demo */

    /**
     * @public
     * @class Demo.App
     */
    Bridge.define(‘Demo.App’, {
        statics: {
            config: {
                init: function () {
                    Bridge.ready(this.main);
                }
            },
            /**
             * The Main method is marked with a Ready attribute to be executed on page load.
             *
             * @static
             * @public
             * @this Demo.App
             * @memberof Demo.App
             * @return  {void}
             */
            main: function () {
                var s = Demo.App.convertToString(100);
            },
            /**
             * Converts the specified number to a string
             *
             * @static
             * @public
             * @this Demo.App
             * @memberof Demo.App
             * @param   {number}    number    The number to be converted to a string
             * @return  {string}              The result of converting the number to a string
             */
            convertToString: function (number) {
                return number.toString();
            }
        }
    });

    Bridge.init();
})(this); 

 

Custom bridge.json files are not limited to Debug and Release build modes. If you have a custom build mode, for example Staging, just add a bridge.Staging.json file.

IE8 Dropped

IE8 Crossed Logo

As of Bridge 1.12, IE8 support has been dropped. Most JavaScript libraries have already dropped IE8, such as jQuery, React and Angular, as well as Microsoft itself, so we’re not really treading new ground here.

As we’re no longer being held back with legacy IE8 support, several significant performance enhancements within Bridge and an API cleanup were triggered by the removal of several hacks or restrictions. One of the nicest enhancements has been the replacement of inline Bridge.get() calls for static properties with a centralized Object.defineProperty definition. (Issue #1050).

Before officially dropping IE8, we polled the Bridge Community in our forums and GitHub, as well as on Twitter. The poll results were 94% for, and against 6%. The decision was obvious. Goodbye IE8.

New Enum Attribute Options

A recent feature request uncovered a lack of convenient settings in how to manage Enum constants that are emitted by the compiler.

By default Bridge follows JavaScript naming conventions in the compiled output and defaults to lowercase first characters of member names, such as: methods, fields, Enum constants.

The user’s requirement was to preserve the case of emitted Enum constants. There is an existing Name(false) attribute which is an easy solution, but it must be set on each Enum constant. Not a problem if an Enum has only a few options, but totally inconvenient and polluting if there are many.

Bridge 1.12 makes the task easier with introduction of Emit.NamePreserveCase. In the sample below you can see the difference between the old and new notations. ShapeOld and ShapeNew Enums produce the same JavaScript code, but the C# source code of ShapeNew is much cleaner.

Example (Live)

C#
// Prior to Bridge 1.12
public enum ShapeOld
{
    [Name(false)]
    CIRCLE = 1,

    [Name(false)]
    SQUARE = 2,

    [Name(false)]
    RECTANGLE = 3
}

// As of Bridge 1.12
[Enum(Emit.NamePreserveCase)]
public enum ShapeNew
{
    CIRCLE = 1,
    SQUARE = 2,
    RECTANGLE = 3
}

[Ready]
public static void Main()
{
    var c1 = ShapeOld.CIRCLE;
    var m1 = ShapeOld.SQUARE;
    var r1 = ShapeOld.RECTANGLE;

    var c2 = ShapeNew.CIRCLE;
    var m2 = ShapeNew.SQUARE;
    var r2 = ShapeNew.RECTANGLE;
}
 
 
  
JS
Bridge.define(‘Demo.App’, {
    statics: {
        config: {
            init: function () {
                Bridge.ready(this.main);
            }
        },
        main: function () {
            var c1 = Demo.App.ShapeOld.CIRCLE;
            var m1 = Demo.App.ShapeOld.SQUARE;
            var r1 = Demo.App.ShapeOld.RECTANGLE;

            var c2 = Demo.App.ShapeNew.CIRCLE;
            var m2 = Demo.App.ShapeNew.SQUARE;
            var r2 = Demo.App.ShapeNew.RECTANGLE;
        }
    }
});

Bridge.define(‘Demo.App.ShapeNew’, {
    statics: {
        CIRCLE: 1,
        SQUARE: 2,
        RECTANGLE: 3
    },
    $enum: true
});

Bridge.define(‘Demo.App.ShapeOld’, {
    statics: {
        CIRCLE: 1,
        SQUARE: 2,
        RECTANGLE: 3
    },
    $enum: true
}); 

In addition to Emit.NamePreserveCase, we’ve also added Emit.NameLowerCase and Emit.NameUpperCase options. This job was completed in Issue #1059.

5500+ New Unit Tests

13300 Test Assertions

5576 new unit tests has been added to the Bridge Test Project, a 70% increase from the 1.11 release.

The total amount of test assertions is now 13300. The more tests — the higher the quality. We ensure each bug fix and new feature added to Bridge is covered by unit tests, and this massive Unit Test collection will ensure backwards-compatibility as we move forward with our upcoming core compiler improvements.

Since the beginning of Bridge, the project has been under intensive development. Huge features are being added in a continual stream, and we’re regularly improving performance within the Bridge Core. The only guarantee these enhancements do not break something unforeseen is to have excellent unit test coverage.

On the team we have a dedicated member responsible for Continuous Integration and Quality Control. It’s a high priority for us, and helps provide the Bridge Community with a truly professional quality platform.

Live Bridge Enhancements

A couple of annoying bugs were fixed in Live Bridge. The bugs caused various weird issues, such as code samples compiling for some reason in Live Bridge, but not in Visual Studio. The problems were isolated and fixed.

While fixing these bugs we also improved rendering of errors if they’re thrown during compilation. Descriptive messages are now bubbled up to the output window which helps the developer isolate and correct syntax errors.

Now there are two types of error messages in Live Bridge:

C# Source Code Compilation Error Live Sample
C# To JavaScript Compilation Error Live Sample

Thread.Sleep

A basic implementation of Thread.Sleep has been included in Bridge 1.12, mostly to simplify testing of C# code samples that mimic a long running process. Our implementation is probably better described as Thread Blocking, as it doesn’t actually reproduce the same functionality as Thread.Sleep within a C# application.

Example (Live)

C#
var watch = System.Diagnostics.Stopwatch.StartNew();

Console.WriteLine(watch.ElapsedMilliseconds);

Thread.Sleep(2000); // Block for 2 seconds

Console.WriteLine(watch.ElapsedMilliseconds); 
JS
var watch = Bridge.Stopwatch.startNew();

console.log(watch.milliseconds().toString());

Bridge.sleep(2000); // Block for 2 seconds

console.log(watch.milliseconds().toString()); 

The following output was printed to the browsers console:

0
2000 

You shouldn’t be using Thread.Sleep within your app. We’re only including within Bridge to provide C# API compatibility and mocking of the native C# functionality.

Need a deeper understanding of why Thread.Sleep is a bad idea? Read this.

PRO TIP If you need to delay execution of a Method without blocking the thread, in native JavaScript a great option is using the setTimeout function. The following sample reproduces something similar to the Thread.Sleep sample above, but taking advantage of the non-thread blocking SetTimeout Method in C#.

Example (Live)

C#
var watch = Stopwatch.StartNew();

Console.WriteLine(watch.ElapsedMilliseconds);

Console.WriteLine(“Waiting…”);

Global.SetTimeout(() =>
{
    Console.WriteLine(watch.ElapsedMilliseconds);
}, 2000); 
JS
var watch = Bridge.Stopwatch.startNew();

console.log(watch.milliseconds().toString());

console.log(“Waiting…”);

Bridge.global.setTimeout(function () {
    console.log(watch.milliseconds().toString());
}, 2000);
  

The following output was printed to the browsers console:

0
2000 

For more sophisticated API on delaying execution of a Method, please checkout the next section dedicated to the Timer class support.

We give you the tools, but cannot protect you from yourself. If you block the main thread (the only thread) in JavaScript, you’ll lock up the interface. This is bad.

Need asynchronous non-blocking operations? Experiment with SetTimeout, or dive into using the C# async and await actions that are fully supported within Bridge. Read more in our Asynchronous Programming Knowledge Base article, and check out the cool Async sample in Live Bridge.

Timer Support

Issue #1013 triggered an implementation of the System.Threading.Timer class which provides a mechanism for executing a Method at specified intervals.

Important Note: The native .NET System.Threading.Timer class is intended for use as a server-based or service component in a multi-threaded environment. Since JavaScript is a single-threaded environment, the Bridge Timer implementation is limited to a single thread and not intended to mimic multiple threading in any way.

That said, the Timer class is useful in the single-threaded environment. Delayed execution of a Method is a very common requirement and the Timer class provides a convenient and sophisticated API for the Bridge developers. Other than the single-threaded limitation, the Bridge Timer class exactly reproduces the .NET Timer API. The sample below demonstrates the API highlights, with instructions in the comments.

Example (Live)

C#
// We need the Timer reference to have access to it in the callback
public static Timer timer;

// A flag of changing the Timer’s due time and period
public static bool timerChanged;

[Ready]
public static void Main()
{
    // The Timer callback
    TimerCallback callback = (state) =>
    {
        // We passed the stopwatch to the Timer’s constructor
        // So, we can access it within the callback
        Stopwatch stopwatch = state as Stopwatch;
        var seconds = stopwatch.Elapsed.Seconds;

        Console.WriteLine(seconds);

        if (!App.timerChanged && seconds >= 5 && seconds < 10)
        {
            App.timerChanged = true;

            // Changes the Timer’s due time and period
            timer.Change(2000, 2000);
        }

        if (seconds >= 10)
        {
            // Stops and disposes the Timer
            App.timer.Dispose();
            Console.WriteLine(“The Timer has been disposed.”);
        }
    };

    // The stopwatch to monitor the executions of the callback
    var sw = new Stopwatch();

    sw.Start();

    // The amount of time to wait before the first execution of the callback
    var dueTime = 0;

    // The amount of time to wait between subsequent executions of the callback
    var period = 1000;

    // We should keep the reference to the Timer,
    // to be able to access within the callback
    App.timer = new Timer(callback, sw, dueTime, period);
}
 
 
 
 
 
 
 
  
JS
Bridge.define(‘Demo.App’, {
    statics: {
        timer: null,
        timerChanged: false,
        config: {
            init: function () {
                Bridge.ready(this.main);
            }
        },
        main: function () {
            // The Timer callback
            var callback = $_.Demo.App.f1;

            // The stopwatch to monitor the executions of the callback
            var sw = new Bridge.Stopwatch();

            sw.start();

            // The amount of time to wait before the first execution of the callback
            var dueTime = 0;

            // The amount of time to wait between subsequent executions of the callback
            var period = 1000;

            // We should keep the reference to the Timer,
            // to be able to access within the callback
            Demo.App.timer = new Bridge.Threading.Timer(“constructor$1”, callback, sw, dueTime, period);
        }
    }
});

var $_ = {};

Bridge.ns(“Demo.App”, $_)

Bridge.apply($_.Demo.App, {
    f1: function (state) {
        // We passed the stopwatch to the Timer’s constructor
        // So, we can access it within the callback
        var stopwatch = Bridge.as(state, Bridge.Stopwatch);
        var seconds = stopwatch.timeSpan().getSeconds();

        console.log(seconds);

        if (!Demo.App.timerChanged && seconds >= 5 && seconds < 10) {
            Demo.App.timerChanged = true;

            // Changes the Timer’s due time and period
            Demo.App.timer.change(2000, 2000);
        }

        if (seconds >= 10) {
            // Stops and disposes the Timer
            Demo.App.timer.dispose();
            console.log(“The Timer has been disposed.”);
        }
    }
}); 

The sample Timer is initialized to trigger the callback Method every second. After 5 seconds, the interval is reconfigured to 2 seconds. If the Timer works more than 10 seconds it is stopped, then disposed. To monitor the callback Method executions we output the elapsed seconds on each execution proving the sample works as expected. You are welcome to test it Live.

Contributors

We want to extend a huge thanks to the Bridge.NET community for contributing code, ideas and energy to the project. The community is growing every day and we would like to point out a few community contribution highlights from the last development cycle.

A relatively new community member, @Dia6lo, stepped in with three amazing pull requests. HTML5 WebSocket API support has been merged into 1.12, as well as a fix for a couple Window.Location issues. One more pull request with an .NET WebSocket API implementation is slated for the next 1.13 release. @Dia6lo, thank you very much from all the Bridge Community!

One of the earliest adopters and biggest fans of Bridge, Dan Roberts (twitter, GitHub, Bridge forums) has contributed to the Bridge world an epic series of blog posts on writing React JS apps using Bridge: Part 1, Part 2, Part 3. We encourage everybody to learn more about Dan’s Bridge.React and ProductiveRage.Immutable libraries. Unleash the power of .NET and C# for React JS applications.

@michaelcheers, thank you very much for a nice pull request with a draft implementation of the C# Random class. It really helped us to introduce Random class support into Bridge.

Check out this epic bug report by @t-nelis. If @t-nelis or @ProductiveRage report a bug, we snap to attention. They’re always packed full of helpful explanations, implementation details and valued opinions. Thank You!

It’s so great when a community member helps other community member, as our active forum member @Suchiman did recently within this forum thread. Bridge is an open-source platform and we’re excited to see the community grow by building upon the successes of its members. @Suchiman also provided us with several excellent bug reports, suggestions, and helped with testing the fixes.

A big thanks also goes out to @Simon, @kcudnik, @danw, @michaelcheers and many others.

You’re Awesome!!!

Summary

Following the pattern of previous Bridge.NET releases, there was a lot packed into 1.12. Much more than we can document in a single blog post. For a complete list, please review the Closed Issues for details of all fixes and features.

Now get out there and Build Something™.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Markdown and code formatting supported in comments, including use of code formatting shortcodes ([cs], [js], [code], [html])