Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
硅谷海盗
kotlin
提交
d17f095e
K
kotlin
项目概览
硅谷海盗
/
kotlin
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kotlin
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
d17f095e
编写于
4月 21, 2011
作者:
D
Dmitry Jemerov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
namespace properties are mostly working
上级
c91cd748
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
216 addition
and
20 deletion
+216
-20
idea/src/org/jetbrains/jet/codegen/ClassCodegen.java
idea/src/org/jetbrains/jet/codegen/ClassCodegen.java
+4
-2
idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java
idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java
+23
-4
idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java
idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java
+11
-8
idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java
idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java
+24
-0
idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java
idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java
+3
-2
idea/src/org/jetbrains/jet/codegen/PropertyCodegen.java
idea/src/org/jetbrains/jet/codegen/PropertyCodegen.java
+55
-1
idea/src/org/jetbrains/jet/codegen/StackValue.java
idea/src/org/jetbrains/jet/codegen/StackValue.java
+40
-0
idea/testData/codegen/fieldPropertyAccess.jet
idea/testData/codegen/fieldPropertyAccess.jet
+5
-0
idea/testData/codegen/fieldSetter.jet
idea/testData/codegen/fieldSetter.jet
+7
-0
idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java
idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java
+6
-1
idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java
idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java
+1
-2
idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java
idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java
+37
-0
未找到文件。
idea/src/org/jetbrains/jet/codegen/ClassCodegen.java
浏览文件 @
d17f095e
...
...
@@ -11,6 +11,7 @@ import org.jetbrains.jet.lang.types.JetType;
import
org.objectweb.asm.ClassVisitor
;
import
org.objectweb.asm.Opcodes
;
import
java.util.ArrayList
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Set
;
...
...
@@ -151,8 +152,9 @@ public class ClassCodegen {
}
private
void
generateClassBody
(
JetClass
aClass
,
ClassVisitor
v
,
OwnerKind
kind
)
{
final
PropertyCodegen
propertyCodegen
=
new
PropertyCodegen
(
v
);
final
FunctionCodegen
functionCodegen
=
new
FunctionCodegen
(
v
,
JetStandardLibrary
.
getJetStandardLibrary
(
project
),
bindingContext
);
final
JetStandardLibrary
standardLibrary
=
JetStandardLibrary
.
getJetStandardLibrary
(
project
);
final
FunctionCodegen
functionCodegen
=
new
FunctionCodegen
(
v
,
standardLibrary
,
bindingContext
);
final
PropertyCodegen
propertyCodegen
=
new
PropertyCodegen
(
v
,
standardLibrary
,
bindingContext
,
functionCodegen
);
for
(
JetDeclaration
declaration
:
aClass
.
getDeclarations
())
{
if
(
declaration
instanceof
JetProperty
)
{
...
...
idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java
浏览文件 @
d17f095e
...
...
@@ -370,6 +370,28 @@ public class ExpressionCodegen extends JetVisitor {
final
JetType
outType
=
((
VariableDescriptor
)
descriptor
).
getOutType
();
myStack
.
push
(
StackValue
.
local
(
index
,
typeMapper
.
mapType
(
outType
)));
}
else
if
(
descriptor
instanceof
PropertyDescriptor
)
{
final
PropertyDescriptor
propertyDescriptor
=
(
PropertyDescriptor
)
descriptor
;
if
(
descriptor
.
getContainingDeclaration
()
instanceof
NamespaceDescriptor
)
{
JetNamespace
ns
=
(
JetNamespace
)
bindingContext
.
getDeclarationPsiElement
(
descriptor
.
getContainingDeclaration
());
String
owner
=
JetTypeMapper
.
jvmName
(
ns
);
final
JetType
outType
=
((
VariableDescriptor
)
descriptor
).
getOutType
();
Method
getter
;
Method
setter
;
if
(
expression
.
getReferencedNameElementType
()
==
JetTokens
.
FIELD_IDENTIFIER
)
{
getter
=
null
;
setter
=
null
;
}
else
{
getter
=
typeMapper
.
mapGetterSignature
(
propertyDescriptor
);
setter
=
typeMapper
.
mapSetterSignature
(
propertyDescriptor
);
}
myStack
.
push
(
StackValue
.
property
(
descriptor
.
getName
(),
owner
,
typeMapper
.
mapType
(
outType
),
getter
,
setter
));
}
else
{
throw
new
UnsupportedOperationException
(
"don't know how to generate non-namespace property reference "
+
descriptor
);
}
}
else
{
throw
new
UnsupportedOperationException
(
"don't know how to generate reference "
+
descriptor
);
}
...
...
@@ -887,10 +909,7 @@ public class ExpressionCodegen extends JetVisitor {
int
increment
=
op
.
getName
().
equals
(
"inc"
)
?
1
:
-
1
;
if
(
operand
instanceof
JetReferenceExpression
)
{
final
int
index
=
indexOfLocal
((
JetReferenceExpression
)
operand
);
if
(
index
<
0
)
{
throw
new
UnsupportedOperationException
(
"don't know how to increment or decrement something which is not a local var"
);
}
if
(
isIntPrimitive
(
asmType
))
{
if
(
index
>=
0
&&
isIntPrimitive
(
asmType
))
{
v
.
iinc
(
index
,
increment
);
return
StackValue
.
local
(
index
,
asmType
);
}
...
...
idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java
浏览文件 @
d17f095e
...
...
@@ -44,6 +44,12 @@ public class FunctionCodegen {
}
public
void
gen
(
JetFunction
f
,
OwnerKind
kind
)
{
Method
method
=
typeMapper
.
mapSignature
(
f
);
List
<
ValueParameterDescriptor
>
paramDescrs
=
bindingContext
.
getFunctionDescriptor
(
f
).
getUnsubstitutedValueParameters
();
generateMethod
(
f
,
kind
,
method
,
paramDescrs
);
}
public
void
generateMethod
(
JetDeclarationWithBody
f
,
OwnerKind
kind
,
Method
jvmSignature
,
List
<
ValueParameterDescriptor
>
paramDescrs
)
{
int
flags
=
Opcodes
.
ACC_PUBLIC
;
// TODO.
boolean
isStatic
=
kind
==
OwnerKind
.
NAMESPACE
;
...
...
@@ -53,8 +59,7 @@ public class FunctionCodegen {
boolean
isAbstract
=
kind
==
OwnerKind
.
INTERFACE
||
bodyExpression
==
null
;
if
(
isAbstract
)
flags
|=
Opcodes
.
ACC_ABSTRACT
;
Method
method
=
typeMapper
.
mapSignature
(
f
);
final
MethodVisitor
mv
=
v
.
visitMethod
(
flags
,
method
.
getName
(),
method
.
getDescriptor
(),
null
,
null
);
final
MethodVisitor
mv
=
v
.
visitMethod
(
flags
,
jvmSignature
.
getName
(),
jvmSignature
.
getDescriptor
(),
null
,
null
);
if
(
kind
!=
OwnerKind
.
INTERFACE
)
{
mv
.
visitCode
();
FrameMap
frameMap
=
new
FrameMap
();
...
...
@@ -63,15 +68,13 @@ public class FunctionCodegen {
frameMap
.
enterTemp
();
// 0 slot for this
}
List
<
ValueParameterDescriptor
>
parameDescrs
=
bindingContext
.
getFunctionDescriptor
(
f
).
getUnsubstitutedValueParameters
();
Type
[]
argTypes
=
method
.
getArgumentTypes
();
for
(
int
i
=
0
;
i
<
parameDescrs
.
size
();
i
++)
{
ValueParameterDescriptor
parameter
=
parameDescrs
.
get
(
i
);
Type
[]
argTypes
=
jvmSignature
.
getArgumentTypes
();
for
(
int
i
=
0
;
i
<
paramDescrs
.
size
();
i
++)
{
ValueParameterDescriptor
parameter
=
paramDescrs
.
get
(
i
);
frameMap
.
enter
(
parameter
,
argTypes
[
i
].
getSize
());
}
ExpressionCodegen
codegen
=
new
ExpressionCodegen
(
mv
,
bindingContext
,
frameMap
,
typeMapper
,
method
.
getReturnType
());
ExpressionCodegen
codegen
=
new
ExpressionCodegen
(
mv
,
bindingContext
,
frameMap
,
typeMapper
,
jvmSignature
.
getReturnType
());
bodyExpression
.
accept
(
codegen
);
generateReturn
(
mv
,
bodyExpression
,
codegen
);
mv
.
visitMaxs
(
0
,
0
);
...
...
idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java
浏览文件 @
d17f095e
...
...
@@ -2,7 +2,9 @@ package org.jetbrains.jet.codegen;
import
com.intellij.psi.PsiClass
;
import
com.intellij.psi.PsiElement
;
import
org.jetbrains.annotations.Nullable
;
import
org.jetbrains.jet.lang.psi.JetFunction
;
import
org.jetbrains.jet.lang.psi.JetNamespace
;
import
org.jetbrains.jet.lang.psi.JetParameter
;
import
org.jetbrains.jet.lang.psi.JetTypeReference
;
import
org.jetbrains.jet.lang.resolve.BindingContext
;
...
...
@@ -32,6 +34,10 @@ public class JetTypeMapper {
return
Type
.
getType
(
"L"
+
jvmName
(
psiClass
)
+
";"
);
}
static
String
jvmName
(
JetNamespace
namespace
)
{
return
NamespaceCodegen
.
getJVMClassName
(
namespace
.
getFQName
());
}
public
Type
mapType
(
final
JetType
jetType
)
{
if
(
jetType
.
equals
(
JetStandardClasses
.
getUnitType
()))
{
return
Type
.
VOID_TYPE
;
...
...
@@ -130,4 +136,22 @@ public class JetTypeMapper {
}
return
new
Method
(
f
.
getName
(),
returnType
,
parameterTypes
);
}
@Nullable
public
Method
mapGetterSignature
(
PropertyDescriptor
descriptor
)
{
if
(
descriptor
.
getGetter
()
==
null
)
{
return
null
;
}
Type
returnType
=
mapType
(
descriptor
.
getOutType
());
return
new
Method
(
PropertyCodegen
.
getterName
(
descriptor
.
getName
()),
returnType
,
new
Type
[
0
]);
}
@Nullable
public
Method
mapSetterSignature
(
PropertyDescriptor
descriptor
)
{
if
(
descriptor
.
getSetter
()
==
null
)
{
return
null
;
}
Type
paramType
=
mapType
(
descriptor
.
getInType
());
return
new
Method
(
PropertyCodegen
.
setterName
(
descriptor
.
getName
()),
Type
.
VOID_TYPE
,
new
Type
[]
{
paramType
});
}
}
idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java
浏览文件 @
d17f095e
...
...
@@ -35,8 +35,9 @@ public class NamespaceCodegen {
public
void
generate
(
JetNamespace
namespace
)
{
BindingContext
bindingContext
=
AnalyzingUtils
.
analyzeNamespace
(
namespace
,
ErrorHandler
.
THROW_EXCEPTION
);
final
PropertyCodegen
propertyCodegen
=
new
PropertyCodegen
(
v
);
final
FunctionCodegen
functionCodegen
=
new
FunctionCodegen
(
v
,
JetStandardLibrary
.
getJetStandardLibrary
(
project
),
bindingContext
);
final
JetStandardLibrary
standardLibrary
=
JetStandardLibrary
.
getJetStandardLibrary
(
project
);
final
FunctionCodegen
functionCodegen
=
new
FunctionCodegen
(
v
,
standardLibrary
,
bindingContext
);
final
PropertyCodegen
propertyCodegen
=
new
PropertyCodegen
(
v
,
standardLibrary
,
bindingContext
,
functionCodegen
);
final
ClassCodegen
classCodegen
=
codegens
.
forClass
(
bindingContext
);
for
(
JetDeclaration
declaration
:
namespace
.
getDeclarations
())
{
...
...
idea/src/org/jetbrains/jet/codegen/PropertyCodegen.java
浏览文件 @
d17f095e
package
org.jetbrains.jet.codegen
;
import
com.intellij.openapi.util.text.StringUtil
;
import
org.jetbrains.jet.lang.psi.JetConstantExpression
;
import
org.jetbrains.jet.lang.psi.JetExpression
;
import
org.jetbrains.jet.lang.psi.JetProperty
;
import
org.jetbrains.jet.lang.psi.JetPropertyAccessor
;
import
org.jetbrains.jet.lang.resolve.BindingContext
;
import
org.jetbrains.jet.lang.types.*
;
import
org.objectweb.asm.ClassVisitor
;
import
org.objectweb.asm.Opcodes
;
import
java.util.Collections
;
/**
* @author max
*/
public
class
PropertyCodegen
{
private
final
BindingContext
context
;
private
final
FunctionCodegen
functionCodegen
;
private
final
ClassVisitor
v
;
private
final
JetTypeMapper
mapper
;
public
PropertyCodegen
(
ClassVisitor
v
)
{
public
PropertyCodegen
(
ClassVisitor
v
,
JetStandardLibrary
standardLibrary
,
BindingContext
context
,
FunctionCodegen
functionCodegen
)
{
this
.
v
=
v
;
this
.
context
=
context
;
this
.
functionCodegen
=
functionCodegen
;
this
.
mapper
=
new
JetTypeMapper
(
standardLibrary
,
context
);
}
public
void
genInNamespace
(
JetProperty
p
)
{
...
...
@@ -30,6 +45,45 @@ public class PropertyCodegen {
}
public
void
gen
(
JetProperty
p
,
OwnerKind
kind
)
{
if
(
kind
==
OwnerKind
.
NAMESPACE
)
{
final
VariableDescriptor
descriptor
=
context
.
getVariableDescriptor
(
p
);
if
(!(
descriptor
instanceof
PropertyDescriptor
))
{
throw
new
UnsupportedOperationException
(
"expect a property to have a property descriptor"
);
}
final
PropertyDescriptor
propertyDescriptor
=
(
PropertyDescriptor
)
descriptor
;
if
(
context
.
hasBackingField
(
propertyDescriptor
))
{
Object
value
=
null
;
final
JetExpression
initializer
=
p
.
getInitializer
();
if
(
initializer
!=
null
)
{
if
(
initializer
instanceof
JetConstantExpression
)
{
value
=
((
JetConstantExpression
)
initializer
).
getValue
();
}
}
v
.
visitField
(
Opcodes
.
ACC_STATIC
|
Opcodes
.
ACC_PRIVATE
,
p
.
getName
(),
mapper
.
mapType
(
descriptor
.
getOutType
()).
getDescriptor
(),
null
,
value
);
}
final
JetPropertyAccessor
getter
=
p
.
getGetter
();
if
(
getter
!=
null
)
{
functionCodegen
.
generateMethod
(
getter
,
kind
,
mapper
.
mapGetterSignature
(
propertyDescriptor
),
Collections
.<
ValueParameterDescriptor
>
emptyList
());
}
final
JetPropertyAccessor
setter
=
p
.
getSetter
();
if
(
setter
!=
null
)
{
final
PropertySetterDescriptor
setterDescriptor
=
propertyDescriptor
.
getSetter
();
assert
setterDescriptor
!=
null
;
functionCodegen
.
generateMethod
(
setter
,
kind
,
mapper
.
mapSetterSignature
(
propertyDescriptor
),
setterDescriptor
.
getUnsubstitutedValueParameters
());
}
}
}
public
static
String
getterName
(
String
propertyName
)
{
return
"get"
+
StringUtil
.
capitalizeWithJavaBeanConvention
(
propertyName
);
}
public
static
String
setterName
(
String
propertyName
)
{
return
"set"
+
StringUtil
.
capitalizeWithJavaBeanConvention
(
propertyName
);
}
}
idea/src/org/jetbrains/jet/codegen/StackValue.java
浏览文件 @
d17f095e
...
...
@@ -6,6 +6,7 @@ import org.objectweb.asm.Label;
import
org.objectweb.asm.Opcodes
;
import
org.objectweb.asm.Type
;
import
org.objectweb.asm.commons.InstructionAdapter
;
import
org.objectweb.asm.commons.Method
;
/**
* @author yole
...
...
@@ -70,6 +71,10 @@ public abstract class StackValue {
return
new
Field
(
type
,
owner
,
name
,
isStatic
);
}
public
static
StackValue
property
(
String
name
,
String
owner
,
Type
type
,
Method
getter
,
Method
setter
)
{
return
new
Property
(
name
,
owner
,
getter
,
setter
,
type
);
}
private
static
void
box
(
final
Type
type
,
InstructionAdapter
v
)
{
if
(
type
==
Type
.
INT_TYPE
)
{
v
.
invokestatic
(
"java/lang/Integer"
,
"valueOf"
,
"(I)Ljava/lang/Integer;"
);
...
...
@@ -368,4 +373,39 @@ public abstract class StackValue {
v
.
visitFieldInsn
(
isStatic
?
Opcodes
.
PUTSTATIC
:
Opcodes
.
PUTFIELD
,
owner
,
name
,
this
.
type
.
getDescriptor
());
}
}
private
static
class
Property
extends
StackValue
{
private
final
String
name
;
private
final
String
owner
;
private
final
Method
getter
;
private
final
Method
setter
;
public
Property
(
String
name
,
String
owner
,
Method
getter
,
Method
setter
,
Type
type
)
{
super
(
type
);
this
.
name
=
name
;
this
.
owner
=
owner
;
this
.
getter
=
getter
;
this
.
setter
=
setter
;
}
@Override
public
void
put
(
Type
type
,
InstructionAdapter
v
)
{
if
(
getter
==
null
)
{
v
.
visitFieldInsn
(
Opcodes
.
GETSTATIC
,
owner
,
name
,
type
.
getDescriptor
());
}
else
{
v
.
invokestatic
(
owner
,
getter
.
getName
(),
getter
.
getDescriptor
());
}
}
@Override
public
void
store
(
InstructionAdapter
v
)
{
if
(
setter
==
null
)
{
v
.
visitFieldInsn
(
Opcodes
.
PUTSTATIC
,
owner
,
name
,
type
.
getDescriptor
());
}
else
{
v
.
invokestatic
(
owner
,
setter
.
getName
(),
setter
.
getDescriptor
());
}
}
}
}
idea/testData/codegen/fieldPropertyAccess.jet
0 → 100644
浏览文件 @
d17f095e
private var x = 0;
fun increment(): Int {
return ++x;
}
idea/testData/codegen/fieldSetter.jet
0 → 100644
浏览文件 @
d17f095e
var collector: String = ""
set(it) { $collector = $collector + it }
fun append(s: String): String {
collector = s;
return collector;
}
idea/tests/org/jetbrains/jet/codegen/CodegenTestCase.java
浏览文件 @
d17f095e
...
...
@@ -42,7 +42,7 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase {
return
answer
.
toString
();
}
pr
ivate
Class
generateNamespaceClass
()
{
pr
otected
Class
generateNamespaceClass
()
{
JetFile
jetFile
=
(
JetFile
)
myFixture
.
getFile
();
final
JetNamespace
namespace
=
jetFile
.
getRootNamespace
();
String
fqName
=
NamespaceCodegen
.
getJVMClassName
(
namespace
.
getFQName
()).
replace
(
"/"
,
"."
);
...
...
@@ -89,6 +89,11 @@ public abstract class CodegenTestCase extends LightCodeInsightFixtureTestCase {
throw
new
IllegalArgumentException
(
"couldn't find method "
+
name
);
}
protected
void
assertIsCurrentTime
(
long
returnValue
)
{
long
currentTime
=
System
.
currentTimeMillis
();
assertTrue
(
Math
.
abs
(
returnValue
-
currentTime
)
<=
1L
);
}
private
static
class
MyClassLoader
extends
ClassLoader
{
public
MyClassLoader
(
ClassLoader
parent
)
{
super
(
parent
);
...
...
idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java
浏览文件 @
d17f095e
...
...
@@ -56,8 +56,7 @@ public class NamespaceGenTest extends CodegenTestCase {
System
.
out
.
println
(
generateToText
());
final
Method
main
=
generateFunction
();
final
long
returnValue
=
(
Long
)
main
.
invoke
(
null
);
long
currentTime
=
System
.
currentTimeMillis
();
assertTrue
(
Math
.
abs
(
returnValue
-
currentTime
)
<=
1L
);
assertIsCurrentTime
(
returnValue
);
}
public
void
testIdentityHashCode
()
throws
Exception
{
...
...
idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java
浏览文件 @
d17f095e
package
org.jetbrains.jet.codegen
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Modifier
;
/**
* @author yole
*/
...
...
@@ -8,6 +12,39 @@ public class PropertyGenTest extends CodegenTestCase {
loadFile
(
"privateVal.jet"
);
System
.
out
.
println
(
generateToText
());
// TODO
}
public
void
testPropertyInNamespace
()
throws
Exception
{
loadText
(
"private val x = 239"
);
final
Class
nsClass
=
generateNamespaceClass
();
final
Field
[]
fields
=
nsClass
.
getDeclaredFields
();
assertEquals
(
1
,
fields
.
length
);
final
Field
field
=
fields
[
0
];
field
.
setAccessible
(
true
);
assertEquals
(
"x"
,
field
.
getName
());
assertEquals
(
Modifier
.
PRIVATE
|
Modifier
.
STATIC
,
field
.
getModifiers
());
assertEquals
(
239
,
field
.
get
(
null
));
}
public
void
testFieldPropertyAccess
()
throws
Exception
{
loadFile
(
"fieldPropertyAccess.jet"
);
final
Method
method
=
generateFunction
();
assertEquals
(
1
,
method
.
invoke
(
null
));
assertEquals
(
2
,
method
.
invoke
(
null
));
}
public
void
testFieldGetter
()
throws
Exception
{
loadText
(
"val now: Long get() = System.currentTimeMillis(); fun foo() = now"
);
final
Method
method
=
generateFunction
(
"foo"
);
assertIsCurrentTime
((
Long
)
method
.
invoke
(
null
));
}
public
void
testFieldSetter
()
throws
Exception
{
loadFile
(
"fieldSetter.jet"
);
System
.
out
.
println
(
generateToText
());
final
Method
method
=
generateFunction
(
"append"
);
method
.
invoke
(
null
,
"IntelliJ "
);
String
value
=
(
String
)
method
.
invoke
(
null
,
"IDEA"
);
assertEquals
(
value
,
"IntelliJ IDEA"
);
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录