Transpiler is a specific term for taking source code written in one language and transforming into another language that has a similar level of abstraction. Pas2JS is a type of compiler that translates/parses Object Pascal and emits JavaScript.
The language used by Pas2JS compiler is an Object Pascal dialect (subset of Delphi/FPC syntax) that retains as much compatibility as possible with the Object Pascal language used by the Delphi. As you know, Object Pascal is a highly-structured, strongly-typed, object-oriented language that is also simple to use due to its English-like keywords and statements. The Pas2JS compiler can be used in the Lazarus IDE, it takes advantage of the strongly-typed nature of the Object Pascal language, that combine both the design and coding aspects of applications into one unified, highly-productive tool - you have the code completion feature all the time! Any functions, procedures, constants, variables, types, classes, member variables, methods, or properties that aren't referenced in the source code for an application are not emitted. This keeps the size of the resultant application as small as possible.
The Pas2JS compiler includes a very small runtime overhead of around 25KB for an empty, uncompressed, non-visual application, it is a small javascript file called rtl.js. Exactly, for the generated code to work (project.js) it's required the rtl.js. This core file just defines an object RTL. This object will start the Object Pascal code if you include a call to rtl.run() in the html page. By default, pas2js automatically include this file in the generated output.
Tiny Pas2JS application
The following pas2js example illustrates the use of modules, all classes available in the JavaScript runtime, and in the browser are available through import units (comparable to the windows or unix units for the native compiler).
Example
program project1;
{$mode objfpc}
uses
JS, Classes, SysUtils, Web, Unit1;
var
app: TApplication;
begin
// Your code here
app:= TApplication.Create;
app.RunApp;
end.
//-----------------------------------------------------
unit Unit1;
{$mode objfpc}
interface
uses
JS, Web, Types, Math, Classes, SysUtils;
type
TApplication = class
procedure RunApp;
end;
implementation
{ TApplication }
procedure TApplication.RunApp;
begin
WriteLn('starting app');
end;
initialization
end.
Please take a moment to check out the emitted javascript of the above example.
var pas = {};
var rtl = {
quiet : false,
debug_load_units : false,
debug_rtti : false,
debug : function () {
if (rtl.quiet || !console || !console.log)
return;
console.log(arguments);
},
error : function (s) {
rtl.debug('Error: ', s);
throw s;
},
warn : function (s) {
rtl.debug('Warn: ', s);
},
hasString : function (s) {
return rtl.isString(s) && (s.length > 0);
},
isArray : function (a) {
return Array.isArray(a);
},
isFunction : function (f) {
return typeof(f) === "function";
},
isModule : function (m) {
return rtl.isObject(m) && rtl.hasString(m.$name) && (pas[m.$name] === m);
},
isImplementation : function (m) {
return rtl.isObject(m) && rtl.isModule(m.$module) && (m.$module.$impl === m);
},
isNumber : function (n) {
return typeof(n) === "number";
},
isObject : function (o) {
var s = typeof(o);
return (typeof(o) === "object") && (o != null);
},
isString : function (s) {
return typeof(s) === "string";
},
getNumber : function (n) {
return typeof(n) === "number" ? n : NaN;
},
getChar : function (c) {
return ((typeof(c) === "string") && (c.length === 1)) ? c : "";
},
getObject : function (o) {
return ((typeof(o) === "object") || (typeof(o) === 'function')) ? o : null;
},
m_loading : 0,
m_loading_intf : 1,
m_intf_loaded : 2,
m_loading_impl : 3, // loading all used unit
m_initializing : 4, // running initialization
m_initialized : 5,
module : function (module_name, intfuseslist, intfcode, impluseslist, implcode) {
if (rtl.debug_load_units)
rtl.debug('rtl.module name="' + module_name + '" intfuses=' + intfuseslist + ' impluses=' + impluseslist + ' hasimplcode=' + rtl.isFunction(implcode));
if (!rtl.hasString(module_name))
rtl.error('invalid module name "' + module_name + '"');
if (!rtl.isArray(intfuseslist))
rtl.error('invalid interface useslist of "' + module_name + '"');
if (!rtl.isFunction(intfcode))
rtl.error('invalid interface code of "' + module_name + '"');
if (!(impluseslist == undefined) && !rtl.isArray(impluseslist))
rtl.error('invalid implementation useslist of "' + module_name + '"');
if (!(implcode == undefined) && !rtl.isFunction(implcode))
rtl.error('invalid implementation code of "' + module_name + '"');
if (pas[module_name])
rtl.error('module "' + module_name + '" is already registered');
var module = pas[module_name] = {
$name : module_name,
$intfuseslist : intfuseslist,
$impluseslist : impluseslist,
$state : rtl.m_loading,
$intfcode : intfcode,
$implcode : implcode,
$impl : null,
$rtti : Object.create(rtl.tSectionRTTI),
};
module.$rtti.$module = module;
if (implcode)
module.$impl = {
$module : module,
$rtti : module.$rtti,
};
},
exitcode : 0,
run : function (module_name) {
function doRun() {
if (!rtl.hasString(module_name))
module_name = 'program';
if (rtl.debug_load_units)
rtl.debug('rtl.run module="' + module_name + '"');
rtl.initRTTI();
var module = pas[module_name];
if (!module)
rtl.error('rtl.run module "' + module_name + '" missing');
rtl.loadintf(module);
rtl.loadimpl(module);
if (module_name == 'program') {
if (rtl.debug_load_units)
rtl.debug('running $main');
var r = pas.program.$main();
if (rtl.isNumber(r))
rtl.exitcode = r;
}
}
if (rtl.showUncaughtExceptions) {
try {
doRun();
} catch (re) {
var errMsg = re.hasOwnProperty('$class') ? re.$class.$classname : '';
errMsg += ((errMsg) ? ': ' : '') + (re.hasOwnProperty('fMessage') ? re.fMessage : re);
alert('Uncaught Exception : ' + errMsg);
rtl.exitCode = 216;
}
} else {
doRun();
}
return rtl.exitcode;
},
loadintf : function (module) {
if (module.$state > rtl.m_loading_intf)
return; // already finished
if (rtl.debug_load_units)
rtl.debug('loadintf: "' + module.$name + '"');
if (module.$state === rtl.m_loading_intf)
rtl.error('unit cycle detected "' + module.$name + '"');
module.$state = rtl.m_loading_intf;
// load interfaces of interface useslist
rtl.loaduseslist(module, module.$intfuseslist, rtl.loadintf);
// run interface
if (rtl.debug_load_units)
rtl.debug('loadintf: run intf of "' + module.$name + '"');
module.$intfcode(module.$intfuseslist);
// success
module.$state = rtl.m_intf_loaded;
// Note: units only used in implementations are not yet loaded (not even their interfaces)
},
loaduseslist : function (module, useslist, f) {
if (useslist == undefined)
return;
for (var i in useslist) {
var unitname = useslist[i];
if (rtl.debug_load_units)
rtl.debug('loaduseslist of "' + module.$name + '" uses="' + unitname + '"');
if (pas[unitname] == undefined)
rtl.error('module "' + module.$name + '" misses "' + unitname + '"');
f(pas[unitname]);
}
},
loadimpl : function (module) {
if (module.$state >= rtl.m_loading_impl)
return; // already processing
if (module.$state < rtl.m_intf_loaded)
rtl.error('loadimpl: interface not loaded of "' + module.$name + '"');
if (rtl.debug_load_units)
rtl.debug('loadimpl: load uses of "' + module.$name + '"');
module.$state = rtl.m_loading_impl;
// load interfaces of implementation useslist
rtl.loaduseslist(module, module.$impluseslist, rtl.loadintf);
// load implementation of interfaces useslist
rtl.loaduseslist(module, module.$intfuseslist, rtl.loadimpl);
// load implementation of implementation useslist
rtl.loaduseslist(module, module.$impluseslist, rtl.loadimpl);
// Note: At this point all interfaces used by this unit are loaded. If
// there are implementation uses cycles some used units might not yet be
// initialized. This is by design.
// run implementation
if (rtl.debug_load_units)
rtl.debug('loadimpl: run impl of "' + module.$name + '"');
if (rtl.isFunction(module.$implcode))
module.$implcode(module.$impluseslist);
// run initialization
if (rtl.debug_load_units)
rtl.debug('loadimpl: run init of "' + module.$name + '"');
module.$state = rtl.m_initializing;
if (rtl.isFunction(module.$init))
module.$init();
// unit initialized
module.$state = rtl.m_initialized;
},
createCallback : function (scope, fn) {
var cb;
if (typeof(fn) === 'string') {
cb = function () {
return scope[fn].apply(scope, arguments);
};
} else {
cb = function () {
return fn.apply(scope, arguments);
};
};
cb.scope = scope;
cb.fn = fn;
return cb;
},
cloneCallback : function (cb) {
return rtl.createCallback(cb.scope, cb.fn);
},
eqCallback : function (a, b) {
// can be a function or a function wrapper
if (a == b) {
return true;
} else {
return (a != null) && (b != null) && (a.fn) && (a.scope === b.scope) && (a.fn == b.fn);
}
},
initClass : function (c, parent, name, initfn) {
parent[name] = c;
c.$classname = name;
if ((parent.$module) && (parent.$module.$impl === parent))
parent = parent.$module;
c.$parent = parent;
c.$fullname = parent.$name + '.' + name;
if (rtl.isModule(parent)) {
c.$module = parent;
c.$name = name;
} else {
c.$module = parent.$module;
c.$name = parent.name + '.' + name;
};
// rtti
if (rtl.debug_rtti)
rtl.debug('initClass ' + c.$fullname);
var t = c.$module.$rtti.$Class(c.$name, {
"class" : c,
module : parent
});
c.$rtti = t;
if (rtl.isObject(c.$ancestor))
t.ancestor = c.$ancestor.$rtti;
if (!t.ancestor)
t.ancestor = null;
// init members
initfn.call(c);
},
createClass : function (parent, name, ancestor, initfn) {
// create a normal class,
// ancestor must be null or a normal class,
// the root ancestor can be an external class
var c = null;
if (ancestor != null) {
c = Object.create(ancestor);
c.$ancestor = ancestor;
// Note:
// if root is an "object" then c.$ancestor === Object.getPrototypeOf(c)
// if root is a "function" then c.$ancestor === c.__proto__, Object.getPrototypeOf(c) returns the root
} else {
c = {};
c.$create = function (fnname, args) {
if (args == undefined)
args = [];
var o = Object.create(this);
o.$class = this; // Note: o.$class === Object.getPrototypeOf(o)
o.$init();
try {
o[fnname].apply(o, args);
o.AfterConstruction();
} catch ($e) {
o.$destroy;
throw $e;
}
return o;
};
c.$destroy = function (fnname) {
this.BeforeDestruction();
this[fnname]();
this.$final;
};
};
rtl.initClass(c, parent, name, initfn);
},
createClassExt : function (parent, name, ancestor, newinstancefnname, initfn) {
// Create a class using an external ancestor.
// If newinstancefnname is given, use that function to create the new object.
// If exist call BeforeDestruction and AfterConstruction.
var c = null;
c = Object.create(ancestor);
c.$create = function (fnname, args) {
if (args == undefined)
args = [];
var o = null;
if (newinstancefnname.length > 0) {
o = this[newinstancefnname](fnname, args);
} else {
o = Object.create(this);
}
o.$class = this; // Note: o.$class === Object.getPrototypeOf(o)
o.$init();
try {
o[fnname].apply(o, args);
if (o.AfterConstruction)
o.AfterConstruction();
} catch ($e) {
o.$destroy;
throw $e;
}
return o;
};
c.$destroy = function (fnname) {
if (this.BeforeDestruction)
this.BeforeDestruction();
this[fnname]();
this.$final;
};
rtl.initClass(c, parent, name, initfn);
},
tObjectDestroy : "Destroy",
free : function (obj, name) {
if (obj[name] == null)
return;
obj[name].$destroy(rtl.tObjectDestroy);
obj[name] = null;
},
freeLoc : function (obj) {
if (obj == null)
return;
obj.$destroy(rtl.tObjectDestroy);
return null;
},
is : function (descendant, type) {
return type.isPrototypeOf(descendant) || (descendant === type);
},
isExt : function (instance, type) {
// Notes:
// isPrototypeOf and instanceof return false on equal
// isPrototypeOf does not work for Date.isPrototypeOf(new Date())
// so if isPrototypeOf is false test with instanceof
// instanceof needs a function on right side
if (instance == null)
return false; // Note: ==null checks for undefined
if ((typeof(type) !== 'object') && (typeof(type) !== 'function'))
return false;
if (instance === type)
return true;
if (type.isPrototypeOf && type.isPrototypeOf(instance))
return true;
if ((typeof type == 'function') && (instance instanceof type))
return true;
return false;
},
EInvalidCast : null,
as : function (instance, type) {
if (rtl.is(instance, type))
return instance;
throw rtl.EInvalidCast.$create("create");
},
asExt : function (instance, type) {
if (rtl.isExt(instance, type))
return instance;
throw rtl.EInvalidCast.$create("create");
},
length : function (arr) {
return (arr == null) ? 0 : arr.length;
},
arraySetLength : function (arr, defaultvalue, newlength) {
// multi dim: (arr,defaultvalue,dim1,dim2,...)
if (arr == null)
arr = [];
var p = arguments;
function setLength(a, argNo) {
var oldlen = a.length;
var newlen = p[argNo];
if (oldlen !== newlength) {
a.length = newlength;
if (argNo === p.length - 1) {
if (rtl.isArray(defaultvalue)) {
for (var i = oldlen; i < newlen; i++)
a[i] = []; // nested array
} else if (rtl.isFunction(defaultvalue)) {
for (var i = oldlen; i < newlen; i++)
a[i] = new defaultvalue(); // e.g. record
} else if (rtl.isObject(defaultvalue)) {
for (var i = oldlen; i < newlen; i++)
a[i] = {}; // e.g. set
} else {
for (var i = oldlen; i < newlen; i++)
a[i] = defaultvalue;
}
} else {
for (var i = oldlen; i < newlen; i++)
a[i] = []; // nested array
}
}
if (argNo < p.length - 1) {
// multi argNo
for (var i = 0; i < newlen; i++)
a[i] = setLength(a[i], argNo + 1);
}
return a;
}
return setLength(arr, 2);
},
arrayClone : function (type, src, srcpos, end, dst, dstpos) {
// type: 0 for references, "refset" for calling refSet(), a function for new type()
// src must not be null
// This function does not range check.
if (rtl.isFunction(type)) {
for (; srcpos < end; srcpos++)
dst[dstpos++] = new type(src[srcpos]); // clone record
} else if (isString(type) && (type === 'refSet')) {
for (; srcpos < end; srcpos++)
dst[dstpos++] = refSet(src[srcpos]); // ref set
} else {
for (; srcpos < end; srcpos++)
dst[dstpos++] = src[srcpos]; // reference
};
},
arrayConcat : function (type) {
// type: see rtl.arrayClone
var a = [];
var l = 0;
for (var i = 1; i < arguments.length; i++)
l += arguments[i].length;
a.length = l;
l = 0;
for (var i = 1; i < arguments.length; i++) {
var src = arguments[i];
if (src == null)
continue;
rtl.arrayClone(type, src, 0, src.length, a, l);
l += src.length;
};
return a;
},
arrayCopy : function (type, srcarray, index, count) {
// type: see rtl.arrayClone
// if count is missing, use srcarray.length
if (srcarray == null)
return [];
if (index < 0)
index = 0;
if (count === undefined)
count = srcarray.length;
var end = index + count;
if (end > scrarray.length)
end = scrarray.length;
if (index >= end)
return [];
if (type === 0) {
return srcarray.slice(index, end);
} else {
var a = [];
a.length = end - index;
rtl.arrayClone(type, srcarray, index, end, a, 0);
return a;
}
},
setCharAt : function (s, index, c) {
return s.substr(0, index) + c + s.substr(index + 1);
},
getResStr : function (mod, name) {
var rs = mod.$resourcestrings[name];
return rs.current ? rs.current : rs.org;
},
createSet : function () {
var s = {};
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] != null) {
s[arguments[i]] = true;
} else {
var first = arguments[i += 1];
var last = arguments[i += 1];
for (var j = first; j <= last; j++)
s[j] = true;
}
}
return s;
},
cloneSet : function (s) {
var r = {};
for (var key in s)
if (s.hasOwnProperty(key))
r[key] = true;
return r;
},
refSet : function (s) {
s.$shared = true;
return s;
},
includeSet : function (s, enumvalue) {
if (s.$shared)
s = cloneSet(s);
s[enumvalue] = true;
return s;
},
excludeSet : function (s, enumvalue) {
if (s.$shared)
s = cloneSet(s);
delete s[enumvalue];
return s;
},
diffSet : function (s, t) {
var r = {};
for (var key in s)
if (s.hasOwnProperty(key) && !t[key])
r[key] = true;
delete r.$shared;
return r;
},
unionSet : function (s, t) {
var r = {};
for (var key in s)
if (s.hasOwnProperty(key))
r[key] = true;
for (var key in t)
if (t.hasOwnProperty(key))
r[key] = true;
delete r.$shared;
return r;
},
intersectSet : function (s, t) {
var r = {};
for (var key in s)
if (s.hasOwnProperty(key) && t[key])
r[key] = true;
delete r.$shared;
return r;
},
symDiffSet : function (s, t) {
var r = {};
for (var key in s)
if (s.hasOwnProperty(key) && !t[key])
r[key] = true;
for (var key in t)
if (t.hasOwnProperty(key) && !s[key])
r[key] = true;
delete r.$shared;
return r;
},
eqSet : function (s, t) {
for (var key in s)
if (s.hasOwnProperty(key) && !t[key] && (key != '$shared'))
return false;
for (var key in t)
if (t.hasOwnProperty(key) && !s[key] && (key != '$shared'))
return false;
return true;
},
neSet : function (s, t) {
return !rtl.eqSet(s, t);
},
leSet : function (s, t) {
for (var key in s)
if (s.hasOwnProperty(key) && !t[key] && (key != '$shared'))
return false;
return true;
},
geSet : function (s, t) {
for (var key in t)
if (t.hasOwnProperty(key) && !s[key] && (key != '$shared'))
return false;
return true;
},
strSetLength : function (s, newlen) {
var oldlen = s.length;
if (oldlen > newlen) {
return s.substring(0, newlen);
} else if (s.repeat) {
// Note: repeat needs ECMAScript6!
return s + ' '.repeat(newlen - oldlen);
} else {
while (oldlen < newlen) {
s += ' ';
oldlen++;
};
return s;
}
},
spaceLeft : function (s, width) {
var l = s.length;
if (l >= width)
return s;
if (s.repeat) {
// Note: repeat needs ECMAScript6!
return ' '.repeat(width - l) + s;
} else {
while (l < width) {
s = ' ' + s;
l++;
};
};
},
floatToStr : function (d, w, p) {
// input 1-3 arguments: double, width, precision
if (arguments.length > 2) {
return rtl.spaceLeft(d.toFixed(p), w);
} else {
// exponent width
var pad = "";
var ad = Math.abs(d);
if (ad < 1.0e+10) {
pad = '00';
} else if (ad < 1.0e+100) {
pad = '0';
}
if (arguments.length < 2) {
w = 9;
} else if (w < 9) {
w = 9;
}
var p = w - 8;
var s = (d > 0 ? " " : "") + d.toExponential(p);
s = s.replace(/e(.)/, 'E$1' + pad);
return rtl.spaceLeft(s, w);
}
},
initRTTI : function () {
if (rtl.debug_rtti)
rtl.debug('initRTTI');
// base types
rtl.tTypeInfo = {
name : "tTypeInfo"
};
function newBaseTI(name, kind, ancestor) {
if (!ancestor)
ancestor = rtl.tTypeInfo;
if (rtl.debug_rtti)
rtl.debug('initRTTI.newBaseTI "' + name + '" ' + kind + ' ("' + ancestor.name + '")');
var t = Object.create(ancestor);
t.name = name;
t.kind = kind;
rtl[name] = t;
return t;
};
function newBaseInt(name, minvalue, maxvalue, ordtype) {
var t = newBaseTI(name, 1 /* tkInteger */
, rtl.tTypeInfoInteger);
t.minvalue = minvalue;
t.maxvalue = maxvalue;
t.ordtype = ordtype;
return t;
};
newBaseTI("tTypeInfoInteger", 1 /* tkInteger */
);
newBaseInt("shortint", -0x80, 0x7f, 0);
newBaseInt("byte", 0, 0xff, 1);
newBaseInt("smallint", -0x8000, 0x7fff, 2);
newBaseInt("word", 0, 0xffff, 3);
newBaseInt("longint", -0x80000000, 0x7fffffff, 4);
newBaseInt("longword", 0, 0xffffffff, 5);
newBaseInt("nativeint", -0x10000000000000, 0xfffffffffffff, 6);
newBaseInt("nativeuint", 0, 0xfffffffffffff, 7);
newBaseTI("char", 2 /* tkChar */
);
newBaseTI("string", 3 /* tkString */
);
newBaseTI("tTypeInfoEnum", 4 /* tkEnumeration */
, rtl.tTypeInfoInteger);
newBaseTI("tTypeInfoSet", 5 /* tkSet */
);
newBaseTI("double", 6 /* tkDouble */
);
newBaseTI("boolean", 7 /* tkBool */
);
newBaseTI("tTypeInfoProcVar", 8 /* tkProcVar */
);
newBaseTI("tTypeInfoMethodVar", 9 /* tkMethod */
, rtl.tTypeInfoProcVar);
newBaseTI("tTypeInfoArray", 10 /* tkArray */
);
newBaseTI("tTypeInfoDynArray", 11 /* tkDynArray */
);
newBaseTI("tTypeInfoPointer", 15 /* tkPointer */
);
var t = newBaseTI("pointer", 15 /* tkPointer */
, rtl.tTypeInfoPointer);
t.reftype = null;
newBaseTI("jsvalue", 16 /* tkJSValue */
);
newBaseTI("tTypeInfoRefToProcVar", 17 /* tkRefToProcVar */
, rtl.tTypeInfoProcVar);
// member kinds
rtl.tTypeMember = {};
function newMember(name, kind) {
var m = Object.create(rtl.tTypeMember);
m.name = name;
m.kind = kind;
rtl[name] = m;
};
newMember("tTypeMemberField", 1); // tmkField
newMember("tTypeMemberMethod", 2); // tmkMethod
newMember("tTypeMemberProperty", 3); // tmkProperty
// base object for storing members: a simple object
rtl.tTypeMembers = {};
// tTypeInfoStruct - base object for tTypeInfoClass and tTypeInfoRecord
var tis = newBaseTI("tTypeInfoStruct", 0);
tis.$addMember = function (name, ancestor, options) {
if (rtl.debug_rtti) {
if (!rtl.hasString(name) || (name.charAt() === '$'))
throw 'invalid member "' + name + '", this="' + this.name + '"';
if (!rtl.is(ancestor, rtl.tTypeMember))
throw 'invalid ancestor "' + ancestor + ':' + ancestor.name + '", "' + this.name + '.' + name + '"';
if ((options != undefined) && (typeof(options) != 'object'))
throw 'invalid options "' + options + '", "' + this.name + '.' + name + '"';
};
var t = Object.create(ancestor);
t.name = name;
this.members[name] = t;
this.names.push(name);
if (rtl.isObject(options)) {
for (var key in options)
if (options.hasOwnProperty(key))
t[key] = options[key];
};
return t;
};
tis.addField = function (name, type, options) {
var t = this.$addMember(name, rtl.tTypeMemberField, options);
if (rtl.debug_rtti) {
if (!rtl.is(type, rtl.tTypeInfo))
throw 'invalid type "' + type + '", "' + this.name + '.' + name + '"';
};
t.typeinfo = type;
this.fields.push(name);
return t;
};
tis.addFields = function () {
var i = 0;
while (i < arguments.length) {
var name = arguments[i++];
var type = arguments[i++];
if ((i < arguments.length) && (typeof(arguments[i]) === 'object')) {
this.addField(name, type, arguments[i++]);
} else {
this.addField(name, type);
};
};
};
tis.addMethod = function (name, methodkind, params, result, options) {
var t = this.$addMember(name, rtl.tTypeMemberMethod, options);
t.methodkind = methodkind;
t.procsig = rtl.newTIProcSig(params);
t.procsig.resulttype = result ? result : null;
this.methods.push(name);
return t;
};
tis.addProperty = function (name, flags, result, getter, setter, options) {
var t = this.$addMember(name, rtl.tTypeMemberProperty, options);
t.flags = flags;
t.typeinfo = result;
t.getter = getter;
t.setter = setter;
// Note: in options: params, stored, defaultvalue
if (rtl.isArray(t.params))
t.params = rtl.newTIParams(t.params);
this.properties.push(name);
if (!rtl.isString(t.stored))
t.stored = "";
return t;
};
tis.getField = function (index) {
return this.members[this.fields[index]];
};
tis.getMethod = function (index) {
return this.members[this.methods[index]];
};
tis.getProperty = function (index) {
return this.members[this.properties[index]];
};
newBaseTI("tTypeInfoRecord", 12 /* tkRecord */
, rtl.tTypeInfoStruct);
newBaseTI("tTypeInfoClass", 13 /* tkClass */
, rtl.tTypeInfoStruct);
newBaseTI("tTypeInfoClassRef", 14 /* tkClassRef */
);
},
tSectionRTTI : {
$module : null,
$inherited : function (name, ancestor, o) {
if (rtl.debug_rtti) {
rtl.debug('tSectionRTTI.newTI "' + (this.$module ? this.$module.$name : "(no module)")
+ '"."' + name + '" (' + ancestor.name + ') ' + (o ? 'init' : 'forward'));
};
var t = this[name];
if (t) {
if (!t.$forward)
throw 'duplicate type "' + name + '"';
if (!ancestor.isPrototypeOf(t))
throw 'typeinfo ancestor mismatch "' + name + '" ancestor="' + ancestor.name + '" t.name="' + t.name + '"';
} else {
t = Object.create(ancestor);
t.name = name;
t.module = this.module;
this[name] = t;
}
if (o) {
delete t.$forward;
for (var key in o)
if (o.hasOwnProperty(key))
t[key] = o[key];
} else {
t.$forward = true;
}
return t;
},
$Scope : function (name, ancestor, o) {
var t = this.$inherited(name, ancestor, o);
t.members = {};
t.names = [];
t.fields = [];
t.methods = [];
t.properties = [];
return t;
},
$TI : function (name, kind, o) {
var t = this.$inherited(name, rtl.tTypeInfo, o);
t.kind = kind;
return t;
},
$Int : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoInteger, o);
},
$Enum : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoEnum, o);
},
$Set : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoSet, o);
},
$StaticArray : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoArray, o);
},
$DynArray : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoDynArray, o);
},
$ProcVar : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoProcVar, o);
},
$RefToProcVar : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoRefToProcVar, o);
},
$MethodVar : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoMethodVar, o);
},
$Record : function (name, o) {
return this.$Scope(name, rtl.tTypeInfoRecord, o);
},
$Class : function (name, o) {
return this.$Scope(name, rtl.tTypeInfoClass, o);
},
$ClassRef : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoClassRef, o);
},
$Pointer : function (name, o) {
return this.$inherited(name, rtl.tTypeInfoPointer, o);
},
},
newTIParam : function (param) {
// param is an array, 0=name, 1=type, 2=optional flags
var t = {
name : param[0],
typeinfo : param[1],
flags : (rtl.isNumber(param[2]) ? param[2] : 0),
};
return t;
},
newTIParams : function (list) {
// list: optional array of [paramname,typeinfo,optional flags]
var params = [];
if (rtl.isArray(list)) {
for (var i = 0; i < list.length; i++)
params.push(rtl.newTIParam(list[i]));
};
return params;
},
newTIProcSig : function (params, result, flags) {
var s = {
params : rtl.newTIParams(params),
resulttype : result,
flags : flags
};
return s;
},
}
//--------------- compiler will emit this --------------------------//
rtl.module("Unit1", ["System", "Classes", "SysUtils"], function () {
"use strict";
var $mod = this;
rtl.createClass($mod, "TApplication", pas.System.TObject, function () {
this.RunApp = function () {
pas.System.Writeln("starting app");
};
});
$mod.$init = function () {};
});
Compiler features
Some of the features of the pas2js compiler are:
The compiler can display automatic warnings and hints for source code issues such as uninitialized variable references or variables that are declared but never referenced;
Fast compiler that compiles the source code of any project into JavaScript in seconds or even in milliseconds;
Any functions, procedures, constants, variables, types, classes, member variables, methods, or properties that aren't referenced in the source code for an application are not emitted. This keeps the size of the resultant application as small as possible;
The JavaScript emitted by the pas2js compiler is currently of level ECMAScript 5 and should run in any modern browser or in Node.js (target "nodejs"). It is available in 3 forms:
as a library;
as a command-line program; and
as a webserver.
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:
file cache: loading, caching files, converting to UTF-8.
file resolver: handling search paths, finding used units and include files.
scanner: reading tokens, handling compiler directives like $IfDef and $Include.
parser : reading the tokens, checking syntax, creating Pascal nodes.
resolver: resolving references, type checking and checking duplicate identifiers.
use analyzer: finding unused identifiers, emit hints and warning.
converter: translating Pascal nodes into JavaScript nodes.
compiler: handling config files, parameters, compiling recursively all used units, writes js.
command line interface: a small wrapper to embed the compiler into a console program.
library and interface: a small wrapper to embed the compiler into a library.
It transpiles from actual Pascal source, it has no intermediate .ppu files, that means all sources must always be available;
You can use third party tools to compress and obfuscate the emitted JavaScript, which makes it very difficult to read with the naked eye or reverse-engineer into any meaningful source code;
Through external class definitions, the compiler can use JavaScript classes:
All classes available in the JavaScript runtime, and in the browser are available through import units;
For Node.js, basic support for the nodejs runtime environment is available;
An import unit for jQuery is available (libjquery).