* Patch from Mattias Gaertner

- quote object keys if needed  { "1":"value" }
  - use " or ' for string literals

git-svn-id: trunk@35416 -
This commit is contained in:
michael 2017-02-10 23:24:13 +00:00
parent 203bd85c38
commit 55241e283f
2 changed files with 111 additions and 11 deletions

View File

@ -26,6 +26,8 @@ Type
TJSType = (jstUNDEFINED,jstNull,jstBoolean,jstNumber,jstString,jstObject,jstReference,JSTCompletion);
TJSString = UnicodeString;
TJSChar = WideChar;
TJSPChar = PWideChar;
TJSNumber = Double;
{ TJSValue }

View File

@ -83,8 +83,14 @@ Type
Property AsUnicodeString : UnicodeString Read GetUnicodeString;
end;
TJSEscapeQuote = (
jseqSingle,
jseqDouble,
jseqBoth
);
{ TJSWriter }
TWriteOption = (woCompact,
woUseUTF8,
woTabIndent,
@ -151,7 +157,7 @@ Type
Procedure WritePrimaryExpression(El: TJSPrimaryExpression);virtual;
Procedure WriteBinary(El: TJSBinary);virtual;
Public
Function EscapeString(const S: TJSString): String;
Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): String;
Function JSStringToStr(const S: TJSString): string;
Constructor Create(AWriter : TTextWriter);
Constructor Create(Const AFileName : String);
@ -164,7 +170,9 @@ Type
Property IndentSize : Byte Read FIndentSize Write FIndentSize;
Property UseUTF8 : Boolean Read GetUseUTF8;
end;
EJSWriter = CLass(Exception);
EJSWriter = Class(Exception);
function IsValidJSIdentifier(Name: TJSString; AllowEscapeSeq: boolean = false): boolean;
implementation
@ -172,6 +180,85 @@ Resourcestring
SErrUnknownJSClass = 'Unknown javascript element class : %s';
SErrNilNode = 'Nil node in Javascript';
function IsValidJSIdentifier(Name: TJSString; AllowEscapeSeq: boolean): boolean;
var
p: TJSPChar;
i: Integer;
begin
Result:=false;
if Name='' then exit;
p:=TJSPChar(Name);
repeat
case p^ of
#0:
if p-TJSPChar(Name)=length(Name) then
exit(true)
else
exit;
'0'..'9':
if p=TJSPChar(Name) then
exit
else
inc(p);
'a'..'z','A'..'Z','_','$': inc(p);
'\':
begin
if not AllowEscapeSeq then exit;
inc(p);
if p^='x' then
begin
// \x00
for i:=1 to 2 do
begin
inc(p);
if not (p^ in ['0'..'9','a'..'f','A'..'F']) then exit;
end;
end
else if p^='u' then
begin
inc(p);
if p^='{' then
begin
// \u{00000}
i:=0;
repeat
inc(p);
case p^ of
'}': break;
'0'..'9': i:=i*16+ord(p^)-ord('0');
'a'..'f': i:=i*16+ord(p^)-ord('a')+10;
'A'..'F': i:=i*16+ord(p^)-ord('A')+10;
else exit;
end;
if i>$10FFFF then exit;
until false;
inc(p);
end
else
begin
// \u0000
for i:=1 to 4 do
begin
inc(p);
if not (p^ in ['0'..'9','a'..'f','A'..'F']) then exit;
end;
end;
end
else
exit; // unknown sequence
end;
#$200C,#$200D: inc(p); // zero width non-joiner/joiner
#$00AA..#$2000,
#$200E..#$D7FF:
inc(p); // ToDo: only those with ID_START/ID_CONTINUE see https://codepoints.net/search?IDC=1
#$D800..#$DBFF:
inc(p,2); // see above
else
exit;
end;
until false;
end;
{ TBufferWriter }
function TBufferWriter.GetBufferLength: Integer;
@ -380,27 +467,30 @@ begin
end;
end;
function TJSWriter.EscapeString(const S: TJSString): String;
function TJSWriter.EscapeString(const S: TJSString; Quote: TJSEscapeQuote
): String;
Var
I,J,L : Integer;
P : PWideChar;
P : TJSPChar;
begin
I:=1;
J:=1;
Result:='';
L:=Length(S);
P:=PWideChar(S);
P:=TJSPChar(S);
While I<=L do
begin
if (P^ in ['"','/','\',#8,#9,#10,#12,#13]) then
if (P^ in [#0..#31,'"','''','/','\']) then
begin
Result:=Result+JSStringToStr(Copy(S,J,I-J));
Case P^ of
'\' : Result:=Result+'\\';
'/' : Result:=Result+'\/';
'"' : Result:=Result+'\"';
'"' : if Quote=jseqSingle then Result:=Result+'"' else Result:=Result+'\"';
'''': if Quote=jseqDouble then Result:=Result+'''' else Result:=Result+'\''';
#0..#7,#11,#14..#31: Result:=Result+'\x'+hexStr(ord(P^),2);
#8 : Result:=Result+'\b';
#9 : Result:=Result+'\t';
#10 : Result:=Result+'\n';
@ -427,6 +517,7 @@ procedure TJSWriter.WriteValue(V: TJSValue);
Var
S : String;
JS: TJSString;
begin
if V.CustomValue<>'' then
S:=JSStringToStr(V.CustomValue)
@ -435,7 +526,14 @@ begin
jstUNDEFINED : S:='undefined';
jstNull : s:='null';
jstBoolean : if V.AsBoolean then s:='true' else s:='false';
jstString : S:='"'+EscapeString(V.AsString)+'"';
jstString :
begin
JS:=V.AsString;
if Pos('"',JS)>0 then
S:=''''+EscapeString(JS,jseqSingle)+''''
else
S:='"'+EscapeString(JS,jseqDouble)+'"';
end;
jstNumber :
if Frac(V.AsNumber)=0 then // this needs to be improved
Str(Round(V.AsNumber),S)
@ -544,10 +642,10 @@ procedure TJSWriter.WriteRegularExpressionLiteral(
begin
Write('/');
Write(EscapeString(El.Pattern.AsString));
Write(EscapeString(El.Pattern.AsString,jseqBoth));
Write('/');
If Assigned(El.PatternFlags) then
Write(EscapeString(El.PatternFlags.AsString));
Write(EscapeString(El.PatternFlags.AsString,jseqBoth));
end;
procedure TJSWriter.WriteLiteral(El: TJSLiteral);
@ -642,7 +740,7 @@ begin
For I:=0 to C do
begin
S:=El.Elements[i].Name;
if QE then
if QE or not IsValidJSIdentifier(S) then
S:='"'+S+'"';
Write(S+': ');
Indent;