Wiki copymirror

This is a mirror of the wiki at pascal.chat. Editing, history and sign-in are there.

Pas2js Transpiler

About pas2js

pas2js is a compiler/transpiler to translate programs written in Pascal (subset of Delphi/ObjFPC syntax) to JavaScript.
The goal is to use strong typing, while still be able to use low level whenever you choose.
The compiled Pascal functions can be used in DOM events or called by JavaScript.
pas2js is written completely in FPC, runs on many platforms like Windows, Mac and Linux and more. It is built modular consisting of the following parts:

Each part is tested separately and is used by other FPC tools as well. For example the scanner and parser are used by fpdoc too. Thus they are tested and extended by other programmers, reducing greatly the work for developing pas2js. Consistency is kept by several test suites, containing thousands of tests.

Note: The modular structure allows to compile any parts or the whole compiler into an IDE addon (not yet started).

Command line parameters

Most parameters work the same as their FPC equivalent. pas2js has some options of its own (see -J options).

pascal
Options:
Put + after a boolean switch option to enable it, - to disable it
  @<x>   : Read compiler options from file <x> in addition to the default
            pas2js.cfg
  -B     : Rebuild all
  -d<x>  : Defines the symbol <x>. Optional: -d<x>:=<value>
  -i<x>  : Write information and halt. <x> is a combination of the following:
    -iD  : Write compiler date
    -iSO : Write compiler OS
    -iSP : Write compiler host processor
    -iTO : Write target platform
    -iTP : Write target processor
    -iV  : Write short compiler version
    -iW  : Write full compiler version
    -ic  : Write list of supported JS processors usable by -P<x>
    -im  : Write list of supported modeswitches usable by -M<x>
    -io  : Write list of supported optimizations usable by -Oo<x>
    -it  : Write list of supported targets usable by -T<x>
    -iJ  : Write list of supported JavaScript identifiers -JoRTL-<x>
  -C<x>  : Code generation options. <x> is a combination of the following
            letters:
    o    : Overflow checking of integer operations
    r    : Range checking
    R    : Object checks. Verify method calls and object type casts.
  -F...   Set file names and paths:
   -Fe<x>: Redirect output to file <x>. UTF-8 encoded.
   -FE<x>: Set main output path to <x>
   -Fi<x>: Add <x> to include paths
   -FN<x>: add <x> to namespaces. Namespaces with trailing - are removed.
            Delphi calls this flag "unit scope names".
   -Fu<x>: Add <x> to unit paths
   -FU<x>: Set unit output path to <x>
  -I<x>  : Add <x> to include paths, same as -Fi
  -J...  Extra options of pas2js
   -Ja<x>: Append JS file <x> to main JS file. E.g. -Jamap.js. Can be given
            multiple times. To remove a file name append a minus, e.g.
            -Jamap.js-.
   -Jc   : Write all JavaScript concatenated into the output file
   -Je<x>: Encode messages as <x>.
     -Jeconsole: Console codepage. This is the default.
     -Jesystem : System codepage. On non Windows console and system are the
            same.
     -Jeutf-8  : Unicode UTF-8. Default when using -Fe.
     -JeJSON   : Output compiler messages as JSON. Logo etc are outputted
            as-is.
   -Ji<x>: Insert JS file <x> into main JS file. E.g. -Jirtl.js. Can be given
            multiple times. To remove a file name append a minus, e.g.
            -Jirtl.js-.
   -Jl   : lower case identifiers
   -Jm   : generate source maps
     -Jmsourceroot=<x>: use x as "sourceRoot", prefix URL for source file
            names.
     -Jmbasedir=<x>: write source file names relative to directory x, default
            is map file folder.
     -Jminclude: include Pascal sources in source map.
     -Jmabsolute: store absolute filenames, not relative.
     -Jmxssiheader: start source map with XSSI protection )]}', default.
     -Jm-: disable generating source maps
   -Jo<x>: Enable or disable extra option. The x is case insensitive:
     -JoSearchLikeFPC: search source files like FPC, default: search case
            insensitive.
     -JoUseStrict: add "use strict" to modules, default.
     -JoCheckVersion-: do not add rtl version check, default.
     -JoCheckVersion=main: insert rtl version check into main.
     -JoCheckVersion=system: insert rtl version check into system unit init.
     -JoCheckVersion=unit: insert rtl version check into every unit init.
     -JoRTL-<y>=<z>: set RTL identifier y to value z. See -iJ.
   -Jr<x> Control writing of resource string file
     -Jrnone: Do not write resource string file
     -Jrunit: Write resource string file per unit with all resource strings
     -Jrprogram: Write resource string file per program with all used
            resource strings in program
   -JR<x> Control writing of linked resources
     -JRnone: Skip resource directives
     -JRjs: Write resources in Javascript structure
     -JRhtml[=filename] : Write resources as preload links in HTML file 
            (default is projectfile-res.html)
   -Jpcmd<command>: Run postprocessor. For each generated js execute command
            passing the js as stdin and read the new js from stdout. This
            option can be added multiple times to call several postprocessors
            in succession.
   -Ju<x>: Add <x> to foreign unit paths. Foreign units are not compiled.
   -JU: This pas2js does not support PCU files
  -l     : Write logo
  -M<x>  : Set language mode or enable/disable a modeswitch
    -MDelphi: Delphi 7 compatibility mode
    -MObjFPC: FPC's Object Pascal compatibility mode (default)
    -M<x>  : enable or disable modeswitch, see option -im
    Each mode (as listed above) enables its default set of modeswitches.
    Other modeswitches are disabled and need to be enabled one by another.
  -NS<x> : obsolete: add <x> to namespaces. Same as -FN<x>
  -n     : Do not read the default config files
  -o<x>  : Change main JavaScript file to <x>, "." means stdout
  -O<x>  : Optimizations:
    -O-  : Disable optimizations
    -O1  : Level 1 optimizations (quick and debugger friendly)
    -O2  : Level 2 optimizations (Level 1 + not debugger friendly)
    -Oo<x>: Enable or disable optimization. The x is case insensitive:
      -OoEnumNumbers[-]: write enum value as number instead of name. Default
            in -O1.
      -OoRemoveNotUsedPrivates[-]: Default is enabled
      -OoRemoveNotUsedDeclarations[-]: Default enabled for programs with -Jc
      -OoRemoveNotUsedPublished[-] : Default is disabled
      -OoShortRefGlobals[-]: Insert JS local var for types, modules and
            static functions. Default enabled in -O2
  -P<x>  : Set target processor. Case insensitive:
    -Pecmascript5: default
    -Pecmascript6
  -S<x>  : Syntax options. <x> is a combination of the following letters:
    2    : Same as -Mobjfpc (default)
    a    : Turn on assertions
    c    : Support operators like C (*=,+=,/= and -=)
    d    : Same as -Mdelphi
    j    : Allows typed constants to be writeable (default)
    m    : Enables macro replacements
  -SI<x>  : Set interface style to <x>
    -SIcom  : COM, reference counted interface (default)
    -SIcorba: CORBA interface
  -T<x>  : Set target platform
    -Tbrowser: default
    -Tnodejs : add pas.run(), includes -Jc
    -Telectron: experimental
    -Tmodule : add pas.run(), includes -Jc
  -u<x>  : Undefines the symbol <x>
  -v<x>  : Be verbose. <x> is a combination of the following letters:
    e    : Show errors (default)
    w    : Show warnings
    n    : Show notes
    h    : Show hints
    i    : Show info
    l    : Show line numbers, needs -vi
    a    : Show everything
    0    : Show nothing (except errors)
    b    : Show file names with full path
    c    : Show conditionals
    t    : Show tried/used files
    d    : Show debug notes and info, enables -vni
    q    : Show message numbers
    x    : Show used tools
    v    : Write pas2jsdebug.log with lots of debugging info
    z    : Write messages to stderr, -o. still uses stdout.
  -vm<x>,<y>: Do not show messages numbered <x> and <y>.
  -?     : Show this help
  -h     : Show this help
pascal
Environment variable PAS2JS_OPTS is parsed after default config and before
            command line parameters.

Delphi and ObjFPC mode

Delphi mode

ObjFPC mode

