Creating a Pas2JS project has been completely automated in the development version of lazarus (see the Lazarus Integration section)The steps outlined here show how to do all the needed work manually.
Let's do a simple test. pas2js -?
Pas2JS Compiler version 1.5.1 [2019/07/29] for Win64 x86_64
Copyright (c) 2019 Free Pascal team.
Options:
Put + after a boolean switch option to enable it, - to disable it
@ : Read compiler options from file in addition to the default
pas2js.cfg
-B : Rebuild all
-d : Defines the symbol . Optional: -d:=
-i : Write information and halt. 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
-io : Write list of supported optimizations usable by -Oo
-it : Write list of supported targets usable by -T
-iJ : Write list of supported JavaScript identifiers -JoRTL-
-C : Code generation options. 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: Redirect output to file . UTF-8 encoded.
-FE: Set main output path to
-Fi: Add to include paths
-FN: add to namespaces. Namespaces with trailing - are removed.
Delphi calls this flag "unit scope names".
-Fu: Add to unit paths
-FU: Set unit output path to
-I : Add to include paths, same as -Fi
-J... Extra options of pas2js
-Jc : Write all JavaScript concatenated into the output file
-Je: Encode messages as .
-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: Insert JS file 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=: use x as "sourceRoot", prefix URL for source file
names.
-Jmbasedir=: 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: 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-=: set RTL identifier y to value z. See -iJ.
-Jr 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
-Jpcmd: 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: Add to foreign unit paths. Foreign units are not compiled.
-JU: This pas2js does not support PCU files
-l : Write logo
-MDelphi: Delphi 7 compatibility mode
-MObjFPC: FPC's Object Pascal compatibility mode (default)
-NS : obsolete: add to namespaces. Same as -FN
-n : Do not read the default config files
-o : Change main JavaScript file to , "." means stdout
-O : Optimizations:
-O- : Disable optimizations
-O1 : Level 1 optimizations (quick and debugger friendly)
-Oo: 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
-P : Set target processor. Case insensitive:
-Pecmascript5: default
-Pecmascript6
-S : Syntax options. is a combination of the following letters:
a : Turn on assertions
c : Support operators like C (*=,+=,/= and -=)
d : Same as -Mdelphi
m : Enables macro replacements
2 : Same as -Mobjfpc (default)
-SI : Set interface style to
-SIcom : COM, reference counted interface (default)
-SIcorba: CORBA interface
-T : Set target platform
-Tbrowser: default
-Tnodejs : add pas.run(), includes -Jc
-u : Undefines the symbol
-v : Be verbose. 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,: Do not show messages numbered and .
-? : Show this help
-h : Show this help
Environment variable PAS2JS_OPTS is parsed after default config and before
command line parameters.
Macros: Format is $Name, $Name$ or $Name()
$Pas2jsFullVersion: major.minor.release
$Pas2jsVersion: major.minor.release
$CfgDir: Use within a config file. The directory of this config file
$Env(): environment variable, e.g. $Env(HOME)
The pas2js command-line arguments are kept mostly the same as the FPC command-line arguments. Error messages are also in the same format. The compiler needs access to all sources, and so you need to specify the path to the sources of all used units. As for the FPC compiler, a configuration file is supported, which has the same syntax as the FPC config file. Basically, the command is the same as any FPC command line. The only thing that is different is the target: browser or node.js
Hello World
Now, let's just create a simple delphi based project with a single unit as shown at the following example:
Hello World
program Project1;
uses
JS, Web, Classes, SysUtils, Unit1;
var
app: TApp;
str: String;
begin
app:= TApp.Create;
str := app.EncryptStr('warleyalex', 2);
WriteLn(str);
WriteLn( app.DecryptStr('îÂäØÊòÂØÊð', 2) ); //warleyalex
end.
//-----------------------------------------------------
unit Unit1;
interface
type
TApp = class(TObject)
function EncryptStr(const S: String; Key: Integer): String;
function DecryptStr(const S: String; Key: Integer): String;
end;
implementation
function TApp.EncryptStr(const S: String; Key: Integer): String;
var I: Integer;
fuck: string;
begin
for I := 1 to Length(S) do begin
fuck := fuck + chr(ord(S[I]) * key);
end;
Result := fuck;
end;
function TApp.DecryptStr(const S: String; Key: Integer): String;
var I: Integer;
fuck : string;
begin
for I := 1 to Length(S) do begin
fuck := fuck + chr(ord(S[I]) div key) // DIV the char
end;
Result := fuck;
end;
end.
Here is how to compile:
pas2js -Jc -Jirtl.js project1.dpr
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;
},
}
rtl.module("System", [], function () {
"use strict";
var $mod = this;
var $impl = $mod.$impl;
rtl.createClass($mod, "TObject", null, function () {
this.$init = function () {};
this.$final = function () {};
this.Create = function () {};
this.AfterConstruction = function () {};
this.BeforeDestruction = function () {};
});
this.Writeln = function () {
var i = 0;
var l = 0;
var s = "";
l = rtl.length(arguments) - 1;
if ($impl.WriteCallBack != null) {
for (var $l1 = 0, $end2 = l; $l1 <= $end2; $l1++) {
i = $l1;
$impl.WriteCallBack(arguments[i], i === l);
};
} else {
s = $impl.WriteBuf;
for (var $l3 = 0, $end4 = l; $l3 <= $end4; $l3++) {
i = $l3;
s = s + ("" + arguments[i]);
};
console.log(s);
$impl.WriteBuf = "";
};
};
$mod.$init = function () {
rtl.exitcode = 0;
};
}, null, function () {
"use strict";
var $mod = this;
var $impl = $mod.$impl;
$impl.WriteBuf = "";
$impl.WriteCallBack = null;
});
rtl.module("JS", ["System"], function () {
"use strict";
var $mod = this;
});
rtl.module("SysUtils", ["System", "JS"], function () {
"use strict";
var $mod = this;
rtl.createClass($mod, "Exception", pas.System.TObject, function () {});
rtl.createClass($mod, "EExternal", $mod.Exception, function () {});
rtl.createClass($mod, "EInvalidCast", $mod.Exception, function () {});
rtl.createClass($mod, "EInterror", $mod.EExternal, function () {});
rtl.createClass($mod, "ERangeError", $mod.EInterror, function () {});
this.MonthDays = rtl.arraySetLength(null, 0, 2, 12);
$mod.$init = function () {
$mod.MonthDays[1][0] = 31;
$mod.MonthDays[1][1] = 29;
$mod.MonthDays[1][2] = 31;
$mod.MonthDays[1][3] = 30;
$mod.MonthDays[1][4] = 31;
$mod.MonthDays[1][5] = 30;
$mod.MonthDays[1][6] = 31;
$mod.MonthDays[1][7] = 31;
$mod.MonthDays[1][8] = 30;
$mod.MonthDays[1][9] = 31;
$mod.MonthDays[1][10] = 30;
$mod.MonthDays[1][11] = 31;
$mod.MonthDays[0][0] = 31;
$mod.MonthDays[0][1] = 28;
$mod.MonthDays[0][2] = 31;
$mod.MonthDays[0][3] = 30;
$mod.MonthDays[0][4] = 31;
$mod.MonthDays[0][5] = 30;
$mod.MonthDays[0][6] = 31;
$mod.MonthDays[0][7] = 31;
$mod.MonthDays[0][8] = 30;
$mod.MonthDays[0][9] = 31;
$mod.MonthDays[0][10] = 30;
$mod.MonthDays[0][11] = 31;
rtl.EInvalidCast = $mod.EInvalidCast;
};
});
rtl.module("Classes", ["System", "SysUtils"], function () {
"use strict";
var $mod = this;
var $impl = $mod.$impl;
$mod.$init = function () {
$impl.ClassList = Object.create(null);
};
}, ["JS"], function () {
"use strict";
var $mod = this;
var $impl = $mod.$impl;
$impl.ClassList = null;
});
rtl.module("Unit1", ["System"], function () {
"use strict";
var $mod = this;
rtl.createClass($mod, "TApp", pas.System.TObject, function () {
this.EncryptStr = function (S, Key) {
var Result = "";
var I = 0;
var fuck = "";
for (var $l1 = 1, $end2 = S.length; $l1 <= $end2; $l1++) {
I = $l1;
fuck = fuck + String.fromCharCode(S.charCodeAt(I - 1) * Key);
};
Result = fuck;
return Result;
};
this.DecryptStr = function (S, Key) {
var Result = "";
var I = 0;
var fuck = "";
for (var $l1 = 1, $end2 = S.length; $l1 <= $end2; $l1++) {
I = $l1;
fuck = fuck + String.fromCharCode(Math.floor(S.charCodeAt(I - 1) / Key));
};
Result = fuck;
return Result;
};
});
});
rtl.module("program", ["System", "JS", "Classes", "SysUtils", "Unit1"], function () {
"use strict";
var $mod = this;
this.app = null;
this.str = "";
$mod.$main = function () {
$mod.app = pas.Unit1.TApp.$create("Create");
$mod.str = $mod.app.EncryptStr("warleyalex", 2);
pas.System.Writeln($mod.str);
pas.System.Writeln($mod.app.DecryptStr("îÂäØÊòÂØÊð", 2));
};
});
When compiled succesfully, the code can be run in the browser by opening a html file in the browser with the following content:
For node.js, the compiler will insert the call to rtl.run() automatically at the end of the generated Javascript file. The pas2js RTL is still in flux. There is a basic Object Pascal RTL, several units from the FPC Packages are also available:
system
sysutils
Math
strutils
rtlconst
classes
contnrs
DB (yes, TDataset)
fpcunit testsuite
custapp
restconnection
js (javascript system objects)
web (browser provided objects)
libjquery (jquery is available too)
nodejs (basic node runtime environment)
typinfo
objpas
browserconsole (support writeln)
dateutils
browserapp
nodejsapp
JS file size
Why is a simple hello world program so big?
This is mainly due to the used rtl.js. The rtl.js contains code for Pascal modules, classes, RTTI, sets, range checks, etc and is written with big WebApps in mind, not for scripts with a few lines of code.
You can use a Javascript minifier to reduce the created Javascript
You can create your own minified rtl.js by removing all functions you don't need. Eventually this will be done automatically by pas2js.