[
https://issues.apache.org/jira/browse/GROOVY-9103?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Daniel Sun updated GROOVY-9103:
-------------------------------
Description:
This cloned issue is to cover the rest part of GROOVY-9081.
{color:#d04437}*Case 2, 8, 9* are illegal access and have to be fixed by
changing user's code(including the implementation of some AST
Transformations){color}
h4. _{color:#d04437}2) Sub-class derives the {{protected}} members from public
class,{color}_
{color:#d04437}_*2.1) Clone array via {{clone}} method of {{java.lang.Object}}
, use {{Arrays.copyOf}} instead ( Note: the method is {{protected}}, truely
illegal access, we should fix our code), e.g.*_{color}
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy#L163-L180]
As we can see, {{groovy.transform.Immutable}} will generate the code to invoke
{{clone}} on array object:
{code:java}
@groovy.transform.Generated
public HasList(java.util.Map args) {
metaClass = /*BytecodeExpression*/
if ( args == null) {
args = [:]
}
org.codehaus.groovy.transform.ImmutableASTTransformation.checkPropNames(this,
args)
if ( args .letters == null) {
this .letters = null
} else {
this .letters =
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .letters,
'clone', new java.lang.Object[][])) as java.lang.String[])
}
if ( args .nums == null) {
this .nums = null
} else {
if ( args .nums instanceof java.lang.Cloneable) {
this .nums =
((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.SortedSet ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.SortedSet<E extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.SortedMap ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.SortedMap<K extends java.lang.Object, V extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.Set ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.Set<E extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.Map ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.Map<K extends java.lang.Object, V extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.List ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.List<E extends java.lang.Object>) :
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.Collection)) as java.lang.Object)
} else {
this .nums = (( args .nums instanceof java.util.SortedSet ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.SortedSet<E extends java.lang.Object>) : args .nums instanceof
java.util.SortedMap ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.SortedMap<K extends java.lang.Object, V extends java.lang.Object>) :
args .nums instanceof java.util.Set ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.Set<E extends java.lang.Object>) : args .nums instanceof
java.util.Map ? org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((
args .nums) as java.util.Map<K extends java.lang.Object, V extends
java.lang.Object>) : args .nums instanceof java.util.List ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.List<E extends java.lang.Object>) :
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.Collection)) as java.lang.Object)
}
}
}
{code}
{color:#d04437}_*2.2) Access the overrided {{protected}} method of
sub-class(Truely illegal access, we should fix our code), e.g.*_{color}
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy#L124]
{{AppClassLoader}} derives {{ClassLoader}}, but {{Class<?> loadClass(String
cn, boolean resolve)}} of {{AppClassLoader}} is still {{protected}}, we should
not access it if we do not want warnings.
{color:#d04437}_*2.3) Access the {{protected final}} method(Truely illegal
access, we should fix our code), e.g.*_{color}
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/reflection/SecurityTest.java#L243-L258]
{{protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)}} of {{ClassLoader}}
h4. -6) ?Get {{properties}} of objects(including {{private}} methods)-
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy#L68]
{{''.properties}} will access {{java.lang.String.isLatin1}}, which is
{{private}}
We fix the case by filtering non-properties and allow illegal access properties
for backward compatibility via switching on JVM option
{{-Dgroovy.allow.illegal.access.properties=true}}(Note: {{false}} is the
default value)
h4. {color:#d04437}_8) Access invisible members or explicitly invoke
{{setAccessible}} (Truely illegal access, we should fix our code), e.g._{color}
[https://github.com/apache/groovy/blob/master/src/test/groovy/transform/ThreadInterruptTest.groovy#L65]
h4. {color:#d04437}9) Avoid use {{java.math.BigInteger.multiply(long)}}, and
use {{BigInteger.multiply(BigInteger)}} instead*(Truely illegal access)*,
e.g.{color}
[https://github.com/apache/groovy/blob/master/src/test/groovy/transform/stc/STCAssignmentTest.groovy#L692]
*More examples:*
{code:java}
1G * 2 // emits illegal access warnings
1G * 2G // Recommend to append "G" to the number to represent BigInteger, no
illegal access warnings
{code}
We make Groovy a bit smarter by find the proper method, i.e.
{{BigInteger.multiply(BigInteger)}}
was:
This cloned issue is to cover the rest part of GROOVY-9081.
{color:#d04437}*Case 2, 8, 9* are illegal access and have to be fixed by
changing user's code(including the implementation of some AST
Transformations){color}
h4. _{color:#d04437}2) Sub-class derives the {{protected}} members from public
class,{color}_
{color:#d04437}_*2.1) Clone array via {{clone}} method of {{java.lang.Object}}
, use {{Arrays.copyOf}} instead ( Note: the method is {{protected}}, truely
illegal access, we should fix our code), e.g.*_{color}
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy#L163-L180]
As we can see, {{groovy.transform.Immutable}} will generate the code to invoke
{{clone}} on array object:
{code:java}
@groovy.transform.Generated
public HasList(java.util.Map args) {
metaClass = /*BytecodeExpression*/
if ( args == null) {
args = [:]
}
org.codehaus.groovy.transform.ImmutableASTTransformation.checkPropNames(this,
args)
if ( args .letters == null) {
this .letters = null
} else {
this .letters =
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .letters,
'clone', new java.lang.Object[][])) as java.lang.String[])
}
if ( args .nums == null) {
this .nums = null
} else {
if ( args .nums instanceof java.lang.Cloneable) {
this .nums =
((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.SortedSet ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.SortedSet<E extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.SortedMap ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.SortedMap<K extends java.lang.Object, V extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.Set ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.Set<E extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.Map ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.Map<K extends java.lang.Object, V extends java.lang.Object>) :
((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
java.util.List ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.List<E extends java.lang.Object>) :
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
java.util.Collection)) as java.lang.Object)
} else {
this .nums = (( args .nums instanceof java.util.SortedSet ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.SortedSet<E extends java.lang.Object>) : args .nums instanceof
java.util.SortedMap ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.SortedMap<K extends java.lang.Object, V extends java.lang.Object>) :
args .nums instanceof java.util.Set ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.Set<E extends java.lang.Object>) : args .nums instanceof
java.util.Map ? org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((
args .nums) as java.util.Map<K extends java.lang.Object, V extends
java.lang.Object>) : args .nums instanceof java.util.List ?
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.List<E extends java.lang.Object>) :
org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
java.util.Collection)) as java.lang.Object)
}
}
}
{code}
{color:#d04437}_*2.2) Access the overrided {{protected}} method of
sub-class(Truely illegal access, we should fix our code), e.g.*_{color}
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy#L124]
{{AppClassLoader}} derives {{ClassLoader}}, but {{Class<?> loadClass(String
cn, boolean resolve)}} of {{AppClassLoader}} is still {{protected}}, we should
not access it if we do not want warnings.
{color:#d04437}_*2.3) Access the {{protected final}} method(Truely illegal
access, we should fix our code), e.g.*_{color}
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/reflection/SecurityTest.java#L243-L258]
{{protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)}} of {{ClassLoader}}
h4. -6) ?Get {{properties}} of objects(including {{private}} methods)-
[https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy#L68]
{{''.properties}} will access {{java.lang.String.isLatin1}}, which is
{{private}}
We fix the case by filtering non-properties and allow illegal access properties
for backward compatibility via switching on JVM option
{{-Dgroovy.allow.illegal.access.properties=true}}(Note: {{false}} is the
default value)
h4. {color:#d04437}_8) Access invisible members or explicitly invoke
{{setAccessible}} (Truely illegal access, we should fix our code), e.g._{color}
[https://github.com/apache/groovy/blob/master/src/test/groovy/transform/ThreadInterruptTest.groovy#L65]
h4. {color:#d04437}9) Avoid use {{java.math.BigInteger.multiply(long)}}, and
use {{BigInteger.multiply(BigInteger)}} instead*(Truely illegal access)*,
e.g.{color}
[https://github.com/apache/groovy/blob/master/src/test/groovy/transform/stc/STCAssignmentTest.groovy#L692]
*More examples:*
{code:java}
1G * 2 // emits illegal access warnings
1G * 2G // append "G" to the number to represent BigInteger, no illegal access
warnings
{code}
We can make Groovy a bit smarter by find the proper method, i.e.
{{BigInteger.multiply(BigInteger)}}
> CLONE - CLONE - Fix warning "An illegal reflective access operation has
> occurred"
> ---------------------------------------------------------------------------------
>
> Key: GROOVY-9103
> URL: https://issues.apache.org/jira/browse/GROOVY-9103
> Project: Groovy
> Issue Type: Improvement
> Components: groovy-jdk
> Affects Versions: 2.4.11, 2.4.15
> Environment: >gradle --version
> Gradle 4.2
> Build time: 2017-09-20 14:48:23 UTC
> Revision: 5ba503cc17748671c83ce35d7da1cffd6e24dfbd
> Groovy: 2.4.11
> Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015
> JVM: 9 (Oracle Corporation 9+181)
> OS: Windows 10 10.0 amd64
> Reporter: Benjamin Roedell
> Assignee: Daniel Sun
> Priority: Major
> Labels: breaking_change
> Fix For: 3.0.0-beta-2
>
> Time Spent: 0.5h
> Remaining Estimate: 0h
>
> This cloned issue is to cover the rest part of GROOVY-9081.
> {color:#d04437}*Case 2, 8, 9* are illegal access and have to be fixed by
> changing user's code(including the implementation of some AST
> Transformations){color}
> h4. _{color:#d04437}2) Sub-class derives the {{protected}} members from
> public class,{color}_
> {color:#d04437}_*2.1) Clone array via {{clone}} method of
> {{java.lang.Object}} , use {{Arrays.copyOf}} instead ( Note: the method is
> {{protected}}, truely illegal access, we should fix our code), e.g.*_{color}
>
> [https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy#L163-L180]
> As we can see, {{groovy.transform.Immutable}} will generate the code to
> invoke {{clone}} on array object:
> {code:java}
> @groovy.transform.Generated
> public HasList(java.util.Map args) {
> metaClass = /*BytecodeExpression*/
> if ( args == null) {
> args = [:]
> }
>
> org.codehaus.groovy.transform.ImmutableASTTransformation.checkPropNames(this,
> args)
> if ( args .letters == null) {
> this .letters = null
> } else {
> this .letters =
> ((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .letters,
> 'clone', new java.lang.Object[][])) as java.lang.String[])
> }
> if ( args .nums == null) {
> this .nums = null
> } else {
> if ( args .nums instanceof java.lang.Cloneable) {
> this .nums =
> ((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
> 'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
> java.util.SortedSet ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
> args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
> java.util.SortedSet<E extends java.lang.Object>) :
> ((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
> 'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
> java.util.SortedMap ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
> args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
> java.util.SortedMap<K extends java.lang.Object, V extends java.lang.Object>)
> : ((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
> 'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
> java.util.Set ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
> args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
> java.util.Set<E extends java.lang.Object>) :
> ((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
> 'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
> java.util.Map ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
> args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
> java.util.Map<K extends java.lang.Object, V extends java.lang.Object>) :
> ((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke( args .nums,
> 'clone', new java.lang.Object[][])) as java.lang.Object) instanceof
> java.util.List ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
> args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
> java.util.List<E extends java.lang.Object>) :
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable((((org.codehaus.groovy.runtime.ReflectionMethodInvoker.invoke(
> args .nums, 'clone', new java.lang.Object[][])) as java.lang.Object)) as
> java.util.Collection)) as java.lang.Object)
> } else {
> this .nums = (( args .nums instanceof java.util.SortedSet ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
> java.util.SortedSet<E extends java.lang.Object>) : args .nums instanceof
> java.util.SortedMap ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
> java.util.SortedMap<K extends java.lang.Object, V extends java.lang.Object>)
> : args .nums instanceof java.util.Set ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
> java.util.Set<E extends java.lang.Object>) : args .nums instanceof
> java.util.Map ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
> java.util.Map<K extends java.lang.Object, V extends java.lang.Object>) : args
> .nums instanceof java.util.List ?
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
> java.util.List<E extends java.lang.Object>) :
> org.codehaus.groovy.runtime.DefaultGroovyMethods.asImmutable(( args .nums) as
> java.util.Collection)) as java.lang.Object)
> }
> }
> }
> {code}
> {color:#d04437}_*2.2) Access the overrided {{protected}} method of
> sub-class(Truely illegal access, we should fix our code), e.g.*_{color}
>
> [https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/transform/classloading/TransformsAndCustomClassLoadersTest.groovy#L124]
> {{AppClassLoader}} derives {{ClassLoader}}, but {{Class<?> loadClass(String
> cn, boolean resolve)}} of {{AppClassLoader}} is still {{protected}}, we
> should not access it if we do not want warnings.
> {color:#d04437}_*2.3) Access the {{protected final}} method(Truely illegal
> access, we should fix our code), e.g.*_{color}
>
> [https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/reflection/SecurityTest.java#L243-L258]
> {{protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
> ProtectionDomain protectionDomain)}} of {{ClassLoader}}
> h4. -6) ?Get {{properties}} of objects(including {{private}} methods)-
> [https://github.com/apache/groovy/blob/master/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy#L68]
> {{''.properties}} will access {{java.lang.String.isLatin1}}, which is
> {{private}}
> We fix the case by filtering non-properties and allow illegal access
> properties for backward compatibility via switching on JVM option
> {{-Dgroovy.allow.illegal.access.properties=true}}(Note: {{false}} is the
> default value)
> h4. {color:#d04437}_8) Access invisible members or explicitly invoke
> {{setAccessible}} (Truely illegal access, we should fix our code),
> e.g._{color}
> [https://github.com/apache/groovy/blob/master/src/test/groovy/transform/ThreadInterruptTest.groovy#L65]
> h4. {color:#d04437}9) Avoid use {{java.math.BigInteger.multiply(long)}}, and
> use {{BigInteger.multiply(BigInteger)}} instead*(Truely illegal access)*,
> e.g.{color}
> [https://github.com/apache/groovy/blob/master/src/test/groovy/transform/stc/STCAssignmentTest.groovy#L692]
> *More examples:*
> {code:java}
> 1G * 2 // emits illegal access warnings
> 1G * 2G // Recommend to append "G" to the number to represent BigInteger, no
> illegal access warnings
> {code}
> We make Groovy a bit smarter by find the proper method, i.e.
> {{BigInteger.multiply(BigInteger)}}
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)