rtl.js 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145
  1. var pas = {};
  2. var rtl = {
  3. quiet: false,
  4. debug_load_units: false,
  5. debug_rtti: false,
  6. debug: function(){
  7. if (rtl.quiet || !console || !console.log) return;
  8. console.log(arguments);
  9. },
  10. error: function(s){
  11. rtl.debug('Error: ',s);
  12. throw s;
  13. },
  14. warn: function(s){
  15. rtl.debug('Warn: ',s);
  16. },
  17. hasString: function(s){
  18. return rtl.isString(s) && (s.length>0);
  19. },
  20. isArray: function(a) {
  21. return Array.isArray(a);
  22. },
  23. isFunction: function(f){
  24. return typeof(f)==="function";
  25. },
  26. isModule: function(m){
  27. return rtl.isObject(m) && rtl.hasString(m.$name) && (pas[m.$name]===m);
  28. },
  29. isImplementation: function(m){
  30. return rtl.isObject(m) && rtl.isModule(m.$module) && (m.$module.$impl===m);
  31. },
  32. isNumber: function(n){
  33. return typeof(n)==="number";
  34. },
  35. isObject: function(o){
  36. var s=typeof(o);
  37. return (typeof(o)==="object") && (o!=null);
  38. },
  39. isString: function(s){
  40. return typeof(s)==="string";
  41. },
  42. getNumber: function(n){
  43. return typeof(n)==="number"?n:NaN;
  44. },
  45. getChar: function(c){
  46. return ((typeof(c)==="string") && (c.length===1)) ? c : "";
  47. },
  48. getObject: function(o){
  49. return ((typeof(o)==="object") || (typeof(o)==='function')) ? o : null;
  50. },
  51. isPasClass: function(type){
  52. return (rtl.isObject(type) && type.hasOwnProperty('$classname') && rtl.isObject(type.$module));
  53. },
  54. isPasClassInstance: function(type){
  55. return (rtl.isObject(type) && rtl.isPasClass(type.$class));
  56. },
  57. m_loading: 0,
  58. m_loading_intf: 1,
  59. m_intf_loaded: 2,
  60. m_loading_impl: 3, // loading all used unit
  61. m_initializing: 4, // running initialization
  62. m_initialized: 5,
  63. module: function(module_name, intfuseslist, intfcode, impluseslist, implcode){
  64. if (rtl.debug_load_units) rtl.debug('rtl.module name="'+module_name+'" intfuses='+intfuseslist+' impluses='+impluseslist+' hasimplcode='+rtl.isFunction(implcode));
  65. if (!rtl.hasString(module_name)) rtl.error('invalid module name "'+module_name+'"');
  66. if (!rtl.isArray(intfuseslist)) rtl.error('invalid interface useslist of "'+module_name+'"');
  67. if (!rtl.isFunction(intfcode)) rtl.error('invalid interface code of "'+module_name+'"');
  68. if (!(impluseslist==undefined) && !rtl.isArray(impluseslist)) rtl.error('invalid implementation useslist of "'+module_name+'"');
  69. if (!(implcode==undefined) && !rtl.isFunction(implcode)) rtl.error('invalid implementation code of "'+module_name+'"');
  70. if (pas[module_name])
  71. rtl.error('module "'+module_name+'" is already registered');
  72. var module = pas[module_name] = {
  73. $name: module_name,
  74. $intfuseslist: intfuseslist,
  75. $impluseslist: impluseslist,
  76. $state: rtl.m_loading,
  77. $intfcode: intfcode,
  78. $implcode: implcode,
  79. $impl: null,
  80. $rtti: Object.create(rtl.tSectionRTTI)
  81. };
  82. module.$rtti.$module = module;
  83. if (implcode) module.$impl = {
  84. $module: module,
  85. $rtti: module.$rtti
  86. };
  87. },
  88. exitcode: 0,
  89. run: function(module_name){
  90. function doRun(){
  91. if (!rtl.hasString(module_name)) module_name='program';
  92. if (rtl.debug_load_units) rtl.debug('rtl.run module="'+module_name+'"');
  93. rtl.initRTTI();
  94. var module = pas[module_name];
  95. if (!module) rtl.error('rtl.run module "'+module_name+'" missing');
  96. rtl.loadintf(module);
  97. rtl.loadimpl(module);
  98. if (module_name=='program'){
  99. if (rtl.debug_load_units) rtl.debug('running $main');
  100. var r = pas.program.$main();
  101. if (rtl.isNumber(r)) rtl.exitcode = r;
  102. }
  103. }
  104. if (rtl.showUncaughtExceptions) {
  105. try{
  106. doRun();
  107. } catch(re) {
  108. var errMsg = re.hasOwnProperty('$class') ? re.$class.$classname : '';
  109. errMsg += ((errMsg) ? ': ' : '') + (re.hasOwnProperty('fMessage') ? re.fMessage : re);
  110. alert('Uncaught Exception : '+errMsg);
  111. rtl.exitCode = 216;
  112. }
  113. } else {
  114. doRun();
  115. }
  116. return rtl.exitcode;
  117. },
  118. loadintf: function(module){
  119. if (module.$state>rtl.m_loading_intf) return; // already finished
  120. if (rtl.debug_load_units) rtl.debug('loadintf: "'+module.$name+'"');
  121. if (module.$state===rtl.m_loading_intf)
  122. rtl.error('unit cycle detected "'+module.$name+'"');
  123. module.$state=rtl.m_loading_intf;
  124. // load interfaces of interface useslist
  125. rtl.loaduseslist(module,module.$intfuseslist,rtl.loadintf);
  126. // run interface
  127. if (rtl.debug_load_units) rtl.debug('loadintf: run intf of "'+module.$name+'"');
  128. module.$intfcode(module.$intfuseslist);
  129. // success
  130. module.$state=rtl.m_intf_loaded;
  131. // Note: units only used in implementations are not yet loaded (not even their interfaces)
  132. },
  133. loaduseslist: function(module,useslist,f){
  134. if (useslist==undefined) return;
  135. for (var i in useslist){
  136. var unitname=useslist[i];
  137. if (rtl.debug_load_units) rtl.debug('loaduseslist of "'+module.$name+'" uses="'+unitname+'"');
  138. if (pas[unitname]==undefined)
  139. rtl.error('module "'+module.$name+'" misses "'+unitname+'"');
  140. f(pas[unitname]);
  141. }
  142. },
  143. loadimpl: function(module){
  144. if (module.$state>=rtl.m_loading_impl) return; // already processing
  145. if (module.$state<rtl.m_intf_loaded) rtl.error('loadimpl: interface not loaded of "'+module.$name+'"');
  146. if (rtl.debug_load_units) rtl.debug('loadimpl: load uses of "'+module.$name+'"');
  147. module.$state=rtl.m_loading_impl;
  148. // load interfaces of implementation useslist
  149. rtl.loaduseslist(module,module.$impluseslist,rtl.loadintf);
  150. // load implementation of interfaces useslist
  151. rtl.loaduseslist(module,module.$intfuseslist,rtl.loadimpl);
  152. // load implementation of implementation useslist
  153. rtl.loaduseslist(module,module.$impluseslist,rtl.loadimpl);
  154. // Note: At this point all interfaces used by this unit are loaded. If
  155. // there are implementation uses cycles some used units might not yet be
  156. // initialized. This is by design.
  157. // run implementation
  158. if (rtl.debug_load_units) rtl.debug('loadimpl: run impl of "'+module.$name+'"');
  159. if (rtl.isFunction(module.$implcode)) module.$implcode(module.$impluseslist);
  160. // run initialization
  161. if (rtl.debug_load_units) rtl.debug('loadimpl: run init of "'+module.$name+'"');
  162. module.$state=rtl.m_initializing;
  163. if (rtl.isFunction(module.$init)) module.$init();
  164. // unit initialized
  165. module.$state=rtl.m_initialized;
  166. },
  167. createCallback: function(scope, fn){
  168. var cb;
  169. if (typeof(fn)==='string'){
  170. cb = function(){
  171. return scope[fn].apply(scope,arguments);
  172. };
  173. } else {
  174. cb = function(){
  175. return fn.apply(scope,arguments);
  176. };
  177. };
  178. cb.scope = scope;
  179. cb.fn = fn;
  180. return cb;
  181. },
  182. cloneCallback: function(cb){
  183. return rtl.createCallback(cb.scope,cb.fn);
  184. },
  185. eqCallback: function(a,b){
  186. // can be a function or a function wrapper
  187. if (a==b){
  188. return true;
  189. } else {
  190. return (a!=null) && (b!=null) && (a.fn) && (a.scope===b.scope) && (a.fn==b.fn);
  191. }
  192. },
  193. initClass: function(c,parent,name,initfn){
  194. parent[name] = c;
  195. c.$classname = name;
  196. if ((parent.$module) && (parent.$module.$impl===parent)) parent=parent.$module;
  197. c.$parent = parent;
  198. c.$fullname = parent.$name+'.'+name;
  199. if (rtl.isModule(parent)){
  200. c.$module = parent;
  201. c.$name = name;
  202. } else {
  203. c.$module = parent.$module;
  204. c.$name = parent.name+'.'+name;
  205. };
  206. // rtti
  207. if (rtl.debug_rtti) rtl.debug('initClass '+c.$fullname);
  208. var t = c.$module.$rtti.$Class(c.$name,{ "class": c, module: parent });
  209. c.$rtti = t;
  210. if (rtl.isObject(c.$ancestor)) t.ancestor = c.$ancestor.$rtti;
  211. if (!t.ancestor) t.ancestor = null;
  212. // init members
  213. initfn.call(c);
  214. },
  215. createClass: function(parent,name,ancestor,initfn){
  216. // create a normal class,
  217. // ancestor must be null or a normal class,
  218. // the root ancestor can be an external class
  219. var c = null;
  220. if (ancestor != null){
  221. c = Object.create(ancestor);
  222. c.$ancestor = ancestor;
  223. // Note:
  224. // if root is an "object" then c.$ancestor === Object.getPrototypeOf(c)
  225. // if root is a "function" then c.$ancestor === c.__proto__, Object.getPrototypeOf(c) returns the root
  226. } else {
  227. c = {};
  228. c.$create = function(fnname,args){
  229. if (args == undefined) args = [];
  230. var o = Object.create(this);
  231. o.$class = this; // Note: o.$class === Object.getPrototypeOf(o)
  232. o.$init();
  233. try{
  234. o[fnname].apply(o,args);
  235. o.AfterConstruction();
  236. } catch($e){
  237. o.$destroy;
  238. throw $e;
  239. }
  240. return o;
  241. };
  242. c.$destroy = function(fnname){
  243. this.BeforeDestruction();
  244. this[fnname]();
  245. this.$final;
  246. };
  247. };
  248. rtl.initClass(c,parent,name,initfn);
  249. },
  250. createClassExt: function(parent,name,ancestor,newinstancefnname,initfn){
  251. // Create a class using an external ancestor.
  252. // If newinstancefnname is given, use that function to create the new object.
  253. // If exist call BeforeDestruction and AfterConstruction.
  254. var c = null;
  255. c = Object.create(ancestor);
  256. c.$create = function(fnname,args){
  257. if (args == undefined) args = [];
  258. var o = null;
  259. if (newinstancefnname.length>0){
  260. o = this[newinstancefnname](fnname,args);
  261. } else {
  262. o = Object.create(this);
  263. }
  264. o.$class = this; // Note: o.$class === Object.getPrototypeOf(o)
  265. o.$init();
  266. try{
  267. o[fnname].apply(o,args);
  268. if (o.AfterConstruction) o.AfterConstruction();
  269. } catch($e){
  270. o.$destroy;
  271. throw $e;
  272. }
  273. return o;
  274. };
  275. c.$destroy = function(fnname){
  276. if (this.BeforeDestruction) this.BeforeDestruction();
  277. this[fnname]();
  278. this.$final;
  279. };
  280. rtl.initClass(c,parent,name,initfn);
  281. },
  282. tObjectDestroy: "Destroy",
  283. free: function(obj,name){
  284. if (obj[name]==null) return;
  285. obj[name].$destroy(rtl.tObjectDestroy);
  286. obj[name]=null;
  287. },
  288. freeLoc: function(obj){
  289. if (obj==null) return;
  290. obj.$destroy(rtl.tObjectDestroy);
  291. return null;
  292. },
  293. is: function(instance,type){
  294. return type.isPrototypeOf(instance) || (instance===type);
  295. },
  296. isExt: function(instance,type,mode){
  297. // mode===1 means instance must be a Pascal class instance
  298. // mode===2 means instance must be a Pascal class
  299. // Notes:
  300. // isPrototypeOf and instanceof return false on equal
  301. // isPrototypeOf does not work for Date.isPrototypeOf(new Date())
  302. // so if isPrototypeOf is false test with instanceof
  303. // instanceof needs a function on right side
  304. if (instance == null) return false; // Note: ==null checks for undefined too
  305. if ((typeof(type) !== 'object') && (typeof(type) !== 'function')) return false;
  306. if (instance === type){
  307. if (mode===1) return false;
  308. if (mode===2) return rtl.isPasClass(instance);
  309. return true;
  310. }
  311. if (type.isPrototypeOf && type.isPrototypeOf(instance)){
  312. if (mode===1) return rtl.isPasClassInstance(instance);
  313. if (mode===2) return rtl.isPasClass(instance);
  314. return true;
  315. }
  316. if ((typeof type == 'function') && (instance instanceof type)) return true;
  317. return false;
  318. },
  319. Exception: null,
  320. EInvalidCast: null,
  321. EAbstractError: null,
  322. ERangeError: null,
  323. raiseE: function(typename){
  324. var t = rtl[typename];
  325. if (t==null){
  326. var mod = pas.SysUtils;
  327. if (!mod) mod = pas.sysutils;
  328. if (mod){
  329. t = mod[typename];
  330. if (!t) t = mod[typename.toLowerCase()];
  331. if (!t) t = mod['Exception'];
  332. if (!t) t = mod['exception'];
  333. }
  334. }
  335. if (t){
  336. if (t.Create){
  337. throw t.$create("Create");
  338. } else if (t.create){
  339. throw t.$create("create");
  340. }
  341. }
  342. if (typename === "EInvalidCast") throw "invalid type cast";
  343. if (typename === "EAbstractError") throw "Abstract method called";
  344. if (typename === "ERangeError") throw "range error";
  345. throw typename;
  346. },
  347. as: function(instance,type){
  348. if((instance === null) || rtl.is(instance,type)) return instance;
  349. rtl.raiseE("EInvalidCast");
  350. },
  351. asExt: function(instance,type,mode){
  352. if((instance === null) || rtl.isExt(instance,type,mode)) return instance;
  353. rtl.raiseE("EInvalidCast");
  354. },
  355. createInterface: function(module, name, guid, fnnames, ancestor, initfn){
  356. //console.log('createInterface name="'+name+'" guid="'+guid+'" names='+fnnames);
  357. var i = ancestor?Object.create(ancestor):{};
  358. module[name] = i;
  359. i.$module = module;
  360. i.$name = name;
  361. i.$fullname = module.$name+'.'+name;
  362. i.$guid = guid;
  363. i.$guidr = null;
  364. i.$names = fnnames?fnnames:[];
  365. if (rtl.isFunction(initfn)){
  366. // rtti
  367. if (rtl.debug_rtti) rtl.debug('createInterface '+i.$fullname);
  368. var t = i.$module.$rtti.$Interface(name,{ "interface": i, module: module });
  369. i.$rtti = t;
  370. if (ancestor) t.ancestor = ancestor.$rtti;
  371. if (!t.ancestor) t.ancestor = null;
  372. initfn.call(i);
  373. }
  374. return i;
  375. },
  376. strToGUIDR: function(s,g){
  377. var p = 0;
  378. function n(l){
  379. var h = s.substr(p,l);
  380. p+=l;
  381. return parseInt(h,16);
  382. }
  383. p+=1; // skip {
  384. g.D1 = n(8);
  385. p+=1; // skip -
  386. g.D2 = n(4);
  387. p+=1; // skip -
  388. g.D3 = n(4);
  389. p+=1; // skip -
  390. if (!g.D4) g.D4=[];
  391. g.D4[0] = n(2);
  392. g.D4[1] = n(2);
  393. p+=1; // skip -
  394. for(var i=2; i<8; i++) g.D4[i] = n(2);
  395. return g;
  396. },
  397. guidrToStr: function(g){
  398. if (g.$intf) return g.$intf.$guid;
  399. function h(n,digits){
  400. return ("0000000"+n.toString(16).toUpperCase()).slice(-digits);
  401. }
  402. var s='{'+h(g.D1,8)+'-'+h(g.D2,4)+'-'+h(g.D3,4)+'-'+h(g.D4[0],2)+h(g.D4[1],2)+'-';
  403. for (var i=2; i<8; i++) s+=h(g.D4[i],2);
  404. s+='}';
  405. return s;
  406. },
  407. createTGUID: function(guid){
  408. var TGuid = (pas.System)?pas.System.TGuid:pas.system.tguid;
  409. var g = rtl.strToGUIDR(guid,new TGuid());
  410. return g;
  411. },
  412. getIntfGUIDR: function(intfTypeOrVar){
  413. if (!intfTypeOrVar) return null;
  414. if (!intfTypeOrVar.$guidr){
  415. var g = rtl.createTGUID(intfTypeOrVar.$guid);
  416. if (!intfTypeOrVar.hasOwnProperty('$guid')) intfTypeOrVar = Object.getPrototypeOf(intfTypeOrVar);
  417. g.$intf = intfTypeOrVar;
  418. intfTypeOrVar.$guidr = g;
  419. }
  420. return intfTypeOrVar.$guidr;
  421. },
  422. addIntf: function (aclass, intf, map){
  423. function jmp(fn){
  424. if (typeof(fn)==="function"){
  425. return function(){ return fn.apply(this.$o,arguments); };
  426. } else {
  427. return function(){ rtl.raiseE('EAbstractError'); };
  428. }
  429. }
  430. if(!map) map = {};
  431. var t = intf;
  432. var item = Object.create(t);
  433. aclass.$intfmaps[intf.$guid] = item;
  434. do{
  435. var names = t.$names;
  436. if (!names) break;
  437. for (var i=0; i<names.length; i++){
  438. var intfname = names[i];
  439. var fnname = map[intfname];
  440. if (!fnname) fnname = intfname;
  441. //console.log('addIntf: intftype='+t.$name+' index='+i+' intfname="'+intfname+'" fnname="'+fnname+'" proc='+typeof(fn));
  442. item[intfname] = jmp(aclass[fnname]);
  443. }
  444. t = Object.getPrototypeOf(t);
  445. }while(t!=null);
  446. },
  447. getIntfG: function (obj, guid, query){
  448. if (!obj) return null;
  449. //console.log('getIntfG: obj='+obj.$classname+' guid='+guid+' query='+query);
  450. // search
  451. var maps = obj.$intfmaps;
  452. if (!maps) return null;
  453. var item = maps[guid];
  454. if (!item) return null;
  455. // check delegation
  456. //console.log('getIntfG: obj='+obj.$classname+' guid='+guid+' query='+query+' item='+typeof(item));
  457. if (typeof item === 'function') return item.call(obj); // COM: contains _AddRef
  458. // check cache
  459. var intf = null;
  460. if (obj.$interfaces){
  461. intf = obj.$interfaces[guid];
  462. //console.log('getIntfG: obj='+obj.$classname+' guid='+guid+' cache='+typeof(intf));
  463. }
  464. if (!intf){ // intf can be undefined!
  465. intf = Object.create(item);
  466. intf.$o = obj;
  467. if (!obj.$interfaces) obj.$interfaces = {};
  468. obj.$interfaces[guid] = intf;
  469. }
  470. if (typeof(query)==='object'){
  471. // called by queryIntfT
  472. var o = null;
  473. if (intf.QueryInterface(rtl.getIntfGUIDR(query),
  474. {get:function(){ return o; }, set:function(v){ o=v; }}) === 0){
  475. return o;
  476. } else {
  477. return null;
  478. }
  479. } else if(query===2){
  480. // called by TObject.GetInterfaceByStr
  481. if (intf.$kind === 'com') intf._AddRef();
  482. }
  483. return intf;
  484. },
  485. getIntfT: function(obj,intftype){
  486. return rtl.getIntfG(obj,intftype.$guid);
  487. },
  488. queryIntfT: function(obj,intftype){
  489. return rtl.getIntfG(obj,intftype.$guid,intftype);
  490. },
  491. queryIntfIsT: function(obj,intftype){
  492. var i = rtl.queryIntfG(obj,intftype.$guid);
  493. if (!i) return false;
  494. if (i.$kind === 'com') i._Release();
  495. return true;
  496. },
  497. asIntfT: function (obj,intftype){
  498. var i = rtl.getIntfG(obj,intftype.$guid);
  499. if (i!==null) return i;
  500. rtl.raiseEInvalidCast();
  501. },
  502. intfIsClass: function(intf,classtype){
  503. return (intf!=null) && (rtl.is(intf.$o,classtype));
  504. },
  505. intfAsClass: function(intf,classtype){
  506. if (intf==null) return null;
  507. return rtl.as(intf.$o,classtype);
  508. },
  509. intfToClass: function(intf,classtype){
  510. if ((intf!==null) && rtl.is(intf.$o,classtype)) return intf.$o;
  511. return null;
  512. },
  513. // interface reference counting
  514. intfRefs: { // base object for temporary interface variables
  515. ref: function(id,intf){
  516. // called for temporary interface references needing delayed release
  517. var old = this[id];
  518. //console.log('rtl.intfRefs.ref: id='+id+' old="'+(old?old.$name:'null')+'" intf="'+(intf?intf.$name:'null'));
  519. if (old){
  520. // called again, e.g. in a loop
  521. delete this[id];
  522. old._Release(); // may fail
  523. }
  524. this[id]=intf;
  525. return intf;
  526. },
  527. free: function(){
  528. //console.log('rtl.intfRefs.free...');
  529. for (var id in this){
  530. if (this.hasOwnProperty(id)) this[id]._Release;
  531. }
  532. }
  533. },
  534. createIntfRefs: function(){
  535. //console.log('rtl.createIntfRefs');
  536. return Object.create(rtl.intfRefs);
  537. },
  538. setIntfP: function(path,name,value,skipAddRef){
  539. var old = path[name];
  540. //console.log('rtl.setIntfP path='+path+' name='+name+' old="'+(old?old.$name:'null')+'" value="'+(value?value.$name:'null')+'"');
  541. if (old === value) return;
  542. if (old !== null){
  543. path[name]=null;
  544. old._Release();
  545. }
  546. if (value !== null){
  547. if (!skipAddRef) value._AddRef();
  548. path[name]=value;
  549. }
  550. },
  551. setIntfL: function(old,value,skipAddRef){
  552. //console.log('rtl.setIntfL old="'+(old?old.$name:'null')+'" value="'+(value?value.$name:'null')+'"');
  553. if (old !== value){
  554. if (value!==null){
  555. if (!skipAddRef) value._AddRef();
  556. }
  557. if (old!==null){
  558. old._Release(); // Release after AddRef, to avoid double Release if Release creates an exception
  559. }
  560. } else if (skipAddRef){
  561. if (old!==null){
  562. old._Release(); // value has an AddRef
  563. }
  564. }
  565. return value;
  566. },
  567. _AddRef: function(intf){
  568. //if (intf) console.log('rtl._AddRef intf="'+(intf?intf.$name:'null')+'"');
  569. if (intf) intf._AddRef();
  570. return intf;
  571. },
  572. _Release: function(intf){
  573. //if (intf) console.log('rtl._Release intf="'+(intf?intf.$name:'null')+'"');
  574. if (intf) intf._Release();
  575. return intf;
  576. },
  577. checkMethodCall: function(obj,type){
  578. if (rtl.isObject(obj) && rtl.is(obj,type)) return;
  579. rtl.raiseE("EInvalidCast");
  580. },
  581. rc: function(i,minval,maxval){
  582. // range check integer
  583. if ((Math.floor(i)===i) && (i>=minval) && (i<=maxval)) return i;
  584. rtl.raiseE('ERangeError');
  585. },
  586. rcc: function(c,minval,maxval){
  587. // range check char
  588. if (typeof(c)==='string') && (c.length===1)){
  589. var i = c.charCodeAt(0);
  590. if ((i>=minval) && (i<=maxval)) return c;
  591. }
  592. rtl.raiseE('ERangeError');
  593. },
  594. rcArrR: function(arr,index){
  595. // range check read array
  596. if (Array.isArray(arr) && (typeof(index)==='number') && (index>=0) && (index<arr.length)){
  597. if (arguments.length>2){
  598. // arr,index1,index2,...
  599. arr=arr[index];
  600. for (var i=2; i<arguments.length; i++) arr=rtl.rcArrR(arr,arguments[i]);
  601. return arr;
  602. }
  603. return arr[index];
  604. }
  605. rtl.raiseE('ERangeError');
  606. },
  607. rcArrW: function(arr,index,value){
  608. // range check write array
  609. // arr,index1,index2,...,value
  610. for (var i=3; i<arguments.length; i++){
  611. arr=rcArrR(arr,index);
  612. index=arguments[i-1];
  613. value=arguments[i];
  614. }
  615. if (Array.isArray(arr) && (typeof(index)==='number') && (index>=0) && (index<arr.length)){
  616. return arr[index]=value;
  617. }
  618. rtl.raiseE('ERangeError');
  619. },
  620. length: function(arr){
  621. return (arr == null) ? 0 : arr.length;
  622. },
  623. arraySetLength: function(arr,defaultvalue,newlength){
  624. // multi dim: (arr,defaultvalue,dim1,dim2,...)
  625. if (arr == null) arr = [];
  626. var p = arguments;
  627. function setLength(a,argNo){
  628. var oldlen = a.length;
  629. var newlen = p[argNo];
  630. if (oldlen!==newlength){
  631. a.length = newlength;
  632. if (argNo === p.length-1){
  633. if (rtl.isArray(defaultvalue)){
  634. for (var i=oldlen; i<newlen; i++) a[i]=[]; // nested array
  635. } else if (rtl.isFunction(defaultvalue)){
  636. for (var i=oldlen; i<newlen; i++) a[i]=new defaultvalue(); // e.g. record
  637. } else if (rtl.isObject(defaultvalue)) {
  638. for (var i=oldlen; i<newlen; i++) a[i]={}; // e.g. set
  639. } else {
  640. for (var i=oldlen; i<newlen; i++) a[i]=defaultvalue;
  641. }
  642. } else {
  643. for (var i=oldlen; i<newlen; i++) a[i]=[]; // nested array
  644. }
  645. }
  646. if (argNo < p.length-1){
  647. // multi argNo
  648. for (var i=0; i<newlen; i++) a[i]=setLength(a[i],argNo+1);
  649. }
  650. return a;
  651. }
  652. return setLength(arr,2);
  653. },
  654. arrayEq: function(a,b){
  655. if (a===null) return b===null;
  656. if (b===null) return false;
  657. if (a.length!==b.length) return false;
  658. for (var i=0; i<a.length; i++) if (a[i]!==b[i]) return false;
  659. return true;
  660. },
  661. arrayClone: function(type,src,srcpos,end,dst,dstpos){
  662. // type: 0 for references, "refset" for calling refSet(), a function for new type()
  663. // src must not be null
  664. // This function does not range check.
  665. if (rtl.isFunction(type)){
  666. for (; srcpos<end; srcpos++) dst[dstpos++] = new type(src[srcpos]); // clone record
  667. } else if((typeof(type)==="string") && (type === 'refSet')) {
  668. for (; srcpos<end; srcpos++) dst[dstpos++] = rtl.refSet(src[srcpos]); // ref set
  669. } else {
  670. for (; srcpos<end; srcpos++) dst[dstpos++] = src[srcpos]; // reference
  671. };
  672. },
  673. arrayConcat: function(type){
  674. // type: see rtl.arrayClone
  675. var a = [];
  676. var l = 0;
  677. for (var i=1; i<arguments.length; i++) l+=arguments[i].length;
  678. a.length = l;
  679. l=0;
  680. for (var i=1; i<arguments.length; i++){
  681. var src = arguments[i];
  682. if (src == null) continue;
  683. rtl.arrayClone(type,src,0,src.length,a,l);
  684. l+=src.length;
  685. };
  686. return a;
  687. },
  688. arrayCopy: function(type, srcarray, index, count){
  689. // type: see rtl.arrayClone
  690. // if count is missing, use srcarray.length
  691. if (srcarray == null) return [];
  692. if (index < 0) index = 0;
  693. if (count === undefined) count=srcarray.length;
  694. var end = index+count;
  695. if (end>scrarray.length) end = srcarray.length;
  696. if (index>=end) return [];
  697. if (type===0){
  698. return srcarray.slice(index,end);
  699. } else {
  700. var a = [];
  701. a.length = end-index;
  702. rtl.arrayClone(type,srcarray,index,end,a,0);
  703. return a;
  704. }
  705. },
  706. setCharAt: function(s,index,c){
  707. return s.substr(0,index)+c+s.substr(index+1);
  708. },
  709. getResStr: function(mod,name){
  710. var rs = mod.$resourcestrings[name];
  711. return rs.current?rs.current:rs.org;
  712. },
  713. createSet: function(){
  714. var s = {};
  715. for (var i=0; i<arguments.length; i++){
  716. if (arguments[i]!=null){
  717. s[arguments[i]]=true;
  718. } else {
  719. var first=arguments[i+=1];
  720. var last=arguments[i+=1];
  721. for(var j=first; j<=last; j++) s[j]=true;
  722. }
  723. }
  724. return s;
  725. },
  726. cloneSet: function(s){
  727. var r = {};
  728. for (var key in s) r[key]=true;
  729. return r;
  730. },
  731. refSet: function(s){
  732. s.$shared = true;
  733. return s;
  734. },
  735. includeSet: function(s,enumvalue){
  736. if (s.$shared) s = rtl.cloneSet(s);
  737. s[enumvalue] = true;
  738. return s;
  739. },
  740. excludeSet: function(s,enumvalue){
  741. if (s.$shared) s = rtl.cloneSet(s);
  742. delete s[enumvalue];
  743. return s;
  744. },
  745. diffSet: function(s,t){
  746. var r = {};
  747. for (var key in s) if (!t[key]) r[key]=true;
  748. delete r.$shared;
  749. return r;
  750. },
  751. unionSet: function(s,t){
  752. var r = {};
  753. for (var key in s) r[key]=true;
  754. for (var key in t) r[key]=true;
  755. delete r.$shared;
  756. return r;
  757. },
  758. intersectSet: function(s,t){
  759. var r = {};
  760. for (var key in s) if (t[key]) r[key]=true;
  761. delete r.$shared;
  762. return r;
  763. },
  764. symDiffSet: function(s,t){
  765. var r = {};
  766. for (var key in s) if (!t[key]) r[key]=true;
  767. for (var key in t) if (!s[key]) r[key]=true;
  768. delete r.$shared;
  769. return r;
  770. },
  771. eqSet: function(s,t){
  772. for (var key in s) if (!t[key] && (key!='$shared')) return false;
  773. for (var key in t) if (!s[key] && (key!='$shared')) return false;
  774. return true;
  775. },
  776. neSet: function(s,t){
  777. return !rtl.eqSet(s,t);
  778. },
  779. leSet: function(s,t){
  780. for (var key in s) if (!t[key] && (key!='$shared')) return false;
  781. return true;
  782. },
  783. geSet: function(s,t){
  784. for (var key in t) if (!s[key] && (key!='$shared')) return false;
  785. return true;
  786. },
  787. strSetLength: function(s,newlen){
  788. var oldlen = s.length;
  789. if (oldlen > newlen){
  790. return s.substring(0,newlen);
  791. } else if (s.repeat){
  792. // Note: repeat needs ECMAScript6!
  793. return s+' '.repeat(newlen-oldlen);
  794. } else {
  795. while (oldlen<newlen){
  796. s+=' ';
  797. oldlen++;
  798. };
  799. return s;
  800. }
  801. },
  802. spaceLeft: function(s,width){
  803. var l=s.length;
  804. if (l>=width) return s;
  805. if (s.repeat){
  806. // Note: repeat needs ECMAScript6!
  807. return ' '.repeat(width-l) + s;
  808. } else {
  809. while (l<width){
  810. s=' '+s;
  811. l++;
  812. };
  813. };
  814. },
  815. floatToStr : function(d,w,p){
  816. // input 1-3 arguments: double, width, precision
  817. if (arguments.length>2){
  818. return rtl.spaceLeft(d.toFixed(p),w);
  819. } else {
  820. // exponent width
  821. var pad = "";
  822. var ad = Math.abs(d);
  823. if (ad<1.0e+10) {
  824. pad='00';
  825. } else if (ad<1.0e+100) {
  826. pad='0';
  827. }
  828. if (arguments.length<2) {
  829. w=9;
  830. } else if (w<9) {
  831. w=9;
  832. }
  833. var p = w-8;
  834. var s=(d>0 ? " " : "" ) + d.toExponential(p);
  835. s=s.replace(/e(.)/,'E$1'+pad);
  836. return rtl.spaceLeft(s,w);
  837. }
  838. },
  839. initRTTI: function(){
  840. if (rtl.debug_rtti) rtl.debug('initRTTI');
  841. // base types
  842. rtl.tTypeInfo = { name: "tTypeInfo" };
  843. function newBaseTI(name,kind,ancestor){
  844. if (!ancestor) ancestor = rtl.tTypeInfo;
  845. if (rtl.debug_rtti) rtl.debug('initRTTI.newBaseTI "'+name+'" '+kind+' ("'+ancestor.name+'")');
  846. var t = Object.create(ancestor);
  847. t.name = name;
  848. t.kind = kind;
  849. rtl[name] = t;
  850. return t;
  851. };
  852. function newBaseInt(name,minvalue,maxvalue,ordtype){
  853. var t = newBaseTI(name,1 /* tkInteger */,rtl.tTypeInfoInteger);
  854. t.minvalue = minvalue;
  855. t.maxvalue = maxvalue;
  856. t.ordtype = ordtype;
  857. return t;
  858. };
  859. newBaseTI("tTypeInfoInteger",1 /* tkInteger */);
  860. newBaseInt("shortint",-0x80,0x7f,0);
  861. newBaseInt("byte",0,0xff,1);
  862. newBaseInt("smallint",-0x8000,0x7fff,2);
  863. newBaseInt("word",0,0xffff,3);
  864. newBaseInt("longint",-0x80000000,0x7fffffff,4);
  865. newBaseInt("longword",0,0xffffffff,5);
  866. newBaseInt("nativeint",-0x10000000000000,0xfffffffffffff,6);
  867. newBaseInt("nativeuint",0,0xfffffffffffff,7);
  868. newBaseTI("char",2 /* tkChar */);
  869. newBaseTI("string",3 /* tkString */);
  870. newBaseTI("tTypeInfoEnum",4 /* tkEnumeration */,rtl.tTypeInfoInteger);
  871. newBaseTI("tTypeInfoSet",5 /* tkSet */);
  872. newBaseTI("double",6 /* tkDouble */);
  873. newBaseTI("boolean",7 /* tkBool */);
  874. newBaseTI("tTypeInfoProcVar",8 /* tkProcVar */);
  875. newBaseTI("tTypeInfoMethodVar",9 /* tkMethod */,rtl.tTypeInfoProcVar);
  876. newBaseTI("tTypeInfoArray",10 /* tkArray */);
  877. newBaseTI("tTypeInfoDynArray",11 /* tkDynArray */);
  878. newBaseTI("tTypeInfoPointer",15 /* tkPointer */);
  879. var t = newBaseTI("pointer",15 /* tkPointer */,rtl.tTypeInfoPointer);
  880. t.reftype = null;
  881. newBaseTI("jsvalue",16 /* tkJSValue */);
  882. newBaseTI("tTypeInfoRefToProcVar",17 /* tkRefToProcVar */,rtl.tTypeInfoProcVar);
  883. // member kinds
  884. rtl.tTypeMember = {};
  885. function newMember(name,kind){
  886. var m = Object.create(rtl.tTypeMember);
  887. m.name = name;
  888. m.kind = kind;
  889. rtl[name] = m;
  890. };
  891. newMember("tTypeMemberField",1); // tmkField
  892. newMember("tTypeMemberMethod",2); // tmkMethod
  893. newMember("tTypeMemberProperty",3); // tmkProperty
  894. // base object for storing members: a simple object
  895. rtl.tTypeMembers = {};
  896. // tTypeInfoStruct - base object for tTypeInfoClass, tTypeInfoRecord, tTypeInfoInterface
  897. var tis = newBaseTI("tTypeInfoStruct",0);
  898. tis.$addMember = function(name,ancestor,options){
  899. if (rtl.debug_rtti){
  900. if (!rtl.hasString(name) || (name.charAt()==='$')) throw 'invalid member "'+name+'", this="'+this.name+'"';
  901. if (!rtl.is(ancestor,rtl.tTypeMember)) throw 'invalid ancestor "'+ancestor+':'+ancestor.name+'", "'+this.name+'.'+name+'"';
  902. if ((options!=undefined) && (typeof(options)!='object')) throw 'invalid options "'+options+'", "'+this.name+'.'+name+'"';
  903. };
  904. var t = Object.create(ancestor);
  905. t.name = name;
  906. this.members[name] = t;
  907. this.names.push(name);
  908. if (rtl.isObject(options)){
  909. for (var key in options) if (options.hasOwnProperty(key)) t[key] = options[key];
  910. };
  911. return t;
  912. };
  913. tis.addField = function(name,type,options){
  914. var t = this.$addMember(name,rtl.tTypeMemberField,options);
  915. if (rtl.debug_rtti){
  916. if (!rtl.is(type,rtl.tTypeInfo)) throw 'invalid type "'+type+'", "'+this.name+'.'+name+'"';
  917. };
  918. t.typeinfo = type;
  919. this.fields.push(name);
  920. return t;
  921. };
  922. tis.addFields = function(){
  923. var i=0;
  924. while(i<arguments.length){
  925. var name = arguments[i++];
  926. var type = arguments[i++];
  927. if ((i<arguments.length) && (typeof(arguments[i])==='object')){
  928. this.addField(name,type,arguments[i++]);
  929. } else {
  930. this.addField(name,type);
  931. };
  932. };
  933. };
  934. tis.addMethod = function(name,methodkind,params,result,options){
  935. var t = this.$addMember(name,rtl.tTypeMemberMethod,options);
  936. t.methodkind = methodkind;
  937. t.procsig = rtl.newTIProcSig(params);
  938. t.procsig.resulttype = result?result:null;
  939. this.methods.push(name);
  940. return t;
  941. };
  942. tis.addProperty = function(name,flags,result,getter,setter,options){
  943. var t = this.$addMember(name,rtl.tTypeMemberProperty,options);
  944. t.flags = flags;
  945. t.typeinfo = result;
  946. t.getter = getter;
  947. t.setter = setter;
  948. // Note: in options: params, stored, defaultvalue
  949. if (rtl.isArray(t.params)) t.params = rtl.newTIParams(t.params);
  950. this.properties.push(name);
  951. if (!rtl.isString(t.stored)) t.stored = "";
  952. return t;
  953. };
  954. tis.getField = function(index){
  955. return this.members[this.fields[index]];
  956. };
  957. tis.getMethod = function(index){
  958. return this.members[this.methods[index]];
  959. };
  960. tis.getProperty = function(index){
  961. return this.members[this.properties[index]];
  962. };
  963. newBaseTI("tTypeInfoRecord",12 /* tkRecord */,rtl.tTypeInfoStruct);
  964. newBaseTI("tTypeInfoClass",13 /* tkClass */,rtl.tTypeInfoStruct);
  965. newBaseTI("tTypeInfoClassRef",14 /* tkClassRef */);
  966. newBaseTI("tTypeInfoInterface",15 /* tkInterface */,rtl.tTypeInfoStruct);
  967. },
  968. tSectionRTTI: {
  969. $module: null,
  970. $inherited: function(name,ancestor,o){
  971. if (rtl.debug_rtti){
  972. rtl.debug('tSectionRTTI.newTI "'+(this.$module?this.$module.$name:"(no module)")
  973. +'"."'+name+'" ('+ancestor.name+') '+(o?'init':'forward'));
  974. };
  975. var t = this[name];
  976. if (t){
  977. if (!t.$forward) throw 'duplicate type "'+name+'"';
  978. if (!ancestor.isPrototypeOf(t)) throw 'typeinfo ancestor mismatch "'+name+'" ancestor="'+ancestor.name+'" t.name="'+t.name+'"';
  979. } else {
  980. t = Object.create(ancestor);
  981. t.name = name;
  982. t.module = this.module;
  983. this[name] = t;
  984. }
  985. if (o){
  986. delete t.$forward;
  987. for (var key in o) if (o.hasOwnProperty(key)) t[key]=o[key];
  988. } else {
  989. t.$forward = true;
  990. }
  991. return t;
  992. },
  993. $Scope: function(name,ancestor,o){
  994. var t=this.$inherited(name,ancestor,o);
  995. t.members = {};
  996. t.names = [];
  997. t.fields = [];
  998. t.methods = [];
  999. t.properties = [];
  1000. return t;
  1001. },
  1002. $TI: function(name,kind,o){ var t=this.$inherited(name,rtl.tTypeInfo,o); t.kind = kind; return t; },
  1003. $Int: function(name,o){ return this.$inherited(name,rtl.tTypeInfoInteger,o); },
  1004. $Enum: function(name,o){ return this.$inherited(name,rtl.tTypeInfoEnum,o); },
  1005. $Set: function(name,o){ return this.$inherited(name,rtl.tTypeInfoSet,o); },
  1006. $StaticArray: function(name,o){ return this.$inherited(name,rtl.tTypeInfoArray,o); },
  1007. $DynArray: function(name,o){ return this.$inherited(name,rtl.tTypeInfoDynArray,o); },
  1008. $ProcVar: function(name,o){ return this.$inherited(name,rtl.tTypeInfoProcVar,o); },
  1009. $RefToProcVar: function(name,o){ return this.$inherited(name,rtl.tTypeInfoRefToProcVar,o); },
  1010. $MethodVar: function(name,o){ return this.$inherited(name,rtl.tTypeInfoMethodVar,o); },
  1011. $Record: function(name,o){ return this.$Scope(name,rtl.tTypeInfoRecord,o); },
  1012. $Class: function(name,o){ return this.$Scope(name,rtl.tTypeInfoClass,o); },
  1013. $ClassRef: function(name,o){ return this.$inherited(name,rtl.tTypeInfoClassRef,o); },
  1014. $Pointer: function(name,o){ return this.$inherited(name,rtl.tTypeInfoPointer,o); },
  1015. $Interface: function(name,o){ return this.$Scope(name,rtl.tTypeInfoInterface,o); }
  1016. },
  1017. newTIParam: function(param){
  1018. // param is an array, 0=name, 1=type, 2=optional flags
  1019. var t = {
  1020. name: param[0],
  1021. typeinfo: param[1],
  1022. flags: (rtl.isNumber(param[2]) ? param[2] : 0)
  1023. };
  1024. return t;
  1025. },
  1026. newTIParams: function(list){
  1027. // list: optional array of [paramname,typeinfo,optional flags]
  1028. var params = [];
  1029. if (rtl.isArray(list)){
  1030. for (var i=0; i<list.length; i++) params.push(rtl.newTIParam(list[i]));
  1031. };
  1032. return params;
  1033. },
  1034. newTIProcSig: function(params,result,flags){
  1035. var s = {
  1036. params: rtl.newTIParams(params),
  1037. resulttype: result,
  1038. flags: flags
  1039. };
  1040. return s;
  1041. },
  1042. }