Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
539cfa82
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
539cfa82
编写于
9月 25, 2014
作者:
P
Pharring
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
PERF: Reuse the XmlReader used to parse Xml doc comments (changeset 1342552)
上级
751847f2
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
342 addition
and
103 deletion
+342
-103
Src/Workspaces/Core/Shared/Utilities/DocumentationComment.cs
Src/Workspaces/Core/Shared/Utilities/DocumentationComment.cs
+127
-103
Src/Workspaces/Core/Shared/Utilities/XmlFragmentParser.cs
Src/Workspaces/Core/Shared/Utilities/XmlFragmentParser.cs
+214
-0
Src/Workspaces/Core/Workspaces.csproj
Src/Workspaces/Core/Workspaces.csproj
+1
-0
未找到文件。
Src/Workspaces/Core/Shared/Utilities/DocumentationComment.cs
浏览文件 @
539cfa82
...
...
@@ -67,9 +67,9 @@ internal sealed class DocumentationComment
private
DocumentationComment
()
{
ParameterNames
=
ImmutableArray
.
Create
<
string
>()
;
TypeParameterNames
=
ImmutableArray
.
Create
<
string
>()
;
ExceptionTypes
=
ImmutableArray
.
Create
<
string
>()
;
ParameterNames
=
ImmutableArray
<
string
>.
Empty
;
TypeParameterNames
=
ImmutableArray
<
string
>.
Empty
;
ExceptionTypes
=
ImmutableArray
<
string
>.
Empty
;
}
/// <summary>
...
...
@@ -79,126 +79,150 @@ private DocumentationComment()
/// <returns>A DocumentationComment instance.</returns>
public
static
DocumentationComment
FromXmlFragment
(
string
xml
)
{
try
return
CommentBuilder
.
Parse
(
xml
);
}
/// <summary>
/// Helper class for parsing XML doc comments. Encapsulates the state required during parsing.
/// </summary>
private
class
CommentBuilder
{
private
readonly
DocumentationComment
comment
;
private
ImmutableArray
<
string
>.
Builder
parameterNamesBuilder
=
null
;
private
ImmutableArray
<
string
>.
Builder
typeParameterNamesBuilder
=
null
;
private
ImmutableArray
<
string
>.
Builder
exceptionTypesBuilder
=
null
;
private
Dictionary
<
string
,
ImmutableArray
<
string
>.
Builder
>
exceptionTextBuilders
=
null
;
/// <summary>
/// Parse and construct a <see cref="DocumentationComment" /> from the given fragment of XML.
/// </summary>
/// <param name="xml">The fragment of XML to parse.</param>
/// <returns>A DocumentationComment instance.</returns>
public
static
DocumentationComment
Parse
(
string
xml
)
{
// TODO: probably want to preserve whitespace (DevDiv #13045).
XmlReader
reader
=
XmlReader
.
Create
(
new
StringReader
(
xml
),
new
XmlReaderSettings
{
ConformanceLevel
=
ConformanceLevel
.
Fragment
});
try
{
return
new
CommentBuilder
(
xml
).
ParseInternal
(
xml
);
}
catch
(
Exception
)
{
// It would be nice if we only had to catch XmlException to handle invalid XML
// while parsing doc comments. Unfortunately, other exceptions can also occur,
// so we just catch them all. See Dev12 Bug 612456 for an example.
return
new
DocumentationComment
{
FullXmlFragment
=
xml
,
HadXmlParseError
=
true
};
}
}
DocumentationComment
comment
=
new
DocumentationComment
();
comment
.
FullXmlFragment
=
xml
;
private
CommentBuilder
(
string
xml
)
{
comment
=
new
DocumentationComment
()
{
FullXmlFragment
=
xml
};
}
List
<
string
>
parameterNamesBuilder
=
new
List
<
string
>();
List
<
string
>
typeParameterNamesBuilder
=
new
List
<
string
>();
List
<
string
>
exceptionTypesBuilder
=
new
List
<
string
>(
);
private
DocumentationComment
ParseInternal
(
string
xml
)
{
XmlFragmentParser
.
ParseFragment
(
xml
,
ParseCallback
,
this
);
try
if
(
exceptionTextBuilders
!=
null
)
{
Dictionary
<
string
,
List
<
string
>>
exceptionTextBuilders
=
new
Dictionary
<
string
,
List
<
string
>>();
foreach
(
var
typeAndBuilderPair
in
exceptionTextBuilders
)
{
comment
.
exceptionTexts
.
Add
(
typeAndBuilderPair
.
Key
,
typeAndBuilderPair
.
Value
.
AsImmutable
());
}
}
comment
.
ParameterNames
=
parameterNamesBuilder
==
null
?
ImmutableArray
<
string
>.
Empty
:
parameterNamesBuilder
.
ToImmutable
();
comment
.
TypeParameterNames
=
typeParameterNamesBuilder
==
null
?
ImmutableArray
<
string
>.
Empty
:
typeParameterNamesBuilder
.
ToImmutable
();
comment
.
ExceptionTypes
=
exceptionTypesBuilder
==
null
?
ImmutableArray
<
string
>.
Empty
:
exceptionTypesBuilder
.
ToImmutable
();
return
comment
;
}
while
(!
reader
.
EOF
)
private
static
void
ParseCallback
(
XmlReader
reader
,
CommentBuilder
builder
)
{
builder
.
ParseCallback
(
reader
);
}
private
void
ParseCallback
(
XmlReader
reader
)
{
if
(
reader
.
NodeType
==
XmlNodeType
.
Element
)
{
string
localName
=
reader
.
LocalName
;
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ExampleElementName
)
&&
comment
.
ExampleText
==
null
)
{
comment
.
ExampleText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
SummaryElementName
)
&&
comment
.
SummaryText
==
null
)
{
comment
.
SummaryText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ReturnsElementName
)
&&
comment
.
ReturnsText
==
null
)
{
comment
.
ReturnsText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
RemarksElementName
)
&&
comment
.
RemarksText
==
null
)
{
comment
.
RemarksText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ParameterElementName
))
{
if
(
reader
.
IsStartElement
())
string
name
=
reader
.
GetAttribute
(
XmlNames
.
NameAttributeName
);
string
paramText
=
reader
.
ReadInnerXml
();
if
(!
string
.
IsNullOrWhiteSpace
(
name
)
&&
!
comment
.
parameterTexts
.
ContainsKey
(
name
))
{
string
localName
=
reader
.
LocalName
;
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ExampleElementName
)
&&
comment
.
ExampleText
==
null
)
{
comment
.
ExampleText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
SummaryElementName
)
&&
comment
.
SummaryText
==
null
)
{
comment
.
SummaryText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ReturnsElementName
)
&&
comment
.
ReturnsText
==
null
)
{
comment
.
ReturnsText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
RemarksElementName
)
&&
comment
.
RemarksText
==
null
)
{
comment
.
RemarksText
=
reader
.
ReadInnerXml
().
Trim
();
// TODO: trim each line
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ParameterElementName
))
{
string
name
=
reader
.
GetAttribute
(
XmlNames
.
NameAttributeName
);
string
paramText
=
reader
.
ReadInnerXml
();
if
(!
string
.
IsNullOrWhiteSpace
(
name
)
&&
!
comment
.
parameterTexts
.
ContainsKey
(
name
))
{
parameterNamesBuilder
.
Add
(
name
);
comment
.
parameterTexts
.
Add
(
name
,
paramText
.
Trim
());
// TODO: trim each line
}
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
TypeParameterElementName
))
{
string
name
=
reader
.
GetAttribute
(
XmlNames
.
NameAttributeName
);
string
typeParamText
=
reader
.
ReadInnerXml
();
if
(!
string
.
IsNullOrWhiteSpace
(
name
)
&&
!
comment
.
typeParameterTexts
.
ContainsKey
(
name
))
{
typeParameterNamesBuilder
.
Add
(
name
);
comment
.
typeParameterTexts
.
Add
(
name
,
typeParamText
.
Trim
());
// TODO: trim each line
}
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ExceptionElementName
))
{
string
type
=
reader
.
GetAttribute
(
XmlNames
.
CrefAttributeName
);
string
exceptionText
=
reader
.
ReadInnerXml
();
if
(!
string
.
IsNullOrWhiteSpace
(
type
))
{
if
(!
exceptionTextBuilders
.
ContainsKey
(
type
))
{
exceptionTypesBuilder
.
Add
(
type
);
exceptionTextBuilders
.
Add
(
type
,
new
List
<
string
>());
}
exceptionTextBuilders
[
type
].
Add
(
exceptionText
);
}
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
CompletionListElementName
))
{
string
cref
=
reader
.
GetAttribute
(
XmlNames
.
CrefAttributeName
);
if
(!
string
.
IsNullOrWhiteSpace
(
cref
))
{
comment
.
CompletionListCref
=
cref
;
}
(
parameterNamesBuilder
??
(
parameterNamesBuilder
=
ImmutableArray
.
CreateBuilder
<
string
>())).
Add
(
name
);
comment
.
parameterTexts
.
Add
(
name
,
paramText
.
Trim
());
// TODO: trim each line
}
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
TypeParameterElementName
))
{
string
name
=
reader
.
GetAttribute
(
XmlNames
.
NameAttributeName
);
string
typeParamText
=
reader
.
ReadInnerXml
();
reader
.
ReadInnerXml
();
}
else
if
(!
string
.
IsNullOrWhiteSpace
(
name
)
&&
!
comment
.
typeParameterTexts
.
ContainsKey
(
name
))
{
(
typeParameterNamesBuilder
??
(
typeParameterNamesBuilder
=
ImmutableArray
.
CreateBuilder
<
string
>())).
Add
(
name
);
comment
.
typeParameterTexts
.
Add
(
name
,
typeParamText
.
Trim
());
// TODO: trim each line
}
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
ExceptionElementName
))
{
string
type
=
reader
.
GetAttribute
(
XmlNames
.
CrefAttributeName
);
string
exceptionText
=
reader
.
ReadInnerXml
();
if
(!
string
.
IsNullOrWhiteSpace
(
type
))
{
if
(
exceptionTextBuilders
==
null
||
!
exceptionTextBuilders
.
ContainsKey
(
type
))
{
// This is an element we don't handle. Skip it.
reader
.
Read
(
);
(
exceptionTypesBuilder
??
(
exceptionTypesBuilder
=
ImmutableArray
.
CreateBuilder
<
string
>())).
Add
(
type
);
(
exceptionTextBuilders
??
(
exceptionTextBuilders
=
new
Dictionary
<
string
,
ImmutableArray
<
string
>.
Builder
>())).
Add
(
type
,
ImmutableArray
.
CreateBuilder
<
string
>()
);
}
exceptionTextBuilders
[
type
].
Add
(
exceptionText
);
}
else
}
else
if
(
XmlNames
.
ElementEquals
(
localName
,
XmlNames
.
CompletionListElementName
))
{
string
cref
=
reader
.
GetAttribute
(
XmlNames
.
CrefAttributeName
);
if
(!
string
.
IsNullOrWhiteSpace
(
cref
))
{
// We came across something that isn't a start element, like a block of text.
// Skip it.
reader
.
Read
();
comment
.
CompletionListCref
=
cref
;
}
}
foreach
(
var
typeAndBuilderPair
in
exceptionTextBuilders
)
reader
.
ReadInnerXml
();
}
else
{
comment
.
exceptionTexts
.
Add
(
typeAndBuilderPair
.
Key
,
typeAndBuilderPair
.
Value
.
AsImmutable
()
);
// This is an element we don't handle. Skip it.
reader
.
Read
(
);
}
}
finally
else
{
comment
.
ParameterNames
=
parameterNamesBuilder
.
AsImmutable
();
comment
.
TypeParameterNames
=
typeParameterNamesBuilder
.
AsImmutable
();
comment
.
ExceptionTypes
=
exceptionTypesBuilder
.
AsImmutable
();
// We came across something that isn't a start element, like a block of text.
// Skip it.
reader
.
Read
();
}
return
comment
;
}
catch
(
Exception
)
{
// It would be nice if we only had to catch XmlException to handle invalid XML
// while parsing doc comments. Unfortunately, other exceptions can also occur,
// so we just catch them all. See Dev12 Bug 612456 for an example.
return
new
DocumentationComment
{
FullXmlFragment
=
xml
,
HadXmlParseError
=
true
};
}
}
...
...
Src/Workspaces/Core/Shared/Utilities/XmlFragmentParser.cs
0 → 100644
浏览文件 @
539cfa82
using
System
;
using
System.Diagnostics
;
using
System.IO
;
using
System.Xml
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.Shared.Utilities
{
/// <summary>
/// An XML parser that is designed to parse small fragments of XML such as those that appear in documentation comments.
/// PERF: We try to re-use the same underlying <see cref="XmlReader"/> to reduce the allocation costs of multiple parses.
/// </summary>
internal
sealed
class
XmlFragmentParser
{
private
XmlReader
xmlReader
;
private
readonly
Reader
textReader
=
new
Reader
();
private
static
readonly
ObjectPool
<
XmlFragmentParser
>
pool
=
new
ObjectPool
<
XmlFragmentParser
>(()
=>
new
XmlFragmentParser
(),
size
:
2
);
/// <summary>
/// Parse the given XML fragment. The given callback is executed until either the end of the fragment
/// is reached or an exception occurs.
/// </summary>
/// <typeparam name="TArg">Type of an additional argument passed to the <paramref name="callback"/> delegate.</typeparam>
/// <param name="xmlFragment">The fragment to parse.</param>
/// <param name="callback">Action to execute while there is still more to read.</param>
/// <param name="arg">Additional argument passed to the callback.</param>
/// <remarks>
/// It is important that the <paramref name="callback"/> action advances the <see cref="XmlReader"/>,
/// otherwise parsing will never complete.
/// </remarks>
public
static
void
ParseFragment
<
TArg
>(
string
xmlFragment
,
Action
<
XmlReader
,
TArg
>
callback
,
TArg
arg
)
{
var
instance
=
pool
.
Allocate
();
try
{
instance
.
ParseInternal
(
xmlFragment
,
callback
,
arg
);
}
finally
{
pool
.
Free
(
instance
);
}
}
private
void
ParseInternal
<
TArg
>(
string
text
,
Action
<
XmlReader
,
TArg
>
callback
,
TArg
arg
)
{
textReader
.
SetText
(
text
);
if
(
xmlReader
==
null
)
{
xmlReader
=
XmlReader
.
Create
(
textReader
);
}
try
{
while
(!
ReachedEnd
)
{
if
(
BeforeStart
)
{
// Skip over the synthetic root element and first node
xmlReader
.
Read
();
}
else
{
callback
(
xmlReader
,
arg
);
}
}
// Read the final EndElement to reset things for the next user.
xmlReader
.
ReadEndElement
();
}
catch
{
// The reader is in a bad state, so dispose of it and recreate a new one next time we get called.
xmlReader
.
Dispose
();
xmlReader
=
null
;
textReader
.
Reset
();
throw
;
}
}
private
bool
BeforeStart
{
get
{
// Depth 0 = Document root
// Depth 1 = Synthetic wrapper, "CurrentElement"
// Depth 2 = Start of user's fragment.
return
xmlReader
.
Depth
<
2
;
}
}
private
bool
ReachedEnd
{
get
{
return
xmlReader
.
Depth
==
1
&&
xmlReader
.
NodeType
==
XmlNodeType
.
EndElement
&&
xmlReader
.
LocalName
==
Reader
.
CurrentElementName
;
}
}
/// <summary>
/// A text reader over a synthesized XML stream consisting of a single root element followed by a potentially
/// infinite stream of fragments. Each time "SetText" is called the stream is rewound to the element immediately
/// following the synthetic root node.
/// </summary>
private
sealed
class
Reader
:
TextReader
{
/// <summary>
/// Current text to validate.
/// </summary>
private
string
text
;
private
int
position
;
// Base the root element name on a GUID to avoid accidental (or intentional) collisions. An underscore is
// prefixed because element names must not start with a number.
private
static
readonly
string
RootElementName
=
"_"
+
Guid
.
NewGuid
().
ToString
(
"N"
);
// We insert an extra synthetic element name to allow for raw text at the root
internal
static
readonly
string
CurrentElementName
=
"_"
+
Guid
.
NewGuid
().
ToString
(
"N"
);
private
static
readonly
string
RootStart
=
"<"
+
RootElementName
+
">"
;
private
static
readonly
string
CurrentStart
=
"<"
+
CurrentElementName
+
">"
;
private
static
readonly
string
CurrentEnd
=
"</"
+
CurrentElementName
+
">"
;
public
void
Reset
()
{
this
.
text
=
null
;
this
.
position
=
0
;
}
public
void
SetText
(
string
text
)
{
this
.
text
=
text
;
// The first read shall read the <root>,
// the subsequents reads shall start with <current> element
if
(
this
.
position
>
0
)
{
this
.
position
=
RootStart
.
Length
;
}
}
public
override
int
Read
(
char
[]
buffer
,
int
index
,
int
count
)
{
if
(
count
==
0
)
{
return
0
;
}
// The stream synthesizes an XML document with:
// 1. A root element start tag
// 2. Current element start tag
// 3. The user text (xml fragments)
// 4. Current element end tag
int
initialCount
=
count
;
// <root>
position
+=
EncodeAndAdvance
(
RootStart
,
position
,
buffer
,
ref
index
,
ref
count
);
// <current>
position
+=
EncodeAndAdvance
(
CurrentStart
,
position
-
RootStart
.
Length
,
buffer
,
ref
index
,
ref
count
);
// text
position
+=
EncodeAndAdvance
(
text
,
position
-
RootStart
.
Length
-
CurrentStart
.
Length
,
buffer
,
ref
index
,
ref
count
);
// </current>
position
+=
EncodeAndAdvance
(
CurrentEnd
,
position
-
RootStart
.
Length
-
CurrentStart
.
Length
-
text
.
Length
,
buffer
,
ref
index
,
ref
count
);
// Pretend that the stream is infinite, i.e. never return 0 characters read.
if
(
initialCount
==
count
)
{
buffer
[
index
++]
=
' '
;
count
--;
}
return
initialCount
-
count
;
}
private
static
int
EncodeAndAdvance
(
string
src
,
int
srcIndex
,
char
[]
dest
,
ref
int
destIndex
,
ref
int
destCount
)
{
if
(
destCount
==
0
||
srcIndex
<
0
||
srcIndex
>=
src
.
Length
)
{
return
0
;
}
int
charCount
=
Math
.
Min
(
src
.
Length
-
srcIndex
,
destCount
);
Debug
.
Assert
(
charCount
>
0
);
src
.
CopyTo
(
srcIndex
,
dest
,
destIndex
,
charCount
);
destIndex
+=
charCount
;
destCount
-=
charCount
;
Debug
.
Assert
(
destCount
>=
0
);
return
charCount
;
}
public
override
int
Read
()
{
// XmlReader does not call this API
throw
ExceptionUtilities
.
Unreachable
;
}
public
override
int
Peek
()
{
// XmlReader does not call this API
throw
ExceptionUtilities
.
Unreachable
;
}
}
}
}
Src/Workspaces/Core/Workspaces.csproj
浏览文件 @
539cfa82
...
...
@@ -355,6 +355,7 @@
<Compile
Include=
"Log\Logger.LogBlock.cs"
/>
<Compile
Include=
"Log\LogMessage.cs"
/>
<Compile
Include=
"Shared\Extensions\ITypeSymbolExtensions.ReplaceTypeParameterBasedOnTypeConstraintVisitor.cs"
/>
<Compile
Include=
"Shared\Utilities\XmlFragmentParser.cs"
/>
<Compile
Include=
"Simplification\SimplifyTypeNameCodeAction.cs"
/>
<Compile
Include=
"Utilities\WeakEventHandler.cs"
/>
<Compile
Include=
"Versions\Extensions.cs"
/>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录