jsbase.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. { *********************************************************************
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2016 Michael Van Canneyt.
  4. Javascript base definitions
  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. unit jsbase;
  12. {$mode objfpc}{$H+}
  13. interface
  14. {$ifdef pas2js}
  15. uses js;
  16. {$endif}
  17. const
  18. MinSafeIntDouble = -$1fffffffffffff; // -9007199254740991 53 bits (52 explicitly stored)
  19. MaxSafeIntDouble = $1fffffffffffff; // 9007199254740991
  20. Type
  21. TJSType = (jstUNDEFINED,jstNull,jstBoolean,jstNumber,jstString,jstObject,jstReference,jstCompletion);
  22. TJSString = UnicodeString;
  23. TJSChar = WideChar;
  24. TJSNumber = Double;
  25. {$ifdef fpc}
  26. TJSPChar = PWideChar;
  27. {$endif}
  28. { TJSValue }
  29. TJSValue = Class(TObject)
  30. private
  31. FValueType: TJSType;
  32. {$ifdef pas2js}
  33. FValue: JSValue;
  34. {$else}
  35. FValue : Record
  36. Case Integer of
  37. 0 : (P : Pointer);
  38. 1 : (F : TJSNumber);
  39. 2 : (I : Integer);
  40. end;
  41. {$endif}
  42. FCustomValue: TJSString;
  43. procedure ClearValue(ANewValue: TJSType);
  44. function GetAsBoolean: Boolean;
  45. function GetAsCompletion: TObject;
  46. function GetAsNumber: TJSNumber;
  47. function GetAsObject: TObject;
  48. function GetAsReference: TObject;
  49. function GetAsString: TJSString;
  50. function GetIsNull: Boolean;
  51. function GetIsUndefined: Boolean;
  52. procedure SetAsBoolean(const AValue: Boolean);
  53. procedure SetAsCompletion(const AValue: TObject);
  54. procedure SetAsNumber(const AValue: TJSNumber);
  55. procedure SetAsObject(const AValue: TObject);
  56. procedure SetAsReference(const AValue: TObject);
  57. procedure SetAsString(const AValue: TJSString);
  58. procedure SetIsNull(const AValue: Boolean);
  59. procedure SetIsUndefined(const AValue: Boolean);
  60. Public
  61. Constructor Create;
  62. Constructor CreateNull;
  63. Constructor Create(ANumber : TJSNumber);
  64. Constructor Create(ABoolean : Boolean);
  65. Constructor Create(AString: TJSString);
  66. Destructor Destroy; override;
  67. Property ValueType : TJSType Read FValueType;
  68. Property CustomValue: TJSString Read FCustomValue Write FCustomValue;
  69. Property IsUndefined : Boolean Read GetIsUndefined Write SetIsUndefined;
  70. Property IsNull : Boolean Read GetIsNull Write SetIsNull;
  71. Property AsNumber : TJSNumber Read GetAsNumber Write SetAsNumber;
  72. Property AsBoolean : Boolean Read GetAsBoolean Write SetAsBoolean;
  73. Property AsObject : TObject Read GetAsObject Write SetAsObject;
  74. Property AsString : TJSString Read GetAsString Write SetAsString;
  75. Property AsReference : TObject Read GetAsReference Write SetAsReference;
  76. Property AsCompletion : TObject Read GetAsCompletion Write SetAsCompletion;
  77. end;
  78. function IsValidJSIdentifier(Name: TJSString; AllowEscapeSeq: boolean = false): boolean;
  79. function StrToJSString(const S: String): TJSString; inline;
  80. function JSStringToString(const S: TJSString): String; inline;
  81. implementation
  82. function IsValidJSIdentifier(Name: TJSString; AllowEscapeSeq: boolean): boolean;
  83. {$ifdef pas2js}
  84. const
  85. HexChars = ['0'..'9','a'..'f','A'..'F'];
  86. var
  87. p, l, i: Integer;
  88. begin
  89. Result:=false;
  90. if Name='' then exit;
  91. l:=length(Name);
  92. p:=1;
  93. while p<=l do
  94. case Name[p] of
  95. '0'..'9':
  96. if p=1 then
  97. exit
  98. else
  99. inc(p);
  100. 'a'..'z','A'..'Z','_','$': inc(p);
  101. '\':
  102. begin
  103. if not AllowEscapeSeq then exit;
  104. inc(p);
  105. if p>l then exit;
  106. if Name[p]='x' then
  107. begin
  108. // \x00
  109. inc(p);
  110. if (p>l) or not (Name[p] in HexChars) then exit;
  111. inc(p);
  112. if (p>l) or not (Name[p] in HexChars) then exit;
  113. end
  114. else if Name[p]='u' then
  115. begin
  116. inc(p);
  117. if p>l then exit;
  118. if Name[p]='{' then
  119. begin
  120. // \u{00000}
  121. i:=0;
  122. repeat
  123. inc(p);
  124. if p>l then exit;
  125. case Name[p] of
  126. '}': break;
  127. '0'..'9': i:=i*16+ord(Name[p])-ord('0');
  128. 'a'..'f': i:=i*16+ord(Name[p])-ord('a')+10;
  129. 'A'..'F': i:=i*16+ord(Name[p])-ord('A')+10;
  130. else exit;
  131. end;
  132. if i>$FFFF then exit;
  133. until false;
  134. if (i>=$D800) and (i<$E000) then exit;
  135. inc(p);
  136. end
  137. else
  138. begin
  139. // \u0000
  140. for i:=1 to 4 do
  141. begin
  142. inc(p);
  143. if (p>l) or not (Name[p] in HexChars) then exit;
  144. end;
  145. end;
  146. // ToDo: check for invalid values like #$D800 and #$0041
  147. end
  148. else
  149. exit; // unknown sequence
  150. end;
  151. #$200C,#$200D: inc(p); // zero width non-joiner/joiner
  152. #$00AA..#$2000,
  153. #$200E..#$D7FF:
  154. inc(p); // ToDo: only those with ID_START/ID_CONTINUE see https://codepoints.net/search?IDC=1
  155. #$D800..#$DFFF:
  156. exit; // double code units are not allowed for JS identifiers
  157. #$E000..#$FFFF:
  158. inc(p);
  159. else
  160. exit;
  161. end;
  162. Result:=true;
  163. end;
  164. {$else}
  165. var
  166. p: TJSPChar;
  167. i: Integer;
  168. begin
  169. Result:=false;
  170. if Name='' then exit;
  171. p:=TJSPChar(Name);
  172. repeat
  173. case p^ of
  174. #0:
  175. if p-TJSPChar(Name)=length(Name) then
  176. exit(true)
  177. else
  178. exit;
  179. '0'..'9':
  180. if p=TJSPChar(Name) then
  181. exit
  182. else
  183. inc(p);
  184. 'a'..'z','A'..'Z','_','$': inc(p);
  185. '\':
  186. begin
  187. if not AllowEscapeSeq then exit;
  188. inc(p);
  189. if p^='x' then
  190. begin
  191. // \x00
  192. for i:=1 to 2 do
  193. begin
  194. inc(p);
  195. if not (p^ in ['0'..'9','a'..'f','A'..'F']) then exit;
  196. end;
  197. end
  198. else if p^='u' then
  199. begin
  200. inc(p);
  201. if p^='{' then
  202. begin
  203. // \u{00000}
  204. i:=0;
  205. repeat
  206. inc(p);
  207. case p^ of
  208. '}': break;
  209. '0'..'9': i:=i*16+ord(p^)-ord('0');
  210. 'a'..'f': i:=i*16+ord(p^)-ord('a')+10;
  211. 'A'..'F': i:=i*16+ord(p^)-ord('A')+10;
  212. else exit;
  213. end;
  214. if i>$FFFF then exit;
  215. until false;
  216. if (i>=$D800) and (i<$E000) then exit;
  217. inc(p);
  218. end
  219. else
  220. begin
  221. // \u0000
  222. for i:=1 to 4 do
  223. begin
  224. inc(p);
  225. if not (p^ in ['0'..'9','a'..'f','A'..'F']) then exit;
  226. end;
  227. end;
  228. // ToDo: check for invalid values like #$D800 and #$0041
  229. end
  230. else
  231. exit; // unknown sequence
  232. end;
  233. #$200C,#$200D: inc(p); // zero width non-joiner/joiner
  234. #$00AA..#$2000,
  235. #$200E..#$D7FF:
  236. inc(p); // ToDo: only those with ID_START/ID_CONTINUE see https://codepoints.net/search?IDC=1
  237. #$D800..#$DFFF:
  238. exit; // double code units are not allowed for JS identifiers
  239. #$E000..#$FFFF:
  240. inc(p);
  241. else
  242. exit;
  243. end;
  244. until false;
  245. end;
  246. {$endif}
  247. function StrToJSString(const S: String): TJSString;
  248. begin
  249. Result:={$ifdef pas2js}S{$else}UTF8Decode(S){$endif};
  250. end;
  251. function JSStringToString(const S: TJSString): String;
  252. begin
  253. Result:={$ifdef pas2js}S{$else}UTF8Encode(S){$endif};
  254. end;
  255. { TJSValue }
  256. function TJSValue.GetAsBoolean: Boolean;
  257. begin
  258. If (ValueType=jstBoolean) then
  259. Result:={$ifdef pas2js}boolean(FValue){$else}(FValue.I<>0){$endif}
  260. else
  261. Result:=False;
  262. end;
  263. function TJSValue.GetAsCompletion: TObject;
  264. begin
  265. Result:=TObject(FValue{$ifdef fpc}.P{$endif});
  266. end;
  267. function TJSValue.GetAsNumber: TJSNumber;
  268. begin
  269. If (ValueType=jstNumber) then
  270. Result:={$ifdef pas2js}TJSNumber(FValue){$else}FValue.F{$endif}
  271. else
  272. Result:=0.0;
  273. end;
  274. function TJSValue.GetAsObject: TObject;
  275. begin
  276. If (ValueType=jstObject) then
  277. Result:=TObject(FValue{$ifdef fpc}.P{$endif})
  278. else
  279. Result:=nil;
  280. end;
  281. function TJSValue.GetAsReference: TObject;
  282. begin
  283. If (ValueType=jstReference) then
  284. Result:=TObject(FValue{$ifdef fpc}.P{$endif})
  285. else
  286. Result:=nil;
  287. end;
  288. function TJSValue.GetAsString: TJSString;
  289. begin
  290. If (ValueType=jstString) then
  291. Result:=TJSString(FValue{$ifdef fpc}.P{$endif})
  292. else
  293. Result:='';
  294. end;
  295. function TJSValue.GetIsNull: Boolean;
  296. begin
  297. Result:=(ValueType=jstNull);
  298. end;
  299. function TJSValue.GetIsUndefined: Boolean;
  300. begin
  301. Result:=(fValueType=jstUndefined);
  302. end;
  303. procedure TJSValue.ClearValue(ANewValue : TJSType);
  304. begin
  305. {$ifdef pas2js}
  306. Case FValueType of
  307. jstUNDEFINED: FValue:=JS.Undefined;
  308. jstString : FValue:='';
  309. jstNumber : FValue:=0;
  310. jstBoolean : FValue:=false;
  311. else
  312. FValue:=JS.Null;
  313. end;
  314. {$else}
  315. Case FValueType of
  316. jstString : String(FValue.P):='';
  317. jstNumber : FValue.F:=0;
  318. else
  319. FValue.I:=0;
  320. end;
  321. {$endif}
  322. FValueType:=ANewValue;
  323. FCustomValue:='';
  324. end;
  325. procedure TJSValue.SetAsBoolean(const AValue: Boolean);
  326. begin
  327. ClearValue(jstBoolean);
  328. {$ifdef pas2js}
  329. FValue:=AValue;
  330. {$else}
  331. FValue.I:=Ord(AValue);
  332. {$endif}
  333. end;
  334. procedure TJSValue.SetAsCompletion(const AValue: TObject);
  335. begin
  336. ClearValue(jstBoolean);
  337. FValue{$ifdef fpc}.P{$endif}:=AValue;
  338. end;
  339. procedure TJSValue.SetAsNumber(const AValue: TJSNumber);
  340. begin
  341. ClearValue(jstNumber);
  342. FValue{$ifdef fpc}.F{$endif}:=AValue;
  343. end;
  344. procedure TJSValue.SetAsObject(const AValue: TObject);
  345. begin
  346. ClearValue(jstObject);
  347. FValue{$ifdef fpc}.P{$endif}:=AVAlue;
  348. end;
  349. procedure TJSValue.SetAsReference(const AValue: TObject);
  350. begin
  351. ClearValue(jstReference);
  352. FValue{$ifdef fpc}.P{$endif}:=AVAlue;
  353. end;
  354. procedure TJSValue.SetAsString(const AValue: TJSString);
  355. begin
  356. ClearValue(jstString);
  357. {$ifdef pas2js}FValue{$else}TJSString(FValue.P){$endif}:=AValue;
  358. end;
  359. procedure TJSValue.SetIsNull(const AValue: Boolean);
  360. begin
  361. if AValue then
  362. ClearValue(jstNull)
  363. else if IsNull then
  364. ClearValue(jstUNDEFINED);
  365. end;
  366. procedure TJSValue.SetIsUndefined(const AValue: Boolean);
  367. begin
  368. if AValue then
  369. ClearValue(jstUndefined)
  370. else if IsUndefined then
  371. ClearValue(jstNull);
  372. end;
  373. constructor TJSValue.CreateNull;
  374. begin
  375. IsNull:=True;
  376. end;
  377. constructor TJSValue.Create;
  378. begin
  379. IsUndefined:=True;
  380. end;
  381. constructor TJSValue.Create(ANumber: TJSNumber);
  382. begin
  383. AsNumber:=ANumber;
  384. end;
  385. constructor TJSValue.Create(ABoolean: Boolean);
  386. begin
  387. AsBoolean:=ABoolean;
  388. end;
  389. constructor TJSValue.Create(AString: TJSString);
  390. begin
  391. AsString:=AString;
  392. end;
  393. destructor TJSValue.Destroy;
  394. begin
  395. ClearValue(jstUndefined);
  396. inherited Destroy;
  397. end;
  398. end.