| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.codehaus.groovy.classgen.asm.sc |
| |
| import groovy.transform.stc.BugsSTCTest |
| |
| /** |
| * Unit tests for static type checking : bugs. |
| */ |
| class BugsStaticCompileTest extends BugsSTCTest implements StaticCompilationTestSupport { |
| |
| void testGroovy5498PropertyAccess() { |
| assertScript ''' |
| class Test { |
| |
| List getListVar() { |
| new ArrayList() |
| } |
| |
| void someMethod() { |
| def t = new Object() |
| t = this |
| |
| t.getListVar() //No error here |
| t.listVar //error is being reported here |
| assert t.listVar == t.getListVar() |
| } |
| } |
| new Test().someMethod() |
| ''' |
| } |
| |
| // GROOVY-5512 |
| void testCreateRangeInInnerClass() { |
| def shell = new GroovyShell() |
| shell.evaluate ''' |
| class Outer { |
| static class Inner { |
| @groovy.transform.CompileStatic |
| int m() { |
| int x = 0 |
| for (int i in 1..10) {x++} |
| x |
| } |
| } |
| } |
| assert new Outer.Inner().m() == 10 |
| ''' |
| } |
| |
| // GROOVY-5526 |
| void testAssertEqualsShouldNotThrowVerifyError() { |
| assertScript ''' |
| import static org.junit.Assert.*; |
| import groovy.transform.CompileStatic; |
| |
| class CompilerBugs { |
| |
| public static void main(String[] args) { |
| int expected = 0 |
| assertEquals(expected, args.length) |
| } |
| |
| } |
| ''' |
| } |
| |
| // GROOVY-5529 |
| void testStaticCompilationOfClosureWhenSingleMethodAnnotated() { |
| new GroovyShell().evaluate '''import groovy.transform.ASTTest |
| import static org.codehaus.groovy.control.CompilePhase.* |
| |
| interface Row { |
| int getKey() |
| } |
| |
| class RowImpl implements Row { |
| int getKey() { 1 } |
| } |
| |
| @groovy.transform.CompileStatic |
| def test() { |
| def rows = [new RowImpl(), new RowImpl(), new RowImpl()] |
| |
| rows.each { Row row -> |
| println row.key |
| } |
| } |
| |
| test() |
| ''' |
| } |
| |
| // GROOVY-5536 |
| void testShouldNotThrowVerifyErrorWithNullDereferenceInIf() { |
| assertScript ''' |
| boolean getDescriptorForPlugin(File pluginDir) { |
| if (pluginDir?.exists()) { true } else { false } |
| } |
| assert getDescriptorForPlugin(null) == false |
| ''' |
| } |
| |
| // GROOVY- |
| void testPowerShouldNotThrowVerifyError() { |
| assertScript '''int squarePlusOne(int num) { |
| num ** num + 1 |
| } |
| assert squarePlusOne(2) == 5 |
| ''' |
| } |
| |
| // GROOVY-5570 |
| void testShouldNotThrowVerifyErrorRegisterContainsWrongType() { |
| assertScript ''' |
| void foo() { |
| boolean idx = false |
| def cl = { idx } |
| } |
| ''' |
| assertScript ''' |
| void foo() { |
| int idx = 0 |
| def cl = { idx } |
| } |
| ''' |
| } |
| |
| // GROOVY-5572 |
| void testTernaryOperatorWithNull() { |
| assertScript ''' |
| assert (true ? null : true) == null |
| ''' |
| } |
| |
| // GROOVY-5564 |
| void testSkipStaticCompile() { |
| new GroovyShell().evaluate '''import groovy.transform.CompileStatic |
| import static groovy.transform.TypeCheckingMode.SKIP |
| |
| @CompileStatic |
| class A { |
| @CompileStatic(SKIP) |
| String toString(Object o) { o } |
| } |
| |
| def a = new A() |
| assert a.toString('foo')=='foo' |
| assert a.toString(1) == '1' |
| ''' |
| } |
| |
| // GROOVY-5586 |
| void testCanonicalInInnerClass() { |
| new GroovyShell().evaluate '''import groovy.transform.* |
| @CompileStatic |
| class CanonicalStaticTest extends GroovyTestCase { |
| @Canonical class Thing { |
| String stuff |
| } |
| |
| Thing testCanonical() { |
| new Thing() |
| } |
| } |
| assert new CanonicalStaticTest().testCanonical().toString() == 'CanonicalStaticTest$Thing(null)' |
| ''' |
| } |
| |
| // GROOVY-5607 |
| // duplicate of GROOVY-5573, see ArraysAndCollectionsSTCTest#testArrayNewInstance() |
| void testStaticNewInstanceMethodClash() { |
| assertScript ''' |
| class Sql { |
| static Sql newInstance(String s1, String s2, String s3, String s4) { |
| new Sql() |
| } |
| } |
| |
| @groovy.transform.CompileStatic |
| class Main { |
| void test() { |
| @ASTTest(phase=INSTRUCTION_SELECTION, value= { |
| assert node.rightExpression.getNodeMetaData(INFERRED_TYPE).nameWithoutPackage == 'Sql' |
| }) |
| def sql = Sql.newInstance("a", "b", "c", "d") |
| } |
| } |
| |
| new Main().test() |
| ''' |
| } |
| |
| void testCompileStaticTwiceShouldNotBeAProblem() { |
| new GroovyShell().evaluate '''import groovy.transform.CompileStatic |
| @CompileStatic |
| class Tool { |
| @CompileStatic // annotated too, even if class is already annotated |
| String relativePath(File relbase, File file) { |
| def pathParts = [] |
| def currentFile = file |
| while (currentFile != null && currentFile != relbase) { |
| pathParts += currentFile.name |
| currentFile = currentFile.parentFile |
| } |
| pathParts.reverse().join('/') |
| } |
| } |
| File a = new File('foo') |
| File b = new File(new File(a, 'bar'), 'baz') |
| assert new Tool().relativePath(a,b) == 'bar/baz' |
| ''' |
| } |
| |
| void testCompileStaticTwiceShouldNotBeAProblemUsingCustomizer() { |
| assertScript '''import groovy.transform.CompileStatic |
| @CompileStatic |
| class Tool { |
| @CompileStatic // annotated too, even if class is already annotated |
| String relativePath(File relbase, File file) { |
| def pathParts = [] |
| def currentFile = file |
| while (currentFile != null && currentFile != relbase) { |
| pathParts += currentFile.name |
| currentFile = currentFile.parentFile |
| } |
| pathParts.reverse().join('/') |
| } |
| } |
| File a = new File('foo') |
| File b = new File(new File(a, 'bar'), 'baz') |
| assert new Tool().relativePath(a,b) == 'bar/baz' |
| ''' |
| } |
| |
| // GROOVY-5613 |
| void testNullSafeAssignment() { |
| assertScript ''' |
| class A { |
| int x = -1 |
| } |
| A a = new A() |
| @ASTTest(phase=INSTRUCTION_SELECTION, value={ |
| assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE |
| }) |
| def x = a?.x |
| ''' |
| } |
| |
| void testNullSafeAssignmentWithLong() { |
| assertScript ''' |
| class A { |
| long x = -1 |
| } |
| A a = new A() |
| @ASTTest(phase=INSTRUCTION_SELECTION, value={ |
| assert node.getNodeMetaData(INFERRED_TYPE) == Long_TYPE |
| }) |
| def x = a?.x |
| ''' |
| } |
| |
| void testNullSafeAssignmentWithChar() { |
| assertScript ''' |
| class A { |
| char x = 'a' |
| } |
| A a = new A() |
| @ASTTest(phase=INSTRUCTION_SELECTION, value={ |
| assert node.getNodeMetaData(INFERRED_TYPE) == Character_TYPE |
| }) |
| def x = a?.x |
| assert x == 'a' |
| ''' |
| } |
| |
| void testCallStaticallyImportedMethodWithNullSafeArgument() { |
| assertScript '''import static java.lang.Math.abs |
| class A { |
| int x = -1 |
| } |
| def a = new A() |
| def x = a?.x |
| assert abs(a?.x) == 1 |
| ''' |
| } |
| |
| void testClosureAsInterfaceArgument() { |
| assertScript ''' |
| Closure c = { Integer x, Integer y -> x <=> y } |
| def list = [ 3,1,5,2,4 ] |
| assert ((Collection)list).sort(c) == [1,2,3,4,5] |
| ''' |
| } |
| |
| void testInferredTypeForInteger() { |
| assertScript ''' |
| @ASTTest(phase=INSTRUCTION_SELECTION, value={ |
| assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE |
| }) |
| Integer x = 1 |
| ''' |
| } |
| |
| // GROOVY-5671 |
| void testPostfixIncPrimitiveInteger() { |
| assertScript ''' |
| int x = 0 |
| x++ |
| x++ |
| assert x == 2 |
| assert x++ == 2 |
| assert x == 3 |
| ''' |
| } |
| |
| void testPostfixIncInteger() { |
| assertScript ''' |
| Integer x = 0 |
| x++ |
| x++ |
| assert x == 2 |
| assert x++ == 2 |
| assert x == 3 |
| ''' |
| } |
| |
| void testPostfixDecInt() { |
| assertScript ''' |
| int x = 0 |
| x-- |
| x-- |
| assert x == -2 |
| assert x-- == -2 |
| assert x == -3 |
| ''' |
| } |
| |
| void testPostfixDecInteger() { |
| assertScript ''' |
| Integer x = 0 |
| x-- |
| x-- |
| assert x == -2 |
| assert x-- == -2 |
| assert x == -3 |
| ''' |
| } |
| |
| void testPrefixIncPrimitiveInteger() { |
| assertScript ''' |
| int x = 0 |
| ++x |
| ++x |
| assert x == 2 |
| assert ++x == 3 |
| assert x == 3 |
| ''' |
| } |
| |
| void testPrefixIncInteger() { |
| assertScript ''' |
| Integer x = 0 |
| ++x |
| ++x |
| assert x == 2 |
| assert ++x == 3 |
| assert x == 3 |
| ''' |
| } |
| |
| void testPrefixDecInt() { |
| assertScript ''' |
| int x = 0 |
| --x |
| --x |
| assert --x == -3 |
| assert x == -3 |
| ''' |
| } |
| |
| void testPrefixDecInteger() { |
| assertScript ''' |
| Integer x = 0 |
| --x |
| --x |
| assert --x == -3 |
| assert x == -3 |
| ''' |
| } |
| |
| void testShouldSkipSpreadOperator() { |
| new GroovyShell().evaluate '''import groovy.transform.TypeCheckingMode |
| import groovy.transform.CompileStatic |
| |
| @CompileStatic // top level must be @CS |
| class Foo { |
| @CompileStatic(TypeCheckingMode.SKIP) |
| static void foo(fun, args) { |
| new Runnable() { // create an anonymous class which should *not* be visited |
| void run() { |
| fun(*args) // spread operator is disallowed with STC/SC, but SKIP should prevent from an error |
| } |
| } |
| } |
| } |
| new Foo() |
| ''' |
| } |
| |
| // GROOVY-5672 |
| void testTypeCheckedPlusCompileStatic() { |
| new GroovyShell().evaluate '''import groovy.transform.CompileStatic |
| import groovy.transform.TypeChecked |
| |
| @TypeChecked |
| @CompileStatic |
| class SampleClass { |
| def a = "some string" |
| def b = a.toString() |
| } |
| new SampleClass() |
| ''' |
| } |
| |
| void testSubclassShouldNotThrowArrayIndexOutOfBoundsException() { |
| assertScript ''' |
| // The error only shows up if the subclass is compiled *before* the superclass |
| class Subclass extends Z { |
| public Subclass(double x, double y, double z) { |
| super(x,y,z) |
| } |
| } |
| class Z { |
| double x, y, z |
| |
| public Z(double x, double y, double z) { this.x = x; this.y = y; this.z = z } |
| |
| public Z negative() { return new Z(-x, -y, -z) } |
| } |
| new Subclass(0,0,0) |
| ''' |
| } |
| |
| void testIncrementOperatorOnInt() { |
| assertScript ''' |
| int incInt(int n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt(5) == 7''' |
| } |
| |
| void testIncrementOperatorOnShort() { |
| assertScript ''' |
| short incInt(short n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt((short)5) == 7''' |
| } |
| |
| void testIncrementOperatorOnByte() { |
| assertScript ''' |
| byte incInt(byte n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt((byte)5) == 7''' |
| } |
| |
| void testIncrementOperatorOnLong() { |
| assertScript ''' |
| long incInt(long n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt(5) == 7''' |
| } |
| |
| void testIncrementOperatorOnFloat() { |
| assertScript ''' |
| float incInt(float n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt(5) == 7''' |
| } |
| |
| void testIncrementOperatorOnDouble() { |
| assertScript ''' |
| double incInt(double n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt(5) == 7''' |
| } |
| |
| void testIncrementOperatorOnChar() { |
| assertScript ''' |
| char incInt(char n) { |
| def result = n |
| ++result |
| result++ |
| return result |
| } |
| assert incInt((char)'a') == (char)('c')''' |
| } |
| |
| void testIncrementField() { |
| assertScript ''' |
| class Foo { |
| int x |
| } |
| def f = new Foo() |
| f.x++ |
| assert f.x == 1 |
| assert f.x++ == 1 |
| assert f.x == 2 |
| ''' |
| } |
| |
| // GROOVY-5789 |
| void testLoopWithIncrement() { |
| assertScript ''' |
| int execute() { |
| // using a list, so that if the loop is endless, the test eventually fails with OOM |
| List<Integer> list = new LinkedList<Integer>() |
| for (def i = 0; i < 4; ++i) { println i; list << i } |
| list.size() |
| } |
| assert execute() == 4 |
| ''' |
| } |
| |
| // GROOVY-5800 |
| void testInOperator() { |
| assertScript ''' |
| boolean m( Integer i ) { |
| i in [ 1, 2, 3 ] |
| } |
| assert m(1) == true |
| assert m(4) == false |
| ''' |
| } |
| |
| // GROOVY-5814 |
| void testCompileStaticImmutable() { |
| assertScript ''' |
| import groovy.transform.* |
| |
| @Immutable |
| class Test { |
| int a |
| String b |
| } |
| |
| new Test( 1, 'tim' ) |
| ''' |
| } |
| |
| // GROOVY-5738 |
| void testAccessFieldFromGStringAfterInstanceOf() { |
| new GroovyShell().evaluate ''' |
| class Greeting { String who } |
| |
| @groovy.transform.CompileStatic |
| class GreetingActor { |
| |
| def receive = { |
| if(it instanceof Greeting) { |
| println "Hello ${it.who}" |
| } |
| } |
| |
| } |
| new GreetingActor().receive(new Greeting(who:'cedric')) |
| ''' |
| } |
| |
| // GROOVY-5738 |
| void testAccessMethodFromGStringAfterInstanceOf() { |
| new GroovyShell().evaluate ''' |
| class Greeting { |
| String who |
| String whoAmI() { who } |
| } |
| |
| @groovy.transform.CompileStatic |
| class GreetingActor { |
| |
| def receive = { |
| if(it instanceof Greeting) { |
| println "Hello ${it.whoAmI()}" |
| } |
| } |
| |
| } |
| new GreetingActor().receive(new Greeting(who:'cedric')) |
| ''' |
| } |
| |
| // GROOVY-5804 |
| void testNegateSharedBooleanInClosure() { |
| assertScript ''' |
| boolean x = false |
| def cl = { |
| if (!x) { |
| assert true |
| } else { |
| assert false |
| } |
| } |
| cl() |
| ''' |
| } |
| |
| void testCallClosureInInnerClass() { |
| assertScript ''' |
| class A { |
| static class B { // bug doesn't occur if not wrapped into an inner class |
| static void foo() { |
| def cl = { -> println 'ok' } |
| cl() |
| } |
| } |
| } |
| A.B.foo() |
| ''' |
| } |
| |
| // GROOVY-5890 |
| void testIsCaseWithClassLiteral() { |
| assertScript ''' |
| assert 'a' in String |
| ''' |
| } |
| |
| // GROOVY-5887 |
| void testSpreadCallWithArray() { |
| assertScript ''' |
| def list = 'a,b,c'.split(/,/)*.trim() |
| assert list == ['a','b','c'] |
| ''' |
| } |
| |
| // GROOVY-5919 |
| void testPrivateAccessorsWithSubClass() { |
| assertScript ''' |
| class Top { |
| private int foo = 666 |
| private class InnerTop { |
| int foo() { foo } |
| } |
| } |
| class Bottom extends Top { |
| private int bar = 666 |
| private class InnerBottom { |
| int bar() { bar } // name clash for fpaccess$0 |
| } |
| } |
| new Bottom() |
| ''' |
| } |
| |
| void testSuperMethodCallInSkippedSection() { |
| assertScript '''import groovy.transform.CompileStatic |
| import groovy.transform.TypeCheckingMode |
| class Top { |
| public int foo() { 123 } |
| } |
| class Bottom extends Top { |
| @CompileStatic(TypeCheckingMode.SKIP) |
| public int bar() { |
| foo() |
| } |
| } |
| def obj = new Bottom() |
| assert obj.bar() == 123 |
| ''' |
| } |
| |
| void testArrayGetOnObject() { |
| shouldFailWithMessages ''' |
| def foo(Object o) { |
| o[0] |
| } |
| ''', 'Cannot find matching method java.lang.Object#getAt(int)' |
| } |
| |
| void testStaticCompileWithPattern() { |
| shouldFailWithMessages ''' |
| def fieldMatcher = '[x=1] [a=b] [foo=bar]' =~ ~"\\\\[([^=\\\\[]+)=([^\\\\]]+)\\\\]" |
| assert fieldMatcher instanceof java.util.regex.Matcher |
| @ASTTest(phase=INSTRUCTION_SELECTION, value = { |
| assert node.getNodeMetaData(INFERRED_TYPE) == OBJECT_TYPE |
| }) |
| def value = fieldMatcher[0] |
| // should not pass |
| def str = value[0] |
| ''', 'Cannot find matching method java.lang.Object#getAt(int)' |
| } |
| |
| void testChainedNullSafePropertyOnMap() { |
| assertScript ''' |
| Map<String, Map<String,Map<String,Integer>>> m=[:] |
| // this is ok |
| assert m?.a == null |
| assert m?.a?.b == null |
| assert m?.a?.b?.c == null |
| assert m?.a?.b?.c?.intValue() == null |
| ''' |
| } |
| |
| void testNullSafePropertyOnList() { |
| assertScript ''' |
| List<Class> classes = null |
| // this is ok |
| assert classes?.name == null |
| ''' |
| } |
| |
| // GROOVY-6061 |
| void testShouldCallPutAt() { |
| assertScript ''' |
| def swapUseStatic(List<Integer> a, int i, int j) { |
| int temp = a[i] |
| a[i] = a[j] //BUG HERE, a[i] = ... doesn't call "putAt" |
| a[j] = temp |
| } |
| def list = [1,2] |
| swapUseStatic(list, 0, 1) |
| assert list == [2,1] |
| ''' |
| } |
| |
| // GROOVY-6101 |
| void testShouldNotGenerateInvalidClassWithNullSafeInvocationOnMethodReturningPrimitiveType() { |
| assertScript ''' |
| class Piece { |
| int x() { 333 } |
| } |
| |
| void foo() { |
| Piece[] pieces = [new Piece(), null] as Piece[] |
| int sum = 0 |
| for (int i=0;i<pieces.length;i++) { |
| if (pieces[i]?.x()) { |
| sum += pieces[i].x() |
| } |
| } |
| assert sum == 333 |
| } |
| foo() |
| ''' |
| } |
| |
| // GROOVY-6101 fix side effect |
| void testMakeSureOptimizationKeepsSideEffect() { |
| assertScript ''' |
| class Foo { |
| boolean hasSideEffect = false |
| int x() { hasSideEffect=true; 333 } |
| } |
| Foo foo = new Foo() |
| if (foo.x()==null) { |
| println 'ok' |
| } |
| assert foo.hasSideEffect |
| ''' |
| } |
| |
| void testShouldNotThrowNPEBecauseLHSIsNull() { |
| assertScript ''' |
| class A { |
| int foo() { 123 } |
| } |
| A a = null |
| def bar = a?.foo() == 123 // will evaluate to false |
| assert bar == false |
| ''' |
| } |
| |
| // GROOVY-5921 |
| void testShouldHandleLongArray() { |
| assertScript ''' |
| class Groovy5921 { |
| long[] data = [1L] |
| |
| void doSomething() { |
| data[ 0 ] |= 0x7ffffffl |
| } |
| } |
| |
| def b = new Groovy5921() |
| b.doSomething() |
| |
| ''' |
| } |
| |
| void testShouldHandleLongArrayWithUnboxing() { |
| assertScript ''' |
| class Groovy5921 { |
| long[] data = [1L] |
| |
| void doSomething() { |
| data[ 0 ] |= Long.valueOf(0x7ffffffl) |
| } |
| } |
| |
| def b = new Groovy5921() |
| b.doSomething() |
| |
| ''' |
| } |
| |
| void testShouldHandleBoxedLongArray() { |
| assertScript ''' |
| class Groovy5921 { |
| Long[] data = [1L] |
| |
| void doSomething() { |
| data[ 0 ] |= Long.valueOf(0x7ffffffl) |
| } |
| } |
| |
| def b = new Groovy5921() |
| b.doSomething() |
| |
| ''' |
| } |
| |
| void testShouldHandleBoxedLongArrayWithBoxing() { |
| assertScript ''' |
| class Groovy5921 { |
| Long[] data = [1L] |
| |
| void doSomething() { |
| data[ 0 ] |= 0x7ffffffl |
| } |
| } |
| |
| def b = new Groovy5921() |
| b.doSomething() |
| |
| ''' |
| } |
| |
| // GROOVY-6113 |
| void testCallObjectVargsMethodWithPrimitiveIntConstant() { |
| try { |
| assertScript ''' |
| int sum(Object... elems) { |
| (Integer)elems.toList().sum() |
| } |
| int x = sum(Closure.DELEGATE_FIRST) |
| assert x == Closure.DELEGATE_FIRST |
| ''' |
| } finally { |
| // println astTrees |
| } |
| } |
| |
| // GROOVY-6095 |
| void testServletError() { |
| def shell = new GroovyShell() |
| shell.evaluate ''' |
| @Grab('javax.servlet:javax.servlet-api:3.0.1') |
| import groovy.transform.CompileStatic |
| |
| import javax.servlet.ServletContext |
| import javax.servlet.ServletRegistration |
| |
| /** |
| * author: Richard Vowles - http://gplus.to/RichardVowles |
| */ |
| @CompileStatic |
| class ServletExample { |
| |
| public void myMethod(ServletContext ctx) { |
| ctx.getServletRegistrations().each { String name, ServletRegistration sr -> |
| println name |
| } |
| |
| } |
| } |
| new ServletExample() |
| ''' |
| } |
| |
| // GROOVY-6137 |
| void testIsCaseShouldBeNullSafe() { |
| assertScript ''' |
| def isCaseNullCS(a, b) { |
| a in b |
| } |
| assert isCaseNullCS(1, null) == false |
| assert isCaseNullCS(null, 1) == false |
| assert isCaseNullCS(1,1) == true |
| assert isCaseNullCS(1,2) == false |
| assert isCaseNullCS(2,1) == false |
| ''' |
| } |
| |
| // GROOVY-6242 |
| void testGetAtBigInt() { |
| assertScript ''' |
| class Sequence { |
| public Sequence() {} |
| static BigInteger getAt(final int index) { 1G } |
| static BigInteger getAt(final BigInteger index) { getAt(index as int) } |
| } |
| |
| class Iterator implements java.util.Iterator { |
| private BigInteger currentIndex = 0G |
| private final Sequence sequence |
| Iterator(final Sequence s) { sequence = s } |
| boolean hasNext() { return true } |
| @ASTTest(phase=INSTRUCTION_SELECTION,value={ |
| def expr = node.code.statements[0].expression |
| def indexType = expr.rightExpression.getNodeMetaData(INFERRED_TYPE) |
| assert indexType == make(BigInteger) |
| }) |
| BigInteger next() { sequence[currentIndex++] } |
| void remove() { throw new UnsupportedOperationException() } |
| } |
| |
| def it = new Iterator(new Sequence()) |
| assert it.next() == 1G |
| ''' |
| } |
| |
| // GROOVY-6243 |
| void testSwapUsingMultipleAssignment() { |
| assertScript ''' |
| def swap(result,next) { |
| print "($result,$next) -> " |
| (result, next) = [next, result] |
| println "($result,$next)" |
| [result, next] |
| } |
| |
| assert swap(0,1) == [1,0] |
| assert swap('a','b') == ['b','a'] |
| assert swap('a', 1) == [1, 'a'] |
| def o1 = new Object() |
| def o2 = new Date() |
| assert swap(o1,o2) == [o2, o1] |
| ''' |
| } |
| |
| // GROOVY-5882 |
| void testMod() { |
| assertScript """ |
| int foo(Map<Integer, Object> markers, int i) { |
| int res = 0 |
| for (e in markers.entrySet()) { |
| res += i % e.key |
| } |
| return res |
| } |
| assert foo([(1):null,(2):null,(3):null],2)==2 |
| """ |
| |
| assertScript """ |
| int foo(Map<Integer, Object> markers, int i) { |
| int res = 0 |
| for (e in markers.entrySet()) { |
| int intKey = e.key |
| res += i % intKey |
| } |
| return res |
| } |
| assert foo([(1):null,(2):null,(3):null],2)==2 |
| """ |
| assertScript """ |
| int foo(Map<Integer, Object> markers, int i) { |
| int res = 0 |
| for (e in markers.entrySet()) { |
| res += i % e.key.intValue() |
| } |
| return res |
| } |
| assert foo([(1):null,(2):null,(3):null],2)==2 |
| """ |
| } |
| |
| void testSuperCallShouldBeDirect() { |
| try { |
| assertScript ''' |
| class TwoException extends Exception { |
| @ASTTest(phase=INSTRUCTION_SELECTION,value={ |
| def superCall = node.code.statements[0].expression |
| assert superCall.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)!=null |
| }) |
| public TwoException(Throwable t) { |
| super(t) |
| } |
| } |
| def e = new TwoException(null) // will not throw an exception |
| ''' |
| } finally { |
| assert !astTrees.TwoException.contains('selectConstructorAndTransformArguments') |
| } |
| } |
| |
| void testNullSafeOperatorShouldNotCallMethodTwice() { |
| assertScript ''' |
| import java.util.concurrent.atomic.AtomicLong |
| |
| class Sequencer { |
| private final AtomicLong sequenceNumber = new AtomicLong(0) |
| |
| public Long getNext() { |
| return sequenceNumber.getAndIncrement() |
| } |
| } |
| |
| final seq = new Sequencer() |
| (1..5).each { |
| println seq.next?.longValue() |
| } |
| assert seq.next == 5 |
| ''' |
| } |
| |
| void testNullSafeOperatorShouldNotCallMethodTwiceWithPrimitive() { |
| assertScript ''' |
| import java.util.concurrent.atomic.AtomicLong |
| |
| class Sequencer { |
| private final AtomicLong sequenceNumber = new AtomicLong(0) |
| |
| public long getNext() { |
| sequenceNumber.getAndIncrement() |
| } |
| } |
| |
| final seq = new Sequencer() |
| (1..5).each { |
| println seq.next?.longValue() |
| } |
| assert seq.next == 5 |
| ''' |
| } |
| |
| void testNullSafeOperatorShouldNotCallMethodTwice1Arg() { |
| assertScript ''' |
| import java.util.concurrent.atomic.AtomicLong |
| |
| class Sequencer { |
| private final AtomicLong sequenceNumber = new AtomicLong(0) |
| |
| public Long getNext(int factor) { |
| factor*sequenceNumber.getAndIncrement() |
| } |
| } |
| |
| final seq = new Sequencer() |
| (1..5).each { |
| println seq.getNext(2)?.longValue() |
| } |
| assert seq.getNext(2) == 10 |
| ''' |
| } |
| |
| void testNullSafeOperatorShouldNotCallMethodTwiceWithPrimitive1Arg() { |
| assertScript ''' |
| import java.util.concurrent.atomic.AtomicLong |
| |
| class Sequencer { |
| private final AtomicLong sequenceNumber = new AtomicLong(0) |
| |
| public long getNext(int factor) { |
| factor*sequenceNumber.getAndIncrement() |
| } |
| } |
| |
| final seq = new Sequencer() |
| (1..5).each { |
| println seq.getNext(2)?.longValue() |
| } |
| assert seq.getNext(2) == 10 |
| ''' |
| } |
| |
| void testShouldAllowSubscriptOperatorOnSet() { |
| assertScript ''' |
| def map = new LinkedHashMap<>([a:1,b:2]) |
| @ASTTest(phase=INSTRUCTION_SELECTION, value={ |
| def ift = node.getNodeMetaData(INFERRED_TYPE) |
| assert ift == make(Set) |
| assert ift.isUsingGenerics() |
| assert ift.genericsTypes[0].type==STRING_TYPE |
| }) |
| def set = map.keySet() |
| def key = set[0] |
| assert key=='a' |
| ''' |
| assertScript ''' |
| def map = new LinkedHashMap([a:1,b:2]) |
| @ASTTest(phase=INSTRUCTION_SELECTION, value={ |
| def ift = node.getNodeMetaData(INFERRED_TYPE) |
| assert ift == make(Set) |
| assert ift.isUsingGenerics() |
| assert ift.genericsTypes[0].name=='K' |
| }) |
| def set = map.keySet() |
| def key = set[0] |
| assert key=='a' |
| ''' |
| } |
| |
| // GROOVY-6552 |
| void testShouldNotThrowClassCastException() { |
| assertScript '''import java.util.concurrent.Callable |
| |
| String text(Class clazz) { |
| new Callable<String>() { |
| String call() throws Exception { |
| new Callable<String>() { |
| String call() throws Exception { |
| clazz.getName() |
| } |
| }.call() |
| } |
| }.call() |
| } |
| |
| assert text(String) == 'java.lang.String' |
| ''' |
| } |
| |
| // GROOVY-6851 |
| void testShouldNotThrowNPEIfElvisOperatorIsUsedInDefaultArgumentValue() { |
| assertScript '''import org.codehaus.groovy.ast.expr.MethodCallExpression |
| |
| class GrailsHomeWorkspaceReader { |
| @ASTTest(phase=INSTRUCTION_SELECTION,value={ |
| def defaultValue = node.parameters[0].initialExpression |
| assert defaultValue instanceof MethodCallExpression |
| def target = defaultValue.getNodeMetaData(DIRECT_METHOD_CALL_TARGET) |
| assert target != null |
| }) |
| GrailsHomeWorkspaceReader(String grailsHome = System.getProperty('grails.home')) { |
| } |
| } |
| new GrailsHomeWorkspaceReader() |
| ''' |
| assertScript ''' |
| class GrailsHomeWorkspaceReader { |
| GrailsHomeWorkspaceReader(String grailsHome = System.getProperty('grails.home') ?: System.getenv('GRAILS_HOME')) { |
| } |
| } |
| new GrailsHomeWorkspaceReader() |
| ''' |
| } |
| |
| // GROOVY-6342 |
| void testShouldNotThrowNPEIfElvisOperatorIsUsedInsideTernary() { |
| assertScript '''class Inner { |
| int somestuff |
| } |
| Inner inner = null |
| int someInt = inner?.somestuff ?: 0 |
| println someInt |
| |
| ''' |
| } |
| |
| void testAccessOuterClassMethodFromInnerClassConstructor() { |
| assertScript ''' |
| class Parent { |
| String str |
| Parent(String s) { str = s } |
| } |
| class Outer { |
| String a |
| |
| private class Inner extends Parent { |
| Inner() { super(getA()) } |
| } |
| |
| String test() { new Inner().str } |
| } |
| def o = new Outer(a:'ok') |
| assert o.test() == 'ok' |
| ''' |
| } |
| |
| void testAccessOuterClassMethodFromInnerClassConstructorUsingExplicitOuterThis() { |
| assertScript ''' |
| class Parent { |
| String str |
| Parent(String s) { str = s } |
| } |
| class Outer { |
| String a |
| |
| private class Inner extends Parent { |
| Inner() { super(Outer.this.getA()) } |
| } |
| |
| String test() { new Inner().str } |
| } |
| def o = new Outer(a:'ok') |
| assert o.test() == 'ok' |
| ''' |
| } |
| |
| void testAccessOuterClassMethodFromInnerClassConstructorUsingExplicitOuterThisAndProperty() { |
| assertScript ''' |
| class Parent { |
| String str |
| Parent(String s) { str = s } |
| } |
| class Outer { |
| String a |
| |
| private class Inner extends Parent { |
| Inner() { super(Outer.this.a) } |
| } |
| |
| String test() { new Inner().str } |
| } |
| def o = new Outer(a:'ok') |
| assert o.test() == 'ok' |
| ''' |
| } |
| |
| void testAccessOuterClassStaticMethodFromInnerClassConstructor() { |
| assertScript ''' |
| class Parent { |
| String str |
| Parent(String s) { str = s } |
| } |
| class Outer { |
| static String a |
| |
| private class Inner extends Parent { |
| Inner() { super(getA()) } |
| } |
| |
| String test() { new Inner().str } |
| } |
| def o = new Outer() |
| Outer.a = 'ok' |
| assert o.test() == 'ok' |
| ''' |
| } |
| |
| void testStaticMethodFromInnerClassConstructor() { |
| assertScript ''' |
| class Parent { |
| String str |
| Parent(String s) { str = s } |
| } |
| class Outer { |
| private class Inner extends Parent { |
| static String a = 'ok' |
| Inner() { super(getA()) } |
| } |
| |
| String test() { new Inner().str } |
| } |
| def o = new Outer() |
| assert o.test() == 'ok' |
| ''' |
| } |
| |
| // GROOVY-6876 |
| void testSetterOfPrimitiveType() { |
| assertScript ''' |
| class Foo { |
| long bar |
| def method() { |
| setBar(-1L) |
| bar |
| } |
| } |
| assert new Foo().method() == -1L |
| ''' |
| |
| assertScript ''' |
| class Foo { |
| long bar |
| def method(Long v) { |
| setBar(v) |
| bar |
| } |
| } |
| assert new Foo().method(-1L) == -1L |
| ''' |
| |
| assertScript ''' |
| class Foo { |
| long rankOrderingOrId |
| void setRankOrderingOrId(long rankOrderingOrId) { |
| this.rankOrderingOrId = rankOrderingOrId < 0 ? -1 : rankOrderingOrId |
| } |
| } |
| def f = new Foo() |
| f.setRankOrderingOrId(1L) |
| assert f.getRankOrderingOrId() == 1L |
| assert f.rankOrderingOrId == 1L |
| f.rankOrderingOrId = 2L |
| assert f.getRankOrderingOrId() == 2L |
| assert f.rankOrderingOrId == 2L |
| ''' |
| } |
| |
| // GROOVY-6921 |
| void testSubscriptOnClosureSharedVariable() { |
| assertScript ''' |
| def versionRanges = [['1.7', 3]] |
| def versions = versionRanges.collect { versionRange -> (0..versionRange[1]).collect { "${versionRange[0]}.${it}" } }.flatten() |
| assert versions == ['1.7.0','1.7.1','1.7.2','1.7.3'] |
| ''' |
| } |
| |
| // GROOVY-6924 |
| void testShouldNotThrowIncompatibleClassChangeError() { |
| try { |
| assertScript '''import org.codehaus.groovy.classgen.asm.sc.Groovy6924Support |
| class Test { |
| static void foo() { |
| Groovy6924Support bean = new Groovy6924Support() |
| bean.with { |
| foo = 'foo' |
| bar = 'bar' |
| } |
| String val = "$bean.foo and $bean.bar" |
| assert val == 'foo and bar' |
| } |
| } |
| Test.foo() |
| ''' |
| } finally { |
| assert astTrees['Test$_foo_closure1'][1].contains('INVOKEVIRTUAL org/codehaus/groovy/classgen/asm/sc/Groovy6924Support.setFoo (Ljava/lang/String;)V') |
| } |
| } |
| |
| // GROOVY-7381 |
| void testNonVoidSetterCalls(){ |
| assertScript ''' |
| class Foo { |
| int num |
| String name |
| |
| Foo setNum(int num){ |
| this.num = num |
| this |
| } |
| |
| Foo setName(String name){ |
| this.name = name |
| this |
| } |
| } |
| Foo foo = new Foo().setNum(1).setName('fluent') |
| assert foo.num == 1 |
| assert foo.name == 'fluent' |
| ''' |
| } |
| |
| // GROOVY-7610 |
| void testNullSafeIsCallConditionShouldNotThrowVerifyError() { |
| assertScript ''' |
| class A { |
| void ifCondition(Object x, Object y) { |
| if (x?.is(y)) |
| return |
| } |
| |
| void ternaryCondition(Object x, Object y) { |
| x?.is(y) ? 'foo' : 'bar' |
| } |
| } |
| new A() |
| ''' |
| } |
| |
| // GROOVY-7631 |
| void testPrimitiveNotEqualNullShouldReturnTrue() { |
| assertScript ''' |
| assert false != null |
| assert true != null |
| assert (byte) 1 != null |
| assert (short) 1 != null |
| assert 0 != null |
| assert 1 != null |
| assert 1L != null |
| assert 1f != null |
| assert 1d != null |
| assert (char) 1 != null |
| ''' |
| } |
| |
| // GROOVY-7639 |
| void testComparisonOfPrimitiveWithNullSafePrimitivePropertyExpression() { |
| assertScript ''' |
| class Foo { |
| int bar |
| } |
| Foo foo = null |
| assert !(foo?.bar == 7) |
| assert !(foo?.bar > 7) |
| assert foo?.bar < 7 |
| ''' |
| } |
| |
| // GROOVY-7841 |
| void testAssertionOfNonZeroPrimitiveEdgeCases() { |
| assertScript ''' |
| assert 4294967296 |
| assert 0.1f |
| assert 0.1d |
| ''' |
| } |
| |
| // GROOVY-7784 |
| void testWithSamAndVarArgs() { |
| assertScript ''' |
| class Foo { |
| static foo(Integer x, Iterable y, String... z) { [*y.toList(), *z].join('-') } |
| } |
| |
| class Groovy7784 { |
| static emptyVarArgs() { Foo.foo(42, { ['foo', 'bar'].iterator() }) } |
| static singleVarArgs() { Foo.foo(42, { ['foo', 'bar'].iterator() }, 'baz') } |
| static multiVarArgs() { Foo.foo(42, { ['foo', 'bar'].iterator() }, 'baz1', 'baz2') } |
| } |
| |
| assert Groovy7784.emptyVarArgs() == 'foo-bar' |
| assert Groovy7784.singleVarArgs() == 'foo-bar-baz' |
| assert Groovy7784.multiVarArgs() == 'foo-bar-baz1-baz2' |
| ''' |
| } |
| } |
| |