André Selmanagić

about programming, games and tools

Using TypeScript for Mozilla platform development

Recently I started converting Loomo to TypeScript. Results are currently in the typescript branch.

JS code modules

The most challenging part was the support for Mozilla’s JS code modules, as they don’t fit well with the two module formats TypeScript can generate - CommonJS and AMD.

I created a module (in plain JS) that provides functionality for writing a JS code module using CommonJS syntax (the format TS compiles to).

It does two important things:

  • it gives the module a require function that allows loading of relatively located modules
  • it turns exported objects (exports.foo) into elements of EXPORTED_SYMBOLS
CommonJS.jsm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var EXPORTED_SYMBOLS = ["initCommonJSModule"];

/**
 * Initializes the global object of a Mozilla JS Module to be a CommonJS module
 *    - adds require and exports
 *
 * @param  {Object}   global   Global object of Mozilla JS module
 */
function initCommonJSModule(global){

  // adding CommonJS require
  global.require = function require(path) {
          if (!("__URI__" in global))
            throw Error("require may only be used from a JSM, and its first argument "+
                        "must be that JSM's global object (hint: use this)");
          let uri = global.__URI__;
          let i = uri.lastIndexOf("/");
          var res = {};
          Components.utils.import(uri.substring(0, i+1) + path + ".js", res);
          return res;
      };
  
  // adding the symbols needes for Mozilla JS modules
  global.EXPORTED_SYMBOLS = [];
  
  // Proxy handler used for CommonJS exports that will automatically adjust EXPORTED_SYMBOLS
  var handler = {
      set: function(target, name, value){
          if(global.EXPORTED_SYMBOLS.indexOf(name) === -1)
              global.EXPORTED_SYMBOLS.push(name);
          target[name] = value;
          return true;
      }
  };
  
  // adding CommonJS exports
  global.exports = new Proxy(global, handler);
  global.module = {};
  global.module.exports = global.exports;
}

All you need to do, is to include the JS module in your TypeScript file.

SomeFile.ts
1
2
3
4
5
6
7
8
9
10
// Boilerplate for hacking support JS modules in TypeScript
///<reference path='../../Moz.d.ts' />
Components.utils.import("chrome://path/to/CommonJS.jsm");
initCommonJSModule(eval('this'));

import Bar = module("../Bar");

export class Foo {
  
}

Relative file loading in require uses the __URI__ property every JS code module provides and which is also used in the implementation of XPCOMUtils.importRelative.

There are some things to consider.

You need to provide declarations for Mozilla specific APIs like Components.xxx, which I do provide in MozGlobals.d.ts (which is referenced by Moz.d.ts). You will also have to provide the declaration for initCommonJSModule, otherwise TypeScript won’t compile. Doh!

Also TypeScript does not support using this in global scope, but it is the only way to access the global object of the module and thus needed. I had to hack it using eval(this). I considered using a function which simply returns this, but I think this will be problematic in strict mode.

