Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
3b5dc386
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
3b5dc386
编写于
8月 31, 2012
作者:
J
jcoomes
浏览文件
操作
浏览文件
下载
差异文件
Merge
上级
9f55f327
d911db2d
变更
20
展开全部
隐藏空白更改
内联
并排
Showing
20 changed file
with
1673 addition
and
390 deletion
+1673
-390
src/share/classes/java/lang/invoke/BoundMethodHandle.java
src/share/classes/java/lang/invoke/BoundMethodHandle.java
+5
-0
src/share/classes/java/lang/invoke/DirectMethodHandle.java
src/share/classes/java/lang/invoke/DirectMethodHandle.java
+2
-2
src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
...re/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+12
-5
src/share/classes/java/lang/invoke/Invokers.java
src/share/classes/java/lang/invoke/Invokers.java
+108
-36
src/share/classes/java/lang/invoke/LambdaForm.java
src/share/classes/java/lang/invoke/LambdaForm.java
+12
-9
src/share/classes/java/lang/invoke/MemberName.java
src/share/classes/java/lang/invoke/MemberName.java
+3
-7
src/share/classes/java/lang/invoke/MethodHandle.java
src/share/classes/java/lang/invoke/MethodHandle.java
+8
-2
src/share/classes/java/lang/invoke/MethodHandleImpl.java
src/share/classes/java/lang/invoke/MethodHandleImpl.java
+68
-43
src/share/classes/java/lang/invoke/MethodHandleNatives.java
src/share/classes/java/lang/invoke/MethodHandleNatives.java
+22
-8
src/share/classes/java/lang/invoke/MethodHandles.java
src/share/classes/java/lang/invoke/MethodHandles.java
+11
-0
src/share/classes/java/lang/invoke/MethodType.java
src/share/classes/java/lang/invoke/MethodType.java
+33
-1
src/share/classes/java/lang/invoke/SimpleMethodHandle.java
src/share/classes/java/lang/invoke/SimpleMethodHandle.java
+5
-1
src/share/classes/java/lang/invoke/WrongMethodTypeException.java
...re/classes/java/lang/invoke/WrongMethodTypeException.java
+23
-0
src/share/classes/sun/invoke/util/ValueConversions.java
src/share/classes/sun/invoke/util/ValueConversions.java
+171
-96
test/java/lang/invoke/BigArityTest.java
test/java/lang/invoke/BigArityTest.java
+1044
-0
test/java/lang/invoke/MaxTest.java
test/java/lang/invoke/MaxTest.java
+0
-143
test/java/lang/invoke/MethodHandlesTest.java
test/java/lang/invoke/MethodHandlesTest.java
+74
-21
test/java/lang/invoke/PermuteArgsTest.java
test/java/lang/invoke/PermuteArgsTest.java
+7
-3
test/java/lang/invoke/RicochetTest.java
test/java/lang/invoke/RicochetTest.java
+63
-5
test/sun/invoke/util/ValueConversionsTest.java
test/sun/invoke/util/ValueConversionsTest.java
+2
-8
未找到文件。
src/share/classes/java/lang/invoke/BoundMethodHandle.java
浏览文件 @
3b5dc386
...
...
@@ -142,6 +142,11 @@ import com.sun.xml.internal.ws.org.objectweb.asm.Type;
*/
protected
abstract
SpeciesData
speciesData
();
@Override
final
Object
internalProperties
()
{
return
"/BMH="
+
internalValues
();
}
@Override
final
Object
internalValues
()
{
Object
[]
boundValues
=
new
Object
[
speciesData
().
fieldCount
()];
...
...
src/share/classes/java/lang/invoke/DirectMethodHandle.java
浏览文件 @
3b5dc386
...
...
@@ -108,8 +108,8 @@ class DirectMethodHandle extends MethodHandle {
}
@Override
String
debugString
()
{
return
"
DMH["
+
member
.
toString
()+
"]="
+
super
.
debug
String
();
String
internalProperties
()
{
return
"
/DMH="
+
member
.
to
String
();
}
//// Implementation methods.
...
...
src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
浏览文件 @
3b5dc386
...
...
@@ -185,12 +185,17 @@ class InvokerBytecodeGenerator {
}
class
CpPatch
{
int
index
;
Object
value
;
CpPatch
(
int
index
,
Object
value
)
{
final
int
index
;
final
String
placeholder
;
final
Object
value
;
CpPatch
(
int
index
,
String
placeholder
,
Object
value
)
{
this
.
index
=
index
;
this
.
placeholder
=
placeholder
;
this
.
value
=
value
;
}
public
String
toString
()
{
return
"CpPatch/index="
+
index
+
",placeholder="
+
placeholder
+
",value="
+
value
;
}
}
Map
<
Object
,
CpPatch
>
cpPatches
=
new
HashMap
<>();
...
...
@@ -205,7 +210,7 @@ class InvokerBytecodeGenerator {
}
// insert placeholder in CP and remember the patch
int
index
=
cw
.
newConst
((
Object
)
cpPlaceholder
);
// TODO check if aready in the constant pool
cpPatches
.
put
(
cpPlaceholder
,
new
CpPatch
(
index
,
arg
));
cpPatches
.
put
(
cpPlaceholder
,
new
CpPatch
(
index
,
cpPlaceholder
,
arg
));
return
cpPlaceholder
;
}
...
...
@@ -213,7 +218,9 @@ class InvokerBytecodeGenerator {
int
size
=
getConstantPoolSize
(
classFile
);
Object
[]
res
=
new
Object
[
size
];
for
(
CpPatch
p
:
cpPatches
.
values
())
{
res
[
p
.
index
]
=
p
.
value
;
if
(
p
.
index
>=
size
)
throw
new
InternalError
(
"in cpool["
+
size
+
"]: "
+
p
+
"\n"
+
Arrays
.
toString
(
Arrays
.
copyOf
(
classFile
,
20
)));
res
[
p
.
index
]
=
p
.
value
;
}
return
res
;
}
...
...
src/share/classes/java/lang/invoke/Invokers.java
浏览文件 @
3b5dc386
...
...
@@ -74,8 +74,18 @@ class Invokers {
MethodHandle
invoker
=
exactInvoker
;
if
(
invoker
!=
null
)
return
invoker
;
MethodType
mtype
=
targetType
;
LambdaForm
lform
=
invokeForm
(
mtype
,
MethodTypeForm
.
LF_EX_INVOKER
);
invoker
=
BoundMethodHandle
.
bindSingle
(
mtype
.
invokerType
(),
lform
,
mtype
);
MethodType
invokerType
=
mtype
.
invokerType
();
LambdaForm
lform
;
final
int
MTYPE_ARG_APPENDED
=
1
;
// argument count for appended mtype value
if
(
mtype
.
parameterSlotCount
()
<=
MethodType
.
MAX_MH_INVOKER_ARITY
-
MTYPE_ARG_APPENDED
)
{
lform
=
invokeForm
(
mtype
,
false
,
MethodTypeForm
.
LF_EX_INVOKER
);
invoker
=
BoundMethodHandle
.
bindSingle
(
invokerType
,
lform
,
mtype
);
}
else
{
// At maximum arity, we cannot afford an extra mtype argument,
// so build a fully customized (non-cached) invoker form.
lform
=
invokeForm
(
mtype
,
true
,
MethodTypeForm
.
LF_EX_INVOKER
);
invoker
=
SimpleMethodHandle
.
make
(
invokerType
,
lform
);
}
assert
(
checkInvoker
(
invoker
));
exactInvoker
=
invoker
;
return
invoker
;
...
...
@@ -85,9 +95,20 @@ class Invokers {
MethodHandle
invoker
=
generalInvoker
;
if
(
invoker
!=
null
)
return
invoker
;
MethodType
mtype
=
targetType
;
prepareForGenericCall
(
mtype
);
LambdaForm
lform
=
invokeForm
(
mtype
,
MethodTypeForm
.
LF_GEN_INVOKER
);
invoker
=
BoundMethodHandle
.
bindSingle
(
mtype
.
invokerType
(),
lform
,
mtype
);
MethodType
invokerType
=
mtype
.
invokerType
();
LambdaForm
lform
;
final
int
MTYPE_ARG_APPENDED
=
1
;
// argument count for appended mtype value
assert
(
GENERIC_INVOKER_SLOP
>=
MTYPE_ARG_APPENDED
);
if
(
mtype
.
parameterSlotCount
()
<=
MethodType
.
MAX_MH_INVOKER_ARITY
-
GENERIC_INVOKER_SLOP
)
{
prepareForGenericCall
(
mtype
);
lform
=
invokeForm
(
mtype
,
false
,
MethodTypeForm
.
LF_GEN_INVOKER
);
invoker
=
BoundMethodHandle
.
bindSingle
(
invokerType
,
lform
,
mtype
);
}
else
{
// At maximum arity, we cannot afford an extra mtype argument,
// so build a fully customized (non-cached) invoker form.
lform
=
invokeForm
(
mtype
,
true
,
MethodTypeForm
.
LF_GEN_INVOKER
);
invoker
=
SimpleMethodHandle
.
make
(
invokerType
,
lform
);
}
assert
(
checkInvoker
(
invoker
));
generalInvoker
=
invoker
;
return
invoker
;
...
...
@@ -102,6 +123,7 @@ class Invokers {
}
static
MemberName
invokeBasicMethod
(
MethodType
type
)
{
type
=
type
.
basicType
();
String
name
=
"invokeBasic"
;
try
{
//Lookup.findVirtual(MethodHandle.class, name, type);
...
...
@@ -135,9 +157,31 @@ class Invokers {
/*non-public*/
MethodHandle
spreadInvoker
(
int
leadingArgCount
)
{
MethodHandle
vaInvoker
=
spreadInvokers
[
leadingArgCount
];
if
(
vaInvoker
!=
null
)
return
vaInvoker
;
MethodHandle
gInvoker
=
generalInvoker
();
int
spreadArgCount
=
targetType
.
parameterCount
()
-
leadingArgCount
;
vaInvoker
=
gInvoker
.
asSpreader
(
Object
[].
class
,
spreadArgCount
);
MethodType
spreadInvokerType
=
targetType
.
replaceParameterTypes
(
leadingArgCount
,
targetType
.
parameterCount
(),
Object
[].
class
);
if
(
targetType
.
parameterSlotCount
()
<=
MethodType
.
MAX_MH_INVOKER_ARITY
)
{
// Factor sinvoker.invoke(mh, a) into ginvoker.asSpreader().invoke(mh, a)
// where ginvoker.invoke(mh, a*) => mh.invoke(a*).
MethodHandle
genInvoker
=
generalInvoker
();
vaInvoker
=
genInvoker
.
asSpreader
(
Object
[].
class
,
spreadArgCount
);
}
else
{
// Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]).
// Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a)
// where filter(mh) == mh.asSpreader(Object[], spreadArgCount)
MethodHandle
arrayInvoker
=
MethodHandles
.
exactInvoker
(
spreadInvokerType
);
MethodHandle
makeSpreader
;
try
{
makeSpreader
=
IMPL_LOOKUP
.
findVirtual
(
MethodHandle
.
class
,
"asSpreader"
,
MethodType
.
methodType
(
MethodHandle
.
class
,
Class
.
class
,
int
.
class
));
}
catch
(
ReflectiveOperationException
ex
)
{
throw
new
InternalError
(
ex
);
}
makeSpreader
=
MethodHandles
.
insertArguments
(
makeSpreader
,
1
,
Object
[].
class
,
spreadArgCount
);
vaInvoker
=
MethodHandles
.
filterArgument
(
arrayInvoker
,
0
,
makeSpreader
);
}
assert
(
vaInvoker
.
type
().
equals
(
spreadInvokerType
.
invokerType
()));
spreadInvokers
[
leadingArgCount
]
=
vaInvoker
;
return
vaInvoker
;
}
...
...
@@ -171,7 +215,7 @@ class Invokers {
.
findStatic
(
CallSite
.
class
,
"uninitializedCallSite"
,
MethodType
.
methodType
(
Empty
.
class
));
}
catch
(
ReflectiveOperationException
ex
)
{
throw
new
RuntimeException
(
ex
);
throw
new
InternalError
(
ex
);
}
}
invoker
=
MethodHandles
.
explicitCastArguments
(
invoker
,
MethodType
.
methodType
(
targetType
.
returnType
()));
...
...
@@ -185,30 +229,39 @@ class Invokers {
return
"Invokers"
+
targetType
;
}
private
static
MethodType
fixMethodType
(
Class
<?>
callerClass
,
Object
type
)
{
if
(
type
instanceof
MethodType
)
return
(
MethodType
)
type
;
else
return
MethodType
.
fromMethodDescriptorString
((
String
)
type
,
callerClass
.
getClassLoader
());
}
static
MemberName
exactInvokerMethod
(
Class
<?>
callerClass
,
Object
type
,
Object
[]
appendixResult
)
{
MethodType
mtype
=
fixMethodType
(
callerClass
,
type
);
LambdaForm
lform
=
invokeForm
(
mtype
,
MethodTypeForm
.
LF_EX_LINKER
);
appendixResult
[
0
]
=
mtype
;
static
MemberName
exactInvokerMethod
(
MethodType
mtype
,
Object
[]
appendixResult
)
{
LambdaForm
lform
;
final
int
MTYPE_ARG_APPENDED
=
1
;
// argument count for appended mtype value
if
(
mtype
.
parameterSlotCount
()
<=
MethodType
.
MAX_MH_ARITY
-
MTYPE_ARG_APPENDED
)
{
lform
=
invokeForm
(
mtype
,
false
,
MethodTypeForm
.
LF_EX_LINKER
);
appendixResult
[
0
]
=
mtype
;
}
else
{
lform
=
invokeForm
(
mtype
,
true
,
MethodTypeForm
.
LF_EX_LINKER
);
}
return
lform
.
vmentry
;
}
static
MemberName
genericInvokerMethod
(
Class
<?>
callerClass
,
Object
type
,
Object
[]
appendixResult
)
{
MethodType
mtype
=
fixMethodType
(
callerClass
,
type
);
LambdaForm
lform
=
invokeForm
(
mtype
,
MethodTypeForm
.
LF_GEN_LINKER
);
prepareForGenericCall
(
mtype
);
appendixResult
[
0
]
=
mtype
;
static
MemberName
genericInvokerMethod
(
MethodType
mtype
,
Object
[]
appendixResult
)
{
LambdaForm
lform
;
final
int
MTYPE_ARG_APPENDED
=
1
;
// argument count for appended mtype value
if
(
mtype
.
parameterSlotCount
()
<=
MethodType
.
MAX_MH_ARITY
-
(
MTYPE_ARG_APPENDED
+
GENERIC_INVOKER_SLOP
))
{
lform
=
invokeForm
(
mtype
,
false
,
MethodTypeForm
.
LF_GEN_LINKER
);
appendixResult
[
0
]
=
mtype
;
prepareForGenericCall
(
mtype
);
}
else
{
lform
=
invokeForm
(
mtype
,
true
,
MethodTypeForm
.
LF_GEN_LINKER
);
}
return
lform
.
vmentry
;
}
private
static
LambdaForm
invokeForm
(
MethodType
mtype
,
int
which
)
{
mtype
=
mtype
.
basicType
();
// normalize Z to I, String to Object, etc.
private
static
LambdaForm
invokeForm
(
MethodType
mtype
,
boolean
customized
,
int
which
)
{
boolean
isCached
;
if
(!
customized
)
{
mtype
=
mtype
.
basicType
();
// normalize Z to I, String to Object, etc.
isCached
=
true
;
}
else
{
isCached
=
false
;
// maybe cache if mtype == mtype.basicType()
}
boolean
isLinker
,
isGeneric
;
String
debugName
;
switch
(
which
)
{
...
...
@@ -218,27 +271,32 @@ class Invokers {
case
MethodTypeForm
.
LF_GEN_INVOKER
:
isLinker
=
false
;
isGeneric
=
true
;
debugName
=
"invoker"
;
break
;
default
:
throw
new
InternalError
();
}
LambdaForm
lform
=
mtype
.
form
().
cachedLambdaForm
(
which
);
if
(
lform
!=
null
)
return
lform
;
LambdaForm
lform
;
if
(
isCached
)
{
lform
=
mtype
.
form
().
cachedLambdaForm
(
which
);
if
(
lform
!=
null
)
return
lform
;
}
// exactInvokerForm (Object,Object)Object
// link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
final
int
THIS_MH
=
0
;
final
int
CALL_MH
=
THIS_MH
+
(
isLinker
?
0
:
1
);
final
int
ARG_BASE
=
CALL_MH
+
1
;
final
int
OUTARG_LIMIT
=
ARG_BASE
+
mtype
.
parameterCount
();
final
int
INARG_LIMIT
=
OUTARG_LIMIT
+
(
isLinker
?
1
:
0
);
final
int
INARG_LIMIT
=
OUTARG_LIMIT
+
(
isLinker
&&
!
customized
?
1
:
0
);
int
nameCursor
=
OUTARG_LIMIT
;
final
int
MTYPE_ARG
=
nameCursor
++;
// might be last in-argument
final
int
MTYPE_ARG
=
customized
?
-
1
:
nameCursor
++;
// might be last in-argument
final
int
CHECK_TYPE
=
nameCursor
++;
final
int
LINKER_CALL
=
nameCursor
++;
MethodType
invokerFormType
=
mtype
.
invokerType
();
if
(
isLinker
)
{
invokerFormType
=
invokerFormType
.
appendParameterTypes
(
MemberName
.
class
);
if
(!
customized
)
invokerFormType
=
invokerFormType
.
appendParameterTypes
(
MemberName
.
class
);
}
else
{
invokerFormType
=
invokerFormType
.
invokerType
();
}
Name
[]
names
=
arguments
(
nameCursor
-
INARG_LIMIT
,
invokerFormType
);
assert
(
names
.
length
==
nameCursor
);
assert
(
names
.
length
==
nameCursor
)
:
Arrays
.
asList
(
mtype
,
customized
,
which
,
nameCursor
,
names
.
length
);
if
(
MTYPE_ARG
>=
INARG_LIMIT
)
{
assert
(
names
[
MTYPE_ARG
]
==
null
);
names
[
MTYPE_ARG
]
=
BoundMethodHandle
.
getSpeciesData
(
"L"
).
getterName
(
names
[
THIS_MH
],
0
);
...
...
@@ -248,31 +306,42 @@ class Invokers {
// Make the final call. If isGeneric, then prepend the result of type checking.
MethodType
outCallType
;
Object
[]
outArgs
;
Object
mtypeArg
=
(
customized
?
mtype
:
names
[
MTYPE_ARG
]);
if
(!
isGeneric
)
{
names
[
CHECK_TYPE
]
=
new
Name
(
NF_checkExactType
,
names
[
CALL_MH
],
names
[
MTYPE_ARG
]
);
names
[
CHECK_TYPE
]
=
new
Name
(
NF_checkExactType
,
names
[
CALL_MH
],
mtypeArg
);
// mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
outArgs
=
Arrays
.
copyOfRange
(
names
,
CALL_MH
,
OUTARG_LIMIT
,
Object
[].
class
);
outCallType
=
mtype
;
}
else
if
(
customized
)
{
names
[
CHECK_TYPE
]
=
new
Name
(
NF_asType
,
names
[
CALL_MH
],
mtypeArg
);
// mh.invokeGeneric(a*):R =>
// let mt=TYPEOF(a*:R), tmh=asType(mh, mt);
// tmh.invokeBasic(a*)
outArgs
=
Arrays
.
copyOfRange
(
names
,
CALL_MH
,
OUTARG_LIMIT
,
Object
[].
class
);
outCallType
=
mtype
;
}
else
{
names
[
CHECK_TYPE
]
=
new
Name
(
NF_checkGenericType
,
names
[
CALL_MH
],
names
[
MTYPE_ARG
]
);
names
[
CHECK_TYPE
]
=
new
Name
(
NF_checkGenericType
,
names
[
CALL_MH
],
mtypeArg
);
// mh.invokeGeneric(a*):R =>
// let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
// gamh.invokeBasic(mt, mh, a*)
final
int
PREPEND_GAMH
=
0
,
PREPEND_MT
=
1
,
PREPEND_COUNT
=
2
;
assert
(
GENERIC_INVOKER_SLOP
==
PREPEND_COUNT
);
outArgs
=
Arrays
.
copyOfRange
(
names
,
CALL_MH
,
OUTARG_LIMIT
+
PREPEND_COUNT
,
Object
[].
class
);
// prepend arguments:
System
.
arraycopy
(
outArgs
,
0
,
outArgs
,
PREPEND_COUNT
,
outArgs
.
length
-
PREPEND_COUNT
);
outArgs
[
PREPEND_GAMH
]
=
names
[
CHECK_TYPE
];
outArgs
[
PREPEND_MT
]
=
names
[
MTYPE_ARG
]
;
outArgs
[
PREPEND_MT
]
=
mtypeArg
;
outCallType
=
mtype
.
insertParameterTypes
(
0
,
MethodType
.
class
,
MethodHandle
.
class
);
}
names
[
LINKER_CALL
]
=
new
Name
(
invokeBasicMethod
(
outCallType
),
outArgs
);
lform
=
new
LambdaForm
(
debugName
,
INARG_LIMIT
,
names
);
if
(
isLinker
)
lform
.
compileToBytecode
();
// JVM needs a real methodOop
lform
=
mtype
.
form
().
setCachedLambdaForm
(
which
,
lform
);
if
(
isCached
)
lform
=
mtype
.
form
().
setCachedLambdaForm
(
which
,
lform
);
return
lform
;
}
private
static
final
int
GENERIC_INVOKER_SLOP
=
2
;
// used elsewhere to avoid arity problems
/*non-public*/
static
WrongMethodTypeException
newWrongMethodTypeException
(
MethodType
actual
,
MethodType
expected
)
{
...
...
@@ -370,6 +439,7 @@ class Invokers {
// Local constant functions:
private
static
final
NamedFunction
NF_checkExactType
;
private
static
final
NamedFunction
NF_checkGenericType
;
private
static
final
NamedFunction
NF_asType
;
private
static
final
NamedFunction
NF_getCallSiteTarget
;
static
{
try
{
...
...
@@ -377,6 +447,8 @@ class Invokers {
.
getDeclaredMethod
(
"checkExactType"
,
Object
.
class
,
Object
.
class
));
NF_checkGenericType
=
new
NamedFunction
(
Invokers
.
class
.
getDeclaredMethod
(
"checkGenericType"
,
Object
.
class
,
Object
.
class
));
NF_asType
=
new
NamedFunction
(
MethodHandle
.
class
.
getDeclaredMethod
(
"asType"
,
MethodType
.
class
));
NF_getCallSiteTarget
=
new
NamedFunction
(
Invokers
.
class
.
getDeclaredMethod
(
"getCallSiteTarget"
,
Object
.
class
));
NF_checkExactType
.
resolve
();
...
...
src/share/classes/java/lang/invoke/LambdaForm.java
浏览文件 @
3b5dc386
...
...
@@ -596,14 +596,7 @@ class LambdaForm {
Object
interpretWithArguments
(
Object
...
argumentValues
)
throws
Throwable
{
if
(
TRACE_INTERPRETER
)
return
interpretWithArgumentsTracing
(
argumentValues
);
if
(
COMPILE_THRESHOLD
!=
0
&&
invocationCounter
<
COMPILE_THRESHOLD
)
{
invocationCounter
++;
// benign race
if
(
invocationCounter
>=
COMPILE_THRESHOLD
)
{
// Replace vmentry with a bytecode version of this LF.
compileToBytecode
();
}
}
checkInvocationCounter
();
assert
(
arityCheck
(
argumentValues
));
Object
[]
values
=
Arrays
.
copyOf
(
argumentValues
,
names
.
length
);
for
(
int
i
=
argumentValues
.
length
;
i
<
values
.
length
;
i
++)
{
...
...
@@ -630,6 +623,16 @@ class LambdaForm {
return
name
.
function
.
invokeWithArguments
(
arguments
);
}
private
void
checkInvocationCounter
()
{
if
(
COMPILE_THRESHOLD
!=
0
&&
invocationCounter
<
COMPILE_THRESHOLD
)
{
invocationCounter
++;
// benign race
if
(
invocationCounter
>=
COMPILE_THRESHOLD
)
{
// Replace vmentry with a bytecode version of this LF.
compileToBytecode
();
}
}
}
Object
interpretWithArgumentsTracing
(
Object
...
argumentValues
)
throws
Throwable
{
traceInterpreter
(
"[ interpretWithArguments"
,
this
,
argumentValues
);
if
(
invocationCounter
<
COMPILE_THRESHOLD
)
{
...
...
@@ -703,7 +706,7 @@ class LambdaForm {
}
public
String
toString
()
{
StringBuilder
buf
=
new
StringBuilder
(
"
Lambda("
);
StringBuilder
buf
=
new
StringBuilder
(
debugName
+
"=
Lambda("
);
for
(
int
i
=
0
;
i
<
names
.
length
;
i
++)
{
if
(
i
==
arity
)
buf
.
append
(
")=>{"
);
Name
n
=
names
[
i
];
...
...
src/share/classes/java/lang/invoke/MemberName.java
浏览文件 @
3b5dc386
...
...
@@ -306,12 +306,6 @@ import java.util.Objects;
return
this
;
}
private
void
setFlags
(
int
flags
)
{
this
.
flags
=
flags
;
assert
(
testAnyFlags
(
ALL_KINDS
));
assert
(
referenceKindIsConsistent
());
}
private
boolean
testFlags
(
int
mask
,
int
value
)
{
return
(
flags
&
mask
)
==
value
;
}
...
...
@@ -452,8 +446,10 @@ import java.util.Objects;
this
.
clazz
=
defClass
;
this
.
name
=
name
;
this
.
type
=
type
;
setFlags
(
flags
);
this
.
flags
=
flags
;
assert
(
testAnyFlags
(
ALL_KINDS
));
assert
(
this
.
resolution
==
null
);
// nobody should have touched this yet
//assert(referenceKindIsConsistent()); // do this after resolution
}
private
void
expandFromVM
()
{
...
...
src/share/classes/java/lang/invoke/MethodHandle.java
浏览文件 @
3b5dc386
...
...
@@ -924,7 +924,7 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
if
(
arrayType
!=
type
().
parameterType
(
collectArgPos
))
target
=
convertArguments
(
type
().
changeParameterType
(
collectArgPos
,
arrayType
));
MethodHandle
collector
=
ValueConversions
.
varargsArray
(
arrayType
,
arrayLength
);
return
MethodHandle
Impl
.
makeCollectArguments
(
target
,
collector
,
collectArgPos
,
false
);
return
MethodHandle
s
.
collectArguments
(
target
,
collectArgPos
,
collector
);
}
// private API: return true if last param exactly matches arrayType
...
...
@@ -1226,7 +1226,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
return
"MethodHandle"
+
type
;
}
String
debugString
()
{
return
standardString
()+
"
="
+
internalForm
()+
internalValu
es
();
return
standardString
()+
"
/LF="
+
internalForm
()+
internalProperti
es
();
}
//// Implementation methods.
...
...
@@ -1269,6 +1269,12 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
/*non-public*/
Object
internalValues
()
{
return
null
;
}
/*non-public*/
Object
internalProperties
()
{
// Override to something like "/FOO=bar"
return
""
;
}
...
...
src/share/classes/java/lang/invoke/MethodHandleImpl.java
浏览文件 @
3b5dc386
...
...
@@ -59,7 +59,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
Name
[]
args
=
Arrays
.
copyOfRange
(
names
,
1
,
1
+
srcType
.
parameterCount
());
names
[
names
.
length
-
1
]
=
new
Name
(
accessor
.
asType
(
srcType
),
(
Object
[])
args
);
LambdaForm
form
=
new
LambdaForm
(
"getElement"
,
lambdaType
.
parameterCount
(),
names
);
MethodHandle
mh
=
new
SimpleMethodHandl
e
(
srcType
,
form
);
MethodHandle
mh
=
SimpleMethodHandle
.
mak
e
(
srcType
,
form
);
if
(
ArrayAccessor
.
needCast
(
arrayClass
))
{
mh
=
mh
.
bindTo
(
arrayClass
);
}
...
...
@@ -171,38 +171,46 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
// Calculate extra arguments (temporaries) required in the names array.
// FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step.
int
extra
=
0
;
for
(
int
i
=
0
;
i
<
srcType
.
parameterCount
();
i
++)
{
Class
<?>
src
=
srcType
.
parameterType
(
i
);
Class
<?>
dst
=
dstType
.
parameterType
(
i
);
if
(!
VerifyType
.
isNullConversion
(
src
,
dst
))
{
extra
++;
final
int
INARG_COUNT
=
srcType
.
parameterCount
();
int
conversions
=
0
;
boolean
[]
needConv
=
new
boolean
[
1
+
INARG_COUNT
];
for
(
int
i
=
0
;
i
<=
INARG_COUNT
;
i
++)
{
Class
<?>
src
=
(
i
==
INARG_COUNT
)
?
dstType
.
returnType
()
:
srcType
.
parameterType
(
i
);
Class
<?>
dst
=
(
i
==
INARG_COUNT
)
?
srcType
.
returnType
()
:
dstType
.
parameterType
(
i
);
if
(!
VerifyType
.
isNullConversion
(
src
,
dst
)
||
level
<=
1
&&
dst
.
isInterface
()
&&
!
dst
.
isAssignableFrom
(
src
))
{
needConv
[
i
]
=
true
;
conversions
++;
}
}
boolean
retConv
=
needConv
[
INARG_COUNT
];
Class
<?>
needReturn
=
srcType
.
returnType
();
Class
<?>
haveReturn
=
dstType
.
returnType
();
boolean
retConv
=
!
VerifyType
.
isNullConversion
(
haveReturn
,
needReturn
);
final
int
IN_MH
=
0
;
final
int
INARG_BASE
=
1
;
final
int
INARG_LIMIT
=
INARG_BASE
+
INARG_COUNT
;
final
int
NAME_LIMIT
=
INARG_LIMIT
+
conversions
+
1
;
final
int
RETURN_CONV
=
(!
retConv
?
-
1
:
NAME_LIMIT
-
1
);
final
int
OUT_CALL
=
(!
retConv
?
NAME_LIMIT
:
RETURN_CONV
)
-
1
;
// Now build a LambdaForm.
MethodType
lambdaType
=
srcType
.
invokerType
();
Name
[]
names
=
arguments
(
extra
+
1
,
lambdaType
);
int
[]
indexes
=
new
int
[
lambdaType
.
parameterCount
()];
MethodType
lambdaType
=
srcType
.
basicType
().
invokerType
();
Name
[]
names
=
arguments
(
NAME_LIMIT
-
INARG_LIMIT
,
lambdaType
);
// Collect the arguments to the outgoing call, maybe with conversions:
final
int
OUTARG_BASE
=
0
;
// target MH is Name.function, name Name.arguments[0]
Object
[]
outArgs
=
new
Object
[
OUTARG_BASE
+
INARG_COUNT
];
MethodType
midType
=
dstType
;
for
(
int
i
=
0
,
argIndex
=
1
,
tmpIndex
=
lambdaType
.
parameterCount
();
i
<
srcType
.
parameterCount
();
i
++,
argIndex
++)
{
int
nameCursor
=
INARG_LIMIT
;
for
(
int
i
=
0
;
i
<
INARG_COUNT
;
i
++)
{
Class
<?>
src
=
srcType
.
parameterType
(
i
);
Class
<?>
dst
=
mid
Type
.
parameterType
(
i
);
Class
<?>
dst
=
dst
Type
.
parameterType
(
i
);
if
(
VerifyType
.
isNullConversion
(
src
,
dst
)
)
{
if
(
!
needConv
[
i
]
)
{
// do nothing: difference is trivial
indexes
[
i
]
=
argIndex
;
outArgs
[
OUTARG_BASE
+
i
]
=
names
[
INARG_BASE
+
i
]
;
continue
;
}
// Work the current type backward toward the desired caller type:
midType
=
midType
.
changeParameterType
(
i
,
src
);
// Tricky case analysis follows.
MethodHandle
fn
=
null
;
if
(
src
.
isPrimitive
())
{
...
...
@@ -246,33 +254,41 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
fn
=
ValueConversions
.
cast
(
dst
);
}
}
names
[
tmpIndex
]
=
new
Name
(
fn
,
names
[
argIndex
]);
indexes
[
i
]
=
tmpIndex
;
tmpIndex
++;
Name
conv
=
new
Name
(
fn
,
names
[
INARG_BASE
+
i
]);
assert
(
names
[
nameCursor
]
==
null
);
names
[
nameCursor
++]
=
conv
;
assert
(
outArgs
[
OUTARG_BASE
+
i
]
==
null
);
outArgs
[
OUTARG_BASE
+
i
]
=
conv
;
}
if
(
retConv
)
{
MethodHandle
adjustReturn
;
// Build argument array for the call.
assert
(
nameCursor
==
OUT_CALL
);
names
[
OUT_CALL
]
=
new
Name
(
target
,
outArgs
);
if
(
RETURN_CONV
<
0
)
{
assert
(
OUT_CALL
==
names
.
length
-
1
);
}
else
{
Class
<?>
needReturn
=
srcType
.
returnType
();
Class
<?>
haveReturn
=
dstType
.
returnType
();
MethodHandle
fn
;
Object
[]
arg
=
{
names
[
OUT_CALL
]
};
if
(
haveReturn
==
void
.
class
)
{
// synthesize a zero value for the given void
Object
zero
=
Wrapper
.
forBasicType
(
needReturn
).
zero
();
adjustReturn
=
MethodHandles
.
constant
(
needReturn
,
zero
);
fn
=
MethodHandles
.
constant
(
needReturn
,
zero
);
arg
=
new
Object
[
0
];
// don't pass names[OUT_CALL] to conversion
}
else
{
MethodHandle
identity
=
MethodHandles
.
identity
(
needReturn
);
MethodType
needConversion
=
identity
.
type
().
changeParameterType
(
0
,
haveReturn
);
adjustRetur
n
=
makePairwiseConvert
(
identity
,
needConversion
,
level
);
f
n
=
makePairwiseConvert
(
identity
,
needConversion
,
level
);
}
target
=
makeCollectArguments
(
adjustReturn
,
target
,
0
,
false
);
assert
(
names
[
RETURN_CONV
]
==
null
);
names
[
RETURN_CONV
]
=
new
Name
(
fn
,
arg
);
assert
(
RETURN_CONV
==
names
.
length
-
1
);
}
// Build argument array for the call.
Name
[]
targetArgs
=
new
Name
[
dstType
.
parameterCount
()];
for
(
int
i
=
0
;
i
<
dstType
.
parameterCount
();
i
++)
{
int
idx
=
indexes
[
i
];
targetArgs
[
i
]
=
names
[
idx
];
}
names
[
names
.
length
-
1
]
=
new
Name
(
target
,
(
Object
[])
targetArgs
);
LambdaForm
form
=
new
LambdaForm
(
"convert"
,
lambdaType
.
parameterCount
(),
names
);
return
new
SimpleMethodHandl
e
(
srcType
,
form
);
return
SimpleMethodHandle
.
mak
e
(
srcType
,
form
);
}
static
MethodHandle
makeReferenceIdentity
(
Class
<?>
refType
)
{
...
...
@@ -280,7 +296,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
Name
[]
names
=
arguments
(
1
,
lambdaType
);
names
[
names
.
length
-
1
]
=
new
Name
(
ValueConversions
.
identity
(),
names
[
1
]);
LambdaForm
form
=
new
LambdaForm
(
"identity"
,
lambdaType
.
parameterCount
(),
names
);
return
new
SimpleMethodHandl
e
(
MethodType
.
methodType
(
refType
,
refType
),
form
);
return
SimpleMethodHandle
.
mak
e
(
MethodType
.
methodType
(
refType
,
refType
),
form
);
}
static
MethodHandle
makeVarargsCollector
(
MethodHandle
target
,
Class
<?>
arrayType
)
{
...
...
@@ -334,8 +350,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
MethodHandle
collector
;
try
{
collector
=
asFixedArity
().
asCollector
(
arrayType
,
arrayLength
);
assert
(
collector
.
type
().
parameterCount
()
==
newArity
)
:
"newArity="
+
newArity
+
" but collector="
+
collector
;
}
catch
(
IllegalArgumentException
ex
)
{
throw
new
WrongMethodTypeException
(
"cannot build collector"
);
throw
new
WrongMethodTypeException
(
"cannot build collector"
,
ex
);
}
cache
=
collector
;
return
collector
.
asType
(
newType
);
...
...
@@ -429,12 +446,18 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
names
[
names
.
length
-
1
]
=
new
Name
(
target
,
(
Object
[])
targetArgs
);
LambdaForm
form
=
new
LambdaForm
(
"spread"
,
lambdaType
.
parameterCount
(),
names
);
return
new
SimpleMethodHandl
e
(
srcType
,
form
);
return
SimpleMethodHandle
.
mak
e
(
srcType
,
form
);
}
static
void
checkSpreadArgument
(
Object
av
,
int
n
)
{
// FIXME: regression test for bug 7141637 erroneously expects an NPE, and other tests may expect IAE
// but the actual exception raised by an arity mismatch should be WMTE
final
boolean
RAISE_RANDOM_EXCEPTIONS
=
true
;
// FIXME: delete in JSR 292 M1
if
(
av
==
null
)
{
if
(
n
==
0
)
return
;
int
len
;
if
(
RAISE_RANDOM_EXCEPTIONS
)
len
=
((
Object
[])
av
).
length
;
// throw NPE; but delete this after tests are fixed
}
else
if
(
av
instanceof
Object
[])
{
int
len
=
((
Object
[])
av
).
length
;
if
(
len
==
n
)
return
;
...
...
@@ -443,7 +466,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
if
(
len
==
n
)
return
;
}
// fall through to error:
throw
newIllegalArgumentException
(
"Array is not of length "
+
n
);
if
(
RAISE_RANDOM_EXCEPTIONS
)
throw
newIllegalArgumentException
(
"Array is not of length "
+
n
);
throw
new
WrongMethodTypeException
(
"Array is not of length "
+
n
);
}
private
static
final
NamedFunction
NF_checkSpreadArgument
;
...
...
@@ -508,7 +533,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
names
[
targetNamePos
]
=
new
Name
(
target
,
(
Object
[])
targetArgs
);
LambdaForm
form
=
new
LambdaForm
(
"collect"
,
lambdaType
.
parameterCount
(),
names
);
return
new
SimpleMethodHandl
e
(
srcType
,
form
);
return
SimpleMethodHandle
.
mak
e
(
srcType
,
form
);
}
static
...
...
@@ -555,7 +580,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
names
[
arity
+
3
]
=
new
Name
(
new
NamedFunction
(
invokeBasic
),
targetArgs
);
LambdaForm
form
=
new
LambdaForm
(
"guard"
,
lambdaType
.
parameterCount
(),
names
);
return
new
SimpleMethodHandl
e
(
target
.
type
(),
form
);
return
SimpleMethodHandle
.
mak
e
(
target
.
type
(),
form
);
}
private
static
class
GuardWithCatch
{
...
...
src/share/classes/java/lang/invoke/MethodHandleNatives.java
浏览文件 @
3b5dc386
...
...
@@ -325,15 +325,28 @@ class MethodHandleNatives {
static
MemberName
linkMethodImpl
(
Class
<?>
callerClass
,
int
refKind
,
Class
<?>
defc
,
String
name
,
Object
type
,
Object
[]
appendixResult
)
{
if
(
defc
!=
MethodHandle
.
class
||
refKind
!=
REF_invokeVirtual
)
throw
new
LinkageError
(
"no such method "
+
defc
.
getName
()+
"."
+
name
+
type
);
switch
(
name
)
{
case
"invoke"
:
return
Invokers
.
genericInvokerMethod
(
callerClass
,
type
,
appendixResult
);
case
"invokeExact"
:
return
Invokers
.
exactInvokerMethod
(
callerClass
,
type
,
appendixResult
);
try
{
if
(
defc
==
MethodHandle
.
class
&&
refKind
==
REF_invokeVirtual
)
{
switch
(
name
)
{
case
"invoke"
:
return
Invokers
.
genericInvokerMethod
(
fixMethodType
(
callerClass
,
type
),
appendixResult
);
case
"invokeExact"
:
return
Invokers
.
exactInvokerMethod
(
fixMethodType
(
callerClass
,
type
),
appendixResult
);
}
}
}
catch
(
Throwable
ex
)
{
if
(
ex
instanceof
LinkageError
)
throw
(
LinkageError
)
ex
;
else
throw
new
LinkageError
(
ex
.
getMessage
(),
ex
);
}
throw
new
UnsupportedOperationException
(
"linkMethod "
+
name
);
throw
new
LinkageError
(
"no such method "
+
defc
.
getName
()+
"."
+
name
+
type
);
}
private
static
MethodType
fixMethodType
(
Class
<?>
callerClass
,
Object
type
)
{
if
(
type
instanceof
MethodType
)
return
(
MethodType
)
type
;
else
return
MethodType
.
fromMethodDescriptorString
((
String
)
type
,
callerClass
.
getClassLoader
());
}
// Tracing logic:
static
MemberName
linkMethodTracing
(
Class
<?>
callerClass
,
int
refKind
,
...
...
@@ -351,6 +364,7 @@ class MethodHandleNatives {
}
}
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
...
...
src/share/classes/java/lang/invoke/MethodHandles.java
浏览文件 @
3b5dc386
...
...
@@ -1876,6 +1876,17 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
return
MethodHandleImpl
.
makeCollectArguments
(
target
,
filter
,
pos
,
false
);
}
// FIXME: Make this public in M1.
/*non-public*/
static
MethodHandle
collectArguments
(
MethodHandle
target
,
int
pos
,
MethodHandle
collector
)
{
MethodType
targetType
=
target
.
type
();
MethodType
filterType
=
collector
.
type
();
if
(
filterType
.
returnType
()
!=
void
.
class
&&
filterType
.
returnType
()
!=
targetType
.
parameterType
(
pos
))
throw
newIllegalArgumentException
(
"target and filter types do not match"
,
targetType
,
filterType
);
return
MethodHandleImpl
.
makeCollectArguments
(
target
,
collector
,
pos
,
false
);
}
/**
* Adapts a target method handle by post-processing
* its return value (if any) with a filter (another method handle).
...
...
src/share/classes/java/lang/invoke/MethodType.java
浏览文件 @
3b5dc386
...
...
@@ -111,6 +111,36 @@ class MethodType implements java.io.Serializable {
void
setForm
(
MethodTypeForm
f
)
{
form
=
f
;
}
/** This number, mandated by the JVM spec as 255,
* is the maximum number of <em>slots</em>
* that any Java method can receive in its argument list.
* It limits both JVM signatures and method type objects.
* The longest possible invocation will look like
* {@code staticMethod(arg1, arg2, ..., arg255)} or
* {@code x.virtualMethod(arg1, arg2, ..., arg254)}.
*/
/*non-public*/
static
final
int
MAX_JVM_ARITY
=
255
;
// this is mandated by the JVM spec.
/** This number is the maximum arity of a method handle, 254.
* It is derived from the absolute JVM-imposed arity by subtracting one,
* which is the slot occupied by the method handle itself at the
* beginning of the argument list used to invoke the method handle.
* The longest possible invocation will look like
* {@code mh.invoke(arg1, arg2, ..., arg254)}.
*/
// Issue: Should we allow MH.invokeWithArguments to go to the full 255?
/*non-public*/
static
final
int
MAX_MH_ARITY
=
MAX_JVM_ARITY
-
1
;
// deduct one for mh receiver
/** This number is the maximum arity of a method handle invoker, 253.
* It is derived from the absolute JVM-imposed arity by subtracting two,
* which are the slots occupied by invoke method handle, and the the
* target method handle, which are both at the beginning of the argument
* list used to invoke the target method handle.
* The longest possible invocation will look like
* {@code invokermh.invoke(targetmh, arg1, arg2, ..., arg253)}.
*/
/*non-public*/
static
final
int
MAX_MH_INVOKER_ARITY
=
MAX_MH_ARITY
-
1
;
// deduct one more for invoker
private
static
void
checkRtype
(
Class
<?>
rtype
)
{
rtype
.
equals
(
rtype
);
// null check
}
...
...
@@ -131,7 +161,9 @@ class MethodType implements java.io.Serializable {
return
slots
;
}
static
void
checkSlotCount
(
int
count
)
{
if
((
count
&
0xFF
)
!=
count
)
assert
((
MAX_JVM_ARITY
&
(
MAX_JVM_ARITY
+
1
))
==
0
);
// MAX_JVM_ARITY must be power of 2 minus 1 for following code trick to work:
if
((
count
&
MAX_JVM_ARITY
)
!=
count
)
throw
newIllegalArgumentException
(
"bad parameter count "
+
count
);
}
private
static
IndexOutOfBoundsException
newIndexOutOfBoundsException
(
Object
num
)
{
...
...
src/share/classes/java/lang/invoke/SimpleMethodHandle.java
浏览文件 @
3b5dc386
...
...
@@ -35,10 +35,14 @@ import java.util.logging.Logger;
* @author jrose
*/
final
class
SimpleMethodHandle
extends
MethodHandle
{
SimpleMethodHandle
(
MethodType
type
,
LambdaForm
form
)
{
private
SimpleMethodHandle
(
MethodType
type
,
LambdaForm
form
)
{
super
(
type
,
form
);
}
/*non-public*/
static
SimpleMethodHandle
make
(
MethodType
type
,
LambdaForm
form
)
{
return
new
SimpleMethodHandle
(
type
,
form
);
}
@Override
MethodHandle
bindArgument
(
int
pos
,
char
basicType
,
Object
value
)
{
MethodType
type2
=
type
().
dropParameterTypes
(
pos
,
pos
+
1
);
...
...
src/share/classes/java/lang/invoke/WrongMethodTypeException.java
浏览文件 @
3b5dc386
...
...
@@ -59,4 +59,27 @@ public class WrongMethodTypeException extends RuntimeException {
public
WrongMethodTypeException
(
String
s
)
{
super
(
s
);
}
/**
* Constructs a {@code WrongMethodTypeException} with the specified
* detail message and cause.
*
* @param s the detail message.
* @param cause the cause of the exception, or null.
*/
//FIXME: make this public in MR1
/*non-public*/
WrongMethodTypeException
(
String
s
,
Throwable
cause
)
{
super
(
s
,
cause
);
}
/**
* Constructs a {@code WrongMethodTypeException} with the specified
* cause.
*
* @param cause the cause of the exception, or null.
*/
//FIXME: make this public in MR1
/*non-public*/
WrongMethodTypeException
(
Throwable
cause
)
{
super
(
cause
);
}
}
src/share/classes/sun/invoke/util/ValueConversions.java
浏览文件 @
3b5dc386
此差异已折叠。
点击以展开。
test/java/lang/invoke/BigArityTest.java
0 → 100644
浏览文件 @
3b5dc386
此差异已折叠。
点击以展开。
test/java/lang/invoke/MaxTest.java
已删除
100644 → 0
浏览文件 @
9f55f327
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary BoundMethodHandle tests with primitive types
* @compile MaxTest.java
* @run junit/othervm test.java.lang.invoke.MaxTest
*/
package
test.java.lang.invoke
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.lang.invoke.MethodHandle
;
import
java.lang.invoke.MethodHandles
;
import
java.lang.invoke.MethodType
;
import
org.junit.Test
;
public
class
MaxTest
{
static
MethodHandles
.
Lookup
LOOKUP
=
MethodHandles
.
lookup
();
private
MethodHandle
getMax
(
Class
<?>
t
)
throws
Throwable
{
return
LOOKUP
.
findStatic
(
Math
.
class
,
"max"
,
MethodType
.
methodType
(
t
,
t
,
t
));
}
static
int
ITERATION_COUNT
=
40000
;
static
{
String
iterations
=
System
.
getProperty
(
MaxTest
.
class
.
getSimpleName
()
+
".ITERATION_COUNT"
);
if
(
iterations
==
null
)
{
iterations
=
System
.
getProperty
(
MaxTest
.
class
.
getName
()
+
".ITERATION_COUNT"
);
}
if
(
iterations
!=
null
)
{
ITERATION_COUNT
=
Integer
.
parseInt
(
iterations
);
}
}
@Test
public
void
testMaxLong
()
throws
Throwable
{
final
Class
<?>
C
=
long
.
class
;
final
long
P
=
23L
;
final
long
Q
=
42L
;
final
long
R
=
Math
.
max
(
P
,
Q
);
for
(
int
i
=
0
;
i
<
ITERATION_COUNT
;
++
i
)
{
MethodHandle
h
=
getMax
(
C
);
assertEquals
((
long
)
h
.
invokeExact
(
P
,
Q
),
R
);
MethodHandle
bh
=
MethodHandles
.
insertArguments
(
h
,
0
,
P
);
assertEquals
((
long
)
bh
.
invokeExact
(
Q
),
R
);
MethodHandle
bbh
=
MethodHandles
.
insertArguments
(
bh
,
0
,
Q
);
assertEquals
((
long
)
bbh
.
invokeExact
(),
R
);
MethodHandle
b2h
=
MethodHandles
.
insertArguments
(
h
,
1
,
Q
);
assertEquals
((
long
)
b2h
.
invokeExact
(
P
),
R
);
MethodHandle
bb2h
=
MethodHandles
.
insertArguments
(
b2h
,
0
,
P
);
assertEquals
((
long
)
bb2h
.
invokeExact
(),
R
);
}
}
@Test
public
void
testMaxInt
()
throws
Throwable
{
final
Class
<?>
C
=
int
.
class
;
final
int
P
=
23
;
final
int
Q
=
42
;
final
int
R
=
Math
.
max
(
P
,
Q
);
for
(
int
i
=
0
;
i
<
ITERATION_COUNT
;
++
i
)
{
MethodHandle
h
=
getMax
(
C
);
assertEquals
((
int
)
h
.
invokeExact
(
P
,
Q
),
R
);
MethodHandle
bh
=
MethodHandles
.
insertArguments
(
h
,
0
,
P
);
assertEquals
((
int
)
bh
.
invokeExact
(
Q
),
R
);
MethodHandle
bbh
=
MethodHandles
.
insertArguments
(
bh
,
0
,
Q
);
assertEquals
((
int
)
bbh
.
invokeExact
(),
R
);
MethodHandle
b2h
=
MethodHandles
.
insertArguments
(
h
,
1
,
Q
);
assertEquals
((
int
)
b2h
.
invokeExact
(
P
),
R
);
MethodHandle
bb2h
=
MethodHandles
.
insertArguments
(
b2h
,
0
,
P
);
assertEquals
((
int
)
bb2h
.
invokeExact
(),
R
);
}
}
@Test
public
void
testMaxFloat
()
throws
Throwable
{
final
Class
<?>
C
=
float
.
class
;
final
float
P
=
23
F
;
final
float
Q
=
42
F
;
final
float
R
=
Math
.
max
(
P
,
Q
);
final
float
D
=
0.1
F
;
for
(
int
i
=
0
;
i
<
ITERATION_COUNT
;
++
i
)
{
MethodHandle
h
=
getMax
(
C
);
assertEquals
((
float
)
h
.
invokeExact
(
P
,
Q
),
R
,
D
);
MethodHandle
bh
=
MethodHandles
.
insertArguments
(
h
,
0
,
P
);
assertEquals
((
float
)
bh
.
invokeExact
(
Q
),
R
,
D
);
MethodHandle
bbh
=
MethodHandles
.
insertArguments
(
bh
,
0
,
Q
);
assertEquals
((
float
)
bbh
.
invokeExact
(),
R
,
D
);
MethodHandle
b2h
=
MethodHandles
.
insertArguments
(
h
,
1
,
Q
);
assertEquals
((
float
)
b2h
.
invokeExact
(
P
),
R
,
D
);
MethodHandle
bb2h
=
MethodHandles
.
insertArguments
(
b2h
,
0
,
P
);
assertEquals
((
float
)
bb2h
.
invokeExact
(),
R
,
D
);
}
}
@Test
public
void
testMaxDouble
()
throws
Throwable
{
final
Class
<?>
C
=
double
.
class
;
final
double
P
=
23
F
;
final
double
Q
=
42
F
;
final
double
R
=
Math
.
max
(
P
,
Q
);
final
double
D
=
0.1
;
for
(
int
i
=
0
;
i
<
ITERATION_COUNT
;
++
i
)
{
MethodHandle
h
=
getMax
(
C
);
assertEquals
((
double
)
h
.
invokeExact
(
P
,
Q
),
R
,
D
);
MethodHandle
bh
=
MethodHandles
.
insertArguments
(
h
,
0
,
P
);
assertEquals
((
double
)
bh
.
invokeExact
(
Q
),
R
,
D
);
MethodHandle
bbh
=
MethodHandles
.
insertArguments
(
bh
,
0
,
Q
);
assertEquals
((
double
)
bbh
.
invokeExact
(),
R
,
D
);
MethodHandle
b2h
=
MethodHandles
.
insertArguments
(
h
,
1
,
Q
);
assertEquals
((
double
)
b2h
.
invokeExact
(
P
),
R
,
D
);
MethodHandle
bb2h
=
MethodHandles
.
insertArguments
(
b2h
,
0
,
P
);
assertEquals
((
double
)
bb2h
.
invokeExact
(),
R
,
D
);
}
}
}
test/java/lang/invoke/MethodHandlesTest.java
浏览文件 @
3b5dc386
...
...
@@ -1485,7 +1485,7 @@ public class MethodHandlesTest {
RuntimeException
error
=
null
;
try
{
target
=
id
.
asType
(
newType
);
}
catch
(
Runtim
eException
ex
)
{
}
catch
(
WrongMethodTyp
eException
ex
)
{
error
=
ex
;
}
if
(
verbosity
>=
3
)
...
...
@@ -2381,47 +2381,100 @@ public class MethodHandlesTest {
assertSame
(
thrown
,
caught
);
}
//
@Test
@Test
public
void
testInterfaceCast
()
throws
Throwable
{
//if (CAN_SKIP_WORKING) return;
startTest
(
"interfaceCast"
);
for
(
Class
<?>
ctype
:
new
Class
<?>[]{
Object
.
class
,
String
.
class
,
CharSequence
.
class
,
Number
.
class
,
Iterable
.
class
})
{
testInterfaceCast
(
ctype
,
false
,
false
);
testInterfaceCast
(
ctype
,
true
,
false
);
testInterfaceCast
(
ctype
,
false
,
true
);
testInterfaceCast
(
ctype
,
true
,
true
);
assert
(
(((
Object
)
"foo"
)
instanceof
CharSequence
));
assert
(!(((
Object
)
"foo"
)
instanceof
Iterable
));
for
(
MethodHandle
mh
:
new
MethodHandle
[]{
MethodHandles
.
identity
(
String
.
class
),
MethodHandles
.
identity
(
CharSequence
.
class
),
MethodHandles
.
identity
(
Iterable
.
class
)
})
{
if
(
verbosity
>
0
)
System
.
out
.
println
(
"-- mh = "
+
mh
);
for
(
Class
<?>
ctype
:
new
Class
<?>[]{
Object
.
class
,
String
.
class
,
CharSequence
.
class
,
Number
.
class
,
Iterable
.
class
})
{
if
(
verbosity
>
0
)
System
.
out
.
println
(
"---- ctype = "
+
ctype
.
getName
());
// doret docast
testInterfaceCast
(
mh
,
ctype
,
false
,
false
);
testInterfaceCast
(
mh
,
ctype
,
true
,
false
);
testInterfaceCast
(
mh
,
ctype
,
false
,
true
);
testInterfaceCast
(
mh
,
ctype
,
true
,
true
);
}
}
}
public
void
testInterfaceCast
(
Class
<?>
ctype
,
boolean
doret
,
boolean
docast
)
throws
Throwable
{
String
str
=
"normal return value"
;
MethodHandle
mh
=
MethodHandles
.
identity
(
String
.
class
);
private
static
Class
<?>
i2o
(
Class
<?>
c
)
{
return
(
c
.
isInterface
()
?
Object
.
class
:
c
);
}
public
void
testInterfaceCast
(
MethodHandle
mh
,
Class
<?>
ctype
,
boolean
doret
,
boolean
docast
)
throws
Throwable
{
MethodHandle
mh0
=
mh
;
if
(
verbosity
>
1
)
System
.
out
.
println
(
"mh="
+
mh
+
", ctype="
+
ctype
.
getName
()+
", doret="
+
doret
+
", docast="
+
docast
);
String
normalRetVal
=
"normal return value"
;
MethodType
mt
=
mh
.
type
();
MethodType
mt0
=
mt
;
if
(
doret
)
mt
=
mt
.
changeReturnType
(
ctype
);
else
mt
=
mt
.
changeParameterType
(
0
,
ctype
);
if
(
docast
)
mh
=
MethodHandles
.
explicitCastArguments
(
mh
,
mt
);
else
mh
=
mh
.
asType
(
mt
);
assertEquals
(
mt
,
mh
.
type
());
MethodType
mt1
=
mt
;
// this bit is needed to make the interface types disappear for invokeWithArguments:
mh
=
MethodHandles
.
explicitCastArguments
(
mh
,
mt
.
generic
());
boolean
expectFail
=
!
ctype
.
isInstance
(
str
);
if
(
ctype
.
isInterface
())
{
// special rules: interfaces slide by more frequently
if
(
docast
||
!
doret
)
expectFail
=
false
;
Class
<?>[]
step
=
{
mt1
.
parameterType
(
0
),
// param as passed to mh at first
mt0
.
parameterType
(
0
),
// param after incoming cast
mt0
.
returnType
(),
// return value before cast
mt1
.
returnType
(),
// return value after outgoing cast
};
// where might a checkCast occur?
boolean
[]
checkCast
=
new
boolean
[
step
.
length
];
// the string value must pass each step without causing an exception
if
(!
docast
)
{
if
(!
doret
)
{
if
(
step
[
0
]
!=
step
[
1
])
checkCast
[
1
]
=
true
;
// incoming value is cast
}
else
{
if
(
step
[
2
]
!=
step
[
3
])
checkCast
[
3
]
=
true
;
// outgoing value is cast
}
}
boolean
expectFail
=
false
;
for
(
int
i
=
0
;
i
<
step
.
length
;
i
++)
{
Class
<?>
c
=
step
[
i
];
if
(!
checkCast
[
i
])
c
=
i2o
(
c
);
if
(!
c
.
isInstance
(
normalRetVal
))
{
if
(
verbosity
>
3
)
System
.
out
.
println
(
"expect failure at step "
+
i
+
" in "
+
Arrays
.
toString
(
step
)+
Arrays
.
toString
(
checkCast
));
expectFail
=
true
;
break
;
}
}
countTest
(!
expectFail
);
if
(
verbosity
>
2
)
System
.
out
.
println
(
"expectFail="
+
expectFail
+
", mt="
+
mt
);
Object
res
;
try
{
res
=
mh
.
invokeWithArguments
(
str
);
res
=
mh
.
invokeWithArguments
(
normalRetVal
);
}
catch
(
Exception
ex
)
{
res
=
ex
;
}
boolean
sawFail
=
!(
res
instanceof
String
);
if
(
sawFail
!=
expectFail
)
{
System
.
out
.
println
(
"*** testInterfaceCast: "
+
mh
+
" was "
+
mt
+
" => "
+
res
+(
docast
?
" (explicitCastArguments)"
:
""
));
}
if
(!
sawFail
)
{
assertFalse
(
res
.
toString
(),
expectFail
);
assertEquals
(
str
,
res
);
System
.
out
.
println
(
"*** testInterfaceCast: mh0 = "
+
mh0
);
System
.
out
.
println
(
" retype using "
+(
docast
?
"explicitCastArguments"
:
"asType"
)+
" to "
+
mt
+
" => "
+
mh
);
System
.
out
.
println
(
" call returned "
+
res
);
System
.
out
.
println
(
" expected "
+(
expectFail
?
"an exception"
:
normalRetVal
));
}
if
(!
expectFail
)
{
assertFalse
(
res
.
toString
(),
sawFail
);
assertEquals
(
normalRetVal
,
res
);
}
else
{
assertTrue
(
res
.
toString
(),
expect
Fail
);
assertTrue
(
res
.
toString
(),
saw
Fail
);
}
}
...
...
test/java/lang/invoke/PermuteArgsTest.java
浏览文件 @
3b5dc386
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011,
2012,
Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
...
...
@@ -25,7 +25,7 @@
/* @test
* @summary unit tests for method handles which permute their arguments
* @run junit/othervm -ea -esa -DPermuteArgsTest.MAX_ARITY=8 test.java.lang.invoke.PermuteArgsTest
* @run junit/othervm -
XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -
ea -esa -DPermuteArgsTest.MAX_ARITY=8 test.java.lang.invoke.PermuteArgsTest
*/
/* Examples of manual runs:
* java -DPermuteArgsTest.{DRY_RUN=true,MAX_ARITY=253} test.java.lang.invoke.PermuteArgsTest
...
...
@@ -191,7 +191,11 @@ public class PermuteArgsTest {
pt
=
mt1
.
parameterType
(
mt1
.
parameterCount
()
-
posArgs
);
mt1
=
mt1
.
appendParameterTypes
(
pt
);
}
return
mh
.
asType
(
mt1
);
try
{
return
mh
.
asType
(
mt1
);
}
catch
(
WrongMethodTypeException
|
IllegalArgumentException
ex
)
{
throw
new
IllegalArgumentException
(
"cannot convert to type "
+
mt1
+
" from "
+
mh
,
ex
);
}
}
static
MethodHandle
findTestMH
(
String
name
,
int
[]
perm
)
throws
ReflectiveOperationException
{
int
arity
=
perm
.
length
;
...
...
test/java/lang/invoke/RicochetTest.java
浏览文件 @
3b5dc386
...
...
@@ -256,7 +256,7 @@ public class RicochetTest {
//System.out.println(" expect="+expect);
// now use the combined MH, and test the output:
MethodHandle
mh
=
collectArguments
(
lister
,
pos
,
INT_COLLECTORS
[
collects
]);
MethodHandle
mh
=
collectArguments
(
lister
,
pos
,
int
[].
class
,
INT_COLLECTORS
[
collects
]);
if
(
mh
==
null
)
continue
;
// no infix collection, yet
assert
(
mh
.
type
().
parameterCount
()
==
inputs
);
Object
observe
=
mh
.
asSpreader
(
int
[].
class
,
args
.
length
).
invokeExact
(
args
);
...
...
@@ -266,13 +266,53 @@ public class RicochetTest {
}
}
private
static
MethodHandle
collectArguments
(
MethodHandle
lister
,
int
pos
,
MethodHandle
collector
)
{
@Test
public
void
testByteCollects
()
throws
Throwable
{
if
(!
startTest
(
"testByteCollects"
))
return
;
for
(
MethodHandle
lister
:
BYTE_LISTERS
)
{
int
outputs
=
lister
.
type
().
parameterCount
();
for
(
int
collects
=
0
;
collects
<=
Math
.
min
(
outputs
,
BYTE_COLLECTORS
.
length
-
1
);
collects
++)
{
int
inputs
=
outputs
-
1
+
collects
;
if
(
inputs
<
0
)
continue
;
for
(
int
pos
=
0
;
pos
+
collects
<=
inputs
;
pos
++)
{
MethodHandle
collector
=
BYTE_COLLECTORS
[
collects
];
byte
[]
args
=
new
byte
[
inputs
];
int
ap
=
0
,
arg
=
31
;
for
(
int
i
=
0
;
i
<
pos
;
i
++)
args
[
ap
++]
=
(
byte
)(
arg
++
+
0
);
for
(
int
i
=
0
;
i
<
collects
;
i
++)
args
[
ap
++]
=
(
byte
)(
arg
++
+
10
);
while
(
ap
<
args
.
length
)
args
[
ap
++]
=
(
byte
)(
arg
++
+
20
);
// calculate piecemeal:
//System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
byte
[]
collargs
=
Arrays
.
copyOfRange
(
args
,
pos
,
pos
+
collects
);
byte
coll
=
(
byte
)
collector
.
asSpreader
(
byte
[].
class
,
collargs
.
length
).
invokeExact
(
collargs
);
byte
[]
listargs
=
Arrays
.
copyOfRange
(
args
,
0
,
outputs
);
System
.
arraycopy
(
args
,
pos
+
collects
,
listargs
,
pos
+
1
,
outputs
-
(
pos
+
1
));
listargs
[
pos
]
=
coll
;
//System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs));
Object
expect
=
lister
.
asSpreader
(
byte
[].
class
,
listargs
.
length
).
invokeExact
(
listargs
);
//System.out.println(" expect="+expect);
// now use the combined MH, and test the output:
MethodHandle
mh
=
collectArguments
(
lister
,
pos
,
byte
[].
class
,
BYTE_COLLECTORS
[
collects
]);
if
(
mh
==
null
)
continue
;
// no infix collection, yet
assert
(
mh
.
type
().
parameterCount
()
==
inputs
);
Object
observe
=
mh
.
asSpreader
(
byte
[].
class
,
args
.
length
).
invokeExact
(
args
);
assertEquals
(
expect
,
observe
);
}
}
}
}
private
static
MethodHandle
collectArguments
(
MethodHandle
lister
,
int
pos
,
Class
<?>
array
,
MethodHandle
collector
)
{
int
collects
=
collector
.
type
().
parameterCount
();
int
outputs
=
lister
.
type
().
parameterCount
();
if
(
pos
==
outputs
-
1
)
return
MethodHandles
.
filterArguments
(
lister
,
pos
,
collector
.
asSpreader
(
int
[].
class
,
collects
))
.
asCollector
(
int
[].
class
,
collects
);
collector
.
asSpreader
(
array
,
collects
))
.
asCollector
(
array
,
collects
);
//return MethodHandles.collectArguments(lister, pos, collector); //no such animal
return
null
;
}
...
...
@@ -537,6 +577,9 @@ public class RicochetTest {
private
static
final
MethodHandle
[]
INT_COLLECTORS
=
{
constant
(
int
.
class
,
42
),
opI
,
opI2
,
opI3
,
opI4
};
private
static
final
MethodHandle
[]
BYTE_COLLECTORS
=
{
constant
(
byte
.
class
,
(
byte
)
42
),
i2b
(
opI
),
i2b
(
opI2
),
i2b
(
opI3
),
i2b
(
opI4
)
};
private
static
final
MethodHandle
[]
LONG_COLLECTORS
=
{
constant
(
long
.
class
,
42
),
opJ
,
opJ2
,
opJ3
};
...
...
@@ -559,21 +602,36 @@ public class RicochetTest {
Collections
.
nCopies
(
8
,
int
.
class
));
private
static
final
MethodHandle
list8longs
=
findStatic
(
"list8longs"
,
Object
.
class
,
Collections
.
nCopies
(
8
,
long
.
class
));
private
static
final
MethodHandle
[]
INT_LISTERS
,
LONG_LISTERS
;
private
static
final
MethodHandle
[]
INT_LISTERS
,
LONG_LISTERS
,
BYTE_LISTERS
;
static
{
int
listerCount
=
list8ints
.
type
().
parameterCount
()
+
1
;
INT_LISTERS
=
new
MethodHandle
[
listerCount
];
LONG_LISTERS
=
new
MethodHandle
[
listerCount
];
BYTE_LISTERS
=
new
MethodHandle
[
listerCount
];
MethodHandle
lister
=
list8ints
;
MethodHandle
llister
=
list8longs
;
for
(
int
i
=
listerCount
-
1
;
;
i
--)
{
INT_LISTERS
[
i
]
=
lister
;
LONG_LISTERS
[
i
]
=
llister
;
BYTE_LISTERS
[
i
]
=
i2b
(
lister
);
if
(
i
==
0
)
break
;
lister
=
insertArguments
(
lister
,
i
-
1
,
0
);
llister
=
insertArguments
(
llister
,
i
-
1
,
0L
);
}
}
private
static
MethodHandle
i2b
(
MethodHandle
mh
)
{
return
MethodHandles
.
explicitCastArguments
(
mh
,
subst
(
mh
.
type
(),
int
.
class
,
byte
.
class
));
}
private
static
MethodType
subst
(
MethodType
mt
,
Class
<?>
from
,
Class
<?>
to
)
{
for
(
int
i
=
0
;
i
<
mt
.
parameterCount
();
i
++)
{
if
(
mt
.
parameterType
(
i
)
==
from
)
mt
=
mt
.
changeParameterType
(
i
,
to
);
}
if
(
mt
.
returnType
()
==
from
)
mt
=
mt
.
changeReturnType
(
to
);
return
mt
;
}
private
static
Object
convI_L
(
int
x
)
{
stress
();
return
(
Object
)
x
;
}
private
static
int
convL_I
(
Object
x
)
{
stress
();
return
(
int
)
x
;
}
...
...
test/sun/invoke/util/ValueConversionsTest.java
浏览文件 @
3b5dc386
...
...
@@ -159,14 +159,8 @@ public class ValueConversionsTest {
assertEquals
(
caster
.
type
(),
ValueConversions
.
identity
().
type
());
for
(
Object
obj
:
objects
)
{
Class
<?>
src
=
obj
.
getClass
();
boolean
canCast
;
if
(
dst
.
isInterface
())
{
canCast
=
true
;
}
else
{
canCast
=
dst
.
isAssignableFrom
(
src
);
assertEquals
(
canCast
,
dst
.
isInstance
(
obj
));
}
//System.out.println("obj="+obj+" <: dst="+dst);
boolean
canCast
=
dst
.
isAssignableFrom
(
src
);
//System.out.println("obj="+obj+" <: dst="+dst+(canCast ? " (OK)" : " (will fail)"));
try
{
Object
result
=
caster
.
invokeExact
(
obj
);
if
(
canCast
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录