Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
831f2663
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,发现更多精彩内容 >>
提交
831f2663
编写于
4月 19, 2017
作者:
N
Neal Gafter
提交者:
GitHub
4月 19, 2017
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Classify pattern-matching temp as long-lived (#18756)
Fixes #18257
上级
d3e82424
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
282 addition
and
95 deletion
+282
-95
src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs
src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs
+1
-1
src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs
...ompilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs
+2
-2
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
+83
-0
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
+133
-14
src/Compilers/Core/Portable/SynthesizedLocalKind.cs
src/Compilers/Core/Portable/SynthesizedLocalKind.cs
+9
-6
src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs
...onEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs
+54
-72
未找到文件。
src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs
浏览文件 @
831f2663
...
...
@@ -89,7 +89,7 @@ public static DecisionTree Create(BoundExpression expression, TypeSymbol type, S
{
// Unless it is a constant, the decision tree acts on a copy of the input expression.
// We create a temp to represent that copy. Lowering will assign into this temp.
temp
=
new
SynthesizedLocal
(
enclosingSymbol
as
MethodSymbol
,
type
,
SynthesizedLocalKind
.
PatternMatching
Temp
,
expression
.
Syntax
,
false
,
RefKind
.
None
);
temp
=
new
SynthesizedLocal
(
enclosingSymbol
as
MethodSymbol
,
type
,
SynthesizedLocalKind
.
PatternMatching
,
expression
.
Syntax
,
false
,
RefKind
.
None
);
expression
=
new
BoundLocal
(
expression
.
Syntax
,
temp
,
null
,
type
);
}
...
...
src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs
浏览文件 @
831f2663
...
...
@@ -237,7 +237,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern
if
(
forType
==
null
)
{
var
type
=
value
.
Value
.
Type
;
var
localSymbol
=
new
SynthesizedLocal
(
_enclosingSymbol
as
MethodSymbol
,
type
,
SynthesizedLocalKind
.
PatternMatching
Temp
,
Syntax
,
false
,
RefKind
.
None
);
var
localSymbol
=
new
SynthesizedLocal
(
_enclosingSymbol
as
MethodSymbol
,
type
,
SynthesizedLocalKind
.
PatternMatching
,
Syntax
,
false
,
RefKind
.
None
);
var
narrowedExpression
=
new
BoundLocal
(
Syntax
,
localSymbol
,
null
,
type
);
forType
=
new
DecisionTree
.
ByValue
(
narrowedExpression
,
value
.
Value
.
Type
.
TupleUnderlyingTypeOrSelf
(),
localSymbol
);
byType
.
TypeAndDecision
.
Add
(
new
KeyValuePair
<
TypeSymbol
,
DecisionTree
>(
value
.
Value
.
Type
,
forType
));
...
...
@@ -329,7 +329,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci
if
(
result
==
null
)
{
var
localSymbol
=
new
SynthesizedLocal
(
_enclosingSymbol
as
MethodSymbol
,
type
,
SynthesizedLocalKind
.
PatternMatching
Temp
,
Syntax
,
false
,
RefKind
.
None
);
var
localSymbol
=
new
SynthesizedLocal
(
_enclosingSymbol
as
MethodSymbol
,
type
,
SynthesizedLocalKind
.
PatternMatching
,
Syntax
,
false
,
RefKind
.
None
);
var
expression
=
new
BoundLocal
(
Syntax
,
localSymbol
,
null
,
type
);
result
=
makeDecision
(
expression
,
type
);
Debug
.
Assert
(
result
.
Temp
==
null
);
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
浏览文件 @
831f2663
...
...
@@ -4952,5 +4952,88 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute :
Diagnostic
(
ErrorCode
.
ERR_BadAsyncMethodBuilderTaskProperty
,
"{ await Task.Delay(5); throw null; }"
).
WithArguments
(
"MyTaskBuilder"
,
"MyTask"
,
"int"
).
WithLocation
(
7
,
29
)
);
}
[
Fact
,
WorkItem
(
18257
,
"https://github.com/dotnet/roslyn/issues/18257"
)]
public
void
PatternTempsAreLongLived
()
{
var
source
=
@"using System;
public class Foo {}
public class C {
public static void Main(string[] args)
{
var c = new C();
c.M(new Foo());
c.M(new object());
}
public async void M(object o) {
switch (o)
{
case Foo _:
Console.Write(0);
break;
default:
Console.Write(1);
break;
}
}
}"
;
var
expectedOutput
=
@"01"
;
var
compilation
=
CreateCompilation
(
source
,
options
:
TestOptions
.
ReleaseExe
);
base
.
CompileAndVerify
(
compilation
,
expectedOutput
:
expectedOutput
);
}
[
Fact
,
WorkItem
(
18257
,
"https://github.com/dotnet/roslyn/issues/18257"
)]
public
void
PatternTempsSpill
()
{
// This test exercises the spilling machinery of async for pattern-matching temps
var
source
=
@"using System;
using System.Threading.Tasks;
public class C {
public class Foo
{
public int Value;
}
public static void Main(string[] args)
{
var c = new C();
c.M(new Foo() { Value = 1 });
c.M(new Foo() { Value = 2 });
c.M(new Foo() { Value = 3 });
c.M(new object());
}
public void M(object o)
{
MAsync(o).Wait();
}
public async Task MAsync(object o) {
switch (o)
{
case Foo foo when await Copy(foo.Value) == 1:
Console.Write($""{foo.Value}=1 "");
break;
case Foo foo when await Copy(foo.Value) == 2:
Console.Write($""{foo.Value}=2 "");
break;
case Foo foo:
Console.Write($""{foo.Value} "");
break;
default:
Console.Write(""X "");
break;
}
}
public async Task<int> Copy(int i)
{
await Task.Delay(1);
return i;
}
}"
;
var
expectedOutput
=
@"1=1 2=2 3 X"
;
var
compilation
=
CreateCompilation
(
source
,
options
:
TestOptions
.
ReleaseExe
);
base
.
CompileAndVerify
(
compilation
,
expectedOutput
:
expectedOutput
);
}
}
}
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
浏览文件 @
831f2663
...
...
@@ -2705,9 +2705,9 @@ class Student : Person { public double GPA; }
<customDebugInfo>
<forward declaringType=""Program"" methodName=""Main"" parameterNames=""args"" />
<encLocalSlotMap>
<slot kind=""
temp
"" />
<slot kind=""
temp
"" />
<slot kind=""
temp
"" />
<slot kind=""
35"" offset=""11
"" />
<slot kind=""
35"" offset=""46
"" />
<slot kind=""
35"" offset=""237
"" />
<slot kind=""temp"" />
<slot kind=""0"" offset=""59"" />
<slot kind=""0"" offset=""163"" />
...
...
@@ -2798,9 +2798,9 @@ class Student : Person { public double GPA; }
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""383"" />
<slot kind=""
temp
"" />
<slot kind=""
temp
"" />
<slot kind=""
temp
"" />
<slot kind=""
35"" offset=""11
"" />
<slot kind=""
35"" offset=""46
"" />
<slot kind=""
35"" offset=""249
"" />
<slot kind=""temp"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
...
...
@@ -5827,6 +5827,122 @@ static void M(object o)
}
}"
;
var
c
=
CreateCompilationWithMscorlibAndSystemCore
(
source
,
options
:
TestOptions
.
DebugDll
);
CompileAndVerify
(
c
).
VerifyIL
(
"Program.M"
,
@"{
// Code size 210 (0xd2)
.maxstack 2
.locals init (object V_0,
int V_1,
object V_2,
object V_3,
int? V_4,
int V_5,
object V_6,
int V_7,
object V_8,
object V_9,
object V_10)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.2
IL_0003: ldloc.2
IL_0004: stloc.3
IL_0005: ldloc.3
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brtrue.s IL_000c
IL_000a: br.s IL_005e
IL_000c: ldloc.0
IL_000d: isinst ""int?""
IL_0012: unbox.any ""int?""
IL_0017: stloc.s V_4
IL_0019: ldloca.s V_4
IL_001b: call ""int int?.GetValueOrDefault()""
IL_0020: stloc.1
IL_0021: ldloca.s V_4
IL_0023: call ""bool int?.HasValue.get""
IL_0028: brfalse.s IL_005e
IL_002a: ldloc.1
IL_002b: stloc.s V_5
IL_002d: ldloc.s V_5
IL_002f: ldc.i4.1
IL_0030: sub
IL_0031: switch (
IL_004c,
IL_0054,
IL_005a,
IL_0052,
IL_0058)
IL_004a: br.s IL_005e
IL_004c: br.s IL_0060
IL_004e: br.s IL_006c
IL_0050: br.s IL_007a
IL_0052: br.s IL_006a
IL_0054: br.s IL_0065
IL_0056: br.s IL_005e
IL_0058: br.s IL_0076
IL_005a: br.s IL_0071
IL_005c: br.s IL_005e
IL_005e: br.s IL_0078
IL_0060: ldarg.0
IL_0061: brfalse.s IL_006a
IL_0063: br.s IL_004e
IL_0065: ldarg.0
IL_0066: brfalse.s IL_006a
IL_0068: br.s IL_0056
IL_006a: br.s IL_007c
IL_006c: ldarg.0
IL_006d: brtrue.s IL_0076
IL_006f: br.s IL_0050
IL_0071: ldarg.0
IL_0072: brtrue.s IL_0076
IL_0074: br.s IL_005c
IL_0076: br.s IL_007c
IL_0078: br.s IL_007c
IL_007a: br.s IL_007c
IL_007c: ldarg.0
IL_007d: stloc.2
IL_007e: ldloc.2
IL_007f: stloc.s V_8
IL_0081: ldloc.s V_8
IL_0083: stloc.s V_6
IL_0085: ldloc.s V_6
IL_0087: brtrue.s IL_008b
IL_0089: br.s IL_00b8
IL_008b: ldloc.s V_6
IL_008d: isinst ""int?""
IL_0092: unbox.any ""int?""
IL_0097: stloc.s V_4
IL_0099: ldloca.s V_4
IL_009b: call ""int int?.GetValueOrDefault()""
IL_00a0: stloc.s V_7
IL_00a2: ldloca.s V_4
IL_00a4: call ""bool int?.HasValue.get""
IL_00a9: brfalse.s IL_00b8
IL_00ab: ldloc.s V_7
IL_00ad: stloc.s V_5
IL_00af: ldloc.s V_5
IL_00b1: ldc.i4.1
IL_00b2: beq.s IL_00b6
IL_00b4: br.s IL_00b8
IL_00b6: br.s IL_00ba
IL_00b8: br.s IL_00bc
IL_00ba: br.s IL_00be
IL_00bc: br.s IL_00be
IL_00be: ldarg.0
IL_00bf: stloc.2
IL_00c0: ldloc.2
IL_00c1: stloc.s V_10
IL_00c3: ldloc.s V_10
IL_00c5: stloc.s V_9
IL_00c7: ldloc.s V_9
IL_00c9: brtrue.s IL_00cd
IL_00cb: br.s IL_00cd
IL_00cd: br.s IL_00cf
IL_00cf: br.s IL_00d1
IL_00d1: ret
}
"
);
c
.
VerifyPdb
(
@"<symbols>
<methods>
...
...
@@ -5836,13 +5952,16 @@ static void M(object o)
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""
temp
"" />
<slot kind=""
temp
"" />
<slot kind=""
35"" offset=""11
"" />
<slot kind=""
35"" offset=""46
"" />
<slot kind=""temp"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""413"" />
<slot kind=""1"" offset=""378"" />
<slot kind=""35"" offset=""511"" />
<slot kind=""1"" offset=""511"" />
</encLocalSlotMap>
</customDebugInfo>
...
...
@@ -5860,12 +5979,12 @@ static void M(object o)
<entry offset=""0x7a"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" />
<entry offset=""0x7c"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" />
<entry offset=""0x81"" hidden=""true"" />
<entry offset=""0xb
5
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" />
<entry offset=""0xb
7
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" />
<entry offset=""0xb
9
"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" />
<entry offset=""0x
be
"" hidden=""true"" />
<entry offset=""0xc
8
"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" />
<entry offset=""0x
ca
"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" />
<entry offset=""0xb
a
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" />
<entry offset=""0xb
c
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" />
<entry offset=""0xb
e
"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" />
<entry offset=""0x
c3
"" hidden=""true"" />
<entry offset=""0xc
f
"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" />
<entry offset=""0x
d1
"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" />
</sequencePoints>
</method>
</methods>
...
...
src/Compilers/Core/Portable/SynthesizedLocalKind.cs
浏览文件 @
831f2663
...
...
@@ -32,11 +32,6 @@ internal enum SynthesizedLocalKind
/// </summary>
FrameCache
=
-
5
,
/// <summary>
/// Temp created for pattern matching by type.
/// </summary>
PatternMatchingTemp
=
-
4
,
/// <summary>
/// Temp variable created by the optimizer.
/// </summary>
...
...
@@ -53,7 +48,9 @@ internal enum SynthesizedLocalKind
EmitterTemp
=
-
1
,
/// <summary>
/// The variable is not synthesized (C#, VB).
/// The variable is not synthesized (C#, VB). Note that SynthesizedLocalKind values
/// greater than or equal to this are considered long-lived;
/// see <see cref="SynthesizedLocalKindExtensions.IsLongLived"/>.
/// </summary>
UserDefined
=
0
,
...
...
@@ -206,6 +203,12 @@ internal enum SynthesizedLocalKind
/// </summary>
InstrumentationPayload
=
34
,
/// <summary>
/// Temp created for pattern matching by type. This holds the value of an input value provisionally
/// converted to the type against which it is being matched.
/// </summary>
PatternMatching
=
35
,
/// <summary>
/// All values have to be less than or equal to <see cref="MaxValidValueForLocalVariableSerializedToDebugInformation"/>
/// (<see cref="EditAndContinueMethodDebugInformation"/>)
...
...
src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs
浏览文件 @
831f2663
...
...
@@ -607,15 +607,13 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""C C.<M>d__1.<>4__this""
IL_0006: ret
...
...
@@ -627,15 +625,13 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""object C.<M>d__1.o""
IL_0006: ret
...
...
@@ -647,20 +643,17 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""string C.<M>d__1.<a>5__
1
""
IL_0001: ldfld ""string C.<M>d__1.<a>5__
4
""
IL_0006: ret
}
"
);
}"
);
VerifyLocal
(
testData
,
typeName
,
locals
[
3
],
"<>m3"
,
"s"
,
expectedILOpt
:
@"{
// Code size 7 (0x7)
...
...
@@ -668,17 +661,15 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""string C.<M>d__1.<s>5__
2
""
IL_0001: ldfld ""string C.<M>d__1.<s>5__
5
""
IL_0006: ret
}"
);
locals
.
Free
();
...
...
@@ -699,15 +690,13 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""C C.<M>d__1.<>4__this""
IL_0006: ret
...
...
@@ -719,15 +708,13 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""object C.<M>d__1.o""
IL_0006: ret
...
...
@@ -739,20 +726,17 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""string C.<M>d__1.<a>5__
1
""
IL_0001: ldfld ""string C.<M>d__1.<a>5__
4
""
IL_0006: ret
}
"
);
}"
);
VerifyLocal
(
testData
,
typeName
,
locals
[
3
],
"<>m3"
,
"s"
,
expectedILOpt
:
@"{
// Code size 7 (0x7)
...
...
@@ -760,17 +744,15 @@ .maxstack 1
.locals init (int V_0,
object V_1,
object V_2,
string
V_3,
int
V_4,
object
V_5,
object
V_6,
int?
V_7,
object
V_3,
string
V_4,
int?
V_5,
System.Runtime.CompilerServices.TaskAwaiter<object>
V_6,
C.<M>d__1
V_7,
System.Runtime.CompilerServices.TaskAwaiter<object> V_8,
C.<M>d__1 V_9,
System.Runtime.CompilerServices.TaskAwaiter<object> V_10,
System.Exception V_11)
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<M>d__1.<s>5__
3
""
IL_0001: ldfld ""int C.<M>d__1.<s>5__
6
""
IL_0006: ret
}"
);
locals
.
Free
();
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录