This the default mode of pas2js and is generally more strict than the Delphi mode, and allows some more operations.

Translating modules

A Pascal Program is translated into the following JavaScript structure:

Pascal

JavaScript Structure, not code!

pascal
Program &lt;unitname&gt;;
Implementation
  [implementation section]
Begin
  [main code]
End.
pascal
pas.&lt;program&gt;={
  [implementation section],
  $main: function() {
    [main code]
  }
};

A Pascal Unit is translated into the following JavaScript structure:

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>  [interface section]</code><br /> <code>Implementation</code><br /> <code>  [implementation section]</code><br /> <code>Initialization</code><br /> <code>  [initialization section]</code><br /> <code>End.</code></td>

<code>  [interface section],</code><br /> <code>  $impl: {</code><br /> <code>    [implementation section],</code><br /> <code>  },</code><br /> <code>  $init: function() {</code><br /> <code>    [initialization section]</code><br /> <code>  }</code><br /> <code>};</code></td>

</tbody>

Note: The finalization section is not supported by pas2js.\

To create and initialize the units in topological order the compiler translates an Unit to the following JavaScript code:

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>  [interface section]</code><br /> <code>Implementation</code><br /> <code>  [implementation section]</code><br /> <code>Initialization</code><br /> <code>  [initialization section]</code><br /> <code>End.</code></td>

<code>  ['system',...other used units of the interface section...],</code><br /> <code>  function(){</code><br /> <code>    var $mod = this;</code><br /> <code>    var $impl = $mod.$impl;</code><br /> <code>    [interface section]</code><br /> <code>    $mod.$implcode = function(){</code><br /> <code>      [implementation section]</code><br /> <code>    }</code><br /> <code>    $mod.$init = function(){</code><br /> <code>      [initialization section]</code><br /> <code>    };</code><br /> <code>  },</code><br /> <code>  [...used units of the implementation section]</code><br /> <code>  };</code></td>

</tbody>

Here is a more detailed example to make it more clear:

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Uses Sysutils;</code><br /> <code>var</code><br /> <code>  dIntf: double;</code><br /> <code>  sIntf: string = 'abc';</code><br /> <code>procedure MyIntfProc;</code><br /> <code>Implementation</code><br /> <code>Uses Classes;</code><br /> <code>Var dImpl:double;</code><br /> <code>Procedure MyIntfProc;</code><br /> <code>Begin</code><br /> <code>  dImpl:=dIntf;</code><br /> <code>End;</code><br /> <code>Procedure MyImplProc;</code><br /> <code>Begin</code><br /> <code>  dImpl:=dIntf;</code><br /> <code>End;</code><br /> <code>Initialization</code><br /> <code>End.</code></td>

<code>["System","SysUtils"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  var $impl = $mod.$impl;</code><br /> <code>  this.dIntf = 0.0;</code><br /> <code>  this.sIntf = "abc";</code><br /> <code>  this.MyIntfProc = function(){</code><br /> <code>    $impl.dImpl = $mod.dIntf;</code><br /> <code>  };</code><br /> <code>  $mod.$implcode = function(){</code><br /> <code>    $impl.dImpl = 0.0;</code><br /> <code>    $impl.MyImplProc = function() {</code><br /> <code>      $impl.dImpl = $mod.dIntf;</code><br /> <code>    };</code><br /> <code>  }</code><br /> <code>  $mod.$init = function() {</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>["Classes"]);</code></td>

</tbody>

Notes:

Translating variables

Variables are converted without type, because JavaScript lacks a clear type. They are however always initialized, which helps JavaScript engines to optimize.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Uses Classes,Forms;</code><br /> <code>const</code><br /> <code>  c1:integer=3;</code><br /> <code>  c2 = 'abc';</code><br /> <code>  c3 = 234;</code><br /> <code>  c4 = 12.45;</code><br /> <code>  c5 = nil;</code><br /> <code>var</code><br /> <code>  v1:string;</code><br /> <code>  v2,v3:double;</code><br /> <code>  v4:byte=0;</code><br /> <code>  v5:TForm;</code><br /> <code>  v6:TIdentMapEntry;</code><br /> <code>  v7:string='abc';</code><br /> <code>  v8:char='c';</code><br /> <code>  v9:array of byte;</code><br /> <code>Implementation</code><br /> <code>End.</code></td>

<code>["System","Classes","Forms"],</code><br /> <code>function(){</code><br /> <code>  this.c1 = 3;</code><br /> <code>  this.c2 = "abc";</code><br /> <code>  this.c3 = 234;</code><br /> <code>  this.c4 = 12.45;</code><br /> <code>  this.c5 = null;</code><br /> <code>  this.v1 = "";</code><br /> <code>  this.v2 = 0.0;</code><br /> <code>  this.v3 = 0.0;</code><br /> <code>  this.v4 = 0;</code><br /> <code>  this.v5 = null;</code><br /> <code>  this.v6 = new pas.Classes.TIdentMapEntry();</code><br /> <code>  this.v7 = "abc";</code><br /> <code>  this.v8 = "c";</code><br /> <code>  this.v9 = [];</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating string

Strings are translated to JavaScript strings. They are initialized with "" and are never null.
There are no ShortString, AnsiString or RawByteString. Unicodestring and Widestring are alias of String.
JavaScript strings are immutable, which means that changing a single character in a string, creates a new string. So a s[2]:='c'; is a slow operation in pas2js compared to Delphi/FPC.
Although pas2js creates .js files encoded as UTF-8 with BOM, JavaScript strings are UTF-16 at runtime. Keep in mind that one UTF-16 codepoint can need two char, and a visible glyph can need several codepoints. Same as in Delphi.

Translating resourcestrings

Resourcestrings are translated to JS objects with original (org) and current value.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>resourcestring</code><br /> <code>  rsCompiler = 'pas2js';</code><br /> <code>var</code><br /> <code>  s:string;</code><br /> <code>Implementation</code><br /> <code>initialization</code><br /> <code>  s:=rsCompiler;</code><br /> <code>End.</code></td>

<code>  var $mod = this;</code><br /> <code>  this.s = "";</code><br /> <code>  $mod.$resourcestrings = {rsCompiler: {org: "pas2js"}};</code><br /> <code>  $mod.$init = function () {</code><br /> <code>    $mod.s = rtl.getResStr(pas.test1,"rsCompiler");</code><br /> <code>  };</code><br /> <code>});</code></td>

</tbody>

Translating currency

Currency in Delphi/FPC is an int64 with a factor of 10000. This is translated to a double with factor 10000 and truncated.

Translating Types

JavaScript type design has no declarative form, except for object types (so-called prototypes). That's why all the derivatives from simple Pascal types can not be translated. The compiler ensures type safety at compile time though, which is a big plus for using Pascal.
Complex Pascal types (classes, records, or arrays) are translated into JavaScript objects or arrays respectively.\

Translating pointer

A pointer is translated to a JS reference. It can be assigned a class, a class instance, a class-of, an array, a procedure var, a method var, a @proc address, a @method address, or a pointer of record. There is no pointer arithmetic, i.e. no p+1, and no typed pointers, except for pointer of record. You can find out its type using the functions isArray, isClass, isClassRef, isCallback, etc of unit JS.

Translating record type

