rtl.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. This file is part of the Free Pascal pas2js tool.
  3. Copyright (c) 2017 Mattias Gaertner
  4. Basic RTL for pas2js programs.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. */
  11. var pas = {};
  12. var rtl = {
  13. quiet: false,
  14. debug_load_units: false,
  15. m_loading: 0,
  16. m_loading_intf: 1,
  17. m_intf_loaded: 2,
  18. m_loading_impl: 3, // loading all used unit
  19. m_initializing: 4, // running initialization
  20. m_initialized: 5,
  21. debug: function(){
  22. if (rtl.quiet || !console || !console.log) return;
  23. console.log(arguments);
  24. },
  25. error: function(s){
  26. rtl.debug('Error: ',s);
  27. throw s;
  28. },
  29. warn: function(s){
  30. rtl.debug('Warn: ',s);
  31. },
  32. isArray: function(a) {
  33. return a instanceof Array;
  34. },
  35. isNumber: function(n){
  36. return typeof(n)=="number";
  37. },
  38. isInteger: function(A){
  39. return Math.floor(A)===A;
  40. },
  41. isBoolean: function(b){
  42. return typeof(b)=="boolean";
  43. },
  44. isString: function(s){
  45. return typeof(s)=="string";
  46. },
  47. isObject: function(o){
  48. return typeof(o)=="object";
  49. },
  50. isFunction: function(f){
  51. return typeof(f)=="function";
  52. },
  53. isNull: function(o){
  54. return (o==null && typeof(o)=='object') || o==undefined;
  55. },
  56. isRecord: function(r){
  57. return (typeof(r)=="function") && (typeof(r.$create) == "function");
  58. },
  59. isClass: function(c){
  60. return (typeof(o)=="object") && (o.$class == o);
  61. },
  62. isClassInstance: function(c){
  63. return (typeof(o)=="object") && (o.$class == Object.getPrototypeOf(o));
  64. },
  65. hasString: function(s){
  66. return rtl.isString(s) && (s.length>0);
  67. },
  68. module: function(module_name, intfuseslist, code, impluseslist){
  69. if (rtl.debug_load_units) rtl.debug('rtl.module name="'+module_name+'" intfuses='+intfuseslist+' impluses='+impluseslist);
  70. if (!rtl.hasString(module_name)) rtl.error('invalid module name "'+module_name+'"');
  71. if (!rtl.isArray(intfuseslist)) rtl.error('invalid interface useslist of "'+module_name+'"');
  72. if (!rtl.isFunction(code)) rtl.error('invalid module code of "'+module_name+'"');
  73. if ((impluseslist!=undefined) && !rtl.isArray(impluseslist)) rtl.error('invalid implementation useslist of "'+module_name+'"');
  74. if (pas[module_name])
  75. rtl.error('module "'+module_name+'" already registered');
  76. var module = pas[module_name] = {
  77. $name: module_name,
  78. $intfuseslist: intfuseslist,
  79. $impluseslist: impluseslist,
  80. $state: rtl.m_loading,
  81. $code: code
  82. };
  83. },
  84. run: function(module_name){
  85. if (module_name==undefined) module_name='program';
  86. if (rtl.debug_load_units) rtl.debug('rtl.run module="'+module_name+'"');
  87. var module = pas[module_name];
  88. rtl.loadintf(module);
  89. rtl.loadimpl(module);
  90. if (module_name=='program'){
  91. if (rtl.debug_load_units) rtl.debug('running $main');
  92. pas.program.$main();
  93. }
  94. return pas.System.ExitCode;
  95. },
  96. loadintf: function(module){
  97. if (module.state>rtl.m_loading_intf) return; // already finished
  98. if (rtl.debug_load_units) rtl.debug('loadintf: '+module.$name);
  99. if (module.$state==rtl.m_loading_intf)
  100. rtl.error('unit cycle detected "'+module.$name+'"');
  101. module.$state=rtl.m_loading_intf;
  102. // load interfaces of interface useslist
  103. rtl.loaduseslist(module,module.$intfuseslist,rtl.loadintf);
  104. // run interface
  105. if (rtl.debug_load_units) rtl.debug('loadintf: run intf of '+module.$name);
  106. module.$code(module.$intfuseslist,module);
  107. // success
  108. module.$state=rtl.m_intf_loaded;
  109. // Note: units only used in implementations are not yet loaded (not even their interfaces)
  110. },
  111. loaduseslist: function(module,useslist,f){
  112. if (useslist==undefined) return;
  113. for (var i in useslist){
  114. var unitname=useslist[i];
  115. if (rtl.debug_load_units) rtl.debug('loaduseslist of "'+module.name+'" uses="'+unitname+'"');
  116. if (pas[unitname]==undefined)
  117. rtl.error('module "'+module.$name+'" misses "'+unitname+'"');
  118. f(pas[unitname]);
  119. }
  120. },
  121. loadimpl: function(module){
  122. if (module.$state>=rtl.m_loading_impl) return; // already processing
  123. if (module.$state<rtl.m_loading_intf) rtl.loadintf(module);
  124. if (rtl.debug_load_units) rtl.debug('loadimpl: '+module.$name+' load uses');
  125. module.$state=rtl.m_loading_impl;
  126. // load implementation of interfaces useslist
  127. rtl.loaduseslist(module,module.$intfuseslist,rtl.loadimpl);
  128. // load implementation of implementation useslist
  129. rtl.loaduseslist(module,module.$impluseslist,rtl.loadimpl);
  130. // Note: At this point all interfaces used by this unit are loaded. If
  131. // there are implementation uses cycles some used units might not yet be
  132. // initialized. This is by design.
  133. // run initialization
  134. if (rtl.debug_load_units) rtl.debug('loadimpl: '+module.$name+' run init');
  135. module.$state=rtl.m_initializing;
  136. if (rtl.isFunction(module.$init))
  137. module.$init();
  138. // unit initialized
  139. module.$state=rtl.m_initialized;
  140. },
  141. createCallback: function(scope, fnname){
  142. var cb = function(){
  143. return scope[fnname].apply(scope,arguments);
  144. };
  145. cb.scope = scope;
  146. cb.fnname = fnname;
  147. return cb;
  148. },
  149. cloneCallback: function(cb){
  150. return rtl.createCallback(cb.scope,cb.fnname);
  151. },
  152. eqCallback: function(a,b){
  153. if (a==null){
  154. return (b==null);
  155. } else {
  156. return (b!=null) && (a.scope==b.scope) && (a.fnname==b.fnname);
  157. }
  158. },
  159. createClass: function(owner,name,ancestor,initfn){
  160. var c = null;
  161. if (ancestor != null){
  162. c = Object.create(ancestor);
  163. c.$ancestor = ancestor; // c.$ancestor == Object.getPrototypeOf(c)
  164. } else {
  165. c = {};
  166. c.$create = function(fnname,args){
  167. var o = Object.create(this);
  168. o.$class = this; // Note: o.$class == Object.getPrototypeOf(o)
  169. if (args == undefined) args = [];
  170. o.$init();
  171. o[fnname].apply(o,args);
  172. o.AfterConstruction();
  173. return o;
  174. };
  175. c.$destroy = function(fnname){
  176. this.BeforeDestruction();
  177. this[fnname]();
  178. this.$final;
  179. };
  180. };
  181. c.$classname = name;
  182. c.$name = owner.$name+'.'+name;
  183. c.$unitname = rtl.isString(owner.$unitname) ? owner.$unitname : owner.$name;
  184. owner[name] = c;
  185. initfn.call(c);
  186. },
  187. as: function(instance,typ){
  188. if(typ.isPrototypeOf(instance)) return instance;
  189. throw pas.System.EInvalidCast.$create("create");
  190. },
  191. arraySetLength: function(arr,newlength,defaultvalue){
  192. var oldlen = arr.length;
  193. if (oldlen==newlength) return;
  194. arr.length = newlength;
  195. if (rtl.isArray(defaultvalue)){
  196. for (var i=oldlen; i<newlength; i++) arr[i]=[]; // new array
  197. } else if (rtl.isFunction(defaultvalue)){
  198. for (var i=oldlen; i<newlength; i++) arr[i]=new defaultvalue(); // new record
  199. } else {
  200. for (var i=oldlen; i<newlength; i++) arr[i]=defaultvalue;
  201. }
  202. return arr;
  203. },
  204. arrayNewMultiDim: function(dims,defaultvalue){
  205. function create(dim){
  206. if (dim == dims.length-1){
  207. return rtl.arraySetLength([],dims[dim],defaultvalue);
  208. }
  209. var a = [];
  210. var count = dims[dim];
  211. a.length = count;
  212. for(var i=0; i<count; i++) a[i] = create(dim+1);
  213. return a;
  214. };
  215. return create(0);
  216. },
  217. setCharAt: function(s,index,c){
  218. return s.substr(0,index)+c+s.substr(index+1);
  219. },
  220. createSet: function(){
  221. var s = {};
  222. for (var i=0; i<arguments.length; i++){
  223. if (arguments[i]!=null){
  224. s[arguments[i]]=true;
  225. } else {
  226. var first=arguments[i+=1];
  227. var last=arguments[i+=1];
  228. for(var j=first; j<=last; j++) s[j]=true;
  229. }
  230. }
  231. return s;
  232. },
  233. cloneSet: function(s){
  234. var r = {};
  235. for (var key in s) if (s.hasOwnProperty(key)) r[key]=true;
  236. return r;
  237. },
  238. refSet: function(s){
  239. s.$shared = true;
  240. return s;
  241. },
  242. includeSet: function(s,enumvalue){
  243. if (s.$shared) s = cloneSet(s);
  244. s[enumvalue] = true;
  245. return s;
  246. },
  247. excludeSet: function(s,enumvalue){
  248. if (s.$shared) s = cloneSet(s);
  249. delete s[enumvalue];
  250. return s;
  251. },
  252. diffSet: function(s,t){
  253. var r = {};
  254. for (var key in s) if (s.hasOwnProperty(key) && !t[key]) r[key]=true;
  255. delete r.$shared;
  256. return r;
  257. },
  258. unionSet: function(s,t){
  259. var r = {};
  260. for (var key in s) if (s.hasOwnProperty(key)) r[key]=true;
  261. for (var key in t) if (t.hasOwnProperty(key)) r[key]=true;
  262. delete r.$shared;
  263. return r;
  264. },
  265. intersectSet: function(s,t){
  266. var r = {};
  267. for (var key in s) if (s.hasOwnProperty(key) && t[key]) r[key]=true;
  268. delete r.$shared;
  269. return r;
  270. },
  271. symDiffSet: function(s,t){
  272. var r = {};
  273. for (var key in s) if (s.hasOwnProperty(key) && !t[key]) r[key]=true;
  274. for (var key in t) if (t.hasOwnProperty(key) && !s[key]) r[key]=true;
  275. delete r.$shared;
  276. return r;
  277. },
  278. eqSet: function(s,t){
  279. for (var key in s) if (s.hasOwnProperty(key) && !t[key] && (key!='$shared')) return false;
  280. for (var key in t) if (t.hasOwnProperty(key) && !s[key] && (key!='$shared')) return false;
  281. return true;
  282. },
  283. neSet: function(s,t){
  284. return !rtl.eqSet(s,t);
  285. },
  286. leSet: function(s,t){
  287. for (var key in s) if (s.hasOwnProperty(key) && !t[key] && (key!='$shared')) return false;
  288. return true;
  289. },
  290. geSet: function(s,t){
  291. for (var key in t) if (t.hasOwnProperty(key) && !s[key] && (key!='$shared')) return false;
  292. return true;
  293. },
  294. }