From 6160a2ad51721ccd9498b8c5e442176325c40df1 Mon Sep 17 00:00:00 2001 From: Mattias Gaertner Date: Wed, 30 Jan 2019 16:44:48 +0000 Subject: [PATCH] pastojs: intrinsics lo/hi, issue #34964, patch from Kryvichh git-svn-id: trunk@41148 - --- packages/fcl-passrc/src/pasresolveeval.pas | 45 +++++- packages/fcl-passrc/src/pasresolver.pp | 174 ++++++++++++++++++++- packages/fcl-passrc/tests/tcresolver.pas | 18 +++ packages/pastojs/src/fppas2js.pp | 63 ++++++++ packages/pastojs/tests/tcmodules.pas | 150 ++++++++++++++++++ utils/pas2js/fpmake.pp | 2 - 6 files changed, 441 insertions(+), 11 deletions(-) diff --git a/packages/fcl-passrc/src/pasresolveeval.pas b/packages/fcl-passrc/src/pasresolveeval.pas index 1518c34926..c0ac67d001 100644 --- a/packages/fcl-passrc/src/pasresolveeval.pas +++ b/packages/fcl-passrc/src/pasresolveeval.pas @@ -25,7 +25,7 @@ Works: - int/uint - unary +, - - binary: +, -, *, div, mod, ^^, =, <>, <, >, <=, >=, and, or, xor, not, shl, shr - - low(), high(), pred(), succ(), ord() + - Low(), High(), Pred(), Succ(), Ord(), Lo(), Hi() - typecast longint(-1), word(-2), intsingle(-1), uintsingle(1) - float: - typecast single(double), double(single), float(integer) @@ -714,6 +714,8 @@ type function EvalStrFunc(Params: TParamsExpr; Flags: TResEvalFlags): TResEvalValue; virtual; function EvalStringAddExpr(Expr, LeftExpr, RightExpr: TPasExpr; LeftValue, RightValue: TResEvalValue): TResEvalValue; virtual; + function LoHiValue(Value: TResEvalValue; ShiftSize: Integer; Mask: LongWord; + ErrorEl: TPasElement): TResEvalValue; virtual; function EnumTypeCast(EnumType: TPasEnumType; Expr: TPasExpr; Flags: TResEvalFlags): TResEvalEnum; virtual; {$ifdef FPC_HAS_CPSTRING} @@ -735,6 +737,7 @@ type TResExprEvaluatorClass = class of TResExprEvaluator; procedure ReleaseEvalValue(var Value: TResEvalValue); +function NumberIsFloat(const Value: string): boolean; {$ifdef FPC_HAS_CPSTRING} function RawStrToCaption(const r: RawByteString; MaxLength: integer): string; @@ -757,6 +760,17 @@ begin Value:=nil; end; +function NumberIsFloat(const Value: string): boolean; +var + i: Integer; +begin + if Value='' then exit(false); + if Value[1] in ['$','%','&'] then exit(false); + for i:=2 to length(Value) do + if Value[i] in ['.','E','e'] then exit(true); + Result:=false; +end; + {$ifdef FPC_HAS_CPSTRING} function RawStrToCaption(const r: RawByteString; MaxLength: integer): string; var @@ -4849,6 +4863,35 @@ begin end; end; +function TResExprEvaluator.LoHiValue(Value: TResEvalValue; ShiftSize: Integer; + Mask: LongWord; ErrorEl: TPasElement): TResEvalValue; +var + uint: LongWord; +begin + case Value.Kind of + revkInt: + {$IFDEF Pas2js} + if ShiftSize=32 then + uint := (TResEvalInt(Value).Int div $100000000) and Mask; + else + {$ENDIF} + uint := (TResEvalInt(Value).Int shr ShiftSize) and Mask; + revkUInt: + {$IFDEF Pas2js} + if ShiftSize=32 then + uint := (TResEvalUInt(Value).UInt div $100000000) and Mask; + else + {$ENDIF} + uint := (TResEvalUInt(Value).UInt shr ShiftSize) and Mask; + else + {$IFDEF VerbosePasResEval} + writeln('TResExprEvaluator.LoHiValue ',Value.AsDebugString); + {$ENDIF} + RaiseNotYetImplemented(20190129012100,ErrorEl); + end; + Result := TResEvalInt.CreateValue(uint); +end; + function TResExprEvaluator.EnumTypeCast(EnumType: TPasEnumType; Expr: TPasExpr; Flags: TResEvalFlags): TResEvalEnum; var diff --git a/packages/fcl-passrc/src/pasresolver.pp b/packages/fcl-passrc/src/pasresolver.pp index f124147efe..7f78fd0bae 100644 --- a/packages/fcl-passrc/src/pasresolver.pp +++ b/packages/fcl-passrc/src/pasresolver.pp @@ -228,6 +228,13 @@ Works: - with - self - built-in procedure Val(const s: string; var e: enumtype; out Code: integertype); +- intrinsic functions Lo and Hi, depending on $mode (ObjFPC or Delphi): + - In $MODE DELPHI: + function Lo/Hi(i: ): Byte + - In $MODE OBJFPC: + function Lo/Hi(i: Byte/ShortInt/Word/SmallInt): Byte + function Lo/Hi(i: LongWord/LongInt/UIntSingle/IntSingle): Word + function Lo/Hi(i: QWord/Int64/UIntDouble/IntDouble): LongWord - helpers: - class - record @@ -549,6 +556,8 @@ type bfStrFunc, bfWriteStr, bfVal, + bfLo, + bfHi, bfConcatArray, bfConcatString, bfCopyArray, @@ -584,6 +593,8 @@ const 'Str', 'WriteStr', 'Val', + 'Lo', + 'Hi', 'Concat', 'Concat', 'Copy', @@ -1689,6 +1700,12 @@ type Expr: TPasExpr; RaiseOnError: boolean): integer; virtual; procedure BI_Val_OnFinishParamsExpr(Proc: TResElDataBuiltInProc; Params: TParamsExpr); virtual; + function BI_LoHi_OnGetCallCompatibility(Proc: TResElDataBuiltInProc; + Expr: TPasExpr; RaiseOnError: boolean): integer; virtual; + procedure BI_LoHi_OnGetCallResult(Proc: TResElDataBuiltInProc; + Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual; + procedure BI_LoHi_OnEval(Proc: TResElDataBuiltInProc; + Params: TParamsExpr; Flags: TResEvalFlags; out Evaluated: TResEvalValue); virtual; function BI_ConcatArray_OnGetCallCompatibility(Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer; virtual; procedure BI_ConcatArray_OnGetCallResult({%H-}Proc: TResElDataBuiltInProc; @@ -2015,6 +2032,8 @@ type function IsElementSkipped(El: TPasElement): boolean; virtual; function FindLocalBuiltInSymbol(El: TPasElement): TPasElement; virtual; function GetLastSection: TPasSection; + function GetShiftAndMaskForLoHiFunc(BaseType: TResolverBaseType; + isLoFunc: Boolean; out Mask: LongWord): Integer; public // options property Options: TPasResolverOptions read FOptions write FOptions; @@ -14387,6 +14406,77 @@ begin FinishCallArgAccess(P[2],rraOutParam); end; +function TPasResolver.BI_LoHi_OnGetCallCompatibility( + Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer; +var + Params: TParamsExpr; + Param: TPasExpr; + ParamResolved: TPasResolverResult; +begin + if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then + Exit(cIncompatible); + Params:=TParamsExpr(Expr); + // first Param: any integer type + Param:=Params.params[0]; + ComputeElement(Param,ParamResolved,[]); + Result:=cIncompatible; + if (rrfReadable in ParamResolved.Flags) + and (ParamResolved.BaseType in btAllInteger) + then + Result:=cExact; + if Result=cIncompatible then + Exit(CheckRaiseTypeArgNo(20190128232600,1,Param,ParamResolved,'integer type',RaiseOnError)); + Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError); +end; + +procedure TPasResolver.BI_LoHi_OnGetCallResult(Proc: TResElDataBuiltInProc; + Params: TParamsExpr; out ResolvedEl: TPasResolverResult); +var + ResolvedParam: TPasResolverResult; + BaseType: TResolverBaseType; + Mask: LongWord; +begin + ComputeElement(Params.Params[0],ResolvedParam,[]); + GetShiftAndMaskForLoHiFunc(ResolvedParam.BaseType, Proc.BuiltIn=bfLo, Mask); + case Mask of + $F, $FF: BaseType := btByte; + $FFFF: BaseType := btWord; + else { $FFFFFFFF } BaseType := btLongWord; + end; + SetResolverIdentifier(ResolvedEl,BaseType,Proc.Proc, + FBaseTypes[BaseType],FBaseTypes[BaseType],[rrfReadable]); +end; + +procedure TPasResolver.BI_LoHi_OnEval(Proc: TResElDataBuiltInProc; + Params: TParamsExpr; Flags: TResEvalFlags; out Evaluated: TResEvalValue); +var + Param: TPasExpr; + ResolvedParam: TPasResolverResult; + Value: TResEvalValue; + Shift: Integer; + Mask: LongWord; +begin + Evaluated := nil; + Param := Params.Params[0]; + Value := Eval(Param,Flags); + {$IFDEF VerbosePasResEval} + {AllowWriteln} + if value=nil then + writeln('TPasResolver.BI_LoHi_OnEval Value=NIL') + else + writeln('TPasResolver.BI_LoHi_OnEval Value=',value.AsDebugString); + {AllowWriteln-} + {$ENDIF} + if Value=nil then exit; + try + ComputeElement(Param,ResolvedParam,[]); + Shift := GetShiftAndMaskForLoHiFunc(ResolvedParam.BaseType, Proc.BuiltIn=bfLo, Mask); + Evaluated := fExprEvaluator.LoHiValue(Value,Shift,Mask,Params); + finally + ReleaseEvalValue(Value); + end; +end; + function TPasResolver.BI_ConcatArray_OnGetCallCompatibility( Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer; var @@ -16337,6 +16427,14 @@ begin AddBuiltInProc('Val','procedure Val(const String; var Value: bool|int|float|enum; out Int)', @BI_Val_OnGetCallCompatibility,nil,nil, @BI_Val_OnFinishParamsExpr,bfVal,[bipfCanBeStatement]); + if bfLo in TheBaseProcs then + AddBuiltInProc('Lo','function Lo(X: any integer type): Byte|Word)', + @BI_LoHi_OnGetCallCompatibility,@BI_LoHi_OnGetCallResult, + @BI_LoHi_OnEval,nil,bfLo); + if bfHi in TheBaseProcs then + AddBuiltInProc('Hi','function Hi(X: any integer type): Byte|Word)', + @BI_LoHi_OnGetCallCompatibility,@BI_LoHi_OnGetCallResult, + @BI_LoHi_OnEval,nil,bfHi); if bfConcatArray in TheBaseProcs then AddBuiltInProc('Concat','function Concat(const Array1, Array2, ...): Array', @BI_ConcatArray_OnGetCallCompatibility,@BI_ConcatArray_OnGetCallResult, @@ -21095,6 +21193,8 @@ var ElClass: TClass; bt: TResolverBaseType; TypeEl: TPasType; + Value: TResEvalValue; + Int: TMaxPrecInt; begin if StartEl=nil then StartEl:=El; ResolvedEl:=Default(TPasResolverResult); @@ -21114,14 +21214,35 @@ begin ComputeIdentifier(TPrimitiveExpr(El)); end; pekNumber: - if Pos('.',TPrimitiveExpr(El).Value)>0 then - SetResolverValueExpr(ResolvedEl,BaseTypeExtended, - FBaseTypes[BaseTypeExtended],FBaseTypes[BaseTypeExtended], - TPrimitiveExpr(El),[rrfReadable]) - else - SetResolverValueExpr(ResolvedEl,btLongint, - FBaseTypes[btLongint],FBaseTypes[btLongint], - TPrimitiveExpr(El),[rrfReadable]); + begin + if NumberIsFloat(TPrimitiveExpr(El).Value) then + bt:=BaseTypeExtended + else if length(TPrimitiveExpr(El).Value)<10 then + bt:=btLongint + else + begin + Value:=Eval(TPrimitiveExpr(El),[]); + if Value=nil then + RaiseNotYetImplemented(20190130162601,El); + try + case Value.Kind of + revkInt: + begin + Int:=TResEvalInt(Value).Int; + bt:=GetSmallestIntegerBaseType(Int,Int); + end; + revkUInt: + bt:=btQWord; + else + bt:=BaseTypeExtended; + end; + finally + ReleaseEvalValue(Value); + end; + end; + SetResolverValueExpr(ResolvedEl,bt,FBaseTypes[bt],FBaseTypes[bt], + TPrimitiveExpr(El),[rrfReadable]) + end; pekString: begin {$IFDEF VerbosePasResolver} @@ -22599,6 +22720,43 @@ begin Result:=Module.InterfaceSection; end; +function TPasResolver.GetShiftAndMaskForLoHiFunc(BaseType: TResolverBaseType; + isLoFunc: Boolean; out Mask: LongWord): Integer; +const + SHIFT_SIZE: array[btByte..{$IFDEF HasInt64}btComp{$ELSE}btIntDouble{$ENDIF}] of Integer = ( + 4, // btByte + 8, // btShortInt FPC lo/hi(shortint) works like SmallInt + 8, 8, // btWord, btSmallInt + 16, 16, 16, 16, // btUIntSingle, btIntSingle, btLongWord, btLongint + 32, 32 // btUIntDouble, btIntDouble + {$IFDEF HasInt64} + , 32, 32, 32 // btQWord, btInt64, btComp + {$endif} + ); +begin + if (BaseType >= Low(SHIFT_SIZE)) and (BaseType <= High(SHIFT_SIZE)) then + begin + if msDelphi in CurrentParser.CurrentModeswitches then + Result := 8 + else + Result := SHIFT_SIZE[BaseType]; + case Result of + 8: Mask := $FF; + 16: Mask := $FFFF; + 32: Mask := $FFFFFFFF; + else + {4} Mask := $F; + end; + if isLoFunc then + Result := 0; + end + else + begin + RaiseInternalError(20190130122300); + Result := -1; + end; +end; + function TPasResolver.CheckSrcIsADstType(const ResolvedSrcType, ResolvedDestType: TPasResolverResult): integer; // finds distance between classes SrcType and DestType diff --git a/packages/fcl-passrc/tests/tcresolver.pas b/packages/fcl-passrc/tests/tcresolver.pas index 27be7a23bb..f1e402f051 100644 --- a/packages/fcl-passrc/tests/tcresolver.pas +++ b/packages/fcl-passrc/tests/tcresolver.pas @@ -2797,6 +2797,20 @@ begin ' r=low(word)+high(int64);', ' s=low(longint)+high(integer);', ' t=succ(2)+pred(2);', + ' lo1:byte=lo(word($1234));', + ' hi1:byte=hi(word($1234));', + ' lo2:word=lo(longword($1234CDEF));', + ' hi2:word=hi(longword($1234CDEF));', + ' lo3:word=lo(LongInt(-$1234CDEF));', + ' hi3:word=hi(LongInt(-$1234CDEF));', + ' lo4:byte=lo(byte($34));', + ' hi4:byte=hi(byte($34));', + ' lo5:byte=lo(shortint(-$34));', + ' hi5:byte=hi(shortint(-$34));', + ' lo6:longword=lo($123456789ABCDEF0);', + ' hi6:longword=hi($123456789ABCDEF0);', + ' lo7:longword=lo(-$123456789ABCDEF0);', + ' hi7:longword=hi(-$123456789ABCDEF0);', 'begin']); ParseProgram; CheckResolverUnexpectedHints; @@ -4359,6 +4373,10 @@ begin Add(' if i>=j then;'); Add(' if i 0 then + begin + if Shift=32 then + begin + // JS bitwise operations work only 32bit -> use division for bigger shifts + Result:=CreateMathFloor(El,CreateDivideNumber(El,Result,$100000000)); + end + else + begin + ShiftEx := TJSRShiftExpression(CreateElement(TJSRShiftExpression,El)); + ShiftEx.A := Result; + ShiftEx.B := CreateLiteralNumber(El, Shift); + Result := ShiftEx; + end; + end; + case Mask of + $FF: Digits := 2; + $FFFF: Digits := 4; + $FFFFFFFF: Digits := 8; + else { $F } Digits := 1; + end; + if Digits<8 then + begin + // & Mask + AndEx := TJSBitwiseAndExpression(CreateElement(TJSBitwiseAndExpression,El)); + AndEx.A := Result; + AndEx.B := CreateLiteralHexNumber(El,Mask,Digits); + Result := AndEx; + end + else + begin + // mask to longword -> >>> 0 + ShiftEx:=TJSURShiftExpression(CreateElement(TJSURShiftExpression,El)); + ShiftEx.A:=Result; + ShiftEx.B:=CreateLiteralNumber(El,0); + Result:=ShiftEx; + end; +end; + function TPasToJSConverter.ConvertBuiltIn_ConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; // concat(array1, array2) diff --git a/packages/pastojs/tests/tcmodules.pas b/packages/pastojs/tests/tcmodules.pas index 3b3b362ffe..900275a4fe 100644 --- a/packages/pastojs/tests/tcmodules.pas +++ b/packages/pastojs/tests/tcmodules.pas @@ -370,6 +370,8 @@ type // statements Procedure TestNestBegin; Procedure TestIncDec; + Procedure TestLoHiFpcMode; + Procedure TestLoHiDelphiMode; Procedure TestAssignments; Procedure TestArithmeticOperators1; Procedure TestLogicalOperators; @@ -2683,6 +2685,154 @@ begin ])); end; +procedure TTestModule.TestLoHiFpcMode; +begin + StartProgram(false); + Add([ + '{$mode objfpc}', + 'const', + ' LoByte1 = Lo(Word($1234));', + ' HiByte1 = Hi(Word($1234));', + ' LoByte2 = Lo(SmallInt($1234));', + ' HiByte2 = Hi(SmallInt($1234));', + ' LoWord1 = Lo($1234CDEF);', + ' HiWord1 = Hi($1234CDEF);', + ' LoWord2 = Lo(-$1234CDEF);', + ' HiWord2 = Hi(-$1234CDEF);', + ' lo4:byte=lo(byte($34));', + ' hi4:byte=hi(byte($34));', + ' lo5:byte=lo(shortint(-$34));', + ' hi5:byte=hi(shortint(-$34));', + ' lo6:longword=lo($123456789ABCDE);', + ' hi6:longword=hi($123456789ABCDE);', + ' lo7:longword=lo(-$123456789ABCDE);', + ' hi7:longword=hi(-$123456789ABCDE);', + 'var', + ' b: Byte;', + ' ss: shortint;', + ' w: Word;', + ' si: SmallInt;', + ' lw: LongWord;', + ' li: LongInt;', + ' b2: Byte;', + ' ni: nativeint;', + 'begin', + ' w := $1234;', + ' ss := -$12;', + ' b := lo(ss);', + ' b := HI(ss);', + ' b := lo(w);', + ' b := HI(w);', + ' b2 := lo(b);', + ' b2 := hi(b);', + ' lw := $1234CDEF;', + ' w := lo(lw);', + ' w := hi(lw);', + ' ni := $123456789ABCDE;', + ' lw := lo(ni);', + ' lw := hi(ni);', + '']); + ConvertProgram; + CheckSource('TestLoHiFpcMode', + LinesToStr([ // statements + 'this.LoByte1 = 0x1234 & 0xFF;', + 'this.HiByte1 = (0x1234 >> 8) & 0xFF;', + 'this.LoByte2 = 0x1234 & 0xFF;', + 'this.HiByte2 = (0x1234 >> 8) & 0xFF;', + 'this.LoWord1 = 0x1234CDEF & 0xFFFF;', + 'this.HiWord1 = (0x1234CDEF >> 16) & 0xFFFF;', + 'this.LoWord2 = -0x1234CDEF & 0xFFFF;', + 'this.HiWord2 = (-0x1234CDEF >> 16) & 0xFFFF;', + 'this.lo4 = 0x34 & 0xF;', + 'this.hi4 = (0x34 >> 4) & 0xF;', + 'this.lo5 = (((-0x34 & 255) << 24) >> 24) & 0xFF;', + 'this.hi5 = ((((-0x34 & 255) << 24) >> 24) >> 8) & 0xFF;', + 'this.lo6 = 0x123456789ABCDE >>> 0;', + 'this.hi6 = 1193046 >>> 0;', + 'this.lo7 = -0x123456789ABCDE >>> 0;', + 'this.hi7 = Math.floor(-0x123456789ABCDE / 4294967296) >>> 0;', + 'this.b = 0;', + 'this.ss = 0;', + 'this.w = 0;', + 'this.si = 0;', + 'this.lw = 0;', + 'this.li = 0;', + 'this.b2 = 0;', + 'this.ni = 0;', + '']), + LinesToStr([ // this.$main + '$mod.w = 0x1234;', + '$mod.ss = -0x12;', + '$mod.b = $mod.ss & 0xFF;', + '$mod.b = ($mod.ss >> 8) & 0xFF;', + '$mod.b = $mod.w & 0xFF;', + '$mod.b = ($mod.w >> 8) & 0xFF;', + '$mod.b2 = $mod.b & 0xF;', + '$mod.b2 = ($mod.b >> 4) & 0xF;', + '$mod.lw = 0x1234CDEF;', + '$mod.w = $mod.lw & 0xFFFF;', + '$mod.w = ($mod.lw >> 16) & 0xFFFF;', + '$mod.ni = 0x123456789ABCDE;', + '$mod.lw = $mod.ni >>> 0;', + '$mod.lw = Math.floor($mod.ni / 4294967296) >>> 0;', + ''])); +end; + +procedure TTestModule.TestLoHiDelphiMode; +begin + StartProgram(false); + Add([ + '{$mode delphi}', + 'const', + ' LoByte1 = Lo(Word($1234));', + ' HiByte1 = Hi(Word($1234));', + ' LoByte2 = Lo(SmallInt($1234));', + ' HiByte2 = Hi(SmallInt($1234));', + ' LoByte3 = Lo($1234CDEF);', + ' HiByte3 = Hi($1234CDEF);', + ' LoByte4 = Lo(-$1234CDEF);', + ' HiByte4 = Hi(-$1234CDEF);', + 'var', + ' b: Byte;', + ' w: Word;', + ' si: SmallInt;', + ' lw: LongWord;', + ' li: LongInt;', + 'begin', + ' w := $1234;', + ' b := lo(w);', + ' b := HI(w);', + ' lw := $1234CDEF;', + ' b := lo(lw);', + ' b := hi(lw);', + '']); + ConvertProgram; + CheckSource('TestLoHiDelphiMode', + LinesToStr([ // statements + 'this.LoByte1 = 0x1234 & 0xFF;', + 'this.HiByte1 = (0x1234 >> 8) & 0xFF;', + 'this.LoByte2 = 0x1234 & 0xFF;', + 'this.HiByte2 = (0x1234 >> 8) & 0xFF;', + 'this.LoByte3 = 0x1234CDEF & 0xFF;', + 'this.HiByte3 = (0x1234CDEF >> 8) & 0xFF;', + 'this.LoByte4 = -0x1234CDEF & 0xFF;', + 'this.HiByte4 = (-0x1234CDEF >> 8) & 0xFF;', + 'this.b = 0;', + 'this.w = 0;', + 'this.si = 0;', + 'this.lw = 0;', + 'this.li = 0;' + ]), + LinesToStr([ // this.$main + '$mod.w = 0x1234;', + '$mod.b = $mod.w & 0xFF;', + '$mod.b = ($mod.w >> 8) & 0xFF;', + '$mod.lw = 0x1234CDEF;', + '$mod.b = $mod.lw & 0xFF;', + '$mod.b = ($mod.lw >> 8) & 0xFF;' + ])); +end; + procedure TTestModule.TestAssignments; begin StartProgram(false); diff --git a/utils/pas2js/fpmake.pp b/utils/pas2js/fpmake.pp index 6dcb3e5d5b..d8755c2762 100644 --- a/utils/pas2js/fpmake.pp +++ b/utils/pas2js/fpmake.pp @@ -26,8 +26,6 @@ begin P.Directory:=ADirectory; P.Version:='3.3.1'; P.OSes:=AllUnixOSes+AllBSDOSes+AllWindowsOSes-[WinCE]; - if Defaults.CPU=jvm then - P.OSes := P.OSes - [android]; P.Dependencies.Add('fcl-json'); P.Dependencies.Add('fcl-js'); P.Dependencies.Add('fcl-passrc');