One big caveat is still there: importing modules from other contexts (like “chrome://browser” or extensions) can’t be done, as require can only import relative to the source file. You will have to use Components.utils.import and provide TypeScript declarations.

Some remarks: Mozilla specifics, ES6/Harmony features and host objects

As I already said, you will have to provide declarations for all Mozilla specific APIs. This includes APIs and functions that will appear with ES6/Harmony, which shouldn’t be too complicated.

Really problematic are ES6/Harmony’s syntactic changes. TypeScript currently does not support let and const. I opened a bug report for it, but I don’t see it happening in the near future. I’m not sure about generators and yield either.

Also, for now, host objects like Array can not be extended.

Conclusion

Apart from some problems here and there and missing features of ES6/Harmony, TypeScript conversion went pretty smoothly.

Added basic TypeScript support to SublimeLinter

I took a look at SublimeLinter and added basic TypeScript support, see this pull request.

Sublime can now show errors from TypeScript files, but it is still quite problematic when working with bigger TypeScript projects, as every lint operation will reparse everything (including referenced files) - thus there is still a lot to be done, but it is a start.

Sublime plugin for automatically resizing the active group

I developed a little Sublime plugin that automatically resizes the active layout group to use up the most space. You can find SublimeResizeActiveGroup on github.

Sublime Plugin for resizing the active layout group when the user switches groups. Resize the groups once as usual using the sliders. Whenever you activate a different group now, the selected group will be resized to have the most space.

Link Roundup (October 2012)

Some interesting links …

Functional Programming

JavaScript

  • Rivertrail
    Mozilla started implementing Intel’s Rivertrail specification

Sublime Text 2

TypeScript

TypeScript is a new statically typed programming language from Microsoft that compiles to pure JavaScript.

Switching to Octopress

This is more a test post than anything else.

Switched from Joomla to Octopress. I will transfer the old pages one by one.

Here are some things that I have learned.

Use 32-bit Python

Rubypython seems to depend on 32-bit Python. I had 64-bit Python installed, which gave me weird errors like

1
'block in ffi_lib': Could not open library (LoadError)

followed by “library not found” or some gibberish unicode characters.

C++11 auto and references

I just started playing around with the auto keyword of the new C++ standard. I assumed a variable declared with auto would have the type of the according expression.

Though consider the following example:

Example of using auto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <vector>

using namespace std;

vector<int> data;

vector<int>& getData()
{
  return data;
}

int main( int argc, const char* argv[] )
{
  data.push_back(1);

  auto data2 = getData();
  data.push_back(2);
}

Looking at the variables in the debugger, you see that data contains the values 1 and 2, whereas data2 only contains 1, thus data2 is not the same object as data. I assumed data2 was of type vector<int>& (the return type of getData), though it obviously just is vector<int>.

Declaring it as

1
auto& data2 = getData();

works though. data2 is now a reference to data.

It makes sense, the standard defines it that way. Otherwise, how could you create a copy of the return value? Still it makes using auto a little more confusing …

Coolest Javascript Features - Part 1: let, logical operators, destructuring, labels

This is going to be an unsorted list about the nicest Javascript features. Look closely at the title: Javascript! Thus this list may contain features only present in the Mozilla-implementation and not standardized in ECMAScript! I am going to update this list every now and then.

Block scoping with let

Support: ECMAScript Harmony, Javascript 1.7, Mozilla-only (as of September 2010)

Finally lexical scoping / block scoping in Javascript. Declare variables with let instead of var. May come in handy when you are creating closures in a loop.

Logical operators && and ||

Support: should work in all modern browsers

Logical operators are nothing spectecular in programming languages. But Javascript handles them a little different. && and || don’t necessarily return boolean values, but can also return other types (objects, etc). Here are some examples from Angus Croll’s JavaScript blog:

1
2
3
4
5
//invoke callback if there is one
callback && callback();

//delay by argument or 20
delayBy(delay || 20);

But you should be careful, when using this with values that evaluate to false. See the following example:

1
2
3
//delay by argument or 20
var delay = 0;
delayBy(delay || 20);

0 will evaluate to false and thus delayBy will be called with 20.

|| is often used for setting default-values for functions parameters (well, harmony may contain that natively), but as shown that may not always lead to the expected behaviour, thus I wouldn’t recommend setting default values that way.

Destructuring

Support: ECMAScript Harmony, Javascript 1.7, Mozilla-only (as of September 2010)

Don’t confuse this with destructors known from languages like Java or C++. Destructuring assignment makes it possible to extract data from arrays or objects using a syntax that mirrors the construction of array and object literals

This allows things like swapping variables without a temporary variable, multiple return values and more. Here is an example for multiple return values from MDN:

1
2
3
4
5
6
7
function f() {
    return [1, 2];
}

var a, b;
[a, b] = f();
document.write ("A is " + a + " B is " + b + "\n");

The function simply returns an array, but the destructuring syntax allows to split this into multiple variables.

Labeling statements

Support: should work in all modern browsers

By labeling statements you can give them an identifier that you can refer to from somewhere else. That may come in handy when using loops with continue or break.

1
2
3
4
5
6
7
8
9
10
loopX:
for(var x = 0; x < 5; ++x)
{
    for(var y = 0; y < 5; ++y)
    {
        // ... do something
        if(...)
            break loopX;
    }
}

Compiling a NPAPI-Plugin from the Mozilla source-code

Some days ago I tried to compile the basic NPAPI-Plugin from the Mozilla codebase and of course ran into some problems that could easily be solved, though finding the answers wasn’t that easy.

Here’s the first one, when compiling:

1
c:\program files\microsoft sdks\windows\v7.0\include\winnt.h(6361): error C2146: syntax error : missing ';' before identifier 'ContextRecord'

Huh? winnt.h. Shouldn’t change something in there. I googled a little and someone knew the problem. He suggested to alter npplat.h and move some includes after the include of windows.h. Though this will work, it doesn’t actually sound like something you’re supposed to do. So I digged further and checked out the Gecko-Win32-Plugin-Samples-Package that you can get at MDC. The basic plugin in there compiled fine, which confused me a little, so I diffed the project-files and there I found the solution: The project-file from comm-central missed the preprocessor-tag _X86_.

I used some XPCOM-Components in my plugin, which lead to another compile-error:

1
nsidomnode.h(94) : error C2059: syntax error : 'constant'

I found someone in the net, having the same problem. Seems there is a #define GetNextSibling GetWindow somewhere in windows.h, which collides with the method-declaration of GetNextSibling in nsidomnode.h. So I undef’ed it after including windows.h and got the problem solved.

[Tutorial] Get rid of XPCOM syntax with JS Modules and Components.Constructor

In case you dislike the syntax of creating XPCOM components, you can use Components.Constructor to achieve a more Javascript like syntax for object-creation. Combine this with JS modules and your constructors can be accessed from everywhere.

MozXPCOM.jsm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var EXPORTED_SYMBOLS = ["MOZ"];

const Cc = Components.classes;
const Ci = Components.interfaces;
const CCtor = Components.Constructor;

var MOZ =
{
  CON: {}
};

MOZ.CON.nsLocalFile     = "@mozilla.org/file/local;1";
MOZ.LocalFile           = CCtor(MOZ.CON.nsLocalFile, Ci.nsILocalFile);

MOZ.CON.nsMutableArray  = "@mozilla.org/array;1";
MOZ.MutableArray        = CCtor(MOZ.CON.nsMutableArray, Ci.nsIMutableArray);

Usage

1
2
3
4
5
Components.utils.import("resource://myext/MozXPCOM.jsm");

// ...

var anArray = new MOZ.MutableArray();

I prefer to put all constructors in a MOZ-object, so I don’t have to export them all.