A record is translated to a JavaScript object.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Type</code><br /> <code>  TMyRecord = Record</code><br /> <code>    i: integer;</code><br /> <code>    s: string;</code><br /> <code>    d: TDateTime;</code><br /> <code>  End;</code><br /> <code>Var</code><br /> <code>  r, s: TMyRecord;</code><br /> <code>Implementation</code><br /> <code>Initialization</code><br /> <code>  r.i := 123;</code><br /> <code>  r:=s;</code><br /> <code>  if r=s then ;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  rtl.recNewT($mod, "TMyRecord", function() {</code><br /> <code>    this.i = 0;</code><br /> <code>    this.s = "";</code><br /> <code>    this.d = 0.0;</code><br /> <code>    this.$eq = function (b) {</code><br /> <code>      return (this.i == b.i) &&</code><br /> <code>         (this.s == b.i) && (this.d == b.d);</code><br /> <code>    };</code><br /> <code>    this.$assign = function (s) {</code><br /> <code>      this.i = s.i;</code><br /> <code>      this.s = s.s;</code><br /> <code>      this.d = s.d;</code><br /> <code>      return this;</code><br /> <code>    };</code><br /> <code>  };</code><br /> <code>  this.r = this.TMyRecord.$new();</code><br /> <code>  $mod.$init = function() {</code><br /> <code>    $mod.r.i=123;</code><br /> <code>    $mod.r.$assign($mod.s);</code><br /> <code>    if ($mod.r.$eq($mod.s)) ;</code><br /> <code>  },</code><br /> <code>},</code><br /> <code>[]);</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.TMyRecord = function(s) {</code><br /> <code>    if (s){</code><br /> <code>      this.i = s.i;</code><br /> <code>      this.s = s.s;</code><br /> <code>      this.d = s.d;</code><br /> <code>    } else {</code><br /> <code>      this.i = 0;</code><br /> <code>      this.s = "";</code><br /> <code>      this.d = 0.0;</code><br /> <code>    };</code><br /> <code>    this.$equal = function (b) {</code><br /> <code>      return (this.i == b.i) &&</code><br /> <code>        (this.s == b.i) && (this.d == b.d);</code><br /> <code>    };</code><br /> <code>  };</code><br /> <code>  this.r = new this.TMyRecord();</code><br /> <code>  $mod.$init = function() {</code><br /> <code>    $mod.r.i=123;</code><br /> <code>    $mod.r = new $mod.TMyRecord($mod.s);</code><br /> <code>    if ($mod.r.$equal($mod.s)) ;</code><br /> <code>  },</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Translating generics

Generics are not translated, their specializations are.

Translating functions

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Function DoubleIt(n: integer): integer;</code><br /> <code>Implementation</code><br /> <code>Function DoubleIt(n: integer): integer;</code><br /> <code>Begin</code><br /> <code>  Result:=2*n;</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  this.DoubleIt = function(n){</code><br /> <code>    Result = 0;</code><br /> <code>    Result = 2*n;</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating passing a parameter by reference

JavaScript lacks passing by reference. Instead a temporary object is created with a get and set function. That means changes within the procedure are immediately visible outside, compatible with Pascal.

<thead>

</thead> <tbody>

<code>Procedure DoubleIt(var n: integer);</code><br /> <code>Begin</code><br /> <code>  n:=2*n;</code><br /> <code>End;</code><br /> <code>Function Doubling(n: integer): integer;</code><br /> <code>Begin</code><br /> <code>  DoubleIt(n);</code><br /> <code>  Result:=n;</code><br /> <code>End;</code><br /> <code>Var</code><br /> <code>  i: integer = 7;</code><br /> <code>Begin</code><br /> <code>  Doubling(i);</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.i = 7;</code><br /> <code>  this.DoubleIt = function(n){</code><br /> <code>    n.set(2*n.get());</code><br /> <code>  };</code><br /> <code>  this.Doubling = function(n){</code><br /> <code>    var Result = 0;</code><br /> <code>    DoubleIt({</code><br /> <code>      get:function(){</code><br /> <code>        return n</code><br /> <code>      },</code><br /> <code>      set:function(v){</code><br /> <code>        n=v;</code><br /> <code>      }</code><br /> <code>    });</code><br /> <code>    Result = n;</code><br /> <code>    return n;</code><br /> <code>  };</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    Doubling($mod.i);</code><br /> <code>  }</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

When the passed value is from another context, the context is passed too:

<thead>

</thead> <tbody>

<code>Procedure DoubleIt(var n: integer);</code><br /> <code>Begin</code><br /> <code>  n:=2*n;</code><br /> <code>End;</code><br /> <code>Var</code><br /> <code>  i: integer = 7;</code><br /> <code>Begin</code><br /> <code>  DoubleIt(i);</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.i = 7;</code><br /> <code>  this.DoubleIt = function(n){</code><br /> <code>    n.set(2*n.get());</code><br /> <code>  };</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    DoubleIt({</code><br /> <code>        p:$mod,</code><br /> <code>        get:function(){</code><br /> <code>          return this.p.i</code><br /> <code>        },</code><br /> <code>        set:function(v){</code><br /> <code>          this.p.i=v;</code><br /> <code>        }</code><br /> <code>      });</code><br /> <code>  }</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating nested functions

A nested function is translated to a local variable.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Function SumNNumbers(n, Adder: integer): integer;</code><br /> <code>Implementation</code><br /> <code>Function SumNNumbers(n, Adder: integer): integer;</code><br />

<code>  Function Add(k: integer): integer;</code><br /> <code>  Begin</code><br /> <code>    if k=1 then</code><br /> <code>      Result:=1</code><br /> <code>    else</code><br /> <code>      Result:=Add(k-1)+Adder;</code><br /> <code>  End;</code><br />

<code>Begin</code><br /> <code>  Result:=Add(n);</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  this.DoubleIt = function(n,Adder){</code><br /> <code>    Result = 0;</code><br /> <code>    var Add = function(k) {</code><br /> <code>      Result = 0;</code><br /> <code>      if (k==1) {</code><br /> <code>        Result = 1;</code><br /> <code>      } else {</code><br /> <code>        Result = Add(k-1)+Adder;</code><br /> <code>      }</code><br /> <code>      return Result;</code><br /> <code>    };</code><br /> <code>    Result = Add(n);</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Note: You can assign a nested procedure to a procedure variable. A nested procedure of a method can be assigned to a method variable.
JavaScript preserves the current local scope, including references to the local variables of parent functions. Local types and constants belong to the unit scope (singleton).
When a method has nested functions, the compiler adds a local var Self.

Translating for-loops

The JavaScript for-loop executes the end expression every iteration, while Pascal only executes it once. Therefore a local variable is introduced. If the loop is not entered at all, the variable is not touched. If the loop was entered the variable contanis the last value.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Function SumNNumbers(n: integer): integer;</code><br /> <code>Implementation</code><br /> <code>Function SumNNumbers(n: integer): integer;</code><br /> <code>Var</code><br /> <code>  i, j: integer;</code><br /> <code>Begin</code><br /> <code>  j:=0;</code><br /> <code>  For i:=1 To n Do</code><br /> <code>  Begin</code><br /> <code>    j:=j+i;</code><br /> <code>  End;</code><br /> <code>  if i<1 then j:=1;</code><br /> <code>  Result:=j;</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  this.SumNNumbers=function(n){</code><br /> <code>    Result = 0;</code><br /> <code>    j = 0;</code><br /> <code>    for (var $l1 = 1, $le2 = n; $l1 <= $le2; $l1++) {</code><br /> <code>      i = $l1;</code><br /> <code>      j = j + i;</code><br /> <code>    };</code><br /> <code>    if (i<1) j=1;</code><br /> <code>    Result = j;</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Note: The after-loop decrement is only added if i is read after the loop.\

Translating repeat..until

The repeat..until is translated to a do{}while().

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Function SumNNumbers(n: integer): integer;</code><br /> <code>Implementation</code><br /> <code>Function SumNNumbers(n: integer): integer;</code><br /> <code>Var</code><br /> <code>  i, j: integer;</code><br /> <code>Begin</code><br /> <code>  j:=0;</code><br /> <code>  i:=0;</code><br /> <code>  Repeat</code><br /> <code>    i:=i+1;</code><br /> <code>    j:=j+i;</code><br /> <code>  Until i>=n;</code><br /> <code>  Result:=j;</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  this.SumNNumbers=function(n){</code><br /> <code>    Result = 0;</code><br /> <code>    j = 0;</code><br /> <code>    i = 0;</code><br /> <code>    do{</code><br /> <code>      i = (i + 1);</code><br /> <code>      j = (j + i);</code><br /> <code>    } while (!(i>=n));</code><br /> <code>    Result = j;</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Translating while..do

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Function SumNNumbers(n: integer): integer;</code><br /> <code>Implementation</code><br /> <code>Function SumNNumbers(n: integer): integer;</code><br /> <code>Var</code><br /> <code>  i, j: integer;</code><br /> <code>Begin</code><br /> <code>  j:=0;</code><br /> <code>  i:=0;</code><br /> <code>  While i<n Do Begin</code><br /> <code>    i:=i+1;</code><br /> <code>    j:=j+i;</code><br /> <code>  End;</code><br /> <code>  Result:=j;</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  this.SumNNumbers=function(n){</code><br /> <code>    var Result = 0;</code><br /> <code>    var j = 0;</code><br /> <code>    var i = 0;</code><br /> <code>    while(i<n){</code><br /> <code>      i = (i + 1);</code><br /> <code>      j = (j + i);</code><br /> <code>    };</code><br /> <code>    Result = j;</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Translating case..do

Although JavaScript has something similar in form of the "switch" statement, it lacks ranges and is on current JS engines often slower than "if-else". Therefore a case..of is translated to if..else.

<thead>

</thead> <tbody>

<code>Var</code><br /> <code>  i: integer;</code><br /> <code>Begin</code><br /> <code>  case i of</code><br /> <code>    1: ;</code><br /> <code>    2: i:=3;</code><br /> <code>  else</code><br /> <code>    i:=4;</code><br /> <code>  end;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.i = 0;</code><br /> <code>  $mod.$main=function(n){</code><br /> <code>    $tmp1 = $mod.i;</code><br /> <code>    if ($tmp1 == 1){</code><br /> <code>    } else if ($tmp1 == 2) {</code><br /> <code>      i=3;</code><br /> <code>    } else {</code><br /> <code>      i=4;</code><br /> <code>    }</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Translating with..do

JavaScript has a with, but it is slow and deprecated. Instead a temporary variable is used:

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>type</code><br /> <code>  TClassA = class</code><br /> <code>    i: integer;</code><br /> <code>  end;</code><br />

<code>procedure DoIt;</code><br />

<code>Implementation</code><br />

<code>procedure DoIt;</code><br /> <code>begin</code><br /> <code>  with TClassA.Create do</code><br /> <code>    i:=3;</code><br /> <code>end;</code><br />

<code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  rtl.createClass($mod, "TClassA", pas.System.TObject, function () {</code><br /> <code>    this.$init = function () {</code><br /> <code>      this.i = 0;</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  this.DoIt = function(){</code><br /> <code>    var $with1 = $mod.TClassA.$create("Create");</code><br /> <code>    $with1.i = 3;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Note: If the with-expression is already a local variable no new variable is created. This is Delphi/FPC compatible.

Translating enums

Enum values are translated to numbers. The enum type is translated to an object containing a mapping from name to number and number to name.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>type</code><br /> <code>  TMyEnum = (</code><br /> <code>    Red,</code><br /> <code>    Green,</code><br /> <code>    Blue);</code><br /> <code>var</code><br /> <code>  e: TMyEnum = Blue;</code><br />

<code>procedure DoIt;</code><br />

<code>Implementation</code><br />

<code>procedure DoIt;</code><br /> <code>begin</code><br /> <code>  e := Green;</code><br /> <code>end;</code><br />

<code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.TMyEnum = {</code><br /> <code>    "0":"Red",</code><br /> <code>    Red:0,</code><br /> <code>    "1":"Green",</code><br /> <code>    Green:1,</code><br /> <code>    "2":"Blue",</code><br /> <code>    Blue:2</code><br /> <code>    };</code><br /> <code>  this.e = $mod.TMyEnum.Blue;</code><br /> <code>  this.DoIt = function(){</code><br /> <code>    $mod.e = $mod.TMyEnum.Green;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Translating sets

A set s is translated to a JavaScript object, where for each included enum holds s.enumvalue==true. This allows arbitrary large sets and the in operator is fast.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>type</code><br /> <code>  TColor = (Red, Green, Blue);</code><br /> <code>  TColors = set of TColor;</code><br />

<code>procedure DoIt;</code><br />

<code>Implementation</code><br />

<code>procedure DoIt;</code><br /> <code>var</code><br /> <code>  c: TColor;</code><br /> <code>  S, T: TColors;</code><br /> <code>  b: boolean;</code><br /> <code>begin</code><br /> <code>  S:=T;</code><br /> <code>  b:=Red in S;</code><br /> <code>  Include(S,Blue);</code><br /> <code>  Exclude(S,Blue);</code><br /> <code>  S:=S+T;</code><br /> <code>  S:=S-[Red,c];</code><br /> <code>  b:=c in [Red..Blue];</code><br /> <code>end;</code><br />

<code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.TColor = {</code><br /> <code>    "0":"Red",</code><br /> <code>    Red:0,</code><br /> <code>    "1":"Green",</code><br /> <code>    Green:1,</code><br /> <code>    "2":"Blue",</code><br /> <code>    Blue:2</code><br /> <code>    };</code><br /> <code>  $mod.DoIt = function(){</code><br /> <code>    var c = 0;</code><br /> <code>    var S = {};</code><br /> <code>    var T = {};</code><br /> <code>    var b = false;</code><br /> <code>    S = rtl.refSet(T);</code><br /> <code>    b = $mod.TColor.Red in S;</code><br /> <code>    S = rtl.includeSet(S,$mod.TColor.Blue);</code><br /> <code>    S = rtl.excludeSet(S,$mod.TColor.Blue);</code><br /> <code>    S = rtl.unionSet(S,T);</code><br /> <code>    S = rtl.diffSet(S,rtl.createSet($mod.TColor.Red,c));</code><br /> <code>    b = c in rtl.createSet(null,$mod.TColor.Red,$mod.TColor.Blue);</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Translating array type

All arrays are translated into JavaScript arrays.
Contrary to Delphi/FPC dynamic arrays are not reference counted and do not copy on write. That means if you pass an array to a procedure and change an element, the original array is changed.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Type</code><br /> <code>  TIntArr = Array of integer;</code><br /> <code>  TObjArr = Array of TObject;</code><br /> <code>  TRec = record c: char; end;</code><br /> <code>  TRecArr = Array of TRec;</code><br /> <code>Procedure Test;</code><br /> <code>Implementation</code><br /> <code>Procedure Test;</code><br /> <code>Var</code><br /> <code>  IntArr: TIntArr = (1,2,3);</code><br /> <code>  ObjArr: TObjArr;</code><br /> <code>  RecArr: TRecArr;</code><br /> <code>Begin</code><br /> <code>  IntArr:=nil;</code><br /> <code>  SetLength(IntArr,4);</code><br /> <code>  IntArr[2]:=2;</code><br /> <code>  IntArr[1]:=length(IntArr);</code><br /> <code>  SetLength(ObjArr,5);</code><br /> <code>  SetLength(RecArr,2,TRec);</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.Test = function(){</code><br /> <code>    this.TRec = function(s){</code><br /> <code>      if (s){</code><br /> <code>        this.c = s.c;</code><br /> <code>      } else {</code><br /> <code>        this.c = "";</code><br /> <code>      };</code><br /> <code>      this.$equal = function(b){</code><br /> <code>        return (this.c == b.c);</code><br /> <code>      };</code><br /> <code>    };</code><br /> <code>    this.IntArr = [1,2,3];</code><br /> <code>    this.ObjArr = [];</code><br /> <code>    this.RecArr = [];</code><br /> <code>    this.Test = function(){</code><br /> <code>      $mod.IntArr = [];</code><br /> <code>      rtl.arraySetLength($mod.IntArr,4,0);</code><br /> <code>      $mod.IntArr[2] = 2;</code><br /> <code>      $mod.IntArr[1] = $mod.IntArr.length;</code><br /> <code>      rtl.setArrayLength($mod.ObjArr,5,null);</code><br /> <code>      rtl.setArrayLength($mod.RecArr,2,$mod.TRec);</code><br /> <code>    }</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating class type

Classes are implemented using Object.create and some rtl magic.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Type</code><br /> <code>  TClassA = Class(TObject)</code><br /> <code>  Public</code><br /> <code>    i: integer;</code><br /> <code>    Procedure Add(a: integer);</code><br /> <code>  End;</code><br /> <code>var</code><br /> <code>  ObjA: TClassA;</code><br /> <code>Implementation</code><br /> <code>Procedure TClassA.Add(a: integer);</code><br /> <code>Begin</code><br /> <code>  i:=i+a;</code><br /> <code>End;</code><br /> <code>Initialization</code><br /> <code>  ObjA:=TClassA.Create;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  rtl.createClass($mod,"TClassA",pas.System.TObject,function(){</code><br /> <code>    this.$init = function () {</code><br /> <code>      this.i = 0;</code><br /> <code>    };</code><br /> <code>    this.Add = function(a){</code><br /> <code>      this.i = this.i + a;</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  this.ObjA = null;</code><br /> <code>  $mod.$init = function(){</code><br /> <code>    $mod.ObjA = $mod.TClassA.$create("Create");</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating class-of type

A class-of is a reference to a class. See above about translating class.

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Type</code><br /> <code>  TBird = Class(TObject)</code><br /> <code>  Public</code><br /> <code>    Class var Count: integer;</code><br /> <code>    Class Procedure Add(a: integer); virtual;</code><br /> <code>  End;</code><br /> <code>  TBirds = class of TBird;</code><br />

<code>  TPigeon = Class(TBird)</code><br /> <code>  Public</code><br /> <code>    Class Procedure Add(a: integer); override;</code><br /> <code>  End;</code><br />

<code>var</code><br /> <code>  BirdType: TBirds;</code><br /> <code>Implementation</code><br /> <code>Class Procedure TBird.Add(a: integer);</code><br /> <code>Begin</code><br /> <code>  Count:=Count+a;</code><br /> <code>End;</code><br /> <code>Class Procedure TPigeon.Add(a: integer);</code><br /> <code>Begin</code><br /> <code>  inherited Add(a+1);</code><br /> <code>End;</code><br /> <code>Initialization</code><br /> <code>  BirdType:=TPigeon;</code><br /> <code>  BirdType.Add(1);</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  rtl.createClass($mod,"TBird",pas.System.TObject,function () {</code><br /> <code>    this.Count = 0;</code><br /> <code>    this.Add = function (a) {</code><br /> <code>      this.Count = this.Count + a;</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  rtl.createClass($mod,"TPigeon",$mod.TBird,function () {</code><br /> <code>    this.Add = function (a) {</code><br /> <code>      $mod.TBird.Add.call(this,a + 1);</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  $mod.$init = function(){</code><br /> <code>    $mod.BirdType = $mod.TPigeon;</code><br /> <code>    $mod.BirdType.Add(1);</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Note that this in a class method is the class itself.\

Notes:\

Translating TObject.Free

In Delphi/FPC AnObject.Free checks if Self is nil, then calls the destructor and frees the memory, without changing the reference. In JavaScript however calling a method with AnObject=nil causes a crash. And memory cannot be freed explicitely. Memory is only freed if all references are gone (e.g. set to null).
Therefore pas2js adds code to call the destructor and sets the variable to nil:\

Notes:

Translating class interfaces

JavaScript has nothing like it, so they are emulated.
An interfacetype is a JS-object with some hidden properties, containing the GUID ($guid) and an array with the method names ($names). Here is how IUnknown looks like in JS:\

{
  $module: [object Object],
  $name: "IUnknown",
  $fullname: "System.IUnknown",
  $guid: "{00000000-0000-0000-C000-000000000046}",
  $names: ["QueryInterface","_AddRef","_Release"],
  $rtti: [object Object],
  $kind: "com",
}

A class implementing interfaces has a variable $intfmaps, which has for each implemented GUID a map or delegator function. A map is a JS instance of the interfacetype plus a for each method name a function to call the class method. Here is an example map of IUnknown of TInterfacedObject:\

{
  QueryInterface: function (){ return fn.apply(this.$o,arguments); },
  _AddRef: function (){ return fn.apply(this.$o,arguments); },
  _Release: function (){ return fn.apply(this.$o,arguments); },
  ...
}

When an interface is created for an object (here: a Pascal class instance), for example by using the as-operator "ObjVar as IUnknown", a JS object is created, which is an instance of the map object with its $o set to the ObjVar.\

Supported:

Not yet supported: array of intferfacetype, interface as record member.

Translating helpers

Pas2js supports class helpers, record helpers and type helpers since 1.3. The extend is only virtual, the helped type is kept untouched.\

Translating attributes

Attributes are stored in the TTypeInfo objects as streams stored in an array. See the function GetRTTIAttributes in unit TypInfo for details.

Translating try..finally

JavaScript has the same, so it translates straight forward.

Translating try..except

<thead>

</thead> <tbody>

<code>Interface</code><br /> <code>Uses SysUtils, Math, JS;</code><br /> <code>Function DoIt(n: integer): double;</code><br /> <code>Implementation</code><br /> <code>Function DoIt(n: integer): double;</code><br /> <code>var E: Exception;</code><br /> <code>Begin</code><br /> <code>  try</code><br /> <code>    Result:=double(7.0)/n;</code><br /> <code>    if not IsFinite(Result) then</code><br /> <code>      if n=0 then</code><br /> <code>        raise EZeroDivide.Create</code><br /> <code>      else</code><br /> <code>        raise EOverflow.Create;</code><br /> <code>  except</code><br /> <code>    on EZeroDivide do Result:=0.0;</code><br /> <code>    on E2: EOverflow do Result:=0.0;</code><br /> <code>    else</code><br /> <code>      raise EAbort.Create('Something other: '+String(JS.JSExceptObject));</code><br /> <code>  end;</code><br /> <code>End;</code><br /> <code>End.</code></td>

<code>["System","SysUtils"],</code><br /> <code>function(){</code><br /> <code>  this.DoIt=function(n){</code><br /> <code>    Result = 0;</code><br /> <code>    var E = null;</code><br /> <code>    try{</code><br /> <code>      Result = 7.0 / n;</code><br /> <code>      if (!IsFinite(Result)){</code><br /> <code>        if (n==0){</code><br /> <code>          throw pas.SysUtils.EZeroDivide.$create("Create");</code><br /> <code>        } else {</code><br /> <code>          throw pas.SysUtils.EOverflow.$create("Create");</code><br /> <code>        };</code><br /> <code>      };</code><br /> <code>    }catch($e){</code><br /> <code>      if (pas.SysUtils.EZeroDivide.isPrototypeOf($e)){</code><br /> <code>        Result = 0.0;</code><br /> <code>      } else if (pas.SysUtils.EOverflow.isPrototypeOf($e)){</code><br /> <code>        var E2 = $e;</code><br /> <code>        Result = 0.0;</code><br /> <code>      } else {</code><br /> <code>        throw pas.SysUtils.EAbort.$create("Create",["Something other: "+(""+$e)]);</code><br /> <code>      }</code><br /> <code>    }</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating enumerators

The for..in..do supports enumerating:

The class GetEnumerator function is translated like this:

<thead>

</thead> <tbody>

<code>Interface</code><br />

<code>uses Classes;</code><br />

<code>procedure DoIt(List: TList);</code><br />

<code>Implementation</code><br />

<code>procedure DoIt(List: TList);</code><br /> <code>var</code><br /> <code>  Item: Pointer;</code><br /> <code>begin</code><br /> <code>  for Item in List do</code><br /> <code>    if Item<>nil then ;</code><br /> <code>end;</code><br />

<code>End.</code></td>

<code>["System","Classes"],</code><br /> <code>function(){</code><br /> <code>  this.DoIt=function(List){</code><br /> <code>    var Item = null;</code><br /> <code>    var $in1 = List;</code><br /> <code>    try {</code><br /> <code>      while ($in1.MoveNext()) {</code><br /> <code>        Item = $in1.GetCurrent();</code><br /> <code>        if (Item !== null) ;</code><br /> <code>      }</code><br /> <code>    } finally {</code><br /> <code>      $in1 = rtl.freeLoc($in1)</code><br /> <code>    };</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

Notes:

Translating function types

JavaScript functions work like Delphi's "reference to function", which means like closures, capturing outer variables. Assigning a normal function or nested function to a procedural variable is translated to a simple assignment. A Pascal method needs this to be the class or class instance.
Note that bind cannot be used, because it does not support the equal operator. Instead a wrapper is created:

<thead>

</thead> <tbody>

<code>type</code><br /> <code>  TMyMethod = procedure(n: integer) of object;</code><br /> <code>  TBird = class</code><br /> <code>    procedure DoIt(n: integer); virtual; abstract;</code><br /> <code>  end;</code><br /> <code>  TMyProc = procedure(n: integer);</code><br /> <code>procedure DoSome(n: integer);</code><br /> <code>begin</code><br /> <code>end;</code><br /> <code>var</code><br /> <code>  m: TMyMethod;</code><br /> <code>  Bird: TBird;</code><br /> <code>  p: TMyProc;</code><br /> <code>Begin</code><br /> <code>  m:=@Bird.DoIt;</code><br /> <code>  m(3);</code><br /> <code>  p:=@DoSome;</code><br /> <code>  p(4);</code><br /> <code>End.</code></td>

<code>["System","UnitA"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  rtl.createClass($mod,"TBird",pas.System.TObject,function(){</code><br /> <code>    this.DoIt = function (n) {</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  this.DoSome = function (n) {</code><br /> <code>  };</code><br /> <code>  this.m = null;</code><br /> <code>  this.Bird = null;</code><br /> <code>  this.p = null;</code><br /> <code>  $mod.$main = function() {</code><br /> <code>    $mod.m = rtl.createCallback($mod.Bird,"DoIt");</code><br /> <code>    $mod.m(3);</code><br /> <code>    $mod.p = $mod.DoSome;</code><br /> <code>    $mod.p(4);</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code><br />

<code>rtl = {</code><br /> <code>  ...</code><br /> <code>  createCallback: function(scope, fn){</code><br /> <code>    var cb;</code><br /> <code>    if (typeof(fn)==='string'){</code><br /> <code>      cb = function(){</code><br /> <code>        return scope[fn].apply(scope,arguments);</code><br /> <code>      };</code><br /> <code>    } else {</code><br /> <code>      cb = function(){</code><br /> <code>        return fn.apply(scope,arguments);</code><br /> <code>      };</code><br /> <code>    };</code><br /> <code>    cb.scope = scope;</code><br /> <code>    cb.fn = fn;</code><br /> <code>    return cb;</code><br /> <code>  },</code><br /> <code>  ...</code></td>

</tbody>

Notes:

Translating anonymous functions

Anonymous functions are supported since pas2js 1.1.
Note that in pas2js local procedures are closures as well. See below.
For pas2js 1.0 the next best thing are local procedures. For example:

<thead>

</thead> <tbody>

<code>type</code><br /> <code>  TAdder = reference to function(n: integer): integer;</code><br />

<code>function CreateAdder(a: integer): TAdder;</code><br /> <code>begin</code><br /> <code>  Result:=function(b: integer)</code><br /> <code>    begin</code><br /> <code>      Result:=a+b;</code><br /> <code>    end;</code><br /> <code>end;</code><br />

<code>var</code><br /> <code>  Adder: TAdder;</code><br /> <code>Begin</code><br /> <code>  Adder:=CreateAdder(3);</code><br /> <code>  writeln(Adder(5)); // gives 8</code><br /> <code>End.</code></td>

<code>type</code><br /> <code>  TAdder = reference to function(n: integer): integer;</code><br />

<code>function CreateAdder(a: integer): TAdder;</code><br /> <code>  function Add(b: integer): integer;</code><br /> <code>  begin</code><br /> <code>    Result:=a+b;</code><br /> <code>  end;</code><br /> <code>begin</code><br /> <code>  Result:=@Add;</code><br /> <code>end;</code><br />

<code>var</code><br /> <code>  Adder: TAdder;</code><br /> <code>Begin</code><br /> <code>  Adder:=CreateAdder(3);</code><br /> <code>  writeln(Adder(5)); // gives 8</code><br /> <code>End.</code></td>

</tbody>

Translating var modifier absolute

The absolute modifier works as an alias. That means it works FPC/Delphi compatible for related types like Pointer and TObject, and works incompatible for unrelated types like longword and record (e.g. var r: TPoint absolute MyLongInt).
The modifier is currently only supported for local variables.

Translating assert()

The Assert(boolean[,string]) function is translated to if(bool) throw x. If unit sysutils is used, it creates an EAssertFailed exception.
Otherwise it throws a string.\

Dispatch messages

The procedure modifier message and the Dispatch method works similar to FPC/Delphi, as it expects a record of a specific format and TObject.Dispatch calls the corresponding method with that message number or string.
The procedure modifier message <integer> adds an entry to hidden YourClass.$msgint object, while the modifier message <string> adds an entry to the hidden YourClass.$msgstr object.
Two new directives {$DispatchField fieldname}'' and**{$DispatchStrField fieldname}**'' were added. Insert these directives in front of your class declaration to let the compiler check all methods with message modifiers of this class and its descendants whether they pass a record with the required field. For example:

  {$DispatchField Msg} // enable checking message methods for record field name "Msg"
  {$DispatchStrField MsgStr}
  TObject = class
    procedure Dispatch(var aMessage); virtual;
    procedure DispatchStr(var aMessage); virtual;
  end;
  TMouseDownMsg = record
    Id: integer; // Id instead of Msg, works in FPC, but not in pas2js
    x,y: integer;
  end;
  TMouseUpMsg = record
    MsgStr: string;
    X,Y: integer;
  end;
  TWinControl = class
    procedure MouseDownMsg(var Msg: TMouseDownMsg); message 3; // warning: Dispatch requires record field Msg
    procedure MouseUpMsg(var Msg: TMouseUpMsg); message 'up'; // ok, record with string field name MsgStr
  end;

Note that descendant classes can override the $DispatchField or disable the check using {$DispatchField -}.

Calling JavaScript from Pascal

Pas2js allows to write low level functions and/or access a JavaScript library with the following possibilities:

The asm block

The asm block is pure JavaScript, that is copied directly into the generated .js file.

<thead>

</thead> <tbody>

<code>var</code><br /> <code>  s: string;</code><br /> <code>Begin</code><br /> <code>  s = 'Hello World!';</code><br /> <code>  Asm</code><br /> <code>    console.log(s);</code><br /> <code>  End;</code><br /> <code>End.</code></td>

<code> ["System"],</code><br /> <code> function(){</code><br /> <code>   var $mod = this;</code><br /> <code>   this.s = '';</code><br /> <code>   $mod.$main = function(){</code><br /> <code>     $mod.s = "Hello World!";</code><br /> <code>     console.log(s);</code><br /> <code>   };</code><br /> <code> },</code><br /> <code> []);</code><br /> <code> </code></td>

</tbody>

Notes:

The procedure modifier assembler

You can write pure JavaScript functions like this:

<thead>

</thead> <tbody>

<code>Procedure Log(const s: string); assembler;</code><br /> <code>Asm</code><br /> <code>  console.log(s);</code><br /> <code>end;</code><br />

<code>Begin</code><br /> <code>  Log('Hello World!');</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.Log = function(s){</code><br /> <code>    console.log(s);</code><br /> <code>  };</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    $mod.Log("Hello World!");</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

See also [#asm asm].

The procedure modifier external

The procedure modifier external requires a string constant and tells the compiler to replace a reference with this string value. The value is not checked for JS syntax.

<thead>

</thead> <tbody>

<code>Procedure ConsoleLog(const s: string); external name 'console.log';</code><br /> <code>// Note: an external procedure has no begin..end block</code><br /> <code>Begin</code><br /> <code>  ConsoleLog('Hello World!');</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    console.log("Hello World!");</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

The procedure modifier varargs

Appending the varargs modifier to a procedure allows to pass arbitrary more parameters to a function. By default these parameters are untyped, i.e. any type fits. Alternatively you can use varargs of aType to allow only specific types.
To access these arguments use either JSArguments from unit JS or an asm..end block.

<thead>

</thead> <tbody>

<code>uses JS;</code><br /> <code>function Sum(b: boolean): longint; varargs;</code><br /> <code>var i: longint;</code><br /> <code>begin</code><br /> <code>  if b then</code><br /> <code>    asm</code><br /> <code>      for (var i=0; i<arguments.length; i++) Result+=arguments[i];</code><br /> <code>    end</code><br /> <code>  else</code><br /> <code>    for i:=0 to JSArguments.length-1 do</code><br /> <code>      Result:=Result+longint(JSArguments[i]);</code><br /> <code>end;</code><br /> <code>var</code><br /> <code>  i: integer;</code><br /> <code>Begin</code><br /> <code>  i:=Sum(true,2,4,6); // i=12</code><br /> <code>  i:=Sum(false,2,4,6); // i=12</code><br /> <code>End.</code></td>

<code>["System","JS"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.Sum = function(b){</code><br /> <code>    var Result = 0;</code><br /> <code>    var i = 0;</code><br /> <code>    if (b){</code><br /> <code>      for (var i=0; i<arguments.length; i++) Result+=arguments[i];</code><br /> <code>    } else {</code><br /> <code>      for (var $l1 = 1, $le2 = argumens.length; $l1 <= $le2; $l1++){</code><br /> <code>        $i = $l1;</code><br /> <code>        Result = Result + arguments[i];</code><br /> <code>      }</code><br /> <code>    }</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>  this.i = 0;</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    $mod.i = $mod.Sum(true,2,4,6);</code><br /> <code>    $mod.i = $mod.Sum(false,2,4,6);</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

The above example defines a function Sum, that requires the first parameter to be a boolean and then an arbitrary number of parameters. The compiler does not type check the other parameters, so you can pass anything readable.

The var modifier external

The var modifier external allows to use a JavaScript variable or constant.

<thead>

</thead> <tbody>

<code>var</code><br /> <code>  EulersNumber: Double; external name 'Math.E';</code><br /> <code>  d: double;</code><br /> <code>Begin</code><br /> <code>  d:=EulersNumber;</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  this.d = 0.0;</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    $mod.d = Math.E;</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

The external modifier of class members

The method modifier external works as the procedure modifier, except it uses the scope of the class or instance.
The field modifier external works as the var modifier, except it uses the scope of the class or instance.
Requires the modeswitch externalclass.

<thead>

</thead> <tbody>

<code>{$modeswitch externalclass}</code><br /> <code>type</code><br /> <code>  TWrapper = class</code><br /> <code>  private</code><br /> <code>    // let's assume this object has the properties "$Handle", "$id", and "0"</code><br /> <code>  public</code><br /> <code>    Id: NativeInt; external name '$Id';</code><br /> <code>    x: NativeInt; external name '[0]';</code><br /> <code>    y: NativeInt; external name '["A B"]';</code><br /> <code>    function GetState(typ: longint): NativeInt; external name '$Handle.GetState';</code><br /> <code>    procedure DoIt;</code><br /> <code>  end;</code><br /> <code>procedure TWrapper.DoIt;</code><br /> <code>begin</code><br /> <code>  Id := GetState(4);</code><br /> <code>end;</code><br /> <code>var</code><br /> <code>  W: TWrapper;</code><br /> <code>Begin</code><br /> <code>  W.Id := 2;</code><br /> <code>  W.x := 3;</code><br /> <code>  W.y := 4;</code><br /> <code>  W.GetState(5);</code><br /> <code>End.</code></td>

<code>["System"],</code><br /> <code>function(){</code><br /> <code>  var $mod = this;</code><br /> <code>  rtl.createClass($mod, "TWrapper", pas.System.TObject, function () {</code><br /> <code>    this.DoIt = function(){</code><br /> <code>      this.$Id = this.$Handle.GetState(4);</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  this.W = null;</code><br /> <code>  $mod.$main = function(){</code><br /> <code>    $mod.W.$Id = 2;</code><br /> <code>    $mod.W[0] = 3;</code><br /> <code>    $mod.W["A B"] = 4;</code><br /> <code>    $mod.W.$Handle.GetState(5);</code><br /> <code>  };</code><br /> <code>},</code><br /> <code>[]);</code></td>

</tbody>

External classes

pas2js introduces a new class modifier "external name", which makes the whole class external. External classes allow to easily declare Pascal wrappers for JavaScript objects and function objects.
They need the modeswitch externalclass in front of the class.
An external class is not a TObject and has none of its methods.
All members are external. If you omit the external modifier the external name is the member name. Keep in mind that JS is case sensitive.
Properties work the same as with Pascal classes, i.e. are replaced by Getter/Setter.
Destructors are not allowed.
Constructors are supported in four ways:

<thead>

</thead> <tbody>

<code>{$modeswitch externalclass}</code><br /> <code>type</code><br /> <code>  TJSDate = class external name 'Date'</code><br /> <code>  private</code><br /> <code>    function getYear: NativeInt;</code><br /> <code>    procedure setYear(const AValue: NativeInt);</code><br /> <code>  public</code><br /> <code>    constructor New;</code><br /> <code>    constructor New(const MilliSecsSince1970: NativeInt);</code><br /> <code>    class function now: NativeInt;</code><br /> <code>    property Year: NativeInt read getYear write setYear;</code><br /> <code>  end;</code><br /> <code>var</code><br /> <code>  d: TJSDate;</code><br /> <code>Begin</code><br /> <code>  d:=TJSDate.New;</code><br /> <code>  d.Year:=d.Year+1;</code><br /> <code>End.</code></td>

<code>  var $mod = this;</code><br /> <code>  this.d = null;</code><br /> <code>  $mod.$main = function () {</code><br /> <code>    $mod.d = new Date();</code><br /> <code>    $mod.d.setYear($mod.d.getYear() + 1);</code><br /> <code>  };</code><br /> <code>});</code></td>

</tbody>

Notes:

External class as ancestor

A Pascal class can descend from an external class - a JS object or function.
The methods AfterConstruction and BeforeDestruction are called if they exist.
New instances of a JS Object descendant are created by default with Object.create(ancestorclass).
New instances of a JS Function descendant are created by default with new DescendantFunc().
You can override this, by providing a non private
class function NewInstance(fnname: string; const paramsarray): TPasClass; virtual;. This method is called to create a new instance and before calling the constructor. The name is arbitrary, but the function must be the first non private, non external, virtual class function with the class as result type.\

<thead>

</thead> <tbody>

<code>Program MyModule;</code><br /> <code>{$modeswitch externalclass}</code><br /> <code>type</code><br /> <code>  TExtA = class external name 'ExtA'</code><br /> <code>  end;</code><br /> <code>  TMyB = class(TExtA)</code><br /> <code>  protected</code><br /> <code>    // optional: override default allocation</code><br /> <code>    class function NewInstance(fnname: string; const paramarray): TMyB; virtual;</code><br /> <code>  end;</code><br /> <code>class function TMyB.NewInstance(fnname: string; const paramarray): TMyB;</code><br /> <code>Begin</code><br /> <code>  asm</code><br /> <code>  Result = Object.create(ExtA); // that is what the rtl does</code><br /> <code>  end;</code><br /> <code>End;</code><br />

<code>Begin</code><br /> <code>End.</code></td>

<code>  var $mod = this;</code><br /> <code>  rtl.createClassExt($mod, "TMyB", ExtA, "NewInstance", function () {</code><br /> <code>    this.$init = function () {</code><br /> <code>    };</code><br /> <code>    this.$final = function () {</code><br /> <code>    };</code><br /> <code>    this.NewInstance = function (fnname, paramarray) {</code><br /> <code>      var Result = null;</code><br /> <code>      Result = Object.create(ExtA);</code><br /> <code>      return Result;</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  $mod.$main = function () {</code><br /> <code>  };</code><br /> <code>});</code></td>

</tbody>

<thead>

</thead> <tbody>

<code>Program MyModule;</code><br /> <code>{$modeswitch externalclass}</code><br /> <code>uses JS;</code><br /> <code>type</code><br /> <code>  TExternalFunc = class external name 'ExternalFunc'(TJSFunction)</code><br /> <code>    constructor New(a: word);</code><br /> <code>  end;</code><br /> <code>  TMyFunc = class(TExternalFunc)</code><br /> <code>    constructor Create(b: word);</code><br /> <code>  end;</code><br /> <code>constructor TMyFunc.Create(b: word);</code><br /> <code>Begin</code><br /> <code>  inherited New(b+1); // optional: call inherited constructor function</code><br /> <code>End;</code><br />

<code>var</code><br /> <code>  f: TMyFunc;</code><br /> <code>Begin</code><br /> <code>  f:=TMyFunc.Create(3);</code><br /> <code>  writeln(jsInstanceOf(f,TExternalFunc)); // writes true, instanceof operator works as expected</code><br /> <code>End.</code></td>

<code>  var $mod = this;</code><br /> <code>  rtl.createClassExt($mod, "TMyFunc", ExternalFunc, "", function () {</code><br /> <code>    this.$init = function () {</code><br /> <code>    };</code><br /> <code>    this.$final = function () {</code><br /> <code>    };</code><br /> <code>    this.Create$2 = function (b) {</code><br /> <code>      this.$ancestorfunc(b+1);</code><br /> <code>    };</code><br /> <code>  });</code><br /> <code>  this.f = null;</code><br /> <code>  $mod.$main = function () {</code><br /> <code>    f = $mod.TMyFunc.$create("Create$2",[3]);</code><br /> <code>    pas.System.Writeln(pas.JS.jsInstanceOf(f,ExternalFunc));</code><br /> <code>  };</code><br /> <code>});</code></td>

</tbody>

The JSValue type

Pas2js introduces a new type JSValue, which works similar to a JS variable. You can assign almost any value to it and it can be type casted to many types. JSValue is useful for JS wrappers, when a variable can have multiple types. And it can be used for containers storing arbitrary data, e.g. a list of JSValue.
Key features:\

Accessing JS object properties with the bracket accessor

Pas2js allows to define index properties that map directly to the JS object properties. For example the default property of TJSObject allows to get and set the properties of an object. For example TJSObject(AnObject)['Name']:=Value;
Another example is the default property of TJSArray, that allows access via integers aTJSArray[3]:=Value;
To define your own bracket accessor define a normal index property and define the getter/setter as external name '[]'.
Here is an example for a read only accessor:

<thead>

</thead> <tbody>

<code>{$modeswitch externalclass}</code><br /> <code>type</code><br /> <code>  TExtA = class external name 'ExtA'</code><br /> <code>  private</code><br /> <code>    function GetItems(Index: integer): String; external name '[]';</code><br /> <code>  public</code><br /> <code>    property Items[Index: integer]: String read GetItems; default;</code><br /> <code>  end;</code><br /> <code>var</code><br /> <code>  Obj: TExtA;</code><br /> <code>  s: String;</code><br /> <code>Begin</code><br /> <code>  ... get Obj from somewhere ...</code><br /> <code>  s:=Obj[2];</code><br /> <code>End.</code></td>

<code>  var $mod = this;</code><br /> <code>  this.Obj = undefined;</code><br /> <code>  this.s = "";</code><br /> <code>  $mod.$main = function () {</code><br /> <code>    $mod.s = Obj[2];</code><br /> <code>  };</code><br /> <code>});</code></td>

</tbody>

Notes:

RTTI - Run Time Type Information

The RTTI provides access to the type data of all published properties, fields and methods. The type data provides similar information as Delphi/FPC, but the internals are very different. Delphi/FPC uses pointers, variant records and fake static arrays, which have no equivalent in JS. Instead pas2js uses external classes. For example:

    TTypeInfo = class external name 'rtl.tTypeInfo'
    public
      Name: String external name 'name';
      Kind: TTypeKind external name 'kind';
    end;
    TTypeInfoClass = class of TTypeInfo;\

    TTypeInfoInteger = class external name 'rtl.tTypeInfoInteger'(TTypeInfo)
    public
      MinValue: NativeInt external name 'minvalue';
      MaxValue: NativeInt external name 'maxvalue';
      OrdType : TOrdType external name 'ordtype';
    end;\

The typeinfo function works on type, var, const and property identifiers. By default it returns a pointer. If the typinfo unit is used it returns the appropiate TTypeInfo. For instance typeinfo(integer) returns a TTypeInfoInteger.
Typeinfo of a var or const returns the typeinfo of its type, not of its current runtime value. The exception is a class and class-of instance variable (e.g. var o: TObject; ... typeinfo(o)), which returns the typeinfo of the current runtime value. If o is nil it will give a JS error.
Local types (i.e. inside a procedure) do not have typeinfo.
Open array parameters are not yet supported.
Note that FPC typeinfo(aClassVar) returns the compiletime type, so it works on nil.\

Async/AWait

Pas2js supports the JS operators async and await to simplify the use of Promise. The await operator corresponds to three intrinsic Pas2js functions:

The await function can only be used inside a procedure with the async modifier.
Example for the explicit promise:

<thead>

</thead> <tbody>

<code>uses JS, Web;</code><br />

<code>function ResolveAfter2Seconds: TJSPromise;</code><br /> <code>begin</code><br /> <code>  Result:=TJSPromise.new(procedure(resolve, reject : TJSPromiseResolver)</code><br /> <code>    begin</code><br /> <code>    window.setTimeout(procedure</code><br /> <code>      begin</code><br /> <code>      resolve('resolved');</code><br /> <code>      end,</code><br /> <code>      2000); // wait 2 seconds</code><br /> <code>    end);</code><br /> <code>end;</code><br />

<code>procedure AsyncCall; async;</code><br /> <code>var s: string;</code><br /> <code>begin</code><br /> <code>  writeln('calling');</code><br /> <code>  s := await(string,resolveAfter2Seconds()); // does not check if result is really a string</code><br /> <code>  writeln(s); // expected output: 'resolved'</code><br /> <code>end;</code><br />

<code>begin</code><br /> <code>  AsyncCall;</code><br /> <code>end.</code></td>

<code>  "use strict";</code><br /> <code>  var $mod = this;</code><br /> <code>  this.ResolveAfter2Seconds = function () {</code><br /> <code>    var Result = null;</code><br /> <code>    Result = new Promise(function (resolve, reject) {</code><br /> <code>      window.setTimeout(function () {</code><br /> <code>        resolve("resolved");</code><br /> <code>      },2000);</code><br /> <code>    });</code><br /> <code>    return Result;</code><br /> <code>  };</code><br /> <code>  this.AsyncCall = async function () {</code><br /> <code>    var s = "";</code><br /> <code>    pas.System.Writeln("calling");</code><br /> <code>    s = await $mod.ResolveAfter2Seconds();</code><br /> <code>    pas.System.Writeln(s);</code><br /> <code>  };</code><br /> <code>  $mod.$main = function () {</code><br /> <code>    $mod.AsyncCall();</code><br /> <code>  };</code><br /> <code>});</code></td>

</tbody>

Notes:

Compiler directives

In config files:

In source files:

Defines:

Numbers

JavaScript only supports double. All Pascal number types and enum values are mapped to this. A double supports integers from
 MinInteger = -$10000000000000;
 MaxInteger = $fffffffffffff;
 MinDouble = 5.0e-324;
 MaxDouble = 1.7e+308;\

 Intrinsic integer types:

Notes:

Other supported Pascal elements

Not supported elements

JavaScript Version

Code generation depending on -P option:

Creating source maps

Source maps are files telling the browser what JavaScript comes from which original source (e.g. Pascal file), similar to debug information in FPC/Delphi.
In 2017 FireFox and Chrome supports source maps.
You can enable generating source map files by using the -Jm option.
The compiler generates one module.js.map file for every generated module.js file. The last line of the .js file contains the line
//# sourceMappingURL=module.js.map
telling the browser where to find the source map.
The source map contains references to the Pascal files and included .js files (e.g. -Jirtl.js) relative to the location of the source map. Note that if the Pascal file lies in a parent directory, the relativ path contains '../'. You can change the base directory of the relative paths by using the option -Jmbasedir=<x>. For example -JmC:\www\pas creates paths relative to C:\www\pas.
You can set the base URL, where the browser finds the Pascal sources, by passing the -Jmsourceroot=<x> option. For example -Jmsourceroot=http://www.yoursite.com/pas/. The browser prepends this to the source map filenames when downloading the original source files (e.g. the .pas files).
You can include the whole Pascal sources in the source map using the option -Jminclude.\

To show the generated mapping for each line you can use the tool fpc/packages/fcl-js/examples/srcmapdump.\

See Also


From the Free Pascal wiki, page "Pas2js Transpiler", as it stood on 2023-05-08 after a change by Maxerist, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 2, last changed by WikiArchive .