diff --git a/Site-org.codehaus.groovy.eclipse/site.xml b/Site-org.codehaus.groovy.eclipse/site.xml index d5b6d591a9..a3366b5966 100644 --- a/Site-org.codehaus.groovy.eclipse/site.xml +++ b/Site-org.codehaus.groovy.eclipse/site.xml @@ -51,6 +51,10 @@ + + + + diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java index 028b1641d0..f96b758f2a 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/locations/SourceLocationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,9 @@ */ package org.eclipse.jdt.core.groovy.tests.locations; +import static org.eclipse.jdt.groovy.core.tests.GroovyBundle.isAtLeastGroovy; import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; import java.util.List; @@ -225,12 +227,12 @@ private ICompilationUnit createCompilationUnitFor(String pack, String name, Stri @Test public void testSourceLocations() throws Exception { String source = "package p1;\n"+ - "/*t0s*/public class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public static void /*m0sn*/main/*m0en*/(String[] args) /*m0sb*/{\n"+ - " System.out.println(\"Hello world\");\n"+ - " }/*m0e*/\n"+ - " /*f1s*/int /*f1sn*/x/*f1en*/ = 9/*f1e*/;\n"+ - "}/*t0e*/\n"; + "/*t0s*/public class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public static void /*m0sn*/main/*m0en*/(String[] args) /*m0sb*/{\n"+ + " System.out.println(\"Hello world\");\n"+ + " }/*m0e*/\n"+ + " /*f1s*/int /*f1sn*/x/*f1en*/ = 9/*f1e*/;\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -238,12 +240,12 @@ public void testSourceLocations() throws Exception { @Test public void testSourceLocationsNoSemiColons() throws Exception { String source = "package p1;\n"+ - "/*t0s*/public class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public static void /*m0sn*/main/*m0en*/(String[] args) /*m0sb*/{\n"+ - " System.out.println(\"Hello world\");\n"+ - " }/*m0e*/\n"+ - " /*f1s*/int /*f1sn*/x/*f1en*/ = 9/*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/public class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public static void /*m0sn*/main/*m0en*/(String[] args) /*m0sb*/{\n"+ + " System.out.println(\"Hello world\");\n"+ + " }/*m0e*/\n"+ + " /*f1s*/int /*f1sn*/x/*f1en*/ = 9/*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -251,12 +253,12 @@ public void testSourceLocationsNoSemiColons() throws Exception { @Test public void testSourceLocationsNoModifiers() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/def /*m0sn*/main/*m0en*/(String[] args) /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/def /*m0sn*/main/*m0en*/(String[] args) /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -264,9 +266,9 @@ public void testSourceLocationsNoModifiers() throws Exception { @Test public void testSourceLocationsMultipleVariableFragments() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " int /*f0sn*/x/*f0en*/, /*f1sn*/y/*f1en*/, /*f2sn*/z/*f2en*/ = 7\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " int /*f0sn*/x/*f0en*/, /*f1sn*/y/*f1en*/, /*f2sn*/z/*f2en*/ = 7\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -274,11 +276,11 @@ public void testSourceLocationsMultipleVariableFragments() throws Exception { @Test public void testSourceLocationsNoParameterTypes() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/def /*m0sn*/main/*m0en*/(args, fargs, blargs) /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/def /*m0sn*/main/*m0en*/(args, fargs, blargs) /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -286,17 +288,17 @@ public void testSourceLocationsNoParameterTypes() throws Exception { @Test public void testSourceLocationsNoParameters() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/def /*m0sn*/main/*m0en*/() /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*m1s*/def /*m1sn*/main2/*m1en*/() /*m1sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m1e*/\n"+ - " /*m2s*/def /*m2sn*/main3/*m2en*/() /*m2sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m2e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/def /*m0sn*/main/*m0en*/() /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*m1s*/def /*m1sn*/main2/*m1en*/() /*m1sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m1e*/\n"+ + " /*m2s*/def /*m2sn*/main3/*m2en*/() /*m2sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m2e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -304,14 +306,14 @@ public void testSourceLocationsNoParameters() throws Exception { @Test public void testSourceLocationsDefaultParameters() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/def /*m0sn*/main/*m0en*/(args = \"hi!\") /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*m1s*/def /*m1sn*/main2/*m1en*/(args = \"hi!\", blargs = \"bye\") /*m1sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/def /*m0sn*/main/*m0en*/(args = \"hi!\") /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*m1s*/def /*m1sn*/main2/*m1en*/(args = \"hi!\", blargs = \"bye\") /*m1sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -319,12 +321,12 @@ public void testSourceLocationsDefaultParameters() throws Exception { @Test public void testSourceLocationsConstructor() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public /*m0sn*/Hello/*m0en*/() /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public /*m0sn*/Hello/*m0en*/() /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -332,12 +334,12 @@ public void testSourceLocationsConstructor() throws Exception { @Test public void testSourceLocationsConstructorWithParam() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public /*m0sn*/Hello/*m0en*/(String x) /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public /*m0sn*/Hello/*m0en*/(String x) /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -345,12 +347,12 @@ public void testSourceLocationsConstructorWithParam() throws Exception { @Test public void testSourceLocationsConstructorWithParamNoType() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public /*m0sn*/Hello/*m0en*/(x) /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public /*m0sn*/Hello/*m0en*/(x) /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -358,12 +360,12 @@ public void testSourceLocationsConstructorWithParamNoType() throws Exception { @Test public void testSourceLocationsConstructorWithDefaultParam() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public /*m0sn*/Hello/*m0en*/(args = \"9\") /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public /*m0sn*/Hello/*m0en*/(args = \"9\") /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -371,7 +373,7 @@ public void testSourceLocationsConstructorWithDefaultParam() throws Exception { @Test public void testSourceLocationsForScript1() throws Exception { String source = "package p1;\n"+ - "def x"; + "def x"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertScript(source, unit, "def x", "def x"); assertUnit(unit, source); @@ -380,7 +382,7 @@ public void testSourceLocationsForScript1() throws Exception { @Test public void testSourceLocationsForScript2() throws Exception { String source = "package p1;\n"+ - "def x() { }"; + "def x() { }"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertScript(source, unit, "def x", "{ }"); assertUnit(unit, source); @@ -389,7 +391,7 @@ public void testSourceLocationsForScript2() throws Exception { @Test public void testSourceLocationsForScript3() throws Exception { String source = "package p1;\n"+ - "x() \n def x() { }"; + "x() \n def x() { }"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertScript(source, unit, "x()", "{ }"); assertUnit(unit, source); @@ -398,7 +400,7 @@ public void testSourceLocationsForScript3() throws Exception { @Test public void testSourceLocationsForScript4() throws Exception { String source = "package p1;\n"+ - "def x() { }\nx()"; + "def x() { }\nx()"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertScript(source, unit, "def x", "x()"); assertUnit(unit, source); @@ -407,7 +409,7 @@ public void testSourceLocationsForScript4() throws Exception { @Test public void testSourceLocationsForScript5() throws Exception { String source = "package p1;\n"+ - "def x() { }\nx()\ndef y() { }"; + "def x() { }\nx()\ndef y() { }"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertScript(source, unit, "def x", "def y() { }"); assertUnit(unit, source); @@ -416,7 +418,7 @@ public void testSourceLocationsForScript5() throws Exception { @Test public void testSourceLocationsForScript6() throws Exception { String source = "package p1;\n"+ - "x()\n def x() { }\n\ndef y() { }\ny()"; + "x()\n def x() { }\n\ndef y() { }\ny()"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertScript(source, unit, "x()", "\ny()"); assertUnit(unit, source); @@ -425,12 +427,12 @@ public void testSourceLocationsForScript6() throws Exception { @Test public void testSourceLocationsConstructorWithDefaultParams() throws Exception { String source = "package p1;\n"+ - "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public /*m0sn*/Hello/*m0en*/(args = \"9\", String blargs = \"8\") /*m0sb*/{\n"+ - " System.out.println(\"Hello world\")\n"+ - " }/*m0e*/\n"+ - " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public /*m0sn*/Hello/*m0en*/(args = \"9\", String blargs = \"8\") /*m0sb*/{\n"+ + " System.out.println(\"Hello world\")\n"+ + " }/*m0e*/\n"+ + " /*f1s*/def /*f1sn*/x/*f1en*//*f1e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -438,9 +440,9 @@ public void testSourceLocationsConstructorWithDefaultParams() throws Exception { @Test public void testSourceLocationsInterface() throws Exception { String source = "package p1;\n"+ - "/*t0s*/interface /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public /*m0sn*/hello/*m0en*/(args, String blargs)/*m0e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/interface /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public /*m0sn*/hello/*m0en*/(args, String blargs)/*m0e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -448,9 +450,9 @@ public void testSourceLocationsInterface() throws Exception { @Test public void testSourceLocationsAbstractClass() throws Exception { String source = "package p1;\n"+ - "/*t0s*/abstract class /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/public abstract /*m0sn*/hello/*m0en*/(args, String blargs)/*m0e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/abstract class /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/public abstract /*m0sn*/hello/*m0en*/(args, String blargs)/*m0e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -458,9 +460,9 @@ public void testSourceLocationsAbstractClass() throws Exception { @Test public void testSourceLocationsAnnotationDeclaration() throws Exception { String source = "package p1;\n"+ - "/*t0s*/@interface /*t0sn*/Hello/*t0en*/ {\n"+ - " /*m0s*/String /*m0sn*/val/*m0en*/()/*m0sb*//*m0e*/\n"+ - "}/*t0e*/\n"; + "/*t0s*/@interface /*t0sn*/Hello/*t0en*/ {\n"+ + " /*m0s*/String /*m0sn*/val/*m0en*/()/*m0sb*//*m0e*/\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @@ -468,20 +470,21 @@ public void testSourceLocationsAnnotationDeclaration() throws Exception { @Test public void testSourceLocationsEnumDeclaration() throws Exception { String source = "package p1;\n"+ - "/*t0s*/enum /*t0sn*/Hello/*t0en*/ {\n"+ - "}/*t0e*/\n"; + "/*t0s*/enum /*t0sn*/Hello/*t0en*/ {\n"+ + "}/*t0e*/\n"; ICompilationUnit unit = createCompilationUnitFor("p1", "Hello", source); assertUnitWithSingleType(source, unit); } @Test // STS-3878 public void testErrorPositionForUnsupportedOperation() throws Exception { + assumeTrue(!isAtLeastGroovy(26)); String source = - "def a = 'a'\n" + - "def b = 'b'\n" + - "println a === b\n"; + "def a = 'a'\n" + + "def b = 'b'\n" + + "println a === b\n"; IPath root = createGenericProject(); - IPath path = env.addGroovyClass(root, "p", "Hello", source); + IPath path = env.addGroovyClass(root, "", "Hello", source); fullBuild(); expectingSpecificProblemFor(root, new Problem("p/Hello", "Groovy:Operator (\"===\" at 3:11: \"===\" ) not supported @ line 3, column 11.", path, 34, 37, 60, IMarker.SEVERITY_ERROR)); } diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovyCompilerTestSuite.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovyCompilerTestSuite.java index 4fd4a36bad..dbda85e23e 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovyCompilerTestSuite.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovyCompilerTestSuite.java @@ -123,7 +123,7 @@ protected String[] getDefaultClassPaths() { System.arraycopy(cps, 0, newcps, 0, cps.length); String[] ivyVersions = {"2.5.0", "2.4.0"}; - String[] groovyVersions = {"2.5.0-indy", "2.4.13"}; + String[] groovyVersions = {"2.6.0-indy", "2.5.0-indy", "2.4.13"}; try { URL groovyJar = null; for (String groovyVer : groovyVersions) { diff --git a/base/org.codehaus.groovy.eclipse.compilerResolver/src/org/codehaus/groovy/frameworkadapter/util/SpecifiedVersion.java b/base/org.codehaus.groovy.eclipse.compilerResolver/src/org/codehaus/groovy/frameworkadapter/util/SpecifiedVersion.java index 73f529b240..bb7a771385 100644 --- a/base/org.codehaus.groovy.eclipse.compilerResolver/src/org/codehaus/groovy/frameworkadapter/util/SpecifiedVersion.java +++ b/base/org.codehaus.groovy.eclipse.compilerResolver/src/org/codehaus/groovy/frameworkadapter/util/SpecifiedVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ public enum SpecifiedVersion { _23(2, 3, "23"), _24(2, 4, "24"), _25(2, 5, "25"), + _26(2, 6, "26"), DONT_CARE(0, 0, "-1"), UNSPECIFIED(0, 0, "0"); @@ -101,6 +102,8 @@ public static SpecifiedVersion parseVersion(String jarName) { return _24; case 5: return _25; + case 6: + return _26; } break; } @@ -146,6 +149,9 @@ public static SpecifiedVersion findVersionFromString(String compilerLevel) { if ("25".equals(compilerLevel) || "2.5".equals(compilerLevel)) { return _25; } + if ("26".equals(compilerLevel) || "2.6".equals(compilerLevel)) { + return _26; + } if ("0".equals(compilerLevel)) { return UNSPECIFIED; } @@ -154,7 +160,7 @@ public static SpecifiedVersion findVersionFromString(String compilerLevel) { } System.out.println("Invalid Groovy compiler level specified: " + compilerLevel + - "\nMust be one of 16, 1.6, 17, 1.7, 18, 1.8, 19, 1.9, 20, 2.0, 21, 2.1, 22, 2.2, 23, 2.3, 24, 2.4, 25 or 2.5"); + "\nMust be one of 16, 1.6, 17, 1.7, 18, 1.8, 19, 1.9, 20, 2.0, 21, 2.1, 22, 2.2, 23, 2.3, 24, 2.4, 25, 2.5, 26 or 2.6"); return UNSPECIFIED; } @@ -185,6 +191,8 @@ public static SpecifiedVersion findVersion(Version ver) { return _24; case 5: return _25; + case 6: + return _26; } } return UNSPECIFIED; diff --git a/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/ClassNode.java b/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/ClassNode.java index 00856fbc3a..8810d15eff 100644 --- a/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/ClassNode.java +++ b/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/ClassNode.java @@ -216,7 +216,7 @@ public void setHasInconsistentHierarchy(boolean b) { /** * Returns the ClassNode this ClassNode is redirecting to. */ - public ClassNode redirect(){ + public ClassNode redirect() { if (redirect==null) return this; return redirect.redirect(); } @@ -562,15 +562,10 @@ public void setModifiers(int modifiers) { redirect().modifiers = modifiers; } - // GRECLIPSE add -- overridable method for JDTClassNode - protected void ensurePropertiesInitialized() { - } - // GRECLIPSE end - public List getProperties() { final ClassNode r = redirect(); // GRECLIPSE add - r.ensurePropertiesInitialized(); + if (r != this) return r.getProperties(); // GRECLIPSE end if (r.properties == null) r.properties = new ArrayList (); @@ -642,24 +637,19 @@ public void addFieldFirst(FieldNode node) { r.fieldIndex.put(node.getName(), node); } - // GRECLIPSE add - public void addPropertyWithoutField(PropertyNode node) { - ClassNode r = redirect(); - node.setDeclaringClass(r); - if (r.properties == null) - r.properties = new ArrayList(); - r.properties.add(node); - } - // GRECLIPSE end - public void addProperty(PropertyNode node) { + // GRECLIPSE add + getProperties().add(node); + // GRECLIPSE end node.setDeclaringClass(redirect()); FieldNode field = node.getField(); addField(field); + /* GRECLIPSE edit final ClassNode r = redirect(); if (r.properties == null) r.properties = new ArrayList (); r.properties.add(node); + */ } public PropertyNode addProperty(String name, diff --git a/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/expr/LambdaExpression.java b/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/expr/LambdaExpression.java new file mode 100644 index 0000000000..eded47e137 --- /dev/null +++ b/base/org.codehaus.groovy24/src/org/codehaus/groovy/ast/expr/LambdaExpression.java @@ -0,0 +1,12 @@ +package org.codehaus.groovy.ast.expr; + +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.stmt.Statement; + +public class LambdaExpression extends ClosureExpression { + + public LambdaExpression(Parameter[] args, Statement code) { + super(args, code); + } + +} diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassNode.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassNode.java index d90c2b0a47..76b2dcc121 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassNode.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/ClassNode.java @@ -215,7 +215,7 @@ public void setHasInconsistentHierarchy(boolean b) { /** * Returns the ClassNode this ClassNode is redirecting to. */ - public ClassNode redirect(){ + public ClassNode redirect() { if (redirect==null) return this; return redirect.redirect(); } @@ -551,15 +551,10 @@ public void setModifiers(int modifiers) { redirect().modifiers = modifiers; } - // GRECLIPSE add -- overridable method for JDTClassNode - protected void ensurePropertiesInitialized() { - } - // GRECLIPSE end - public List getProperties() { final ClassNode r = redirect(); // GRECLIPSE add - r.ensurePropertiesInitialized(); + if (r != this) return r.getProperties(); // GRECLIPSE end if (r.properties == null) r.properties = new ArrayList(); @@ -636,24 +631,19 @@ public Map getFieldIndex() { return fieldIndex; } - // GRECLIPSE add - public void addPropertyWithoutField(PropertyNode node) { - ClassNode r = redirect(); - node.setDeclaringClass(r); - if (r.properties == null) - r.properties = new ArrayList<>(); - r.properties.add(node); - } - // GRECLIPSE end - public void addProperty(PropertyNode node) { + // GRECLIPSE add + getProperties().add(node); + // GRECLIPSE end node.setDeclaringClass(redirect()); FieldNode field = node.getField(); addField(field); + /* GRECLIPSE edit final ClassNode r = redirect(); if (r.properties == null) r.properties = new ArrayList(); r.properties.add(node); + */ } public PropertyNode addProperty(String name, diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/expr/LambdaExpression.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/expr/LambdaExpression.java new file mode 100644 index 0000000000..eded47e137 --- /dev/null +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/expr/LambdaExpression.java @@ -0,0 +1,12 @@ +package org.codehaus.groovy.ast.expr; + +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.stmt.Statement; + +public class LambdaExpression extends ClosureExpression { + + public LambdaExpression(Parameter[] args, Statement code) { + super(args, code); + } + +} diff --git a/base/org.codehaus.groovy26/.classpath b/base/org.codehaus.groovy26/.classpath new file mode 100644 index 0000000000..acaf6474b6 --- /dev/null +++ b/base/org.codehaus.groovy26/.classpath @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/org.codehaus.groovy26/.externalToolBuilders/Ant Builder.launch b/base/org.codehaus.groovy26/.externalToolBuilders/Ant Builder.launch new file mode 100644 index 0000000000..85953ebc4e --- /dev/null +++ b/base/org.codehaus.groovy26/.externalToolBuilders/Ant Builder.launch @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/base/org.codehaus.groovy26/.gitignore b/base/org.codehaus.groovy26/.gitignore new file mode 100644 index 0000000000..5db06e9fd1 --- /dev/null +++ b/base/org.codehaus.groovy26/.gitignore @@ -0,0 +1,4 @@ +/bin +/bin-trace +/eclipse-trace.jar +/groovy-eclipse.jar diff --git a/base/org.codehaus.groovy26/.project b/base/org.codehaus.groovy26/.project new file mode 100644 index 0000000000..e4985e0e81 --- /dev/null +++ b/base/org.codehaus.groovy26/.project @@ -0,0 +1,35 @@ + + + + org.codehaus.groovy26 + + + org.eclipse.ui.externaltools.ExternalToolBuilder + auto,full,incremental,clean, + + + LaunchConfigHandle + <project>/.externalToolBuilders/Ant Builder.launch + + + incclean + true + + + + + org.eclipse.jdt.core.javabuilder + + + org.eclipse.pde.ManifestBuilder + + + org.eclipse.pde.SchemaBuilder + + + + org.eclipse.jdt.groovy.core.groovyNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + + diff --git a/base/org.codehaus.groovy26/.settings/org.eclipse.core.runtime.prefs b/base/org.codehaus.groovy26/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..5a0ad22d2a --- /dev/null +++ b/base/org.codehaus.groovy26/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\n diff --git a/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.core.prefs b/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..2da0b8fec0 --- /dev/null +++ b/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,93 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=disabled +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=ignore +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=ignore +org.eclipse.jdt.core.compiler.problem.unusedLabel=ignore +org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 diff --git a/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.groovy.core.prefs b/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.groovy.core.prefs new file mode 100644 index 0000000000..ae98feaa79 --- /dev/null +++ b/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.groovy.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +groovy.compiler.level=24 diff --git a/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.ui.prefs b/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..25d4208b1c --- /dev/null +++ b/base/org.codehaus.groovy26/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=;javax;java;\#; +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 diff --git a/base/org.codehaus.groovy26/META-INF/MANIFEST.MF b/base/org.codehaus.groovy26/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..801fb288d4 --- /dev/null +++ b/base/org.codehaus.groovy26/META-INF/MANIFEST.MF @@ -0,0 +1,111 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: org.codehaus.groovy +Bundle-Name: Groovy +Bundle-Vendor: Codehaus.org +Bundle-Version: 2.6.0.qualifier +Bundle-ClassPath: eclipse-trace.jar, + groovy-eclipse.jar, + lib/ivy-2.4.0.jar, + lib/groovy-2.6.0-indy.jar, + lib/groovy-test-2.6.0-indy.jar +Export-Package: groovy.beans;version="2.6.0", + groovy.cli;version="2.6.0", + groovy.grape;version="2.6.0", + groovy.inspect;version="2.6.0", + groovy.io;version="2.6.0", + groovy.lang;version="2.6.0", + groovy.lang.groovydoc;version="2.6.0", + groovy.mock.interceptor;version="2.6.0", + groovy.security;version="2.6.0", + groovy.test;version="2.6.0", + groovy.time;version="2.6.0", + groovy.transform;version="2.6.0", + groovy.transform.builder;version="2.6.0", + groovy.transform.options;version="2.6.0", + groovy.transform.stc;version="2.6.0", + groovy.ui;version="2.6.0", + groovy.util;version="2.6.0", + groovy.util.logging;version="2.6.0", + groovy.xml;version="2.6.0", + groovyjarjarantlr;x-internal:=true, + groovyjarjarasm.asm;x-internal:=true, + org.apache.groovy.ast.tools;version="2.6.0", + org.apache.groovy.internal.metaclass;version="2.6.0", + org.apache.groovy.internal.util;version="2.6.0", + org.apache.groovy.io;version="2.6.0", + org.apache.groovy.lang.annotation;version="2.6.0", + org.apache.groovy.metaclass;version="2.6.0", + org.apache.groovy.parser;version="2.6.0", + org.apache.groovy.parser.antlr4;version="2.6.0", + org.apache.groovy.parser.antlr4.util;version="2.6.0", + org.apache.groovy.plugin;version="2.6.0", + org.apache.groovy.util;version="2.6.0", + org.apache.groovy.util.concurrentlinkedhashmap;version="2.6.0", + org.codehaus.greclipse;x-internal:=true, + org.codehaus.groovy;version="2.6.0", + org.codehaus.groovy.activator, + org.codehaus.groovy.antlr;version="2.6.0", + org.codehaus.groovy.antlr.java;version="2.6.0", + org.codehaus.groovy.antlr.parser;version="2.6.0", + org.codehaus.groovy.antlr.treewalker;version="2.6.0", + org.codehaus.groovy.ast;version="2.6.0", + org.codehaus.groovy.ast.builder;version="2.6.0", + org.codehaus.groovy.ast.decompiled;version="2.6.0", + org.codehaus.groovy.ast.expr;version="2.6.0", + org.codehaus.groovy.ast.stmt;version="2.6.0", + org.codehaus.groovy.ast.tools;version="2.6.0", + org.codehaus.groovy.classgen;version="2.6.0", + org.codehaus.groovy.classgen.asm;version="2.6.0", + org.codehaus.groovy.classgen.asm.indy;version="2.6.0", + org.codehaus.groovy.classgen.asm.indy.sc;version="2.6.0", + org.codehaus.groovy.classgen.asm.sc;version="2.6.0", + org.codehaus.groovy.classgen.asm.util;version="2.6.0", + org.codehaus.groovy.cli;version="2.6.0", + org.codehaus.groovy.control;version="2.6.0", + org.codehaus.groovy.control.customizers;version="2.6.0", + org.codehaus.groovy.control.customizers.builder;version="2.6.0", + org.codehaus.groovy.control.io;version="2.6.0", + org.codehaus.groovy.control.messages;version="2.6.0", + org.codehaus.groovy.eclipse, + org.codehaus.groovy.plugin;version="2.6.0", + org.codehaus.groovy.reflection;version="2.6.0", + org.codehaus.groovy.reflection.android;version="2.6.0", + org.codehaus.groovy.reflection.stdclasses;version="2.6.0", + org.codehaus.groovy.reflection.v7;version="2.6.0", + org.codehaus.groovy.runtime;version="2.6.0", + org.codehaus.groovy.runtime.callsite;version="2.6.0", + org.codehaus.groovy.runtime.dgmimpl;version="2.6.0", + org.codehaus.groovy.runtime.dgmimpl.arrays;version="2.6.0", + org.codehaus.groovy.runtime.m12n;version="2.6.0", + org.codehaus.groovy.runtime.memoize;version="2.6.0", + org.codehaus.groovy.runtime.metaclass;version="2.6.0", + org.codehaus.groovy.runtime.powerassert;version="2.6.0", + org.codehaus.groovy.runtime.typehandling;version="2.6.0", + org.codehaus.groovy.runtime.wrappers;version="2.6.0", + org.codehaus.groovy.syntax;version="2.6.0", + org.codehaus.groovy.tools;version="2.6.0", + org.codehaus.groovy.tools.ast;version="2.6.0", + org.codehaus.groovy.tools.gse;version="2.6.0", + org.codehaus.groovy.tools.javac;version="2.6.0", + org.codehaus.groovy.tools.shell;version="2.6.0", + org.codehaus.groovy.tools.shell.util;version="2.6.0", + org.codehaus.groovy.transform;version="2.6.0", + org.codehaus.groovy.transform.sc;version="2.6.0", + org.codehaus.groovy.transform.sc.transformers;version="2.6.0", + org.codehaus.groovy.transform.stc;version="2.6.0", + org.codehaus.groovy.transform.tailrec;version="2.6.0", + org.codehaus.groovy.transform.trait;version="2.6.0", + org.codehaus.groovy.util;version="2.6.0", + org.codehaus.groovy.vmplugin;version="2.6.0", + org.codehaus.groovy.vmplugin.v5;version="2.6.0", + org.codehaus.groovy.vmplugin.v6;version="2.6.0", + org.codehaus.groovy.vmplugin.v7;version="2.6.0", + org.codehaus.groovy.vmplugin.v8;version="2.6.0" +Require-Bundle: org.eclipse.core.runtime, + org.apache.ant;resolution:=optional, + org.junit;resolution:=optional +Bundle-ActivationPolicy: lazy +Bundle-Activator: org.codehaus.groovy.activator.GroovyActivator +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Eclipse-BundleShape: dir diff --git a/base/org.codehaus.groovy26/VERSION b/base/org.codehaus.groovy26/VERSION new file mode 100644 index 0000000000..11bc1910d3 --- /dev/null +++ b/base/org.codehaus.groovy26/VERSION @@ -0,0 +1 @@ +2018-02-26: GROOVY_2_6_0_SNAPSHOT diff --git a/base/org.codehaus.groovy26/about.html b/base/org.codehaus.groovy26/about.html new file mode 100644 index 0000000000..b849e061e3 --- /dev/null +++ b/base/org.codehaus.groovy26/about.html @@ -0,0 +1,49 @@ + + + +About + + +

About This Content

+ +

February 15, 2013

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ +

Third Party Content

+ +

groovy-2.6.0-indy.jar

+

groovy-test-2.6.0-indy.jar

+ + + +

ivy-2.4.0.jar

+ + + + + diff --git a/base/org.codehaus.groovy26/about_files/antlr2-license.txt b/base/org.codehaus.groovy26/about_files/antlr2-license.txt new file mode 100644 index 0000000000..3ce8ed2d56 --- /dev/null +++ b/base/org.codehaus.groovy26/about_files/antlr2-license.txt @@ -0,0 +1,23 @@ +ANTLR 2 License + +We reserve no legal rights to the ANTLR--it is fully in the public domain. An individual or company may do +whatever they wish with source code distributed with ANTLR or the code generated by ANTLR, including the +incorporation of ANTLR, or its output, into commerical software. + +We encourage users to develop software with ANTLR. However, we do ask that credit is given to us for +developing ANTLR. By "credit", we mean that if you use ANTLR or incorporate any source code into one of your +programs (commercial product, research project, or otherwise) that you acknowledge this fact somewhere in +the documentation, research report, etc... If you like ANTLR and have developed a nice tool with the output, +please mention that you developed it using ANTLR. In addition, we ask that the headers remain intact in our +source code. As long as these guidelines are kept, we expect to continue enhancing this system and expect to +make other tools available as they are completed. + +In countries where the Public Domain status of the work may not be valid, the author grants a copyright +licence to the general public to deal in the work without restriction and permission to sublicence derivates +under the terms of any (OSI approved) Open Source licence. + +The Python parser generator code under antlr/actions/python/ is covered by the 3-clause BSD licence (this +part is included in the binary JAR files); the run-time part under lib/python/ is covered by the GNU GPL, +version 3 or later (this part is not included in the binary JAR files). See [1] for the full details. + +https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=750643#80%22 diff --git a/base/org.codehaus.groovy26/about_files/antlr4-license.txt b/base/org.codehaus.groovy26/about_files/antlr4-license.txt new file mode 100644 index 0000000000..b7660bb6e1 --- /dev/null +++ b/base/org.codehaus.groovy26/about_files/antlr4-license.txt @@ -0,0 +1,28 @@ +ANTLR 4 License + +[The "BSD 3-clause license"] +Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/base/org.codehaus.groovy26/about_files/asl2-license.txt b/base/org.codehaus.groovy26/about_files/asl2-license.txt new file mode 100644 index 0000000000..939e067203 --- /dev/null +++ b/base/org.codehaus.groovy26/about_files/asl2-license.txt @@ -0,0 +1,286 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + +------------------------------------------------------------------------ + +ANTLR 2 License + +Antlr2 is released in the public domain. +See licenses/antlr2-license.txt for details. + +------------------------------------------------------------------------ + +ASM 4 License + +ASM 4 uses a 3-clause BSD license. For details, see licenses/asm-license.txt. + +------------------------------------------------------------------------ + +Hamcrest License (needed when using optional JUnit dependency) + +This product bundles the Hamcrest jar, which is available under a +BSD license. For details, see licenses/hamcrest-license. + +------------------------------------------------------------------------ + +JLine2 License (optional dependency used with groovysh) + +This product bundles the JLine2 jar, which is available under a +BSD License. For details, see licenses/jline2-license. + +------------------------------------------------------------------------ + +JLine2 Patch License + +The following class within this product: + + org.codehaus.groovy.tools.shell.completion.FileNameCompleter + +was derived from JLine 2.12, and the following patch: +https://github.com/jline/jline2/issues/90 +JLine2 is made available under a BSD License. +For details, see licenses/jline2-license. + +------------------------------------------------------------------------ + +JSR166y License (optionally used by the optional GPars dependency) + +This product bundles the jsr166y jar (containing works from +the JSR-166 EG, Doug Lea, and Jason T. Greene) made available in +the public domain. For details, see licenses/jsr166y-license. + +------------------------------------------------------------------------ + +JSR223 License + +The following classes within this product: + + org.codehaus.groovy.jsr223.GroovyCompiledScript + org.codehaus.groovy.jsr223.GroovyScriptEngineFactory + org.codehaus.groovy.jsr223.GroovyScriptEngineImpl + +were derived from reference implementation files developed by Sun in +collaboration with the Groovy community. The reference implementation +has a BSD-style license. Details can be found in: licenses/jsr223-license.txt + +------------------------------------------------------------------------ + +JUnit License (optional dependency when using Groovy for testing) + +This product bundles the JUnit jar, which is available under the +Eclipse Public License v1.0. For details, see licenses/junit-license. + +------------------------------------------------------------------------ + +normalize.css License + +The stylesheet.css file (originally normalize.css) is used by the +groovydoc and docgenerator components for groovy-jdk/gapi documentation. +It is made available under a MIT License. Details: licenses/normalize-stylesheet-license.txt + +------------------------------------------------------------------------ + +XStream License (optional dependency when serializing AST as XML) + +This product bundles the XStream jar, which is available under a +"3-clause BSD" license. For details, see licenses/xstream-license. diff --git a/base/org.codehaus.groovy26/about_files/asm-license.txt b/base/org.codehaus.groovy26/about_files/asm-license.txt new file mode 100644 index 0000000000..d4230a5f72 --- /dev/null +++ b/base/org.codehaus.groovy26/about_files/asm-license.txt @@ -0,0 +1,32 @@ +ASM License + +ASM: a very small and fast Java bytecode manipulation framework +Copyright (c) 2000-2011 INRIA, France Telecom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/base/org.codehaus.groovy26/build.antlr2x b/base/org.codehaus.groovy26/build.antlr2x new file mode 100644 index 0000000000..11b73c533c --- /dev/null +++ b/base/org.codehaus.groovy26/build.antlr2x @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/base/org.codehaus.groovy26/build.properties b/base/org.codehaus.groovy26/build.properties new file mode 100644 index 0000000000..15beab54e8 --- /dev/null +++ b/base/org.codehaus.groovy26/build.properties @@ -0,0 +1,23 @@ +compilerAdapter=org.codehaus.groovy.eclipse.ant.GroovyCompilerAdapter +compilerAdapter.useLog=true +compilerArg=-nowarn + +jars.compile.order=eclipse-trace.jar,groovy-eclipse.jar +jars.extra.classpath=platform:/plugin/org.junit/junit.jar +sourceFileExtensions=*.java,*.groovy + +output.eclipse-trace.jar = bin-trace/ +source.eclipse-trace.jar = src-trace/ +output.groovy-eclipse.jar = bin/ +source.groovy-eclipse.jar = src/ + +bin.includes = eclipse-trace.jar,\ + groovy-eclipse.jar,\ + META-INF/,\ + lib/,\ + conf/,\ + plugin_dsld_support/,\ + about_files/,\ + about.html +src.includes = about_files/,\ + about.html diff --git a/base/org.codehaus.groovy26/conf/groovy-starter.conf b/base/org.codehaus.groovy26/conf/groovy-starter.conf new file mode 100644 index 0000000000..3c700e29b4 --- /dev/null +++ b/base/org.codehaus.groovy26/conf/groovy-starter.conf @@ -0,0 +1,36 @@ +## 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. + +############################################################################## +## ## +## Groovy Classloading Configuration ## +## ## +############################################################################## + +## Note: do not add classes from java.lang here. No rt.jar and on some +## platforms no tools.jar +## +## See http://docs.groovy-lang.org/docs/latest/html/api/org/codehaus/groovy/tools/LoaderConfiguration.html +## for the file format + + # load required libraries + load !{groovy.home}/lib/*.jar + + # load user specific libraries + load !{user.home}/.groovy/lib/*.jar + + # tools.jar for ant tasks + load ${tools.jar} + diff --git a/base/org.codehaus.groovy26/lib/groovy-2.6.0-indy.jar b/base/org.codehaus.groovy26/lib/groovy-2.6.0-indy.jar new file mode 100644 index 0000000000..3f91230b1a Binary files /dev/null and b/base/org.codehaus.groovy26/lib/groovy-2.6.0-indy.jar differ diff --git a/base/org.codehaus.groovy26/lib/groovy-test-2.6.0-indy.jar b/base/org.codehaus.groovy26/lib/groovy-test-2.6.0-indy.jar new file mode 100644 index 0000000000..23aedfbccb Binary files /dev/null and b/base/org.codehaus.groovy26/lib/groovy-test-2.6.0-indy.jar differ diff --git a/base/org.codehaus.groovy26/lib/ivy-2.4.0-javadoc.jar b/base/org.codehaus.groovy26/lib/ivy-2.4.0-javadoc.jar new file mode 100644 index 0000000000..4ffb2f5107 Binary files /dev/null and b/base/org.codehaus.groovy26/lib/ivy-2.4.0-javadoc.jar differ diff --git a/base/org.codehaus.groovy26/lib/ivy-2.4.0-sources.jar b/base/org.codehaus.groovy26/lib/ivy-2.4.0-sources.jar new file mode 100644 index 0000000000..4d0433b899 Binary files /dev/null and b/base/org.codehaus.groovy26/lib/ivy-2.4.0-sources.jar differ diff --git a/base/org.codehaus.groovy26/lib/ivy-2.4.0.jar b/base/org.codehaus.groovy26/lib/ivy-2.4.0.jar new file mode 100644 index 0000000000..14ff88e260 Binary files /dev/null and b/base/org.codehaus.groovy26/lib/ivy-2.4.0.jar differ diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/dsld/basic_transforms.dsld b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/basic_transforms.dsld new file mode 100644 index 0000000000..368eb87d41 --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/basic_transforms.dsld @@ -0,0 +1,204 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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 dsld + +import groovy.transform.* +import org.codehaus.groovy.ast.* +import org.codehaus.groovy.ast.expr.* +import org.codehaus.groovy.runtime.MetaClassHelper + +// http://groovy-lang.org/metaprogramming.html#_available_ast_transformations + +//@groovy.lang.Category +contribute(currentType(annos: annotatedBy(Category))) { + provider = '{@link groovy.lang.Category Category} AST transform' + for (AnnotationNode node : annos) { + Expression value = node.getMember('value') + for (MethodNode meth : currentType.methods.findAll { + it.isStatic() && it.parameters.length >= 1 && it.parameters[0].name == '$this' && it.parameters[0].type == value.type + }) { + Map parms = params(meth) + parms = [self: parms['$this'], *: parms.subMap(parms.keySet().tail())] + method name: meth.name, type: meth.returnType, params: parms, isStatic: true + } + } +} + +//@groovy.lang.Delegate +contribute(currentType(fields: fields(annotatedBy(Delegate)))) { + provider = '{@link groovy.lang.Delegate Delegate} AST transform' + for (FieldNode field : fields) { + delegatesTo type: field.type + } +} + +//@groovy.lang.Grab, et al. + +//@groovy.lang.Lazy + +//@groovy.lang.Mixin +contribute(currentType(mixins: annotatedBy(Mixin))) { + provider = '{@link groovy.lang.Mixin Mixin} AST transform' + for (AnnotationNode mixin : mixins) { + Expression expr = mixin.getMember('value') + if (expr instanceof ClassExpression) { + delegatesTo type: expr.type + } else if (expr instanceof ListExpression) { + for (Expression e : expr.expressions) { + delegatesTo type: e.type + } + } + } +} + +//@groovy.lang.Newify +contribute(enclosingClass(annos: annotatedBy(Newify)) | enclosingField(annos: annotatedBy(Newify)) | enclosingMethod(annos: annotatedBy(Newify)) | assignedVariable(annos: annotatedBy(Newify))) { + provider = '{@link groovy.lang.Newify Newify} AST transform' + + def addNewifyMethods = { ClassNode type, String name = type.nameWithoutPackage -> + for (ConstructorNode ctor : type.declaredConstructors.findAll { !it.isPrivate() }) { + method name: name, params: params(ctor), type: type, declaringType: type, isStatic: true //TODO: doc = ctor javadoc + } + } + + for (AnnotationNode node : annos) { + // check for Ruby style (i.e. auto=true) + Boolean auto = node.getMember('auto')?.value + if (auto == null) { + auto = Newify.getMethod('auto').defaultValue + } + if (auto && currentNode instanceof PropertyExpression && + currentNode.objectExpression instanceof ClassExpression) { + addNewifyMethods(currentType, 'new'); + } + + // check for Python style (i.e. value=Type(s)) + Expression expr = node.getMember('value') + if (expr instanceof ClassExpression) { + addNewifyMethods(expr.type) + } else if (expr instanceof ListExpression) { + for (Expression e : expr.expressions) { + addNewifyMethods(e.type) + } + } + } +} + +//@groovy.lang.Singleton +contribute(currentType(annos: annotatedBy(Singleton))) { + provider = '{@link groovy.lang.Singleton Singleton} AST transform' + AnnotationNode node = annos[0] + String propertyName = node.getMember('property')?.text ?: 'instance' + String accessorName = 'get' + MetaClassHelper.capitalize(propertyName) + + method name: accessorName, type: currentType, isStatic: true + property name: propertyName, type: currentType, isStatic: true +} + +//------------------------------------------------------------------------------ + +//@groovy.transform.AnnotationCollector + +//@groovy.transform.ASTTest + +//@groovy.transform.AutoClone +contribute(currentType(annotatedBy(AutoClone))) { + provider = '{@link groovy.transform.AutoClone AutoClone} AST transform' + delegatesTo type: Cloneable +} + +//@groovy.transform.AutoExternalize +contribute(enclosingClass(annotatedBy(AutoExternalize))) { + provider = '{@link groovy.transform.AutoExternalize AutoExternalize} AST transform' + delegatesTo type: Externalizable +} + +//@groovy.transform.AutoImplement + +//@groovy.transform.BaseScript + +//@groovy.transform.Canonical, et al. + +//@groovy.transform.Field + +//@groovy.transform.IndexedProperty +contribute(currentType(fields: fields(annotatedBy(IndexedProperty)))) { + provider = '{@link groovy.transform.IndexedProperty IndexedProperty} AST transform' + for (FieldNode field : fields) { + String propertyName = MetaClassHelper.capitalize(field.name) + + // field must be an array or collection; get its component type + ClassNode type + if (type.isArray()) { + type = type.componentType + } else { + GenericsType[] gt = type.genericsTypes + if (gt && gt.length > 0) { + type = gt[0]?.type + } else { + type = ClassHelper.OBJECT_TYPE + } + } + + method name: 'get' + propertyName, type: type, params: [index: int] + method name: 'set' + propertyName, type: void, params: [index: int, element: type] + } +} + +//@groovy.transform.Immutable + +//@groovy.transform.InheritConstructors + +//@groovy.transform.MapConstructor + +//@groovy.transform.Memoized + +//@groovy.transform.NotYetImplemented + +//@groovy.transform.PackageScope + +//@groovy.transform.SelfType +contribute(bind(clazz: enclosingClass(annos: annotatedBy(SelfType)))) { + provider = '{@link groovy.transform.SelfType SelfType} AST transform' + if (org.codehaus.groovy.transform.trait.Traits.isTrait(clazz[0])) { + for (AnnotationNode node : annos) { + Expression expr = node.getMember('value') + if (expr instanceof ClassExpression) { + delegatesTo type: expr.type + } else if (expr instanceof ListExpression) { + for (Expression e : expr.expressions) { + delegatesTo type: e.type + } + } + } + } +} + +//@groovy.transform.Sortable +contribute(currentType(annotatedBy(Sortable))) { + provider = '{@link groovy.transform.Sortable Sortable} AST transform' + //delegatesTo type: "java.lang.Comparable<${currentType.name}>" + for (MethodNode node : currentType.methods.findAll { MethodNode mn -> + mn.isPublic() && mn.isStatic() && mn.name.startsWith('comparatorBy') && !mn.parameters + }) { + method name: node.name, type: node.returnType, isStatic: true + //, doc: "Returns a {@code Comparator} that compares the ${node.name.substring(12).uncapitalize()} properties of {@link ${currentType.name} ${currentType.nameWithoutPackage}} instances" + } +} + +//@groovy.transform.Synchronized, et al. + +//@groovy.transform.TailRecursive diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/dsld/beans_transforms.dsld b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/beans_transforms.dsld new file mode 100644 index 0000000000..18b5fb26a5 --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/beans_transforms.dsld @@ -0,0 +1,239 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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 dsld + +import java.beans.PropertyChangeListener +import java.beans.VetoableChangeListener + +import org.codehaus.groovy.ast.* +import org.codehaus.groovy.runtime.MetaClassHelper + +/** + * Hackergarten contribution + * + * @author Andres Almiray + * @author Tom Bujok (reficio.org) + */ +contribute(currentType(annotatedBy(groovy.beans.Bindable) | fields(annotatedBy(groovy.beans.Bindable)))) { + provider = '{@link groovy.beans.Bindable Bindable} AST transform' + + method name: 'addPropertyChangeListener', + type: void, + params: [propertyName: String, listener: PropertyChangeListener], + doc: '''\ + Add a PropertyChangeListener to the listener list. The listener is registered for all properties. + The same listener object may be added more than once, and will be called as many times as it is added. + If listener is null, no exception is thrown and no action is taken. + @param listener The PropertyChangeListener to be added + '''.stripIndent() + + method name: 'addPropertyChangeListener', + type: void, + params: [listener: PropertyChangeListener], + doc: '''\ + Add a PropertyChangeListener for a specific property. The listener will be invoked only when a call on + firePropertyChange names that specific property. The same listener object may be added more than once. + For each property, the listener will be invoked the number of times it was added for that property. + If propertyName or listener is null, no exception is thrown and no action is taken. + @param propertyName The name of the property to listen on. + @param listener The PropertyChangeListener to be added + '''.stripIndent() + + method name: 'removePropertyChangeListener', + type: void, + params: [propertyName: String, listener: PropertyChangeListener], + doc: '''\ + Remove a PropertyChangeListener for a specific property. If listener was added more than once to the same + event source for the specified property, it will be notified one less time after being removed. If + propertyName is null, no exception is thrown and no action is taken. If listener is null, or was never + added for the specified property, no exception is thrown and no action is taken. + @param propertyName The name of the property that was listened on. + @param listener The PropertyChangeListener to be removed + '''.stripIndent() + + method name: 'removePropertyChangeListener', + type: void, + params: [listener: PropertyChangeListener], + doc: '''\ + Remove a PropertyChangeListener from the listener list. This removes a PropertyChangeListener that was + registered for all properties. If listener was added more than once to the same event source, it will + be notified one less time after being removed. If listener is null, or was never added, no exception is + thrown and no action is taken. + @param listener The PropertyChangeListener to be removed + '''.stripIndent() + + method name: 'firePropertyChange', + type: void, + params: [propertyName: String, oldValue: Object, newValue: Object], + doc: '''\ + Report a bound property update to any registered listeners. + This is merely a convenience wrapper around the more general firePropertyChange method that takes PropertyChangeEvent value. + @param propertyName The programmatic name of the property that was changed. + @param oldValue The old value of the property. + @param newValue The new value of the property. + '''.stripIndent() + + method name: 'getPropertyChangeListeners', + type: 'java.beans.PropertyChangeListener[]', + doc: '''\ + Returns an array of all the listeners that were added to the PropertyChangeSupport object with addPropertyChangeListener(). + @return all of the PropertyChangeListeners added or an empty array if no listeners have been added + '''.stripIndent() + + method name: 'getPropertyChangeListeners', + type: 'java.beans.PropertyChangeListener[]', + params: [propertyName: String], + doc: '''\ + Returns an array of all the listeners which have been associated with the named property. + @param propertyName The name of the property being listened to + @return all of the PropertyChangeListeners associated with the named property. If no such + listeners have been added, or if propertyName is null, an empty array is returned. + '''.stripIndent() +} + +/** + * Hackergarten contribution + * + * @author Andres Almiray + * @author Tom Bujok (reficio.org) + */ +contribute(currentType(annotatedBy(groovy.beans.Vetoable) | fields(annotatedBy(groovy.beans.Vetoable)))) { + provider = '{@link groovy.beans.Vetoable Vetoable} AST transform' + + method name: 'addVetoableChangeListener', + type: void, + params: [propertyName: String, listener: VetoableChangeListener], + doc: '''\ + Add a VetoableChangeListener for a specific property. The listener will be invoked only when a call on + fireVetoableChange names that specific property. The same listener object may be added more than once. + For each property, the listener will be invoked the number of times it was added for that property. If + propertyName or listener is null, no exception is thrown and no action is taken. + @param propertyName The name of the property to listen on. + @param listener The VetoableChangeListener to be added + '''.stripIndent() + + method name: 'addVetoableChangeListener', + type: void, + params: [listener: VetoableChangeListener], + doc: '''\ + Add a VetoableListener to the listener list. The listener is registered for all properties. The same listener + object may beadded more than once, and will be called as many times as it is added. If listener is null, no + exception is thrown and no action is taken. + @param listener The VetoableChangeListener to be added + '''.stripIndent() + + method name: 'removeVetoableChangeListener', + type: void, + params: [propertyName: String, listener: VetoableChangeListener], + doc: '''\ + Remove a VetoableChangeListener for a specific property. If listener was added more than once to the same event + source for the specified property, it will be notified one less time after being removed. If propertyName is null, + no exception is thrown and no action is taken. If listener is null, or was never added for the specified property, + no exception is thrown and no action is taken. + @param propertyName The name of the property that was listened on. + @param listener The VetoableChangeListener to be removed + '''.stripIndent() + + method name: 'removeVetoableChangeListener', + type: void, + params: [listener: VetoableChangeListener], + doc: '''\ + Remove a VetoableChangeListener from the listener list. This removes a VetoableChangeListener that was registered + for all properties. If listener was added more than once to the same event source, it will be notified one less + time after being removed. If listener is null, or was never added, no exception is thrown and no action is taken. + @param listener The VetoableChangeListener to be removed + '''.stripIndent() + + method name: 'fireVetoableChange', + type: void, + params: [propertyName: String, oldValue: Object, newValue: Object], + doc: '''\ + Report a vetoable property update to any registered listeners. If anyone vetos the change, then fire a new event + reverting everyone to the old value and then rethrow the PropertyVetoException. + No event is fired if old and new are equal and non-null. + @param propertyName The programmatic name of the property that is about to change.. + @param oldValue The old value of the property. + @param newValue The new value of the property. + '''.stripIndent() + + method name: 'getVetoableChangeListeners', + type: 'java.beans.VetoableChangeListener[]', + doc: '''\ + Returns the list of VetoableChangeListeners. If named vetoable change listeners were added, then VetoableChangeListenerProxy + wrappers will returned. + @return List of VetoableChangeListeners and VetoableChangeListenerProxys if named property change listeners were added. + '''.stripIndent() + + method name: 'getVetoableChangeListeners', + type: 'java.beans.VetoableChangeListener[]', + params: [propertyName: String], + doc: '''\ + Returns an array of all the listeners which have been associated with the named property. + @param propertyName The name of the property being listened to + @return all the VetoableChangeListeners associated with the named property. If no such listeners have been added, or if + propertyName is null, an empty array is returned. + '''.stripIndent() +} + +/** + * Hackergarten contribution + * + * @author Andres Almiray + * @author Lukasz Pielak + * @author Max Rydahl Andersen + */ +contribute(currentType(fields : fields(annotatedBy(groovy.beans.ListenerList)))) { + provider = '{@link groovy.beans.ListenerList ListenerList} AST transform' + + for (FieldNode field : fields) { + def propertyName = MetaClassHelper.capitalize(field.name) + def type = findElementType(field) + + def shortType = getShortName(type.name) + method name: 'add' + shortType, type: 'void', params: [listener: type], doc: 'Add ' + shortType + ' listener' + method name: 'remove' + shortType, type: 'void', params: [listener: type], doc: 'Remove ' + shortType + ' listener' + + // TODO: array types does not work, result in completions for plain Object + method name: 'get' + shortType + 's', type: type.name + '[]', doc: 'Get ' + shortType + ' listeners' + + type.methods.each { MethodNode m -> + method name: 'fire' + MetaClassHelper.capitalize(m.name), type: void, params: m.parameters.collectEntries { + Collections.singletonMap(it.name, it.type) + }, doc: 'fire the ' + m.name + ' event' + } + } +} + +private String getShortName(String className) { + int i = className.lastIndexOf('.') + if (i > -1) { + className = className.substring(i + 1, className.length()) + } + className +} + +private ClassNode findElementType(FieldNode field) { + ClassNode type = field.type + if (type.isArray()) { + return type.componentType + } + GenericsType[] gt = type.genericsTypes + if (gt && gt.length > 0) { + gt[0]?.type + } else { + ClassHelper.OBJECT_TYPE + } +} diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/dsld/builder_transform.dsld b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/builder_transform.dsld new file mode 100644 index 0000000000..8331608b82 --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/builder_transform.dsld @@ -0,0 +1,39 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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 dsld + +import org.codehaus.groovy.ast.* +import org.codehaus.groovy.ast.expr.* + +contribute(currentType(annos: annotatedBy(groovy.transform.builder.Builder))) { //TODO: ctor, meth + provider = '{@link groovy.transform.builder.Builder Builder} AST transform' + + AnnotationNode annotation = annos[0] + String strategy = annotation.getMember('builderStrategy')?.type?.name ?: + groovy.transform.builder.Builder.getMethod('builderStrategy').defaultValue.name + + switch (strategy) { + case 'groovy.transform.builder.DefaultStrategy': + String builderCall = annotation.getMember('builderMethodName')?.value ?: 'builder' + String builderType = annotation.getMember('builderClassName')?.value ?: (currentType.nameWithoutPackage + 'Builder') + + method name: builderCall, type: (currentType.name + '$' + builderType), isStatic: true + break + + default: + log 'Unsupported builder strategy: ' + strategy + } +} diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/dsld/logging_transformns.dsld b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/logging_transformns.dsld new file mode 100644 index 0000000000..08c650fb0a --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/logging_transformns.dsld @@ -0,0 +1,47 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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 dsld + +[ + [ + annotation: 'groovy.util.logging.Log', + loggerType: 'java.util.logging.Logger' + ], + [ + annotation: 'groovy.util.logging.Commons', + loggerType: 'org.apache.commons.logging.Log' + ], + [ + annotation: 'groovy.util.logging.Log4j', + loggerType: 'org.apache.log4j.Logger' + ], + [ + annotation: 'groovy.util.logging.Log4j2', + loggerType: 'org.apache.logging.log4j.core.Logger' + ], + [ + annotation: 'groovy.util.logging.Slf4j', + loggerType: 'org.slf4j.Logger' + ] +].each { data -> + contribute(currentType(annotations: annotatedBy(data.annotation))) { + provider = 'Logging AST transform' + for (annotationNode in annotations) { + def loggerName = annotationNode.getMember('value') ?: 'log' + property name: loggerName, type: data.loggerType, isStatic: true // also final, private, transient + } + } +} diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/dsld/meta_script.dsld b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/meta_script.dsld new file mode 100644 index 0000000000..f175f0297d --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/meta_script.dsld @@ -0,0 +1,226 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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 dsld + +import org.codehaus.groovy.ast.* +import org.codehaus.groovy.ast.expr.* +import org.codehaus.groovy.eclipse.dsl.script.PointcutFactory + +/** + * This is the meta DSL descriptor for *.dsld files. + * + * @author Andrew Eisenberg + */ + +// first thing to do is to check that versions are correct +// for this script to be evaluated any further, all conditions must be met +// also supports grailsTooling and sts, which are synonyms and correspond to the STS version eg- 2.6.0 +// we can add more version checking on request +supportsVersion(groovy: '1.7.10', groovyEclipse: '2.5.1') + +interface IPointcut { + /** + * Accepts a contribution group to this Pointcut. What this means + * is that whenever this pointcut evaluates to a match, then the + * contribution group is evaluated with the bindings that have + * been generated from the pointcut match + * + * @param group the contribution group to accept + * + * @deprecated Use contribution() instead + */ + @Deprecated + def accept(Closure group) +} + +// You can store shared pointcuts in a variable +// This particular pointcut matches all join points that are inside of a +// groovy project and inside a file with a *.dsld extension and are scripts +def dsldFile = nature('org.eclipse.jdt.groovy.core.groovyNature') & fileExtension('dsld') & isScript() + +// You can also create a closure around pointcuts and assign them to +// a variable. Note that when using these variables, you must include parens. +def insideContribution = { (enclosingCallName('accept') | enclosingCallName('contribute')) & inClosure() } + +// Ensure that the 'accept' method is available for all closures and variables that correspond to pointcuts +(dsldFile & (~insideContribution()) & (~currentType(subType(Script)))).accept { delegatesTo IPointcut } + +// Store all bound names inside of the wormhole so that they can be available later +(dsldFile & enclosingCallDeclaringType(subType(Script)) & (~enclosingCallName('supportsVersion')) & (~inClosure()) & bind(var: currentIdentifier())).accept { + if (enclosingNode instanceof MapEntryExpression && var.contains(((MapEntryExpression) enclosingNode).keyExpression)) { + def bindings = wormhole.bindings + if (!bindings) { + bindings = [] + wormhole.bindings = bindings + } + var.each { bindings << it.text } + } +} + +// Define the kinds of pointcuts +// note the different ways of calling the two composite pointcuts +// Also, be careful to use parens around negation '~' since operator precedence may make the '~' apply to the call to 'accept' +(dsldFile & (~insideContribution()) & currentType(subType(Script)) & (~enclosingCallName('registerPointcut')) & isThisType()).accept { + provider = 'the meta-DSLD script' + + // in here, we can list all pointcuts explicitly, or we can access the internal PointcutFactory object + // A little bit naughty, but this is the easiest way to maintain consistency with all possible pointcuts + // ...the PointcutFactory class is a secret class that is declared by groovy-eclipse + // Yes, you can use many Eclipse classes here. The editor won't like them, so they must be fully qualified + // and you cannot import them or use them as types for variables + + Map pointcutNames = PointcutFactory.docRegistry + pointcutNames.each { pointcutName, pointcutDoc -> + method name: pointcutName, type: IPointcut, params: [pointcutArg: Object], doc: pointcutDoc, isDeprecated: PointcutFactory.deprecatedRegistry.contains(pointcutName) + } + + method name: 'contribute', + type: void, + params: [pointcut: IPointcut, contributionBlock: Closure], + doc: 'Associates a pointcut expression with a contribution block.' + + method name: 'registerPointcut', + type: void, + params: [name: String, pointcutBody: Closure], + doc: 'Registers a custom pointcut. This pointcut is only available from within the current script. You must specify a name for the pointcut as well as a closure that evaluates whether or not there is a match.' + + method name: 'supportsVersion', + type: void, + params: [versionConstraints: Map], + doc: '''\ + Specifies that this script is only active when the specified version constraints are met. Currently, the + following constraint keys are available: {@code groovy}, {@code groovyEclipse}, and {@code grailsTooling}. +

For example, to indicate that a script requires both Groovy 1.7.10 or later and Groovy-Eclipse 2.1.3 or later: + supportsVersion(groovy: '1.7.10', groovyEclipse: '2.1.3') + '''.stripIndent() + + method name: 'log', type: void, params: [message: String], doc: 'Logs a message to the Groovy Event Console.' +} + +// Here, specify everything that can be used inside of an accept block (also called a Contribution Group) +(dsldFile & insideContribution()).accept { + provider = 'the meta-DSLD script' + + property name : 'provider', type: String, doc: 'Specifies a Provider for the current contribution. This provider is displayed during content assist and in other places to give a quick hint as to where this contribution elementcomes from.' + + method name: 'property', + namedParams: [ + name: String, + type: Object, + isStatic: boolean + ], + optionalParams: [ + isDeprecated: Boolean, + declaringType: Object, + provider: String, + doc: String + ], + doc: 'Specifies a new property contribution. name is mandatory, but all other parameters are optional.' + + method name: 'method', + namedParams: [ + name: String, + type: Object, + params: Map + ], + optionalParams: [ + optionalParams: Map, + namedParams: Map, + isStatic: Boolean, + isDeprecated: Boolean, + declaringType: Object, + provider: String, + doc: String + ], + doc: '''\ + Specifies a new property contribution. name is mandatory, but all other parameters are optional. + Use {@code namedParams} to specify parameters that should be named, and use {@code optionalParams} to + specify parameters that will not appear in content assist. + '''.stripIndent() + + method name: 'params', + params: [node: MethodNode], + type: 'java.util.Map', + doc: '''\ + Returns the parameter names and types of the given method node. Parameter name resolution is performed + using the enclosing project's classpath. + '''.stripIndent() + + method name: 'delegatesToCategory', + params: [type: Object], + isDeprecated: true, + doc : '''\ + Specifies that the currentType delegates to the given type. The currentType is the type being analyzed. + And the given type is specified as a parameter (either a String, Class, AnnotatedNode). All methods of + the given type will be available from the currentType. + '''.stripIndent() + + method name: 'delegatesToUseNamedArgs', + params: [type: Object], + isDeprecated: true, + doc: '''\ + Specifies that the currentType delegates to the given type. The currentType is the type being analyzed. + And the given type is specified as a parameter (either a String, Class, or ClassNode). All fields and + methods of the given type will be available from the currentType. +

Named arguments will be used for all methods. + '''.stripIndent() + + method name : 'delegatesTo', + namedParams: [type: Object], + optionalParams: [useNamed: Boolean, except: 'java.util.List', asCategory: Boolean], + doc: '''\ + Specifies that the currentType delegates to the given type. The currentType is the type being analyzed. + And the given type is specified as a parameter (either a String, Class, or ClassNode). All fields and + methods of the given type will be available from the currentType. +

This variant allows you to specify options through the optional arguments. + @param type (required) type to delegate to + @param except (optional) list of method names to exclude from the delegation + @param useNamed (optional) boolean and if true named arguments are used + @param asCategory (optional) boolean and if true, this delegation is treated as a category and the first + parameter of each method is used to see if it is applicable for the current caller + '''.stripIndent() + + method name: 'setDelegateType', + type: void, + params: [newDelegateType: String], + doc: '''\ + Sets the delegate type inside a closure. This is different from {@code delegatesTo} in that the receiving + type is changed in the current scope. In {@code delegatesTo}, the scope is not changed -- rather, the + methods of the delegate are added to the target type's list of recognized methods. + '''.stripIndent() + + method name: 'log', type: void, params: [message: String], doc: 'Logs a message to the Groovy Event Console.' + + property name: 'currentType', type: ClassNode, doc: 'This is the declaring type of the current expression being evaluated.' + + property name: 'currentNode', type: ASTNode, doc: 'This is the ASTNode being evaluated.' + + property name: 'enclosingNode', type: ASTNode, doc: 'This is the ASTNode enclosing the ASTNode being evaluated.' + + property name: 'wormhole', type: Map, doc: 'Use the wormhole to stuff in values calculated in one contribution group to make it available later in another contribution group.' + + // extract all bindings from the wormhole and add them as contributions + for (binding in wormhole.bindings) { + property name: binding, type: Collection, doc: 'Binding created from pointcut' + } +} + +// Adds the contributions for inside a 'registerPointcut' call. User-registered pointcuts are not fully fleshed out yet, so best not to use them yet. +(dsldFile & inClosure() & enclosingCallName('registerPointcut')).accept { + // a little naughty again...reference an internal Groovy-Eclipse type; this may be removed in future versions + property name: 'currentScope', type: org.eclipse.jdt.groovy.search.VariableScope + property name: 'currenType', type: ClassNode +} diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/dsld/swing_builder.dsld b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/swing_builder.dsld new file mode 100644 index 0000000000..faf4608aa4 --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/dsld/swing_builder.dsld @@ -0,0 +1,150 @@ +/* + * Copyright 2009-2018 the original author or authors. + * + * Licensed 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 dsld + +import java.awt.* +import javax.swing.* + +/** + * This is the DSLD for SwingBuilder + * + * @author Andrew Eisenberg + */ +contribute(currentType('groovy.swing.SwingBuilder')) { + provider = '{@link groovy.swing.SwingBuilder SwingBuilder} DSL' + + method name: 'action', + type: Action, + useNamedArgs: true, + params: [ + name: String, + shortDescription: String, + smallIcon: String, + accelerator: String, + closure: Closure + ], + doc: '''\ + Actions are the lifeblood of a full fledged swing application. Other sources can expound on their usefullness. +

see http://groovy.codehaus.org/SwingBuilder.action + '''.stripIndent() + + method name: 'frame', + type: JFrame, + useNamedArgs: true, + params: [ + contentPane: Container, + defaultCloseOperation: int, + title: String, + glassPane: Component, + layeredPane: JLayeredPane, + contentPane: Container + ], + doc: '''\ + Generates a JFrame. +

see http://groovy.codehaus.org/SwingBuilder.frame + '''.stripIndent() + + def add = { String name, type -> + String doc = "See SwingBuilder.${name}" + method name: name, type: type, doc: doc + } + + // and so on... + add('actions', List) + add('bean', Void) + add('bind', 'org.codehaus.groovy.binding.FullBinding') + add('borderLayout', BorderLayout) + add('boundedRangeModel', DefaultBoundedRangeModel) + add('box', Box) + add('boxLayout', BoxLayout) + add('button', JButton) + add('buttonGroup', ButtonGroup) + add('cardLayout', CardLayout) + add('checkBox', JCheckBox) + add('checkBoxMenuItem', JCheckBoxMenuItem) + add('closureColumn', 'groovy.model.DefaultTableColumn') + add('compoundBorder', border.CompoundBorder) + add('colorChooser', JColorChooser) + add('comboBox', JComboBox) + add('container', Container) + add('desktopPane', JDesktopPane) + add('dialog', JDialog) + add('editorPane', JEditorPane) + add('emptyBorder', border.EmptyBorder) + add('etchedBorder', border.EtchedBorder) + add('fileChooser', JFileChooser) + add('flowLayout', FlowLayout) + add('formattedTextField', JFormattedTextField) + add('gbc', GridBagConstraints) + add('gridBagConstraints', GridBagConstraints) + add('glue', Component) + add('gridBagConstraints', GridBagConstraints) + add('gridBagLayout', GridBagLayout) + add('gridLayout', GridLayout) + add('hbox', Box) + add('hglue', Component) + add('hstrut', Component) + add('internalFrame', JInternalFrame) + add('label', JLabel) + add('layeredPane', JLayeredPane) + add('list', JList) + add('loweredBevelBorder', border.BevelBorder) + add('loweredEtchedBorder', border.EtchedBorder) + add('map', Map) + add('matteBorder', border.MatteBorder) + add('menu', JMenu) + add('menuBar', JMenuBar) + add('menuItem', JMenuItem) + add('optionPane', JOptionPane) + add('overlayLayout', OverlayLayout) + add('panel', JPanel) + add('passwordField', JPasswordField) + add('popupMenum', JPopupMenu) + add('progressBar', JProgressBar) + add('propertyColumn', table.TableColumn) + add('radioButton', JRadioButton) + add('radioButtonMenuItem', JRadioButtonMenuItem) + add('rigidArea', Component) + add('scrollBar', JScrollBar) + add('scrollPane', JScrollPane) + add('separator', JSeparator) + add('slider', JSlider) + add('spinner', JSpinner) + add('spinnerDateModel', SpinnerDateModel) + add('spinnerListModel', SpinnerListModel) + add('spinnerNumberModel', SpinnerNumberModel) + add('splitPane', JSplitPane) + add('springLayout', SpringLayout) + add('tabbedPane', JTabbedPane) + add('table', JTable) + add('tableColumn', table.TableColumn) + add('tableLayout', 'groovy.swing.impl.TableLayoutRow') + add('tableModel', table.TableModel) + add('td', 'groovy.swing.impl.TableLayoutCell') + add('textArea', JTextArea) + add('textField', JTextField) + add('textPane', JTextPane) + add('toggleButton', JToggleButton) + add('toolbar', JToolBar) + add('tr', 'groovy.swing.impl.TableLayoutRow') + add('tree', JTree) + add('vbox', Box) + add('vglue', Component) + add('viewport', JViewport) + add('vstrut', Component) + add('widget', Component) + add('window', JWindow) +} diff --git a/base/org.codehaus.groovy26/plugin_dsld_support/readme.txt b/base/org.codehaus.groovy26/plugin_dsld_support/readme.txt new file mode 100644 index 0000000000..f044a53aa1 --- /dev/null +++ b/base/org.codehaus.groovy26/plugin_dsld_support/readme.txt @@ -0,0 +1,5 @@ +This folder contains some standard DSL descriptor files that are +specific to this version of Groovy-Eclipse and are applicable to +all projects in your workspace. In general, you should not edit +the files here. If you want to add custom DSLDs for your entire +workspace, then you should add them to ~/.groovy/greclipse/dsld. diff --git a/base/org.codehaus.groovy26/pom.xml b/base/org.codehaus.groovy26/pom.xml new file mode 100644 index 0000000000..31525764fa --- /dev/null +++ b/base/org.codehaus.groovy26/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + + ../../pom.xml + org.codehaus.groovy.eclipse + org.codehaus.groovy.eclipse.parent + 3.0.0-SNAPSHOT + + org.codehaus.groovy.eclipse + org.codehaus.groovy + 2.6.0-SNAPSHOT + eclipse-plugin + + + + + maven-clean-plugin + 2.5 + + + + ${basedir} + + eclipse-trace.jar + groovy-eclipse.jar + + + + + + + + \ No newline at end of file diff --git a/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/DefaultGroovyLogger.java b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/DefaultGroovyLogger.java new file mode 100644 index 0000000000..beb6fd5aca --- /dev/null +++ b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/DefaultGroovyLogger.java @@ -0,0 +1,34 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.eclipse; + +import java.util.Date; + +/** + * Default logger logs to sysout and includes a timestamp + * + * @author Andrew Eisenberg + */ +public class DefaultGroovyLogger implements IGroovyLogger { + + public void log(TraceCategory category, String message) { + System.out.println(category.label + " : " + new Date() + " : " + message); + } + + public boolean isCategoryEnabled(TraceCategory category) { + return true; + } +} diff --git a/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/GroovyLogManager.java b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/GroovyLogManager.java new file mode 100644 index 0000000000..724c46b5a4 --- /dev/null +++ b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/GroovyLogManager.java @@ -0,0 +1,171 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.eclipse; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * Manages the current {@link IGroovyLogger} instance. + * + * NOTE: This class is a singleton. + */ +// Some code here borrowed from org.eclipse.ajdt.core.AJLog under EPL license +// See http://www.eclipse.org/legal/epl-v10.html +public class GroovyLogManager { + public static final GroovyLogManager manager = new GroovyLogManager(); + + private GroovyLogManager() { + } + + private IGroovyLogger[] loggers; + + // only use default logger if no others are registered + private final IGroovyLogger defaultLogger = new DefaultGroovyLogger(); + + private final Map timers = new HashMap<>(); + + private boolean useDefaultLogger; + + /** + * @return true if logger was added; false if not if not added -- + * then this means the exact logger is already in the list + */ + public boolean addLogger(IGroovyLogger logger) { + int newIndex; + if (loggers == null) { + loggers = new IGroovyLogger[1]; + newIndex = 0; + } else { + // check to see if already there + for (IGroovyLogger igl : loggers) { + if (igl == logger) { + return false; + } + } + newIndex = loggers.length; + IGroovyLogger[] newLoggers = new IGroovyLogger[newIndex + 1]; + System.arraycopy(loggers, 0, newLoggers, 0, newIndex); + loggers = newLoggers; + } + loggers[newIndex] = logger; + return true; + } + + /** + * Removes the logger from the logger list. + * + * @return true iff found and removed; false iff nothing found + */ + public boolean removeLogger(IGroovyLogger logger) { + if (logger != null && loggers != null) { + int foundIndex = -1; + for (int i = 0, n = loggers.length; i < n; i += 1) { + if (loggers[i] == logger) { + foundIndex = i; + } + } + if (foundIndex >= 0) { + if (loggers.length > 1) { + IGroovyLogger[] newLoggers = new IGroovyLogger[loggers.length - 1]; + if (foundIndex > 0) { + System.arraycopy(loggers, 0, newLoggers, 0, foundIndex); + } + System.arraycopy(loggers, foundIndex + 1, newLoggers, foundIndex, loggers.length - foundIndex - 1); + loggers = newLoggers; + } else { + loggers = null; + } + return true; + } + } + // not found + return false; + } + + public void logStart(String event) { + timers.put(event, System.currentTimeMillis()); + } + + public void logEnd(String event, TraceCategory category) { + logEnd(event, category, null); + } + + public void logEnd(String event, TraceCategory category, String message) { + Long then = timers.get(event); + if (then != null) { + if (hasLoggers()) { + long now = System.currentTimeMillis(); + long elapsed = now - then.longValue(); + if (message != null && !message.isEmpty()) { + log(category, "Event complete: " + elapsed + "ms: " + event + " (" + message + ")"); + } else { + log(category, "Event complete: " + elapsed + "ms: " + event); + } + } + timers.remove(event); + } + } + + public void log(String message) { + log(TraceCategory.DEFAULT, message); + } + + public void log(TraceCategory category, String message) { + if (!hasLoggers()) { + return; + } + + if (loggers != null) { + for (IGroovyLogger logger : loggers) { + if (logger.isCategoryEnabled(category)) { + logger.log(category, message); + } + } + } + + if (useDefaultLogger) { + defaultLogger.log(category, message); + } + } + + /** + * Call this method to check if any loggers are currently + * installed. Doing so can help avoid creating costly + * logging messages unless required. + */ + public boolean hasLoggers() { + return loggers != null || useDefaultLogger; + } + + /** + * Enables/disables the default logger (printing to sysout). + */ + public void setUseDefaultLogger(boolean useDefaultLogger) { + this.useDefaultLogger = useDefaultLogger; + } + + public void logException(TraceCategory cat, Throwable t) { + if (hasLoggers()) { + // only log if logger is available, otherwise, ignore + StringWriter writer = new StringWriter(); + t.printStackTrace(new PrintWriter(writer)); + log(cat, "Exception caught.\n" + writer.getBuffer()); + } + } +} diff --git a/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/IGroovyLogger.java b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/IGroovyLogger.java new file mode 100644 index 0000000000..75f56d8e3a --- /dev/null +++ b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/IGroovyLogger.java @@ -0,0 +1,28 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.eclipse; + +/** + * A means to send log messages to a logger. + * + * @author Andrew Eisenberg + */ +public interface IGroovyLogger { + + void log(TraceCategory category, String message); + + boolean isCategoryEnabled(TraceCategory category); +} diff --git a/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/LoggerTest.java b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/LoggerTest.java new file mode 100644 index 0000000000..77d9b495e7 --- /dev/null +++ b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/LoggerTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.eclipse; + +import junit.framework.TestCase; + +/** + * Simple test to make sure that logs are added and removed properly. + * + * @author Andrew Eisenberg + */ +public class LoggerTest extends TestCase { + + public void testLoggers() throws Exception { + DefaultGroovyLogger l1 = new DefaultGroovyLogger(); + DefaultGroovyLogger l2 = new DefaultGroovyLogger(); + DefaultGroovyLogger l3 = new DefaultGroovyLogger(); + DefaultGroovyLogger l4 = new DefaultGroovyLogger(); + DefaultGroovyLogger l5 = new DefaultGroovyLogger(); + + assertTrue(GroovyLogManager.manager.addLogger(l1)); + assertFalse(GroovyLogManager.manager.addLogger(l1)); + assertTrue(GroovyLogManager.manager.addLogger(l2)); + assertFalse(GroovyLogManager.manager.addLogger(l2)); + assertTrue(GroovyLogManager.manager.addLogger(l3)); + assertFalse(GroovyLogManager.manager.addLogger(l3)); + assertTrue(GroovyLogManager.manager.addLogger(l4)); + assertFalse(GroovyLogManager.manager.addLogger(l4)); + assertTrue(GroovyLogManager.manager.addLogger(l5)); + assertFalse(GroovyLogManager.manager.addLogger(l5)); + + assertTrue(GroovyLogManager.manager.removeLogger(l1)); + assertFalse(GroovyLogManager.manager.removeLogger(l1)); + assertTrue(GroovyLogManager.manager.removeLogger(l2)); + assertFalse(GroovyLogManager.manager.removeLogger(l2)); + assertTrue(GroovyLogManager.manager.removeLogger(l3)); + assertFalse(GroovyLogManager.manager.removeLogger(l3)); + assertTrue(GroovyLogManager.manager.removeLogger(l4)); + assertFalse(GroovyLogManager.manager.removeLogger(l4)); + assertTrue(GroovyLogManager.manager.removeLogger(l5)); + assertFalse(GroovyLogManager.manager.removeLogger(l5)); + + // now reverse order of removal + assertTrue(GroovyLogManager.manager.addLogger(l1)); + assertFalse(GroovyLogManager.manager.addLogger(l1)); + assertTrue(GroovyLogManager.manager.addLogger(l2)); + assertFalse(GroovyLogManager.manager.addLogger(l2)); + assertTrue(GroovyLogManager.manager.addLogger(l3)); + assertFalse(GroovyLogManager.manager.addLogger(l3)); + assertTrue(GroovyLogManager.manager.addLogger(l4)); + assertFalse(GroovyLogManager.manager.addLogger(l4)); + assertTrue(GroovyLogManager.manager.addLogger(l5)); + assertFalse(GroovyLogManager.manager.addLogger(l5)); + + assertTrue(GroovyLogManager.manager.removeLogger(l5)); + assertFalse(GroovyLogManager.manager.removeLogger(l5)); + assertTrue(GroovyLogManager.manager.removeLogger(l4)); + assertFalse(GroovyLogManager.manager.removeLogger(l4)); + assertTrue(GroovyLogManager.manager.removeLogger(l3)); + assertFalse(GroovyLogManager.manager.removeLogger(l3)); + assertTrue(GroovyLogManager.manager.removeLogger(l2)); + assertFalse(GroovyLogManager.manager.removeLogger(l2)); + assertTrue(GroovyLogManager.manager.removeLogger(l1)); + assertFalse(GroovyLogManager.manager.removeLogger(l1)); + + // now removal from middle + assertTrue(GroovyLogManager.manager.addLogger(l1)); + assertFalse(GroovyLogManager.manager.addLogger(l1)); + assertTrue(GroovyLogManager.manager.addLogger(l2)); + assertFalse(GroovyLogManager.manager.addLogger(l2)); + assertTrue(GroovyLogManager.manager.addLogger(l3)); + assertFalse(GroovyLogManager.manager.addLogger(l3)); + assertTrue(GroovyLogManager.manager.addLogger(l4)); + assertFalse(GroovyLogManager.manager.addLogger(l4)); + assertTrue(GroovyLogManager.manager.addLogger(l5)); + assertFalse(GroovyLogManager.manager.addLogger(l5)); + + assertTrue(GroovyLogManager.manager.removeLogger(l3)); + assertFalse(GroovyLogManager.manager.removeLogger(l3)); + assertTrue(GroovyLogManager.manager.removeLogger(l2)); + assertFalse(GroovyLogManager.manager.removeLogger(l2)); + assertTrue(GroovyLogManager.manager.removeLogger(l4)); + assertFalse(GroovyLogManager.manager.removeLogger(l4)); + assertTrue(GroovyLogManager.manager.removeLogger(l5)); + assertFalse(GroovyLogManager.manager.removeLogger(l5)); + assertTrue(GroovyLogManager.manager.removeLogger(l1)); + assertFalse(GroovyLogManager.manager.removeLogger(l1)); + } +} diff --git a/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/TraceCategory.java b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/TraceCategory.java new file mode 100644 index 0000000000..61307ac2bb --- /dev/null +++ b/base/org.codehaus.groovy26/src-trace/org/codehaus/groovy/eclipse/TraceCategory.java @@ -0,0 +1,84 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.eclipse; + +import java.util.Arrays; + +/** + * @author Andrew Eisenberg + */ +public enum TraceCategory { + + DEFAULT("_"), + COMPILER("Compiler"), + CLASSPATH("Classpath"), + + DSL("DSL"), + CODE_SELECT("Code select"), + REFACTORING("Refactoring"), + AST_TRANSFORM("AST Transforms"), + CONTENT_ASSIST("Content assist"), + ORGANIZE_IMPORTS("Clean imports"); + + TraceCategory(String label) { + this.label = label; + } + + public final String label; + + private String paddedLabel; + + public String getPaddedLabel() { + if (paddedLabel == null) { + synchronized (TraceCategory.class) { + if (longestLabel == -1) { + calculateLongest(); + } + } + int extraSpace = longestLabel - label.length(); + paddedLabel = spaces(extraSpace) + label; + } + return paddedLabel; + } + + private String spaces(int extraSpace) { + char[] a = new char[extraSpace]; + Arrays.fill(a, ' '); + return new String(a); + } + + private static void calculateLongest() { + int maybeLongest = longestLabel; + for (TraceCategory category : values()) { + maybeLongest = Math.max(category.label.length(), maybeLongest); + } + longestLabel = maybeLongest; + } + + public static String[] stringValues() { + if (stringValues == null) { + TraceCategory[] values = values(); + stringValues = new String[values.length]; + for (int i = 0, n = values.length; i < n; i += 1) { + stringValues[i] = values[i].label; + } + } + return stringValues; + } + + private static String[] stringValues; + private static int longestLabel = -1; +} diff --git a/base/org.codehaus.groovy26/src/groovy/grape/GrabAnnotationTransformation.java b/base/org.codehaus.groovy26/src/groovy/grape/GrabAnnotationTransformation.java new file mode 100644 index 0000000000..05585dfec9 --- /dev/null +++ b/base/org.codehaus.groovy26/src/groovy/grape/GrabAnnotationTransformation.java @@ -0,0 +1,665 @@ +/* + * 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 groovy.grape; + +import groovy.lang.Grab; +import groovy.lang.GrabConfig; +import groovy.lang.GrabExclude; +import groovy.lang.GrabResolver; +import groovy.lang.Grapes; +import groovy.transform.CompilationUnitAware; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.CompilationUnit; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.io.StringReaderSource; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.codehaus.groovy.tools.GrapeUtil; +import org.codehaus.groovy.transform.ASTTransformation; +import org.codehaus.groovy.transform.ASTTransformationVisitor; +import org.codehaus.groovy.transform.AbstractASTTransformation; +import org.codehaus.groovy.transform.GroovyASTTransformation; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.codehaus.groovy.ast.tools.GeneralUtils.args; +import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.constX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.eqX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS; +import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; +import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue; + +/** + * Transformation for declarative dependency management. + */ +@GroovyASTTransformation(phase=CompilePhase.CONVERSION) +public class GrabAnnotationTransformation extends ClassCodeVisitorSupport implements ASTTransformation, CompilationUnitAware { + private static final String GRAB_CLASS_NAME = Grab.class.getName(); + private static final String GRAB_DOT_NAME = GRAB_CLASS_NAME.substring(GRAB_CLASS_NAME.lastIndexOf(".")); + private static final String GRAB_SHORT_NAME = GRAB_DOT_NAME.substring(1); + + private static final String GRABEXCLUDE_CLASS_NAME = GrabExclude.class.getName(); + private static final String GRABEXCLUDE_DOT_NAME = dotName(GRABEXCLUDE_CLASS_NAME); + private static final String GRABEXCLUDE_SHORT_NAME = shortName(GRABEXCLUDE_DOT_NAME); + + private static final String GRABCONFIG_CLASS_NAME = GrabConfig.class.getName(); + private static final String GRABCONFIG_DOT_NAME = dotName(GRABCONFIG_CLASS_NAME); + private static final String GRABCONFIG_SHORT_NAME = shortName(GRABCONFIG_DOT_NAME); + + private static final String GRAPES_CLASS_NAME = Grapes.class.getName(); + private static final String GRAPES_DOT_NAME = dotName(GRAPES_CLASS_NAME); + private static final String GRAPES_SHORT_NAME = shortName(GRAPES_DOT_NAME); + + private static final String GRABRESOLVER_CLASS_NAME = GrabResolver.class.getName(); + private static final String GRABRESOLVER_DOT_NAME = dotName(GRABRESOLVER_CLASS_NAME); + private static final String GRABRESOLVER_SHORT_NAME = shortName(GRABRESOLVER_DOT_NAME); + + private static final ClassNode THREAD_CLASSNODE = ClassHelper.make(Thread.class); + private static final ClassNode SYSTEM_CLASSNODE = ClassHelper.make(System.class); + + private static final List GRABEXCLUDE_REQUIRED = Arrays.asList("group", "module"); + private static final List GRABRESOLVER_REQUIRED = Arrays.asList("name", "root"); + private static final List GRAB_REQUIRED = Arrays.asList("group", "module", "version"); + private static final List GRAB_OPTIONAL = Arrays.asList("classifier", "transitive", "conf", "ext", "type", "changing", "force", "initClass"); + private static final List GRAB_BOOLEAN = Arrays.asList("transitive", "changing", "force", "initClass"); + private static final Collection GRAB_ALL = DefaultGroovyMethods.plus(GRAB_REQUIRED, GRAB_OPTIONAL); + private static final Pattern IVY_PATTERN = Pattern.compile("([a-zA-Z0-9-/._+=]+)#([a-zA-Z0-9-/._+=]+)(;([a-zA-Z0-9-/.\\(\\)\\[\\]\\{\\}_+=,:@][a-zA-Z0-9-/.\\(\\)\\]\\{\\}_+=,:@]*))?(\\[([a-zA-Z0-9-/._+=,]*)\\])?"); + private static final Pattern ATTRIBUTES_PATTERN = Pattern.compile("(.*;|^)([a-zA-Z0-9]+)=([a-zA-Z0-9.*\\[\\]\\-\\(\\),]*)$"); + + private static final String AUTO_DOWNLOAD_SETTING = Grape.AUTO_DOWNLOAD_SETTING; + private static final String DISABLE_CHECKSUMS_SETTING = Grape.DISABLE_CHECKSUMS_SETTING; + private static final String SYSTEM_PROPERTIES_SETTING = Grape.SYSTEM_PROPERTIES_SETTING; + + private static String dotName(String className) { + return className.substring(className.lastIndexOf(".")); + } + + private static String shortName(String className) { + return className.substring(1); + } + + boolean allowShortGrab; + Set grabAliases; + List grabAnnotations; + + boolean allowShortGrabExcludes; + Set grabExcludeAliases; + List grabExcludeAnnotations; + + boolean allowShortGrabConfig; + Set grabConfigAliases; + List grabConfigAnnotations; + + boolean allowShortGrapes; + Set grapesAliases; + List grapesAnnotations; + + boolean allowShortGrabResolver; + Set grabResolverAliases; + List grabResolverAnnotations; + + CompilationUnit compilationUnit; + SourceUnit sourceUnit; + ClassLoader loader; + boolean initContextClassLoader; + Boolean autoDownload; + Boolean disableChecksums; + Map systemProperties; + + public SourceUnit getSourceUnit() { + return sourceUnit; + } + + public void setCompilationUnit(final CompilationUnit compilationUnit) { + this.compilationUnit = compilationUnit; + } + + public void visit(ASTNode[] nodes, SourceUnit source) { + sourceUnit = source; + loader = null; + initContextClassLoader = false; + + ModuleNode mn = (ModuleNode) nodes[0]; + + allowShortGrab = true; + allowShortGrabExcludes = true; + allowShortGrabConfig = true; + allowShortGrapes = true; + allowShortGrabResolver = true; + grabAliases = new HashSet(); + grabExcludeAliases = new HashSet(); + grabConfigAliases = new HashSet(); + grapesAliases = new HashSet(); + grabResolverAliases = new HashSet(); + for (ImportNode im : mn.getImports()) { + String alias = im.getAlias(); + String className = im.getClassName(); + if ((className.endsWith(GRAB_DOT_NAME) && ((alias == null) || (alias.length() == 0))) + || (GRAB_CLASS_NAME.equals(alias))) + { + allowShortGrab = false; + } else if (GRAB_CLASS_NAME.equals(className)) { + grabAliases.add(im.getAlias()); + } + if ((className.endsWith(GRAPES_DOT_NAME) && ((alias == null) || (alias.length() == 0))) + || (GRAPES_CLASS_NAME.equals(alias))) + { + allowShortGrapes = false; + } else if (GRAPES_CLASS_NAME.equals(className)) { + grapesAliases.add(im.getAlias()); + } + if ((className.endsWith(GRABRESOLVER_DOT_NAME) && ((alias == null) || (alias.length() == 0))) + || (GRABRESOLVER_CLASS_NAME.equals(alias))) + { + allowShortGrabResolver = false; + } else if (GRABRESOLVER_CLASS_NAME.equals(className)) { + grabResolverAliases.add(im.getAlias()); + } + } + + // GRECLIPSE - if this is a list and not a set then you can duplicates when multiple classes + // are visited in the same source file. The same grabs are accumulated. There is maybe a + // better fix but this is easy. If there are duplicates it will work but we are calling + // grab with unnecessary dup info. + Collection> grabMaps = new LinkedHashSet>(); + List> grabMapsInit = new ArrayList>(); + List> grabExcludeMaps = new ArrayList>(); + + for (ClassNode classNode : sourceUnit.getAST().getClasses()) { + grabAnnotations = new ArrayList(); + grabExcludeAnnotations = new ArrayList(); + grabConfigAnnotations = new ArrayList(); + grapesAnnotations = new ArrayList(); + grabResolverAnnotations = new ArrayList(); + + visitClass(classNode); + + ClassNode grapeClassNode = ClassHelper.make(Grape.class); + + List grabResolverInitializers = new ArrayList(); + + if (!grapesAnnotations.isEmpty()) { + for (AnnotationNode node : grapesAnnotations) { + Expression init = node.getMember("initClass"); + Expression value = node.getMember("value"); + if (value instanceof ListExpression) { + for (Object o : ((ListExpression)value).getExpressions()) { + if (o instanceof ConstantExpression) { + extractGrab(init, (ConstantExpression) o); + } + } + } else if (value instanceof ConstantExpression) { + extractGrab(init, (ConstantExpression) value); + } + // don't worry if it's not a ListExpression, or AnnotationConstant, etc. + // the rest of GroovyC will flag it as a syntax error later, so we don't + // need to raise the error ourselves + } + } + + if (!grabResolverAnnotations.isEmpty()) { + grabResolverAnnotationLoop: + for (AnnotationNode node : grabResolverAnnotations) { + Map grabResolverMap = new HashMap(); + String sval = getMemberStringValue(node, "value"); + if (sval != null && sval.length() > 0) { + for (String s : GRABRESOLVER_REQUIRED) { + String mval = getMemberStringValue(node, s); + if (mval != null && mval.isEmpty()) mval = null; + if (mval != null) { + addError("The attribute \"" + s + "\" conflicts with attribute 'value' in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabResolverAnnotationLoop; + } + } + grabResolverMap.put("name", sval); + grabResolverMap.put("root", sval); + } else { + for (String s : GRABRESOLVER_REQUIRED) { + String mval = getMemberStringValue(node, s); + Expression member = node.getMember(s); + if (member == null || (mval != null && mval.isEmpty())) { + addError("The missing attribute \"" + s + "\" is required in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabResolverAnnotationLoop; + } else if (mval == null) { + addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant String in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabResolverAnnotationLoop; + } + grabResolverMap.put(s, mval); + } + } + + // If no scheme is specified for the repository root, + // then turn it into a URI relative to that of the source file. + String root = (String) grabResolverMap.get("root"); + if (root != null && !root.contains(":")) { + URI sourceURI = null; + // Since we use the data: scheme for StringReaderSources (which are fairly common) + // and those are not hierarchical we can't use them for making an absolute URI. + if (!(getSourceUnit().getSource() instanceof StringReaderSource)) { + // Otherwise let's trust the source to know where it is from. + // And actually InputStreamReaderSource doesn't know what to do and so returns null. + sourceURI = getSourceUnit().getSource().getURI(); + } + // If source doesn't know how to get a reference to itself, + // then let's use the current working directory, since the repo can be relative to that. + if (sourceURI == null) { + sourceURI = new File(".").toURI(); + } + try { + URI rootURI = sourceURI.resolve(new URI(root)); + grabResolverMap.put("root", rootURI.toString()); + } catch (URISyntaxException e) { + // We'll be silent here. + // If the URI scheme is unknown or not hierarchical, then we just can't help them and shouldn't cause any trouble either. + // addError("Attribute \"root\" has value '" + root + "' which can't be turned into a valid URI relative to it's source '" + getSourceUnit().getName() + "' @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + } + } + + Grape.addResolver(grabResolverMap); + addGrabResolverAsStaticInitIfNeeded(grapeClassNode, node, grabResolverInitializers, grabResolverMap); + } + } + + if (!grabConfigAnnotations.isEmpty()) { + for (AnnotationNode node : grabConfigAnnotations) { + checkForClassLoader(node); + checkForInitContextClassLoader(node); + checkForAutoDownload(node); + checkForSystemProperties(node); + checkForDisableChecksums(node); + } + addInitContextClassLoaderIfNeeded(classNode); + } + + if (!grabExcludeAnnotations.isEmpty()) { + grabExcludeAnnotationLoop: + for (AnnotationNode node : grabExcludeAnnotations) { + Map grabExcludeMap = new HashMap(); + checkForConvenienceForm(node, true); + for (String s : GRABEXCLUDE_REQUIRED) { + Expression member = node.getMember(s); + if (member == null) { + addError("The missing attribute \"" + s + "\" is required in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabExcludeAnnotationLoop; + } else if (!(member instanceof ConstantExpression)) { + addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabExcludeAnnotationLoop; + } + grabExcludeMap.put(s, ((ConstantExpression)member).getValue()); + } + grabExcludeMaps.add(grabExcludeMap); + } + } + + if (!grabAnnotations.isEmpty()) { + grabAnnotationLoop: + for (AnnotationNode node : grabAnnotations) { + Map grabMap = new HashMap(); + checkForConvenienceForm(node, false); + for (String s : GRAB_ALL) { + Expression member = node.getMember(s); + String mval = getMemberStringValue(node, s); + if (mval != null && mval.isEmpty()) member = null; + if (member == null && !GRAB_OPTIONAL.contains(s)) { + addError("The missing attribute \"" + s + "\" is required in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabAnnotationLoop; + } else if (member != null && !(member instanceof ConstantExpression)) { + addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node); + continue grabAnnotationLoop; + } + if (node.getMember(s) != null) { + grabMap.put(s, ((ConstantExpression)member).getValue()); + } + } + grabMaps.add(grabMap); + if ((node.getMember("initClass") == null) || (node.getMember("initClass") == ConstantExpression.TRUE)) { + grabMapsInit.add(grabMap); + } + } + callGrabAsStaticInitIfNeeded(classNode, grapeClassNode, grabMapsInit, grabExcludeMaps); + } + + if (!grabResolverInitializers.isEmpty()) { + classNode.addStaticInitializerStatements(grabResolverInitializers, true); + } + } + + if (!grabMaps.isEmpty()) { + Map basicArgs = new HashMap(); + basicArgs.put("classLoader", loader != null ? loader : sourceUnit.getClassLoader()); + if (!grabExcludeMaps.isEmpty()) basicArgs.put("excludes", grabExcludeMaps); + if (autoDownload != null) basicArgs.put(AUTO_DOWNLOAD_SETTING, autoDownload); + if (disableChecksums != null) basicArgs.put(DISABLE_CHECKSUMS_SETTING, disableChecksums); + if (systemProperties != null) basicArgs.put(SYSTEM_PROPERTIES_SETTING, systemProperties); + + // GRECLIPSE edit + /*try { + Grape.grab(basicArgs, grabMaps.toArray(new Map[grabMaps.size()])); + // grab may have added more transformations through new URLs added to classpath, so do one more scan + if (compilationUnit!=null) { + ASTTransformationVisitor.addGlobalTransformsAfterGrab(compilationUnit.getASTTransformationsContext()); + } + } catch (RuntimeException re) { + // Decided against syntax exception since this is not a syntax error. + // The down side is we lose line number information for the offending + // @Grab annotation. + source.addException(re); + }*/ + // grab one thing at a time (so the errors are discovered individually) + Map[] grabMapsAsMapArray = grabMaps.toArray(new Map[grabMaps.size()]); + for (int i = 0, n = grabMapsAsMapArray.length; i < n; i += 1) { + try { + Grape.grab(new HashMap(basicArgs), grabMapsAsMapArray[i]); + // grab may have added more transformations through new URLs added to classpath, so do one more scan + if (compilationUnit != null) { + ASTTransformationVisitor.addGlobalTransformsAfterGrab(compilationUnit.getASTTransformationsContext()); + } + } catch (RuntimeException re) { + // Error grabbing Grapes -- [unresolved dependency: joda-timxxe#joda-time;1.6: not found] + String msg = re.getMessage(); + if (grabAnnotations.size() > i) { + addError(msg, grabAnnotations.get(i)); + } else { + source.addException(re); + } + } + } + // GRECLIPSE end + } + } + + private void callGrabAsStaticInitIfNeeded(ClassNode classNode, ClassNode grapeClassNode, List> grabMapsInit, List> grabExcludeMaps) { + List grabInitializers = new ArrayList(); + MapExpression basicArgs = new MapExpression(); + if (autoDownload != null) { + basicArgs.addMapEntryExpression(constX(AUTO_DOWNLOAD_SETTING), constX(autoDownload)); + } + + if (disableChecksums != null) { + basicArgs.addMapEntryExpression(constX(DISABLE_CHECKSUMS_SETTING), constX(disableChecksums)); + } + + if (systemProperties != null && !systemProperties.isEmpty()) { + BlockStatement block = new BlockStatement(); + for(Map.Entry e : systemProperties.entrySet()) { + block.addStatement(stmt(callX(SYSTEM_CLASSNODE, "setProperty", args(constX(e.getKey()), constX(e.getValue()))))); + } + StaticMethodCallExpression enabled = callX(SYSTEM_CLASSNODE, "getProperty", args(constX("groovy.grape.enable"), constX("true"))); + grabInitializers.add(ifS(eqX(enabled, constX("true")), block)); + } + + if (!grabExcludeMaps.isEmpty()) { + ListExpression list = new ListExpression(); + for (Map map : grabExcludeMaps) { + Set> entries = map.entrySet(); + MapExpression inner = new MapExpression(); + for (Map.Entry entry : entries) { + inner.addMapEntryExpression(constX(entry.getKey()), constX(entry.getValue())); + } + list.addExpression(inner); + } + basicArgs.addMapEntryExpression(constX("excludes"), list); + } + + List argList = new ArrayList(); + argList.add(basicArgs); + if (grabMapsInit.isEmpty()) return; + for (Map grabMap : grabMapsInit) { + // add Grape.grab(excludeArgs, [group:group, module:module, version:version, classifier:classifier]) + // or Grape.grab([group:group, module:module, version:version, classifier:classifier]) + MapExpression dependencyArg = new MapExpression(); + for (String s : GRAB_REQUIRED) { + dependencyArg.addMapEntryExpression(constX(s), constX(grabMap.get(s))); + } + for (String s : GRAB_OPTIONAL) { + if (grabMap.containsKey(s)) + dependencyArg.addMapEntryExpression(constX(s), constX(grabMap.get(s))); + } + argList.add(dependencyArg); + } + grabInitializers.add(stmt(callX(grapeClassNode, "grab", args(argList)))); + + // insert at beginning so we have the classloader set up before the class is called + classNode.addStaticInitializerStatements(grabInitializers, true); + } + + private static void addGrabResolverAsStaticInitIfNeeded(ClassNode grapeClassNode, AnnotationNode node, + List grabResolverInitializers, Map grabResolverMap) { + if ((node.getMember("initClass") == null) + || (node.getMember("initClass") == ConstantExpression.TRUE)) + { + MapExpression resolverArgs = new MapExpression(); + for (Map.Entry next : grabResolverMap.entrySet()) { + resolverArgs.addMapEntryExpression(constX(next.getKey()), constX(next.getValue())); + } + grabResolverInitializers.add(stmt(callX(grapeClassNode, "addResolver", args(resolverArgs)))); + } + } + + private void addInitContextClassLoaderIfNeeded(ClassNode classNode) { + if (initContextClassLoader) { + Statement initStatement = stmt(callX( + callX(THREAD_CLASSNODE, "currentThread"), + "setContextClassLoader", + callX(callThisX("getClass"), "getClassLoader") + ) + ); + classNode.addObjectInitializerStatements(initStatement); + } + } + + private void checkForClassLoader(AnnotationNode node) { + Object val = node.getMember("systemClassLoader"); + if (val == null || !(val instanceof ConstantExpression)) return; + Object systemClassLoaderObject = ((ConstantExpression)val).getValue(); + if (!(systemClassLoaderObject instanceof Boolean)) return; + Boolean systemClassLoader = (Boolean) systemClassLoaderObject; + if (systemClassLoader) loader = ClassLoader.getSystemClassLoader(); + } + + private void checkForInitContextClassLoader(AnnotationNode node) { + Object val = node.getMember("initContextClassLoader"); + if (val == null || !(val instanceof ConstantExpression)) return; + Object initContextClassLoaderObject = ((ConstantExpression)val).getValue(); + if (!(initContextClassLoaderObject instanceof Boolean)) return; + initContextClassLoader = (Boolean) initContextClassLoaderObject; + } + + private void checkForAutoDownload(AnnotationNode node) { + Object val = node.getMember(AUTO_DOWNLOAD_SETTING); + if (val == null || !(val instanceof ConstantExpression)) return; + Object autoDownloadValue = ((ConstantExpression)val).getValue(); + if (!(autoDownloadValue instanceof Boolean)) return; + autoDownload = (Boolean) autoDownloadValue; + } + + private void checkForDisableChecksums(AnnotationNode node) { + Object val = node.getMember(DISABLE_CHECKSUMS_SETTING); + if (val == null || !(val instanceof ConstantExpression)) return; + Object disableChecksumsValue = ((ConstantExpression)val).getValue(); + if (!(disableChecksumsValue instanceof Boolean)) return; + disableChecksums = (Boolean) disableChecksumsValue; + } + + private void checkForSystemProperties(AnnotationNode node) { + systemProperties = new HashMap(); + List nameValueList = AbstractASTTransformation.getMemberStringList(node, SYSTEM_PROPERTIES_SETTING); + if (nameValueList != null) { + for (String nameValue : nameValueList) { + int equalsDelim = nameValue.indexOf('='); + if (equalsDelim != -1) { + systemProperties.put(nameValue.substring(0, equalsDelim), nameValue.substring(equalsDelim + 1)); + } + } + } + } + + private static void checkForConvenienceForm(AnnotationNode node, boolean exclude) { + Object val = node.getMember("value"); + if (val == null || !(val instanceof ConstantExpression)) return; + Object allParts = ((ConstantExpression)val).getValue(); + if (!(allParts instanceof String)) return; + String allstr = (String) allParts; + + // strip off trailing attributes + boolean done = false; + while (!done) { + Matcher attrs = ATTRIBUTES_PATTERN.matcher(allstr); + if (attrs.find()) { + String attrName = attrs.group(2); + String attrValue = attrs.group(3); + if (attrName == null || attrValue == null) continue; + boolean isBool = GRAB_BOOLEAN.contains(attrName); + ConstantExpression value = constX(isBool ? Boolean.valueOf(attrValue) : attrValue); + value.setSourcePosition(node); + node.addMember(attrName, value); + int lastSemi = allstr.lastIndexOf(';'); + if (lastSemi == -1) { + allstr = ""; + break; + } + allstr = allstr.substring(0, lastSemi); + } else { + done = true; + } + } + + if (allstr.contains("#")) { + // see: http://ant.apache.org/ivy/history/latest-milestone/textual.html + Matcher m = IVY_PATTERN.matcher(allstr); + if (!m.find()) return; + if (m.group(1) == null || m.group(2) == null) return; + node.addMember("module", constX(m.group(2))); + node.addMember("group", constX(m.group(1))); + if (m.group(6) != null) node.addMember("conf", constX(m.group(6))); + if (m.group(4) != null) node.addMember("version", constX(m.group(4))); + else if (!exclude && node.getMember("version") == null) node.addMember("version", constX("*")); + node.getMembers().remove("value"); + } else if (allstr.contains(":")) { + // assume gradle syntax + // see: http://www.gradle.org/latest/docs/userguide/dependency_management.html#sec:how_to_declare_your_dependencies + Map parts = GrapeUtil.getIvyParts(allstr); + for (Map.Entry entry : parts.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue().toString(); + if (!key.equals("version") || !value.equals("*") || !exclude) { + node.addMember(key, constX(value)); + } + } + node.getMembers().remove("value"); + } + } + + private void extractGrab(Expression init, ConstantExpression ce) { + if (ce.getValue() instanceof AnnotationNode) { + AnnotationNode annotation = (AnnotationNode) ce.getValue(); + if ((init != null) && (annotation.getMember("initClass") != null)) { + annotation.setMember("initClass", init); + } + String name = annotation.getClassNode().getName(); + if ((GRAB_CLASS_NAME.equals(name)) + || (allowShortGrab && GRAB_SHORT_NAME.equals(name)) + || (grabAliases.contains(name))) { + grabAnnotations.add(annotation); + } + if ((GRABEXCLUDE_CLASS_NAME.equals(name)) + || (allowShortGrabExcludes && GRABEXCLUDE_SHORT_NAME.equals(name)) + || (grabExcludeAliases.contains(name))) { + grabExcludeAnnotations.add(annotation); + } + if ((GRABCONFIG_CLASS_NAME.equals(name)) + || (allowShortGrabConfig && GRABCONFIG_SHORT_NAME.equals(name)) + || (grabConfigAliases.contains(name))) { + grabConfigAnnotations.add(annotation); + } + if ((GRABRESOLVER_CLASS_NAME.equals(name)) + || (allowShortGrabResolver && GRABRESOLVER_SHORT_NAME.equals(name)) + || (grabResolverAliases.contains(name))) { + grabResolverAnnotations.add(annotation); + } + } + } + + /** + * Adds the annotation to the internal target list if a match is found. + * + * @param node the AST node we are processing + */ + public void visitAnnotations(AnnotatedNode node) { + super.visitAnnotations(node); + for (AnnotationNode an : node.getAnnotations()) { + String name = an.getClassNode().getName(); + if ((GRAB_CLASS_NAME.equals(name)) + || (allowShortGrab && GRAB_SHORT_NAME.equals(name)) + || (grabAliases.contains(name))) { + grabAnnotations.add(an); + } + if ((GRABEXCLUDE_CLASS_NAME.equals(name)) + || (allowShortGrabExcludes && GRABEXCLUDE_SHORT_NAME.equals(name)) + || (grabExcludeAliases.contains(name))) { + grabExcludeAnnotations.add(an); + } + if ((GRABCONFIG_CLASS_NAME.equals(name)) + || (allowShortGrabConfig && GRABCONFIG_SHORT_NAME.equals(name)) + || (grabConfigAliases.contains(name))) { + grabConfigAnnotations.add(an); + } + if ((GRAPES_CLASS_NAME.equals(name)) + || (allowShortGrapes && GRAPES_SHORT_NAME.equals(name)) + || (grapesAliases.contains(name))) { + grapesAnnotations.add(an); + } + if ((GRABRESOLVER_CLASS_NAME.equals(name)) + || (allowShortGrabResolver && GRABRESOLVER_SHORT_NAME.equals(name)) + || (grabResolverAliases.contains(name))) { + grabResolverAnnotations.add(an); + } + } + } + +} diff --git a/base/org.codehaus.groovy26/src/groovy/grape/GrapeIvy.groovy b/base/org.codehaus.groovy26/src/groovy/grape/GrapeIvy.groovy new file mode 100644 index 0000000000..d70915b31d --- /dev/null +++ b/base/org.codehaus.groovy26/src/groovy/grape/GrapeIvy.groovy @@ -0,0 +1,730 @@ +/* + * 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 groovy.grape + +import org.apache.groovy.plugin.GroovyRunner +import org.apache.groovy.plugin.GroovyRunnerRegistry +import org.apache.ivy.Ivy +import org.apache.ivy.core.cache.ResolutionCacheManager +import org.apache.ivy.core.event.IvyListener +import org.apache.ivy.core.event.download.PrepareDownloadEvent +import org.apache.ivy.core.event.resolve.StartResolveEvent +import org.apache.ivy.core.module.descriptor.Configuration +import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor +import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor +import org.apache.ivy.core.module.descriptor.DefaultExcludeRule +import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor +import org.apache.ivy.core.module.id.ArtifactId +import org.apache.ivy.core.module.id.ModuleId +import org.apache.ivy.core.module.id.ModuleRevisionId +import org.apache.ivy.core.report.ArtifactDownloadReport +import org.apache.ivy.core.report.ResolveReport +import org.apache.ivy.core.resolve.ResolveOptions +import org.apache.ivy.core.settings.IvySettings +import org.apache.ivy.plugins.matcher.ExactPatternMatcher +import org.apache.ivy.plugins.matcher.PatternMatcher +import org.apache.ivy.plugins.resolver.ChainResolver +import org.apache.ivy.plugins.resolver.IBiblioResolver +import org.apache.ivy.util.DefaultMessageLogger +import org.apache.ivy.util.Message +import org.codehaus.groovy.reflection.CachedClass +import org.codehaus.groovy.reflection.ClassInfo +import org.codehaus.groovy.reflection.ReflectionUtils +import org.codehaus.groovy.runtime.m12n.ExtensionModuleScanner +import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl + +import javax.xml.parsers.DocumentBuilderFactory +import java.util.jar.JarFile +import java.util.regex.Pattern +import java.util.zip.ZipEntry +import java.util.zip.ZipException +import java.util.zip.ZipFile + +/** + * Implementation suppoting {@code @Grape} and {@code @Grab} annotations based on Ivy. + */ +class GrapeIvy implements GrapeEngine { + + static final int DEFAULT_DEPTH = 3 + + private static final String METAINF_PREFIX = 'META-INF/services/' + private static final String RUNNER_PROVIDER_CONFIG = GroovyRunner.class.getName() + + private final exclusiveGrabArgs = [ + ['group', 'groupId', 'organisation', 'organization', 'org'], + ['module', 'artifactId', 'artifact'], + ['version', 'revision', 'rev'], + ['conf', 'scope', 'configuration'], + ].inject([:], {m, g -> g.each {a -> m[a] = (g - a) as Set}; m}) + + boolean enableGrapes + Ivy ivyInstance + Set resolvedDependencies + Set downloadedArtifacts + // weak hash map so we don't leak loaders directly + Map> loadedDeps = new WeakHashMap>() + // set that stores the IvyGrabRecord(s) for all the dependencies in each grab() call + Set grabRecordsForCurrDependencies = new LinkedHashSet() + // we keep the settings so that addResolver can add to the resolver chain + IvySettings settings + + public GrapeIvy() { + // if we are already initialized, quit + if (enableGrapes) return + + // start ivy + Message.defaultLogger = new DefaultMessageLogger(System.getProperty("ivy.message.logger.level", "-1") as int) + settings = new IvySettings() + + // configure settings + def grapeConfig = getLocalGrapeConfig() + if (!grapeConfig.exists()) { + grapeConfig = GrapeIvy.getResource("defaultGrapeConfig.xml") + } + try { + settings.load(grapeConfig) // exploit multi-methods for convenience + } catch (java.text.ParseException ex) { + def configLocation = grapeConfig instanceof File ? grapeConfig.canonicalPath : grapeConfig.toString() + System.err.println "Local Ivy config file '$configLocation' appears corrupt - ignoring it and using default config instead\nError was: " + ex.message + grapeConfig = GrapeIvy.getResource("defaultGrapeConfig.xml") + settings.load(grapeConfig) + } + + // set up the cache dirs + settings.defaultCache = getGrapeCacheDir() + + settings.setVariable("ivy.default.configuration.m2compatible", "true") + ivyInstance = Ivy.newInstance(settings) + org.apache.ivy.core.IvyContext.getContext().setIvy(ivyInstance); + resolvedDependencies = [] + downloadedArtifacts = [] + + //TODO add grab to the DGM?? + + enableGrapes = true + } + + public File getGroovyRoot() { + String root = System.getProperty("groovy.root") + def groovyRoot + if (root == null) { + groovyRoot = new File(System.getProperty("user.home"), ".groovy") + } else { + groovyRoot = new File(root) + } + try { + groovyRoot = groovyRoot.canonicalFile + } catch (IOException e) { + // skip canonicalization then, it may not exist yet + } + return groovyRoot + } + + public File getLocalGrapeConfig() { + String grapeConfig = System.getProperty("grape.config") + if(grapeConfig) { + return new File(grapeConfig) + } + return new File(getGrapeDir(), 'grapeConfig.xml') + } + + public File getGrapeDir() { + String root = System.getProperty("grape.root") + if(root == null) { + return getGroovyRoot() + } + File grapeRoot = new File(root) + try { + grapeRoot = grapeRoot.canonicalFile + } catch (IOException e) { + // skip canonicalization then, it may not exist yet + } + return grapeRoot + } + + public File getGrapeCacheDir() { + File cache = new File(getGrapeDir(), 'grapes') + if (!cache.exists()) { + cache.mkdirs() + } else if (!cache.isDirectory()) { + throw new RuntimeException("The grape cache dir $cache is not a directory") + } + return cache + } + + public def chooseClassLoader(Map args) { + def loader = args.classLoader + if (!isValidTargetClassLoader(loader)) { + loader = (args.refObject?.class + ?:ReflectionUtils.getCallingClass(args.calleeDepth?:1) + )?.classLoader + while (loader && !isValidTargetClassLoader(loader)) { + loader = loader.parent + } + //if (!isValidTargetClassLoader(loader)) { + // loader = Thread.currentThread().contextClassLoader + //} + //if (!isValidTargetClassLoader(loader)) { + // loader = GrapeIvy.class.classLoader + //} + /* GRECLIPSE edit -- don't check this. Removing this check will only affect our copy of GrapeIvy that is used during compilation where the classloader does not matter. + if (!isValidTargetClassLoader(loader)) { + throw new RuntimeException("No suitable ClassLoader found for grab") + } + */ + } + return loader + } + + private boolean isValidTargetClassLoader(loader) { + return isValidTargetClassLoaderClass(loader?.class) + } + + private boolean isValidTargetClassLoaderClass(Class loaderClass) { + return (loaderClass != null) && + ( + (loaderClass.name == 'groovy.lang.GroovyClassLoader') || + (loaderClass.name == 'org.codehaus.groovy.tools.RootLoader') || + isValidTargetClassLoaderClass(loaderClass.superclass) + ) + } + + public IvyGrabRecord createGrabRecord(Map deps) { + // parse the actual dependency arguments + String module = deps.module ?: deps.artifactId ?: deps.artifact + if (!module) { + throw new RuntimeException('grab requires at least a module: or artifactId: or artifact: argument') + } + + String groupId = deps.group ?: deps.groupId ?: deps.organisation ?: deps.organization ?: deps.org ?: '' + String ext = deps.ext ?: deps.type ?: '' + String type = deps.type ?: '' + + //TODO accept ranges and decode them? except '1.0.0'..<'2.0.0' won't work in groovy + String version = deps.version ?: deps.revision ?: deps.rev ?: '*' + if ('*' == version) version = 'latest.default' + + ModuleRevisionId mrid = ModuleRevisionId.newInstance(groupId, module, version) + + boolean force = deps.containsKey('force') ? deps.force : true + boolean changing = deps.containsKey('changing') ? deps.changing : false + boolean transitive = deps.containsKey('transitive') ? deps.transitive : true + def conf = deps.conf ?: deps.scope ?: deps.configuration ?: ['default'] + if (conf instanceof String) { + if (conf.startsWith("[") && conf.endsWith("]")) conf = conf[1..-2] + conf = conf.split(",").toList() + } + def classifier = deps.classifier ?: null + + return new IvyGrabRecord(mrid:mrid, conf:conf, changing:changing, transitive:transitive, force:force, classifier:classifier, ext:ext, type:type) + } + + public grab(String endorsedModule) { + return grab(group:'groovy.endorsed', module:endorsedModule, version:GroovySystem.version) + } + + public grab(Map args) { + args.calleeDepth = args.calleeDepth?:DEFAULT_DEPTH + 1 + return grab(args, args) + } + + public grab(Map args, Map... dependencies) { + ClassLoader loader = null + grabRecordsForCurrDependencies.clear() + + try { + // identify the target classloader early, so we fail before checking repositories + loader = chooseClassLoader( + classLoader:args.remove('classLoader'), + refObject:args.remove('refObject'), + calleeDepth:args.calleeDepth?:DEFAULT_DEPTH, + ) + + // check for non-fail null. + // If we were in fail mode we would have already thrown an exception + if (!loader) return + + def uris = resolve(loader, args, dependencies) + for (URI uri in uris) { + loader.addURL(uri.toURL()) + } + boolean runnerServicesFound = false + for (URI uri in uris) { + //TODO check artifact type, jar vs library, etc + File file = new File(uri) + processCategoryMethods(loader, file) + Collection services = processMetaInfServices(loader, file) + if (!runnerServicesFound) { + runnerServicesFound = services.contains(RUNNER_PROVIDER_CONFIG) + } + } + if (runnerServicesFound) { + GroovyRunnerRegistry.getInstance().load(loader) + } + } catch (Exception e) { + // clean-up the state first + Set grabRecordsForCurrLoader = getLoadedDepsForLoader(loader) + grabRecordsForCurrLoader.removeAll(grabRecordsForCurrDependencies) + grabRecordsForCurrDependencies.clear() + + if (args.noExceptions) { + return e + } + throw e + } + return null + } + + private processCategoryMethods(ClassLoader loader, File file) { + // register extension methods if jar + if (file.name.toLowerCase().endsWith(".jar")) { + def mcRegistry = GroovySystem.metaClassRegistry + if (mcRegistry instanceof MetaClassRegistryImpl) { + try { + JarFile jar = new JarFile(file) + def entry = jar.getEntry(ExtensionModuleScanner.MODULE_META_INF_FILE) + if (entry) { + Properties props = new Properties() + props.load(jar.getInputStream(entry)) + Map> metaMethods = new HashMap>() + mcRegistry.registerExtensionModuleFromProperties(props, loader, metaMethods) + // add old methods to the map + metaMethods.each { CachedClass c, List methods -> + // GROOVY-5543: if a module was loaded using grab, there are chances that subclasses + // have their own ClassInfo, and we must change them as well! + Set classesToBeUpdated = [c] + ClassInfo.onAllClassInfo { ClassInfo info -> + if (c.theClass.isAssignableFrom(info.cachedClass.theClass)) { + classesToBeUpdated << info.cachedClass + } + } + classesToBeUpdated*.addNewMopMethods(methods) + } + } + } + catch(ZipException zipException) { + throw new RuntimeException("Grape could not load jar '$file'", zipException) + } + } + } + } + + void processOtherServices(ClassLoader loader, File f) { + processMetaInfServices(loader, f) // ignore result + } + + /** + * Searches the given File for known service provider + * configuration files to process. + * + * @param loader used to locate service provider files + * @param f ZipFile in which to search for services + * @return a collection of service provider files that were found + */ + private Collection processMetaInfServices(ClassLoader loader, File f) { + List services = new ArrayList<>() + try { + ZipFile zf = new ZipFile(f) + String providerConfig = 'org.codehaus.groovy.runtime.SerializedCategoryMethods' + ZipEntry serializedCategoryMethods = zf.getEntry(METAINF_PREFIX + providerConfig) + if (serializedCategoryMethods != null) { + services.add(providerConfig) + processSerializedCategoryMethods(zf.getInputStream(serializedCategoryMethods)) + } + // TODO: remove in a future release (replaced by GroovyRunnerRegistry) + providerConfig = 'org.codehaus.groovy.plugins.Runners' + ZipEntry pluginRunners = zf.getEntry(METAINF_PREFIX + providerConfig) + if (pluginRunners != null) { + services.add(providerConfig) + processRunners(zf.getInputStream(pluginRunners), f.getName(), loader) + } + // GroovyRunners are loaded per ClassLoader using a ServiceLoader so here + // it only needs to be indicated that the service provider file was found + if (zf.getEntry(METAINF_PREFIX + RUNNER_PROVIDER_CONFIG) != null) { + services.add(RUNNER_PROVIDER_CONFIG) + } + } catch(ZipException ignore) { + // ignore files we can't process, e.g. non-jar/zip artifacts + // TODO log a warning + } + return services + } + + void processSerializedCategoryMethods(InputStream is) { + is.text.readLines().each { + println it.trim() // TODO implement this or delete it + } + } + + void processRunners(InputStream is, String name, ClassLoader loader) { + GroovyRunnerRegistry registry = GroovyRunnerRegistry.getInstance() + is.text.readLines()*.trim().findAll{ !it.isEmpty() && it[0] != '#' }.each { + try { + registry[name] = loader.loadClass(it).newInstance() + } catch (Exception ex) { + throw new IllegalStateException("Error registering runner class '" + it + "'", ex) + } + } + } + + public ResolveReport getDependencies(Map args, IvyGrabRecord... grabRecords) { + ResolutionCacheManager cacheManager = ivyInstance.resolutionCacheManager + + def millis = System.currentTimeMillis() + def md = new DefaultModuleDescriptor(ModuleRevisionId + .newInstance("caller", "all-caller", "working" + millis.toString()[-2..-1]), "integration", null, true) + md.addConfiguration(new Configuration('default')) + md.setLastModified(millis) + + addExcludesIfNeeded(args, md) + + for (IvyGrabRecord grabRecord : grabRecords) { + def conf = grabRecord.conf ?: ['*'] + DefaultDependencyDescriptor dd = md.dependencies.find {it.dependencyRevisionId.equals(grabRecord.mrid)} + if (dd) { + createAndAddDependencyArtifactDescriptor(dd, grabRecord, conf) + } else { + dd = new DefaultDependencyDescriptor(md, grabRecord.mrid, grabRecord.force, + grabRecord.changing, grabRecord.transitive) + conf.each {dd.addDependencyConfiguration('default', it)} + createAndAddDependencyArtifactDescriptor(dd, grabRecord, conf) + md.addDependency(dd) + } + } + + // resolve grab and dependencies + ResolveOptions resolveOptions = new ResolveOptions()\ + .setConfs(['default'] as String[])\ + .setOutputReport(false)\ + .setValidate(args.containsKey('validate') ? args.validate : false) + + ivyInstance.settings.defaultResolver = args.autoDownload ? 'downloadGrapes' : 'cachedGrapes' + if (args.disableChecksums) { + ivyInstance.settings.setVariable('ivy.checksums', '') + } + boolean reportDownloads = System.getProperty('groovy.grape.report.downloads', 'false') == 'true' + if (reportDownloads) { + ivyInstance.eventManager.addIvyListener([progress:{ ivyEvent -> switch(ivyEvent) { + case StartResolveEvent: + ivyEvent.moduleDescriptor.dependencies.each { it -> + def name = it.toString() + if (!resolvedDependencies.contains(name)) { + resolvedDependencies << name + System.err.println "Resolving " + name + } + } + break + case PrepareDownloadEvent: + ivyEvent.artifacts.each { it -> + def name = it.toString() + if (!downloadedArtifacts.contains(name)) { + downloadedArtifacts << name + System.err.println "Preparing to download artifact " + name + } + } + break + } } ] as IvyListener) + } + + ResolveReport report = null + int attempt = 8 // max of 8 times + while (true) { + try { + report = ivyInstance.resolve(md, resolveOptions) + break + } catch(IOException ioe) { + if (attempt--) { + if (reportDownloads) + System.err.println "Grab Error: retrying..." + sleep attempt > 4 ? 350 : 1000 + continue + } + throw new RuntimeException("Error grabbing grapes -- $ioe.message") + } + } + + if (report.hasError()) { + throw new RuntimeException("Error grabbing Grapes -- $report.allProblemMessages") + } + if (report.downloadSize && reportDownloads) { + System.err.println "Downloaded ${report.downloadSize >> 10} Kbytes in ${report.downloadTime}ms:\n ${report.allArtifactsReports*.toString().join('\n ')}" + } + md = report.moduleDescriptor + + if (!args.preserveFiles) { + cacheManager.getResolvedIvyFileInCache(md.moduleRevisionId).delete() + cacheManager.getResolvedIvyPropertiesInCache(md.moduleRevisionId).delete() + } + + return report + } + + private void createAndAddDependencyArtifactDescriptor(DefaultDependencyDescriptor dd, IvyGrabRecord grabRecord, List conf) { + // TODO: find out "unknown" reason and change comment below - also, confirm conf[0] check vs conf.contains('optional') + if (conf[0]!="optional" || grabRecord.classifier) { // for some unknown reason optional dependencies should not have an artifactDescriptor + def dad = new DefaultDependencyArtifactDescriptor(dd, + grabRecord.mrid.name, grabRecord.type ?: 'jar', grabRecord.ext ?: 'jar', null, grabRecord.classifier ? [classifier: grabRecord.classifier] : null) + conf.each { dad.addConfiguration(it) } + dd.addDependencyArtifact('default', dad) + } + } + + public void uninstallArtifact(String group, String module, String rev) { + // TODO consider transitive uninstall as an option + Pattern ivyFilePattern = ~/ivy-(.*)\.xml/ //TODO get pattern from ivy conf + grapeCacheDir.eachDir { File groupDir -> + if (groupDir.name == group) groupDir.eachDir { File moduleDir -> + if (moduleDir.name == module) moduleDir.eachFileMatch(ivyFilePattern) { File ivyFile -> + def m = ivyFilePattern.matcher(ivyFile.name) + if (m.matches() && m.group(1) == rev) { + // TODO handle other types? e.g. 'dlls' + def jardir = new File(moduleDir, 'jars') + if (!jardir.exists()) return + def dbf = DocumentBuilderFactory.newInstance() + def db = dbf.newDocumentBuilder() + def root = db.parse(ivyFile).documentElement + def publis = root.getElementsByTagName('publications') + for (int i=0; i + def excludeRule = new DefaultExcludeRule(new ArtifactId( + new ModuleId(map.group, map.module), PatternMatcher.ANY_EXPRESSION, + PatternMatcher.ANY_EXPRESSION, + PatternMatcher.ANY_EXPRESSION), + ExactPatternMatcher.INSTANCE, null) + excludeRule.addConfiguration('default') + md.addExcludeRule(excludeRule) + } + } + + public Map>> enumerateGrapes() { + Map>> bunches = [:] + Pattern ivyFilePattern = ~/ivy-(.*)\.xml/ //TODO get pattern from ivy conf + grapeCacheDir.eachDir {File groupDir -> + Map> grapes = [:] + bunches[groupDir.name] = grapes + groupDir.eachDir { File moduleDir -> + def versions = [] + moduleDir.eachFileMatch(ivyFilePattern) {File ivyFile -> + def m = ivyFilePattern.matcher(ivyFile.name) + if (m.matches()) versions += m.group(1) + } + grapes[moduleDir.name] = versions + } + } + return bunches + } + + public URI[] resolve(Map args, Map ... dependencies) { + resolve(args, null, dependencies) + } + + public URI[] resolve(Map args, List depsInfo, Map ... dependencies) { + // identify the target classloader early, so we fail before checking repositories + def loader = chooseClassLoader( + classLoader: args.remove('classLoader'), + refObject: args.remove('refObject'), + calleeDepth: args.calleeDepth ?: DEFAULT_DEPTH, + ) + + // check for non-fail null. + // If we were in fail mode we would have already thrown an exception + if (!loader) return + + resolve(loader, args, depsInfo, dependencies) + } + + URI [] resolve(ClassLoader loader, Map args, Map... dependencies) { + return resolve(loader, args, null, dependencies) + } + + URI [] resolve(ClassLoader loader, Map args, List depsInfo, Map... dependencies) { + // check for mutually exclusive arguments + Set keys = args.keySet() + keys.each {a -> + Set badArgs = exclusiveGrabArgs[a] + if (badArgs && !badArgs.disjoint(keys)) { + throw new RuntimeException("Mutually exclusive arguments passed into grab: ${keys.intersect(badArgs) + a}") + } + } + + // check the kill switch + if (!enableGrapes) { return } + + boolean populateDepsInfo = (depsInfo != null) + + Set localDeps = getLoadedDepsForLoader(loader) + + dependencies.each { + IvyGrabRecord igr = createGrabRecord(it) + grabRecordsForCurrDependencies.add(igr) + localDeps.add(igr) + } + // the call to reverse ensures that the newest additions are in + // front causing existing dependencies to come last and thus + // claiming higher priority. Thus when module versions clash we + // err on the side of using the class already loaded into the + // classloader rather than adding another jar of the same module + // with a different version + ResolveReport report = null + try { + report = getDependencies(args, *localDeps.asList().reverse()) + } catch (Exception e) { + // clean-up the state first + localDeps.removeAll(grabRecordsForCurrDependencies) + grabRecordsForCurrDependencies.clear() + throw e + } + + List results = [] + for (ArtifactDownloadReport adl in report.allArtifactsReports) { + //TODO check artifact type, jar vs library, etc + if (adl.localFile) { + results += adl.localFile.toURI() + } + } + + if (populateDepsInfo) { + def deps = report.dependencies + deps.each { depNode -> + def id = depNode.id + depsInfo << ['group' : id.organisation, 'module' : id.name, 'revision' : id.revision] + } + } + + return results as URI[] + } + + private Set getLoadedDepsForLoader(ClassLoader loader) { + Set localDeps = loadedDeps.get(loader) + if (localDeps == null) { + // use a linked set to preserve initial insertion order + localDeps = new LinkedHashSet() + loadedDeps.put(loader, localDeps) + } + return localDeps + } + + public Map[] listDependencies (ClassLoader classLoader) { + if (loadedDeps.containsKey(classLoader)) { + List results = [] + loadedDeps[classLoader].each { IvyGrabRecord grabbed -> + def dep = [ + group : grabbed.mrid.organisation, + module : grabbed.mrid.name, + version : grabbed.mrid.revision + ] + if (grabbed.conf != ['default']) { + dep.conf = grabbed.conf + } + if (grabbed.changing) { + dep.changing = grabbed.changing + } + if (!grabbed.transitive) { + dep.transitive = grabbed.transitive + } + if (!grabbed.force) { + dep.force = grabbed.force + } + if (grabbed.classifier) { + dep.classifier = grabbed.classifier + } + if (grabbed.ext) { + dep.ext = grabbed.ext + } + if (grabbed.type) { + dep.type = grabbed.type + } + results << dep + } + return results + } + return null + } + + public void addResolver(Map args) { + ChainResolver chainResolver = settings.getResolver("downloadGrapes") + + IBiblioResolver resolver = new IBiblioResolver(name: args.name, root:args.root, + m2compatible:(args.m2Compatible ?: true), settings:settings) + + chainResolver.add(resolver) + + ivyInstance = Ivy.newInstance(settings) + resolvedDependencies = [] + downloadedArtifacts = [] + } +} + +class IvyGrabRecord { + ModuleRevisionId mrid + List conf + boolean changing + boolean transitive + boolean force + String classifier + String ext + String type + + public int hashCode() { + return (mrid.hashCode() ^ conf.hashCode() + ^ (changing ? 0xaaaaaaaa : 0x55555555) + ^ (transitive ? 0xbbbbbbbb : 0x66666666) + ^ (force ? 0xcccccccc: 0x77777777) + ^ (classifier ? classifier.hashCode() : 0) + ^ (ext ? ext.hashCode() : 0) + ^ (type ? type.hashCode() : 0)) + } + + public boolean equals(Object o) { + return ((o.class == IvyGrabRecord) + && (changing == o.changing) + && (transitive == o.transitive) + && (force== o.force) + && (mrid == o.mrid) + && (conf == o.conf) + && (classifier == o.classifier) + && (ext == o.ext) + && (type == o.type)) + } + +} diff --git a/base/org.codehaus.groovy26/src/groovy/transform/options/PropertyHandler.java b/base/org.codehaus.groovy26/src/groovy/transform/options/PropertyHandler.java new file mode 100644 index 0000000000..765acc46b9 --- /dev/null +++ b/base/org.codehaus.groovy26/src/groovy/transform/options/PropertyHandler.java @@ -0,0 +1,124 @@ +/* + * 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 groovy.transform.options; + +import groovy.lang.GroovyClassLoader; +import groovy.transform.PropertyOptions; +import org.apache.groovy.lang.annotation.Incubating; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.transform.AbstractASTTransformation; + +import java.lang.annotation.Annotation; +import java.util.List; + +import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching; + +/** + * Used to provide custom property handling when getting, setting or initializing properties. + * + * @since 2.5.0 + */ +@Incubating +public abstract class PropertyHandler { + private static final Class PROPERTY_OPTIONS_CLASS = PropertyOptions.class; + public static final ClassNode PROPERTY_OPTIONS_TYPE = makeWithoutCaching(PROPERTY_OPTIONS_CLASS, false); + public abstract boolean validateAttributes(AbstractASTTransformation xform, AnnotationNode anno); + + public boolean validateProperties(AbstractASTTransformation xform, BlockStatement body, ClassNode cNode, List props) { + return true; + } + + /** + * Create a statement that will initialize the property including any defensive copying. Null if no statement should be added. + * + * @param xform the transform being processed + * @param anno the '@ImmutableBase' annotation node + * @param cNode the classnode containing the property + * @param pNode the property node to initialize + * @param namedArgMap an "args" Map if the property value should come from a named arg map or null if not + */ + public abstract Statement createPropInit(AbstractASTTransformation xform, AnnotationNode anno, ClassNode cNode, PropertyNode pNode, Parameter namedArgMap); + + /** + * Create the getter block used when reading the property including any defensive copying. + * + * @param pNode the property node + */ + public Statement createPropGetter(PropertyNode pNode) { + return pNode.getGetterBlock(); + } + + /** + * Create the setter block used when setting the property. Can be null for read-only properties. + * + * @param pNode the property node + */ + public Statement createPropSetter(PropertyNode pNode) { + return pNode.getSetterBlock(); + } + + protected boolean isValidAttribute(AbstractASTTransformation xform, AnnotationNode anno, String memberName) { + if (xform.getMemberValue(anno, memberName) != null) { + xform.addError("Error during " + xform.getAnnotationName() + " processing: Annotation attribute '" + memberName + + "' not supported for property handler " + getClass().getSimpleName(), anno); + return false; + } + return true; + } + + public static PropertyHandler createPropertyHandler(AbstractASTTransformation xform, GroovyClassLoader loader, ClassNode cNode) { + List annotations = cNode.getAnnotations(PROPERTY_OPTIONS_TYPE); + AnnotationNode anno = annotations.isEmpty() ? null : annotations.get(0); + if (anno == null) return new groovy.transform.options.DefaultPropertyHandler(); + + ClassNode handlerClass = xform.getMemberClassValue(anno, "propertyHandler", ClassHelper.make(groovy.transform.options.DefaultPropertyHandler.class)); + + if (handlerClass == null) { + xform.addError("Couldn't determine propertyHandler class", anno); + return null; + } + + String className = handlerClass.getName(); + try { + // GRECLIPSE edit + //Object instance = loader.loadClass(className).newInstance(); + Object instance = PropertyHandler.class.getClassLoader().loadClass(className).newInstance(); + // GRECLIPSE end + if (instance == null) { + xform.addError("Can't load propertyHandler '" + className + "'", anno); + return null; + } + if (!PropertyHandler.class.isAssignableFrom(instance.getClass())) { + xform.addError("The propertyHandler class '" + handlerClass.getName() + "' on " + xform.getAnnotationName() + " is not a propertyHandler", anno); + return null; + } + + return (PropertyHandler) instance; + } catch (Exception e) { + xform.addError("Can't load propertyHandler '" + className + "' " + e, anno); + return null; + } + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/greclipse/GroovyTokenTypeBridge.java b/base/org.codehaus.groovy26/src/org/codehaus/greclipse/GroovyTokenTypeBridge.java new file mode 100644 index 0000000000..08c76619dc --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/greclipse/GroovyTokenTypeBridge.java @@ -0,0 +1,60 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.greclipse; + +import org.codehaus.groovy.antlr.parser.GroovyTokenTypes; + +/** + * This class contains copies of the {@link GroovyTokenTypes} constants in static non-final + * variables. The reason for this is that we must ensure the contants do not get inlined into + * greclipse bytecode anywhere outside of the org.codehaus.groovyXX bundles. + *

+ * If those constants where inlined, this would create trouble since the values for the constants + * are generated by the antlr parser generator and they tend to differ between version. This will + * break things that uses constants inlined from one version while trying to run against another + * version. + * + * @author Kris De Volder + */ +public class GroovyTokenTypeBridge { + + // Only the GroovyTokenTypes that we use inside Greclipse are represented here. + // More can be added on an as needed basis. + + public static int IDENT = GroovyTokenTypes.IDENT; + public static int LBRACK = GroovyTokenTypes.LBRACK; + public static int LCURLY = GroovyTokenTypes.LCURLY; + public static int LPAREN = GroovyTokenTypes.LPAREN; + public static int RPAREN = GroovyTokenTypes.RPAREN; + public static int RBRACK = GroovyTokenTypes.RBRACK; + public static int RCURLY = GroovyTokenTypes.RCURLY; + public static int STRING_CTOR_START = GroovyTokenTypes.STRING_CTOR_START; + public static int STRING_CTOR_END = GroovyTokenTypes.STRING_CTOR_END; + public static int COMMA = GroovyTokenTypes.COMMA; + public static int SEMI = GroovyTokenTypes.SEMI; + public static int EOF = GroovyTokenTypes.EOF; + public static int NLS = GroovyTokenTypes.NLS; + public static int WS = GroovyTokenTypes.WS; + public static int ML_COMMENT = GroovyTokenTypes.ML_COMMENT; + public static int SL_COMMENT = GroovyTokenTypes.SL_COMMENT; + public static int CLOSABLE_BLOCK_OP = GroovyTokenTypes.CLOSABLE_BLOCK_OP; + public static int LITERAL_as = GroovyTokenTypes.LITERAL_as; + public static int LITERAL_if = GroovyTokenTypes.LITERAL_if; + public static int LITERAL_else = GroovyTokenTypes.LITERAL_else; + public static int LITERAL_for = GroovyTokenTypes.LITERAL_for; + public static int LITERAL_switch = GroovyTokenTypes.LITERAL_switch; + public static int LITERAL_while = GroovyTokenTypes.LITERAL_while; +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/activator/GroovyActivator.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/activator/GroovyActivator.java new file mode 100644 index 0000000000..83daf48a37 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/activator/GroovyActivator.java @@ -0,0 +1,113 @@ +/* + * Copyright 2009-2018 the original author or authors. + * + * Licensed 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.activator; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import java.io.IOException; +import java.net.URL; + +public class GroovyActivator extends Plugin { + + public static final String PLUGIN_ID = "org.codehaus.groovy"; + + public static final String GROOVY_JAR = "lib/groovy-2.6.0-indy.jar"; + + public static URL GROOVY_JAR_URL; + + private static GroovyActivator DEFAULT; + + public GroovyActivator() { + DEFAULT = this; + } + + public static GroovyActivator getDefault() { + return DEFAULT; + } + + public static void initialize() throws IOException { + Bundle bundle = getDefault().getBundle(); + URL entry = bundle.getEntry(GROOVY_JAR); + if (entry == null) { + throw new RuntimeException( + "Couldn't find '" + GROOVY_JAR + "' in bundle " + bundle.getSymbolicName() + " " + bundle.getVersion()); + } + GROOVY_JAR_URL = FileLocator.resolve(entry); + } + + //-------------------------------------------------------------------------- + + @Override + public void start(BundleContext context) throws Exception { + if (Boolean.getBoolean("greclipse.debug.trace_compiler_start")) { + System.out.println("------------"); + System.out.println("GRECLIPSE-1642: stack trace and other info as Groovy compiler starts"); + printBundleState("org.codehaus.groovy.eclipse.compilerResolver"); + printBundleState("org.eclipse.jdt.core"); + new Exception().printStackTrace(); + System.out.println("------------"); + } + + // enable InvokeDynamic support across the board + System.setProperty("groovy.target.indy", "true"); + + super.start(context); + try { + initialize(); + } catch (Exception e) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "Error starting Groovy plugin", e)); + } + } + + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + } + + private static void printBundleState(String id) { + Bundle bundle = Platform.getBundle(id); + if (bundle != null) { + int state = bundle.getState(); + String stateString = "UNKNOWN"; + switch (state) { + case Bundle.ACTIVE: + stateString = "ACTIVE"; + break; + case Bundle.RESOLVED: + stateString = "RESOLVED"; + break; + case Bundle.STOPPING: + stateString = "STOPPING"; + break; + case Bundle.INSTALLED: + stateString = "INSTALLED"; + break; + case Bundle.UNINSTALLED: + stateString = "UNINSTALLED"; + break; + } + System.out.println(id + " state: " + stateString); + } else { + System.out.println(id + " is not installed"); + } + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java new file mode 100644 index 0000000000..efb527f16c --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/AntlrParserPlugin.java @@ -0,0 +1,3936 @@ +/* + * 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.antlr; + +import groovyjarjarantlr.RecognitionException; +import groovyjarjarantlr.TokenStreamException; +import groovyjarjarantlr.TokenStreamRecognitionException; +import groovyjarjarantlr.collections.AST; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.antlr.parser.GroovyLexer; +import org.codehaus.groovy.antlr.parser.GroovyRecognizer; +import org.codehaus.groovy.antlr.parser.GroovyTokenTypes; +import org.codehaus.groovy.antlr.treewalker.CompositeVisitor; +import org.codehaus.groovy.antlr.treewalker.MindMapPrinter; +import org.codehaus.groovy.antlr.treewalker.NodeAsHTMLPrinter; +import org.codehaus.groovy.antlr.treewalker.PreOrderTraversal; +import org.codehaus.groovy.antlr.treewalker.SourceCodeTraversal; +import org.codehaus.groovy.antlr.treewalker.SourcePrinter; +import org.codehaus.groovy.antlr.treewalker.Visitor; +import org.codehaus.groovy.antlr.treewalker.VisitorAdapter; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.EnumConstantClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.ImmutableClassNode; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.MixinNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.PackageNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ArrayExpression; +import org.codehaus.groovy.ast.expr.AttributeExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; +import org.codehaus.groovy.ast.expr.EmptyExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ExpressionTransformer; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.MethodPointerExpression; +import org.codehaus.groovy.ast.expr.NamedArgumentListExpression; +import org.codehaus.groovy.ast.expr.NotExpression; +import org.codehaus.groovy.ast.expr.PostfixExpression; +import org.codehaus.groovy.ast.expr.PrefixExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.SpreadExpression; +import org.codehaus.groovy.ast.expr.SpreadMapExpression; +import org.codehaus.groovy.ast.expr.TernaryExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.UnaryMinusExpression; +import org.codehaus.groovy.ast.expr.UnaryPlusExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.AssertStatement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.SynchronizedStatement; +import org.codehaus.groovy.ast.stmt.ThrowStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.control.CompilationFailedException; +import org.codehaus.groovy.control.ParserPlugin; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.syntax.ASTHelper; +import org.codehaus.groovy.syntax.Numbers; +import org.codehaus.groovy.syntax.ParserException; +import org.codehaus.groovy.syntax.Reduction; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.Types; +import groovyjarjarasm.asm.Opcodes; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * A parser plugin which adapts the JSR Antlr Parser to the Groovy runtime + * + * @author James Strachan + */ +public class AntlrParserPlugin extends ASTHelper implements ParserPlugin, GroovyTokenTypes { + + private static class AnonymousInnerClassCarrier extends Expression { + ClassNode innerClass; + + public Expression transformExpression(ExpressionTransformer transformer) { + return null; + } + + @Override + public void setSourcePosition(final ASTNode node) { + super.setSourcePosition(node); + innerClass.setSourcePosition(node); + } + + @Override + public void setColumnNumber(final int columnNumber) { + super.setColumnNumber(columnNumber); + innerClass.setColumnNumber(columnNumber); + } + + @Override + public void setLineNumber(final int lineNumber) { + super.setLineNumber(lineNumber); + innerClass.setLineNumber(lineNumber); + } + + @Override + public void setLastColumnNumber(final int columnNumber) { + super.setLastColumnNumber(columnNumber); + innerClass.setLastColumnNumber(columnNumber); + } + + @Override + public void setLastLineNumber(final int lineNumber) { + super.setLastLineNumber(lineNumber); + innerClass.setLastLineNumber(lineNumber); + } + } + + protected AST ast; + private ClassNode classNode; + private MethodNode methodNode; + // GRECLIPSE private->protected + protected String[] tokenNames; + private int innerClassCounter = 1; + private boolean enumConstantBeingDef = false; + private boolean forStatementBeingDef = false; + private boolean annotationBeingDef = false; + private boolean firstParamIsVarArg = false; + private boolean firstParam = false; + // GRECLIPSE add + protected LocationSupport locations = LocationSupport.NO_LOCATIONS; + // GRECLIPSE end + + public /*final*/ Reduction parseCST(final SourceUnit sourceUnit, Reader reader) throws CompilationFailedException { + final SourceBuffer sourceBuffer = new SourceBuffer(); + transformCSTIntoAST(sourceUnit, reader, sourceBuffer); + processAST(); + return outputAST(sourceUnit, sourceBuffer); + } + + protected void transformCSTIntoAST(SourceUnit sourceUnit, Reader reader, SourceBuffer sourceBuffer) throws CompilationFailedException { + ast = null; + + setController(sourceUnit); + + // TODO find a way to inject any GroovyLexer/GroovyRecognizer + + UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(reader, sourceBuffer); + UnicodeLexerSharedInputState inputState = new UnicodeLexerSharedInputState(unicodeReader); + GroovyLexer lexer = new GroovyLexer(inputState); + unicodeReader.setLexer(lexer); + GroovyRecognizer parser = GroovyRecognizer.make(lexer); + parser.setSourceBuffer(sourceBuffer); + tokenNames = parser.getTokenNames(); + parser.setFilename(sourceUnit.getName()); + + // start parsing at the compilationUnit rule + try { + parser.compilationUnit(); + } + catch (TokenStreamRecognitionException tsre) { + RecognitionException e = tsre.recog; + SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn()); + se.setFatal(true); + sourceUnit.addError(se); + } + catch (RecognitionException e) { + SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn()); + se.setFatal(true); + sourceUnit.addError(se); + } + catch (TokenStreamException e) { + sourceUnit.addException(e); + } + + // GRECLIPSE add + configureLocationSupport(sourceBuffer); + // GRECLIPSE end + + ast = parser.getAST(); + } + + // GRECLIPSE add + protected void configureLocationSupport(SourceBuffer sourceBuffer) { + locations = sourceBuffer.getLocationSupport(); + } + // GRECLIPSE end + + protected void processAST() { + AntlrASTProcessor snippets = new AntlrASTProcessSnippets(); + ast = snippets.process(ast); + } + + public Reduction outputAST(final SourceUnit sourceUnit, final SourceBuffer sourceBuffer) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + outputASTInVariousFormsIfNeeded(sourceUnit, sourceBuffer); + return null; + } + }); + + return null; //new Reduction(Tpken.EOF); + } + + // GRECLIPSE: from private->protected + protected void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) { + // straight xstream output of AST + String formatProp = System.getProperty("ANTLR.AST".toLowerCase()); // uppercase to hide from jarjar + + if ("xml".equals(formatProp)) { + saveAsXML(sourceUnit.getName(), ast); + } + + // 'pretty printer' output of AST + if ("groovy".equals(formatProp)) { + try { + PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".pretty.groovy")); + Visitor visitor = new SourcePrinter(out, tokenNames); + AntlrASTProcessor treewalker = new SourceCodeTraversal(visitor); + treewalker.process(ast); + } catch (FileNotFoundException e) { + System.out.println("Cannot create " + sourceUnit.getName() + ".pretty.groovy"); + } + } + + // output AST in format suitable for opening in http://freemind.sourceforge.net + // which is a really nice way of seeing the AST, folding nodes etc + if ("mindmap".equals(formatProp)) { + try { + PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm")); + Visitor visitor = new MindMapPrinter(out, tokenNames); + AntlrASTProcessor treewalker = new PreOrderTraversal(visitor); + treewalker.process(ast); + } catch (FileNotFoundException e) { + System.out.println("Cannot create " + sourceUnit.getName() + ".mm"); + } + } + + // include original line/col info and source code on the mindmap output + if ("extendedMindmap".equals(formatProp)) { + try { + PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm")); + Visitor visitor = new MindMapPrinter(out, tokenNames, sourceBuffer); + AntlrASTProcessor treewalker = new PreOrderTraversal(visitor); + treewalker.process(ast); + } catch (FileNotFoundException e) { + System.out.println("Cannot create " + sourceUnit.getName() + ".mm"); + } + } + + // html output of AST + if ("html".equals(formatProp)) { + try { + PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".html")); + List v = new ArrayList(); + v.add(new NodeAsHTMLPrinter(out, tokenNames)); + v.add(new SourcePrinter(out, tokenNames)); + Visitor visitors = new CompositeVisitor(v); + AntlrASTProcessor treewalker = new SourceCodeTraversal(visitors); + treewalker.process(ast); + } catch (FileNotFoundException e) { + System.out.println("Cannot create " + sourceUnit.getName() + ".html"); + } + } + } + + private static void saveAsXML(String name, AST ast) { + // GRECLIPSE edit + //XStreamUtils.serialize(name+".antlr", ast); + // GRECLIPSE end + } + + public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException { + setClassLoader(classLoader); + makeModule(); + try { + // GRECLIPSE add + // just in case buildAST is called twice + innerClassCounter = 1; + // GRECLIPSE end + + convertGroovy(ast); + + // GRECLIPSE edit + //if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) { + // does it look broken (i.e. have we built a script for it containing rubbish) + boolean hasNoMethods = output.getMethods().isEmpty(); + if (hasNoMethods && sourceUnit.getErrorCollector().hasErrors() && looksBroken(output)) { + output.setEncounteredUnrecoverableError(true); + } + if (output.getStatementBlock().isEmpty() && hasNoMethods && output.getClasses().isEmpty()) { + if (ast == null && sourceUnit.getErrorCollector().hasErrors()) { + output.setEncounteredUnrecoverableError(true); + } + // GRECLIPSE end + output.addStatement(ReturnStatement.RETURN_NULL_OR_VOID); + } + + // set the script source position + + ClassNode scriptClassNode = output.getScriptClassDummy(); + if (scriptClassNode != null) { + List statements = output.getStatementBlock().getStatements(); + if (!statements.isEmpty()) { + Statement firstStatement = statements.get(0); + Statement lastStatement = statements.get(statements.size() - 1); + + scriptClassNode.setSourcePosition(firstStatement); + scriptClassNode.setLastColumnNumber(lastStatement.getLastColumnNumber()); + scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber()); + } + } + + // GRECLIPSE add + fixModuleNodeLocations(); + // GRECLIPSE end + } + catch (ASTRuntimeException e) { + throw new ASTParserException(e.getMessage() + ". File: " + sourceUnit.getName(), e); + } + // GRECLIPSE add + ast = null; + // GRECLIPSE end + return output; + } + + // GRECLIPSE add + private boolean looksBroken(ModuleNode moduleNode) { + List classes = moduleNode.getClasses(); + if (classes.size() != 1 || !classes.get(0).isScript()) { + return false; + } + BlockStatement statementBlock = moduleNode.getStatementBlock(); + if (statementBlock.isEmpty()) { + return true; + } + // Is it just a constant expression containing the word error? + // do we need to change it from ERROR to something more unlikely? + List statements = statementBlock.getStatements(); + if (statements != null && statements.size() == 1) { + Statement statement = statements.get(0); + if (statement instanceof ExpressionStatement) { + Expression expression = ((ExpressionStatement) statement).getExpression(); + if (expression instanceof ConstantExpression) { + // ERROR node set at unknownAST + if (expression.toString().equals("ConstantExpression[ERROR]")) { + return true; + } + } + } + } + return false; + } + // GRECLIPSE end + + /** + * Converts the Antlr AST to the Groovy AST + */ + protected void convertGroovy(AST node) { + while (node != null) { + int type = node.getType(); + switch (type) { + case PACKAGE_DEF: + packageDef(node); + break; + + case STATIC_IMPORT: + case IMPORT: + importDef(node); + break; + + case TRAIT_DEF: + case CLASS_DEF: + classDef(node); + break; + + case INTERFACE_DEF: + interfaceDef(node); + break; + + case METHOD_DEF: + methodDef(node); + break; + + case ENUM_DEF: + enumDef(node); + break; + + case ANNOTATION_DEF: + annotationDef(node); + break; + + default: { + Statement statement = statement(node); + output.addStatement(statement); + } + } + node = node.getNextSibling(); + } + } + + // Top level control structures + //------------------------------------------------------------------------- + + protected void packageDef(AST packageDef) { + List annotations = new ArrayList(); + AST node = packageDef.getFirstChild(); + if (isType(ANNOTATIONS, node)) { + processAnnotations(annotations, node); + node = node.getNextSibling(); + } + String name = qualifiedName(node); + // TODO should we check package node doesn't already exist? conflict? + PackageNode packageNode = setPackage(name, annotations); + // GRECLIPSE edit + //configureAST(packageNode, packageDef); + configureAST(packageNode, node); + // GRECLIPSE end + } + + protected void importDef(AST importNode) { + try { + // GROOVY-6094 + output.putNodeMetaData(ImportNode.class, ImportNode.class); + + boolean isStatic = importNode.getType() == STATIC_IMPORT; + List annotations = new ArrayList(); + + AST node = importNode.getFirstChild(); + if (isType(ANNOTATIONS, node)) { + processAnnotations(annotations, node); + node = node.getNextSibling(); + } + + String alias = null; + // GRECLIPSE add + AST aliasNode = null; + // GRECLIPSE end + if (isType(LITERAL_as, node)) { + //import is like "import Foo as Bar" + node = node.getFirstChild(); + /*GRECLIPSE AST*/ aliasNode = node.getNextSibling(); + alias = identifier(aliasNode); + } + + if (node.getNumberOfChildren() == 0) { + String name = identifier(node); + // import is like "import Foo" + ClassNode type = ClassHelper.make(name); + configureAST(type, importNode); + addImport(type, name, alias, annotations); + return; + } + + AST packageNode = node.getFirstChild(); + String packageName = qualifiedName(packageNode); + AST nameNode = packageNode.getNextSibling(); + if (isType(STAR, nameNode)) { + if (isStatic) { + // import is like "import static foo.Bar.*" + // packageName is actually a className in this case + ClassNode type = ClassHelper.make(packageName); + // GRECLIPSE edit + //configureAST(type, importNode); + //addStaticStarImport(type, packageName, annotations); + configureAST(type, packageNode); + addStaticStarImport(type, packageName, annotations); + ASTNode imp = output.getStaticStarImports().get(packageName); + if (type instanceof ImmutableClassNode) { + ClassExpression typeNode = new ClassExpression(type); + imp.setNodeMetaData(ClassExpression.class, typeNode); + configureAST(typeNode, packageNode); + } + configureAST(imp, importNode); + // GRECLIPSE end + } else { + // import is like "import foo.*" + addStarImport(packageName, annotations); + // GRECLIPSE add + // must configure sloc for import node + ASTNode imp = output.getStarImports().get(output.getStarImports().size() - 1); + configureAST(imp, importNode); + // GRECLIPSE end + } + + if (alias != null) throw new GroovyBugError( + "imports like 'import foo.* as Bar' are not " + + "supported and should be caught by the grammar"); + } else { + // GRECLIPSE add + ImportNode imp; + // GRECLIPSE end + String name = identifier(nameNode); + if (isStatic) { + // import is like "import static foo.Bar.method" + // packageName is really class name in this case + ClassNode type = ClassHelper.make(packageName); + // GRECLIPSE edit + //configureAST(type, importNode); + //addStaticImport(type, name, alias, annotations); + configureAST(type, packageNode); + addStaticImport(type, name, alias, annotations); + imp = output.getStaticImports().get(alias == null ? name : alias); + if (type instanceof ImmutableClassNode) { + ClassExpression typeNode = new ClassExpression(type); + imp.setNodeMetaData(ClassExpression.class, typeNode); + configureAST(typeNode, packageNode); + } + configureAST(imp, importNode); + ConstantExpression nameExpr = new ConstantExpression(name); + configureAST(nameExpr, nameNode); + imp.setFieldNameExpr(nameExpr); + // GRECLIPSE end + } else { + // import is like "import foo.Bar" + ClassNode type = ClassHelper.make(packageName + "." + name); + // GRECLIPSE edit + // sloc for importNode configured by the ModuleNode + configureAST(type, /*importNode*/nameNode); + // GRECLIPSE end + addImport(type, name, alias, annotations); + // GRECLIPSE add + // be more precise about the sloc for the import node + imp = output.getImport(alias == null ? name : alias); + if (type instanceof ImmutableClassNode) { + ClassExpression typeNode = new ClassExpression(type); + imp.setNodeMetaData(ClassExpression.class, typeNode); + configureAST(typeNode, nameNode); + } + configureAST(imp, importNode); + // GRECLIPSE end + } + // GRECLIPSE add + // configure alias ast + if (alias != null) { + ConstantExpression aliasExpr = new ConstantExpression(alias); + configureAST(aliasExpr, aliasNode); + imp.setAliasExpr(aliasExpr); + } + // GRECLIPSE end + } + } finally { + // we're using node metadata here in order to fix GROOVY-6094 + // without breaking external APIs + Object node = output.getNodeMetaData(ImportNode.class); + if (node!=null && node!=ImportNode.class) { + configureAST((ImportNode)node, importNode); + } + output.removeNodeMetaData(ImportNode.class); + } + } + + private void processAnnotations(List annotations, AST node) { + AST child = node.getFirstChild(); + while (child != null) { + if (isType(ANNOTATION, child)) + annotations.add(annotation(child)); + child = child.getNextSibling(); + } + } + + protected void annotationDef(AST classDef) { + List annotations = new ArrayList(); + AST node = classDef.getFirstChild(); + int modifiers = Opcodes.ACC_PUBLIC; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + checkNoInvalidModifier(classDef, "Annotation Definition", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized"); + node = node.getNextSibling(); + } + modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_ANNOTATION; + + String name = identifier(node); + // GRECLIPSE add + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumn()); + int nameEnd = locations.findOffset(groovySourceAST.getLineLast(), groovySourceAST.getColumnLast()) - 1; + // GRECLIPSE end + node = node.getNextSibling(); + ClassNode superClass = ClassHelper.OBJECT_TYPE; + + GenericsType[] genericsType = null; + if (isType(TYPE_PARAMETERS, node)) { + genericsType = makeGenericsType(node); + node = node.getNextSibling(); + } + + ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; + if (isType(EXTENDS_CLAUSE, node)) { + interfaces = interfaces(node); + node = node.getNextSibling(); + } + + boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); + modifiers &= ~Opcodes.ACC_SYNTHETIC; + classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null); + classNode.setSyntheticPublic(syntheticPublic); + classNode.addAnnotations(annotations); + classNode.setGenericsTypes(genericsType); + classNode.addInterface(ClassHelper.Annotation_TYPE); + // GRECLIPSE add + classNode.setNameStart(nameStart); + classNode.setNameEnd(nameEnd); + // GRECLIPSE end + configureAST(classNode, classDef); + + assertNodeType(OBJBLOCK, node); + objectBlock(node); + output.addClass(classNode); + classNode = null; + } + + protected void interfaceDef(AST classDef) { + int oldInnerClassCounter = innerClassCounter; + innerInterfaceDef(classDef); + classNode = null; + innerClassCounter = oldInnerClassCounter; + } + + protected void innerInterfaceDef(AST classDef) { + List annotations = new ArrayList(); + AST node = classDef.getFirstChild(); + int modifiers = Opcodes.ACC_PUBLIC; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + checkNoInvalidModifier(classDef, "Interface", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized"); + node = node.getNextSibling(); + } + modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE; + + String name = identifier(node); + // GRECLIPSE add + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumn()); + int nameEnd = locations.findOffset(groovySourceAST.getLineLast(), groovySourceAST.getColumnLast()) - 1; + // GRECLIPSE end + node = node.getNextSibling(); + ClassNode superClass = ClassHelper.OBJECT_TYPE; + + GenericsType[] genericsType = null; + if (isType(TYPE_PARAMETERS, node)) { + genericsType = makeGenericsType(node); + node = node.getNextSibling(); + } + + ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; + if (isType(EXTENDS_CLAUSE, node)) { + interfaces = interfaces(node); + node = node.getNextSibling(); + } + + ClassNode outerClass = classNode; + boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); + modifiers &= ~Opcodes.ACC_SYNTHETIC; + if (classNode != null) { + name = classNode.getNameWithoutPackage() + "$" + name; + String fullName = dot(classNode.getPackageName(), name); + classNode = new InnerClassNode(classNode, fullName, modifiers, superClass, interfaces, null); + } else { + classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null); + } + classNode.setSyntheticPublic(syntheticPublic); + classNode.addAnnotations(annotations); + classNode.setGenericsTypes(genericsType); + configureAST(classNode, classDef); + // GRECLIPSE add + classNode.setNameStart(nameStart); + classNode.setNameEnd(nameEnd); + // GRECLIPSE end + + int oldClassCount = innerClassCounter; + + assertNodeType(OBJBLOCK, node); + objectBlock(node); + output.addClass(classNode); + + classNode = outerClass; + innerClassCounter = oldClassCount; + } + + protected void classDef(AST classDef) { + int oldInnerClassCounter = innerClassCounter; + innerClassDef(classDef); + classNode = null; + innerClassCounter = oldInnerClassCounter; + } + + private ClassNode getClassOrScript(ClassNode node) { + if (node != null) return node; + return output.getScriptClassDummy(); + } + + protected Expression anonymousInnerClassDef(AST node) { + ClassNode oldNode = classNode; + ClassNode outerClass = getClassOrScript(oldNode); + String fullName = outerClass.getName() + '$' + innerClassCounter; + innerClassCounter++; + if (enumConstantBeingDef) { + classNode = new EnumConstantClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); + } else { + classNode = new InnerClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE); + } + ((InnerClassNode) classNode).setAnonymous(true); + classNode.setEnclosingMethod(methodNode); + + assertNodeType(OBJBLOCK, node); + objectBlock(node); + output.addClass(classNode); + AnonymousInnerClassCarrier ret = new AnonymousInnerClassCarrier(); + ret.innerClass = classNode; + // GRECLIPSE add + configureAST(classNode, node); + // GRECLIPSE end + classNode = oldNode; + + return ret; + } + + protected void innerClassDef(AST classDef) { + List annotations = new ArrayList(); + + if (isType(TRAIT_DEF, classDef)) { + annotations.add(new AnnotationNode(ClassHelper.make("groovy.transform.Trait"))); + } + + AST node = classDef.getFirstChild(); + int modifiers = Opcodes.ACC_PUBLIC; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + checkNoInvalidModifier(classDef, "Class", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized"); + node = node.getNextSibling(); + } + + String name = identifier(node); + // GRECLIPSE add + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumn()); + int nameEnd = nameStart + name.length() - 1; + // GRECLIPSE end + node = node.getNextSibling(); + + GenericsType[] genericsType = null; + if (isType(TYPE_PARAMETERS, node)) { + genericsType = makeGenericsType(node); + node = node.getNextSibling(); + } + + ClassNode superClass = null; + if (isType(EXTENDS_CLAUSE, node)) { + superClass = makeTypeWithArguments(node); + node = node.getNextSibling(); + } + + ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; + if (isType(IMPLEMENTS_CLAUSE, node)) { + interfaces = interfaces(node); + node = node.getNextSibling(); + } + + // TODO read mixins + MixinNode[] mixins = {}; + ClassNode outerClass = classNode; + boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); + modifiers &= ~Opcodes.ACC_SYNTHETIC; + if (classNode != null) { + name = classNode.getNameWithoutPackage() + "$" + name; + String fullName = dot(classNode.getPackageName(), name); + if (classNode.isInterface()) { + modifiers |= Opcodes.ACC_STATIC; + } + classNode = new InnerClassNode(classNode, fullName, modifiers, superClass, interfaces, mixins); + } else { + classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, mixins); + } + classNode.addAnnotations(annotations); + classNode.setGenericsTypes(genericsType); + classNode.setSyntheticPublic(syntheticPublic); + configureAST(classNode, classDef); + // GRECLIPSE add + classNode.setNameStart(nameStart); + classNode.setNameEnd(nameEnd); + // GRECLIPSE end + + // we put the class already in output to avoid the most inner classes + // will be used as first class later in the loader. The first class + // there determines what GCL#parseClass for example will return, so we + // have here to ensure it won't be the inner class + output.addClass(classNode); + + int oldClassCount = innerClassCounter; + + // GRECLIPSE add + // a null node means the classbody is missing but the parser recovered + // an error will already have been recorded against the file + if (node != null) { + // GRECLIPSE end + assertNodeType(OBJBLOCK, node); + objectBlock(node); + // GRECLIPSE add + } + // GRECLIPSE end + + classNode = outerClass; + innerClassCounter = oldClassCount; + } + + protected void objectBlock(AST objectBlock) { + for (AST node = objectBlock.getFirstChild(); node != null; node = node.getNextSibling()) { + int type = node.getType(); + switch (type) { + case OBJBLOCK: + objectBlock(node); + break; + + case ANNOTATION_FIELD_DEF: + case METHOD_DEF: + methodDef(node); + break; + + case CTOR_IDENT: + constructorDef(node); + break; + + case VARIABLE_DEF: + fieldDef(node); + break; + + case STATIC_INIT: + staticInit(node); + break; + + case INSTANCE_INIT: + objectInit(node); + break; + + case ENUM_DEF: + enumDef(node); + break; + + case ENUM_CONSTANT_DEF: + enumConstantDef(node); + break; + + case TRAIT_DEF: + case CLASS_DEF: + innerClassDef(node); + break; + + case INTERFACE_DEF: + innerInterfaceDef(node); + break; + + default: + unknownAST(node); + } + } + } + + protected void enumDef(AST enumNode) { + assertNodeType(ENUM_DEF, enumNode); + List annotations = new ArrayList(); + + AST node = enumNode.getFirstChild(); + int modifiers = Opcodes.ACC_PUBLIC; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + node = node.getNextSibling(); + } + + // GRECLIPSE add + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumn()); + int nameEnd = locations.findOffset(groovySourceAST.getLineLast(), groovySourceAST.getColumnLast()) - 1; + // GRECLIPSE end + + String name = identifier(node); + node = node.getNextSibling(); + + ClassNode[] interfaces = interfaces(node); + node = node.getNextSibling(); + + boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); + modifiers &= ~Opcodes.ACC_SYNTHETIC; + String enumName = (classNode != null ? name : dot(getPackageName(), name)); + ClassNode enumClass = EnumHelper.makeEnumNode(enumName, modifiers, interfaces, classNode); + enumClass.setSyntheticPublic(syntheticPublic); + ClassNode oldNode = classNode; + enumClass.addAnnotations(annotations); + classNode = enumClass; + // GRECLIPSE move + //configureAST(classNode, enumNode); + // GRECLIPSE end + assertNodeType(OBJBLOCK, node); + objectBlock(node); + // GRECLIPSE add + classNode.setNameStart(nameStart); + classNode.setNameEnd(nameEnd); + configureAST(classNode, enumNode); + // GRECLIPSE end + classNode = oldNode; + + output.addClass(enumClass); + } + + protected void enumConstantDef(AST node) { + enumConstantBeingDef = true; + assertNodeType(ENUM_CONSTANT_DEF, node); + List annotations = new ArrayList(); + AST element = node.getFirstChild(); + if (isType(ANNOTATIONS, element)) { + processAnnotations(annotations, element); + element = element.getNextSibling(); + } + String identifier = identifier(element); + // GRECLIPSE add + int savedLine = element.getLine(); + int savedColumn = element.getColumn(); + // GRECLIPSE end + Expression init = null; + element = element.getNextSibling(); + + if (element != null) { + init = expression(element); + ClassNode innerClass; + if (element.getNextSibling() == null) { + innerClass = getAnonymousInnerClassNode(init); + if (innerClass != null) { + init = null; + } + } else { + element = element.getNextSibling(); + Expression next = expression(element); + innerClass = getAnonymousInnerClassNode(next); + } + + if (innerClass != null) { + // we have to handle an enum constant with a class overriding + // a method in which case we need to configure the inner class + innerClass.setSuperClass(classNode.getPlainNodeReference()); + innerClass.setModifiers(classNode.getModifiers() | Opcodes.ACC_FINAL); + // we use a ClassExpression for transportation to EnumVisitor + Expression inner = new ClassExpression(innerClass); + if (init == null) { + ListExpression le = new ListExpression(); + le.addExpression(inner); + init = le; + } else { + if (init instanceof ListExpression) { + ((ListExpression) init).addExpression(inner); + } else { + ListExpression le = new ListExpression(); + le.addExpression(init); + le.addExpression(inner); + init = le; + } + } + // and remove the final modifier from classNode to allow the sub class + classNode.setModifiers(classNode.getModifiers() & ~Opcodes.ACC_FINAL); + } else if (isType(ELIST, element)) { + if (init instanceof ListExpression && !((ListExpression) init).isWrapped()) { + ListExpression le = new ListExpression(); + le.addExpression(init); + init = le; + } + } + } + // GRECLIPSE edit + //FieldNode enumField = EnumHelper.addEnumConstant(classNode, identifier, init); + ClassNode nonDeclaredTypeOfEnumValue = + ClassHelper.make(classNode.getName()); + nonDeclaredTypeOfEnumValue.setRedirect(classNode); + FieldNode enumField = EnumHelper.addEnumConstant(nonDeclaredTypeOfEnumValue, classNode, identifier, init, savedLine, savedColumn); + enumField.setNameStart(locations.findOffset(savedLine, savedColumn)); + enumField.setNameEnd(enumField.getNameStart() + identifier.length() - 1); + // GRECLIPSE end + enumField.addAnnotations(annotations); + configureAST(enumField, node); + enumConstantBeingDef = false; + } + + protected void throwsList(AST node, List list) { + String name; + if (isType(DOT, node)) { + name = qualifiedName(node); + } else { + name = identifier(node); + } + ClassNode exception = ClassHelper.make(name); + configureAST(exception, node); + list.add(exception); + AST next = node.getNextSibling(); + if (next != null) throwsList(next, list); + } + + protected void methodDef(AST methodDef) { + MethodNode oldNode = methodNode; + List annotations = new ArrayList(); + AST node = methodDef.getFirstChild(); + + GenericsType[] generics = null; + if (isType(TYPE_PARAMETERS, node)) { + generics = makeGenericsType(node); + node = node.getNextSibling(); + } + + int modifiers = Opcodes.ACC_PUBLIC; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + checkNoInvalidModifier(methodDef, "Method", modifiers, Opcodes.ACC_VOLATILE, "volatile"); + node = node.getNextSibling(); + } + // GRECLIPSE add + else { + modifiers |= Opcodes.ACC_SYNTHETIC; + } + // GRECLIPSE end + if (isAnInterface()) { + modifiers |= Opcodes.ACC_ABSTRACT; + } + + ClassNode returnType = null; + if (isType(TYPE, node)) { + returnType = makeTypeWithArguments(node); + node = node.getNextSibling(); + } + + String name = identifier(node); + if (classNode != null && !classNode.isAnnotationDefinition()) { + if (classNode.getNameWithoutPackage().equals(name)) { + if (isAnInterface()) { + throw new ASTRuntimeException(methodDef, "Constructor not permitted within an interface."); + } + throw new ASTRuntimeException(methodDef, "Invalid constructor format. Remove '" + returnType.getName() + + "' as the return type if you want a constructor, or use a different name if you want a method."); + } + } + // GRECLIPSE add + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumn()); + int nameEnd = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumnLast()) - 1; + // GRECLIPSE end + node = node.getNextSibling(); + + Parameter[] parameters = Parameter.EMPTY_ARRAY; + ClassNode[] exceptions = ClassNode.EMPTY_ARRAY; + + if (classNode == null || !classNode.isAnnotationDefinition()) { + + assertNodeType(PARAMETERS, node); + parameters = parameters(node); + if (parameters == null) parameters = Parameter.EMPTY_ARRAY; + node = node.getNextSibling(); + + if (isType(LITERAL_throws, node)) { + AST throwsNode = node.getFirstChild(); + List exceptionList = new ArrayList(); + throwsList(throwsNode, exceptionList); + exceptions = exceptionList.toArray(exceptions); + node = node.getNextSibling(); + } + } + + boolean hasAnnotationDefault = false; + Statement code = null; + boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); + modifiers &= ~Opcodes.ACC_SYNTHETIC; + methodNode = new MethodNode(name, modifiers, returnType, parameters, exceptions, code); + if ((modifiers & Opcodes.ACC_ABSTRACT) == 0) { + if (node == null) { + // GRECLIPSE edit + // throw new ASTRuntimeException(methodDef, "You defined a method without body. Try adding a body, or declare it abstract."); + //} + getController().addError(new SyntaxException( + "You defined a method without body. Try adding a body, or declare it abstract.", methodDef.getLine(), methodDef.getColumn())); + // create a fake node that can pretend to be the body + code = statementListNoChild(null, methodDef); + } else { + // GRECLIPSE end + assertNodeType(SLIST, node); + code = statementList(node); + // GRECLIPSE add + } + // GRECLIPSE end + } else if (node != null && classNode.isAnnotationDefinition()) { + code = statement(node); + hasAnnotationDefault = true; + } else if ((modifiers & Opcodes.ACC_ABSTRACT) > 0) { + if (node != null) { + throw new ASTRuntimeException(methodDef, "Abstract methods do not define a body."); + } + } + methodNode.setCode(code); + methodNode.addAnnotations(annotations); + methodNode.setGenericsTypes(generics); + methodNode.setAnnotationDefault(hasAnnotationDefault); + methodNode.setSyntheticPublic(syntheticPublic); + configureAST(methodNode, methodDef); + // GRECLIPSE add + methodNode.setNameStart(nameStart); + methodNode.setNameEnd(nameEnd); + // GRECLIPSE end + + if (classNode != null) { + classNode.addMethod(methodNode); + } else { + output.addMethod(methodNode); + } + methodNode = oldNode; + } + + private static void checkNoInvalidModifier(AST node, String nodeType, int modifiers, int modifier, String modifierText) { + if ((modifiers & modifier) != 0) { + throw new ASTRuntimeException(node, nodeType + " has an incorrect modifier '" + modifierText + "'."); + } + } + + private boolean isAnInterface() { + return classNode != null && (classNode.getModifiers() & Opcodes.ACC_INTERFACE) > 0; + } + + protected void staticInit(AST staticInit) { + BlockStatement code = (BlockStatement) statementList(staticInit); + classNode.addStaticInitializerStatements(code.getStatements(), false); + // GRECLIPSE add + MethodNode clinit = classNode.getDeclaredMethod("", Parameter.EMPTY_ARRAY); + if (clinit.getEnd() < 1) { // set source position for first initializer only + configureAST(clinit, staticInit); + } + // GRECLIPSE end + } + + protected void objectInit(AST init) { + BlockStatement code = (BlockStatement) statementList(init); + classNode.addObjectInitializerStatements(code); + } + + protected void constructorDef(AST constructorDef) { + List annotations = new ArrayList(); + AST node = constructorDef.getFirstChild(); + // GRECLIPSE add + // constructor name is not stored as an AST node + // instead grab the end of the Modifiers node and the start of the parameters node + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLineLast(), groovySourceAST.getColumnLast()); + // GRECLIPSE end + int modifiers = Opcodes.ACC_PUBLIC; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_STATIC, "static"); + checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_FINAL, "final"); + checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_ABSTRACT, "abstract"); + checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_NATIVE, "native"); + node = node.getNextSibling(); + } + // GRECLIPSE add + else { + modifiers |= Opcodes.ACC_SYNTHETIC; + } + // GRECLIPSE end + + assertNodeType(PARAMETERS, node); + Parameter[] parameters = parameters(node); + if (parameters == null) parameters = Parameter.EMPTY_ARRAY; + // GRECLIPSE add + int nameEnd = locations.findOffset(node.getLine(), node.getColumn()) - 2; + // GRECLIPSE end + node = node.getNextSibling(); + + ClassNode[] exceptions = ClassNode.EMPTY_ARRAY; + if (isType(LITERAL_throws, node)) { + AST throwsNode = node.getFirstChild(); + List exceptionList = new ArrayList(); + throwsList(throwsNode, exceptionList); + exceptions = exceptionList.toArray(exceptions); + node = node.getNextSibling(); + } + + assertNodeType(SLIST, node); + boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); + modifiers &= ~Opcodes.ACC_SYNTHETIC; + ConstructorNode constructorNode = classNode.addConstructor(modifiers, parameters, exceptions, null); + MethodNode oldMethod = methodNode; + methodNode = constructorNode; + Statement code = statementList(node); + methodNode = oldMethod; + constructorNode.setCode(code); + constructorNode.setSyntheticPublic(syntheticPublic); + constructorNode.addAnnotations(annotations); + configureAST(constructorNode, constructorDef); + // GRECLIPSE add + constructorNode.setNameStart(nameStart); + constructorNode.setNameEnd(nameEnd); + // GRECLIPSE end + } + + protected void fieldDef(AST fieldDef) { + List annotations = new ArrayList(); + AST node = fieldDef.getFirstChild(); + + int modifiers = 0; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + node = node.getNextSibling(); + } + // GRECLIPSE add + modifiers &= ~Opcodes.ACC_SYNTHETIC; + // GRECLIPSE end + if (classNode.isInterface()) { + modifiers |= Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; + if ((modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0) { + modifiers |= Opcodes.ACC_PUBLIC; + } + } + + ClassNode type = null; + if (isType(TYPE, node)) { + type = makeTypeWithArguments(node); + node = node.getNextSibling(); + } + + String name = identifier(node); + // GRECLIPSE add + GroovySourceAST groovySourceAST = (GroovySourceAST) node; + int nameStart = locations.findOffset(groovySourceAST.getLine(), groovySourceAST.getColumn()); + int nameEnd = nameStart + name.length() - 1; + // GRECLIPSE end + node = node.getNextSibling(); + + Expression initialValue = null; + if (node != null) { + assertNodeType(ASSIGN, node); + initialValue = expression(node.getFirstChild()); + } + + if (classNode.isInterface() && initialValue == null && type != null) { + initialValue = getDefaultValueForPrimitive(type); + } + + + FieldNode fieldNode = new FieldNode(name, modifiers, type, classNode, initialValue); + fieldNode.addAnnotations(annotations); + configureAST(fieldNode, fieldDef); + // GRECLIPSE add + fieldNode.setNameStart(nameStart); + fieldNode.setNameEnd(nameEnd); + // GRECLIPSE end + + if (!hasVisibility(modifiers)) { + // let's set the modifiers on the field + int fieldModifiers = 0; + int flags = Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE | Opcodes.ACC_FINAL; + + if (!hasVisibility(modifiers)) { + modifiers |= Opcodes.ACC_PUBLIC; + fieldModifiers |= Opcodes.ACC_PRIVATE; + } + + // let's pass along any other modifiers we need + fieldModifiers |= (modifiers & flags); + fieldNode.setModifiers(fieldModifiers); + fieldNode.setSynthetic(true); + + // in the case that there is already a field, we would + // like to use that field, instead of the default field + // for the property + FieldNode storedNode = classNode.getDeclaredField(fieldNode.getName()); + if (storedNode != null && !classNode.hasProperty(name)) { + fieldNode = storedNode; + // we remove it here, because addProperty will add it + // again and we want to avoid it showing up multiple + // times in the fields list. + classNode.getFields().remove(storedNode); + } + + PropertyNode propertyNode = new PropertyNode(fieldNode, modifiers, null, null); + configureAST(propertyNode, fieldDef); + classNode.addProperty(propertyNode); + } else { + fieldNode.setModifiers(modifiers); + // if there is a property of that name, then a field of that + // name already exists, which means this new field here should + // be used instead of the field the property originally has. + PropertyNode pn = classNode.getProperty(name); + if (pn != null && pn.getField().isSynthetic()) { + classNode.getFields().remove(pn.getField()); + pn.setField(fieldNode); + } + classNode.addField(fieldNode); + } + } + + public static Expression getDefaultValueForPrimitive(ClassNode type) { + if (type == ClassHelper.int_TYPE) { + return new ConstantExpression(0); + } + if (type == ClassHelper.long_TYPE) { + return new ConstantExpression(0L); + } + if (type == ClassHelper.double_TYPE) { + return new ConstantExpression(0.0); + } + if (type == ClassHelper.float_TYPE) { + return new ConstantExpression(0.0F); + } + if (type == ClassHelper.boolean_TYPE) { + return ConstantExpression.FALSE; + } + if (type == ClassHelper.short_TYPE) { + return new ConstantExpression((short) 0); + } + if (type == ClassHelper.byte_TYPE) { + return new ConstantExpression((byte) 0); + } + if (type == ClassHelper.char_TYPE) { + return new ConstantExpression((char) 0); + } + return null; + } + + protected ClassNode[] interfaces(AST node) { + List interfaceList = new ArrayList(); + for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) { + // GRECLIPSE edit + //interfaceList.add(makeTypeWithArguments(implementNode)); + ClassNode cn = makeTypeWithArguments(implementNode); + configureAST(cn, implementNode); + interfaceList.add(cn); + // GRECLIPSE end + } + ClassNode[] interfaces = ClassNode.EMPTY_ARRAY; + if (!interfaceList.isEmpty()) { + interfaces = new ClassNode[interfaceList.size()]; + interfaceList.toArray(interfaces); + } + return interfaces; + } + + protected Parameter[] parameters(AST parametersNode) { + AST node = parametersNode.getFirstChild(); + firstParam = false; + firstParamIsVarArg = false; + if (node == null) { + if (isType(IMPLICIT_PARAMETERS, parametersNode)) return Parameter.EMPTY_ARRAY; + return null; + } else { + List parameters = new ArrayList(); + AST firstParameterNode = null; + do { + firstParam = (firstParameterNode == null); + if (firstParameterNode == null) firstParameterNode = node; + parameters.add(parameter(node)); + node = node.getNextSibling(); + } + while (node != null); + + verifyParameters(parameters, firstParameterNode); + + Parameter[] answer = new Parameter[parameters.size()]; + parameters.toArray(answer); + return answer; + } + } + + private void verifyParameters(List parameters, AST firstParameterNode) { + if (parameters.size() <= 1) return; + + Parameter first = parameters.get(0); + if (firstParamIsVarArg) { + throw new ASTRuntimeException(firstParameterNode, "The var-arg parameter " + first.getName() + " must be the last parameter."); + } + } + + protected Parameter parameter(AST paramNode) { + List annotations = new ArrayList(); + boolean variableParameterDef = isType(VARIABLE_PARAMETER_DEF, paramNode); + AST node = paramNode.getFirstChild(); + + int modifiers = 0; + if (isType(MODIFIERS, node)) { + modifiers = modifiers(node, annotations, modifiers); + node = node.getNextSibling(); + } + + ClassNode type = ClassHelper.DYNAMIC_TYPE; + if (isType(TYPE, node)) { + type = makeTypeWithArguments(node); + // GROOVY edit + //if (variableParameterDef) type = type.makeArray(); + if (variableParameterDef) { + type = type.makeArray(); + configureAST(type, node); + } + // GROOVY end + node = node.getNextSibling(); + } + + String name = identifier(node); + // GRECLIPSE add + int nameStart = locations.findOffset(node.getLine(), node.getColumn()); + int nameEnd = nameStart + name.length(); + // GRECLIPSE end + node = node.getNextSibling(); + + VariableExpression leftExpression = new VariableExpression(name, type); + leftExpression.setModifiers(modifiers); + configureAST(leftExpression, paramNode); + + Parameter parameter = null; + if (node != null) { + assertNodeType(ASSIGN, node); + Expression rightExpression = expression(node.getFirstChild()); + if (isAnInterface()) { + throw new ASTRuntimeException(node, "Cannot specify default value for method parameter '" + name + " = " + rightExpression.getText() + "' inside an interface"); + } + parameter = new Parameter(type, name, rightExpression); + } else + parameter = new Parameter(type, name); + + if (firstParam) firstParamIsVarArg = variableParameterDef; + + configureAST(parameter, paramNode); + // GRECLIPSE add + parameter.setNameStart(nameStart); + parameter.setNameEnd(nameEnd); + // GRECLIPSE end + parameter.addAnnotations(annotations); + parameter.setModifiers(modifiers); + return parameter; + } + + protected int modifiers(AST modifierNode, List annotations, int defaultModifiers) { + assertNodeType(MODIFIERS, modifierNode); + + boolean access = false; + int answer = 0; + + for (AST node = modifierNode.getFirstChild(); node != null; node = node.getNextSibling()) { + int type = node.getType(); + switch (type) { + case STATIC_IMPORT: + // ignore + break; + + // annotations + case ANNOTATION: + annotations.add(annotation(node)); + break; + + // core access scope modifiers + case LITERAL_private: + answer = setModifierBit(node, answer, Opcodes.ACC_PRIVATE); + access = setAccessTrue(node, access); + break; + + case LITERAL_protected: + answer = setModifierBit(node, answer, Opcodes.ACC_PROTECTED); + access = setAccessTrue(node, access); + break; + + case LITERAL_public: + answer = setModifierBit(node, answer, Opcodes.ACC_PUBLIC); + access = setAccessTrue(node, access); + break; + + // other modifiers + case ABSTRACT: + answer = setModifierBit(node, answer, Opcodes.ACC_ABSTRACT); + break; + + case FINAL: + answer = setModifierBit(node, answer, Opcodes.ACC_FINAL); + break; + + case LITERAL_native: + answer = setModifierBit(node, answer, Opcodes.ACC_NATIVE); + break; + + case LITERAL_static: + answer = setModifierBit(node, answer, Opcodes.ACC_STATIC); + break; + + case STRICTFP: + answer = setModifierBit(node, answer, Opcodes.ACC_STRICT); + break; + + case LITERAL_synchronized: + answer = setModifierBit(node, answer, Opcodes.ACC_SYNCHRONIZED); + break; + + case LITERAL_transient: + answer = setModifierBit(node, answer, Opcodes.ACC_TRANSIENT); + break; + + case LITERAL_volatile: + answer = setModifierBit(node, answer, Opcodes.ACC_VOLATILE); + break; + + default: + unknownAST(node); + } + } + if (!access) { + answer |= defaultModifiers; + // ACC_SYNTHETIC isn't used here, use it as a special flag + if (defaultModifiers == Opcodes.ACC_PUBLIC) answer |= Opcodes.ACC_SYNTHETIC; + } + return answer; + } + + protected boolean setAccessTrue(AST node, boolean access) { + if (!access) { + return true; + } else { + throw new ASTRuntimeException(node, "Cannot specify modifier: " + node.getText() + " when access scope has already been defined"); + } + } + + protected int setModifierBit(AST node, int answer, int bit) { + if ((answer & bit) != 0) { + throw new ASTRuntimeException(node, "Cannot repeat modifier: " + node.getText()); + } + return answer | bit; + } + + protected AnnotationNode annotation(AST annotationNode) { + annotationBeingDef = true; + AST node = annotationNode.getFirstChild(); + String name = qualifiedName(node); + AnnotationNode annotatedNode = new AnnotationNode(ClassHelper.make(name)); + // GRECLIPSE edit + //configureAST(annotatedNode, annotationNode); + configureAnnotationAST(annotatedNode, annotationNode); + // GRECLIPSE end + while (true) { + node = node.getNextSibling(); + if (isType(ANNOTATION_MEMBER_VALUE_PAIR, node)) { + AST memberNode = node.getFirstChild(); + String param = identifier(memberNode); + Expression expression = expression(memberNode.getNextSibling()); + if (annotatedNode.getMember(param) != null) { + throw new ASTRuntimeException(memberNode, "Annotation member '" + param + "' has already been associated with a value"); + } + annotatedNode.setMember(param, expression); + } else { + break; + } + } + annotationBeingDef = false; + return annotatedNode; + } + + + // Statements + //------------------------------------------------------------------------- + + protected Statement statement(AST node) { + // GRECLIPSE add -- avoid NPEs on bad code + // NOTE: EmptyStatement.INSTANCE is immutable. + if (node == null) return new EmptyStatement(); + // GRECLIPSE end + Statement statement = null; + int type = node.getType(); + switch (type) { + case SLIST: + case LITERAL_finally: + statement = statementList(node); + break; + + case METHOD_CALL: + statement = methodCall(node); + break; + + case VARIABLE_DEF: + statement = variableDef(node); + break; + + case LABELED_STAT: + return labelledStatement(node); + + case LITERAL_assert: + statement = assertStatement(node); + break; + + case LITERAL_break: + statement = breakStatement(node); + break; + + case LITERAL_continue: + statement = continueStatement(node); + break; + + case LITERAL_if: + statement = ifStatement(node); + break; + + case LITERAL_for: + statement = forStatement(node); + break; + + case LITERAL_return: + statement = returnStatement(node); + break; + + case LITERAL_synchronized: + statement = synchronizedStatement(node); + break; + + case LITERAL_switch: + statement = switchStatement(node); + break; + + case LITERAL_try: + statement = tryStatement(node); + break; + + case LITERAL_throw: + statement = throwStatement(node); + break; + + case LITERAL_while: + statement = whileStatement(node); + break; + + default: + statement = new ExpressionStatement(expression(node)); + } + if (statement != null) { + configureAST(statement, node); + } + return statement; + } + + protected Statement statementList(AST code) { + BlockStatement block = siblingsToBlockStatement(code.getFirstChild()); + configureAST(block, code); + return block; + } + + protected Statement statementListNoChild(AST node, AST alternativeConfigureNode) { + BlockStatement block = siblingsToBlockStatement(node); + // alternativeConfigureNode is used only to set the source position + /* GRECLIPSE edit + if (node != null) { + configureAST(block, node); + } else { + configureAST(block, alternativeConfigureNode); + } + */ + if (alternativeConfigureNode != null) { + configureAST(block, alternativeConfigureNode); + } else if (node != null) { + configureAST(block, node); + } + // GRECLIPSE end + return block; + } + + private BlockStatement siblingsToBlockStatement(AST firstSiblingNode) { + BlockStatement block = new BlockStatement(); + for (AST node = firstSiblingNode; node != null; node = node.getNextSibling()) { + block.addStatement(statement(node)); + } + return block; + } + + protected Statement assertStatement(AST assertNode) { + AST node = assertNode.getFirstChild(); + BooleanExpression booleanExpression = booleanExpression(node); + Expression messageExpression = null; + + node = node.getNextSibling(); + if (node != null) { + messageExpression = expression(node); + } else { + messageExpression = ConstantExpression.NULL; + } + AssertStatement assertStatement = new AssertStatement(booleanExpression, messageExpression); + configureAST(assertStatement, assertNode); + return assertStatement; + } + + protected Statement breakStatement(AST node) { + BreakStatement breakStatement = new BreakStatement(label(node)); + configureAST(breakStatement, node); + return breakStatement; + } + + protected Statement continueStatement(AST node) { + ContinueStatement continueStatement = new ContinueStatement(label(node)); + configureAST(continueStatement, node); + return continueStatement; + } + + protected Statement forStatement(AST forNode) { + AST inNode = forNode.getFirstChild(); + Expression collectionExpression; + Parameter forParameter; + if (isType(CLOSURE_LIST, inNode)) { + forStatementBeingDef = true; + ClosureListExpression clist = closureListExpression(inNode); + forStatementBeingDef = false; + int size = clist.getExpressions().size(); + if (size != 3) { + throw new ASTRuntimeException(inNode, "3 expressions are required for the classic for loop, you gave " + size); + } + collectionExpression = clist; + forParameter = ForStatement.FOR_LOOP_DUMMY; + } else { + AST variableNode = inNode.getFirstChild(); + AST collectionNode = variableNode.getNextSibling(); + + ClassNode type = ClassHelper.OBJECT_TYPE; + if (isType(VARIABLE_DEF, variableNode)) { + AST node = variableNode.getFirstChild(); + // skip the final modifier if it's present + if (isType(MODIFIERS, node)) { + int modifiersMask = modifiers(node, new ArrayList(), 0); + // only final modifier allowed + if ((modifiersMask & ~Opcodes.ACC_FINAL) != 0) { + throw new ASTRuntimeException(node, "Only the 'final' modifier is allowed in front of the for loop variable."); + } + node = node.getNextSibling(); + } + type = makeTypeWithArguments(node); + + variableNode = node.getNextSibling(); + } + String variable = identifier(variableNode); + + collectionExpression = expression(collectionNode); + forParameter = new Parameter(type, variable); + configureAST(forParameter, variableNode); + // GRECLIPSE add + forParameter.setNameStart(forParameter.getStart()); + forParameter.setNameEnd(forParameter.getEnd()); + if (type.getStart() > 0) { + // set start of parameter node to start of parameter type + forParameter.setStart(type.getStart()); + forParameter.setLineNumber(type.getLineNumber()); + forParameter.setColumnNumber(type.getColumnNumber()); + } + // GRECLIPSE end + } + + final AST node = inNode.getNextSibling(); + Statement block; + if (isType(SEMI, node)) { + block = EmptyStatement.INSTANCE; + } else { + block = statement(node); + } + ForStatement forStatement = new ForStatement(forParameter, collectionExpression, block); + configureAST(forStatement, forNode); + return forStatement; + } + + protected Statement ifStatement(AST ifNode) { + AST node = ifNode.getFirstChild(); + assertNodeType(EXPR, node); + BooleanExpression booleanExpression = booleanExpression(node); + + node = node.getNextSibling(); + Statement ifBlock = statement(node); + + Statement elseBlock = EmptyStatement.INSTANCE; + // GRECLIPSE add + // coping with a missing 'then' block (can happen due to recovery) + if (node != null) { + // GRECLIPSE end + node = node.getNextSibling(); + if (node != null) { + elseBlock = statement(node); + } + // GRECLIPSE add + } + // GRECLIPSE end + + IfStatement ifStatement = new IfStatement(booleanExpression, ifBlock, elseBlock); + configureAST(ifStatement, ifNode); + return ifStatement; + } + + protected Statement labelledStatement(AST labelNode) { + AST node = labelNode.getFirstChild(); + String label = identifier(node); + Statement statement = statement(node.getNextSibling()); + statement.addStatementLabel(label); + return statement; + } + + protected Statement methodCall(AST code) { + Expression expression = methodCallExpression(code); + ExpressionStatement expressionStatement = new ExpressionStatement(expression); + configureAST(expressionStatement, code); + return expressionStatement; + } + + protected Expression declarationExpression(AST variableDef) { + AST node = variableDef.getFirstChild(); + ClassNode type = null; + List annotations = new ArrayList(); + int modifiers = 0; + if (isType(MODIFIERS, node)) { + // force check of modifier conflicts + modifiers = modifiers(node, annotations, 0); + node = node.getNextSibling(); + } + if (isType(TYPE, node)) { + type = makeTypeWithArguments(node); + node = node.getNextSibling(); + } + + Expression leftExpression; + Expression rightExpression = EmptyExpression.INSTANCE; + AST right; + + if (isType(ASSIGN, node)) { + node = node.getFirstChild(); + AST left = node.getFirstChild(); + ArgumentListExpression alist = new ArgumentListExpression(); + for (AST varDef = left; varDef != null; varDef = varDef.getNextSibling()) { + assertNodeType(VARIABLE_DEF, varDef); + DeclarationExpression de = (DeclarationExpression) declarationExpression(varDef); + alist.addExpression(de.getVariableExpression()); + } + leftExpression = alist; + right = node.getNextSibling(); + if (right != null) rightExpression = expression(right); + } else { + String name = identifier(node); + VariableExpression ve = new VariableExpression(name, type); + ve.setModifiers(modifiers); + leftExpression = ve; + + right = node.getNextSibling(); + if (right != null) { + assertNodeType(ASSIGN, right); + rightExpression = expression(right.getFirstChild()); + } + } + + configureAST(leftExpression, node); + + Token token = makeToken(Types.ASSIGN, variableDef); + DeclarationExpression expression = new DeclarationExpression(leftExpression, token, rightExpression); + expression.addAnnotations(annotations); + configureAST(expression, variableDef); + ExpressionStatement expressionStatement = new ExpressionStatement(expression); + configureAST(expressionStatement, variableDef); + return expression; + } + + protected Statement variableDef(AST variableDef) { + ExpressionStatement expressionStatement = new ExpressionStatement(declarationExpression(variableDef)); + configureAST(expressionStatement, variableDef); + return expressionStatement; + } + + protected Statement returnStatement(AST node) { + AST exprNode = node.getFirstChild(); + + // This will pick up incorrect sibling node if 'node' is a plain 'return' + // + //if (exprNode == null) { + // exprNode = node.getNextSibling(); + //} + Expression expression = exprNode == null ? ConstantExpression.NULL : expression(exprNode); + ReturnStatement returnStatement = new ReturnStatement(expression); + configureAST(returnStatement, node); + return returnStatement; + } + + protected Statement switchStatement(AST switchNode) { + AST node = switchNode.getFirstChild(); + Expression expression = expression(node); + Statement defaultStatement = EmptyStatement.INSTANCE; + + List list = new ArrayList(); + for (node = node.getNextSibling(); isType(CASE_GROUP, node); node = node.getNextSibling()) { + Statement tmpDefaultStatement; + AST child = node.getFirstChild(); + if (isType(LITERAL_case, child)) { + List cases = new LinkedList(); + // default statement can be grouped with previous case + tmpDefaultStatement = caseStatements(child, cases); + list.addAll(cases); + } else { + tmpDefaultStatement = statement(child.getNextSibling()); + } + if (tmpDefaultStatement != EmptyStatement.INSTANCE) { + if (defaultStatement == EmptyStatement.INSTANCE) { + defaultStatement = tmpDefaultStatement; + } else { + throw new ASTRuntimeException(switchNode, "The default case is already defined."); + } + } + } + if (node != null) { + unknownAST(node); + } + SwitchStatement switchStatement = new SwitchStatement(expression, list, defaultStatement); + configureAST(switchStatement, switchNode); + return switchStatement; + } + + protected Statement caseStatements(AST node, List cases) { + List expressions = new LinkedList(); + Statement statement = EmptyStatement.INSTANCE; + Statement defaultStatement = EmptyStatement.INSTANCE; + AST nextSibling = node; + do { + Expression expression = expression(nextSibling.getFirstChild()); + expressions.add(expression); + nextSibling = nextSibling.getNextSibling(); + } while (isType(LITERAL_case, nextSibling)); + if (nextSibling != null) { + if (isType(LITERAL_default, nextSibling)) { + defaultStatement = statement(nextSibling.getNextSibling()); + statement = EmptyStatement.INSTANCE; + } else { + statement = statement(nextSibling); + } + } + Iterator iterator = expressions.iterator(); + while (iterator.hasNext()) { + Expression expr = (Expression) iterator.next(); + Statement stmt; + if (iterator.hasNext()) { + stmt = new CaseStatement(expr, EmptyStatement.INSTANCE); + } else { + stmt = new CaseStatement(expr, statement); + } + configureAST(stmt, node); + cases.add(stmt); + } + return defaultStatement; + } + + protected Statement synchronizedStatement(AST syncNode) { + AST node = syncNode.getFirstChild(); + Expression expression = expression(node); + Statement code = statement(node.getNextSibling()); + SynchronizedStatement synchronizedStatement = new SynchronizedStatement(expression, code); + configureAST(synchronizedStatement, syncNode); + return synchronizedStatement; + } + + protected Statement throwStatement(AST node) { + AST expressionNode = node.getFirstChild(); + if (expressionNode == null) { + expressionNode = node.getNextSibling(); + } + if (expressionNode == null) { + throw new ASTRuntimeException(node, "No expression available"); + } + ThrowStatement throwStatement = new ThrowStatement(expression(expressionNode)); + configureAST(throwStatement, node); + return throwStatement; + } + + protected Statement tryStatement(AST tryStatementNode) { + AST tryNode = tryStatementNode.getFirstChild(); + Statement tryStatement = statement(tryNode); + Statement finallyStatement = EmptyStatement.INSTANCE; + AST node = tryNode.getNextSibling(); + + // let's do the catch nodes + List catches = new ArrayList(); + for (; node != null && isType(LITERAL_catch, node); node = node.getNextSibling()) { + final List catchStatements = catchStatement(node); + catches.addAll(catchStatements); + } + + if (isType(LITERAL_finally, node)) { + finallyStatement = statement(node); + node = node.getNextSibling(); + } + + if (finallyStatement instanceof EmptyStatement && catches.isEmpty()) { + throw new ASTRuntimeException(tryStatementNode, "A try statement must have at least one catch or finally block."); + } + + TryCatchStatement tryCatchStatement = new TryCatchStatement(tryStatement, finallyStatement); + configureAST(tryCatchStatement, tryStatementNode); + for (CatchStatement statement : catches) { + tryCatchStatement.addCatch(statement); + } + return tryCatchStatement; + } + + protected List catchStatement(AST catchNode) { + AST node = catchNode.getFirstChild(); + List catches = new LinkedList(); + Statement code = statement(node.getNextSibling()); + if (MULTICATCH == node.getType()) { + AST variableNode = node.getNextSibling(); + final AST multicatches = node.getFirstChild(); + if (multicatches.getType() != MULTICATCH_TYPES) { + // catch (e) + // catch (def e) + String variable = identifier(multicatches); + Parameter catchParameter = new Parameter(ClassHelper.DYNAMIC_TYPE, variable); + // GRECLIPSE add + configureAST(catchParameter, multicatches); + catchParameter.setNameEnd(catchParameter.getEnd()); + catchParameter.setNameStart(catchParameter.getEnd() - catchParameter.getName().length()); + // GRECLIPSE end + CatchStatement answer = new CatchStatement(catchParameter, code); + configureAST(answer, catchNode); + catches.add(answer); + } else { + // catch (Exception e) + // catch (Exception1 | Exception2 e) + AST exceptionNodes = multicatches.getFirstChild(); + String variable = identifier(multicatches.getNextSibling()); + while (exceptionNodes != null) { + ClassNode exceptionType = buildName(exceptionNodes); + Parameter catchParameter = new Parameter(exceptionType, variable); + // GRECLIPSE add + // a little tricky since there can be multi-catches and the + // multicatches node doesn't include sloc for parameter name + configureAST(catchParameter, multicatches); + GroovySourceAST paramAST = (GroovySourceAST) multicatches.getNextSibling(); + int lastLine = paramAST.getLineLast(); + catchParameter.setLastLineNumber(lastLine); + int lastCol = paramAST.getColumnLast(); + catchParameter.setLastColumnNumber(lastCol); + catchParameter.setEnd(locations.findOffset(lastLine, lastCol)); + catchParameter.setNameEnd(catchParameter.getEnd()); + catchParameter.setNameStart(catchParameter.getEnd() - catchParameter.getName().length()); + // GRECLIPSE end + CatchStatement answer = new CatchStatement(catchParameter, code); + configureAST(answer, catchNode); + catches.add(answer); + exceptionNodes = exceptionNodes.getNextSibling(); + } + } + } + return catches; + } + + protected Statement whileStatement(AST whileNode) { + AST node = whileNode.getFirstChild(); + assertNodeType(EXPR, node); + // TODO remove this once we support declarations in the while condition + if (isType(VARIABLE_DEF, node.getFirstChild())) { + throw new ASTRuntimeException(whileNode, + "While loop condition contains a declaration; this is currently unsupported."); + } + BooleanExpression booleanExpression = booleanExpression(node); + + node = node.getNextSibling(); + Statement block; + if (isType(SEMI, node)) { + block = EmptyStatement.INSTANCE; + } else { + block = statement(node); + } + WhileStatement whileStatement = new WhileStatement(booleanExpression, block); + configureAST(whileStatement, whileNode); + return whileStatement; + } + + + // Expressions + //------------------------------------------------------------------------- + + protected Expression expression(AST node) { + return expression(node, false); + } + + protected Expression expression(AST node, boolean convertToConstant) { + // GRECLIPSE add + // error recovery for missing brackets + // determine if this error handling strategy is the right one to use + if (node == null) { + return new ConstantExpression("ERROR"); + } + // GRECLIPSE end + Expression expression = expressionSwitch(node); + if (convertToConstant && expression instanceof VariableExpression) { + // a method name can never be a VariableExpression, so it must converted + // to a ConstantExpression then. This is needed as the expression + // method doesn't know we want a ConstantExpression instead of a + // VariableExpression + VariableExpression ve = (VariableExpression) expression; + if (!ve.isThisExpression() && !ve.isSuperExpression()) { + expression = new ConstantExpression(ve.getName()); + } + } + // GRECLIPSE add -- expressionSwitch > blockExpression can return NULL for an empty GString expression + if (expression != ConstantExpression.NULL) + // GRECLIPSE end + configureAST(expression, node); + return expression; + } + + protected Expression expressionSwitch(AST node) { + int type = node.getType(); + switch (type) { + case EXPR: + // GRECLIPSE edit + //return expression(node.getFirstChild()); + // binary expression may not have correct sloc, so try to set the correct one here + Expression expression = expression(node.getFirstChild()); + if (expression instanceof BinaryExpression) { + // binary expression with parens + configureAST(expression, node); + } + return expression; + // GRECLIPSE end + + case ELIST: + return expressionList(node); + + case SLIST: + return blockExpression(node); + + case CLOSABLE_BLOCK: + return closureExpression(node); + + case SUPER_CTOR_CALL: + return specialConstructorCallExpression(node, ClassNode.SUPER); + + case METHOD_CALL: + return methodCallExpression(node); + + case LITERAL_new: + return constructorCallExpression(node); + + case CTOR_CALL: + return specialConstructorCallExpression(node, ClassNode.THIS); + + case QUESTION: + case ELVIS_OPERATOR: + return ternaryExpression(node); + + case OPTIONAL_DOT: + case SPREAD_DOT: + case DOT: + return dotExpression(node); + + case IDENT: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_double: + case LITERAL_float: + case LITERAL_int: + case LITERAL_long: + case LITERAL_short: + case LITERAL_void: + case LITERAL_this: + case LITERAL_super: + return variableExpression(node); + + case LIST_CONSTRUCTOR: + return listExpression(node); + + case MAP_CONSTRUCTOR: + return mapExpression(node); + + case LABELED_ARG: + return mapEntryExpression(node); + + case SPREAD_ARG: + return spreadExpression(node); + + case SPREAD_MAP_ARG: + return spreadMapExpression(node); + + // commented out of groovy.g due to non determinisms + //case MEMBER_POINTER_DEFAULT: + // return defaultMethodPointerExpression(node); + + case MEMBER_POINTER: + return methodPointerExpression(node); + + case INDEX_OP: + return indexExpression(node); + + case LITERAL_instanceof: + return instanceofExpression(node); + + case LITERAL_as: + return asExpression(node); + + case TYPECAST: + return castExpression(node); + + // literals + + case LITERAL_true: + return literalExpression(node, Boolean.TRUE); + case LITERAL_false: + return literalExpression(node, Boolean.FALSE); + case LITERAL_null: + return literalExpression(node, null); + case STRING_LITERAL: + return literalExpression(node, node.getText()); + + case STRING_CONSTRUCTOR: + return gstring(node); + + case NUM_DOUBLE: + case NUM_FLOAT: + case NUM_BIG_DECIMAL: + return decimalExpression(node); + + case NUM_BIG_INT: + case NUM_INT: + case NUM_LONG: + return integerExpression(node); + + // Unary expressions + case LNOT: + NotExpression notExpression = new NotExpression(expression(node.getFirstChild())); + configureAST(notExpression, node); + return notExpression; + + case UNARY_MINUS: + return unaryMinusExpression(node); + + case BNOT: + BitwiseNegationExpression bitwiseNegationExpression = new BitwiseNegationExpression(expression(node.getFirstChild())); + configureAST(bitwiseNegationExpression, node); + return bitwiseNegationExpression; + + case UNARY_PLUS: + return unaryPlusExpression(node); + + // Prefix expressions + case INC: + return prefixExpression(node, Types.PLUS_PLUS); + + case DEC: + return prefixExpression(node, Types.MINUS_MINUS); + + // Postfix expressions + case POST_INC: + return postfixExpression(node, Types.PLUS_PLUS); + + case POST_DEC: + return postfixExpression(node, Types.MINUS_MINUS); + + + // Binary expressions + + case ASSIGN: + return binaryExpression(Types.ASSIGN, node); + + case EQUAL: + return binaryExpression(Types.COMPARE_EQUAL, node); + + case IDENTICAL: + return binaryExpression(Types.COMPARE_IDENTICAL, node); + + case NOT_EQUAL: + return binaryExpression(Types.COMPARE_NOT_EQUAL, node); + + case NOT_IDENTICAL: + return binaryExpression(Types.COMPARE_NOT_IDENTICAL, node); + + case COMPARE_TO: + return binaryExpression(Types.COMPARE_TO, node); + + case LE: + return binaryExpression(Types.COMPARE_LESS_THAN_EQUAL, node); + + case LT: + return binaryExpression(Types.COMPARE_LESS_THAN, node); + + case GT: + return binaryExpression(Types.COMPARE_GREATER_THAN, node); + + case GE: + return binaryExpression(Types.COMPARE_GREATER_THAN_EQUAL, node); + + /** + * TODO treble equal? + return binaryExpression(Types.COMPARE_IDENTICAL, node); + + case ???: + return binaryExpression(Types.LOGICAL_AND_EQUAL, node); + + case ???: + return binaryExpression(Types.LOGICAL_OR_EQUAL, node); + + */ + + case LAND: + return binaryExpression(Types.LOGICAL_AND, node); + + case LOR: + return binaryExpression(Types.LOGICAL_OR, node); + + case BAND: + return binaryExpression(Types.BITWISE_AND, node); + + case BAND_ASSIGN: + return binaryExpression(Types.BITWISE_AND_EQUAL, node); + + case BOR: + return binaryExpression(Types.BITWISE_OR, node); + + case BOR_ASSIGN: + return binaryExpression(Types.BITWISE_OR_EQUAL, node); + + case BXOR: + return binaryExpression(Types.BITWISE_XOR, node); + + case BXOR_ASSIGN: + return binaryExpression(Types.BITWISE_XOR_EQUAL, node); + + + case PLUS: + return binaryExpression(Types.PLUS, node); + + case PLUS_ASSIGN: + return binaryExpression(Types.PLUS_EQUAL, node); + + + case MINUS: + return binaryExpression(Types.MINUS, node); + + case MINUS_ASSIGN: + return binaryExpression(Types.MINUS_EQUAL, node); + + + case STAR: + return binaryExpression(Types.MULTIPLY, node); + + case STAR_ASSIGN: + return binaryExpression(Types.MULTIPLY_EQUAL, node); + + + case STAR_STAR: + return binaryExpression(Types.POWER, node); + + case STAR_STAR_ASSIGN: + return binaryExpression(Types.POWER_EQUAL, node); + + + case DIV: + return binaryExpression(Types.DIVIDE, node); + + case DIV_ASSIGN: + return binaryExpression(Types.DIVIDE_EQUAL, node); + + + case MOD: + return binaryExpression(Types.MOD, node); + + case MOD_ASSIGN: + return binaryExpression(Types.MOD_EQUAL, node); + + case SL: + return binaryExpression(Types.LEFT_SHIFT, node); + + case SL_ASSIGN: + return binaryExpression(Types.LEFT_SHIFT_EQUAL, node); + + case SR: + return binaryExpression(Types.RIGHT_SHIFT, node); + + case SR_ASSIGN: + return binaryExpression(Types.RIGHT_SHIFT_EQUAL, node); + + case BSR: + return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED, node); + + case BSR_ASSIGN: + return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED_EQUAL, node); + + case VARIABLE_DEF: + return declarationExpression(node); + + // Regex + case REGEX_FIND: + return binaryExpression(Types.FIND_REGEX, node); + + case REGEX_MATCH: + return binaryExpression(Types.MATCH_REGEX, node); + + + // Ranges + case RANGE_INCLUSIVE: + return rangeExpression(node, true); + + case RANGE_EXCLUSIVE: + return rangeExpression(node, false); + + case DYNAMIC_MEMBER: + return dynamicMemberExpression(node); + + case LITERAL_in: + return binaryExpression(Types.KEYWORD_IN, node); + + case ANNOTATION: + return new AnnotationConstantExpression(annotation(node)); + + case CLOSURE_LIST: + return closureListExpression(node); + + case LBRACK: + case LPAREN: + return tupleExpression(node); + + case OBJBLOCK: + return anonymousInnerClassDef(node); + + default: + // GRECLIPSE add + return + // GRECLIPSE end + unknownAST(node); + } + //return null; + } + + private TupleExpression tupleExpression(AST node) { + TupleExpression exp = new TupleExpression(); + configureAST(exp, node); + node = node.getFirstChild(); + while (node != null) { + assertNodeType(VARIABLE_DEF, node); + AST nameNode = node.getFirstChild().getNextSibling(); + VariableExpression varExp = new VariableExpression(nameNode.getText()); + configureAST(varExp, nameNode); + exp.addExpression(varExp); + node = node.getNextSibling(); + } + return exp; + } + + private ClosureListExpression closureListExpression(AST node) { + isClosureListExpressionAllowedHere(node); + AST exprNode = node.getFirstChild(); + List list = new LinkedList(); + while (exprNode != null) { + if (isType(EXPR, exprNode)) { + Expression expr = expression(exprNode); + configureAST(expr, exprNode); + list.add(expr); + } else { + assertNodeType(EMPTY_STAT, exprNode); + list.add(EmptyExpression.INSTANCE); + } + + exprNode = exprNode.getNextSibling(); + } + ClosureListExpression cle = new ClosureListExpression(list); + configureAST(cle, node); + return cle; + } + + private void isClosureListExpressionAllowedHere(AST node) { + if (!forStatementBeingDef) { + throw new ASTRuntimeException(node, + "Expression list of the form (a; b; c) is not supported in this context."); + } + } + + protected Expression dynamicMemberExpression(AST dynamicMemberNode) { + AST node = dynamicMemberNode.getFirstChild(); + return expression(node); + } + + protected Expression ternaryExpression(AST ternaryNode) { + AST node = ternaryNode.getFirstChild(); + Expression base = expression(node); + node = node.getNextSibling(); + Expression left = expression(node); + node = node.getNextSibling(); + Expression ret; + if (node == null) { + ret = new ElvisOperatorExpression(base, left); + } else { + Expression right = expression(node); + BooleanExpression booleanExpression = new BooleanExpression(base); + booleanExpression.setSourcePosition(base); + ret = new TernaryExpression(booleanExpression, left, right); + } + // GRECLIPSE edit -- sloc was for operator only; TODO include surrounding parentheses + //configureAST(ret, ternaryNode); + ret.setStart(base.getStart()); + ret.setLineNumber(base.getLineNumber()); + ret.setColumnNumber(base.getColumnNumber()); + Expression rhs = ((TernaryExpression) ret).getFalseExpression(); + ret.setEnd(rhs.getEnd()); + ret.setLastLineNumber(rhs.getLastLineNumber()); + ret.setLastColumnNumber(rhs.getLastColumnNumber()); + // GRECLIPSE end + return ret; + } + + protected Expression variableExpression(AST node) { + String text = node.getText(); + + // TODO we might wanna only try to resolve the name if we are + // on the left hand side of an expression or before a dot? + VariableExpression variableExpression = new VariableExpression(text); + configureAST(variableExpression, node); + return variableExpression; + } + + protected Expression literalExpression(AST node, Object value) { + ConstantExpression constantExpression = new ConstantExpression(value, value instanceof Boolean); + configureAST(constantExpression, node); + return constantExpression; + } + + protected Expression rangeExpression(AST rangeNode, boolean inclusive) { + AST node = rangeNode.getFirstChild(); + Expression left = expression(node); + Expression right = expression(node.getNextSibling()); + RangeExpression rangeExpression = new RangeExpression(left, right, inclusive); + configureAST(rangeExpression, rangeNode); + return rangeExpression; + } + + protected Expression spreadExpression(AST node) { + AST exprNode = node.getFirstChild(); + AST listNode = exprNode.getFirstChild(); + Expression right = expression(listNode); + SpreadExpression spreadExpression = new SpreadExpression(right); + configureAST(spreadExpression, node); + return spreadExpression; + } + + protected Expression spreadMapExpression(AST node) { + AST exprNode = node.getFirstChild(); + Expression expr = expression(exprNode); + SpreadMapExpression spreadMapExpression = new SpreadMapExpression(expr); + configureAST(spreadMapExpression, node); + return spreadMapExpression; + } + + protected Expression methodPointerExpression(AST node) { + AST exprNode = node.getFirstChild(); + Expression objectExpression = expression(exprNode); + AST mNode = exprNode.getNextSibling(); + Expression methodName; + if (isType(DYNAMIC_MEMBER, mNode)) { + methodName = expression(mNode); + } else { + methodName = new ConstantExpression(identifier(mNode)); + } + configureAST(methodName, mNode); + MethodPointerExpression methodPointerExpression = new MethodPointerExpression(objectExpression, methodName); + configureAST(methodPointerExpression, node); + return methodPointerExpression; + } + +/* commented out due to groovy.g non-determinisms + protected Expression defaultMethodPointerExpression(AST node) { + AST exprNode = node.getFirstChild(); + String methodName = exprNode.toString(); + MethodPointerExpression methodPointerExpression = new MethodPointerExpression(null, methodName); + configureAST(methodPointerExpression, node); + return methodPointerExpression; + } +*/ + + protected Expression listExpression(AST listNode) { + List expressions = new ArrayList(); + AST elist = listNode.getFirstChild(); + assertNodeType(ELIST, elist); + + for (AST node = elist.getFirstChild(); node != null; node = node.getNextSibling()) { + // check for stray labeled arguments: + switch (node.getType()) { + case LABELED_ARG: + assertNodeType(COMMA, node); + break; // helpful error? + case SPREAD_MAP_ARG: + assertNodeType(SPREAD_ARG, node); + break; // helpful error + } + expressions.add(expression(node)); + } + ListExpression listExpression = new ListExpression(expressions); + configureAST(listExpression, listNode); + return listExpression; + } + + /** + * Typically only used for map constructors I think? + */ + protected Expression mapExpression(AST mapNode) { + List expressions = new ArrayList(); + AST elist = mapNode.getFirstChild(); + if (elist != null) { // totally empty in the case of [:] + assertNodeType(ELIST, elist); + for (AST node = elist.getFirstChild(); node != null; node = node.getNextSibling()) { + switch (node.getType()) { + case LABELED_ARG: + case SPREAD_MAP_ARG: + break; // legal cases + case SPREAD_ARG: + assertNodeType(SPREAD_MAP_ARG, node); + break; // helpful error + default: + assertNodeType(LABELED_ARG, node); + break; // helpful error + } + expressions.add(mapEntryExpression(node)); + } + } + MapExpression mapExpression = new MapExpression(expressions); + configureAST(mapExpression, mapNode); + // GRECLIPSE add + // GRECLIPSE-768 check to see if the sloc is definitely wrong for empty map expressions + // if so, make an educated guess that the text looks like '[:]' + if (expressions.isEmpty() && mapExpression.getLength() <= 1) { + mapExpression.setEnd(mapExpression.getStart() + 3); + mapExpression.setLastColumnNumber(mapExpression.getColumnNumber() + 3); + } + // GRECLIPSE end + return mapExpression; + } + + protected MapEntryExpression mapEntryExpression(AST node) { + if (node.getType() == SPREAD_MAP_ARG) { + AST rightNode = node.getFirstChild(); + Expression keyExpression = spreadMapExpression(node); + Expression rightExpression = expression(rightNode); + MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, rightExpression); + configureAST(mapEntryExpression, node); + return mapEntryExpression; + } else { + AST keyNode = node.getFirstChild(); + Expression keyExpression = expression(keyNode); + AST valueNode = keyNode.getNextSibling(); + Expression valueExpression = expression(valueNode); + MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, valueExpression); + // GRECLIPSE add + // GRECLIPSE-768 MapEntryExpression have the slocs of the ':' only; fix that here. + mapEntryExpression.setStart(keyExpression.getStart()); + mapEntryExpression.setLineNumber(keyExpression.getLineNumber()); + mapEntryExpression.setColumnNumber(keyExpression.getColumnNumber()); + mapEntryExpression.setEnd(valueExpression.getEnd()); + mapEntryExpression.setLastLineNumber(valueExpression.getLastLineNumber()); + mapEntryExpression.setLastColumnNumber(valueExpression.getLastColumnNumber()); + // potentially re-set the sloc if the 'real' one is more correct + // GRECLIPSE end + configureAST(mapEntryExpression, node); + return mapEntryExpression; + } + } + + + protected Expression instanceofExpression(AST node) { + AST leftNode = node.getFirstChild(); + Expression leftExpression = expression(leftNode); + + AST rightNode = leftNode.getNextSibling(); + ClassNode type = buildName(rightNode); + assertTypeNotNull(type, rightNode); + + Expression rightExpression = new ClassExpression(type); + configureAST(rightExpression, rightNode); + BinaryExpression binaryExpression = new BinaryExpression(leftExpression, makeToken(Types.KEYWORD_INSTANCEOF, node), rightExpression); + configureAST(binaryExpression, node); + return binaryExpression; + } + + protected void assertTypeNotNull(ClassNode type, AST rightNode) { + if (type == null) { + throw new ASTRuntimeException(rightNode, "No type available for: " + qualifiedName(rightNode)); + } + } + + protected Expression asExpression(AST node) { + AST leftNode = node.getFirstChild(); + Expression leftExpression = expression(leftNode); + + AST rightNode = leftNode.getNextSibling(); + ClassNode type = makeTypeWithArguments(rightNode); + + // GRECLIPSE edit -- set the sloc from the start of the target and the end of the type + //return CastExpression.asExpression(type, leftExpression); + CastExpression asExpression = CastExpression.asExpression(type, leftExpression); + asExpression.setStart(leftExpression.getStart()); + asExpression.setLineNumber(leftExpression.getLineNumber()); + asExpression.setColumnNumber(leftExpression.getColumnNumber()); + int typeStart; + if (type.getEnd() > 0) { + typeStart = type.getStart(); + asExpression.setEnd(type.getEnd()); + asExpression.setLastLineNumber(type.getLastLineNumber()); + asExpression.setLastColumnNumber(type.getLastColumnNumber()); + } else { + SourceInfo typeNode = (SourceInfo) rightNode.getFirstChild(); + typeStart = locations.findOffset(typeNode.getLine(), typeNode.getColumn()); + asExpression.setEnd(locations.findOffset(typeNode.getLineLast(), typeNode.getColumnLast())); + asExpression.setLastLineNumber(typeNode.getLineLast()); + asExpression.setLastColumnNumber(typeNode.getColumnLast()); + } + // set the range of the type by itself + asExpression.setNameStart(typeStart); + asExpression.setNameEnd(asExpression.getEnd()); + return asExpression; + // GRECLIPSE end + } + + protected Expression castExpression(AST castNode) { + AST node = castNode.getFirstChild(); + ClassNode type = makeTypeWithArguments(node); + assertTypeNotNull(type, node); + + AST expressionNode = node.getNextSibling(); + Expression expression = expression(expressionNode); + + CastExpression castExpression = new CastExpression(type, expression); + configureAST(castExpression, castNode); + // GRECLIPSE add -- set the sloc to the end of the target + castExpression.setEnd(expression.getEnd()); + castExpression.setLastLineNumber(expression.getLastLineNumber()); + castExpression.setLastColumnNumber(expression.getLastColumnNumber()); + // set the range of the type by itself + if (type.getEnd() > 0) { + castExpression.setNameStart(type.getStart()); + castExpression.setNameEnd(type.getEnd()); + } else { + SourceInfo typeNode = (SourceInfo) node.getFirstChild(); + castExpression.setNameStart(locations.findOffset(typeNode.getLine(), typeNode.getColumn())); + castExpression.setNameEnd(locations.findOffset(typeNode.getLineLast(), typeNode.getColumnLast())); + } + // GRECLIPSE end + return castExpression; + } + + + protected Expression indexExpression(AST indexNode) { + AST bracket = indexNode.getFirstChild(); + AST leftNode = bracket.getNextSibling(); + Expression leftExpression = expression(leftNode); + + AST rightNode = leftNode.getNextSibling(); + Expression rightExpression = expression(rightNode); + // easier to massage here than in the grammar + if (rightExpression instanceof SpreadExpression) { + ListExpression wrapped = new ListExpression(); + wrapped.addExpression(rightExpression); + rightExpression = wrapped; + } + + BinaryExpression binaryExpression = new BinaryExpression(leftExpression, makeToken(Types.LEFT_SQUARE_BRACKET, bracket), rightExpression); + configureAST(binaryExpression, indexNode); + return binaryExpression; + } + + protected Expression binaryExpression(int type, AST node) { + Token token = makeToken(type, node); + + AST leftNode = node.getFirstChild(); + Expression leftExpression = expression(leftNode); + + AST rightNode = leftNode.getNextSibling(); + if (rightNode == null) { + return leftExpression; + } + + if (Types.ofType(type, Types.ASSIGNMENT_OPERATOR)) { + if (leftExpression instanceof VariableExpression || + leftExpression.getClass() == PropertyExpression.class || + leftExpression instanceof FieldExpression || + leftExpression instanceof AttributeExpression || + leftExpression instanceof DeclarationExpression || + leftExpression instanceof TupleExpression) { + // Do nothing. + } else if (leftExpression instanceof ConstantExpression) { + throw new ASTRuntimeException(node, "\n[" + ((ConstantExpression) leftExpression).getValue() + "] is a constant expression, but it should be a variable expression"); + } else if (leftExpression instanceof BinaryExpression) { + int lefttype = ((BinaryExpression) leftExpression).getOperation().getType(); + if (!Types.ofType(lefttype, Types.ASSIGNMENT_OPERATOR) && lefttype != Types.LEFT_SQUARE_BRACKET) { + throw new ASTRuntimeException(node, "\n" + ((BinaryExpression) leftExpression).getText() + " is a binary expression, but it should be a variable expression"); + } + } else if (leftExpression instanceof GStringExpression) { + throw new ASTRuntimeException(node, "\n\"" + ((GStringExpression) leftExpression).getText() + "\" is a GString expression, but it should be a variable expression"); + } else if (leftExpression instanceof MethodCallExpression) { + throw new ASTRuntimeException(node, "\n\"" + ((MethodCallExpression) leftExpression).getText() + "\" is a method call expression, but it should be a variable expression"); + } else if (leftExpression instanceof MapExpression) { + throw new ASTRuntimeException(node, "\n'" + ((MapExpression) leftExpression).getText() + "' is a map expression, but it should be a variable expression"); + } else { + throw new ASTRuntimeException(node, "\n" + leftExpression.getClass() + ", with its value '" + leftExpression.getText() + "', is a bad expression as the left hand side of an assignment operator"); + } + } + /*if (rightNode == null) { + throw new NullPointerException("No rightNode associated with binary expression"); + }*/ + Expression rightExpression = expression(rightNode); + BinaryExpression binaryExpression = new BinaryExpression(leftExpression, token, rightExpression); + // GRECLIPSE edit + //configureAST(binaryExpression, node); + // sloc for binary expressions was only the operator + // must include the left and right expressions + // unfortunately, this does not include any surrounding parens + binaryExpression.setStart(leftExpression.getStart()); + binaryExpression.setLineNumber(leftExpression.getLineNumber()); + binaryExpression.setColumnNumber(leftExpression.getColumnNumber()); + binaryExpression.setEnd(rightExpression.getEnd()); + binaryExpression.setLastLineNumber(rightExpression.getLastLineNumber()); + binaryExpression.setLastColumnNumber(rightExpression.getLastColumnNumber()); + // GRECLIPSE end + return binaryExpression; + } + + protected Expression prefixExpression(AST node, int token) { + Expression expression = expression(node.getFirstChild()); + PrefixExpression prefixExpression = new PrefixExpression(makeToken(token, node), expression); + configureAST(prefixExpression, node); + return prefixExpression; + } + + protected Expression postfixExpression(AST node, int token) { + Expression expression = expression(node.getFirstChild()); + PostfixExpression postfixExpression = new PostfixExpression(expression, makeToken(token, node)); + configureAST(postfixExpression, node); + return postfixExpression; + } + + protected BooleanExpression booleanExpression(AST node) { + BooleanExpression booleanExpression = new BooleanExpression(expression(node)); + configureAST(booleanExpression, node); + return booleanExpression; + } + + protected Expression dotExpression(AST node) { + // let's decide if this is a property invocation or a method call + AST leftNode = node.getFirstChild(); + if (leftNode != null) { + AST identifierNode = leftNode.getNextSibling(); + if (identifierNode != null) { + Expression leftExpression = expression(leftNode); + if (isType(SELECT_SLOT, identifierNode)) { + Expression field = expression(identifierNode.getFirstChild(), true); + AttributeExpression attributeExpression = new AttributeExpression(leftExpression, field, node.getType() != DOT); + if (node.getType() == SPREAD_DOT) { + attributeExpression.setSpreadSafe(true); + } + configureAST(attributeExpression, node); + return attributeExpression; + } + if (isType(SLIST, identifierNode)) { + Statement code = statementList(identifierNode); + ClosureExpression closureExpression = new ClosureExpression(Parameter.EMPTY_ARRAY, code); + configureAST(closureExpression, identifierNode); + final PropertyExpression propertyExpression = new PropertyExpression(leftExpression, closureExpression); + if (node.getType() == SPREAD_DOT) { + propertyExpression.setSpreadSafe(true); + } + configureAST(propertyExpression, node); + return propertyExpression; + } + Expression property = expression(identifierNode, true); + + + // A."this" assumes a VariableExpression can be used for "this" + // we correct that here into a ConstantExpression + if (property instanceof VariableExpression) { + VariableExpression ve = (VariableExpression) property; + property = new ConstantExpression(ve.getName()); + // GRECLIPSE add + property.setSourcePosition(ve); + // GRECLIPSE end + } + + PropertyExpression propertyExpression = new PropertyExpression(leftExpression, property, node.getType() != DOT); + if (node.getType() == SPREAD_DOT) { + propertyExpression.setSpreadSafe(true); + } + configureAST(propertyExpression, node); + return propertyExpression; + } + } + return methodCallExpression(node); + } + + protected Expression specialConstructorCallExpression(AST methodCallNode, ClassNode special) { + AST node = methodCallNode.getFirstChild(); + Expression arguments = arguments(node); + + ConstructorCallExpression expression = new ConstructorCallExpression(special, arguments); + // GRECLIPSE add + int keywordLength = (special == ClassNode.SUPER ? 5 : 4); + GroovySourceAST ctorCallNode = (GroovySourceAST) methodCallNode; + // locate the keyword relative to the method call expression; assume no spaces + ctorCallNode.setColumn(Math.max(1, ctorCallNode.getColumn() - keywordLength)); + // GRECLIPSE end + configureAST(expression, methodCallNode); + // GRECLIPSE add + expression.setNameStart(expression.getStart()); + expression.setNameEnd(expression.getStart() + keywordLength - 1); + // GRECLIPSE end + return expression; + } + + protected Expression methodCallExpression(AST methodCallNode) { + AST node = methodCallNode.getFirstChild(); + Expression objectExpression; + AST selector; + AST elist = node.getNextSibling(); + List typeArgumentList = null; + + boolean implicitThis = false; + boolean safe = isType(OPTIONAL_DOT, node); + boolean spreadSafe = isType(SPREAD_DOT, node); + if (isType(DOT, node) || safe || spreadSafe) { + AST objectNode = node.getFirstChild(); + objectExpression = expression(objectNode); + selector = objectNode.getNextSibling(); + } else { + implicitThis = true; + objectExpression = VariableExpression.THIS_EXPRESSION; + selector = node; + } + + if (isType(TYPE_ARGUMENTS, selector)) { + typeArgumentList = getTypeArgumentsList(selector); + selector = selector.getNextSibling(); + } + + Expression name = null; + if (isType(LITERAL_super, selector)) { + implicitThis = true; + name = new ConstantExpression("super"); + if (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()) { + objectExpression = VariableExpression.SUPER_EXPRESSION; + } + } else if (isPrimitiveTypeLiteral(selector)) { + throw new ASTRuntimeException(selector, "Primitive type literal: " + selector.getText() + + " cannot be used as a method name"); + } else if (isType(SELECT_SLOT, selector)) { + Expression field = expression(selector.getFirstChild(), true); + AttributeExpression attributeExpression = new AttributeExpression(objectExpression, field, node.getType() != DOT); + configureAST(attributeExpression, node); + Expression arguments = arguments(elist); + MethodCallExpression expression = new MethodCallExpression(attributeExpression, "call", arguments); + setTypeArgumentsOnMethodCallExpression(expression, typeArgumentList); + configureAST(expression, methodCallNode); + return expression; + } else if (!implicitThis || isType(DYNAMIC_MEMBER, selector) || isType(IDENT, selector) || + isType(STRING_CONSTRUCTOR, selector) || isType(STRING_LITERAL, selector)) { + name = expression(selector, true); + } else { + implicitThis = false; + name = new ConstantExpression("call"); + objectExpression = expression(selector, true); + } + + // if node text is found to be "super"/"this" when a method call is being processed, it is a + // call like this(..)/super(..) after the first statement, which shouldn't be allowed. GROOVY-2836 + if (selector.getText().equals("this") || selector.getText().equals("super")) { + if (!(annotationBeingDef && selector.getText().equals("super"))) { + throw new ASTRuntimeException(elist, "Constructor call must be the first statement in a constructor."); + } + } + + Expression arguments = arguments(elist); + MethodCallExpression expression = new MethodCallExpression(objectExpression, name, arguments); + expression.setSafe(safe); + expression.setSpreadSafe(spreadSafe); + expression.setImplicitThis(implicitThis); + setTypeArgumentsOnMethodCallExpression(expression, typeArgumentList); + Expression ret = expression; + //FIXME: do we really want this() to create a new object regardless + // the position.. for example not as first statement in a constructor + // this=first statement in constructor is handled by specialConstructorCallExpression + // we may have to add a check and remove this part of the code + if (implicitThis && "this".equals(expression.getMethodAsString())) { + ret = new ConstructorCallExpression(this.classNode, arguments); + } + // GRECLIPSE add + ret.setNameStart(name.getStart()); + ret.setNameEnd(name.getEnd() - 1); + // in the case of command expressions, the slocs are incorrect for the start of the method + if (!implicitThis && methodCallNode.getText().equals("")) { + ret.setStart(objectExpression.getStart()); + ret.setLineNumber(objectExpression.getLineNumber()); + ret.setColumnNumber(objectExpression.getColumnNumber()); + ret.setEnd(arguments.getEnd()); + ret.setLastLineNumber(arguments.getLastLineNumber()); + ret.setLastColumnNumber(arguments.getLastColumnNumber()); + } + // GRECLIPSE end + configureAST(ret, methodCallNode); + return ret; + } + + private static void setTypeArgumentsOnMethodCallExpression(MethodCallExpression expression, + List typeArgumentList) { + if (typeArgumentList != null && !typeArgumentList.isEmpty()) { + expression.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); + } + } + + protected Expression constructorCallExpression(AST node) { + AST constructorCallNode = node; + ClassNode type = makeTypeWithArguments(constructorCallNode); + + if (isType(CTOR_CALL, node) || isType(LITERAL_new, node)) { + node = node.getFirstChild(); + } + // GRECLIPSE add + // not quite ideal -- a null node is a sign of a new call without the type being specified + // (it is a syntax error); setting up with Object here prevents multiple downstream issues + if (node == null) { + return new ConstructorCallExpression(ClassHelper.OBJECT_TYPE, new ArgumentListExpression()); + } + // GRECLIPSE end + + AST elist = node.getNextSibling(); + + if (elist == null && isType(ELIST, node)) { + elist = node; + if ("(".equals(type.getName())) { + type = classNode; + } + } + + if (isType(ARRAY_DECLARATOR, elist)) { + AST expressionNode = elist.getFirstChild(); + if (expressionNode == null) { + throw new ASTRuntimeException(elist, "No expression for the array constructor call"); + } + List size = arraySizeExpression(expressionNode); + ArrayExpression arrayExpression = new ArrayExpression(type, null, size); + configureAST(arrayExpression, constructorCallNode); + return arrayExpression; + } + Expression arguments = arguments(elist); + ClassNode innerClass = getAnonymousInnerClassNode(arguments); + ConstructorCallExpression ret = new ConstructorCallExpression(type, arguments); + if (innerClass != null) { + ret.setType(innerClass); + ret.setUsingAnonymousInnerClass(true); + innerClass.setUnresolvedSuperClass(type); + // GRECLIPSE add + innerClass.setNameStart(type.getStart()); + innerClass.setNameEnd(type.getEnd() - 1); + // GRECLIPSE end + } + + configureAST(ret, constructorCallNode); + // GRECLIPSE add + ret.setNameStart(type.getStart()); + ret.setNameEnd(type.getEnd() - 1); + // GRECLIPSE end + return ret; + } + + private static ClassNode getAnonymousInnerClassNode(Expression arguments) { + if (arguments instanceof TupleExpression) { + TupleExpression te = (TupleExpression) arguments; + List expressions = te.getExpressions(); + if (expressions.isEmpty()) return null; + Expression last = expressions.remove(expressions.size() - 1); + if (last instanceof AnonymousInnerClassCarrier) { + AnonymousInnerClassCarrier carrier = (AnonymousInnerClassCarrier) last; + return carrier.innerClass; + } else { + expressions.add(last); + } + } else if (arguments instanceof AnonymousInnerClassCarrier) { + AnonymousInnerClassCarrier carrier = (AnonymousInnerClassCarrier) arguments; + return carrier.innerClass; + } + return null; + } + + protected List arraySizeExpression(AST node) { + List list; + Expression size = null; + if (isType(ARRAY_DECLARATOR, node)) { + AST right = node.getNextSibling(); + if (right != null) { + size = expression(right); + } else { + size = ConstantExpression.EMPTY_EXPRESSION; + } + AST child = node.getFirstChild(); + if (child == null) { + throw new ASTRuntimeException(node, "No expression for the array constructor call"); + } + list = arraySizeExpression(child); + } else { + size = expression(node); + list = new ArrayList(); + } + list.add(size); + return list; + } + + protected Expression enumArguments(AST elist) { + List expressionList = new ArrayList(); + for (AST node = elist; node != null; node = node.getNextSibling()) { + Expression expression = expression(node); + expressionList.add(expression); + } + ArgumentListExpression argumentListExpression = new ArgumentListExpression(expressionList); + configureAST(argumentListExpression, elist); + return argumentListExpression; + } + + protected Expression arguments(AST elist) { + List expressionList = new ArrayList(); + // FIXME: all labeled arguments should follow any unlabeled arguments + boolean namedArguments = false; + for (AST node = elist; node != null; node = node.getNextSibling()) { + if (isType(ELIST, node)) { + for (AST child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + namedArguments |= addArgumentExpression(child, expressionList); + } + } else { + namedArguments |= addArgumentExpression(node, expressionList); + } + } + if (namedArguments) { + if (!expressionList.isEmpty()) { + // let's remove any non-MapEntryExpression instances + // such as if the last expression is a ClosureExpression + // so let's wrap the named method calls in a Map expression + List argumentList = new ArrayList(); + for (Object next : expressionList) { + Expression expression = (Expression) next; + if (!(expression instanceof MapEntryExpression)) { + argumentList.add(expression); + } + } + if (!argumentList.isEmpty()) { + expressionList.removeAll(argumentList); + checkDuplicateNamedParams(elist, expressionList); + MapExpression mapExpression = new MapExpression(expressionList); + configureAST(mapExpression, elist); + argumentList.add(0, mapExpression); + ArgumentListExpression argumentListExpression = new ArgumentListExpression(argumentList); + configureAST(argumentListExpression, elist); + return argumentListExpression; + } + } + checkDuplicateNamedParams(elist, expressionList); + NamedArgumentListExpression namedArgumentListExpression = new NamedArgumentListExpression(expressionList); + configureAST(namedArgumentListExpression, elist); + return namedArgumentListExpression; + } else { + ArgumentListExpression argumentListExpression = new ArgumentListExpression(expressionList); + // GRECLIPSE add + // For an unfinished declaration 'new Foo' where the parentheses are missing an error + // will be raised but recovery should allow for that; here that incorrect declaration + // manifests as a null elist + if (elist != null) + // GRECLIPSE end + configureAST(argumentListExpression, elist); + return argumentListExpression; + } + } + + private static void checkDuplicateNamedParams(AST elist, List expressionList) { + if (expressionList.isEmpty()) return; + + Set namedArgumentNames = new HashSet(); + for (Object expression : expressionList) { + MapEntryExpression meExp = (MapEntryExpression) expression; + if (meExp.getKeyExpression() instanceof ConstantExpression) { + String argName = meExp.getKeyExpression().getText(); + if (!namedArgumentNames.contains(argName)) { + namedArgumentNames.add(argName); + } else { + throw new ASTRuntimeException(elist, "Duplicate named parameter '" + argName + + "' found."); + } + } + } + } + + protected boolean addArgumentExpression(AST node, List expressionList) { + if (node.getType() == SPREAD_MAP_ARG) { + AST rightNode = node.getFirstChild(); + Expression keyExpression = spreadMapExpression(node); + Expression rightExpression = expression(rightNode); + MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, rightExpression); + expressionList.add(mapEntryExpression); + return true; + } else { + Expression expression = expression(node); + expressionList.add(expression); + return expression instanceof MapEntryExpression; + } + } + + protected Expression expressionList(AST node) { + List expressionList = new ArrayList(); + for (AST child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + expressionList.add(expression(child)); + } + if (expressionList.size() == 1) { + return expressionList.get(0); + } else { + ListExpression listExpression = new ListExpression(expressionList); + listExpression.setWrapped(true); + configureAST(listExpression, node); + return listExpression; + } + } + + protected ClosureExpression closureExpression(AST node) { + AST paramNode = node.getFirstChild(); + Parameter[] parameters = null; + AST codeNode = paramNode; + if (isType(PARAMETERS, paramNode) || isType(IMPLICIT_PARAMETERS, paramNode)) { + parameters = parameters(paramNode); + codeNode = paramNode.getNextSibling(); + } + Statement code = statementListNoChild(codeNode, node); + ClosureExpression closureExpression = new ClosureExpression(parameters, code); + configureAST(closureExpression, node); + return closureExpression; + } + + protected Expression blockExpression(AST node) { + AST codeNode = node.getFirstChild(); + if (codeNode == null) return ConstantExpression.NULL; + if (codeNode.getType() == EXPR && codeNode.getNextSibling() == null) { + // Simplify common case of {expr} to expr. + return expression(codeNode); + } + Parameter[] parameters = Parameter.EMPTY_ARRAY; + Statement code = statementListNoChild(codeNode, node); + ClosureExpression closureExpression = new ClosureExpression(parameters, code); + configureAST(closureExpression, node); + // Call it immediately. + String callName = "call"; + Expression noArguments = new ArgumentListExpression(); + MethodCallExpression call = new MethodCallExpression(closureExpression, callName, noArguments); + configureAST(call, node); + return call; + } + + protected Expression unaryMinusExpression(AST unaryMinusExpr) { + AST node = unaryMinusExpr.getFirstChild(); + + // if we are a number literal then let's just parse it + // as the negation operator on MIN_INT causes rounding to a long + String text = node.getText(); + switch (node.getType()) { + case NUM_DOUBLE: + case NUM_FLOAT: + case NUM_BIG_DECIMAL: + ConstantExpression constantExpression = new ConstantExpression(Numbers.parseDecimal("-" + text)); + configureAST(constantExpression, unaryMinusExpr); + return constantExpression; + + case NUM_BIG_INT: + case NUM_INT: + case NUM_LONG: + ConstantExpression constantLongExpression = new ConstantExpression(Numbers.parseInteger(unaryMinusExpr,"-" + text)); + configureAST(constantLongExpression, unaryMinusExpr); + return constantLongExpression; + + default: + UnaryMinusExpression unaryMinusExpression = new UnaryMinusExpression(expression(node)); + configureAST(unaryMinusExpression, unaryMinusExpr); + return unaryMinusExpression; + } + } + + protected Expression unaryPlusExpression(AST unaryPlusExpr) { + AST node = unaryPlusExpr.getFirstChild(); + switch (node.getType()) { + case NUM_DOUBLE: + case NUM_FLOAT: + case NUM_BIG_DECIMAL: + case NUM_BIG_INT: + case NUM_INT: + case NUM_LONG: + return expression(node); + + default: + UnaryPlusExpression unaryPlusExpression = new UnaryPlusExpression(expression(node)); + configureAST(unaryPlusExpression, unaryPlusExpr); + return unaryPlusExpression; + } + } + + protected ConstantExpression decimalExpression(AST node) { + String text = node.getText(); + Object number = Numbers.parseDecimal(text); + ConstantExpression constantExpression = new ConstantExpression(number, + number instanceof Double || number instanceof Float); + configureAST(constantExpression, node); + return constantExpression; + } + + protected ConstantExpression integerExpression(AST node) { + String text = node.getText(); + Object number = Numbers.parseInteger(node, text); + boolean keepPrimitive = number instanceof Integer || number instanceof Long; + ConstantExpression constantExpression = new ConstantExpression(number, keepPrimitive); + configureAST(constantExpression, node); + return constantExpression; + } + + protected Expression gstring(AST gstringNode) { + List strings = new ArrayList(); + List values = new ArrayList(); + + StringBuilder buffer = new StringBuilder(); + + boolean isPrevString = false; + + for (AST node = gstringNode.getFirstChild(); node != null; node = node.getNextSibling()) { + int type = node.getType(); + String text = null; + switch (type) { + + case STRING_LITERAL: + if (isPrevString) assertNodeType(IDENT, node); // parser bug + isPrevString = true; + text = node.getText(); + ConstantExpression constantExpression = new ConstantExpression(text); + configureAST(constantExpression, node); + strings.add(constantExpression); + buffer.append(text); + break; + + default: { + if (!isPrevString) assertNodeType(IDENT, node); // parser bug + isPrevString = false; + Expression expression = expression(node); + values.add(expression); + buffer.append("$"); + buffer.append(expression.getText()); + } + break; + } + } + GStringExpression gStringExpression = new GStringExpression(buffer.toString(), strings, values); + configureAST(gStringExpression, gstringNode); + return gStringExpression; + } + + protected ClassNode type(AST typeNode) { + // TODO intern types? + // TODO configureAST(...) + return buildName(typeNode.getFirstChild()); + } + + public static String qualifiedName(AST qualifiedNameNode) { + if (isType(IDENT, qualifiedNameNode)) { + return qualifiedNameNode.getText(); + } + if (isType(DOT, qualifiedNameNode)) { + AST node = qualifiedNameNode.getFirstChild(); + StringBuilder buffer = new StringBuilder(); + boolean first = true; + + while (node != null && !isType(TYPE_ARGUMENTS, node)) { + if (first) { + first = false; + } else { + buffer.append("."); + } + buffer.append(qualifiedName(node)); + node = node.getNextSibling(); + } + return buffer.toString(); + } else { + return qualifiedNameNode.getText(); + } + } + + private int getBoundType(AST node) { + if (node == null) return -1; + if (isType(TYPE_UPPER_BOUNDS, node)) return TYPE_UPPER_BOUNDS; + if (isType(TYPE_LOWER_BOUNDS, node)) return TYPE_LOWER_BOUNDS; + throw new ASTRuntimeException(node, + "Unexpected node type: " + getTokenName(node) + + " found when expecting type: " + getTokenName(TYPE_UPPER_BOUNDS) + + " or type: " + getTokenName(TYPE_LOWER_BOUNDS)); + } + + private GenericsType makeGenericsArgumentType(AST typeArgument) { + GenericsType gt; + AST rootNode = typeArgument.getFirstChild(); + if (isType(WILDCARD_TYPE, rootNode)) { + ClassNode base = ClassHelper.makeWithoutCaching("?"); + if (rootNode.getNextSibling() != null) { + int boundType = getBoundType(rootNode.getNextSibling()); + ClassNode[] gts = makeGenericsBounds(rootNode, boundType); + if (boundType == TYPE_UPPER_BOUNDS) { + gt = new GenericsType(base, gts, null); + } else { + gt = new GenericsType(base, null, gts[0]); + } + } else { + gt = new GenericsType(base, null, null); + } + gt.setName("?"); + gt.setWildcard(true); + } else { + ClassNode argument = makeTypeWithArguments(rootNode); + gt = new GenericsType(argument); + } + configureAST(gt, typeArgument); + return gt; + } + + protected ClassNode makeTypeWithArguments(AST rootNode) { + ClassNode basicType = makeType(rootNode); + AST node = rootNode.getFirstChild(); + if (node == null || isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) return basicType; + + if (!isType(DOT, node)) { + node = node.getFirstChild(); + if (node == null) return basicType; + return addTypeArguments(basicType, node); + } else { + node = node.getFirstChild(); + while (node != null && !isType(TYPE_ARGUMENTS, node)) + node = node.getNextSibling(); + return node == null ? basicType : addTypeArguments(basicType, node); + } + } + + private ClassNode addTypeArguments(ClassNode basicType, AST node) { + List typeArgumentList = getTypeArgumentsList(node); + // a 0-length type argument list means we face the diamond operator + basicType.setGenericsTypes(typeArgumentList.toArray(new GenericsType[typeArgumentList.size()])); + // GRECLIPSE add + // super type source locations is not right, so set them here + // GRECLIPSE: What to do about generics? + //configureAST(basicType, rootNode); + // GRECLIPSE end + return basicType; + } + + private List getTypeArgumentsList(AST node) { + assertNodeType(TYPE_ARGUMENTS, node); + List typeArgumentList = new LinkedList(); + AST typeArgument = node.getFirstChild(); + + while (typeArgument != null) { + assertNodeType(TYPE_ARGUMENT, typeArgument); + GenericsType gt = makeGenericsArgumentType(typeArgument); + typeArgumentList.add(gt); + typeArgument = typeArgument.getNextSibling(); + } + return typeArgumentList; + } + + private ClassNode[] makeGenericsBounds(AST rn, int boundType) { + AST boundsRoot = rn.getNextSibling(); + if (boundsRoot == null) return null; + assertNodeType(boundType, boundsRoot); + LinkedList bounds = new LinkedList(); + for (AST boundsNode = boundsRoot.getFirstChild(); + boundsNode != null; + boundsNode = boundsNode.getNextSibling() + ) { + ClassNode bound = null; + bound = makeTypeWithArguments(boundsNode); + configureAST(bound, boundsNode); + bounds.add(bound); + } + if (bounds.isEmpty()) return null; + return (ClassNode[]) bounds.toArray(new ClassNode[bounds.size()]); + } + + protected GenericsType[] makeGenericsType(AST rootNode) { + AST typeParameter = rootNode.getFirstChild(); + LinkedList ret = new LinkedList(); + assertNodeType(TYPE_PARAMETER, typeParameter); + + while (isType(TYPE_PARAMETER, typeParameter)) { + AST typeNode = typeParameter.getFirstChild(); + ClassNode type = makeType(typeParameter); + + GenericsType gt = new GenericsType(type, makeGenericsBounds(typeNode, TYPE_UPPER_BOUNDS), null); + configureAST(gt, typeParameter); + + ret.add(gt); + typeParameter = typeParameter.getNextSibling(); + } + return (GenericsType[]) ret.toArray(new GenericsType[ret.size()]); + } + + protected ClassNode makeType(AST typeNode) { + ClassNode answer = ClassHelper.DYNAMIC_TYPE; + AST node = typeNode.getFirstChild(); + if (node != null) { + if (isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) { + // GRECLIPSE edit + //answer = makeType(node).makeArray(); + answer = makeTypeWithArguments(node).makeArray(); + // GRECLIPSE end + } else { + checkTypeArgs(node, false); + answer = ClassHelper.make(qualifiedName(node)); + if (answer.isUsingGenerics()) { + ClassNode newAnswer = ClassHelper.makeWithoutCaching(answer.getName()); + newAnswer.setRedirect(answer); + answer = newAnswer; + } + } + configureAST(answer, node); + } + return answer; + } + + private boolean checkTypeArgs(AST node, boolean seenTypeArgs) { + if (isType(IDENT, node) && seenTypeArgs) { + throw new ASTRuntimeException(node, "Unexpected type arguments found prior to: " + qualifiedName(node)); + } + if (isType(DOT, node)) { + AST next = node.getFirstChild(); + while (next != null && !isType(TYPE_ARGUMENTS, next)) { + seenTypeArgs |= checkTypeArgs(next, seenTypeArgs); + seenTypeArgs |= isType(TYPE_ARGUMENTS, next.getFirstChild()) || isType(TYPE_ARGUMENTS, next.getNextSibling()); + next = next.getNextSibling(); + } + } + return seenTypeArgs; + } + + /** + * Performs a name resolution to see if the given name is a type from imports, + * aliases or newly created classes + */ + /*protected String resolveTypeName(String name, boolean safe) { + if (name == null) { + return null; + } + return resolveNewClassOrName(name, safe); + }*/ + + /** + * Extracts an identifier from the Antlr AST and then performs a name resolution + * to see if the given name is a type from imports, aliases or newly created classes + */ + protected ClassNode buildName(AST node) { + if (isType(TYPE, node)) { + node = node.getFirstChild(); + } + ClassNode answer = null; + if (isType(DOT, node) || isType(OPTIONAL_DOT, node)) { + answer = ClassHelper.make(qualifiedName(node)); + } else if (isPrimitiveTypeLiteral(node)) { + answer = ClassHelper.make(node.getText()); + } else if (isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) { + AST child = node.getFirstChild(); + answer = buildName(child).makeArray(); + configureAST(answer, node); + return answer; + } else { + String identifier = node.getText(); + answer = ClassHelper.make(identifier); + } + AST nextSibling = node.getNextSibling(); + if (isType(INDEX_OP, nextSibling) || isType(ARRAY_DECLARATOR, node)) { + answer = answer.makeArray(); + configureAST(answer, node); + return answer; + } else { + configureAST(answer, node); + return answer; + } + } + + protected boolean isPrimitiveTypeLiteral(AST node) { + int type = node.getType(); + switch (type) { + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_double: + case LITERAL_float: + case LITERAL_int: + case LITERAL_long: + case LITERAL_short: + return true; + + default: + return false; + } + } + + /** + * Extracts an identifier from the Antlr AST + */ + protected String identifier(AST node) { + assertNodeType(IDENT, node); + return node.getText(); + } + + protected String label(AST labelNode) { + AST node = labelNode.getFirstChild(); + if (node == null) { + return null; + } + return identifier(node); + } + + + // Helper methods + //------------------------------------------------------------------------- + + + /** + * Returns true if the modifiers flags contain a visibility modifier + */ + protected boolean hasVisibility(int modifiers) { + return (modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) != 0; + } + + protected void configureAST(ASTNode node, AST ast) { + if (ast == null) + throw new ASTRuntimeException(ast, "PARSER BUG: Tried to configure " + node.getClass().getName() + " with null Node"); + /* GRECLIPSE edit + node.setColumnNumber(ast.getColumn()); + node.setLineNumber(ast.getLine()); + if (ast instanceof GroovySourceAST) { + node.setLastColumnNumber(((GroovySourceAST) ast).getColumnLast()); + node.setLastLineNumber(((GroovySourceAST) ast).getLineLast()); + } + */ + int startcol = ast.getColumn(); + int startline = ast.getLine(); + int startoffset = locations.findOffset(startline, startcol); + int lastcol; + int lastline; + int endoffset; + + if (ast instanceof GroovySourceAST) { + GroovySourceAST groovySourceAST = (GroovySourceAST) ast; + lastcol = groovySourceAST.getColumnLast(); + lastline = groovySourceAST.getLineLast(); + if ((ast.getType() == UNARY_MINUS || ast.getType() == UNARY_PLUS) && + node instanceof ConstantExpression) { // extend for literal + lastline = ((GroovySourceAST) ast.getFirstChild()).getLineLast(); + lastcol = ((GroovySourceAST) ast.getFirstChild()).getColumnLast(); + } + endoffset = locations.findOffset(lastline, lastcol); + + // GRECLIPSE-768: Only re-set the sloc for these kinds of expressions + // if the new sloc is larger than the old. When nested inside of a + // BinaryExpression, their slocs would be wrong instead, they are set + // according to their childrens' slocs. + if ((node instanceof BinaryExpression || + node instanceof TernaryExpression || + node instanceof MapEntryExpression || + node instanceof MapExpression || + node instanceof CastExpression || + node instanceof MethodCallExpression) && + node.getStart() <= startoffset && node.getEnd() >= endoffset) { + // sloc has already been set and it is larger than this one + return; + } + + // GRECLIPSE-829: VariableExpression inside of GStrings contain the + // openning '{', but shouldn't. If the new sloc is larger than the + // one being set, then ignore it and don't reset. Also numbers can + // result in an expression node that includes trailing whitespaces. + if ((node instanceof VariableExpression || + (node instanceof ConstantExpression && ast.getType() == EXPR)) && + node.getEnd() > 0 && startoffset <= node.getStart() && endoffset >= node.getEnd()) { + return; + } + + node.setLastColumnNumber(lastcol); + node.setLastLineNumber(lastline); + node.setEnd(endoffset); + } + + node.setColumnNumber(startcol); + node.setLineNumber(startline); + node.setStart(startoffset); + // GRECLIPSE end + + // TODO we could one day store the Antlr AST on the Groovy AST + // node.setCSTNode(ast); + } + + protected static Token makeToken(int typeCode, AST node) { + return Token.newSymbol(typeCode, node.getLine(), node.getColumn()); + } + + protected String getFirstChildText(AST node) { + AST child = node.getFirstChild(); + return child != null ? child.getText() : null; + } + + + public static boolean isType(int typeCode, AST node) { + return node != null && node.getType() == typeCode; + } + + private String getTokenName(int token) { + if (tokenNames == null) return "" + token; + return tokenNames[token]; + } + + private String getTokenName(AST node) { + if (node == null) return "null"; + return getTokenName(node.getType()); + } + + protected void assertNodeType(int type, AST node) { + if (node == null) { + throw new ASTRuntimeException(node, "No child node available in AST when expecting type: " + getTokenName(type)); + } + if (node.getType() != type) { + throw new ASTRuntimeException(node, "Unexpected node type: " + getTokenName(node) + " found when expecting type: " + getTokenName(type)); + } + } + + protected void notImplementedYet(AST node) { + throw new ASTRuntimeException(node, "AST node not implemented yet for type: " + getTokenName(node)); + } + + // GRECLIPSE edit + protected /*void*/Expression unknownAST(AST node) { + if (node.getType() == CLASS_DEF) { + throw new ASTRuntimeException(node, + "Class definition not expected here. Please define the class at an appropriate place or perhaps try using a block/Closure instead."); + } + if (node.getType() == METHOD_DEF) { + throw new ASTRuntimeException(node, + "Method definition not expected here. Please define the method at an appropriate place or perhaps try using a block/Closure instead."); + } + // GRECLIPSE edit + //throw new ASTRuntimeException(node, "Unknown type: " + getTokenName(node)); + return new ConstantExpression("ERROR"); + // GRECLIPSE end + } + + protected void dumpTree(AST ast) { + for (AST node = ast.getFirstChild(); node != null; node = node.getNextSibling()) { + dump(node); + } + } + + protected void dump(AST node) { + System.out.println("Type: " + getTokenName(node) + " text: " + node.getText()); + } + + // GRECLIPSE add + private void fixModuleNodeLocations() { + // only occurs if in a script + + output.setStart(0); + output.setEnd(locations.getEnd()); + output.setLineNumber(1); + output.setColumnNumber(1); + output.setLastColumnNumber(locations.getEndColumn()); + output.setLastLineNumber(locations.getEndLine()); + + // start location should be the start of either the first method or statement + // end location should be the end of either the last method or statement + BlockStatement statement = output.getStatementBlock(); + List methods = output.getMethods(); + if (hasScriptStatements(statement) || hasScriptMethods(methods)) { + ASTNode first = getFirst(statement, methods); + ASTNode last = getLast(statement, methods); + if (hasScriptStatements(statement)) { + statement.setStart(first.getStart()); + statement.setLineNumber(first.getLineNumber()); + statement.setColumnNumber(first.getColumnNumber()); + statement.setEnd(last.getEnd()); + statement.setLastLineNumber(last.getLastLineNumber()); + statement.setLastColumnNumber(last.getLastColumnNumber()); + } + if (!output.getClasses().isEmpty()) { + ClassNode scriptClass = output.getClasses().get(0); + scriptClass.setStart(first.getStart()); + scriptClass.setLineNumber(first.getLineNumber()); + scriptClass.setColumnNumber(first.getColumnNumber()); + scriptClass.setEnd(last.getEnd()); + scriptClass.setLastLineNumber(last.getLastLineNumber()); + scriptClass.setLastColumnNumber(last.getLastColumnNumber()); + + // fix the run method to contain the start and end locations of the statement block + MethodNode runMethod = scriptClass.getDeclaredMethod("run", Parameter.EMPTY_ARRAY); + runMethod.setStart(first.getStart()); + runMethod.setLineNumber(first.getLineNumber()); + runMethod.setColumnNumber(first.getColumnNumber()); + runMethod.setEnd(last.getEnd()); + runMethod.setLastLineNumber(last.getLastLineNumber()); + runMethod.setLastColumnNumber(last.getLastColumnNumber()); + } + } + } + + private boolean hasScriptMethods(List methods) { + return methods != null && !methods.isEmpty(); + } + + private boolean hasScriptStatements(BlockStatement statement) { + return statement != null && statement.getStatements() != null && !statement.getStatements().isEmpty(); + } + + /** Returns the first ast node in the script, either a method or a statement. */ + private ASTNode getFirst(BlockStatement statement, List methods) { + Statement firstStatement = hasScriptStatements(statement) ? statement.getStatements().get(0) : null; + MethodNode firstMethod = hasScriptMethods(methods) ? methods.get(0) : null; + if (firstMethod == null && (firstStatement == null || (firstStatement.getStart() == 0 && firstStatement.getLength() == 0))) { + // An empty script with no methods or statements. + // instead make a synthetic statement from the end of the package declaration/import statements + firstStatement = createSyntheticAfterImports(); + } + int statementStart = firstStatement != null ? firstStatement.getStart() : Integer.MAX_VALUE; + int methodStart = firstMethod != null ? firstMethod.getStart() : Integer.MAX_VALUE; + return statementStart <= methodStart ? firstStatement : firstMethod; + } + + /** Returns the last ast node in the script, either a method or a statement. */ + private ASTNode getLast(BlockStatement statement, List methods) { + Statement lastStatement = hasScriptStatements(statement) ? statement.getStatements().get(statement.getStatements().size() - 1) : null; + MethodNode lastMethod = hasScriptMethods(methods) ? methods.get(methods.size() - 1) : null; + if (lastMethod == null && (lastStatement == null || (lastStatement.getStart() == 0 && lastStatement.getLength() == 0))) { + // An empty script with no methods or statements. + // instead make a synthetic statement from the end of the package declaration/import statements + lastStatement = createSyntheticAfterImports(); + } + int statementStart = lastStatement != null ? lastStatement.getEnd() : Integer.MIN_VALUE; + int methodStart = lastMethod != null ? lastMethod.getStart() : Integer.MIN_VALUE; + return statementStart >= methodStart ? lastStatement : lastMethod; + } + + /** Creates a synthetic statement that starts after the last import or package statement. */ + private Statement createSyntheticAfterImports() { + Statement synthetic = ReturnStatement.RETURN_NULL_OR_VOID; + ASTNode target = null; + if (output.getImports() != null && !output.getImports().isEmpty()) { + target = output.getImports().get(output.getImports().size() - 1); + } else if (output.hasPackage()) { + target = output.getPackage(); + } + if (target != null) { + // import/package nodes do not include trailing semicolon, so use end of line instead of end of node + int off = Math.min(locations.findOffset(target.getLastLineNumber() + 1, 1), locations.getEnd() - 1); + int[] row_col = locations.getRowCol(off); + + synthetic = new ReturnStatement(ConstantExpression.NULL); + synthetic.setStart(off); + synthetic.setEnd(off); + synthetic.setLineNumber(row_col[0]); + synthetic.setColumnNumber(row_col[1]); + synthetic.setLastLineNumber(row_col[0]); + synthetic.setLastColumnNumber(row_col[1]); + } + return synthetic; + } + + protected void configureAnnotationAST(AnnotationNode node, AST ast) { + if (ast == null) { + throw new ASTRuntimeException(ast, "PARSER BUG: Tried to configure " + node.getClass().getName() + " with null AST"); + } + if (!(ast instanceof GroovySourceAST)) { + throw new ASTRuntimeException(ast, "PARSER BUG: Expected a GroovySourceAST node for annotation, but got: " + ast.getClass().getName()); + } + // Structure of incoming parameter 'ast': + // ANNOTATION + // - down='annotationName' (IDENT:84) + configureAST(node, ast.getFirstChild()); + configureAST(node.getClassNode(), ast.getFirstChild()); + // save the full source range of the annotation for future use + long start = locations.findOffset(ast.getLine(), ast.getColumn()); + long until = locations.findOffset(((GroovySourceAST) ast).getLineLast(), ((GroovySourceAST) ast).getColumnLast()); + node.setNodeMetaData("source.offsets", (start << 32) | until); // pack the two offsets into one long integer value + + node.setEnd(node.getEnd() - 1); // Eclipse wants this for error reporting + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/EnumHelper.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/EnumHelper.java new file mode 100644 index 0000000000..ce0f7ef52c --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/EnumHelper.java @@ -0,0 +1,77 @@ +/* + * 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.antlr; + +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MixinNode; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import groovyjarjarasm.asm.Opcodes; + +public class EnumHelper { + private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC; + private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS; + + public static ClassNode makeEnumNode(String name, int modifiers, ClassNode[] interfaces, ClassNode outerClass) { + modifiers = modifiers | Opcodes.ACC_FINAL | Opcodes.ACC_ENUM; + ClassNode enumClass; + if (outerClass==null) { + enumClass = new ClassNode(name,modifiers,null,interfaces,MixinNode.EMPTY_ARRAY); + } else { + name = outerClass.getName() + "$" + name; + modifiers |= Opcodes.ACC_STATIC; + enumClass = new InnerClassNode(outerClass,name,modifiers,null,interfaces,MixinNode.EMPTY_ARRAY); + } + + // set super class and generics info + // "enum X" -> class X extends Enum + GenericsType gt = new GenericsType(enumClass); + ClassNode superClass = ClassHelper.makeWithoutCaching("java.lang.Enum"); + superClass.setGenericsTypes(new GenericsType[]{gt}); + enumClass.setSuperClass(superClass); + superClass.setRedirect(ClassHelper.Enum_Type); + + return enumClass; + } + + // GRECLIPSE edit + public static void addEnumConstant(ClassNode enumClass, String name, Expression init) { + addEnumConstant(enumClass, enumClass, name, init, -1, -1); + } + + // modified to return the FieldNode it creates, so that we can fix up the position + public static FieldNode addEnumConstant(ClassNode enumClassType, ClassNode enumClassOwner, String name, Expression init, int lineNumber, int colNumber) { + int modifiers = PUBLIC_FS | Opcodes.ACC_ENUM; + if (init != null && !(init instanceof ListExpression)) { + ListExpression list = new ListExpression(); + list.addExpression(init); + init = list; + } + FieldNode fn = new FieldNode(name, modifiers, enumClassType.getPlainNodeReference(), enumClassOwner, init); + fn.setLineNumber(lineNumber); + fn.setColumnNumber(colNumber); + enumClassOwner.addField(fn); + return fn; + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ErrorRecoveredCSTParserPlugin.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ErrorRecoveredCSTParserPlugin.java new file mode 100644 index 0000000000..336c7e6cac --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ErrorRecoveredCSTParserPlugin.java @@ -0,0 +1,154 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.antlr; + +import groovyjarjarantlr.RecognitionException; +import groovyjarjarantlr.TokenStreamException; +import groovyjarjarantlr.TokenStreamIOException; +import groovyjarjarantlr.TokenStreamRecognitionException; +import org.codehaus.groovy.antlr.parser.GroovyLexer; +import org.codehaus.groovy.antlr.parser.GroovyRecognizer; +import org.codehaus.groovy.control.CompilationFailedException; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.syntax.SyntaxException; + +import java.io.Reader; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Parser plugin using grammar which has error recovery enabled/implemented in + * select places. + * + * @author empovazan + */ +public class ErrorRecoveredCSTParserPlugin extends AntlrParserPlugin { + private final ICSTReporter reporter; + + ErrorRecoveredCSTParserPlugin(ICSTReporter reporter) { + this.reporter = reporter; + } + + @Override + public void transformCSTIntoAST(final SourceUnit sourceUnit, Reader reader, SourceBuffer sourceBuffer) throws CompilationFailedException { + super.ast = null; + + setController(sourceUnit); + + // GRECLIPSE-805 Support for unicode escape sequences + UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(reader, sourceBuffer); + GroovyLexer lexer = new GroovyLexer(new UnicodeLexerSharedInputState(unicodeReader)); + unicodeReader.setLexer(lexer); + GroovyRecognizer parser = GroovyRecognizer.make(lexer); + parser.setSourceBuffer(sourceBuffer); + super.tokenNames = parser.getTokenNames(); + parser.setFilename(sourceUnit.getName()); + + // start parsing at the compilationUnit rule + try { + parser.compilationUnit(); + configureLocationSupport(sourceBuffer); + } catch (TokenStreamRecognitionException tsre) { + configureLocationSupport(sourceBuffer); + RecognitionException e = tsre.recog; + SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn()); + se.setFatal(true); + sourceUnit.addError(se); + } catch (RecognitionException e) { + configureLocationSupport(sourceBuffer); + // sometimes the line/column is after the end of the file + // why is this? Fix if possible + int origLine = e.getLine(); + int origColumn = e.getColumn(); + int[] newInts = fixLineColumn(origLine, origColumn); + int newLine = newInts[0]; + int newColumn = newInts[1]; + SyntaxException se = new SyntaxException(e.getMessage(), e, newLine, newColumn); + se.setFatal(true); + sourceUnit.addError(se); + } catch (TokenStreamException e) { + configureLocationSupport(sourceBuffer); + boolean handled = false; + if (e instanceof TokenStreamIOException) { + // GRECLIPSE-896: "Did not find four digit hex character code. line: 1 col:7" + String m = e.getMessage(); + if (m != null && m.startsWith("Did not find four digit hex character code.")) { + try { + int linepos = m.indexOf("line:"); + int colpos = m.indexOf("col:"); + int line = Integer.valueOf(m.substring(linepos + 5, colpos).trim()); + int col = Integer.valueOf(m.substring(colpos + 4).trim()); + SyntaxException se = new SyntaxException(e.getMessage(), e, line, col); + se.setFatal(true); + sourceUnit.addError(se); + handled = true; + } catch (Throwable t) { + System.err.println(m); + t.printStackTrace(); + } + } + } + if (!handled) { + sourceUnit.addException(e); + } + } + + super.ast = parser.getAST(); + + sourceUnit.setComments(parser.getComments()); + reportCST(sourceUnit, parser); + } + + private void reportCST(final SourceUnit sourceUnit, final GroovyRecognizer parser) { + final List errorList = parser.getErrorList(); + final GroovySourceAST cst = (GroovySourceAST) parser.getAST(); + + if (reporter != null) { + if (cst != null) + reporter.generatedCST(sourceUnit.getName(), cst); + if (errorList.size() != 0) + // Unmodifiable necessary? + reporter.reportErrors(sourceUnit.getName(), Collections.unmodifiableList(errorList)); + } else { + // report directly on the SourceUnit + for (Map error : (List>) errorList) { + // sometimes the line/column is after the end of the file + // why is this? Fix if possible + int origLine = ((Integer) error.get("line")).intValue(); + int origColumn = ((Integer) error.get("column")).intValue(); + int[] newInts = fixLineColumn(origLine, origColumn); + int newLine = newInts[0]; + int newColumn = newInts[1]; + SyntaxException se = new SyntaxException((String) error.get("error"), newLine, newColumn); + sourceUnit.addError(se); + } + } + } + + /* + * Fix the column so that it is not past the end of the file + */ + private int[] fixLineColumn(int origLine, int origColumn) { + if (locations.isPopulated()) { + int offset = locations.findOffset(origLine, origColumn); + if (offset >= locations.getEnd() - 1) { + return locations.getRowCol(locations.getEnd() - 1); + } + } + return new int[] {origLine, origColumn}; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ErrorRecoveredCSTParserPluginFactory.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ErrorRecoveredCSTParserPluginFactory.java new file mode 100644 index 0000000000..fb416a1389 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ErrorRecoveredCSTParserPluginFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.antlr; + +import org.codehaus.groovy.control.ParserPlugin; +import org.codehaus.groovy.control.ParserPluginFactory; + +/** + * Plugin factory creating plugin that reports on creation of error recovered ASTs. + * + * @author empovazan + */ +public class ErrorRecoveredCSTParserPluginFactory extends ParserPluginFactory { + private final ICSTReporter cstReporter; + + public ErrorRecoveredCSTParserPluginFactory(ICSTReporter cstReporter) { + this.cstReporter = cstReporter; + } + + public ErrorRecoveredCSTParserPluginFactory() { + this.cstReporter = null; + } + + public ParserPlugin createParserPlugin() { + return new ErrorRecoveredCSTParserPlugin(cstReporter); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ICSTReporter.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ICSTReporter.java new file mode 100644 index 0000000000..60c09b2811 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/ICSTReporter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.antlr; + +import java.util.List; + +/** + * Reports on CST builds. + * + * @author empovazan + */ +public interface ICSTReporter { + /** + * Report that an AST has been generated. + */ + public void generatedCST(String fileName, GroovySourceAST ast); + + /** + * Report parse errors while attempting to generate a CST. Not that the CST + * may in fact be generated if the parser is error recovering. + */ + public void reportErrors(String fileName, List errors); +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/LocationSupport.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/LocationSupport.java new file mode 100644 index 0000000000..658707625f --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/LocationSupport.java @@ -0,0 +1,106 @@ + /* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.antlr; + +import java.util.List; + +/** + * Maps lines/columns to offsets in a text file. Assumes '\n' is the newline + * delimiter. The newline character is included as the last char on the line. + * Lines and columns are both 1 based + * + *

    + *
  • "" -> [0,0] + *
  • "a" -> [0,1] + *
  • "\n" -> [0,1], [1,0] + *
  • "a\n" -> [0,2], [2,0] + *
  • "a\nb" -> [0,2], [2,1] + *
  • "a\nbc\n" -> [0,2], [2,3], [5,0] + *
+ * + * @author Andrew Eisenberg + */ +public class LocationSupport { + + public static final LocationSupport NO_LOCATIONS = new LocationSupport(); + + private static final int[] NO_LINE_ENDINGS = new int[0]; + + private final int[] lineEndings; + + public LocationSupport() { + lineEndings = NO_LINE_ENDINGS; + } + + public LocationSupport(int[] lineEndings) { + this.lineEndings = lineEndings; + } + + public LocationSupport(List lines) { + if (lines != null) { + lineEndings = processLineEndings(lines); + } else { + lineEndings = NO_LINE_ENDINGS; + } + } + + private int[] processLineEndings(List lines) { + int[] newLineEndings = new int[lines.size() + 1]; // last index stores end of file + int total = 0; + int current = 1; + for (CharSequence line : lines) { + newLineEndings[current++] = (total += (line.length())); + } + return newLineEndings; + } + + // TODO: Maybe should throw exception if out of bounds? + public int findOffset(int row, int col) { + return row <= lineEndings.length && row > 0 ? lineEndings[row - 1] + col - 1 : 0; + } + + public int getEnd() { + return lineEndings.length > 0 ? lineEndings[lineEndings.length - 1] : 0; + } + + public int getEndColumn() { + if (lineEndings.length > 1) { + return lineEndings[lineEndings.length - 1] - lineEndings[lineEndings.length - 2]; + } else if (lineEndings.length > 0) { + return lineEndings[0]; + } else { + return 0; + } + } + + public int getEndLine() { + return lineEndings.length > 0 ? lineEndings.length - 1 : 0; // last index contains length of document + } + + public int[] getRowCol(int offset) { + for (int i = 1, n = lineEndings.length; i < n; i += 1) { + if (lineEndings[i] > offset) { + return new int[] {i, offset - lineEndings[i - 1] + 1}; + } + } + // after end of document + throw new RuntimeException("Location is after end of document. Offset : " + offset); + } + + public boolean isPopulated() { + return lineEndings.length > 0; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/SourceBuffer.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/SourceBuffer.java new file mode 100644 index 0000000000..061564ee83 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/SourceBuffer.java @@ -0,0 +1,174 @@ +/* + * 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.antlr; + +import java.util.ArrayList; +import java.util.List; + +/** + * A simple buffer that provides line/col access to chunks of source code + * held within itself. + * + * @author Jeremy Rayner + */ +public class SourceBuffer { + // GRECLIPSE edit + private final List lines = new ArrayList<>(); + private final List lineEndings = new ArrayList<>(); + private StringBuilder current = new StringBuilder(); + // GRECLIPSE-805: Support for unicode escape sequences + private UnicodeEscapingReader unescaper = new NoEscaper(); + // GRECLIPSE end + + public SourceBuffer() { + // GRECLIPSE edit + lines.add(current); + lineEndings.add(0); + // GRECLIPSE end + } + + /** + * Obtains a snippet of the source code within the bounds specified + * @param start (inclusive line/ inclusive column) + * @param end (inclusive line / exclusive column) + * @return specified snippet of source code as a String, or null if no source available + */ + public String getSnippet(LineColumn start, LineColumn end) { + // preconditions + if (start == null || end == null) { return null; } // no text to return + if (start.equals(end)) { return null; } // no text to return + if (lines.size() == 1 && current.length() == 0) { return null; } // buffer hasn't been filled yet + + // working variables + int startLine = start.getLine(); + int startColumn = start.getColumn(); + int endLine = end.getLine(); + int endColumn = end.getColumn(); + + // reset any out of bounds requests + if (startLine < 1) { startLine = 1;} + if (endLine < 1) { endLine = 1;} + if (startColumn < 1) { startColumn = 1;} + if (endColumn < 1) { endColumn = 1;} + if (startLine > lines.size()) { startLine = lines.size(); } + if (endLine > lines.size()) { endLine = lines.size(); } + + // obtain the snippet from the buffer within specified bounds + StringBuilder snippet = new StringBuilder(); + for (int i = startLine - 1; i < endLine;i++) { + String line = ((StringBuilder)lines.get(i)).toString(); + if (startLine == endLine) { + // reset any out of bounds requests (again) + if (startColumn > line.length()) { startColumn = line.length();} + if (startColumn < 1) { startColumn = 1;} + if (endColumn > line.length()) { endColumn = line.length() + 1;} + if (endColumn < 1) { endColumn = 1;} + if (endColumn < startColumn) { endColumn = startColumn;} + + line = line.substring(startColumn - 1, endColumn - 1); + } else { + if (i == startLine - 1) { + if (startColumn - 1 < line.length()) { + line = line.substring(startColumn - 1); + } + } + if (i == endLine - 1) { + if (endColumn - 1 < line.length()) { + line = line.substring(0,endColumn - 1); + } + } + } + snippet.append(line); + } + return snippet.toString(); + } + + /** + * Writes the specified character into the buffer + * @param c + */ + public void write(int c) { + if (c != -1) { + // GRECLIPSE add + col += 1; + // GRECLIPSE end + current.append((char)c); + } + if (c == '\n') { + current = new StringBuilder(); + // GRECLIPSE add + if (!prevWasCarriageReturn) { + // GRECLIPSE end + lines.add(current); + // GRECLIPSE add + } else { // \r\n was found + // back out previous line and add a \n to the line + lines.get(lines.size() - 1).append('\n'); + lineEndings.remove(lineEndings.size() - 1); + } + lineEndings.add(col + unescaper.getUnescapedUnicodeOffsetCount()); + // GRECLIPSE end + } + // GRECLIPSE add + // handle carriage returns as well as newlines + if (c == '\r') { + current = new StringBuilder(); + lines.add(current); + lineEndings.add(col + unescaper.getUnescapedUnicodeOffsetCount()); + // this may be a \r\n, but may not be + prevWasCarriageReturn = true; + } else { + prevWasCarriageReturn = false; + } + // GRECLIPSE end + } + private int col; + private boolean prevWasCarriageReturn; + + public LocationSupport getLocationSupport() { + lineEndings.add(col + unescaper.getUnescapedUnicodeOffsetCount()); // last line ends where the data runs out + int[] lineEndingsArray = new int[lineEndings.size()]; + for (int i = 0, max = lineEndings.size(); i < max; i += 1) { + lineEndingsArray[i] = lineEndings.get(i).intValue(); + } + return new LocationSupport(lineEndingsArray); + } + + public void setUnescaper(UnicodeEscapingReader unicodeEscapingReader) { + this.unescaper = unicodeEscapingReader; + } + // GRECLIPSE end +} + +/** + * GRECLIPSE-805: Support for unicode escape sequences + * @author Andrew Eisenberg + * @created Mar 3, 2011 + */ +class NoEscaper extends UnicodeEscapingReader { + public NoEscaper() { + super(null, null); + } + public int getUnescapedUnicodeColumnCount() { + return 0; + } + public int getUnescapedUnicodeOffsetCount() { + return 0; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/UnicodeEscapingReader.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/UnicodeEscapingReader.java new file mode 100644 index 0000000000..387cd01d8f --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/UnicodeEscapingReader.java @@ -0,0 +1,195 @@ +/* + * 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.antlr; + +import groovyjarjarantlr.CharScanner; +import groovyjarjarantlr.Token; +import groovyjarjarantlr.TokenStreamException; + +import java.io.IOException; +import java.io.Reader; + +/** + * Translates GLS-defined unicode escapes into characters. Throws an exception + * in the event of an invalid unicode escape being detected. + *

+ * No attempt has been made to optimize this class for speed or space. + */ +public class UnicodeEscapingReader extends Reader { + + private final Reader reader; + private CharScanner lexer; + private boolean hasNextChar = false; + private int nextChar; + private final SourceBuffer sourceBuffer; + private int previousLine; + private int numUnicodeEscapesFound = 0; + private int numUnicodeEscapesFoundOnCurrentLine = 0; + + private static class DummyLexer extends CharScanner { + private final Token t = new Token(); + public Token nextToken() throws TokenStreamException { + return t; + } + @Override + public int getColumn() { + return 0; + } + @Override + public int getLine() { + return 0; + } + } + + /** + * Constructor. + * @param reader The reader that this reader will filter over. + */ + public UnicodeEscapingReader(Reader reader,SourceBuffer sourceBuffer) { + this.reader = reader; + this.sourceBuffer = sourceBuffer; + // GRECLIPSE add + if (sourceBuffer != null) { + sourceBuffer.setUnescaper(this); + } + // GRECLIPSE end + this.lexer = new DummyLexer(); + } + + /** + * Sets the lexer that is using this reader. Must be called before the + * lexer is used. + */ + public void setLexer(CharScanner lexer) { + this.lexer = lexer; + } + + /** + * Reads characters from the underlying reader. + * @see java.io.Reader#read(char[],int,int) + */ + public int read(char cbuf[], int off, int len) throws IOException { + int c = 0; + int count = 0; + while (count < len && (c = read())!= -1) { + cbuf[off + count] = (char) c; + count++; + } + return (count == 0 && c == -1) ? -1 : count; + } + + /** + * Gets the next character from the underlying reader, + * translating escapes as required. + * @see java.io.Reader#close() + */ + public int read() throws IOException { + if (hasNextChar) { + hasNextChar = false; + write(nextChar); + return nextChar; + } + + if (previousLine != lexer.getLine()) { + // new line, so reset unicode escapes + numUnicodeEscapesFoundOnCurrentLine = 0; + previousLine = lexer.getLine(); + } + + int c = reader.read(); + if (c != '\\') { + write(c); + return c; + } + + // Have one backslash, continue if next char is 'u' + c = reader.read(); + if (c != 'u') { + hasNextChar = true; + nextChar = c; + write('\\'); + return '\\'; + } + + // Swallow multiple 'u's + int numberOfUChars = 0; + do { + numberOfUChars++; + c = reader.read(); + } while (c == 'u'); + + // Get first hex digit + checkHexDigit(c); + StringBuilder charNum = new StringBuilder(); + charNum.append((char) c); + + // Must now be three more hex digits + for (int i = 0; i < 3; i++) { + c = reader.read(); + checkHexDigit(c); + charNum.append((char) c); + } + int rv = Integer.parseInt(charNum.toString(), 16); + write(rv); + + numUnicodeEscapesFound += 4 + numberOfUChars; + numUnicodeEscapesFoundOnCurrentLine += 4 + numberOfUChars; + + return rv; + } + private void write(int c) { + if (sourceBuffer != null) {sourceBuffer.write(c);} + } + /** + * Checks that the given character is indeed a hex digit. + */ + private void checkHexDigit(int c) throws IOException { + if (c >= '0' && c <= '9') { + return; + } + if (c >= 'a' && c <= 'f') { + return; + } + if (c >= 'A' && c <= 'F') { + return; + } + // Causes the invalid escape to be skipped + hasNextChar = true; + nextChar = c; + throw new IOException("Did not find four digit hex character code." + + " line: " + lexer.getLine() + " col:" + lexer.getColumn()); + } + + public int getUnescapedUnicodeColumnCount() { + return numUnicodeEscapesFoundOnCurrentLine; + } + + public int getUnescapedUnicodeOffsetCount() { + return numUnicodeEscapesFound; + } + + /** + * Closes this reader by calling close on the underlying reader. + * + * @see java.io.Reader#close() + */ + public void close() throws IOException { + reader.close(); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/groovy.g b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/groovy.g new file mode 100644 index 0000000000..6d8cd45726 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/groovy.g @@ -0,0 +1,4749 @@ +/* + * 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. + */ +// Note: Please don't use physical tabs. Logical tabs for indent are width 4. + +// Note: This grammar has error recovery rules and code. It should not be used +// to compile class files. It is intended for IDE tooling and analysis in the +// face of incorrect code. Recovery rules/code is near comment tag 'RECOVERY:' + +header { +package org.codehaus.groovy.antlr.parser; + +import java.io.*; +import java.util.*; + +import antlr.CommonToken; +import antlr.InputBuffer; +import antlr.LexerSharedInputState; +import antlr.TokenStreamRecognitionException; + +import org.codehaus.groovy.antlr.*; +import org.codehaus.groovy.ast.Comment; +} + +/** JSR-241 Groovy Recognizer. + * + * Run 'java Main [-showtree] directory-full-of-groovy-files' + * + * [The -showtree option pops up a Swing frame that shows + * the AST constructed from the parser.] + * + * Contributing authors: + * John Mitchell johnm@non.net + * Terence Parr parrt@magelang.com + * John Lilley jlilley@empathy.com + * Scott Stanchfield thetick@magelang.com + * Markus Mohnen mohnen@informatik.rwth-aachen.de + * Peter Williams pete.williams@sun.com + * Allan Jacobs Allan.Jacobs@eng.sun.com + * Steve Messick messick@redhills.com + * James Strachan jstrachan@protique.com + * John Pybus john@pybus.org + * John Rose rose00@mac.com + * Jeremy Rayner groovy@ross-rayner.com + * Alex Popescu the.mindstorm@gmail.com + * Martin Kempf mkempf@hsr.ch + * Reto Kleeb rkleeb@hsr.ch + * + * Version 1.00 December 9, 1997 -- initial release + * Version 1.01 December 10, 1997 + * fixed bug in octal def (0..7 not 0..8) + * Version 1.10 August 1998 (parrt) + * added tree construction + * fixed definition of WS,comments for mac,pc,unix newlines + * added unary plus + * Version 1.11 (Nov 20, 1998) + * Added "shutup" option to turn off last ambig warning. + * Fixed inner class def to allow named class defs as statements + * synchronized requires compound not simple statement + * add [] after builtInType DOT class in primaryExpression + * "const" is reserved but not valid..removed from modifiers + * Version 1.12 (Feb 2, 1999) + * Changed LITERAL_xxx to xxx in tree grammar. + * Updated java.g to use tokens {...} now for 2.6.0 (new feature). + * + * Version 1.13 (Apr 23, 1999) + * Didn't have (stat)? for else clause in tree parser. + * Didn't gen ASTs for interface extends. Updated tree parser too. + * Updated to 2.6.0. + * Version 1.14 (Jun 20, 1999) + * Allowed final/abstract on local classes. + * Removed local interfaces from methods + * Put instanceof precedence where it belongs...in relationalExpr + * It also had expr not type as arg; fixed it. + * Missing ! on SEMI in classBlock + * fixed: (expr) + "string" was parsed incorrectly (+ as unary plus). + * fixed: didn't like Object[].class in parser or tree parser + * Version 1.15 (Jun 26, 1999) + * Screwed up rule with instanceof in it. :( Fixed. + * Tree parser didn't like (expr).something; fixed. + * Allowed multiple inheritance in tree grammar. oops. + * Version 1.16 (August 22, 1999) + * Extending an interface built a wacky tree: had extra EXTENDS. + * Tree grammar didn't allow multiple superinterfaces. + * Tree grammar didn't allow empty var initializer: {} + * Version 1.17 (October 12, 1999) + * ESC lexer rule allowed 399 max not 377 max. + * java.tree.g didn't handle the expression of synchronized + * statements. + * Version 1.18 (August 12, 2001) + * Terence updated to Java 2 Version 1.3 by + * observing/combining work of Allan Jacobs and Steve + * Messick. Handles 1.3 src. Summary: + * o primary didn't include boolean.class kind of thing + * o constructor calls parsed explicitly now: + * see explicitConstructorInvocation + * o add strictfp modifier + * o missing objBlock after new expression in tree grammar + * o merged local class definition alternatives, moved after declaration + * o fixed problem with ClassName.super.field + * o reordered some alternatives to make things more efficient + * o long and double constants were not differentiated from int/float + * o whitespace rule was inefficient: matched only one char + * o add an examples directory with some nasty 1.3 cases + * o made Main.java use buffered IO and a Reader for Unicode support + * o supports UNICODE? + * Using Unicode charVocabulary makes code file big, but only + * in the bitsets at the end. I need to make ANTLR generate + * unicode bitsets more efficiently. + * Version 1.19 (April 25, 2002) + * Terence added in nice fixes by John Pybus concerning floating + * constants and problems with super() calls. John did a nice + * reorg of the primary/postfix expression stuff to read better + * and makes f.g.super() parse properly (it was METHOD_CALL not + * a SUPER_CTOR_CALL). Also: + * + * o "finally" clause was a root...made it a child of "try" + * o Added stuff for asserts too for Java 1.4, but *commented out* + * as it is not backward compatible. + * + * Version 1.20 (October 27, 2002) + * + * Terence ended up reorging John Pybus' stuff to + * remove some nondeterminisms and some syntactic predicates. + * Note that the grammar is stricter now; e.g., this(...) must + * be the first statement. + * + * Trinary ?: operator wasn't working as array name: + * (isBig ? bigDigits : digits)[i]; + * + * Checked parser/tree parser on source for + * Resin-2.0.5, jive-2.1.1, jdk 1.3.1, Lucene, antlr 2.7.2a4, + * and the 110k-line jGuru server source. + * + * Version 1.21 (October 17, 2003) + * Fixed lots of problems including: + * Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g + * He found a problem/fix with floating point that start with 0 + * Ray also fixed problem that (int.class) was not recognized. + * Thorsten van Ellen noticed that \n are allowed incorrectly in strings. + * TJP fixed CHAR_LITERAL analogously. + * + * Version 1.21.2 (March, 2003) + * Changes by Matt Quail to support generics (as per JDK1.5/JSR14) + * Notes: + * o We only allow the "extends" keyword and not the "implements" + * keyword, since that's what JSR14 seems to imply. + * o Thanks to Monty Zukowski for his help on the antlr-interest + * mail list. + * o Thanks to Alan Eliasen for testing the grammar over his + * Fink source base + * + * Version 1.22 (July, 2004) + * Changes by Michael Studman to support Java 1.5 language extensions + * Notes: + * o Added support for annotations types + * o Finished off Matt Quail's generics enhancements to support bound type arguments + * o Added support for new for statement syntax + * o Added support for static import syntax + * o Added support for enum types + * o Tested against JDK 1.5 source base and source base of jdigraph project + * o Thanks to Matt Quail for doing the hard part by doing most of the generics work + * + * Version 1.22.1 (July 28, 2004) + * Bug/omission fixes for Java 1.5 language support + * o Fixed tree structure bug with classOrInterface - thanks to Pieter Vangorpto for + * spotting this + * o Fixed bug where incorrect handling of SR and BSR tokens would cause type + * parameters to be recognised as type arguments. + * o Enabled type parameters on constructors, annotations on enum constants + * and package definitions + * o Fixed problems when parsing if ((char.class.equals(c))) {} - solution by Matt Quail at Cenqua + * + * Version 1.22.2 (July 28, 2004) + * Slight refactoring of Java 1.5 language support + * o Refactored for/"foreach" productions so that original literal "for" literal + * is still used but the for sub-clauses vary by token type + * o Fixed bug where type parameter was not included in generic constructor's branch of AST + * + * Version 1.22.3 (August 26, 2004) + * Bug fixes as identified by Michael Stahl; clean up of tabs/spaces + * and other refactorings + * o Fixed typeParameters omission in identPrimary and newStatement + * o Replaced GT reconcilliation code with simple semantic predicate + * o Adapted enum/assert keyword checking support from Michael Stahl's java15 grammar + * o Refactored typeDefinition production and field productions to reduce duplication + * + * Version 1.22.4 (October 21, 2004) + * Small bux fixes + * o Added typeArguments to explicitConstructorInvocation, e.g. new MyParameterised() + * o Added typeArguments to postfixExpression productions for anonymous inner class super + * constructor invocation, e.g. new Outer().super() + * o Fixed bug in array declarations identified by Geoff Roy + * + * Version 1.22.4.g.1 + * o I have taken java.g for Java1.5 from Michael Studman (1.22.4) + * and have applied the groovy.diff from java.g (1.22) by John Rose + * back onto the new root (1.22.4) - Jeremy Rayner (Jan 2005) + * + * Version 1.22.4.g.2 + * o mkempf, rkleeb, Dec 2007 + * o fixed various rules so that they call the correct Create Method + * to make sure that the line information are correct + * + * Based on an original grammar released in the PUBLIC DOMAIN + */ + +class GroovyRecognizer extends Parser; +options { + k = 2; // two token lookahead + exportVocab=Groovy; // Call its vocabulary "Groovy" + codeGenMakeSwitchThreshold = 2; // Some optimizations + codeGenBitsetTestThreshold = 3; + defaultErrorHandler = false; // Don't generate parser error handlers + buildAST = true; +} + +tokens { + BLOCK; MODIFIERS; OBJBLOCK; SLIST; METHOD_DEF; VARIABLE_DEF; + INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF; TRAIT_DEF; + PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE; + PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP; + POST_INC; POST_DEC; METHOD_CALL; EXPR; + IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION; + FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract"; + UNUSED_GOTO="goto"; UNUSED_CONST="const"; UNUSED_DO="do"; + STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; CTOR_IDENT; VARIABLE_PARAMETER_DEF; + STRING_CONSTRUCTOR; STRING_CTOR_MIDDLE; + CLOSABLE_BLOCK; IMPLICIT_PARAMETERS; + SELECT_SLOT; DYNAMIC_MEMBER; + LABELED_ARG; SPREAD_ARG; SPREAD_MAP_ARG; //deprecated - SCOPE_ESCAPE; + LIST_CONSTRUCTOR; MAP_CONSTRUCTOR; + FOR_IN_ITERABLE; + STATIC_IMPORT; ENUM_DEF; ENUM_CONSTANT_DEF; FOR_EACH_CLAUSE; ANNOTATION_DEF; ANNOTATIONS; + ANNOTATION; ANNOTATION_MEMBER_VALUE_PAIR; ANNOTATION_FIELD_DEF; ANNOTATION_ARRAY_INIT; + TYPE_ARGUMENTS; TYPE_ARGUMENT; TYPE_PARAMETERS; TYPE_PARAMETER; WILDCARD_TYPE; + TYPE_UPPER_BOUNDS; TYPE_LOWER_BOUNDS; CLOSURE_LIST;MULTICATCH;MULTICATCH_TYPES; +} + +{ + /** This factory is the correct way to wire together a Groovy parser and lexer. */ + public static GroovyRecognizer make(GroovyLexer lexer) { + GroovyRecognizer parser = new GroovyRecognizer(lexer.plumb()); + // TODO: set up a common error-handling control block, to avoid excessive tangle between these guys + parser.lexer = lexer; + lexer.parser = parser; + parser.getASTFactory().setASTNodeClass(GroovySourceAST.class); + parser.warningList = new ArrayList(); + // GRECLIPSE add + parser.errorList = new ArrayList(); + // GRECLIPSE end + return parser; + } + // Create a scanner that reads from the input stream passed to us... + public static GroovyRecognizer make(InputStream in) { return make(new GroovyLexer(in)); } + public static GroovyRecognizer make(Reader in) { return make(new GroovyLexer(in)); } + public static GroovyRecognizer make(InputBuffer in) { return make(new GroovyLexer(in)); } + public static GroovyRecognizer make(LexerSharedInputState in) { return make(new GroovyLexer(in)); } + + @SuppressWarnings("unused") + private static GroovySourceAST dummyVariableToforceClassLoaderToFindASTClass = new GroovySourceAST(); + + List warningList; + public List getWarningList() { return warningList; } + + // GRECLIPSE add + List errorList; + public List getErrorList() { return errorList; } + + List comments = new ArrayList(); + public List getComments() { return comments; } + // GRECLIPSE end + + GroovyLexer lexer; + public GroovyLexer getLexer() { return lexer; } + public void setFilename(String f) { super.setFilename(f); lexer.setFilename(f); } + + @SuppressWarnings("unused") + private SourceBuffer sourceBuffer; + public void setSourceBuffer(SourceBuffer sourceBuffer) { + this.sourceBuffer = sourceBuffer; + } + + /** Create an AST node with the token type and text passed in, but + * with the same background information as another supplied Token (e.g. line numbers). + * To be used in place of antlr tree construction syntax, + * i.e. #[TOKEN,"text"] becomes create(TOKEN,"text",anotherToken) + * + * todo - change antlr.ASTFactory to do this instead... + */ + public AST create(int type, String txt, AST first) { + AST t = astFactory.create(type,txt); + if ( t != null && first != null) { + // first copy details from first token + t.initialize(first); + // then ensure that type and txt are specific to this new node + t.initialize(type,txt); + } + return t; + } + + private AST attachLast(AST t, Object last) { + if ((t instanceof GroovySourceAST) && (last instanceof SourceInfo)) { + SourceInfo lastInfo = (SourceInfo) last; + GroovySourceAST node = (GroovySourceAST)t; + node.setColumnLast(lastInfo.getColumn()); + node.setLineLast(lastInfo.getLine()); + // This is a good point to call node.setSnippet(), + // but it bulks up the AST too much for production code. + } + return t; + } + + public AST create(int type, String txt, Token first, Token last) { + return attachLast(create(type, txt, astFactory.create(first)), last); + } + + public AST create(int type, String txt, AST first, Token last) { + return attachLast(create(type, txt, first), last); + } + + public AST create(int type, String txt, AST first, AST last) { + return attachLast(create(type, txt, first), last); + } + + // GRECLIPSE add + public AST create2(int type, String txt, Token first, Token last) { + AST ast = create(type, txt, astFactory.create(first)); + if ((ast instanceof GroovySourceAST) && (last instanceof SourceInfo)) { + ((GroovySourceAST) ast).setLineLast(((SourceInfo) last).getLineLast()); + ((GroovySourceAST) ast).setColumnLast(((SourceInfo) last).getColumnLast()); + } + return ast; + } + + public AST missingIdentifier(Token prev, Token next) { + int line, column; + if (!(prev instanceof SourceInfo)) { + line = prev.getLine(); + column = prev.getColumn() + 1; + } else { + line = ((SourceInfo) prev).getLineLast(); + column = ((SourceInfo) prev).getColumnLast(); + } + GroovySourceToken ident = new GroovySourceToken(IDENT); + ident.setText("?"); + ident.setLine(line); + ident.setColumn(column); + ident.setLineLast(line); + ident.setColumnLast(column + 1); + return #(create(ident.getType(), ident.getText(), ident, next)); + } + + private Stack commentStartPositions = new Stack<>(); + + public void startComment(int line, int column) { + commentStartPositions.push((line << 16) + column); + } + + public void endComment(int type, int line, int column, String text) { + int lineAndColumn = commentStartPositions.pop(); + int startLine = lineAndColumn >>> 16; + int startColumn = lineAndColumn & 0xffff; + if (type == 0) { + Comment comment = Comment.makeSingleLineComment(startLine, startColumn, line, column, text); + comments.add(comment); + } else if (type == 1) { + Comment comment = Comment.makeMultiLineComment(startLine, startColumn, line, column, text); + comments.add(comment); + } + } + // GRECLIPSE end + + /** + * Clones the token + */ + public Token cloneToken(Token t) { + CommonToken clone = new CommonToken(t.getType(),t.getText()); + clone.setLine(t.getLine()); + clone.setColumn(t.getColumn()); + return clone; + } + + + // stuff to adjust ANTLR's tracing machinery + public static boolean tracing = false; // only effective if antlr.Tool is run with -traceParser + public void traceIn(String rname) throws TokenStreamException { + if (!GroovyRecognizer.tracing) return; + super.traceIn(rname); + } + public void traceOut(String rname) throws TokenStreamException { + if (!GroovyRecognizer.tracing) return; + if (returnAST != null) rname += returnAST.toStringList(); + super.traceOut(rname); + } + + // Error handling. This is a funnel through which parser errors go, when the parser can suggest a solution. + public void requireFailed(String problem, String solution) throws SemanticException { + // TODO: Needs more work. + Token lt = null; + int lineNum = Token.badToken.getLine(), colNum = Token.badToken.getColumn(); + try { + lt = LT(1); + if(lt != null) { + lineNum = lt.getLine(); + colNum = lt.getColumn(); + } + } + catch (TokenStreamException ee) { + if(ee instanceof TokenStreamRecognitionException) { + lineNum = ((TokenStreamRecognitionException) ee).recog.getLine(); + colNum = ((TokenStreamRecognitionException) ee).recog.getColumn(); + } + } + throw new SemanticException(problem + ";\n solution: " + solution, + getFilename(), lineNum, colNum); + } + + public void addWarning(String warning, String solution) { + Token lt = null; + try { lt = LT(1); } + catch (TokenStreamException ee) { } + if (lt == null) lt = Token.badToken; + + Map row = new HashMap(); + row.put("warning", warning); + row.put("solution", solution); + row.put("filename", getFilename()); + row.put("line", Integer.valueOf(lt.getLine())); + row.put("column", Integer.valueOf(lt.getColumn())); + // System.out.println(row); + warningList.add(row); + } + + // GRECLIPSE add + /** + * Report a recovered error. + */ + public void reportError(String message) { + Token lt = null; + try { lt = LT(1); } + catch (TokenStreamException e) { } + if (lt == null) lt = Token.badToken; + reportError(message, lt.getLine(), lt.getColumn()); + } + + /** + * Report a recovered error and specify the node. + */ + public void reportError(String message, AST ln) { + reportError(message, ln.getLine(), ln.getColumn()); + } + + /** + * Report a recovered error and specify the token. + */ + public void reportError(String message, Token lt) { + reportError(message, lt.getLine(), lt.getColumn()); + } + + /** + * Report a recovered error and specify the line and column. + */ + public void reportError(String message, int line, int column) { + Map row = new HashMap(); + row.put("error", message); + row.put("filename", getFilename()); + row.put("line", Integer.valueOf(line)); + row.put("column", Integer.valueOf(column)); + errorList.add(row); + } + + /** + * Report a recovered exception. + */ + public void reportError(RecognitionException e) { + Map row = new HashMap(); + row.put("error", e.getMessage()); + row.put("filename", e.getFilename()); + row.put("line", Integer.valueOf(e.getLine())); + row.put("column", Integer.valueOf(e.getColumn())); + errorList.add(row); + } + // GRECLIPSE end + + // Convenience method for checking of expected error syndromes. + private void require(boolean z, String problem, String solution) throws SemanticException { + if (!z) requireFailed(problem, solution); + } + + private boolean matchGenericTypeBrackets(boolean z, String problem, String solution) throws SemanticException { + if (!z) matchGenericTypeBracketsFailed(problem, solution); + return z; + } + + public void matchGenericTypeBracketsFailed(String problem, String solution) throws SemanticException { + Token lt = null; + int lineNum = Token.badToken.getLine(), colNum = Token.badToken.getColumn(); + + try { + lt = LT(1); + if(lt != null) { + lineNum = lt.getLine(); + colNum = lt.getColumn(); + } + } + catch (TokenStreamException ee) { + if(ee instanceof TokenStreamRecognitionException) { + lineNum = ((TokenStreamRecognitionException) ee).recog.getLine(); + colNum = ((TokenStreamRecognitionException) ee).recog.getColumn(); + } + } + + throw new SemanticException(problem + ";\n solution: " + solution, + getFilename(), lineNum, colNum); + } + + // Query a name token to see if it begins with a capital letter. + // This is used to tell the difference (w/o symbol table access) between {String x} and {println x}. + private boolean isUpperCase(Token x) { + if (x == null || x.getType() != IDENT) return false; // cannot happen? + String xtext = x.getText(); + return (xtext.length() > 0 && Character.isUpperCase(xtext.charAt(0))); + } + + private AST currentClass = null; // current enclosing class (for constructor recognition) + // Query a name token to see if it is identical with the current class name. + // This is used to distinguish constructors from other methods. + private boolean isConstructorIdent(Token x) { + if (currentClass == null) return false; + if (currentClass.getType() != IDENT) return false; // cannot happen? + String cname = currentClass.getText(); + + if (x == null || x.getType() != IDENT) return false; // cannot happen? + return cname.equals(x.getText()); + } + + @SuppressWarnings("unused") + private void dumpTree(AST ast, String offset) { + dump(ast, offset); + for (AST node = ast.getFirstChild(); node != null; node = node.getNextSibling()) { + dumpTree(node, offset+"\t"); + } + } + + private void dump(AST node, String offset) { + System.out.println(offset+"Type: " + getTokenName(node) + " text: " + node.getText()); + } + + private String getTokenName(AST node) { + if (node == null) return "null"; + return getTokenName(node.getType()); + } + + // Scratch variable for last 'sep' token. + // Written by the 'sep' rule, read only by immediate callers of 'sep'. + // (Not entirely clean, but better than a million xx=sep occurrences.) + private int sepToken = EOF; + + // Scratch variable for last argument list; tells whether there was a label. + // Written by 'argList' rule, read only by immediate callers of 'argList'. + private boolean argListHasLabels = false; + + // Scratch variable, holds most recently completed pathExpression. + // Read only by immediate callers of 'pathExpression' and 'expression'. + private AST lastPathExpression = null; + + // Inherited attribute pushed into most expression rules. + // If not zero, it means that the left context of the expression + // being parsed is a statement boundary or an initializer sign '='. + // Only such expressions are allowed to reach across newlines + // to pull in an LCURLY and appended block. + private final int LC_STMT = 1, LC_INIT = 2; + + /** + * Counts the number of LT seen in the typeArguments production. + * It is used in semantic predicates to ensure we have seen + * enough closing '>' characters; which actually may have been + * either GT, SR or BSR tokens. + */ + private int ltCounter = 0; + + /* This symbol is used to work around a known ANTLR limitation. + * In a loop with syntactic predicate, ANTLR needs help knowing + * that the loop exit is a second alternative. + * Example usage: ( (LCURLY)=> block | {ANTLR_LOOP_EXIT}? )* + * Probably should be an ANTLR RFE. + */ + ////// Original comment in Java grammar: + // Unfortunately a syntactic predicate can only select one of + // multiple alternatives on the same level, not break out of + // an enclosing loop, which is why this ugly hack (a fake + // empty alternative with always-false semantic predicate) + // is necessary. + @SuppressWarnings("unused") + private static final boolean ANTLR_LOOP_EXIT = false; +} + +// Compilation Unit: In Groovy, this is a single file or script. This is the start +// rule for this parser +compilationUnit + : + // The very first characters of the file may be "#!". If so, ignore the first line. + (SH_COMMENT!)? + + // we can have comments at the top of a file + nls! + + // A compilation unit starts with an optional package definition + ( (annotationsOpt "package")=> packageDefinition + | (statement[EOF])? + ) + + // The main part of the script is a sequence of any number of statements. + // Semicolons and/or significant newlines serve as separators. + ( sep! (statement[sepToken])? )* + EOF! + // GRECLIPSE add + exception + catch [RecognitionException e] { + // report the error but don't throw away what we've successfully parsed + reportError(e); + #compilationUnit = (AST) currentAST.root; + } + // GRECLIPSE end + ; + +/** A Groovy script or simple expression. Can be anything legal inside {...}. */ +snippetUnit + : nls! blockBody[EOF] + ; + +// Package statement: optional annotations followed by "package" then the package identifier +packageDefinition + {Token first = LT(1);} + // GRECLIPSE edit -- recovery for missing identifier + //: an:annotationsOpt! "package"! id:identifier! + // {#packageDefinition = #(create(PACKAGE_DEF,"package",first,LT(1)),an,id);} + : an:annotationsOpt! "package"! (id:identifier!)? + { + if (#id == null) { + #id = missingIdentifier(LT(0), null); + reportError("Invalid package specification", LT(0).getLine(), LT(0).getColumn()-1); + } + #packageDefinition = #(create(PACKAGE_DEF,"package",first,LT(1)),an,id); + } + // GRECLIPSE end + ; + +// Import statement: import followed by a package or class name +importStatement + {Token first = LT(1); boolean isStatic = false;} + // GRECLIPSE edit -- recovery for missing identifier + //: an:annotationsOpt "import"! ( "static"! {isStatic=true;} )? is:identifierStar! + // {if (isStatic) + // #importStatement = #(create(STATIC_IMPORT,"static_import",first,LT(1)),an,is); + // else + // #importStatement = #(create(IMPORT,"import",first,LT(1)),an,is);} + : an:annotationsOpt "import"! ("static"! {isStatic=true;})? (is:identifierStar!)? + { + if (#is == null) { + #is = missingIdentifier(LT(0), null); + } + if (!isStatic) { + #importStatement = #(create(IMPORT,"import",first,LT(1)),an,is); + } else { + #importStatement = #(create(STATIC_IMPORT,"static_import",first,LT(1)),an,is); + } + } + // GRECLIPSE end + ; + +// Protected type definitions production for reuse in other productions +protected typeDefinitionInternal[AST mods] + : cd:classDefinition[#mods] // inner class + {#typeDefinitionInternal = #cd;} + | td:traitDefinition[#mods] // inner trait + {#typeDefinitionInternal = #td;} + | id:interfaceDefinition[#mods] // inner interface + {#typeDefinitionInternal = #id;} + | ed:enumDefinition[#mods] // inner enum + {#typeDefinitionInternal = #ed;} + | ad:annotationDefinition[#mods] // inner annotation + {#typeDefinitionInternal = #ad;} + ; + +/** A declaration is the creation of a reference or primitive-type variable, + * or (if arguments are present) of a method. + * Generically, this is called a 'variable' definition, even in the case of a class field or method. + * It may start with the modifiers and/or a declaration keyword "def". + * It may also start with the modifiers and a capitalized type name. + *

+ * AST effect: Create a separate Type/Var tree for each var in the var list. + * Must be guarded, as in (declarationStart) => declaration. + */ +declaration! + : + // method/variable using a 'def' or a modifier; type is optional + m:modifiers + (t:typeSpec[false])? + v:variableDefinitions[#m, #t] + {#declaration = #v;} + | + // method/variable using a type only + t2:typeSpec[false] + v2:variableDefinitions[null,#t2] + {#declaration = #v2;} + ; + +genericMethod! + : + // method using a 'def' or a modifier; type is optional + m:modifiers + p:typeParameters + t:typeSpec[false] + v:variableDefinitions[#m, #t] + { + #genericMethod = #v; + AST old = #v.getFirstChild(); + #genericMethod.setFirstChild(#p); + #p.setNextSibling(old); + } + ; + +/** A declaration with one declarator and no initialization, like a parameterDeclaration. + * Used to parse loops like for (int x in y) (up to the in keyword). + */ +singleDeclarationNoInit! + : + // method/variable using a 'def' or a modifier; type is optional + m:modifiers + (t:typeSpec[false])? + v:singleVariable[#m, #t] + {#singleDeclarationNoInit = #v;} + | + // method/variable using a type only + t2:typeSpec[false] + v2:singleVariable[null,#t2] + {#singleDeclarationNoInit = #v2;} + ; + +/** A declaration with one declarator and optional initialization, like a parameterDeclaration. + * Used to parse declarations used for both binding and effect, in places like argument + * lists and while statements. + */ +singleDeclaration + : sd:singleDeclarationNoInit! + { #singleDeclaration = #sd; } + (varInitializer)? + ; + +/** Used only as a lookahead predicate, before diving in and parsing a declaration. + * A declaration can be unambiguously introduced with "def", an annotation or a modifier token like "final". + * It may also be introduced by a simple identifier whose first character is an uppercase letter, + * as in {String x}. A declaration can also be introduced with a built in type like 'int' or 'void'. + * Brackets (array and generic) are allowed, as in {List[] x} or {int[][] y}. + * Anything else is parsed as a statement of some sort (expression or command). + *

+ * (In the absence of explicit method-call parens, we assume a capitalized name is a type name. + * Yes, this is a little hacky. Alternatives are to complicate the declaration or command + * syntaxes, or to have the parser query the symbol table. Parse-time queries are evil. + * And we want both {String x} and {println x}. So we need a syntactic razor-edge to slip + * between 'println' and 'String'.) + */ +declarationStart! + : ( ("def" nls) + | modifier nls + | annotation nls + | ( upperCaseIdent + | builtInType + | qualifiedTypeName + ) (typeArguments)? (LBRACK balancedTokens RBRACK)* + )+ + ( IDENT | STRING_LITERAL ) + ; + +/** + * lookahead predicate for usage of generics in methods + * as parameter for the method. Example: + * static T foo(){} + * must be first after the modifier. + * This rule allows more and does no exact match, but it + * is only a lookahead, not the real rule. + */ +genericMethodStart! + : ( "def" nls + | modifier nls + | annotation nls + )+ LT + ; + +qualifiedTypeName! + : + IDENT DOT (IDENT DOT)* upperCaseIdent + ; + +/** Used to look ahead for a constructor + */ +constructorStart! + : + modifiersOpt! id:IDENT! {isConstructorIdent(id)}? nls! LPAREN! //... + ; + + +/** Used only as a lookahead predicate for nested type definitions. */ + +typeDefinitionStart! + : modifiersOpt! ("class" | "interface" | "enum" | "trait" | AT "interface") + ; + +/** An IDENT token whose spelling is required to start with an uppercase letter. + * In the case of a simple statement {UpperID name} the identifier is taken to be a type name, not a command name. + */ +upperCaseIdent + : {isUpperCase(LT(1))}? + IDENT + ; + +// A type specification is a type name with possible brackets afterwards +// (which would make it an array type). +// Set addImagNode true for types inside expressions, not declarations. +typeSpec[boolean addImagNode] + : classTypeSpec[addImagNode] + | builtInTypeSpec[addImagNode] + ; + +// also check that 'classOrInterfaceType[false]' is a suitable substitution for 'identifier' + +// A class type specification is a class type with either: +// - possible brackets afterwards +// (which would make it an array type). +// - generic type arguments after +classTypeSpec[boolean addImagNode] {Token first = LT(1);} + : ct:classOrInterfaceType[false]! + declaratorBrackets[#ct] + { + if ( addImagNode ) { + #classTypeSpec = #(create(TYPE,"TYPE",first,LT(1)), #classTypeSpec); + } + } + ; + +// A non-built in type name, with possible type parameters +classOrInterfaceType[boolean addImagNode] {Token first = LT(1);} + : i1:IDENT^ (typeArguments|typeArgumentsDiamond)? + + ( options{greedy=true;}: // match as many as possible + d:DOT! + i2:IDENT! (ta:typeArguments!)? + {#i1 = #(create(DOT,".",first,LT(1)),i1,i2,ta);} + )* + { + #classOrInterfaceType = #i1; + if ( addImagNode ) { + #classOrInterfaceType = #(create(TYPE,"TYPE",first,LT(1)), #classOrInterfaceType); + } + } + ; + +// A specialised form of typeSpec where built in types must be arrays +typeArgumentSpec + : classTypeSpec[true] + | builtInTypeArraySpec[true] + ; + +// A generic type argument is a class type, a possibly bounded wildcard type or a built-in type array +typeArgument {Token first = LT(1);} + : ( typeArgumentSpec + | wildcardType + ) + {#typeArgument = #(create(TYPE_ARGUMENT,"TYPE_ARGUMENT",first,LT(1)), #typeArgument);} + ; + +// Wildcard type indicating all types (with possible constraint) +wildcardType + : QUESTION + (("extends" | "super")=> typeArgumentBounds)? + {#wildcardType.setType(WILDCARD_TYPE);} + ; + +typeArgumentsDiamond +{Token first = LT(1);} + : LT! GT! nls! + {#typeArgumentsDiamond = #(create(TYPE_ARGUMENTS, "TYPE_ARGUMENTS",first,LT(1)), #typeArgumentsDiamond);} + ; + +// Type arguments to a class or interface type +typeArguments +{Token first = LT(1); +int currentLtLevel = 0;} + : + {currentLtLevel = ltCounter;} + LT! {ltCounter++;} nls! + typeArgument + ( options{greedy=true;}: // match as many as possible + {inputState.guessing !=0 || ltCounter == currentLtLevel + 1}? + COMMA! nls! typeArgument + )* + nls! + ( // turn warning off since Antlr generates the right code, + // plus we have our semantic predicate below + options{generateAmbigWarnings=false;}: + typeArgumentsOrParametersEnd + )? + + // make sure we have gobbled up enough '>' characters + // if we are at the "top level" of nested typeArgument productions + {matchGenericTypeBrackets(((currentLtLevel != 0) || ltCounter == currentLtLevel), + "Missing closing bracket '>' for generics types", "Please specify the missing bracket!")}? + + {#typeArguments = #(create(TYPE_ARGUMENTS, "TYPE_ARGUMENTS",first,LT(1)), #typeArguments);} + ; + +// this gobbles up *some* amount of '>' characters, and counts how many +// it gobbled. +protected typeArgumentsOrParametersEnd + : GT! {ltCounter-=1;} + | SR! {ltCounter-=2;} + | BSR! {ltCounter-=3;} + ; + +// Restriction on wildcard types based on super class or derived class +typeArgumentBounds + {Token first = LT(1);boolean isUpperBounds = false;} + : + ( "extends"! {isUpperBounds=true;} | "super"! ) nls! classOrInterfaceType[true] nls! + { + if (isUpperBounds) + { + #typeArgumentBounds = #(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1)), #typeArgumentBounds); + } + else + { + #typeArgumentBounds = #(create(TYPE_LOWER_BOUNDS,"TYPE_LOWER_BOUNDS",first,LT(1)), #typeArgumentBounds); + } + } + ; + +// A builtin type array specification is a builtin type with brackets afterwards +builtInTypeArraySpec[boolean addImagNode] {Token first = LT(1);} + : bt:builtInType! + ( (LBRACK)=> // require at least one [] + declaratorBrackets[#bt] + | {require(false, + "primitive type parameters not allowed here", + "use the corresponding wrapper type, such as Integer for int" + );} + ) + { + if ( addImagNode ) { + #builtInTypeArraySpec = #(create(TYPE,"TYPE",first,LT(1)), #builtInTypeArraySpec); + } + } + ; + +// A builtin type specification is a builtin type with possible brackets +// afterwards (which would make it an array type). +builtInTypeSpec[boolean addImagNode] {Token first = LT(1);} + : bt:builtInType! + declaratorBrackets[#bt] + { + if ( addImagNode ) { + #builtInTypeSpec = #(create(TYPE,"TYPE",first,LT(1)), #builtInTypeSpec); + } + } + ; + +// A type name. which is either a (possibly qualified and parameterized) +// class name or a primitive (builtin) type +type + : classOrInterfaceType[false] + | builtInType + ; + +// The primitive types. +builtInType + : "void" + | "boolean" + | "byte" + | "char" + | "short" + | "int" + | "float" + | "long" + | "double" + ; + +// A (possibly-qualified) java identifier. We start with the first IDENT +// and expand its name by adding dots and following IDENTS +identifier {Token first = LT(1);} + : i1:IDENT! + ( options { greedy = true; } : + d:DOT! nls! i2:IDENT! + {#i1 = #(create(DOT,".",first,LT(1)),i1,i2);} + )* + {#identifier = #i1;} + ; + +identifierStar {Token first = LT(1); int start = mark();} // GRECLIPSE add + : i1:IDENT! + ( options { greedy = true; } : + d1:DOT! nls! i2:IDENT! + {#i1 = #(create(DOT,".",first,LT(1)),i1,i2);} + )* + ( d2:DOT! nls! s:STAR! + {#i1 = #(create(DOT,".",first,LT(1)),i1,s);} + | "as"! nls! alias:IDENT! + {#i1 = #(create(LITERAL_as,"as",first,LT(1)),i1,alias);} + )? + {#identifierStar = #i1;} + // GRECLIPSE add + /* RECOVERY: notes: + * The start of parsing this structure was marked. If there is a problem an exception + * is caught, error logged, fake ast node created (to satisfy the parent rule) and + * we jump back to the start of this line and proceed to the end of it, hoping + * that parsing can continue on the next. + */ + exception + catch [RecognitionException e] { + reportError("Invalid import", first); + #identifierStar = #(create(DOT,".",first,LT(1)),i1,#(create(STAR,"*",null))); + // Give up on this line and just go to the next + rewind(start); + consumeUntil(NLS); + } + // GRECLIPSE end + ; + +modifiersInternal + { int seenDef = 0; } + : + ( + // Without this hush, there is a warning that @IDENT and @interface + // can follow modifiersInternal. But how is @IDENT possible after + // modifiersInternal? And how is @interface possible inside modifiersInternal? + // Is there an antlr bug? + options{generateAmbigWarnings=false;}: + + // 'def' is an empty modifier, for disambiguating declarations + {seenDef++ == 0}? // do not allow multiple "def" tokens + "def"! nls! + | + // Note: Duplication of modifiers is detected when walking the AST. + modifier nls! + | + {break; /* go out of the ()+ loop*/} + AT "interface" + | + annotation nls! + )+ + ; + +/** A list of one or more modifier, annotation, or "def". */ +modifiers {Token first = LT(1);} + : modifiersInternal + {#modifiers = #(create(MODIFIERS, "MODIFIERS",first,LT(1)), #modifiers);} + ; + +/** A list of zero or more modifiers, annotations, or "def". */ +modifiersOpt {Token first = LT(1);} + : ( + // See comment above on hushing warnings. + options{generateAmbigWarnings=false;}: + modifiersInternal + )? + {#modifiersOpt = #(create(MODIFIERS, "MODIFIERS",first,LT(1)), #modifiersOpt);} + ; + +// modifiers for Java classes, interfaces, class/instance vars and methods +modifier + : "private" + | "public" + | "protected" + | "static" + | "transient" + | "final" + | "abstract" + | "native" + | "threadsafe" + | "synchronized" + | "volatile" + | "strictfp" + ; + +annotation! {Token first = LT(1);} + : AT! i:identifier nls! (options{greedy=true;}: LPAREN! ( args:annotationArguments )? RPAREN! )? + {#annotation = #(create(ANNOTATION,"ANNOTATION",first,LT(1)), i, args);} + // GRECLIPSE add -- allow freestanding '@' for content assist + | AT! nls! + {#annotation = #(create(ANNOTATION,"ANNOTATION",first,LT(1)), missingIdentifier(first,LT(1)), null);} + // GRECLIPSE end + ; + +annotationsInternal + : ( + options{generateAmbigWarnings=false;}: + {break; /* go out of the ()* loop*/} + AT "interface" + | + annotation nls!)* + ; + +annotationsOpt {Token first = LT(1);} + : ( + // See comment above on hushing warnings. + options{generateAmbigWarnings=false;}: + annotationsInternal + )? + {#annotationsOpt = #(create(ANNOTATIONS, "ANNOTATIONS", first, LT(1)), #annotationsOpt);} + ; + +annotationArguments + : v:annotationMemberValueInitializer + { Token itkn = new Token(IDENT, "value"); + AST i; + #i = #(create(IDENT, "value", itkn, itkn)); + #annotationArguments = #(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",LT(1),LT(1)), i, v);} + | annotationMemberValuePairs + ; + +annotationMemberValuePairs + : annotationMemberValuePair ( COMMA! nls! annotationMemberValuePair )* + ; + +annotationMemberValuePair! {Token first = LT(1);} + // GRECLIPSE edit -- allow the pair to exist with no value initializer; user may want content assist for value + //: i:annotationIdent ASSIGN! nls! v:annotationMemberValueInitializer + : i:annotationIdent ASSIGN! nls! ( v:annotationMemberValueInitializer )? + {#annotationMemberValuePair = #(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",first,LT(1)),i,v);} + // GRECLIPSE add + exception + catch [RecognitionException e] { + // finish invalid member-value pair if the closing parenthesis is next + if (LT(1).getType() == RPAREN) { + reportError(e); + if (#i == null) { + #i = missingIdentifier(first, LT(1)); + } + #annotationMemberValuePair = #(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",first,LT(1)),i,v); + } else { + throw e; + } + } + // GRECLIPSE end + ; + +annotationIdent + : IDENT + | keywordPropertyNames + ; + +annotationMemberValueInitializer + : conditionalExpression[0] | annotation + ; + +/*OBS* +// This is an initializer used to set up an annotation member array. +annotationMemberArrayInitializer + : lc:LCURLY^ {#lc.setType(ANNOTATION_ARRAY_INIT);} + ( annotationMemberArrayValueInitializer + ( + // CONFLICT: does a COMMA after an initializer start a new + // initializer or start the option ',' at end? + // ANTLR generates proper code by matching + // the comma as soon as possible. + options { + warnWhenFollowAmbig = false; + } + : + COMMA! nls! annotationMemberArrayValueInitializer + )* + (COMMA! nls!)? + )? + RCURLY! + ; + +// The two things that can initialize an annotation array element are a conditional expression +// and an annotation (nested annotation array initialisers are not valid) +annotationMemberArrayValueInitializer + : conditionalExpression[0] + | annotation nls! + ; +*OBS*/ + +superClassClause! + {Token first = LT(1);} + : + ( "extends" nls! c:classOrInterfaceType[false] nls! )? + {#superClassClause = #(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1)),c);} + ; + +// Definition of a Java class +classDefinition![AST modifiers] +{Token first = cloneToken(LT(1));AST prevCurrentClass = currentClass; +if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); +}} + : "class" IDENT nls! + { currentClass = #IDENT; } + // it _might_ have type parameters + (tp:typeParameters nls!)? + // it _might_ have a superclass... + sc:superClassClause + // it might implement some interfaces... + ic:implementsClause + // now parse the body of the class + // GRECLIPSE edit + //cb:classBlock + //{#classDefinition = #(create(CLASS_DEF,"CLASS_DEF",first,LT(1)), + // modifiers,IDENT,tp,sc,ic,cb);} + /* RECOVERY: notes: + * Here we allow for the classBlock to be optional, the user may be typing: + * class Foo extends Ba + * As with similar rules we record an error and create a placeholder class def + * ast with a missing body. Corresponding check in AntlrParserPlugin that + * was expecting a body has been changed to cope with it being missing. + */ + (cb:classBlock)? + { + if (#cb != null) { + #classDefinition = #(create(CLASS_DEF,"CLASS_DEF",first,LT(1)),modifiers,IDENT,tp,sc,ic,cb); + } else { + reportError("Malformed class declaration", LT(1)); + #classDefinition = #(create(CLASS_DEF,"CLASS_DEF",first,LT(1)),modifiers,IDENT,tp,sc,ic,null); + } + } + // GRECLIPSE end + { currentClass = prevCurrentClass; } + ; + +// Definition of a Trait +traitDefinition![AST modifiers] +{Token first = cloneToken(LT(1));AST prevCurrentClass = currentClass; +if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); +}} + : "trait" IDENT nls! + { currentClass = #IDENT; } + // it _might_ have type parameters + (tp:typeParameters nls!)? + // it _might_ have a superclass... + sc:superClassClause + // it might implement some interfaces... + ic:implementsClause + // now parse the body of the class + cb:classBlock + {#traitDefinition = #(create(TRAIT_DEF, "TRAIT_DEF",first,LT(1)), + modifiers,IDENT,tp,sc,ic,cb);} + { currentClass = prevCurrentClass; } + ; + +// Definition of a Java Interface +interfaceDefinition![AST modifiers] {Token first = cloneToken(LT(1)); + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + }} + : "interface" IDENT nls! + // it _might_ have type parameters + (tp:typeParameters nls!)? + // it might extend some other interfaces + ie:interfaceExtends + // now parse the body of the interface (looks like a class...) + ib:interfaceBlock + {#interfaceDefinition = #(create(INTERFACE_DEF,"INTERFACE_DEF",first,LT(1)), + modifiers,IDENT,tp,ie,ib);} + ; + +enumDefinition![AST modifiers] {Token first = cloneToken(LT(1)); AST prevCurrentClass = currentClass; + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + }} + : "enum" IDENT + { currentClass = #IDENT; } + nls! + // it might implement some interfaces... + ic:implementsClause + nls! + // now parse the body of the enum + eb:enumBlock + {#enumDefinition = #(create(ENUM_DEF,"ENUM_DEF",first,LT(1)), + modifiers,IDENT,ic,eb);} + { currentClass = prevCurrentClass; } + ; + +annotationDefinition![AST modifiers] {Token first = cloneToken(LT(1)); + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + }} + : AT "interface" IDENT nls! + // now parse the body of the annotation + ab:annotationBlock + {#annotationDefinition = #(create(ANNOTATION_DEF,"ANNOTATION_DEF",first,LT(1)), + modifiers,IDENT,ab);} + ; + +typeParameters +{Token first = LT(1);int currentLtLevel = 0;} + : + {currentLtLevel = ltCounter;} + LT! {ltCounter++;} nls! + typeParameter (COMMA! nls! typeParameter)* + nls! + (typeArgumentsOrParametersEnd)? + + // make sure we have gobbled up enough '>' characters + // if we are at the "top level" of nested typeArgument productions + {matchGenericTypeBrackets(((currentLtLevel != 0) || ltCounter == currentLtLevel), + "Missing closing bracket '>' for generics types", "Please specify the missing bracket!")}? + + {#typeParameters = #(create(TYPE_PARAMETERS, "TYPE_PARAMETERS",first,LT(1)), #typeParameters);} + ; + +typeParameter {Token first = LT(1);} + : + // I'm pretty sure Antlr generates the right thing here: + (id:IDENT) ( options{generateAmbigWarnings=false;}: typeParameterBounds )? + {#typeParameter = #(create(TYPE_PARAMETER,"TYPE_PARAMETER",first,LT(1)), #typeParameter);} + ; + +typeParameterBounds {Token first = LT(1);} + : + "extends"! nls! classOrInterfaceType[true] + (BAND! nls! classOrInterfaceType[true])* + {#typeParameterBounds = #(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1)), #typeParameterBounds);} + ; + +// This is the body of a class. You can have classFields and extra semicolons. +classBlock {Token first = LT(1);} + : LCURLY! + ( classField )? ( sep! ( classField )? )* + RCURLY! + {#classBlock = #(create(OBJBLOCK,"OBJBLOCK",first,LT(1)),#classBlock);} + // GRECLIPSE add + // general recovery when class parsing goes haywire in some way - probably needs duplicating for interface/enum/anno/etc *sigh* + exception + catch [RecognitionException e] { + if (errorList.isEmpty()) { + // dirty hack to avoid having trouble with cascading problems + #classBlock = (AST) currentAST.root; + } + reportError(e); + #classBlock = #(create(OBJBLOCK,"OBJBLOCK",first,LT(1)),#classBlock); + currentAST.root = #classBlock; + currentAST.child = #classBlock != null && #classBlock.getFirstChild() != null ? #classBlock.getFirstChild() : #classBlock; + currentAST.advanceChildToEnd(); + } + // GRECLIPSE end + ; + +// This is the body of an interface. You can have interfaceField and extra semicolons. +interfaceBlock {Token first = LT(1);} + : LCURLY! + ( interfaceField )? ( sep! ( interfaceField )? )* + RCURLY! + {#interfaceBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #interfaceBlock);} + ; + +// This is the body of an annotation. You can have annotation fields and extra semicolons, +// That's about it (until you see what an annotation field is...) +annotationBlock {Token first = LT(1);} + : LCURLY! + ( annotationField )? ( sep! ( annotationField )? )* + RCURLY! + {#annotationBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #annotationBlock);} + ; + +// This is the body of an enum. You can have zero or more enum constants +// followed by any number of fields like a regular class +enumBlock {Token first = LT(1);} + : LCURLY! nls! + ( + // Need a syntactic predicate, since enumConstants + // can start with foo() as well as classField. + // (It's a true ambiguity, visible in the specification. + // To resolve in practice, use "def" before a real method.) + (enumConstantsStart) => enumConstants + | (classField)? + ) + ( sep! (classField)? )* + RCURLY! + {#enumBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #enumBlock);} + ; + +/** Guard for enumConstants. */ +enumConstantsStart + : annotationsOpt IDENT (LCURLY | LPAREN | nls (SEMI | COMMA | declarationStart | RCURLY)) + ; + +/** Comma-separated list of one or more enum constant definitions. */ +enumConstants + : + enumConstant + ( options {generateAmbigWarnings=false;} : + (nls (SEMI! | RCURLY | classField)) => { break; /* leave ()* loop */ } + | nls! COMMA! + ( + (nls annotationsOpt IDENT) => nls! enumConstant + | + (nls (SEMI! | RCURLY | classField)) => { break; /* leave ()* loop */ } + ) + )* + ; + +// An annotation field +annotationField! {Token first = LT(1);} + : mods:modifiersOpt! + ( td:typeDefinitionInternal[#mods] + {#annotationField = #td;} + | t:typeSpec[false] // annotation field + ( + // Need a syntactic predicate, since variableDefinitions + // can start with foo() also. Since method defs are not legal + // in this context, there's no harm done. + (IDENT LPAREN)=> + i:IDENT // the name of the field + LPAREN! RPAREN! + + /*OBS* rt:declaratorBrackets[#t] *OBS*/ + + ( "default" nls! amvi:annotationMemberValueInitializer )? + + {#annotationField = + #(create(ANNOTATION_FIELD_DEF,"ANNOTATION_FIELD_DEF",first,LT(1)), + mods, + #(create(TYPE,"TYPE",first,LT(1)),t), + i,amvi + );} + | v:variableDefinitions[#mods,#t] // variable + {#annotationField = #v;} + ) + ) + ; + +//An enum constant may have optional parameters and may have a +//a class body +enumConstant! {Token first = LT(1);} + : an:annotationsOpt // Note: Cannot start with "def" or another modifier. + i:IDENT + ( LPAREN! + a:argList + RPAREN! + )? + ( b:enumConstantBlock )? + {#enumConstant = #(create(ENUM_CONSTANT_DEF, "ENUM_CONSTANT_DEF",first,LT(1)), an, i, a, b);} + ; + +//The class-like body of an enum constant +enumConstantBlock {Token first = LT(1);} + : LCURLY! + (enumConstantField)? ( sep! (enumConstantField)? )* + RCURLY! + {#enumConstantBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #enumConstantBlock);} + ; + +//An enum constant field is just like a class field but without +//the possibility of a constructor definition or a static initializer + +// TODO - maybe allow 'declaration' production within this production, +// but how to disallow constructors and static initializers... +enumConstantField! {Token first = LT(1);} + : ( + (typeDefinitionStart)=> + mods:modifiersOpt! + td:typeDefinitionInternal[#mods] + {#enumConstantField = #td;} + | + (modifiers)=> + m1:modifiers + (tp1:typeParameters)? (t1:typeSpec[false])? + e1:enumConstantFieldInternal[#m1, #tp1, #t1, #first] + {#enumConstantField = #e1;} + | + m2:modifiersOpt! + (tp2:typeParameters)? t2:typeSpec[false] + e2:enumConstantFieldInternal[#m2, #tp2, #t2, #first] + {#enumConstantField = #e2;} + ) + | cs:compoundStatement + {#enumConstantField = #(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1)), cs);} + ; + +protected enumConstantFieldInternal![AST mods, AST tp, AST t, Token first] + : + // Need a syntactic predicate to avoid potential ambiguity + (IDENT LPAREN)=> + IDENT + + // parse the formal parameter declarations. + LPAREN! param:parameterDeclarationList RPAREN! + + // get the list of declared exceptions + ((nls "throws") => tc:throwsClause)? + + ( s2:compoundStatement )? + { + #enumConstantFieldInternal = #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)), + mods, + #(create(TYPE,"TYPE",first,LT(1)),t), + IDENT, + param, + tc, + s2); + if (tp != null) { + AST old = #enumConstantFieldInternal.getFirstChild(); + #enumConstantFieldInternal.setFirstChild(#tp); + #tp.setNextSibling(old); + } + } + + | v:variableDefinitions[#mods,#t] + {#enumConstantFieldInternal = #v;} + ; + +// An interface can extend several other interfaces... +interfaceExtends {Token first = LT(1);} + : ( + e:"extends"! nls! + classOrInterfaceType[true] ( COMMA! nls! classOrInterfaceType[true] )* nls! + )? + {#interfaceExtends = #(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1)), + #interfaceExtends);} + ; + +// A class can implement several interfaces... +implementsClause {Token first = LT(1);} + : ( + i:"implements"! nls! + classOrInterfaceType[true] ( COMMA! nls! classOrInterfaceType[true] )* nls! + )? + {#implementsClause = #(create(IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE",first,LT(1)), + #implementsClause);} + ; + +// Now the various things that can be defined inside a class +classField! {Token first = LT(1);} + : // method, constructor, or variable declaration + (constructorStart)=> + mc:modifiersOpt! ctor:constructorDefinition[#mc] + {#classField = #ctor;} + | + (genericMethodStart)=> + dg:genericMethod + {#classField = #dg;} + | + (multipleAssignmentDeclarationStart)=> + mad:multipleAssignmentDeclaration + {#classField = #mad;} + | + (declarationStart)=> + dd:declaration + {#classField = #dd;} + | + // type definition + (typeDefinitionStart)=> + mods:modifiersOpt! + ( td:typeDefinitionInternal[#mods] + {#classField = #td;} + ) + + // "static { ... }" class initializer + | "static" nls! s3:compoundStatement + {#classField = #(create(STATIC_INIT,"STATIC_INIT",first,LT(1)), s3);} + + // "{ ... }" instance initializer + | s4:compoundStatement + {#classField = #(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1)), s4);} + + // GRECLIPSE add + exception + catch [RecognitionException e] { + // GRECLIPSE-494: "class C {\n def m(){}\n thing\n static main(args){}\n }" + if (LA(1) == IDENT) { + reportError(e); + // create a variable definition for "thing" in hopes that subsequent class members can still be parsed + #classField = #(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)),null,#create(TYPE,"java.lang.Object",LT(1),LT(2)),#create(IDENT,first.getText(),LT(1),LT(2))); + consumeUntil(NLS); + } else { + throw e; + } + } + // GRECLIPSE end + ; + +// Now the various things that can be defined inside an interface +interfaceField! + : // method or variable declaration or inner interface + (declarationStart)=> + d:declaration + {#interfaceField = #d;} + | + (genericMethodStart)=> + dg:genericMethod + {#interfaceField = #dg;} + | + // type definition + (typeDefinitionStart)=> + mods:modifiersOpt + ( td:typeDefinitionInternal[#mods] + {#interfaceField = #td;} + ) + ; + +constructorBody {Token first = LT(1);} + : LCURLY! nls! + ( (explicitConstructorInvocation) => // Java compatibility hack + eci:explicitConstructorInvocation! (sep! bb1:blockBody[sepToken]!)? + | bb2:blockBody[EOF]! + ) + RCURLY! + {if (#eci != null) + #constructorBody = #(create(SLIST,"{",first,LT(1)),eci,bb1); + else + #constructorBody = #(create(SLIST,"{",first,LT(1)),bb2);} + ; + + +/** Catch obvious constructor calls, but not the expr.super(...) calls */ +explicitConstructorInvocation + : (typeArguments)? + ( "this"! lp1:LPAREN^ argList RPAREN! + {#lp1.setType(CTOR_CALL);} + | "super"! lp2:LPAREN^ argList RPAREN! + {#lp2.setType(SUPER_CTOR_CALL);} + ) + ; + +listOfVariables[AST mods, AST t, Token first] + : + variableDeclarator[getASTFactory().dupTree(mods), + getASTFactory().dupTree(t),first] + ( COMMA! nls! + {first = LT(1);} + variableDeclarator[getASTFactory().dupTree(mods), + getASTFactory().dupTree(t),first] + )* + ; + +multipleAssignmentDeclarationStart + : + (modifier nls | annotation nls)* "def" nls LPAREN + ; + +typeNamePairs[AST mods, Token first] + : + (t:typeSpec[false]!)? + singleVariable[getASTFactory().dupTree(mods),#t] + ( COMMA! nls! + {first = LT(1);} + (tn:typeSpec[false]!)? + singleVariable[getASTFactory().dupTree(mods),#tn] + )* + ; + +multipleAssignmentDeclaration {Token first = cloneToken(LT(1));} + : + mods:modifiers! + (t:typeSpec[false]!)? + LPAREN^ nls! typeNamePairs[#mods,first] RPAREN! + ASSIGN^ nls! + ( + (LPAREN nls IDENT (COMMA nls IDENT)* RPAREN ASSIGN) => multipleAssignment[0] + | assignmentExpression[0] + ) + {#multipleAssignmentDeclaration=#(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)), #mods, #(create(TYPE,"TYPE",first,LT(1)),#t), #multipleAssignmentDeclaration);} + ; + +/** The tail of a declaration. + * Either v1, v2, ... (with possible initializers) or else m(args){body}. + * The two arguments are the modifier list (if any) and the declaration head (if any). + * The declaration head is the variable type, or (for a method) the return type. + * If it is missing, then the variable type is taken from its initializer (if there is one). + * Otherwise, the variable type defaults to 'any'. + * DECIDE: Method return types default to the type of the method body, as an expression. + */ +variableDefinitions[AST mods, AST t] {Token first = cloneToken(LT(1)); + if (mods != null) { + first.setLine(mods.getLine()); + first.setColumn(mods.getColumn()); + } else if (t != null) { + first.setLine(t.getLine()); + first.setColumn(t.getColumn()); + }} + : + listOfVariables[mods,t,first] + | + // The parser allows a method definition anywhere a variable definition is accepted. + + ( id:IDENT + | qid:STRING_LITERAL {#qid.setType(IDENT);} // use for operator definitions, etc. + ) + + // parse the formal parameter declarations. + LPAREN! param:parameterDeclarationList! RPAREN! + + /*OBS*rt:declaratorBrackets[#t]*/ + + // get the list of exceptions that this method is + // declared to throw + ((nls "throws") => tc:throwsClause! )? + + // the method body is an open block + // but, it may have an optional constructor call (for constructors only) + // this constructor clause is only used for constructors using 'def' + // which look like method declarations + // since the block is optional and nls is part of sep we have to be sure + // a newline is followed by a block or ignore the nls too + ((nls! LCURLY) => (nlsWarn! mb:openBlock!))? + + { if (#qid != null) #id = #qid; + #variableDefinitions = + #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)), + mods, #(create(TYPE,"TYPE",first,LT(1)),t), id, param, tc, mb); + } + ; + +/** I've split out constructors separately; we could maybe integrate back into variableDefinitions + * later on if we maybe simplified 'def' to be a type declaration? + */ +constructorDefinition[AST mods] {Token first = cloneToken(LT(1)); + if (mods != null) { + first.setLine(mods.getLine()); + first.setColumn(mods.getColumn()); + }} + : + id:IDENT + + // parse the formal parameter declarations. + LPAREN! param:parameterDeclarationList! RPAREN! + + /*OBS*rt:declaratorBrackets[#t]*/ + + // get the list of exceptions that this method is + // declared to throw + ((nls "throws") => tc:throwsClause! )? nlsWarn! + // the method body is an open block + // but, it may have an optional constructor call (for constructors only) + + // TODO assert that the id matches the class + { isConstructorIdent(id); } + + cb:constructorBody! + { #constructorDefinition = #(create(CTOR_IDENT,"CTOR_IDENT",first,LT(1)), mods, param, tc, cb); + } + ; + +/** Declaration of a variable. This can be a class/instance variable, + * or a local variable in a method + * It can also include possible initialization. + */ +variableDeclarator![AST mods, AST t,Token first] + : + id:variableName + /*OBS*d:declaratorBrackets[t]*/ + (v:varInitializer)? + {#variableDeclarator = #(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)), mods, #(create(TYPE,"TYPE",first,LT(1)),t), id, v);} + ; + +/** Used in cases where a declaration cannot have commas, or ends with the "in" operator instead of '='. */ +singleVariable![AST mods, AST t] {Token first = LT(1);} + : + id:variableName + {#singleVariable = #(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)), mods, #(create(TYPE,"TYPE",first,LT(1)),t), id);} + ; + +variableName + : IDENT + ; + +/** After some type names, where zero or more empty bracket pairs are allowed. + * We use ARRAY_DECLARATOR to represent this. + */ +declaratorBrackets[AST typ] + : {#declaratorBrackets=typ;} + ( + // A following list constructor might conflict with index brackets; prefer the declarator. + options {greedy=true;} : + LBRACK! + RBRACK! + {#declaratorBrackets = #(create(ARRAY_DECLARATOR,"[",typ,LT(1)), + #declaratorBrackets);} + )* + ; + +/** An assignment operator '=' followed by an expression. (Never empty.) */ +varInitializer + : ASSIGN^ nls! expressionStatementNoCheck + // In {T x = y}, the left-context of y is that of an initializer. + + // GRECLIPSE add + exception + catch [RecognitionException e] { + // if empty assignment was found, produce something compatible with content assist + int index = 0; + if (ASSIGN == LT(index).getType() || ASSIGN == LT(--index).getType()) { + astFactory.addASTChild(currentAST, missingIdentifier(LT(index), LT(index + 1))); + #varInitializer = (AST) currentAST.root; + reportError(e); + } else { + throw e; + } + } + // GRECLIPSE end + ; + +/*OBS* +// This is an initializer used to set up an array. +arrayInitializer + : lc:LCURLY^ {#lc.setType(ARRAY_INIT);} + ( initializer + ( + // CONFLICT: does a COMMA after an initializer start a new + // initializer or start the option ',' at end? + // ANTLR generates proper code by matching + // the comma as soon as possible. + options { + warnWhenFollowAmbig = false; + } + : + COMMA! initializer + )* + (COMMA!)? + )? + RCURLY! + ; +*OBS*/ + +/*OBS* // Use [...] for initializing all sorts of sequences, including arrays. +// The two "things" that can initialize an array element are an expression +// and another (nested) array initializer. +initializer + : expression + | arrayInitializer + ; +*OBS*/ + +// This is a list of exception classes that the method is declared to throw +throwsClause + : nls! "throws"^ nls! identifier ( COMMA! nls! identifier )* + ; + +/** A list of zero or more formal parameters. + * If a parameter is variable length (e.g. String... myArg) it should be + * to the right of any other parameters of the same kind. + * General form: (req, ..., opt, ..., [rest], key, ..., [restKeys], [block] + * This must be sorted out after parsing, since the various declaration forms + * are impossible to tell apart without backtracking. + */ +parameterDeclarationList {Token first = LT(1);} + : + ( + parameterDeclaration + ( COMMA! nls! + parameterDeclaration + )* + )? + {#parameterDeclarationList = #(create(PARAMETERS,"PARAMETERS",first,LT(1)), + #parameterDeclarationList);} + ; + +/** A formal parameter for a method or closure. */ +parameterDeclaration! + { Token first = LT(1);boolean spreadParam = false; } + : + pm:parameterModifiersOpt + ( options {greedy=true;} : + t:typeSpec[false] + )? + + // TODO: What do formal parameters for keyword arguments look like? + + // Java-style var args + ( TRIPLE_DOT! { spreadParam = true; } )? + + id:IDENT + + // allow an optional default value expression + (exp:varInitializer)? + + /*OBS*pd:declaratorBrackets[#t]*/ + { + if (spreadParam) { + #parameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)), + pm, #(create(TYPE,"TYPE",first,LT(1)),t), id, exp); + } else { + #parameterDeclaration = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)), + pm, #(create(TYPE,"TYPE",first,LT(1)),t), id, exp); + } + } + ; + +multicatch_types +{Token first = LT(1);} + : + nls! + classOrInterfaceType[false] + ( + BOR! nls! classOrInterfaceType[false] + )* + + {#multicatch_types = #(create(MULTICATCH_TYPES, "MULTICATCH_TYPES",first,LT(1)), #multicatch_types);} + ; + +multicatch +{Token first = LT(1);} + : nls! (FINAL)? ("def")? (m:multicatch_types)? id:IDENT! + { + #multicatch = #(create(MULTICATCH,"MULTICATCH",first,LT(1)),m,id); + } + // GRECLIPSE add + exception + catch [RecognitionException e] { + if (#m != null && LT(1).getType() == RPAREN) { + reportError(e); + #id = missingIdentifier(first, LT(1)); + #multicatch = #(create(MULTICATCH,"MULTICATCH",first,LT(1)),m,id); + } else { + throw e; + } + } + // GRECLIPSE end + ; + +/*OBS* +variableLengthParameterDeclaration! {Token first = LT(1);} + : pm:parameterModifier t:typeSpec[false] TRIPLE_DOT! id:IDENT + + pd:declaratorBrackets[#t] + {#variableLengthParameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)), + pm, #(create(TYPE,"TYPE",first,LT(1)),t), id);} + ; +*OBS*/ + +parameterModifiersOpt + { Token first = LT(1);int seenDef = 0; } + //final and/or def can appear amongst annotations in any order + : ( {seenDef++ == 0}? // do not allow multiple "def" tokens + "def"! nls! // redundant, but allowed for symmetry + | "final" nls! + | annotation nls! + )* + {#parameterModifiersOpt = #(create(MODIFIERS,"MODIFIERS",first,LT(1)), #parameterModifiersOpt);} + ; + +/** Closure parameters are exactly like method parameters, + * except that they are not enclosed in parentheses, but rather + * are prepended to the front of a block, just after the brace. + * They are separated from the closure body by a CLOSABLE_BLOCK_OP token '->'. + */ +// With '|' there would be restrictions on bitwise-or expressions. +closableBlockParamsOpt[boolean addImplicit] + : (parameterDeclarationList nls CLOSABLE_BLOCK_OP)=> + parameterDeclarationList nls! CLOSABLE_BLOCK_OP! nls! + | {addImplicit}? + implicitParameters + | + /* else do not parse any parameters at all */ + ; + +/** Lookahead to check whether a block begins with explicit closure arguments. */ +closableBlockParamsStart! + : + nls parameterDeclarationList nls CLOSABLE_BLOCK_OP + ; + +/** Simple names, as in {x|...}, are completely equivalent to {(def x)|...}. Build the right AST. */ +closableBlockParam! {Token first = LT(1);} + : id:IDENT! + {#closableBlockParam = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)), + #(create(MODIFIERS,"MODIFIERS",first,LT(1))), #(create(TYPE,"TYPE",first,LT(1))), + id);} + ; + +// Compound statement. This is used in many contexts: +// Inside a class definition prefixed with "static": +// it is a class initializer +// Inside a class definition without "static": +// it is an instance initializer +// As the body of a method +// As a completely independent braced block of code inside a method +// it starts a new scope for variable definitions +// In Groovy, this is called an "open block". It cannot have closure arguments. + +compoundStatement + : openBlock + ; + +/** An open block is not allowed to have closure arguments. */ +openBlock {Token first = LT(1); int start = mark();} // GRECLIPSE add + : LCURLY! nls! + // AST type of SLIST means "never gonna be a closure" + bb:blockBody[EOF]! + RCURLY! + {#openBlock = #(create(SLIST,"{",first,LT(1)),bb);} + + // GRECLIPSE add + exception + catch [RecognitionException e] { + int end = mark(); + // rewind to the first token on the same line as opening '{' (aka first) + rewind(start); + while (LT(0) != null && LT(0).getLine() == first.getLine()) { + rewind(mark() - 1); + } + // advance through all tokens that have greater indentation + int col = LT(1).getColumn(); + do { + consume(); + } while (LT(1).getColumn() > col && LT(1).getType() != EOF); // TODO: skip 'case', 'default', comments? and statement labels -- they may be in same column as first token + + // if a closing '}' was found in the proper position, create a basic block + if (LT(1).getColumn() == col && LT(1).getType() == RCURLY) { + match(RCURLY); + reportError(e); + #openBlock = #(create(SLIST,"{",first,LT(1))); + } else { + rewind(end); + throw e; + } + } + // GRECLIPSE end + ; + +/** A block body is a parade of zero or more statements or expressions. */ +blockBody[int prevToken] + : + (statement[prevToken])? (sep! (statement[sepToken])?)* + ; + +/** A block which is known to be a closure, even if it has no apparent arguments. + * A block inside an expression or after a method call is always assumed to be a closure. + * Only labeled, unparameterized blocks which occur directly as substatements are kept open. + */ +closableBlock {Token first = LT(1);} + : LCURLY! nls! + cbp:closableBlockParamsOpt[true]! + bb:blockBody[EOF]! + RCURLY! + {#closableBlock = #(create(CLOSABLE_BLOCK,"{",first,LT(1)),cbp,bb);} + ; + +/** A block known to be a closure, but which omits its arguments, is given this placeholder. + * A subsequent pass is responsible for deciding if there is an implicit 'it' parameter, + * or if the parameter list should be empty. + */ +implicitParameters {Token first = LT(1);} + : { #implicitParameters = #(create(IMPLICIT_PARAMETERS,"IMPLICIT_PARAMETERS",first,LT(1))); } + ; + +/** A sub-block of a block can be either open or closable. + * It is closable if and only if there are explicit closure arguments. + * Compare this to a block which is appended to a method call, + * which is given closure arguments, even if they are not explicit in the code. + */ +openOrClosableBlock {Token first = LT(1);} + : LCURLY! nls! + cp:closableBlockParamsOpt[false]! + bb:blockBody[EOF]! + RCURLY! + { + if (#cp == null) #openOrClosableBlock = #(create(SLIST,"{",first,LT(1)),bb); + else #openOrClosableBlock = #(create(CLOSABLE_BLOCK,"{",first,LT(1)),cp,bb); + } + ; + +/** A statement is an element of a block. + * Typical statements are declarations (which are scoped to the block) + * and expressions. + */ +statement[int prevToken] +{boolean sce = false; Token first = LT(1); AST casesGroup_AST = null; int start = mark();} // GRECLIPSE add + // prevToken is NLS if previous statement is separated only by a newline + + : (genericMethodStart)=> + genericMethod + + | (multipleAssignmentDeclarationStart)=> + multipleAssignmentDeclaration + + // declarations are ambiguous with "ID DOT" relative to expression + // statements. Must backtrack to be sure. Could use a semantic + // predicate to test symbol table to see what the type was coming + // up, but that's pretty hard without a symbol table ;) + | (declarationStart)=> + declaration + + // Attach a label to the front of a statement + // This block is executed for effect, unless it has an explicit closure argument. + | + (IDENT COLON)=> + pfx:statementLabelPrefix! + {#statement = #pfx;} // nest it all under the label prefix + ( (LCURLY) => openOrClosableBlock + | statement[COLON] + ) + + // An expression statement. This could be a method call, + // assignment statement, or any other expression evaluated for + // side-effects. + // The prevToken is used to check for dumb expressions like +1. + | es:expressionStatement[prevToken] + //{#statement = #(create(EXPR,"EXPR",first,LT(1)),es);} + + // If-else statement + | "if"! LPAREN! ale:assignmentLessExpression! RPAREN! nlsWarn! ifCbs:compatibleBodyStatement! + ( + // CONFLICT: the old "dangling-else" problem... + // ANTLR generates proper code matching + // as soon as possible. Hush warning. + options { + warnWhenFollowAmbig = false; + } + : // lookahead to check if we're entering an 'else' clause + ( (sep!)? "else"! )=> + (sep!)? // allow SEMI here for compatibility with Java + "else"! nlsWarn! elseCbs:compatibleBodyStatement! + )? + {#statement = #(create(LITERAL_if,"if",first,LT(1)),ale,ifCbs,elseCbs);} + + // For statement + | forStatement + + // While statement + | "while"! LPAREN! sce=while_sce:strictContextExpression[false]! RPAREN! nlsWarn! + (s:SEMI! | while_cbs:compatibleBodyStatement!) + { + if (#s != null) + #statement = #(create(LITERAL_while,"Literal_while",first,LT(1)),while_sce,s); + else + #statement = #(create(LITERAL_while,"Literal_while",first,LT(1)),while_sce,while_cbs); + } + + /*OBS* no do-while statement in Groovy (too ambiguous) + // do-while statement + | "do"^ statement "while"! LPAREN! strictContextExpression RPAREN! SEMI! + *OBS*/ + // GRECLIPSE add + | "do"^ compoundStatement nls! "while"! LPAREN! strictContextExpression[false]! RPAREN! + { + reportError(new NoViableAltException(first, getFilename())); + } + // GRECLIPSE end + + // Import statement. Can be used in any scope. Has "import x as y" also. + | (annotationsOpt "import") => importStatement + + // class definition + | m:modifiersOpt! typeDefinitionInternal[#m] + + // switch/case statement + | "switch"! LPAREN! sce=switchSce:strictContextExpression[false]! RPAREN! nlsWarn! LCURLY! nls! + ( cg:casesGroup! + //expand the list of nodes for each catch statement + {casesGroup_AST = #(null,casesGroup_AST,cg);})* + RCURLY! + {#statement = #(create(LITERAL_switch,"switch",first,LT(1)),switchSce,casesGroup_AST);} + + // exception try-catch block + | tryBlock + + // synchronize a statement + | "synchronized"! LPAREN! sce=synch_sce:strictContextExpression[false]! RPAREN! nlsWarn! synch_cs:compoundStatement! + {#statement = #(create(LITERAL_synchronized,"synchronized",first,LT(1)),synch_sce,synch_cs);} + + /*OBS* + // empty statement + | s:SEMI {#s.setType(EMPTY_STAT);} + *OBS*/ + + | branchStatement + + // GRECLIPSE add + exception + catch [RecognitionException e] { + // GRECLIPSE-1048 + // If the pfx_AST is not null (i.e. a label was encountered) then attempt recovery. Basically if the + // NoViableAltException hit a problem and the token it encountered was on the same line as the prefix, + // skip to the end of the line, otherwise assume we can continue from where we are. + if (#pfx != null) { + reportError(e); + if (e instanceof NoViableAltException) { + NoViableAltException nvae = (NoViableAltException) e; + if (#pfx.getLine() == nvae.token.getLine()) { + consumeUntil(NLS); + } + } + } + // GRECLIPSE-1046 + // Two situations to support: 'if (f.) ' where the 'else' condition is missing. This is now handled + // by a recovery rule in the else clause parsing. And 'if (f.', where even the trailing parenthesis + // is missing, which is dealt with here by noticing the condition exists but ifCbs_AST is null. + // Create a basic if statement and soldier on. + else if (#ale != null && #ifCbs == null) { + // likely missing close paren + #statement = #(create(LITERAL_if,"if",first,LT(1)),ale,ifCbs,elseCbs); + } + else { + throw e; + } + } + // GRECLIPSE end + ; + +forStatement {Token first = LT(1);} + : "for"! + LPAREN! + ( (SEMI |(strictContextExpression[true] SEMI))=>cl:closureList! + // *OBS* + // There's no need at all for squeezing in the new Java 5 "for" + // syntax, since Groovy's is a suitable alternative. + // | (parameterDeclaration COLON)=> forEachClause + // *OBS* + | // the coast is clear; it's a modern Groovy for statement + fic:forInClause! + ) + RPAREN! nls! + (s:SEMI! | forCbs:compatibleBodyStatement!) // statement to loop over + { + if (#cl != null) { + if (#s != null) + #forStatement = #(create(LITERAL_for,"for",first,LT(1)),cl,s); + else + #forStatement = #(create(LITERAL_for,"for",first,LT(1)),cl,forCbs); + } else { + if (#s != null) + #forStatement = #(create(LITERAL_for,"for",first,LT(1)),fic,s); + else + #forStatement = #(create(LITERAL_for,"for",first,LT(1)),fic,forCbs); + } + } + + ; + +closureList + {Token first = LT(1); boolean sce=false;} + : + + ( sce=strictContextExpression[true] + | {astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT"));} + ) + ( + SEMI! sce=strictContextExpression[true] + | SEMI! {astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT"));} + )+ + {#closureList = #(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1)),#closureList);} + ; + +/*OBS* +forEachClause {Token first = LT(1);} + : + p:parameterDeclaration COLON! expression + {#forEachClause = #(create(FOR_EACH_CLAUSE,"FOR_EACH_CLAUSE",first,LT(1)), #forEachClause);} + ; +*OBS*/ + +forInClause + : ( (declarationStart)=> + decl:singleDeclarationNoInit + | IDENT + ) + ( + i:"in"^ {#i.setType(FOR_IN_ITERABLE);} + shiftExpression[0] + | + { addWarning( + "A colon at this point is legal Java but not recommended in Groovy.", + "Use the 'in' keyword." + ); + require(#decl != null, + "Java-style for-each statement requires a type declaration." + , + "Use the 'in' keyword, as for (x in y) {...}" + ); + } + c:COLON^ {#c.setType(FOR_IN_ITERABLE);} + expression[0] + ) + ; + +/** In Java, "if", "while", and "for" statements can take random, non-braced statements as their bodies. + * Support this practice, even though it isn't very Groovy. + */ +compatibleBodyStatement {Token first = LT(1);} + : (LCURLY)=> + compoundStatement + // comma sep decl case converted to multiple statements so must be wrapped in SLIST when single statement occurs after if/while/for + | (declarationStart (varInitializer)? COMMA)=> + de:declaration + {#compatibleBodyStatement = #(create(SLIST,"CBSLIST",first,LT(1)),de);} + | + statement[EOF] + // GRECLIPSE add + exception + catch [RecognitionException e] { + // GRECLIPSE-1046 + reportError(e); + } + // GRECLIPSE end + ; + +/** In Groovy, return, break, continue, throw, and assert can be used in a parenthesized expression context. + * Example: println (x || (return)); println assert x, "won't print a false value!" + * If an optional expression is missing, its value is void (this coerces to null when a value is required). + */ +branchStatement {Token first = LT(1);} + : + // Return an expression + "return"! + ( returnE:expression[0]! )? + // GRECLIPSE edit + //{#branchStatement = #(create(LITERAL_return,"return",first,LT(1)),returnE);} + {#branchStatement = #(create2(LITERAL_return,"return",first,LT(0)),returnE);} + // GRECLIPSE end + + // break: get out of a loop, or switch, or method call + // continue: do next iteration of a loop, or leave a closure + | "break"! + ( breakI:IDENT! )? + {#branchStatement = #(create(LITERAL_break,"break",first,LT(1)),breakI);} + + | "continue"! + ( contI:IDENT! )? + {#branchStatement = #(create(LITERAL_continue,"continue",first,LT(1)),contI);} + + // throw an exception + | "throw"! throwE:expression[0]! + {#branchStatement = #(create(LITERAL_throw,"throw",first,LT(1)),throwE);} + + + // groovy assertion... + | "assert"! assertAle: assignmentLessExpression! + ( options {greedy=true;} : + ( COMMA! nls! // TODO: gratuitous change caused failures + | COLON! nls! // standard Java syntax, but looks funny in Groovy + ) + assertE:expression[0]! + )? + {#branchStatement = #(create(LITERAL_assert,"assert",first,LT(1)),assertAle,assertE);} + ; + +/** A labeled statement, consisting of a vanilla identifier followed by a colon. */ +// Note: Always use this lookahead, to keep antlr from panicking: (IDENT COLON)=> +statementLabelPrefix + : IDENT c:COLON^ {#c.setType(LABELED_STAT);} nls! + ; + +/** An expression statement can be any general expression. + *

+ * An expression statement can also be a command, + * which is a simple method call in which the outermost parentheses are omitted. + *

+ * Certain "suspicious" looking forms are flagged for the user to disambiguate. + */ +// DECIDE: A later semantic pass can flag dumb expressions that don't occur in +// positions where their value is not used, e.g., {1+1;println} +expressionStatement[int prevToken] + { Token first = LT(1); } + : + ( (suspiciousExpressionStatementStart) => + checkSuspiciousExpressionStatement[prevToken] + )? + esn:expressionStatementNoCheck + { #expressionStatement = #(create(EXPR, "EXPR", first, LT(1)), #esn); } + ; + +expressionStatementNoCheck + { boolean isPathExpr = true; } + : + // Checks are now out of the way; here's the real rule: + head:expression[LC_STMT] + { isPathExpr = (#head == lastPathExpression); } + ( + // A path expression (e.g., System.out.print) can take arguments. + {LA(1)!=LITERAL_else && isPathExpr /*&& #head.getType()==METHOD_CALL*/}? + cmd:commandArgumentsGreedy[#head]! + { + #expressionStatementNoCheck = #cmd; + } + )? + ; + +/** + * If two statements are separated by newline (not SEMI), the second had + * better not look like the latter half of an expression. If it does, issue a warning. + *

+ * Also, if the expression starts with a closure, it needs to + * have an explicit parameter list, in order to avoid the appearance of a + * compound statement. This is a hard error. + *

+ * These rules are different from Java's "dumb expression" restriction. + * Unlike Java, Groovy blocks can end with arbitrary (even dumb) expressions, + * as a consequence of optional 'return' and 'continue' tokens. + *

+ * To make the programmer's intention clear, a leading closure must have an + * explicit parameter list, and must not follow a previous statement separated + * only by newlines. + */ +checkSuspiciousExpressionStatement[int prevToken] + : + (~LCURLY | LCURLY closableBlockParamsStart)=> //FIXME too much lookahead + // Either not a block, or a block with an explicit closure parameter list. + ( {prevToken == NLS}? + { addWarning( + "Expression statement looks like it may continue a previous statement", + "Either remove the previous newline, or add an explicit semicolon ';'."); + } + )? + | + // Else we have a block without any visible closure parameters. + {prevToken == NLS}? + // if prevToken is NLS, we have double trouble; issue a double warning + // Example: obj.foo \n {println x} + // Might be appended block: obj.foo {println x} + // Might be closure expression: obj.foo ; {x->println x} + // Might be open block: obj.foo ; L:{println x} + { require(false, + "Ambiguous expression could be a parameterless closure expression, "+ + "an isolated open code block, or it may continue a previous statement", + "Add an explicit parameter list, e.g. {it -> ...}, or force it to be treated "+ + "as an open block by giving it a label, e.g. L:{...}, "+ + "and also either remove the previous newline, or add an explicit semicolon ';'" + ); + } + | + {prevToken != NLS}? + // If prevToken is SEMI or something else, issue a single warning: + // Example: obj.foo ; {println x} + // Might be closure expression: obj.foo ; {x->println x} + // Might be open block: obj.foo ; L:{println x} + { require(false, + "Ambiguous expression could be either a parameterless closure expression or "+ + "an isolated open code block", + "Add an explicit closure parameter list, e.g. {it -> ...}, or force it to "+ + "be treated as an open block by giving it a label, e.g. L:{...}"); + } + ; + +/** Lookahead for suspicious statement warnings and errors. */ +suspiciousExpressionStatementStart + : + ( (PLUS | MINUS) + | (LBRACK | LPAREN | LCURLY) + ) + // TODO: Expand this set? + ; + +// Support for switch/case: +casesGroup {Token first = LT(1);} + : ( // CONFLICT: to which case group do the statements bind? + // ANTLR generates proper code: it groups the + // many "case"/"default" labels together then + // follows them with the statements + options { + greedy = true; + } + : + aCase + )+ + caseSList + {#casesGroup = #(create(CASE_GROUP, "CASE_GROUP",first,LT(1)), #casesGroup);} + ; + +aCase + : ("case"^ expression[0] | "default") COLON! nls! + ; + +caseSList {Token first = LT(1);} + : statement[COLON] (sep! (statement[sepToken])?)* + {#caseSList = #(create(SLIST,"SLIST",first,LT(1)),#caseSList);} + ; + +// The initializer for a for loop +forInit {Token first = LT(1);} + : // if it looks like a declaration, it is + (declarationStart)=> declaration + | // else it's a comma-separated list of expressions + (controlExpressionList)? + {#forInit = #(create(FOR_INIT,"FOR_INIT",first,LT(1)),#forInit);} + ; + +forCond {Token first = LT(1); boolean sce=false;} + : (sce=strictContextExpression[false])? + {#forCond = #(create(FOR_CONDITION,"FOR_CONDITION",first,LT(1)),#forCond);} + ; + +forIter {Token first = LT(1);} + : (controlExpressionList)? + {#forIter = #(create(FOR_ITERATOR,"FOR_ITERATOR",first,LT(1)),#forIter);} + ; + +// an exception handler try/catch block +tryBlock {Token first = LT(1);List catchNodes = new ArrayList();AST newHandler_AST = null;} + : "try"! nlsWarn! tryCs:compoundStatement! + ( options {greedy=true;} : {!(LA(1) == NLS && LA(2) == LPAREN)}? nls! h:handler! + + //expand the list of nodes for each catch statement + {newHandler_AST = #(null,newHandler_AST,h);} )* + ( options {greedy=true;} : nls! fc:finallyClause!)? + + {#tryBlock = #(create(LITERAL_try,"try",first,LT(1)),tryCs,newHandler_AST,fc);} + ; + +finallyClause {Token first = LT(1);} + : "finally"! nlsWarn! finallyCs:compoundStatement! + {#finallyClause = #(create(LITERAL_finally,"finally",first,LT(1)),finallyCs);} + ; + +// an exception handler +handler {Token first = LT(1);} + : "catch"! LPAREN! pd:multicatch! RPAREN! nlsWarn! handlerCs:compoundStatement! + {#handler = #(create(LITERAL_catch,"catch",first,LT(1)),pd,handlerCs);} + ; + +/** A member name (x.y) or element name (x[y]) can serve as a command name, + * which may be followed by a list of arguments. + * Unlike parenthesized arguments, these must be plain expressions, + * without labels or spread operators. + */ +commandArguments[AST head] +{ + Token first = LT(1); +} + : + commandArgument ( options {greedy=true;}: COMMA! nls! commandArgument )* + // println 2+2 //OK + // println(2+2) //OK + // println (2)+2 //BAD + // println((2)+2) //OK + // (println(2)+2) //OK + // compare (2), 2 //BAD + // compare( (2), 2 ) //OK + // foo.bar baz{bat}, bang{boz} //OK + { + AST elist = #(create(ELIST,"ELIST",first,LT(1)), #commandArguments); + AST headid = #(create(METHOD_CALL,"",first,LT(1)), head, elist); + #commandArguments = headid; + } + // GRECLIPSE add + exception + catch [RecognitionException e] { + // GRECLIPSE-1192 + // Do we need better recognition of the specific problem here? + // (if so, see the label recovery for GRECLIPSE-1048) + reportError(e); + } + // GRECLIPSE end + ; + +commandArgumentsGreedy[AST head] +{ + AST prev = #head; +} + : + + // argument to the already existing method name + ( ({#prev==null || #prev.getType()!=METHOD_CALL}? commandArgument)=> ( + first : commandArguments[head]! + { #prev = #first; } + ) + | + ) + + // we start a series of methods and arguments + ( options { greedy = true; } : + ( options { greedy = true; } : + // method name + pre:primaryExpression! + { #prev = #(create(DOT, ".", #prev), #prev, #pre); } + // what follows is either a normal argument, parens, + // an appended block, an index operation, or nothing + // parens (a b already processed): + // a b c() d e -> a(b).c().d(e) + // a b c()() d e -> a(b).c().call().d(e) + // index (a b already processed): + // a b c[x] d e -> a(b).c[x].d(e) + // a b c[x][y] d e -> a(b).c[x][y].d(e) + // block (a b already processed): + // a b c {x} d e -> a(b).c({x}).d(e) + // + // parens/block completes method call + // index makes method call to property get with index + // + (options {greedy=true;}: + (pathElementStart)=> + ( + pc:pathChain[LC_STMT,#prev]! + { #prev = #pc; } + ) + | + ( ca:commandArguments[#prev]! + { #prev = #ca; }) + )? + )* + ) + { #commandArgumentsGreedy = prev; } + ; + +commandArgument + : + (argumentLabel COLON nls!) => ( + argumentLabel c:COLON^ nls! expression[0] { #c.setType(LABELED_ARG); } + ) + | expression[0] + ; + +// expressions +// Note that most of these expressions follow the pattern +// thisLevelExpression : +// nextHigherPrecedenceExpression +// (OPERATOR nextHigherPrecedenceExpression)* +// which is a standard recursive definition for a parsing an expression. +// The operators have the following precedences: +// lowest ( 15) = **= *= /= %= += -= <<= >>= >>>= &= ^= |= (assignments) +// ( 14) ?: (conditional expression and elvis) +// ( 13) || (logical or) +// ( 12) && (logical and) +// ( 11) | ()binary or +// ( 10) ^ (binary xor) +// ( 9) & (binary and) +// (8.5) =~ ==~ (regex find/match) +// ( 8) == != <=> === !== (equals, not equals, compareTo) +// ( 7) < <= > >= instanceof as in (relational, in, instanceof, type coercion) +// ( 6) << >> >>> .. ..< (shift, range) +// ( 5) + - (addition, subtraction) +// ( 4) * / % (multiply div modulo) +// ( 3) ++ -- + - (pre dec/increment, unary signs) +// ( 2) ** (power) +// ( 1) ~ ! $ (type) (negate, not, typecast) +// ?. * *. *: (safe dereference, spread, spread-dot, spread-map) +// . .& .@ (member access, method closure, field/attribute access) +// [] ++ -- (list/map/array index, post inc/decrement) +// () {} [] (method call, closableBlock, list/map literal) +// new () (object creation, explicit parenthesis) +// +// the last two are not usually on a precedence chart; I put them in +// to point out that new has a higher precedence than '.', so you +// can validly use +// new Frame().show() +// +// Note that the above precedence levels map to the rules below... +// Once you have a precedence chart, writing the appropriate rules as below +// is usually very straightforward + + +// the mother of all expressions +// This nonterminal is not used for expression statements, which have a more restricted syntax +// due to possible ambiguities with other kinds of statements. This nonterminal is used only +// in contexts where we know we have an expression. It allows general Java-type expressions. +expression[int lc_stmt] + : +// (LPAREN typeSpec[true] RPAREN expression[lc_stmt])=> +// lp:LPAREN^ {#lp.setType(TYPECAST);} typeSpec[true] RPAREN! +// expression[lc_stmt] +// | + (LPAREN nls IDENT (COMMA nls IDENT)* RPAREN ASSIGN) => + m:multipleAssignment[lc_stmt] {#expression=#m;} + | assignmentExpression[lc_stmt] + ; + +multipleAssignment[int lc_stmt] {Token first = cloneToken(LT(1));} + : LPAREN^ nls! listOfVariables[null,null,first] RPAREN! + ASSIGN^ nls! + ( + (LPAREN nls IDENT (COMMA nls IDENT)* RPAREN ASSIGN) => multipleAssignment[lc_stmt] + | assignmentExpression[lc_stmt] + ) + ; + + +// This is a list of expressions. +// Used for backward compatibility, in a few places where +// comma-separated lists of Java expression statements and declarations are required. +controlExpressionList {Token first = LT(1); boolean sce=false;} + : sce=strictContextExpression[false] (COMMA! nls! sce=strictContextExpression[false])* + {#controlExpressionList = #(create(ELIST,"ELIST",first,LT(1)), controlExpressionList);} + ; + +pathChain[int lc_stmt, AST prefix] + : + ( + options { + // \n{foo} could match here or could begin a new statement + // We do want to match here. Turn off warning. + greedy=true; + // This turns the ambiguity warning of the second alternative + // off. See below. (The "ANTLR_LOOP_EXIT" predicate makes it non-issue) + //@@ warnWhenFollowAmbig=false; + } + // Parsing of this chain is greedy. For example, a pathExpression may be a command name + // followed by a command argument, but that command argument cannot begin with an LPAREN, + // since a parenthesized expression is greedily attached to the pathExpression as a method argument. + // The lookahead is also necessary to reach across newline in foo \n {bar}. + // (Apparently antlr's basic approximate LL(k) lookahead is too weak for this.) + : (pathElementStart)=> + nls! + pe:pathElement[prefix]! + { prefix = #pe; } + | + {lc_stmt == LC_STMT || lc_stmt == LC_INIT}? + (nls LCURLY)=> + nlsWarn! + apb:appendedBlock[prefix]! + { prefix = #apb; } + )+ + + { #pathChain = prefix; } + ; + +/** A "path expression" is a name or other primary, possibly qualified by various + * forms of dot, and/or followed by various kinds of brackets. + * It can be used for value or assigned to, or else further qualified, indexed, or called. + * It is called a "path" because it looks like a linear path through a data structure. + * Examples: x.y, x?.y, x*.y, x.@y; x[], x[y], x[y,z]; x(), x(y), x(y,z); x{s}; a.b[n].c(x).d{s} + * (Compare to a C lvalue, or LeftHandSide in the JLS section 15.26.) + * General expressions are built up from path expressions, using operators like '+' and '='. + */ +pathExpression[int lc_stmt] + { AST prefix = null; } + : + pre:primaryExpression! + { prefix = #pre; } + ( + options { + // \n{foo} could match here or could begin a new statement + // We do want to match here. Turn off warning. + greedy=true; + // This turns the ambiguity warning of the second alternative + // off. See below. (The "ANTLR_LOOP_EXIT" predicate makes it non-issue) + //@@ warnWhenFollowAmbig=false; + } + // Parsing of this chain is greedy. For example, a pathExpression may be a command name + // followed by a command argument, but that command argument cannot begin with an LPAREN, + // since a parenthesized expression is greedily attached to the pathExpression as a method argument. + // The lookahead is also necessary to reach across newline in foo \n {bar}. + // (Apparently antlr's basic approximate LL(k) lookahead is too weak for this.) + : (pathElementStart)=> + nls! + pe:pathElement[prefix]! + { prefix = #pe; } + | + {lc_stmt == LC_STMT || lc_stmt == LC_INIT}? + (nls LCURLY)=> + nlsWarn! + apb:appendedBlock[prefix]! + { prefix = #apb; } + )* + { + #pathExpression = prefix; + lastPathExpression = #pathExpression; + } + ; + +pathElement[AST prefix] {Token operator = LT(1);} + // The primary can then be followed by a chain of .id, (a), [a], and {...} + : + { #pathElement = prefix; } + ( nls! + ( SPREAD_DOT! // Spread operator: x*.y === x?.collect{it.y} + | + OPTIONAL_DOT! // Optional-null operator: x?.y === (x==null)?null:x.y + | + MEMBER_POINTER! // Member pointer operator: foo.&y == foo.metaClass.getMethodPointer(foo, "y") + | + DOT! // The all-powerful dot. + ) + ) nls! + (ta:typeArguments!)? + // GRECLIPSE edit -- recovery for missing identifier + //np:namePart! + //{ #pathElement = #(create(operator.getType(),operator.getText(),prefix,LT(1)),prefix,ta,np); } + (np:namePart!)? + { + if (#np == null) { + GroovySourceToken ident = new GroovySourceToken(IDENT); + ident.setLine(((SourceInfo) LT(0)).getLineLast()); + ident.setColumn(((SourceInfo) LT(0)).getColumnLast()); + ident.setLineLast(((SourceInfo) LT(0)).getLineLast()); + ident.setColumnLast(((SourceInfo) LT(0)).getColumnLast()); + #np = #(create(ident.getType(),ident.getText(),ident,null)); + reportError(new NoViableAltException(LT(1), getFilename())); + } + #pathElement = #(create(operator.getType(),operator.getText(),prefix,LT(1)),prefix,ta,np); + } + // GRECLIPSE end + | + mca:methodCallArgs[prefix]! + { #pathElement = #mca; } + | + // Can always append a block, as foo{bar} + apb:appendedBlock[prefix]! + { #pathElement = #apb; } + | + // Element selection is always an option, too. + // In Groovy, the stuff between brackets is a general argument list, + // since the bracket operator is transformed into a method call. + ipa:indexPropertyArgs[prefix]! + { #pathElement = #ipa; } + ; + +pathElementStart! + : (nls! ( DOT + | SPREAD_DOT + | OPTIONAL_DOT + | MEMBER_POINTER ) ) + | LBRACK + | LPAREN + | LCURLY + ; + +/** This is the grammar for what can follow a dot: x.a, x.@a, x.&a, x.'a', etc. + * Note: typeArguments is handled by the caller of namePart. + */ +namePart {Token first = LT(1);} + : + ( ats:AT^ {#ats.setType(SELECT_SLOT);} )? + // foo.@bar selects the field (or attribute), not property + + ( IDENT + | sl:STRING_LITERAL {#sl.setType(IDENT);} + // foo.'bar' is in all ways same as foo.bar, except that bar can have an arbitrary spelling + | dynamicMemberName + | + openBlock + // PROPOSAL, DECIDE: Is this inline form of the 'with' statement useful? + // Definition: a.{foo} === {with(a) {foo}} + // May cover some path expression use-cases previously handled by dynamic scoping (closure delegates). + + // let's allow common keywords as property names + | keywordPropertyNames + ) + + // (No, x.&@y is not needed; just say x.&y as Slot or some such.) + ; + +/* + * Allowed keywords after dot (as a member name) and before colon (as a label). + * Includes all Java keywords plus "as", "def", "in", and "trait". + */ +keywordPropertyNames + : ( + "as" + | "assert" + | "break" + | "case" + | "catch" + | "class" + | "const" + | "continue" + | "def" + | "default" + | "do" + | "else" + | "enum" + | "extends" + | "false" + | "finally" + | "for" + | "goto" + | "if" + | "implements" + | "import" + | "in" + | "instanceof" + | "interface" + | "new" + | "null" + | "package" + | "return" + | "super" + | "switch" + | "this" + | "throw" + | "throws" + | "trait" + | "true" + | "try" + | "while" + | modifier + | builtInType + ) + { #keywordPropertyNames.setType(IDENT); } + ; + +/** If a dot is followed by a parenthesized or quoted expression, the member is computed dynamically, + * and the member selection is done only at runtime. This forces a statically unchecked member access. + */ +dynamicMemberName {Token first = LT(1);} + : ( pe:parenthesizedExpression! + {#dynamicMemberName = #(create(EXPR,"EXPR",first,LT(1)),pe);} + | stringConstructorExpression + ) + { #dynamicMemberName = #(create(DYNAMIC_MEMBER, "DYNAMIC_MEMBER",first,LT(1)), #dynamicMemberName); } + ; + +/** An expression may be followed by one or both of (...) and {...}. + * Note: If either is (...) or {...} present, it is a method call. + * The {...} is appended to the argument list, and matches a formal of type Closure. + * If there is no method member, a property (or field) is used instead, and must itself be callable. + *

+ * If the methodCallArgs are absent, it is a property reference. + * If there is no property, it is treated as a field reference, but never a method reference. + *

+ * Arguments in the (...) can be labeled, and the appended block can be labeled also. + * If there is a mix of unlabeled and labeled arguments, + * all the labeled arguments must follow the unlabeled arguments, + * except that the closure (labeled or not) is always a separate final argument. + * Labeled arguments are collected up and passed as a single argument to a formal of type Map. + *

+ * Therefore, f(x,y, a:p, b:q) {s} is equivalent in all ways to f(x,y, [a:p,b:q], {s}). + * Spread arguments of sequence type count as unlabeled arguments, + * while spread arguments of map type count as labeled arguments. + * (This distinction must sometimes be checked dynamically.) + * + * A plain unlabeled argument is allowed to match a trailing Map or Closure argument: + * f(x, a:p) {s} === f(*[ x, [a:p], {s} ]) + */ +// AST is [METHOD_CALL, callee, ELIST? CLOSABLE_BLOCK?]. +// Note that callee is often of the form x.y but not always. +// If the callee is not of the form x.y, then an implicit .call is needed. +// Parameter callee is only "null" when called from newExpression +methodCallArgs[AST callee] + : + LPAREN! + al:argList! + RPAREN! + { if (callee != null && callee.getFirstChild() != null) { + //method call like obj.method() + #methodCallArgs = #(create(METHOD_CALL, "(",callee.getFirstChild(),LT(1)), callee, al); + } else { + //method call like method() or new Expr(), in the latter case "callee" is null + #methodCallArgs = #(create(METHOD_CALL, "(",callee, LT(1)), callee, al); + } + } + // GRECLIPSE add + exception + catch [RecognitionException e] { + if (#al != null) { + reportError(e); + // copy of the block above - lets build it (assuming that all that was missing was the RPAREN) + if (callee != null && callee.getFirstChild() != null) { + // method call like obj.method() + #methodCallArgs = #(create(METHOD_CALL,"(",callee.getFirstChild(),LT(1)),callee,al); + } else { + // method call like method() or new Expr(), in the latter case "callee" is null + #methodCallArgs = #(create(METHOD_CALL,"(",callee,LT(1)),callee,al); + } + } else { + throw e; + } + } + // GRECLIPSE end + ; + +/** An appended block follows any expression. + * If the expression is not a method call, it is given an empty argument list. + */ +appendedBlock[AST callee] + : + /* FIXME DECIDE: should appended blocks accept labels? + ( (IDENT COLON nls LCURLY)=> + IDENT c:COLON^ {#c.setType(LABELED_ARG);} nls! + )? */ + cb:closableBlock! + { + // If the callee is itself a call, flatten the AST. + if (callee != null && callee.getType() == METHOD_CALL) { + #appendedBlock = #(create(METHOD_CALL, "(",callee,LT(1)), + callee.getFirstChild(), cb); + } else { + #appendedBlock = #(create(METHOD_CALL, "{",callee,LT(1)), callee, cb); + } + } + ; + +/** An expression may be followed by [...]. + * Unlike Java, these brackets may contain a general argument list, + * which is passed to the array element operator, which can make of it what it wants. + * The brackets may also be empty, as in T[]. This is how Groovy names array types. + *

Returned AST is [INDEX_OP, indexee, ELIST]. + */ +indexPropertyArgs[AST indexee] + : + lb:LBRACK + al:argList! + RBRACK! + { if (indexee != null && indexee.getFirstChild() != null) { + //expression like obj.index[] + #indexPropertyArgs = #(create(INDEX_OP, "INDEX_OP",indexee.getFirstChild(),LT(1)), lb, indexee, al); + } else { + //expression like obj[] + #indexPropertyArgs = #(create(INDEX_OP, "INDEX_OP",indexee,LT(1)), lb, indexee, al); + } + } + ; + +// assignment expression (level 15) +assignmentExpression[int lc_stmt] + : conditionalExpression[lc_stmt] + ( + ( ASSIGN^ + | PLUS_ASSIGN^ + | MINUS_ASSIGN^ + | STAR_ASSIGN^ + | DIV_ASSIGN^ + | MOD_ASSIGN^ + | SR_ASSIGN^ + | BSR_ASSIGN^ + | SL_ASSIGN^ + | BAND_ASSIGN^ + | BXOR_ASSIGN^ + | BOR_ASSIGN^ + | STAR_STAR_ASSIGN^ + //| USEROP_13^ //DECIDE: This is how user-define ops would show up. + ) + nls! + expressionStatementNoCheck + // If left-context of {x = y} is a statement boundary, + // define the left-context of y as an initializer. + )? + ; + +// conditional test (level 14) +conditionalExpression[int lc_stmt] + : logicalOrExpression[lc_stmt] + ( + (nls! ELVIS_OPERATOR)=> nls! ELVIS_OPERATOR^ nls! conditionalExpression[0] + | (nls! QUESTION)=> nls! QUESTION^ nls! assignmentExpression[0] nls! COLON! nls! conditionalExpression[0] + // GRECLIPSE add + exception + catch [RecognitionException e] { + // keep AST if recognition failed at or after ':' + if (currentAST.root.getNumberOfChildren() > 1) { + reportError(e); + } else { + throw e; + } + } + // GRECLIPSE end + )? + ; + + +// logical or (||) (level 13) +logicalOrExpression[int lc_stmt] + : logicalAndExpression[lc_stmt] (LOR^ nls! logicalAndExpression[0])* + ; + + +// logical and (&&) (level 12) +logicalAndExpression[int lc_stmt] + : inclusiveOrExpression[lc_stmt] (LAND^ nls! inclusiveOrExpression[0])* + ; + +// bitwise or non-short-circuiting or (|) (level 11) +inclusiveOrExpression[int lc_stmt] + : exclusiveOrExpression[lc_stmt] (BOR^ nls! exclusiveOrExpression[0])* + ; + + +// exclusive or (^) (level 10) +exclusiveOrExpression[int lc_stmt] + : andExpression[lc_stmt] (BXOR^ nls! andExpression[0])* + ; + + +// bitwise or non-short-circuiting and (&) (level 9) +andExpression[int lc_stmt] + : regexExpression[lc_stmt] (BAND^ nls! regexExpression[0])* + ; + +// regex find and match (=~ and ==~) (level 8.5) +// jez: moved =~ closer to precedence of == etc, as... +// 'if (foo =~ "a.c")' is very close in intent to 'if (foo == "abc")' +regexExpression[int lc_stmt] + : equalityExpression[lc_stmt] ((REGEX_FIND^ | REGEX_MATCH^) nls! equalityExpression[0])* + ; + +// equality/inequality (==/!=) (level 8) +equalityExpression[int lc_stmt] + : relationalExpression[lc_stmt] ((NOT_EQUAL^ | EQUAL^ |IDENTICAL^ |NOT_IDENTICAL^ | COMPARE_TO^) nls! relationalExpression[0])* + ; + +// boolean relational expressions (level 7) +relationalExpression[int lc_stmt] + : shiftExpression[lc_stmt] + ( options {greedy=true;} : ( + ( LT^ + | GT^ + | LE^ + | GE^ + | "in"^ + ) + nls! + shiftExpression[0] + + ) + | "instanceof"^ nls! typeSpec[true] + | "as"^ nls! typeSpec[true] //TODO: Rework to allow type expression? + )? + ; + + + +// bit shift expressions (level 6) +shiftExpression[int lc_stmt] + : additiveExpression[lc_stmt] + ( + ((SL^ | SR^ | BSR^) + | RANGE_INCLUSIVE^ + | RANGE_EXCLUSIVE^ + ) + nls! + additiveExpression[0] + )* + ; + + +// binary addition/subtraction (level 5) +additiveExpression[int lc_stmt] + : multiplicativeExpression[lc_stmt] + ( + options {greedy=true;} : + // Be greedy here, to favor {x+y} instead of {print +value} + (PLUS^ | MINUS^) nls! + multiplicativeExpression[0] + )* + ; + + +// multiplication/division/modulo (level 4) +multiplicativeExpression[int lc_stmt] + : ( INC^ nls! powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ ) nls! powerExpression[0])* ) + | ( DEC^ nls! powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ ) nls! powerExpression[0])* ) + | ( MINUS^ {#MINUS.setType(UNARY_MINUS);} nls! powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ ) nls! powerExpression[0])* ) + | ( PLUS^ {#PLUS.setType(UNARY_PLUS);} nls! powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ ) nls! powerExpression[0])* ) + | ( powerExpressionNotPlusMinus[lc_stmt] ((STAR^ | DIV^ | MOD^ ) nls! powerExpression[0])* ) + ; + +// ++(prefix)/--(prefix)/+(unary)/-(unary) (level 3) +unaryExpression[int lc_stmt] + : INC^ nls! unaryExpression[0] + | DEC^ nls! unaryExpression[0] + | MINUS^ {#MINUS.setType(UNARY_MINUS);} nls! unaryExpression[0] + | PLUS^ {#PLUS.setType(UNARY_PLUS);} nls! unaryExpression[0] + | unaryExpressionNotPlusMinus[lc_stmt] + ; + +// math power operator (**) (level 2) +powerExpression[int lc_stmt] + : unaryExpression[lc_stmt] (STAR_STAR^ nls! unaryExpression[0])* + ; + +// math power operator (**) (level 2) +// (without ++(prefix)/--(prefix)/+(unary)/-(unary)) +// The different rules are needed to avoid ambiguous selection +// of alternatives. +powerExpressionNotPlusMinus[int lc_stmt] + : unaryExpressionNotPlusMinus[lc_stmt] (STAR_STAR^ nls! unaryExpression[0])* + ; + +// ~(BNOT)/!(LNOT)/(type casting) (level 1) +unaryExpressionNotPlusMinus[int lc_stmt] + : BNOT^ nls! unaryExpression[0] + | LNOT^ nls! unaryExpression[0] + | ( // subrule allows option to shut off warnings + options { + // "(int" ambig with postfixExpr due to lack of sequence + // info in linear approximate LL(k). It's ok. Shut up. + generateAmbigWarnings=false; + } + : // If typecast is built in type, must be numeric operand + // Have to backtrack to see if operator follows + // FIXME: DECIDE: This syntax is wormy. Can we deprecate or remove? + (LPAREN builtInTypeSpec[true] RPAREN unaryExpression[0])=> + lpb:LPAREN^ {#lpb.setType(TYPECAST);} builtInTypeSpec[true] RPAREN! + unaryExpression[0] + + // Have to backtrack to see if operator follows. If no operator + // follows, it's a typecast. No semantic checking needed to parse. + // if it _looks_ like a cast, it _is_ a cast; else it's a "(expr)" + // FIXME: DECIDE: This syntax is wormy. Can we deprecate or remove? + // TODO: Rework this mess for Groovy. + | (LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus[0])=> + lp:LPAREN^ {#lp.setType(TYPECAST);} classTypeSpec[true] RPAREN! + unaryExpressionNotPlusMinus[0] + + | postfixExpression[lc_stmt] + ) + ; + +// qualified names, array expressions, method invocation, post inc/dec (level 1) +postfixExpression[int lc_stmt] + : + pathExpression[lc_stmt] + ( + options {greedy=true;} : + // possibly add on a post-increment or post-decrement. + // allows INC/DEC on too much, but semantics can check + in:INC^ {#in.setType(POST_INC);} + | de:DEC^ {#de.setType(POST_DEC);} + )? + ; + +// TODO: Move pathExpression to this point in the file. + +// the basic element of an expression +primaryExpression {Token first = LT(1);} + : IDENT + | constant + | newExpression + | "this" + | "super" + | pe:parenthesizedExpression! // (general stuff...) + {#primaryExpression = #(create(EXPR,"EXPR",first,LT(1)),pe);} + | closableBlockConstructorExpression + | listOrMapConstructorExpression + | stringConstructorExpression // "foo $bar baz"; presented as multiple tokens + | builtInType + ; + +// Note: This is guaranteed to be an EXPR AST. +// That is, parentheses are preserved, in case the walker cares about them. +// They are significant sometimes, as in (f(x)){y} vs. f(x){y}. +parenthesizedExpression +{ Token first = LT(1); + Token declaration = null; + boolean hasClosureList=false; + boolean firstContainsDeclaration=false; + boolean sce=false; +} + : LPAREN! + { declaration=LT(1); } + firstContainsDeclaration = strictContextExpression[true] + (SEMI! + {hasClosureList=true;} + (sce=strictContextExpression[true] | { astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); }) + )* + // if the first expression contained a declaration, + // but we are having only one expression at all, then + // the first declaration is of the kind (def a=b) + // which is invalid. Therefore if there was no closure + // list we let the compiler throw an error if the + // the first declaration exists + { + if (firstContainsDeclaration && !hasClosureList) + throw new NoViableAltException(declaration, getFilename()); + } + RPAREN! + { + if (hasClosureList) { + #parenthesizedExpression = #(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1)),#parenthesizedExpression); + } + } + // GRECLIPSE add + exception + catch [RecognitionException e] { + // GRECLIPSE-1213 - missing closing paren + reportError(e); + #parenthesizedExpression = (AST) currentAST.root; + } + // GRECLIPSE end + ; + +/** Things that can show up as expressions, but only in strict + * contexts like inside parentheses, argument lists, and list constructors. + */ +strictContextExpression[boolean allowDeclaration] +returns [boolean hasDeclaration=false] +{Token first = LT(1);} + : + ( ({allowDeclaration}? declarationStart)=> + {hasDeclaration=true;} singleDeclaration // used for both binding and value, as: while (String xx = nextln()) { println xx } + | expression[0] + | branchStatement // useful to embed inside expressions (cf. C++ throw) + | annotation // creates an annotation value + ) + // For the sake of the AST walker, mark nodes like this very clearly. + {#strictContextExpression = #(create(EXPR,"EXPR",first,LT(1)),#strictContextExpression);} + ; + +assignmentLessExpression {Token first = LT(1);} + : + ( conditionalExpression[0] + ) + // For the sake of the AST walker, mark nodes like this very clearly. + {#assignmentLessExpression = #(create(EXPR,"EXPR",first,LT(1)),#assignmentLessExpression);} + ; + + +closableBlockConstructorExpression + : closableBlock + ; + +// Groovy syntax for "$x $y" or /$x $y/. +stringConstructorExpression {Token first = LT(1);} + : cs:STRING_CTOR_START + { #cs.setType(STRING_LITERAL); } + + stringConstructorValuePart + + ( cm:STRING_CTOR_MIDDLE + { #cm.setType(STRING_LITERAL); } + stringConstructorValuePart + )* + + ce:STRING_CTOR_END + { #ce.setType(STRING_LITERAL); + #stringConstructorExpression = + #(create(STRING_CONSTRUCTOR,"STRING_CONSTRUCTOR",first,LT(1)), stringConstructorExpression); + } + ; + +stringConstructorValuePart + : + ( identifier + | "this" | "super" + | openOrClosableBlock + ) + ; + +/** + * A list constructor is a argument list enclosed in square brackets, without labels. + * Any argument can be decorated with a spread operator (*x), but not a label (a:x). + * Examples: [], [1], [1,2], [1,*l1,2], [*l1,*l2]. + * (The l1, l2 must be a sequence or null.) + *

+ * A map constructor is an argument list enclosed in square brackets, with labels everywhere, + * except on spread arguments, which stand for whole maps spliced in. + * A colon alone between the brackets also forces the expression to be an empty map constructor. + * Examples: [:], [a:1], [a:1,b:2], [a:1,*:m1,b:2], [*:m1,*:m2] + * (The m1, m2 must be a map or null.) + * Values associated with identical keys overwrite from left to right: + * [a:1,a:2] === [a:2] + *

+ * Some malformed constructor expressions are not detected in the parser, but in a post-pass. + * Bad examples: [1,b:2], [a:1,2], [:1]. + * (Note that method call arguments, by contrast, can be a mix of keyworded and non-keyworded arguments.) + */ +// The parser allows a mix of labeled and unlabeled arguments, but there must be a semantic check that +// the arguments are all labeled (or SPREAD_MAP_ARG) or all unlabeled (and not SPREAD_MAP_ARG). +listOrMapConstructorExpression + { boolean hasLabels = false; } + : lcon:LBRACK! + args:argList { hasLabels |= argListHasLabels; } // any argument label implies a map + RBRACK! + { int type = hasLabels ? MAP_CONSTRUCTOR : LIST_CONSTRUCTOR; + #listOrMapConstructorExpression = #(create(type,"[",lcon,LT(1)),args); + } + | + /* Special case: [:] is an empty map constructor. */ + emcon:LBRACK^ COLON! RBRACK! {#emcon.setType(MAP_CONSTRUCTOR);} + ; + + +/*OBS* +/** Match a, a.b.c refs, a.b.c(...) refs, a.b.c[], a.b.c[].class, + * and a.b.c.class refs. Also this(...) and super(...). Match + * this or super. + */ +/*OBS* +identPrimary + : (ta1:typeArguments!)? + IDENT + // Syntax for method invocation with type arguments is + // foo("blah") + ( + options { + // .ident could match here or in postfixExpression. + // We do want to match here. Turn off warning. + greedy=true; + // This turns the ambiguity warning of the second alternative + // off. See below. (The "ANTLR_LOOP_EXIT" predicate makes it non-issue) + warnWhenFollowAmbig=false; + } + // we have a new nondeterminism because of + // typeArguments... only a syntactic predicate will help... + // The problem is that this loop here conflicts with + // DOT typeArguments "super" in postfixExpression (k=2) + // A proper solution would require a lot of refactoring... + : (DOT (typeArguments)? IDENT) => + DOT^ (ta2:typeArguments!)? IDENT + | {ANTLR_LOOP_EXIT}? //(see documentation above) + )* + ( + options { + // ARRAY_DECLARATOR here conflicts with INDEX_OP in + // postfixExpression on LBRACK RBRACK. + // We want to match [] here, so greedy. This overcomes + // limitation of linear approximate lookahead. + greedy=true; + } + : ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} + // if the input is valid, only the last IDENT may + // have preceding typeArguments... rather hacky, this is... + {if (#ta2 != null) astFactory.addASTChild(currentAST, #ta2);} + {if (#ta2 == null) astFactory.addASTChild(currentAST, #ta1);} + argList RPAREN! + ) + | ( options {greedy=true;} : + lbc:LBRACK^ {#lbc.setType(ARRAY_DECLARATOR);} RBRACK! + )+ + )? + ; +*OBS*/ + +/** object instantiation. + * Trees are built as illustrated by the following input/tree pairs: + * + * new T() + * + * new + * | + * T -- ELIST + * | + * arg1 -- arg2 -- .. -- argn + * + * new int[] + * + * new + * | + * int -- ARRAY_DECLARATOR + * + * new int[] {1,2} + * + * new + * | + * int -- ARRAY_DECLARATOR -- ARRAY_INIT + * | + * EXPR -- EXPR + * | | + * 1 2 + * + * new int[3] + * new + * | + * int -- ARRAY_DECLARATOR + * | + * EXPR + * | + * 3 + * + * new int[1][2] + * + * new + * | + * int -- ARRAY_DECLARATOR + * | + * ARRAY_DECLARATOR -- EXPR + * | | + * EXPR 1 + * | + * 2 + * + */ + // GRECLIPSE edit +//newExpression {Token first = LT(1);} +// : "new"! nls! (ta:typeArguments!)? t:type! +newExpression {Token first = LT(1); int start = mark();} + : "new"! nls! (ta:typeArguments!)? (t:type!)? + ( nls! + mca:methodCallArgs[null]! + + ( + options { greedy=true; }: + cb:classBlock + )? + + {#mca = #mca.getFirstChild(); + #newExpression = #(create(LITERAL_new,"new",first,LT(1)),#ta,#t,#mca,#cb);} + + //java 1.1 + // Note: This will allow bad constructs like + // new int[4][][3] {exp,exp}. + // There needs to be a semantic check here... + // to make sure: + // a) [ expr ] and [ ] are not mixed + // b) [ expr ] and an init are not used together + | ad:newArrayDeclarator! //(arrayInitializer)? + // Groovy does not support Java syntax for initialized new arrays. + // Use sequence constructors instead. + {#newExpression = #(create(LITERAL_new,"new",first,LT(1)),#ta,#t,#ad);} + + ) + // GRECLIPSE add + // RECOVERY: missing '(' or '[' + exception + catch [RecognitionException e] { + if (#t == null) { + reportError("missing type for constructor call", first); + #newExpression = #(create(LITERAL_new,"new",first,LT(1)),#ta,null); + // probably others to include - or make this the default? + if (e instanceof MismatchedTokenException || e instanceof NoViableAltException) { + rewind(start); + consumeUntil(NLS); + } + } else if (#mca == null && #ad == null) { + reportError("expecting '(' or '[' after type name to continue new expression", #t); + #newExpression = #(create(LITERAL_new,"new",first,LT(1)),#ta,#t); + if (e instanceof MismatchedTokenException) { + rewind(start); + consume(); + consumeUntil(NLS); + } + } else { + throw e; + } + } + // GRECLIPSE end + ; + +argList + { + Token first = LT(1); + Token lastComma = null; + int hls=0, hls2=0; + boolean hasClosureList=false; + boolean trailingComma=false; + boolean sce=false; + } + : + // Note: nls not needed, since we are inside parens, + // and those insignificant newlines are suppressed by the lexer. + (hls=argument + (( + ( + SEMI! {hasClosureList=true;} + ( + sce=strictContextExpression[true] + | { astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); } + ) + )+ + {#argList = #(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1)),#argList);} + ) | ( + ( {lastComma = LT(1);} + COMMA! + ( + (hls2=argument {hls |= hls2;}) + | + ( + { if (trailingComma) throw new NoViableAltException(lastComma, getFilename()); + trailingComma=true; + } + ) + ) + + )* + {#argList = #(create(ELIST,"ELIST",first,LT(1)), argList);} + ) + ) | ( + {#argList = create(ELIST,"ELIST",first,LT(1));} + ) + ) + {argListHasLabels = (hls&1)!=0; } + // GRECLIPSE add + exception + catch [RecognitionException e] { + // in case of missing right paren "method(obj.exp", complete arglist + if (currentAST != null && !hasClosureList) { + #argList = #(create(ELIST,"ELIST",first,LT(1)),currentAST.root); + } else { + throw e; + } + } + // GRECLIPSE end + ; + +/** A single argument in (...) or [...]. Corresponds to to a method or closure parameter. + * May be labeled. May be modified by the spread operator '*' ('*:' for keywords). + */ +argument +returns [byte hasLabelOrSpread = 0] +{boolean sce=false;} + : + // Optional argument label. + // Usage: Specifies a map key, or a keyworded argument. + ( (argumentLabelStart) => + argumentLabel c:COLON^ {#c.setType(LABELED_ARG);} + + { hasLabelOrSpread |= 1; } // signal to caller the presence of a label + + | // Spread operator: f(*[a,b,c]) === f(a,b,c); f(1,*null,2) === f(1,2). + sp:STAR^ {#sp.setType(SPREAD_ARG);} + { hasLabelOrSpread |= 2; } // signal to caller the presence of a spread operator + // spread maps are marked, as f(*:m) for f(a:x, b:y) if m==[a:x, b:y] + ( + COLON! {#sp.setType(SPREAD_MAP_ARG);} + { hasLabelOrSpread |= 1; } // signal to caller the presence of a label + )? + )? + + sce=strictContextExpression[true] + { + require(LA(1) != COLON, + "illegal colon after argument expression", + "a complex label expression before a colon must be parenthesized"); + } + ; + +/** A label for an argument is of the form a:b, 'a':b, "a":b, (a):b, etc.. + * The labels in (a:b), ('a':b), and ("a":b) are in all ways equivalent, + * except that the quotes allow more spellings. + * Equivalent dynamically computed labels are (('a'):b) and ("${'a'}":b) + * but not ((a):b) or "$a":b, since the latter cases evaluate (a) as a normal identifier. + * Bottom line: If you want a truly variable label, use parens and say ((a):b). + */ +argumentLabel + : (IDENT) => + id:IDENT {#id.setType(STRING_LITERAL);} // identifiers are self-quoting in this context + | (keywordPropertyNames) => + kw:keywordPropertyNames {#kw.setType(STRING_LITERAL);} // identifiers are self-quoting in this context + | primaryExpression // dynamic expression + ; + +/** For lookahead only. Fast approximate parse of an argumentLabel followed by a colon. */ +argumentLabelStart! + // allow number and string literals as labels for maps + : ( + IDENT | keywordPropertyNames + | constantNumber | STRING_LITERAL + | (LPAREN | STRING_CTOR_START)=> balancedBrackets + ) + COLON + ; + +newArrayDeclarator + : ( + // CONFLICT: + // newExpression is a primaryExpression which can be + // followed by an array index reference. This is ok, + // as the generated code will stay in this loop as + // long as it sees an LBRACK (proper behavior) + options { + warnWhenFollowAmbig = false; + } + : + lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} + (expression[0])? + RBRACK! + )+ + ; + +/** Numeric, string, regexp, boolean, or null constant. */ +constant + : constantNumber + | STRING_LITERAL + | "true" + | "false" + | "null" + ; + +/** Numeric constant. */ +constantNumber + : NUM_INT + | NUM_FLOAT + | NUM_LONG + | NUM_DOUBLE + | NUM_BIG_INT + | NUM_BIG_DECIMAL + ; + +/** Fast lookahead across balanced brackets of all sorts. */ +balancedBrackets! + : LPAREN balancedTokens RPAREN + | LBRACK balancedTokens RBRACK + | LCURLY balancedTokens RCURLY + | STRING_CTOR_START balancedTokens STRING_CTOR_END + ; + +balancedTokens! + : ( balancedBrackets + | ~(LPAREN|LBRACK|LCURLY | STRING_CTOR_START + |RPAREN|RBRACK|RCURLY | STRING_CTOR_END) + )* + ; + +/** A statement separator is either a semicolon or a significant newline. + * Any number of additional (insignificant) newlines may accompany it. + */ +// (All the '!' signs simply suppress the default AST building.) +// Returns the type of the separator in this.sepToken, in case it matters. +sep! + : SEMI! + (options { greedy=true; }: NLS!)* + { sepToken = SEMI; } + | NLS! // this newline is significant! + { sepToken = NLS; } + ( + options { greedy=true; }: + SEMI! // this superfluous semicolon is gobbled + (options { greedy=true; }: NLS!)* + { sepToken = SEMI; } + )* + ; + +/** Zero or more insignificant newlines, all gobbled up and thrown away. */ +nls! + : + (options { greedy=true; }: NLS!)? + // Note: Use '?' rather than '*', relying on the fact that the lexer collapses + // adjacent NLS tokens, always. This lets the parser use its LL(3) lookahead + // to "see through" sequences of newlines. If there were a '*' here, the lookahead + // would be weaker, since the parser would have to be prepared for long sequences + // of NLS tokens. + ; + +/** Zero or more insignificant newlines, all gobbled up and thrown away, + * but a warning message is left for the user, if there was a newline. + */ +nlsWarn! + : + ( (NLS)=> + { addWarning( + "A newline at this point does not follow the Groovy Coding Conventions.", + "Keep this statement on one line, or use curly braces to break across multiple lines." + ); } + )? + nls! + ; + + +//---------------------------------------------------------------------------- +// The Groovy scanner +//---------------------------------------------------------------------------- +class GroovyLexer extends Lexer; + +options { + exportVocab=Groovy; // call the vocabulary "Groovy" + testLiterals=false; // don't automatically test for literals + k=4; // four characters of lookahead + charVocabulary='\u0000'..'\uFFFF'; + // without inlining some bitset tests, couldn't do unicode; + // I need to make ANTLR generate smaller bitsets; see + // bottom of GroovyLexer.java + codeGenBitsetTestThreshold=20; +} + +{ + /** flag for enabling the "assert" keyword */ + private boolean assertEnabled = true; + /** flag for enabling the "enum" keyword */ + private boolean enumEnabled = true; + /** flag for including whitespace tokens (for IDE preparsing) */ + private boolean whitespaceIncluded = false; + + /** Enable the "assert" keyword */ + public void enableAssert(boolean shouldEnable) { assertEnabled = shouldEnable; } + /** Query the "assert" keyword state */ + public boolean isAssertEnabled() { return assertEnabled; } + /** Enable the "enum" keyword */ + public void enableEnum(boolean shouldEnable) { enumEnabled = shouldEnable; } + /** Query the "enum" keyword state */ + public boolean isEnumEnabled() { return enumEnabled; } + + /** Include whitespace tokens. Note that this breaks the parser. */ + public void setWhitespaceIncluded(boolean z) { whitespaceIncluded = z; } + /** Are whitespace tokens included? */ + public boolean isWhitespaceIncluded() { return whitespaceIncluded; } + + { + // Initialization actions performed on construction. + setTabSize(1); // get rid of special tab interpretation, for IDEs and general clarity + } + + /** Bumped when inside '[x]' or '(x)', reset inside '{x}'. See ONE_NL. */ + protected int parenLevel = 0; + protected int suppressNewline = 0; // be really mean to newlines inside strings + protected static final int SCS_TYPE = 3, SCS_VAL = 4, SCS_LIT = 8, SCS_LIMIT = 16; + protected static final int SCS_SQ_TYPE = 0, SCS_TQ_TYPE = 1, SCS_RE_TYPE = 2, SCS_DRE_TYPE = 3; + protected int stringCtorState = 0; // hack string and regexp constructor boundaries + /** Push parenLevel here and reset whenever inside '{x}'. */ + protected ArrayList parenLevelStack = new ArrayList(); + protected int lastSigTokenType = EOF; // last returned non-whitespace token + + public void setTokenObjectClass(String name) {/*ignore*/} + + protected Token makeToken(int t) { + GroovySourceToken tok = new GroovySourceToken(t); + tok.setColumn(inputState.getTokenStartColumn()); + tok.setLine(inputState.getTokenStartLine()); + tok.setColumnLast(inputState.getColumn()); + tok.setLineLast(inputState.getLine()); + return tok; + } + + protected void pushParenLevel() { + parenLevelStack.add(Integer.valueOf(parenLevel*SCS_LIMIT + stringCtorState)); + parenLevel = 0; + stringCtorState = 0; + } + + protected void popParenLevel() { + int npl = parenLevelStack.size(); + if (npl == 0) return; + int i = ((Integer) parenLevelStack.remove(--npl)).intValue(); + parenLevel = i / SCS_LIMIT; + stringCtorState = i % SCS_LIMIT; + } + + protected void restartStringCtor(boolean expectLiteral) { + if (stringCtorState != 0) { + stringCtorState = (expectLiteral? SCS_LIT: SCS_VAL) + (stringCtorState & SCS_TYPE); + } + } + + protected boolean allowRegexpLiteral() { + return !isExpressionEndingToken(lastSigTokenType); + } + + /** Return true for an operator or punctuation which can end an expression. + * Return true for keywords, identifiers, and literals. + * Return true for tokens which can end expressions (right brackets, ++, --). + * Return false for EOF and all other operator and punctuation tokens. + * Used to suppress the recognition of /foo/ as opposed to the simple division operator '/'. + */ + // Cf. 'constant' and 'balancedBrackets' rules in the grammar.) + protected static boolean isExpressionEndingToken(int ttype) { + switch (ttype) { + case INC: // x++ / y + case DEC: // x-- / y + case RPAREN: // (x) / y + case RBRACK: // f[x] / y + case RCURLY: // f{x} / y + case STRING_LITERAL: // "x" / y + case STRING_CTOR_END: // "$x" / y + case NUM_INT: // 0 / y + case NUM_FLOAT: // 0f / y + case NUM_LONG: // 0l / y + case NUM_DOUBLE: // 0.0 / y + case NUM_BIG_INT: // 0g / y + case NUM_BIG_DECIMAL: // 0.0g / y + case IDENT: // x / y + // and a bunch of keywords (all of them; no sense picking and choosing): + case LITERAL_as: + case LITERAL_assert: + case LITERAL_boolean: + case LITERAL_break: + case LITERAL_byte: + case LITERAL_case: + case LITERAL_catch: + case LITERAL_char: + case LITERAL_class: + case LITERAL_continue: + case LITERAL_def: + case LITERAL_default: + case LITERAL_double: + case LITERAL_else: + case LITERAL_enum: + case LITERAL_extends: + case LITERAL_false: + case LITERAL_finally: + case LITERAL_float: + case LITERAL_for: + case LITERAL_if: + case LITERAL_implements: + case LITERAL_import: + case LITERAL_in: + case LITERAL_instanceof: + case LITERAL_int: + case LITERAL_interface: + case LITERAL_long: + case LITERAL_native: + case LITERAL_new: + case LITERAL_null: + case LITERAL_package: + case LITERAL_private: + case LITERAL_protected: + case LITERAL_public: + case LITERAL_return: + case LITERAL_short: + case LITERAL_static: + case LITERAL_super: + case LITERAL_switch: + case LITERAL_synchronized: + case LITERAL_trait: + case LITERAL_this: + case LITERAL_threadsafe: + case LITERAL_throw: + case LITERAL_throws: + case LITERAL_transient: + case LITERAL_true: + case LITERAL_try: + case LITERAL_void: + case LITERAL_volatile: + case LITERAL_while: + return true; + default: + return false; + } + } + + protected void newlineCheck(boolean check) throws RecognitionException { + if (check && suppressNewline > 0) { + require(suppressNewline == 0, + "end of line reached within a simple string 'x' or \"x\" or /x/", + "for multi-line literals, use triple quotes '''x''' or \"\"\"x\"\"\" or /x/ or $/x/$"); + suppressNewline = 0; // shut down any flood of errors + } + newline(); + } + + protected boolean atValidDollarEscape() throws CharStreamException { + // '$' (('{' | LETTER) => + int k = 1; + char lc = LA(k++); + if (lc != '$') return false; + lc = LA(k++); + return (lc == '{' || (lc != '$' && Character.isJavaIdentifierStart(lc))); + } + + protected boolean atDollarDollarEscape() throws CharStreamException { + return LA(1) == '$' && LA(2) == '$'; + } + + protected boolean atMultiCommentStart() throws CharStreamException { + return LA(1) == '/' && LA(2) == '*'; + } + + protected boolean atDollarSlashEscape() throws CharStreamException { + return LA(1) == '$' && LA(2) == '/'; + } + + /** This is a bit of plumbing which resumes collection of string constructor bodies, + * after an embedded expression has been parsed. + * Usage: new GroovyRecognizer(new GroovyLexer(in).plumb()). + */ + public TokenStream plumb() { + return new TokenStream() { + public Token nextToken() throws TokenStreamException { + if (stringCtorState >= SCS_LIT) { + // This goo is modeled upon the ANTLR code for nextToken: + int quoteType = (stringCtorState & SCS_TYPE); + stringCtorState = 0; // get out of this mode, now + resetText(); + try { + switch (quoteType) { + case SCS_SQ_TYPE: + mSTRING_CTOR_END(true, /*fromStart:*/false, false); break; + case SCS_TQ_TYPE: + mSTRING_CTOR_END(true, /*fromStart:*/false, true); break; + case SCS_RE_TYPE: + mREGEXP_CTOR_END(true, /*fromStart:*/false); break; + case SCS_DRE_TYPE: + mDOLLAR_REGEXP_CTOR_END(true, /*fromStart:*/false); break; + default: throw new AssertionError(false); + } + lastSigTokenType = _returnToken.getType(); + return _returnToken; + } catch (RecognitionException e) { + throw new TokenStreamRecognitionException(e); + } catch (CharStreamException cse) { + if ( cse instanceof CharStreamIOException ) { + throw new TokenStreamIOException(((CharStreamIOException)cse).io); + } + else { + throw new TokenStreamException(cse.getMessage()); + } + } + } + Token token = GroovyLexer.this.nextToken(); + int lasttype = token.getType(); + if (whitespaceIncluded) { + switch (lasttype) { // filter out insignificant types + case WS: + case ONE_NL: + case SL_COMMENT: + case ML_COMMENT: + lasttype = lastSigTokenType; // back up! + } + } + lastSigTokenType = lasttype; + return token; + } + }; + } + + // stuff to adjust ANTLR's tracing machinery + public static boolean tracing = false; // only effective if antlr.Tool is run with -traceLexer + public void traceIn(String rname) throws CharStreamException { + if (!GroovyLexer.tracing) return; + super.traceIn(rname); + } + public void traceOut(String rname) throws CharStreamException { + if (!GroovyLexer.tracing) return; + if (_returnToken != null) rname += tokenStringOf(_returnToken); + super.traceOut(rname); + } + private static java.util.HashMap ttypes; + private static String tokenStringOf(Token t) { + if (ttypes == null) { + java.util.HashMap map = new java.util.HashMap(); + java.lang.reflect.Field[] fields = GroovyTokenTypes.class.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getType() != int.class) continue; + try { + map.put(fields[i].get(null), fields[i].getName()); + } catch (IllegalAccessException ee) { + } + } + ttypes = map; + } + Integer tt = Integer.valueOf(t.getType()); + Object ttn = ttypes.get(tt); + if (ttn == null) ttn = "<"+tt+">"; + return "["+ttn+",\""+t.getText()+"\"]"; + } + + protected GroovyRecognizer parser; // little-used link; TODO: get rid of + private void require(boolean z, String problem, String solution) throws SemanticException { + // TODO: Direct to a common error handler, rather than through the parser. + if (!z && parser!=null) parser.requireFailed(problem, solution); + if (!z) { + int lineNum = inputState.getLine(), colNum = inputState.getColumn(); + throw new SemanticException(problem + ";\n solution: " + solution, + getFilename(), lineNum, colNum); + } + } +} + +// TODO: Borneo-style ops. + +// OPERATORS +QUESTION options {paraphrase="'?'";} : '?' ; +LPAREN options {paraphrase="'('";} : '(' {++parenLevel;}; +RPAREN options {paraphrase="')'";} : ')' {--parenLevel;}; +LBRACK options {paraphrase="'['";} : '[' {++parenLevel;}; +RBRACK options {paraphrase="']'";} : ']' {--parenLevel;}; +LCURLY options {paraphrase="'{'";} : '{' {pushParenLevel();}; +RCURLY options {paraphrase="'}'";} : '}' {popParenLevel(); if(stringCtorState!=0) restartStringCtor(true);}; +COLON options {paraphrase="':'";} : ':' ; +COMMA options {paraphrase="','";} : ',' ; +DOT options {paraphrase="'.'";} : '.' ; +ASSIGN options {paraphrase="'='";} : '=' ; +COMPARE_TO options {paraphrase="'<=>'";} : "<=>" ; +EQUAL options {paraphrase="'=='";} : "==" ; +IDENTICAL options {paraphrase="'==='";} : "===" ; +LNOT options {paraphrase="'!'";} : '!' ; +BNOT options {paraphrase="'~'";} : '~' ; +NOT_EQUAL options {paraphrase="'!='";} : "!=" ; +NOT_IDENTICAL options {paraphrase="'!=='";} : "!==" ; +protected //switched from combined rule +DIV options {paraphrase="'/'";} : '/' ; +protected //switched from combined rule +DIV_ASSIGN options {paraphrase="'/='";} : "/=" ; +PLUS options {paraphrase="'+'";} : '+' ; +PLUS_ASSIGN options {paraphrase="'+='";} : "+=" ; +INC options {paraphrase="'++'";} : "++" ; +MINUS options {paraphrase="'-'";} : '-' ; +MINUS_ASSIGN options {paraphrase="'-='";} : "-=" ; +DEC options {paraphrase="'--'";} : "--" ; +STAR options {paraphrase="'*'";} : '*' ; +STAR_ASSIGN options {paraphrase="'*='";} : "*=" ; +MOD options {paraphrase="'%'";} : '%' ; +MOD_ASSIGN options {paraphrase="'%='";} : "%=" ; +SR options {paraphrase="'>>'";} : ">>" ; +SR_ASSIGN options {paraphrase="'>>='";} : ">>=" ; +BSR options {paraphrase="'>>>'";} : ">>>" ; +BSR_ASSIGN options {paraphrase="'>>>='";} : ">>>=" ; +GE options {paraphrase="'>='";} : ">=" ; +GT options {paraphrase="'>'";} : ">" ; +SL options {paraphrase="'<<'";} : "<<" ; +SL_ASSIGN options {paraphrase="'<<='";} : "<<=" ; +LE options {paraphrase="'<='";} : "<=" ; +LT options {paraphrase="'<'";} : '<' ; +BXOR options {paraphrase="'^'";} : '^' ; +BXOR_ASSIGN options {paraphrase="'^='";} : "^=" ; +BOR options {paraphrase="'|'";} : '|' ; +BOR_ASSIGN options {paraphrase="'|='";} : "|=" ; +LOR options {paraphrase="'||'";} : "||" ; +BAND options {paraphrase="'&'";} : '&' ; +BAND_ASSIGN options {paraphrase="'&='";} : "&=" ; +LAND options {paraphrase="'&&'";} : "&&" ; +SEMI options {paraphrase="';'";} : ';' ; +protected +DOLLAR options {paraphrase="'$'";} : '$' ; +RANGE_INCLUSIVE options {paraphrase="'..'";} : ".." ; +RANGE_EXCLUSIVE options {paraphrase="'..<'";} : "..<" ; +TRIPLE_DOT options {paraphrase="'...'";} : "..." ; +SPREAD_DOT options {paraphrase="'*.'";} : "*." ; +OPTIONAL_DOT options {paraphrase="'?.'";} : "?." ; +ELVIS_OPERATOR options {paraphrase="'?:'";} : "?:" ; +MEMBER_POINTER options {paraphrase="'.&'";} : ".&" ; +REGEX_FIND options {paraphrase="'=~'";} : "=~" ; +REGEX_MATCH options {paraphrase="'==~'";} : "==~" ; +STAR_STAR options {paraphrase="'**'";} : "**" ; +STAR_STAR_ASSIGN options {paraphrase="'**='";} : "**=" ; +CLOSABLE_BLOCK_OP options {paraphrase="'->'";} : "->" ; + +// Whitespace -- ignored +WS +options { + paraphrase="whitespace"; +} + : + ( + options { greedy=true; }: + ' ' + | '\t' + | '\f' + | '\\' ONE_NL[false] + )+ + { if (!whitespaceIncluded) _ttype = Token.SKIP; } + ; + +protected +ONE_NL![boolean check] +options { + paraphrase="a newline"; +} + : // handle newlines, which are significant in Groovy + ( options {generateAmbigWarnings=false;} + : "\r\n" // Evil DOS + | '\r' // Macintosh + | '\n' // Unix (the right way) + ) + { + // update current line number for error reporting + newlineCheck(check); + } + ; + +// GRECLIPSE add +protected +ONE_NL_KEEP[boolean check] +options { + paraphrase="a newline"; +} + : // handle newlines, which are significant in Groovy + ( options {generateAmbigWarnings=false;} + : "\r\n" // Evil DOS + | '\r' // Macintosh + | '\n' // Unix (the right way) + ) + { + // update current line number for error reporting + newlineCheck(check); + } + ; +// GRECLIPSE end + +// Group any number of newlines (with comments and whitespace) into a single token. +// This reduces the amount of parser lookahead required to parse around newlines. +// It is an invariant that the parser never sees NLS tokens back-to-back. +NLS +options { + paraphrase="some newlines, whitespace or comments"; +} + : ONE_NL[true] + ( {!whitespaceIncluded}? + (ONE_NL[true] | WS | SL_COMMENT | ML_COMMENT)+ + // (gobble, gobble)* + )? + // Inside (...) and [...] but not {...}, ignore newlines. + { if (whitespaceIncluded) { + // keep the token as-is + } else if (parenLevel != 0) { + // when directly inside parens, all newlines are ignored here + $setType(Token.SKIP); + } else { + // inside {...}, newlines must be explicitly matched as 'nls!' + $setText(""); + } + } + ; + +// Single-line comments +SL_COMMENT +options { + paraphrase="a single line comment"; +} + : "//" + // GRECLIPSE add + { if (parser != null) parser.startComment(inputState.getLine(), inputState.getColumn() - 2); } + // GRECLIPSE end + ( + options { greedy = true; }: + // '\uffff' means the EOF character. + // This will fix the issue GROOVY-766 (infinite loop). + ~('\n'|'\r'|'\uffff') + )* + // GRECLIPSE add + { if (parser != null) parser.endComment(0, inputState.getLine(), inputState.getColumn(), String.valueOf(text.getBuffer(), _begin, text.length() - _begin)); + // GRECLIPSE end + if (!whitespaceIncluded) $setType(Token.SKIP); + } + //This might be significant, so don't swallow it inside the comment: + //ONE_NL + ; + +// Script-header comments +SH_COMMENT +options { + paraphrase="a script header"; +} + : {getLine() == 1 && getColumn() == 1}? "#!" + ( + options { greedy = true; }: + // '\uffff' means the EOF character. + // This will fix the issue GROOVY-766 (infinite loop). + ~('\n'|'\r'|'\uffff') + )* + { if (!whitespaceIncluded) $setType(Token.SKIP); } + //ONE_NL //Never a significant newline, but might as well separate it. + ; + +// multiple-line comments +ML_COMMENT +options { + paraphrase="a multi-line comment"; +} + : { atMultiCommentStart() }? "/*" + // GRECLIPSE add + { if (parser != null) parser.startComment(inputState.getLine(), inputState.getColumn() - 2); } + // GRECLIPSE end + ( /* '\r' '\n' can be matched in one alternative or by matching + '\r' in one iteration and '\n' in another. I am trying to + handle any flavor of newline that comes in, but the language + that allows both "\r\n" and "\r" and "\n" to all be valid + newline is ambiguous. Consequently, the resulting grammar + must be ambiguous. I'm shutting this warning off. + */ + options { + generateAmbigWarnings=false; + } + : + ( '*' ~'/' ) => '*' + // GRECLIPSE edit + //| ONE_NL[true] + | ONE_NL_KEEP[true] + // GRECLIPSE end + | ~('*'|'\n'|'\r'|'\uffff') + )* + "*/" + // GRECLIPSE add + { if (parser != null) parser.endComment(1, inputState.getLine(), inputState.getColumn(), String.valueOf(text.getBuffer(), _begin, text.length() - _begin)); + if (!whitespaceIncluded) $setType(Token.SKIP); + } + ; + + +// string literals +STRING_LITERAL +options { + paraphrase="a string literal"; +} + {int tt=0;} + : ("'''") => //...shut off ambiguity warning + "'''"! + ( STRING_CH | ESC | '"' | '$' | STRING_NL[true] + | ('\'' (~'\'' | '\'' ~'\'')) => '\'' // allow 1 or 2 close quotes + )* + "'''"! + | '\''! + {++suppressNewline;} + ( STRING_CH | ESC | '"' | '$' )* + {--suppressNewline;} + '\''! + | ("\"\"\"") => //...shut off ambiguity warning + "\"\"\""! + tt=STRING_CTOR_END[true, /*tripleQuote:*/ true] + {$setType(tt);} + | '"'! + {++suppressNewline;} + tt=STRING_CTOR_END[true, /*tripleQuote:*/ false] + {$setType(tt);} + ; + +protected +STRING_CTOR_END[boolean fromStart, boolean tripleQuote] +returns [int tt=STRING_CTOR_END] +options { + paraphrase="a string literal end"; +} + { boolean dollarOK = false; } + : + ( + options { greedy = true; }: + STRING_CH | ESC | '\'' | STRING_NL[tripleQuote] + | ('"' (~'"' | '"' ~'"'))=> {tripleQuote}? '"' // allow 1 or 2 close quotes + )* + ( ( { !tripleQuote }? "\""! + | { tripleQuote }? "\"\"\""! + ) + { + if (fromStart) tt = STRING_LITERAL; // plain string literal! + if (!tripleQuote) {--suppressNewline;} + // done with string constructor! + //assert(stringCtorState == 0); + } + | {dollarOK = atValidDollarEscape();} + '$'! + { + require(dollarOK, + "illegal string body character after dollar sign", + "either escape a literal dollar sign \"\\$5\" or bracket the value expression \"${5}\""); + // Yes, it's a string constructor, and we've got a value part. + tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE); + stringCtorState = SCS_VAL + (tripleQuote? SCS_TQ_TYPE: SCS_SQ_TYPE); + } + ) + { $setType(tt); } + ; + +protected +STRING_CH +options { + paraphrase="a string character"; +} + : ~('"'|'\''|'\\'|'$'|'\n'|'\r'|'\uffff') + ; + +REGEXP_LITERAL +options { + paraphrase="a multiline regular expression literal"; +} + {int tt=0;} + : { !atMultiCommentStart() }? + ( {allowRegexpLiteral()}? + '/'! + {++suppressNewline;} + //Do this, but require it to be non-trivial: REGEXP_CTOR_END[true] + // There must be at least one symbol or $ escape, lest the regexp collapse to '//'. + // (This should be simpler, but I don't know how to do it w/o ANTLR warnings vs. '//' comments.) + ( + REGEXP_SYMBOL + tt=REGEXP_CTOR_END[true] + | {!atValidDollarEscape()}? '$' + tt=REGEXP_CTOR_END[true] + | '$'! + { + // Yes, it's a regexp constructor, and we've got a value part. + tt = STRING_CTOR_START; + stringCtorState = SCS_VAL + SCS_RE_TYPE; + } + ) + {$setType(tt);} + + | ( '/' ~'=' ) => DIV {$setType(DIV);} + | DIV_ASSIGN {$setType(DIV_ASSIGN);} + ) + ; + +DOLLAR_REGEXP_LITERAL +options { + paraphrase="a multiline dollar escaping regular expression literal"; +} + {int tt=0;} + : {allowRegexpLiteral()}? "$/"! + // Do this, but require it to be non-trivial: DOLLAR_REGEXP_CTOR_END[true] + // There must be at least one symbol or $ escape, otherwise the regexp collapses. + ( + DOLLAR_REGEXP_SYMBOL + tt=DOLLAR_REGEXP_CTOR_END[true] + | {!atValidDollarEscape() && !atDollarSlashEscape() && !atDollarDollarEscape()}? '$' + tt=DOLLAR_REGEXP_CTOR_END[true] + | + ('$' '/') => ESCAPED_SLASH + tt=DOLLAR_REGEXP_CTOR_END[true] + | + ('$' '$') => ESCAPED_DOLLAR + tt=DOLLAR_REGEXP_CTOR_END[true] + | + '$'! + { + // Yes, it's a regexp constructor, and we've got a value part. + tt = STRING_CTOR_START; + stringCtorState = SCS_VAL + SCS_DRE_TYPE; + } + ) + {$setType(tt);} + ; + +protected +REGEXP_CTOR_END[boolean fromStart] +returns [int tt=STRING_CTOR_END] +options { + paraphrase="a multiline regular expression literal end"; +} + : + ( + options { greedy = true; }: + REGEXP_SYMBOL + | + {!atValidDollarEscape()}? '$' + )* + ( '/'! + { + if (fromStart) tt = STRING_LITERAL; // plain regexp literal! + {--suppressNewline;} + // done with regexp constructor! + //assert(stringCtorState == 0); + } + | '$'! + { + // Yes, it's a regexp constructor, and we've got a value part. + tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE); + stringCtorState = SCS_VAL + SCS_RE_TYPE; + } + ) + { $setType(tt); } + ; + +protected +DOLLAR_REGEXP_CTOR_END[boolean fromStart] +returns [int tt=STRING_CTOR_END] +options { + paraphrase="a multiline dollar escaping regular expression literal end"; +} + : + ( + options { greedy = true; }: + { !(LA(1) == '/' && LA(2) == '$') }? DOLLAR_REGEXP_SYMBOL + | + ('$' '/') => ESCAPED_SLASH + | + ('$' '$') => ESCAPED_DOLLAR + | + {!atValidDollarEscape() && !atDollarSlashEscape() && !atDollarDollarEscape()}? '$' + )* + ( + "/$"! + { + if (fromStart) tt = STRING_LITERAL; // plain regexp literal! + } + | '$'! + { + // Yes, it's a regexp constructor, and we've got a value part. + tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE); + stringCtorState = SCS_VAL + SCS_DRE_TYPE; + } + ) + { $setType(tt); } + ; + +protected ESCAPED_SLASH : '$' '/' { $setText('/'); }; + +protected ESCAPED_DOLLAR : '$' '$' { $setText('$'); }; + +protected +REGEXP_SYMBOL +options { + paraphrase="a multiline regular expression character"; +} + : + ~('/'|'$'|'\\'|'\n'|'\r'|'\uffff') + | { LA(2)!='/' && LA(2)!='\n' && LA(2)!='\r' }? '\\' // backslash only escapes '/' and EOL + | '\\' '/' { $setText('/'); } + | STRING_NL[true] + |! '\\' ONE_NL[false] + ; + +protected +DOLLAR_REGEXP_SYMBOL +options { + paraphrase="a multiline dollar escaping regular expression character"; +} + : + ~('$' | '\\' | '/' | '\n' | '\r' | '\uffff') + | { LA(2)!='\n' && LA(2)!='\r' }? '\\' // backslash only escapes EOL + | ('/' ~'$') => '/' // allow a slash if not followed by a $ + | STRING_NL[true] + |! '\\' ONE_NL[false] + ; + +// escape sequence -- note that this is protected; it can only be called +// from another lexer rule -- it will not ever directly return a token to +// the parser +// There are various ambiguities hushed in this rule. The optional +// '0'...'9' digit matches should be matched here rather than letting +// them go back to STRING_LITERAL to be matched. ANTLR does the +// right thing by matching immediately; hence, it's ok to shut off +// the FOLLOW ambig warnings. +protected +ESC +options { + paraphrase="an escape sequence"; +} + : '\\'! + ( 'n' {$setText("\n");} + | 'r' {$setText("\r");} + | 't' {$setText("\t");} + | 'b' {$setText("\b");} + | 'f' {$setText("\f");} + | '"' + | '\'' + | '\\' + | '$' //escape Groovy $ operator uniformly also + | ('u')+ {$setText("");} + HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT + {char ch = (char)Integer.parseInt($getText,16); $setText(ch);} + | '0'..'3' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + )? + {char ch = (char)Integer.parseInt($getText,8); $setText(ch);} + | '4'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + {char ch = (char)Integer.parseInt($getText,8); $setText(ch);} + ) + |! '\\' ONE_NL[false] + ; + +protected +STRING_NL[boolean allowNewline] +options { + paraphrase="a newline inside a string"; +} + : {if (!allowNewline) throw new MismatchedCharException('\n', '\n', true, this); } + ONE_NL[false] { $setText('\n'); } + ; + + +// hexadecimal digit (again, note it's protected!) +protected +HEX_DIGIT +options { + paraphrase="a hexadecimal digit"; +} + : ('0'..'9'|'A'..'F'|'a'..'f') + ; + + +// a dummy rule to force vocabulary to be all characters (except special +// ones that ANTLR uses internally (0 to 2) +protected +VOCAB +options { + paraphrase="a character"; +} + : '\3'..'\377' + ; + + +// an identifier. Note that testLiterals is set to true! This means +// that after we match the rule, we look in the literals table to see +// if it's a literal or really an identifier +IDENT +options { + paraphrase="an identifier"; +} + //options {testLiterals=true;} // Actually, this is done manually in the actions below. + : ( {stringCtorState == 0}? (DOLLAR|LETTER) (LETTER|DIGIT|DOLLAR)* | LETTER (LETTER|DIGIT)* ) + { + if (stringCtorState != 0) { + if (LA(1) == '.' && LA(2) != '$' && + Character.isJavaIdentifierStart(LA(2))) { + // pick up another name component before going literal again: + restartStringCtor(false); + } else { + // go back to the string + restartStringCtor(true); + } + } + int ttype = testLiteralsTable(IDENT); + // Java doesn't have the keywords 'as', 'in' or 'def so we make some allowances + // for them in package names for better integration with existing Java packages + if ((ttype == LITERAL_as || ttype == LITERAL_def || ttype == LITERAL_in || ttype == LITERAL_trait) && + (LA(1) == '.' || lastSigTokenType == DOT || lastSigTokenType == LITERAL_package)) { + ttype = IDENT; + } + // allow access to classes with the name package + if ((ttype == LITERAL_package) && + (LA(1) == '.' || lastSigTokenType == DOT || lastSigTokenType == LITERAL_import + || (LA(1) == ')' && lastSigTokenType == LPAREN))) { + ttype = IDENT; + } + if (ttype == LITERAL_static && LA(1) == '.') { + ttype = IDENT; + } + + $setType(ttype); + + // check if "assert" keyword is enabled + if (assertEnabled && "assert".equals($getText)) { + $setType(LITERAL_assert); // set token type for the rule in the parser + } + // check if "enum" keyword is enabled + if (enumEnabled && "enum".equals($getText)) { + $setType(LITERAL_enum); // set token type for the rule in the parser + } + } + ; + +protected +LETTER +options { + paraphrase="a letter"; +} + : 'a'..'z'|'A'..'Z'|'\u00C0'..'\u00D6'|'\u00D8'..'\u00F6'|'\u00F8'..'\u00FF'|'\u0100'..'\uFFFE'|'_' + // TODO: Recognize all the Java identifier starts here (except '$'). + ; + +protected +DIGIT +options { + paraphrase="a digit"; +} + : '0'..'9' + // TODO: Recognize all the Java identifier parts here (except '$'). + ; + +protected +DIGITS_WITH_UNDERSCORE +options { + paraphrase="a sequence of digits and underscores, bordered by digits"; +} + : DIGIT (DIGITS_WITH_UNDERSCORE_OPT)? + ; + +protected +DIGITS_WITH_UNDERSCORE_OPT +options { + paraphrase="a sequence of digits and underscores with maybe underscore starting"; +} + : (DIGIT | '_')* DIGIT + ; + +// a numeric literal +NUM_INT +options { + paraphrase="a numeric literal"; +} + {boolean isDecimal=false; Token t=null;} + : + // TODO: This complex pattern seems wrong. Verify or fix. + ( '0' {isDecimal = true;} // special case for just '0' + ( // hex digits + ('x'|'X') + {isDecimal = false;} + HEX_DIGIT + ( options { warnWhenFollowAmbig=false; } + : (options { warnWhenFollowAmbig=false; } : HEX_DIGIT | '_')* + HEX_DIGIT + )? + + | //binary literal + ('b'|'B') ('0'|'1') (('0'|'1'|'_')* ('0'|'1'))? + {isDecimal = false;} + + | //float or double with leading zero + ( DIGITS_WITH_UNDERSCORE + ( '.' DIGITS_WITH_UNDERSCORE | EXPONENT | FLOAT_SUFFIX) + ) => DIGITS_WITH_UNDERSCORE + + | // octal + ('0'..'7') (('0'..'7'|'_')* ('0'..'7'))? + {isDecimal = false;} + )? + | ('1'..'9') (DIGITS_WITH_UNDERSCORE_OPT)? {isDecimal=true;} // non-zero decimal + ) + ( ('l'|'L') { _ttype = NUM_LONG; } + | ('i'|'I') { _ttype = NUM_INT; } + | BIG_SUFFIX { _ttype = NUM_BIG_INT; } + + // only check to see if it's a float if looks like decimal so far + | + (~'.' | '.' ('0'..'9')) => + {isDecimal}? + ( '.' DIGITS_WITH_UNDERSCORE (e:EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;} | g2:BIG_SUFFIX {t=g2;})? + | EXPONENT (f3:FLOAT_SUFFIX {t=f3;} | g3:BIG_SUFFIX {t=g3;})? + | f4:FLOAT_SUFFIX {t=f4;} + ) + { + String txt = (t == null ? "" : t.getText().toUpperCase()); + if (txt.indexOf('F') >= 0) { + _ttype = NUM_FLOAT; + } else if (txt.indexOf('G') >= 0) { + _ttype = NUM_BIG_DECIMAL; + } else { + _ttype = NUM_DOUBLE; // assume double + } + } + )? + ; + +// JDK 1.5 token for annotations and their declarations +// also a groovy operator for actual field access e.g. 'telson.@age' +AT +options { + paraphrase="'@'"; +} + : '@' + ; + +// a couple protected methods to assist in matching floating point numbers +protected +EXPONENT +options { + paraphrase="an exponent"; +} + : ('e'|'E') ('+'|'-')? ('0'..'9'|'_')* ('0'..'9') + ; + + +protected +FLOAT_SUFFIX +options { + paraphrase="a float or double suffix"; +} + : 'f'|'F'|'d'|'D' + ; + +protected +BIG_SUFFIX +options { + paraphrase="a big decimal suffix"; +} + : 'g'|'G' + ; + +// Note: Please don't use physical tabs. Logical tabs for indent are width 4. +// Here's a little hint for you, Emacs: +// Local Variables: +// tab-width: 4 +// mode: antlr-mode +// indent-tabs-mode: nil +// End: diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyLexer.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyLexer.java new file mode 100644 index 0000000000..26543ad7bd --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyLexer.java @@ -0,0 +1,3925 @@ +// $ANTLR 2.7.7 (20060906): "groovy.g" -> "GroovyLexer.java"$ + +package org.codehaus.groovy.antlr.parser; + +import java.io.*; +import java.util.*; + +import groovyjarjarantlr.CommonToken; +import groovyjarjarantlr.InputBuffer; +import groovyjarjarantlr.LexerSharedInputState; +import groovyjarjarantlr.TokenStreamRecognitionException; + +import org.codehaus.groovy.antlr.*; +import org.codehaus.groovy.ast.Comment; + +import java.io.InputStream; +import groovyjarjarantlr.TokenStreamException; +import groovyjarjarantlr.TokenStreamIOException; +import groovyjarjarantlr.TokenStreamRecognitionException; +import groovyjarjarantlr.CharStreamException; +import groovyjarjarantlr.CharStreamIOException; +import groovyjarjarantlr.ANTLRException; +import java.io.Reader; +import java.util.Hashtable; +import groovyjarjarantlr.CharScanner; +import groovyjarjarantlr.InputBuffer; +import groovyjarjarantlr.ByteBuffer; +import groovyjarjarantlr.CharBuffer; +import groovyjarjarantlr.Token; +import groovyjarjarantlr.CommonToken; +import groovyjarjarantlr.RecognitionException; +import groovyjarjarantlr.NoViableAltForCharException; +import groovyjarjarantlr.MismatchedCharException; +import groovyjarjarantlr.TokenStream; +import groovyjarjarantlr.ANTLRHashString; +import groovyjarjarantlr.LexerSharedInputState; +import groovyjarjarantlr.collections.impl.BitSet; +import groovyjarjarantlr.SemanticException; + +public class GroovyLexer extends groovyjarjarantlr.CharScanner implements GroovyTokenTypes, TokenStream + { + + /** flag for enabling the "assert" keyword */ + private boolean assertEnabled = true; + /** flag for enabling the "enum" keyword */ + private boolean enumEnabled = true; + /** flag for including whitespace tokens (for IDE preparsing) */ + private boolean whitespaceIncluded = false; + + /** Enable the "assert" keyword */ + public void enableAssert(boolean shouldEnable) { assertEnabled = shouldEnable; } + /** Query the "assert" keyword state */ + public boolean isAssertEnabled() { return assertEnabled; } + /** Enable the "enum" keyword */ + public void enableEnum(boolean shouldEnable) { enumEnabled = shouldEnable; } + /** Query the "enum" keyword state */ + public boolean isEnumEnabled() { return enumEnabled; } + + /** Include whitespace tokens. Note that this breaks the parser. */ + public void setWhitespaceIncluded(boolean z) { whitespaceIncluded = z; } + /** Are whitespace tokens included? */ + public boolean isWhitespaceIncluded() { return whitespaceIncluded; } + + { + // Initialization actions performed on construction. + setTabSize(1); // get rid of special tab interpretation, for IDEs and general clarity + } + + /** Bumped when inside '[x]' or '(x)', reset inside '{x}'. See ONE_NL. */ + protected int parenLevel = 0; + protected int suppressNewline = 0; // be really mean to newlines inside strings + protected static final int SCS_TYPE = 3, SCS_VAL = 4, SCS_LIT = 8, SCS_LIMIT = 16; + protected static final int SCS_SQ_TYPE = 0, SCS_TQ_TYPE = 1, SCS_RE_TYPE = 2, SCS_DRE_TYPE = 3; + protected int stringCtorState = 0; // hack string and regexp constructor boundaries + /** Push parenLevel here and reset whenever inside '{x}'. */ + protected ArrayList parenLevelStack = new ArrayList(); + protected int lastSigTokenType = EOF; // last returned non-whitespace token + + public void setTokenObjectClass(String name) {/*ignore*/} + + protected Token makeToken(int t) { + GroovySourceToken tok = new GroovySourceToken(t); + tok.setColumn(inputState.getTokenStartColumn()); + tok.setLine(inputState.getTokenStartLine()); + tok.setColumnLast(inputState.getColumn()); + tok.setLineLast(inputState.getLine()); + return tok; + } + + protected void pushParenLevel() { + parenLevelStack.add(Integer.valueOf(parenLevel*SCS_LIMIT + stringCtorState)); + parenLevel = 0; + stringCtorState = 0; + } + + protected void popParenLevel() { + int npl = parenLevelStack.size(); + if (npl == 0) return; + int i = ((Integer) parenLevelStack.remove(--npl)).intValue(); + parenLevel = i / SCS_LIMIT; + stringCtorState = i % SCS_LIMIT; + } + + protected void restartStringCtor(boolean expectLiteral) { + if (stringCtorState != 0) { + stringCtorState = (expectLiteral? SCS_LIT: SCS_VAL) + (stringCtorState & SCS_TYPE); + } + } + + protected boolean allowRegexpLiteral() { + return !isExpressionEndingToken(lastSigTokenType); + } + + /** Return true for an operator or punctuation which can end an expression. + * Return true for keywords, identifiers, and literals. + * Return true for tokens which can end expressions (right brackets, ++, --). + * Return false for EOF and all other operator and punctuation tokens. + * Used to suppress the recognition of /foo/ as opposed to the simple division operator '/'. + */ + // Cf. 'constant' and 'balancedBrackets' rules in the grammar.) + protected static boolean isExpressionEndingToken(int ttype) { + switch (ttype) { + case INC: // x++ / y + case DEC: // x-- / y + case RPAREN: // (x) / y + case RBRACK: // f[x] / y + case RCURLY: // f{x} / y + case STRING_LITERAL: // "x" / y + case STRING_CTOR_END: // "$x" / y + case NUM_INT: // 0 / y + case NUM_FLOAT: // 0f / y + case NUM_LONG: // 0l / y + case NUM_DOUBLE: // 0.0 / y + case NUM_BIG_INT: // 0g / y + case NUM_BIG_DECIMAL: // 0.0g / y + case IDENT: // x / y + // and a bunch of keywords (all of them; no sense picking and choosing): + case LITERAL_as: + case LITERAL_assert: + case LITERAL_boolean: + case LITERAL_break: + case LITERAL_byte: + case LITERAL_case: + case LITERAL_catch: + case LITERAL_char: + case LITERAL_class: + case LITERAL_continue: + case LITERAL_def: + case LITERAL_default: + case LITERAL_double: + case LITERAL_else: + case LITERAL_enum: + case LITERAL_extends: + case LITERAL_false: + case LITERAL_finally: + case LITERAL_float: + case LITERAL_for: + case LITERAL_if: + case LITERAL_implements: + case LITERAL_import: + case LITERAL_in: + case LITERAL_instanceof: + case LITERAL_int: + case LITERAL_interface: + case LITERAL_long: + case LITERAL_native: + case LITERAL_new: + case LITERAL_null: + case LITERAL_package: + case LITERAL_private: + case LITERAL_protected: + case LITERAL_public: + case LITERAL_return: + case LITERAL_short: + case LITERAL_static: + case LITERAL_super: + case LITERAL_switch: + case LITERAL_synchronized: + case LITERAL_trait: + case LITERAL_this: + case LITERAL_threadsafe: + case LITERAL_throw: + case LITERAL_throws: + case LITERAL_transient: + case LITERAL_true: + case LITERAL_try: + case LITERAL_void: + case LITERAL_volatile: + case LITERAL_while: + return true; + default: + return false; + } + } + + protected void newlineCheck(boolean check) throws RecognitionException { + if (check && suppressNewline > 0) { + require(suppressNewline == 0, + "end of line reached within a simple string 'x' or \"x\" or /x/", + "for multi-line literals, use triple quotes '''x''' or \"\"\"x\"\"\" or /x/ or $/x/$"); + suppressNewline = 0; // shut down any flood of errors + } + newline(); + } + + protected boolean atValidDollarEscape() throws CharStreamException { + // '$' (('{' | LETTER) => + int k = 1; + char lc = LA(k++); + if (lc != '$') return false; + lc = LA(k++); + return (lc == '{' || (lc != '$' && Character.isJavaIdentifierStart(lc))); + } + + protected boolean atDollarDollarEscape() throws CharStreamException { + return LA(1) == '$' && LA(2) == '$'; + } + + protected boolean atMultiCommentStart() throws CharStreamException { + return LA(1) == '/' && LA(2) == '*'; + } + + protected boolean atDollarSlashEscape() throws CharStreamException { + return LA(1) == '$' && LA(2) == '/'; + } + + /** This is a bit of plumbing which resumes collection of string constructor bodies, + * after an embedded expression has been parsed. + * Usage: new GroovyRecognizer(new GroovyLexer(in).plumb()). + */ + public TokenStream plumb() { + return new TokenStream() { + public Token nextToken() throws TokenStreamException { + if (stringCtorState >= SCS_LIT) { + // This goo is modeled upon the ANTLR code for nextToken: + int quoteType = (stringCtorState & SCS_TYPE); + stringCtorState = 0; // get out of this mode, now + resetText(); + try { + switch (quoteType) { + case SCS_SQ_TYPE: + mSTRING_CTOR_END(true, /*fromStart:*/false, false); break; + case SCS_TQ_TYPE: + mSTRING_CTOR_END(true, /*fromStart:*/false, true); break; + case SCS_RE_TYPE: + mREGEXP_CTOR_END(true, /*fromStart:*/false); break; + case SCS_DRE_TYPE: + mDOLLAR_REGEXP_CTOR_END(true, /*fromStart:*/false); break; + default: throw new AssertionError(false); + } + lastSigTokenType = _returnToken.getType(); + return _returnToken; + } catch (RecognitionException e) { + throw new TokenStreamRecognitionException(e); + } catch (CharStreamException cse) { + if ( cse instanceof CharStreamIOException ) { + throw new TokenStreamIOException(((CharStreamIOException)cse).io); + } + else { + throw new TokenStreamException(cse.getMessage()); + } + } + } + Token token = GroovyLexer.this.nextToken(); + int lasttype = token.getType(); + if (whitespaceIncluded) { + switch (lasttype) { // filter out insignificant types + case WS: + case ONE_NL: + case SL_COMMENT: + case ML_COMMENT: + lasttype = lastSigTokenType; // back up! + } + } + lastSigTokenType = lasttype; + return token; + } + }; + } + + // stuff to adjust ANTLR's tracing machinery + public static boolean tracing = false; // only effective if antlr.Tool is run with -traceLexer + public void traceIn(String rname) throws CharStreamException { + if (!GroovyLexer.tracing) return; + super.traceIn(rname); + } + public void traceOut(String rname) throws CharStreamException { + if (!GroovyLexer.tracing) return; + if (_returnToken != null) rname += tokenStringOf(_returnToken); + super.traceOut(rname); + } + private static java.util.HashMap ttypes; + private static String tokenStringOf(Token t) { + if (ttypes == null) { + java.util.HashMap map = new java.util.HashMap(); + java.lang.reflect.Field[] fields = GroovyTokenTypes.class.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getType() != int.class) continue; + try { + map.put(fields[i].get(null), fields[i].getName()); + } catch (IllegalAccessException ee) { + } + } + ttypes = map; + } + Integer tt = Integer.valueOf(t.getType()); + Object ttn = ttypes.get(tt); + if (ttn == null) ttn = "<"+tt+">"; + return "["+ttn+",\""+t.getText()+"\"]"; + } + + protected GroovyRecognizer parser; // little-used link; TODO: get rid of + private void require(boolean z, String problem, String solution) throws SemanticException { + // TODO: Direct to a common error handler, rather than through the parser. + if (!z && parser!=null) parser.requireFailed(problem, solution); + if (!z) { + int lineNum = inputState.getLine(), colNum = inputState.getColumn(); + throw new SemanticException(problem + ";\n solution: " + solution, + getFilename(), lineNum, colNum); + } + } +public GroovyLexer(InputStream in) { + this(new ByteBuffer(in)); +} +public GroovyLexer(Reader in) { + this(new CharBuffer(in)); +} +public GroovyLexer(InputBuffer ib) { + this(new LexerSharedInputState(ib)); +} +public GroovyLexer(LexerSharedInputState state) { + super(state); + caseSensitiveLiterals = true; + setCaseSensitive(true); + literals = new Hashtable(); + literals.put(new ANTLRHashString("byte", this), new Integer(106)); + literals.put(new ANTLRHashString("public", this), new Integer(116)); + literals.put(new ANTLRHashString("trait", this), new Integer(95)); + literals.put(new ANTLRHashString("case", this), new Integer(150)); + literals.put(new ANTLRHashString("short", this), new Integer(108)); + literals.put(new ANTLRHashString("break", this), new Integer(144)); + literals.put(new ANTLRHashString("while", this), new Integer(139)); + literals.put(new ANTLRHashString("new", this), new Integer(159)); + literals.put(new ANTLRHashString("instanceof", this), new Integer(158)); + literals.put(new ANTLRHashString("implements", this), new Integer(131)); + literals.put(new ANTLRHashString("synchronized", this), new Integer(121)); + literals.put(new ANTLRHashString("const", this), new Integer(41)); + literals.put(new ANTLRHashString("float", this), new Integer(110)); + literals.put(new ANTLRHashString("package", this), new Integer(81)); + literals.put(new ANTLRHashString("return", this), new Integer(143)); + literals.put(new ANTLRHashString("throw", this), new Integer(146)); + literals.put(new ANTLRHashString("null", this), new Integer(160)); + literals.put(new ANTLRHashString("def", this), new Integer(84)); + literals.put(new ANTLRHashString("threadsafe", this), new Integer(120)); + literals.put(new ANTLRHashString("protected", this), new Integer(117)); + literals.put(new ANTLRHashString("class", this), new Integer(92)); + literals.put(new ANTLRHashString("throws", this), new Integer(130)); + literals.put(new ANTLRHashString("do", this), new Integer(42)); + literals.put(new ANTLRHashString("strictfp", this), new Integer(43)); + literals.put(new ANTLRHashString("super", this), new Integer(99)); + literals.put(new ANTLRHashString("transient", this), new Integer(118)); + literals.put(new ANTLRHashString("native", this), new Integer(119)); + literals.put(new ANTLRHashString("interface", this), new Integer(93)); + literals.put(new ANTLRHashString("final", this), new Integer(38)); + literals.put(new ANTLRHashString("if", this), new Integer(137)); + literals.put(new ANTLRHashString("double", this), new Integer(112)); + literals.put(new ANTLRHashString("volatile", this), new Integer(122)); + literals.put(new ANTLRHashString("as", this), new Integer(114)); + literals.put(new ANTLRHashString("assert", this), new Integer(147)); + literals.put(new ANTLRHashString("catch", this), new Integer(153)); + literals.put(new ANTLRHashString("try", this), new Integer(151)); + literals.put(new ANTLRHashString("goto", this), new Integer(40)); + literals.put(new ANTLRHashString("enum", this), new Integer(94)); + literals.put(new ANTLRHashString("int", this), new Integer(109)); + literals.put(new ANTLRHashString("for", this), new Integer(141)); + literals.put(new ANTLRHashString("extends", this), new Integer(98)); + literals.put(new ANTLRHashString("boolean", this), new Integer(105)); + literals.put(new ANTLRHashString("char", this), new Integer(107)); + literals.put(new ANTLRHashString("private", this), new Integer(115)); + literals.put(new ANTLRHashString("default", this), new Integer(129)); + literals.put(new ANTLRHashString("false", this), new Integer(157)); + literals.put(new ANTLRHashString("this", this), new Integer(132)); + literals.put(new ANTLRHashString("static", this), new Integer(83)); + literals.put(new ANTLRHashString("abstract", this), new Integer(39)); + literals.put(new ANTLRHashString("continue", this), new Integer(145)); + literals.put(new ANTLRHashString("finally", this), new Integer(152)); + literals.put(new ANTLRHashString("else", this), new Integer(138)); + literals.put(new ANTLRHashString("import", this), new Integer(82)); + literals.put(new ANTLRHashString("in", this), new Integer(142)); + literals.put(new ANTLRHashString("void", this), new Integer(104)); + literals.put(new ANTLRHashString("switch", this), new Integer(140)); + literals.put(new ANTLRHashString("true", this), new Integer(161)); + literals.put(new ANTLRHashString("long", this), new Integer(111)); +} + +public Token nextToken() throws TokenStreamException { + Token theRetToken=null; +tryAgain: + for (;;) { + Token _token = null; + int _ttype = Token.INVALID_TYPE; + resetText(); + try { // for char stream error handling + try { // for lexical error handling + switch ( LA(1)) { + case '(': + { + mLPAREN(true); + theRetToken=_returnToken; + break; + } + case ')': + { + mRPAREN(true); + theRetToken=_returnToken; + break; + } + case '[': + { + mLBRACK(true); + theRetToken=_returnToken; + break; + } + case ']': + { + mRBRACK(true); + theRetToken=_returnToken; + break; + } + case '{': + { + mLCURLY(true); + theRetToken=_returnToken; + break; + } + case '}': + { + mRCURLY(true); + theRetToken=_returnToken; + break; + } + case ':': + { + mCOLON(true); + theRetToken=_returnToken; + break; + } + case ',': + { + mCOMMA(true); + theRetToken=_returnToken; + break; + } + case '~': + { + mBNOT(true); + theRetToken=_returnToken; + break; + } + case ';': + { + mSEMI(true); + theRetToken=_returnToken; + break; + } + case '\t': case '\u000c': case ' ': case '\\': + { + mWS(true); + theRetToken=_returnToken; + break; + } + case '\n': case '\r': + { + mNLS(true); + theRetToken=_returnToken; + break; + } + case '"': case '\'': + { + mSTRING_LITERAL(true); + theRetToken=_returnToken; + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + { + mNUM_INT(true); + theRetToken=_returnToken; + break; + } + case '@': + { + mAT(true); + theRetToken=_returnToken; + break; + } + default: + if ((LA(1)=='>') && (LA(2)=='>') && (LA(3)=='>') && (LA(4)=='=')) { + mBSR_ASSIGN(true); + theRetToken=_returnToken; + } + else if (((LA(1)=='/') && (LA(2)=='*') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && ((LA(4) >= '\u0000' && LA(4) <= '\ufffe')))&&( atMultiCommentStart() )) { + mML_COMMENT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='<') && (LA(2)=='=') && (LA(3)=='>')) { + mCOMPARE_TO(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='=') && (LA(2)=='=') && (LA(3)=='=')) { + mIDENTICAL(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='!') && (LA(2)=='=') && (LA(3)=='=')) { + mNOT_IDENTICAL(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='>') && (LA(2)=='>') && (LA(3)=='=')) { + mSR_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='>') && (LA(2)=='>') && (LA(3)=='>') && (true)) { + mBSR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='<') && (LA(2)=='<') && (LA(3)=='=')) { + mSL_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='.') && (LA(2)=='.') && (LA(3)=='<')) { + mRANGE_EXCLUSIVE(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='.') && (LA(2)=='.') && (LA(3)=='.')) { + mTRIPLE_DOT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='=') && (LA(2)=='=') && (LA(3)=='~')) { + mREGEX_MATCH(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='*') && (LA(2)=='*') && (LA(3)=='=')) { + mSTAR_STAR_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='=') && (LA(2)=='=') && (true)) { + mEQUAL(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='!') && (LA(2)=='=') && (true)) { + mNOT_EQUAL(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='+') && (LA(2)=='=')) { + mPLUS_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='+') && (LA(2)=='+')) { + mINC(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='-') && (LA(2)=='=')) { + mMINUS_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='-') && (LA(2)=='-')) { + mDEC(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='*') && (LA(2)=='=')) { + mSTAR_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='%') && (LA(2)=='=')) { + mMOD_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='>') && (LA(2)=='>') && (true)) { + mSR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='>') && (LA(2)=='=')) { + mGE(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='<') && (LA(2)=='<') && (true)) { + mSL(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='<') && (LA(2)=='=') && (true)) { + mLE(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='^') && (LA(2)=='=')) { + mBXOR_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='|') && (LA(2)=='=')) { + mBOR_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='|') && (LA(2)=='|')) { + mLOR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='&') && (LA(2)=='=')) { + mBAND_ASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='&') && (LA(2)=='&')) { + mLAND(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='.') && (LA(2)=='.') && (true)) { + mRANGE_INCLUSIVE(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='*') && (LA(2)=='.')) { + mSPREAD_DOT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='?') && (LA(2)=='.')) { + mOPTIONAL_DOT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='?') && (LA(2)==':')) { + mELVIS_OPERATOR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='.') && (LA(2)=='&')) { + mMEMBER_POINTER(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='=') && (LA(2)=='~')) { + mREGEX_FIND(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='*') && (LA(2)=='*') && (true)) { + mSTAR_STAR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='-') && (LA(2)=='>')) { + mCLOSABLE_BLOCK_OP(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='/') && (LA(2)=='/')) { + mSL_COMMENT(true); + theRetToken=_returnToken; + } + else if (((LA(1)=='$') && (LA(2)=='/'))&&(allowRegexpLiteral())) { + mDOLLAR_REGEXP_LITERAL(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='?') && (true)) { + mQUESTION(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='.') && (true)) { + mDOT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='=') && (true)) { + mASSIGN(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='!') && (true)) { + mLNOT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='+') && (true)) { + mPLUS(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='-') && (true)) { + mMINUS(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='*') && (true)) { + mSTAR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='%') && (true)) { + mMOD(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='>') && (true)) { + mGT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='<') && (true)) { + mLT(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='^') && (true)) { + mBXOR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='|') && (true)) { + mBOR(true); + theRetToken=_returnToken; + } + else if ((LA(1)=='&') && (true)) { + mBAND(true); + theRetToken=_returnToken; + } + else if (((LA(1)=='#'))&&(getLine() == 1 && getColumn() == 1)) { + mSH_COMMENT(true); + theRetToken=_returnToken; + } + else if (((LA(1)=='/') && (true) && (true) && (true))&&( !atMultiCommentStart() )) { + mREGEXP_LITERAL(true); + theRetToken=_returnToken; + } + else if ((_tokenSet_0.member(LA(1))) && (true)) { + mIDENT(true); + theRetToken=_returnToken; + } + else { + if (LA(1)==EOF_CHAR) {uponEOF(); _returnToken = makeToken(Token.EOF_TYPE);} + else {throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn());} + } + } + if ( _returnToken==null ) continue tryAgain; // found SKIP token + _ttype = _returnToken.getType(); + _returnToken.setType(_ttype); + return _returnToken; + } + catch (RecognitionException e) { + throw new TokenStreamRecognitionException(e); + } + } + catch (CharStreamException cse) { + if ( cse instanceof CharStreamIOException ) { + throw new TokenStreamIOException(((CharStreamIOException)cse).io); + } + else { + throw new TokenStreamException(cse.getMessage()); + } + } + } +} + + public final void mQUESTION(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = QUESTION; + int _saveIndex; + + match('?'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLPAREN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LPAREN; + int _saveIndex; + + match('('); + if ( inputState.guessing==0 ) { + ++parenLevel; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mRPAREN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = RPAREN; + int _saveIndex; + + match(')'); + if ( inputState.guessing==0 ) { + --parenLevel; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLBRACK(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LBRACK; + int _saveIndex; + + match('['); + if ( inputState.guessing==0 ) { + ++parenLevel; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mRBRACK(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = RBRACK; + int _saveIndex; + + match(']'); + if ( inputState.guessing==0 ) { + --parenLevel; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLCURLY(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LCURLY; + int _saveIndex; + + match('{'); + if ( inputState.guessing==0 ) { + pushParenLevel(); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mRCURLY(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = RCURLY; + int _saveIndex; + + match('}'); + if ( inputState.guessing==0 ) { + popParenLevel(); if(stringCtorState!=0) restartStringCtor(true); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mCOLON(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = COLON; + int _saveIndex; + + match(':'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mCOMMA(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = COMMA; + int _saveIndex; + + match(','); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mDOT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DOT; + int _saveIndex; + + match('.'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ASSIGN; + int _saveIndex; + + match('='); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mCOMPARE_TO(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = COMPARE_TO; + int _saveIndex; + + match("<=>"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mEQUAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = EQUAL; + int _saveIndex; + + match("=="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mIDENTICAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = IDENTICAL; + int _saveIndex; + + match("==="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLNOT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LNOT; + int _saveIndex; + + match('!'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBNOT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BNOT; + int _saveIndex; + + match('~'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mNOT_EQUAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = NOT_EQUAL; + int _saveIndex; + + match("!="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mNOT_IDENTICAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = NOT_IDENTICAL; + int _saveIndex; + + match("!=="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDIV(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DIV; + int _saveIndex; + + match('/'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDIV_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DIV_ASSIGN; + int _saveIndex; + + match("/="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mPLUS(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = PLUS; + int _saveIndex; + + match('+'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mPLUS_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = PLUS_ASSIGN; + int _saveIndex; + + match("+="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mINC(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = INC; + int _saveIndex; + + match("++"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mMINUS(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = MINUS; + int _saveIndex; + + match('-'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mMINUS_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = MINUS_ASSIGN; + int _saveIndex; + + match("-="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mDEC(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DEC; + int _saveIndex; + + match("--"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSTAR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STAR; + int _saveIndex; + + match('*'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSTAR_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STAR_ASSIGN; + int _saveIndex; + + match("*="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mMOD(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = MOD; + int _saveIndex; + + match('%'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mMOD_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = MOD_ASSIGN; + int _saveIndex; + + match("%="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SR; + int _saveIndex; + + match(">>"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSR_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SR_ASSIGN; + int _saveIndex; + + match(">>="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBSR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BSR; + int _saveIndex; + + match(">>>"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBSR_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BSR_ASSIGN; + int _saveIndex; + + match(">>>="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mGE(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = GE; + int _saveIndex; + + match(">="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mGT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = GT; + int _saveIndex; + + match(">"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SL; + int _saveIndex; + + match("<<"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSL_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SL_ASSIGN; + int _saveIndex; + + match("<<="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLE(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LE; + int _saveIndex; + + match("<="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LT; + int _saveIndex; + + match('<'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBXOR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BXOR; + int _saveIndex; + + match('^'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBXOR_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BXOR_ASSIGN; + int _saveIndex; + + match("^="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBOR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BOR; + int _saveIndex; + + match('|'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBOR_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BOR_ASSIGN; + int _saveIndex; + + match("|="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLOR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LOR; + int _saveIndex; + + match("||"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBAND(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BAND; + int _saveIndex; + + match('&'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mBAND_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BAND_ASSIGN; + int _saveIndex; + + match("&="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mLAND(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LAND; + int _saveIndex; + + match("&&"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSEMI(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SEMI; + int _saveIndex; + + match(';'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDOLLAR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DOLLAR; + int _saveIndex; + + match('$'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mRANGE_INCLUSIVE(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = RANGE_INCLUSIVE; + int _saveIndex; + + match(".."); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mRANGE_EXCLUSIVE(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = RANGE_EXCLUSIVE; + int _saveIndex; + + match("..<"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mTRIPLE_DOT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = TRIPLE_DOT; + int _saveIndex; + + match("..."); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSPREAD_DOT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SPREAD_DOT; + int _saveIndex; + + match("*."); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mOPTIONAL_DOT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = OPTIONAL_DOT; + int _saveIndex; + + match("?."); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mELVIS_OPERATOR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ELVIS_OPERATOR; + int _saveIndex; + + match("?:"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mMEMBER_POINTER(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = MEMBER_POINTER; + int _saveIndex; + + match(".&"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mREGEX_FIND(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = REGEX_FIND; + int _saveIndex; + + match("=~"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mREGEX_MATCH(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = REGEX_MATCH; + int _saveIndex; + + match("==~"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSTAR_STAR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STAR_STAR; + int _saveIndex; + + match("**"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSTAR_STAR_ASSIGN(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STAR_STAR_ASSIGN; + int _saveIndex; + + match("**="); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mCLOSABLE_BLOCK_OP(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = CLOSABLE_BLOCK_OP; + int _saveIndex; + + match("->"); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mWS(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = WS; + int _saveIndex; + + { + int _cnt675=0; + _loop675: + do { + if ((LA(1)=='\\') && (LA(2)=='\n'||LA(2)=='\r') && (true) && (true)) { + match('\\'); + mONE_NL(false,false); + } + else if ((LA(1)==' ') && (true) && (true) && (true)) { + match(' '); + } + else if ((LA(1)=='\t') && (true) && (true) && (true)) { + match('\t'); + } + else if ((LA(1)=='\u000c') && (true) && (true) && (true)) { + match('\f'); + } + else { + if ( _cnt675>=1 ) { break _loop675; } else {throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn());} + } + + _cnt675++; + } while (true); + } + if ( inputState.guessing==0 ) { + if (!whitespaceIncluded) _ttype = Token.SKIP; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mONE_NL(boolean _createToken, + boolean check + ) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ONE_NL; + int _saveIndex; + + { + if ((LA(1)=='\r') && (LA(2)=='\n') && (true) && (true)) { + _saveIndex=text.length(); + match("\r\n"); + text.setLength(_saveIndex); + } + else if ((LA(1)=='\r') && (true) && (true) && (true)) { + _saveIndex=text.length(); + match('\r'); + text.setLength(_saveIndex); + } + else if ((LA(1)=='\n')) { + _saveIndex=text.length(); + match('\n'); + text.setLength(_saveIndex); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + + // update current line number for error reporting + newlineCheck(check); + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mONE_NL_KEEP(boolean _createToken, + boolean check + ) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ONE_NL_KEEP; + int _saveIndex; + + { + if ((LA(1)=='\r') && (LA(2)=='\n') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && ((LA(4) >= '\u0000' && LA(4) <= '\ufffe'))) { + match("\r\n"); + } + else if ((LA(1)=='\r') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true)) { + match('\r'); + } + else if ((LA(1)=='\n')) { + match('\n'); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + + // update current line number for error reporting + newlineCheck(check); + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mNLS(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = NLS; + int _saveIndex; + + mONE_NL(false,true); + { + if (((LA(1)=='\t'||LA(1)=='\n'||LA(1)=='\u000c'||LA(1)=='\r'||LA(1)==' '||LA(1)=='/'||LA(1)=='\\'))&&(!whitespaceIncluded)) { + { + int _cnt683=0; + _loop683: + do { + switch ( LA(1)) { + case '\n': case '\r': + { + mONE_NL(false,true); + break; + } + case '\t': case '\u000c': case ' ': case '\\': + { + mWS(false); + break; + } + default: + if ((LA(1)=='/') && (LA(2)=='/')) { + mSL_COMMENT(false); + } + else if ((LA(1)=='/') && (LA(2)=='*')) { + mML_COMMENT(false); + } + else { + if ( _cnt683>=1 ) { break _loop683; } else {throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn());} + } + } + _cnt683++; + } while (true); + } + } + else { + } + + } + if ( inputState.guessing==0 ) { + if (whitespaceIncluded) { + // keep the token as-is + } else if (parenLevel != 0) { + // when directly inside parens, all newlines are ignored here + _ttype = Token.SKIP; + } else { + // inside {...}, newlines must be explicitly matched as 'nls!' + text.setLength(_begin); text.append(""); + } + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSL_COMMENT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SL_COMMENT; + int _saveIndex; + + match("//"); + if ( inputState.guessing==0 ) { + if (parser != null) parser.startComment(inputState.getLine(), inputState.getColumn() - 2); + } + { + _loop687: + do { + if ((_tokenSet_1.member(LA(1))) && (true) && (true) && (true)) { + { + match(_tokenSet_1); + } + } + else { + break _loop687; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + if (parser != null) parser.endComment(0, inputState.getLine(), inputState.getColumn(), String.valueOf(text.getBuffer(), _begin, text.length() - _begin)); + // GRECLIPSE end + if (!whitespaceIncluded) _ttype = Token.SKIP; + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mML_COMMENT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ML_COMMENT; + int _saveIndex; + + if (!( atMultiCommentStart() )) + throw new SemanticException(" atMultiCommentStart() "); + match("/*"); + if ( inputState.guessing==0 ) { + if (parser != null) parser.startComment(inputState.getLine(), inputState.getColumn() - 2); + } + { + _loop697: + do { + boolean synPredMatched695 = false; + if (((LA(1)=='*') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true))) { + int _m695 = mark(); + synPredMatched695 = true; + inputState.guessing++; + try { + { + match('*'); + matchNot('/'); + } + } + catch (RecognitionException pe) { + synPredMatched695 = false; + } + rewind(_m695); +inputState.guessing--; + } + if ( synPredMatched695 ) { + match('*'); + } + else if ((LA(1)=='\n'||LA(1)=='\r')) { + mONE_NL_KEEP(false,true); + } + else if ((_tokenSet_2.member(LA(1)))) { + { + match(_tokenSet_2); + } + } + else { + break _loop697; + } + + } while (true); + } + match("*/"); + if ( inputState.guessing==0 ) { + if (parser != null) parser.endComment(1, inputState.getLine(), inputState.getColumn(), String.valueOf(text.getBuffer(), _begin, text.length() - _begin)); + if (!whitespaceIncluded) _ttype = Token.SKIP; + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSH_COMMENT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = SH_COMMENT; + int _saveIndex; + + if (!(getLine() == 1 && getColumn() == 1)) + throw new SemanticException("getLine() == 1 && getColumn() == 1"); + match("#!"); + { + _loop691: + do { + if ((_tokenSet_1.member(LA(1)))) { + { + match(_tokenSet_1); + } + } + else { + break _loop691; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + if (!whitespaceIncluded) _ttype = Token.SKIP; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mSTRING_LITERAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STRING_LITERAL; + int _saveIndex; + int tt=0; + + boolean synPredMatched700 = false; + if (((LA(1)=='\'') && (LA(2)=='\'') && (LA(3)=='\'') && ((LA(4) >= '\u0000' && LA(4) <= '\ufffe')))) { + int _m700 = mark(); + synPredMatched700 = true; + inputState.guessing++; + try { + { + match("'''"); + } + } + catch (RecognitionException pe) { + synPredMatched700 = false; + } + rewind(_m700); +inputState.guessing--; + } + if ( synPredMatched700 ) { + _saveIndex=text.length(); + match("'''"); + text.setLength(_saveIndex); + { + _loop705: + do { + switch ( LA(1)) { + case '\\': + { + mESC(false); + break; + } + case '"': + { + match('"'); + break; + } + case '$': + { + match('$'); + break; + } + case '\n': case '\r': + { + mSTRING_NL(false,true); + break; + } + default: + boolean synPredMatched704 = false; + if (((LA(1)=='\'') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && ((LA(4) >= '\u0000' && LA(4) <= '\ufffe')))) { + int _m704 = mark(); + synPredMatched704 = true; + inputState.guessing++; + try { + { + match('\''); + { + if ((_tokenSet_3.member(LA(1)))) { + matchNot('\''); + } + else if ((LA(1)=='\'')) { + match('\''); + matchNot('\''); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + } + } + catch (RecognitionException pe) { + synPredMatched704 = false; + } + rewind(_m704); +inputState.guessing--; + } + if ( synPredMatched704 ) { + match('\''); + } + else if ((_tokenSet_4.member(LA(1)))) { + mSTRING_CH(false); + } + else { + break _loop705; + } + } + } while (true); + } + _saveIndex=text.length(); + match("'''"); + text.setLength(_saveIndex); + } + else { + boolean synPredMatched709 = false; + if (((LA(1)=='"') && (LA(2)=='"') && (LA(3)=='"') && ((LA(4) >= '\u0000' && LA(4) <= '\ufffe')))) { + int _m709 = mark(); + synPredMatched709 = true; + inputState.guessing++; + try { + { + match("\"\"\""); + } + } + catch (RecognitionException pe) { + synPredMatched709 = false; + } + rewind(_m709); +inputState.guessing--; + } + if ( synPredMatched709 ) { + _saveIndex=text.length(); + match("\"\"\""); + text.setLength(_saveIndex); + tt=mSTRING_CTOR_END(false,true, /*tripleQuote:*/ true); + if ( inputState.guessing==0 ) { + _ttype = tt; + } + } + else if ((LA(1)=='\'') && (_tokenSet_1.member(LA(2))) && (true) && (true)) { + _saveIndex=text.length(); + match('\''); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + ++suppressNewline; + } + { + _loop707: + do { + switch ( LA(1)) { + case '\\': + { + mESC(false); + break; + } + case '"': + { + match('"'); + break; + } + case '$': + { + match('$'); + break; + } + default: + if ((_tokenSet_4.member(LA(1)))) { + mSTRING_CH(false); + } + else { + break _loop707; + } + } + } while (true); + } + if ( inputState.guessing==0 ) { + --suppressNewline; + } + _saveIndex=text.length(); + match('\''); + text.setLength(_saveIndex); + } + else if ((LA(1)=='"') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true)) { + _saveIndex=text.length(); + match('"'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + ++suppressNewline; + } + tt=mSTRING_CTOR_END(false,true, /*tripleQuote:*/ false); + if ( inputState.guessing==0 ) { + _ttype = tt; + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mSTRING_CH(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STRING_CH; + int _saveIndex; + + { + match(_tokenSet_4); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mESC(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ESC; + int _saveIndex; + + if ((LA(1)=='\\') && (LA(2)=='"'||LA(2)=='$'||LA(2)=='\''||LA(2)=='0'||LA(2)=='1'||LA(2)=='2'||LA(2)=='3'||LA(2)=='4'||LA(2)=='5'||LA(2)=='6'||LA(2)=='7'||LA(2)=='\\'||LA(2)=='b'||LA(2)=='f'||LA(2)=='n'||LA(2)=='r'||LA(2)=='t'||LA(2)=='u')) { + _saveIndex=text.length(); + match('\\'); + text.setLength(_saveIndex); + { + switch ( LA(1)) { + case 'n': + { + match('n'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append("\n"); + } + break; + } + case 'r': + { + match('r'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append("\r"); + } + break; + } + case 't': + { + match('t'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append("\t"); + } + break; + } + case 'b': + { + match('b'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append("\b"); + } + break; + } + case 'f': + { + match('f'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append("\f"); + } + break; + } + case '"': + { + match('"'); + break; + } + case '\'': + { + match('\''); + break; + } + case '\\': + { + match('\\'); + break; + } + case '$': + { + match('$'); + break; + } + case 'u': + { + { + int _cnt754=0; + _loop754: + do { + if ((LA(1)=='u')) { + match('u'); + } + else { + if ( _cnt754>=1 ) { break _loop754; } else {throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn());} + } + + _cnt754++; + } while (true); + } + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append(""); + } + mHEX_DIGIT(false); + mHEX_DIGIT(false); + mHEX_DIGIT(false); + mHEX_DIGIT(false); + if ( inputState.guessing==0 ) { + char ch = (char)Integer.parseInt(new String(text.getBuffer(),_begin,text.length()-_begin),16); text.setLength(_begin); text.append(ch); + } + break; + } + case '0': case '1': case '2': case '3': + { + matchRange('0','3'); + { + if (((LA(1) >= '0' && LA(1) <= '7')) && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true)) { + matchRange('0','7'); + { + if (((LA(1) >= '0' && LA(1) <= '7')) && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true)) { + matchRange('0','7'); + } + else if (((LA(1) >= '\u0000' && LA(1) <= '\ufffe')) && (true) && (true) && (true)) { + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + } + else if (((LA(1) >= '\u0000' && LA(1) <= '\ufffe')) && (true) && (true) && (true)) { + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + char ch = (char)Integer.parseInt(new String(text.getBuffer(),_begin,text.length()-_begin),8); text.setLength(_begin); text.append(ch); + } + break; + } + case '4': case '5': case '6': case '7': + { + matchRange('4','7'); + { + if (((LA(1) >= '0' && LA(1) <= '7')) && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true)) { + matchRange('0','7'); + } + else if (((LA(1) >= '\u0000' && LA(1) <= '\ufffe')) && (true) && (true) && (true)) { + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + char ch = (char)Integer.parseInt(new String(text.getBuffer(),_begin,text.length()-_begin),8); text.setLength(_begin); text.append(ch); + } + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + } + else if ((LA(1)=='\\') && (LA(2)=='\n'||LA(2)=='\r')) { + _saveIndex=text.length(); + match('\\'); + text.setLength(_saveIndex); + _saveIndex=text.length(); + mONE_NL(false,false); + text.setLength(_saveIndex); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mSTRING_NL(boolean _createToken, + boolean allowNewline + ) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STRING_NL; + int _saveIndex; + + if ( inputState.guessing==0 ) { + if (!allowNewline) throw new MismatchedCharException('\n', '\n', true, this); + } + mONE_NL(false,false); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append('\n'); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final int mSTRING_CTOR_END(boolean _createToken, + boolean fromStart, boolean tripleQuote + ) throws RecognitionException, CharStreamException, TokenStreamException { + int tt=STRING_CTOR_END; + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = STRING_CTOR_END; + int _saveIndex; + boolean dollarOK = false; + + { + _loop715: + do { + switch ( LA(1)) { + case '\\': + { + mESC(false); + break; + } + case '\'': + { + match('\''); + break; + } + case '\n': case '\r': + { + mSTRING_NL(false,tripleQuote); + break; + } + default: + boolean synPredMatched714 = false; + if ((((LA(1)=='"') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true))&&(tripleQuote))) { + int _m714 = mark(); + synPredMatched714 = true; + inputState.guessing++; + try { + { + match('"'); + { + if ((_tokenSet_5.member(LA(1)))) { + matchNot('"'); + } + else if ((LA(1)=='"')) { + match('"'); + matchNot('"'); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + } + } + catch (RecognitionException pe) { + synPredMatched714 = false; + } + rewind(_m714); +inputState.guessing--; + } + if ( synPredMatched714 ) { + match('"'); + } + else if ((_tokenSet_4.member(LA(1)))) { + mSTRING_CH(false); + } + else { + break _loop715; + } + } + } while (true); + } + { + switch ( LA(1)) { + case '"': + { + { + if (((LA(1)=='"') && (LA(2)=='"'))&&( tripleQuote )) { + _saveIndex=text.length(); + match("\"\"\""); + text.setLength(_saveIndex); + } + else if (((LA(1)=='"') && (true))&&( !tripleQuote )) { + _saveIndex=text.length(); + match("\""); + text.setLength(_saveIndex); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + + if (fromStart) tt = STRING_LITERAL; // plain string literal! + if (!tripleQuote) {--suppressNewline;} + // done with string constructor! + //assert(stringCtorState == 0); + + } + break; + } + case '$': + { + if ( inputState.guessing==0 ) { + dollarOK = atValidDollarEscape(); + } + _saveIndex=text.length(); + match('$'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + require(dollarOK, + "illegal string body character after dollar sign", + "either escape a literal dollar sign \"\\$5\" or bracket the value expression \"${5}\""); + // Yes, it's a string constructor, and we've got a value part. + tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE); + stringCtorState = SCS_VAL + (tripleQuote? SCS_TQ_TYPE: SCS_SQ_TYPE); + + } + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + _ttype = tt; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + return tt; + } + + public final void mREGEXP_LITERAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = REGEXP_LITERAL; + int _saveIndex; + int tt=0; + + if (!( !atMultiCommentStart() )) + throw new SemanticException(" !atMultiCommentStart() "); + { + if (((LA(1)=='/') && (_tokenSet_6.member(LA(2))) && (true) && (true))&&(allowRegexpLiteral())) { + _saveIndex=text.length(); + match('/'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + ++suppressNewline; + } + { + if (((LA(1)=='$') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')))&&(!atValidDollarEscape())) { + match('$'); + tt=mREGEXP_CTOR_END(false,true); + } + else if ((_tokenSet_7.member(LA(1)))) { + mREGEXP_SYMBOL(false); + tt=mREGEXP_CTOR_END(false,true); + } + else if ((LA(1)=='$') && (true)) { + _saveIndex=text.length(); + match('$'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + // Yes, it's a regexp constructor, and we've got a value part. + tt = STRING_CTOR_START; + stringCtorState = SCS_VAL + SCS_RE_TYPE; + + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + _ttype = tt; + } + } + else if ((LA(1)=='/') && (LA(2)=='=') && (true) && (true)) { + mDIV_ASSIGN(false); + if ( inputState.guessing==0 ) { + _ttype = DIV_ASSIGN; + } + } + else { + boolean synPredMatched724 = false; + if (((LA(1)=='/') && (true))) { + int _m724 = mark(); + synPredMatched724 = true; + inputState.guessing++; + try { + { + match('/'); + matchNot('='); + } + } + catch (RecognitionException pe) { + synPredMatched724 = false; + } + rewind(_m724); +inputState.guessing--; + } + if ( synPredMatched724 ) { + mDIV(false); + if ( inputState.guessing==0 ) { + _ttype = DIV; + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mREGEXP_SYMBOL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = REGEXP_SYMBOL; + int _saveIndex; + + if ((LA(1)=='\\') && (LA(2)=='/') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true)) { + match('\\'); + match('/'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append('/'); + } + } + else if ((LA(1)=='\\') && (LA(2)=='\n'||LA(2)=='\r') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true)) { + _saveIndex=text.length(); + match('\\'); + text.setLength(_saveIndex); + _saveIndex=text.length(); + mONE_NL(false,false); + text.setLength(_saveIndex); + } + else if (((LA(1)=='\\') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true))&&( LA(2)!='/' && LA(2)!='\n' && LA(2)!='\r' )) { + match('\\'); + } + else if ((_tokenSet_8.member(LA(1)))) { + { + match(_tokenSet_8); + } + } + else if ((LA(1)=='\n'||LA(1)=='\r')) { + mSTRING_NL(false,true); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final int mREGEXP_CTOR_END(boolean _createToken, + boolean fromStart + ) throws RecognitionException, CharStreamException, TokenStreamException { + int tt=STRING_CTOR_END; + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = REGEXP_CTOR_END; + int _saveIndex; + + { + _loop733: + do { + if (((LA(1)=='$') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')))&&(!atValidDollarEscape())) { + match('$'); + } + else if ((_tokenSet_7.member(LA(1)))) { + mREGEXP_SYMBOL(false); + } + else { + break _loop733; + } + + } while (true); + } + { + switch ( LA(1)) { + case '/': + { + _saveIndex=text.length(); + match('/'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + if (fromStart) tt = STRING_LITERAL; // plain regexp literal! + {--suppressNewline;} + // done with regexp constructor! + //assert(stringCtorState == 0); + + } + break; + } + case '$': + { + _saveIndex=text.length(); + match('$'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + // Yes, it's a regexp constructor, and we've got a value part. + tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE); + stringCtorState = SCS_VAL + SCS_RE_TYPE; + + } + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + _ttype = tt; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + return tt; + } + + public final void mDOLLAR_REGEXP_LITERAL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DOLLAR_REGEXP_LITERAL; + int _saveIndex; + int tt=0; + + if (!(allowRegexpLiteral())) + throw new SemanticException("allowRegexpLiteral()"); + _saveIndex=text.length(); + match("$/"); + text.setLength(_saveIndex); + { + boolean synPredMatched728 = false; + if (((LA(1)=='$') && (LA(2)=='/') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true))) { + int _m728 = mark(); + synPredMatched728 = true; + inputState.guessing++; + try { + { + match('$'); + match('/'); + } + } + catch (RecognitionException pe) { + synPredMatched728 = false; + } + rewind(_m728); +inputState.guessing--; + } + if ( synPredMatched728 ) { + mESCAPED_SLASH(false); + tt=mDOLLAR_REGEXP_CTOR_END(false,true); + } + else { + boolean synPredMatched730 = false; + if (((LA(1)=='$') && (LA(2)=='$') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true))) { + int _m730 = mark(); + synPredMatched730 = true; + inputState.guessing++; + try { + { + match('$'); + match('$'); + } + } + catch (RecognitionException pe) { + synPredMatched730 = false; + } + rewind(_m730); +inputState.guessing--; + } + if ( synPredMatched730 ) { + mESCAPED_DOLLAR(false); + tt=mDOLLAR_REGEXP_CTOR_END(false,true); + } + else if (((LA(1)=='$') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true))&&(!atValidDollarEscape() && !atDollarSlashEscape() && !atDollarDollarEscape())) { + match('$'); + tt=mDOLLAR_REGEXP_CTOR_END(false,true); + } + else if ((_tokenSet_9.member(LA(1)))) { + mDOLLAR_REGEXP_SYMBOL(false); + tt=mDOLLAR_REGEXP_CTOR_END(false,true); + } + else if ((LA(1)=='$') && (true)) { + _saveIndex=text.length(); + match('$'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + // Yes, it's a regexp constructor, and we've got a value part. + tt = STRING_CTOR_START; + stringCtorState = SCS_VAL + SCS_DRE_TYPE; + + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + _ttype = tt; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDOLLAR_REGEXP_SYMBOL(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DOLLAR_REGEXP_SYMBOL; + int _saveIndex; + + switch ( LA(1)) { + case '/': + { + match('/'); + break; + } + case '\n': case '\r': + { + mSTRING_NL(false,true); + break; + } + default: + if ((LA(1)=='\\') && (LA(2)=='\n'||LA(2)=='\r') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true)) { + _saveIndex=text.length(); + match('\\'); + text.setLength(_saveIndex); + _saveIndex=text.length(); + mONE_NL(false,false); + text.setLength(_saveIndex); + } + else if (((LA(1)=='\\') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true))&&( LA(2)!='\n' && LA(2)!='\r' )) { + match('\\'); + } + else if ((_tokenSet_8.member(LA(1)))) { + { + match(_tokenSet_8); + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final int mDOLLAR_REGEXP_CTOR_END(boolean _createToken, + boolean fromStart + ) throws RecognitionException, CharStreamException, TokenStreamException { + int tt=STRING_CTOR_END; + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DOLLAR_REGEXP_CTOR_END; + int _saveIndex; + + { + _loop741: + do { + boolean synPredMatched738 = false; + if (((LA(1)=='$') && (LA(2)=='/') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true))) { + int _m738 = mark(); + synPredMatched738 = true; + inputState.guessing++; + try { + { + match('$'); + match('/'); + } + } + catch (RecognitionException pe) { + synPredMatched738 = false; + } + rewind(_m738); +inputState.guessing--; + } + if ( synPredMatched738 ) { + mESCAPED_SLASH(false); + } + else { + boolean synPredMatched740 = false; + if (((LA(1)=='$') && (LA(2)=='$') && ((LA(3) >= '\u0000' && LA(3) <= '\ufffe')) && (true))) { + int _m740 = mark(); + synPredMatched740 = true; + inputState.guessing++; + try { + { + match('$'); + match('$'); + } + } + catch (RecognitionException pe) { + synPredMatched740 = false; + } + rewind(_m740); +inputState.guessing--; + } + if ( synPredMatched740 ) { + mESCAPED_DOLLAR(false); + } + else if (((_tokenSet_9.member(LA(1))) && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true))&&( !(LA(1) == '/' && LA(2) == '$') )) { + mDOLLAR_REGEXP_SYMBOL(false); + } + else if (((LA(1)=='$') && ((LA(2) >= '\u0000' && LA(2) <= '\ufffe')) && (true) && (true))&&(!atValidDollarEscape() && !atDollarSlashEscape() && !atDollarDollarEscape())) { + match('$'); + } + else { + break _loop741; + } + } + } while (true); + } + { + switch ( LA(1)) { + case '/': + { + _saveIndex=text.length(); + match("/$"); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + if (fromStart) tt = STRING_LITERAL; // plain regexp literal! + + } + break; + } + case '$': + { + _saveIndex=text.length(); + match('$'); + text.setLength(_saveIndex); + if ( inputState.guessing==0 ) { + + // Yes, it's a regexp constructor, and we've got a value part. + tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE); + stringCtorState = SCS_VAL + SCS_DRE_TYPE; + + } + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + _ttype = tt; + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + return tt; + } + + protected final void mESCAPED_SLASH(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ESCAPED_SLASH; + int _saveIndex; + + match('$'); + match('/'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append('/'); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mESCAPED_DOLLAR(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = ESCAPED_DOLLAR; + int _saveIndex; + + match('$'); + match('$'); + if ( inputState.guessing==0 ) { + text.setLength(_begin); text.append('$'); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mHEX_DIGIT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = HEX_DIGIT; + int _saveIndex; + + { + switch ( LA(1)) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + { + matchRange('0','9'); + break; + } + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': + { + matchRange('A','F'); + break; + } + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': + { + matchRange('a','f'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mVOCAB(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = VOCAB; + int _saveIndex; + + matchRange('\3','\377'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mIDENT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = IDENT; + int _saveIndex; + + { + if (((_tokenSet_0.member(LA(1))) && (true) && (true) && (true))&&(stringCtorState == 0)) { + { + if ((LA(1)=='$')) { + mDOLLAR(false); + } + else if ((_tokenSet_10.member(LA(1)))) { + mLETTER(false); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + { + _loop766: + do { + switch ( LA(1)) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + { + mDIGIT(false); + break; + } + case '$': + { + mDOLLAR(false); + break; + } + default: + if ((_tokenSet_10.member(LA(1)))) { + mLETTER(false); + } + else { + break _loop766; + } + } + } while (true); + } + } + else if ((_tokenSet_10.member(LA(1))) && (true) && (true) && (true)) { + mLETTER(false); + { + _loop768: + do { + if ((_tokenSet_10.member(LA(1)))) { + mLETTER(false); + } + else if (((LA(1) >= '0' && LA(1) <= '9'))) { + mDIGIT(false); + } + else { + break _loop768; + } + + } while (true); + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + if ( inputState.guessing==0 ) { + + if (stringCtorState != 0) { + if (LA(1) == '.' && LA(2) != '$' && + Character.isJavaIdentifierStart(LA(2))) { + // pick up another name component before going literal again: + restartStringCtor(false); + } else { + // go back to the string + restartStringCtor(true); + } + } + int ttype = testLiteralsTable(IDENT); + // Java doesn't have the keywords 'as', 'in' or 'def so we make some allowances + // for them in package names for better integration with existing Java packages + if ((ttype == LITERAL_as || ttype == LITERAL_def || ttype == LITERAL_in || ttype == LITERAL_trait) && + (LA(1) == '.' || lastSigTokenType == DOT || lastSigTokenType == LITERAL_package)) { + ttype = IDENT; + } + // allow access to classes with the name package + if ((ttype == LITERAL_package) && + (LA(1) == '.' || lastSigTokenType == DOT || lastSigTokenType == LITERAL_import + || (LA(1) == ')' && lastSigTokenType == LPAREN))) { + ttype = IDENT; + } + if (ttype == LITERAL_static && LA(1) == '.') { + ttype = IDENT; + } + + _ttype = ttype; + + // check if "assert" keyword is enabled + if (assertEnabled && "assert".equals(new String(text.getBuffer(),_begin,text.length()-_begin))) { + _ttype = LITERAL_assert; // set token type for the rule in the parser + } + // check if "enum" keyword is enabled + if (enumEnabled && "enum".equals(new String(text.getBuffer(),_begin,text.length()-_begin))) { + _ttype = LITERAL_enum; // set token type for the rule in the parser + } + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mLETTER(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = LETTER; + int _saveIndex; + + switch ( LA(1)) { + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + { + matchRange('a','z'); + break; + } + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + { + matchRange('A','Z'); + break; + } + case '\u00c0': case '\u00c1': case '\u00c2': case '\u00c3': + case '\u00c4': case '\u00c5': case '\u00c6': case '\u00c7': + case '\u00c8': case '\u00c9': case '\u00ca': case '\u00cb': + case '\u00cc': case '\u00cd': case '\u00ce': case '\u00cf': + case '\u00d0': case '\u00d1': case '\u00d2': case '\u00d3': + case '\u00d4': case '\u00d5': case '\u00d6': + { + matchRange('\u00C0','\u00D6'); + break; + } + case '\u00d8': case '\u00d9': case '\u00da': case '\u00db': + case '\u00dc': case '\u00dd': case '\u00de': case '\u00df': + case '\u00e0': case '\u00e1': case '\u00e2': case '\u00e3': + case '\u00e4': case '\u00e5': case '\u00e6': case '\u00e7': + case '\u00e8': case '\u00e9': case '\u00ea': case '\u00eb': + case '\u00ec': case '\u00ed': case '\u00ee': case '\u00ef': + case '\u00f0': case '\u00f1': case '\u00f2': case '\u00f3': + case '\u00f4': case '\u00f5': case '\u00f6': + { + matchRange('\u00D8','\u00F6'); + break; + } + case '\u00f8': case '\u00f9': case '\u00fa': case '\u00fb': + case '\u00fc': case '\u00fd': case '\u00fe': case '\u00ff': + { + matchRange('\u00F8','\u00FF'); + break; + } + case '_': + { + match('_'); + break; + } + default: + if (((LA(1) >= '\u0100' && LA(1) <= '\ufffe'))) { + matchRange('\u0100','\uFFFE'); + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDIGIT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DIGIT; + int _saveIndex; + + matchRange('0','9'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDIGITS_WITH_UNDERSCORE(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DIGITS_WITH_UNDERSCORE; + int _saveIndex; + + mDIGIT(false); + { + if ((LA(1)=='0'||LA(1)=='1'||LA(1)=='2'||LA(1)=='3'||LA(1)=='4'||LA(1)=='5'||LA(1)=='6'||LA(1)=='7'||LA(1)=='8'||LA(1)=='9'||LA(1)=='_')) { + mDIGITS_WITH_UNDERSCORE_OPT(false); + } + else { + } + + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mDIGITS_WITH_UNDERSCORE_OPT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = DIGITS_WITH_UNDERSCORE_OPT; + int _saveIndex; + + { + _loop775: + do { + if (((LA(1) >= '0' && LA(1) <= '9')) && (LA(2)=='0'||LA(2)=='1'||LA(2)=='2'||LA(2)=='3'||LA(2)=='4'||LA(2)=='5'||LA(2)=='6'||LA(2)=='7'||LA(2)=='8'||LA(2)=='9'||LA(2)=='_')) { + mDIGIT(false); + } + else if ((LA(1)=='_')) { + match('_'); + } + else { + break _loop775; + } + + } while (true); + } + mDIGIT(false); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mNUM_INT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = NUM_INT; + int _saveIndex; + Token e=null; + Token f2=null; + Token g2=null; + Token f3=null; + Token g3=null; + Token f4=null; + boolean isDecimal=false; Token t=null; + + { + switch ( LA(1)) { + case '0': + { + match('0'); + if ( inputState.guessing==0 ) { + isDecimal = true; + } + { + switch ( LA(1)) { + case 'X': case 'x': + { + { + switch ( LA(1)) { + case 'x': + { + match('x'); + break; + } + case 'X': + { + match('X'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + isDecimal = false; + } + mHEX_DIGIT(false); + { + if ((_tokenSet_11.member(LA(1))) && (true) && (true) && (true)) { + { + _loop782: + do { + if ((_tokenSet_12.member(LA(1))) && (_tokenSet_11.member(LA(2))) && (true) && (true)) { + mHEX_DIGIT(false); + } + else if ((LA(1)=='_')) { + match('_'); + } + else { + break _loop782; + } + + } while (true); + } + mHEX_DIGIT(false); + } + else { + } + + } + break; + } + case 'B': case 'b': + { + { + switch ( LA(1)) { + case 'b': + { + match('b'); + break; + } + case 'B': + { + match('B'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + { + switch ( LA(1)) { + case '0': + { + match('0'); + break; + } + case '1': + { + match('1'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + { + if ((LA(1)=='0'||LA(1)=='1'||LA(1)=='_')) { + { + _loop787: + do { + if ((LA(1)=='0') && (LA(2)=='0'||LA(2)=='1'||LA(2)=='_')) { + match('0'); + } + else if ((LA(1)=='1') && (LA(2)=='0'||LA(2)=='1'||LA(2)=='_')) { + match('1'); + } + else if ((LA(1)=='_')) { + match('_'); + } + else { + break _loop787; + } + + } while (true); + } + { + switch ( LA(1)) { + case '0': + { + match('0'); + break; + } + case '1': + { + match('1'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + } + else { + } + + } + if ( inputState.guessing==0 ) { + isDecimal = false; + } + break; + } + default: + boolean synPredMatched791 = false; + if ((((LA(1) >= '0' && LA(1) <= '9')) && (true) && (true) && (true))) { + int _m791 = mark(); + synPredMatched791 = true; + inputState.guessing++; + try { + { + mDIGITS_WITH_UNDERSCORE(false); + { + switch ( LA(1)) { + case '.': + { + match('.'); + mDIGITS_WITH_UNDERSCORE(false); + break; + } + case 'E': case 'e': + { + mEXPONENT(false); + break; + } + case 'D': case 'F': case 'd': case 'f': + { + mFLOAT_SUFFIX(false); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + } + } + catch (RecognitionException pe) { + synPredMatched791 = false; + } + rewind(_m791); +inputState.guessing--; + } + if ( synPredMatched791 ) { + mDIGITS_WITH_UNDERSCORE(false); + } + else if (((LA(1) >= '0' && LA(1) <= '7')) && (true) && (true) && (true)) { + { + matchRange('0','7'); + } + { + if ((LA(1)=='0'||LA(1)=='1'||LA(1)=='2'||LA(1)=='3'||LA(1)=='4'||LA(1)=='5'||LA(1)=='6'||LA(1)=='7'||LA(1)=='_')) { + { + _loop795: + do { + if (((LA(1) >= '0' && LA(1) <= '7')) && (LA(2)=='0'||LA(2)=='1'||LA(2)=='2'||LA(2)=='3'||LA(2)=='4'||LA(2)=='5'||LA(2)=='6'||LA(2)=='7'||LA(2)=='_')) { + matchRange('0','7'); + } + else if ((LA(1)=='_')) { + match('_'); + } + else { + break _loop795; + } + + } while (true); + } + { + matchRange('0','7'); + } + } + else { + } + + } + if ( inputState.guessing==0 ) { + isDecimal = false; + } + } + else { + } + } + } + break; + } + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + { + { + matchRange('1','9'); + } + { + if ((LA(1)=='0'||LA(1)=='1'||LA(1)=='2'||LA(1)=='3'||LA(1)=='4'||LA(1)=='5'||LA(1)=='6'||LA(1)=='7'||LA(1)=='8'||LA(1)=='9'||LA(1)=='_')) { + mDIGITS_WITH_UNDERSCORE_OPT(false); + } + else { + } + + } + if ( inputState.guessing==0 ) { + isDecimal=true; + } + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + { + switch ( LA(1)) { + case 'L': case 'l': + { + { + switch ( LA(1)) { + case 'l': + { + match('l'); + break; + } + case 'L': + { + match('L'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + _ttype = NUM_LONG; + } + break; + } + case 'I': case 'i': + { + { + switch ( LA(1)) { + case 'i': + { + match('i'); + break; + } + case 'I': + { + match('I'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + _ttype = NUM_INT; + } + break; + } + case 'G': case 'g': + { + mBIG_SUFFIX(false); + if ( inputState.guessing==0 ) { + _ttype = NUM_BIG_INT; + } + break; + } + default: + boolean synPredMatched804 = false; + if ((((LA(1)=='.'||LA(1)=='D'||LA(1)=='E'||LA(1)=='F'||LA(1)=='d'||LA(1)=='e'||LA(1)=='f'))&&(isDecimal))) { + int _m804 = mark(); + synPredMatched804 = true; + inputState.guessing++; + try { + { + if ((_tokenSet_13.member(LA(1)))) { + matchNot('.'); + } + else if ((LA(1)=='.')) { + match('.'); + { + matchRange('0','9'); + } + } + else { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + + } + } + catch (RecognitionException pe) { + synPredMatched804 = false; + } + rewind(_m804); +inputState.guessing--; + } + if ( synPredMatched804 ) { + { + switch ( LA(1)) { + case '.': + { + match('.'); + mDIGITS_WITH_UNDERSCORE(false); + { + if ((LA(1)=='E'||LA(1)=='e')) { + mEXPONENT(true); + e=_returnToken; + } + else { + } + + } + { + switch ( LA(1)) { + case 'D': case 'F': case 'd': case 'f': + { + mFLOAT_SUFFIX(true); + f2=_returnToken; + if ( inputState.guessing==0 ) { + t=f2; + } + break; + } + case 'G': case 'g': + { + mBIG_SUFFIX(true); + g2=_returnToken; + if ( inputState.guessing==0 ) { + t=g2; + } + break; + } + default: + { + } + } + } + break; + } + case 'E': case 'e': + { + mEXPONENT(false); + { + switch ( LA(1)) { + case 'D': case 'F': case 'd': case 'f': + { + mFLOAT_SUFFIX(true); + f3=_returnToken; + if ( inputState.guessing==0 ) { + t=f3; + } + break; + } + case 'G': case 'g': + { + mBIG_SUFFIX(true); + g3=_returnToken; + if ( inputState.guessing==0 ) { + t=g3; + } + break; + } + default: + { + } + } + } + break; + } + case 'D': case 'F': case 'd': case 'f': + { + mFLOAT_SUFFIX(true); + f4=_returnToken; + if ( inputState.guessing==0 ) { + t=f4; + } + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + if ( inputState.guessing==0 ) { + + String txt = (t == null ? "" : t.getText().toUpperCase()); + if (txt.indexOf('F') >= 0) { + _ttype = NUM_FLOAT; + } else if (txt.indexOf('G') >= 0) { + _ttype = NUM_BIG_DECIMAL; + } else { + _ttype = NUM_DOUBLE; // assume double + } + + } + } + else { + } + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mEXPONENT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = EXPONENT; + int _saveIndex; + + { + switch ( LA(1)) { + case 'e': + { + match('e'); + break; + } + case 'E': + { + match('E'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + { + switch ( LA(1)) { + case '+': + { + match('+'); + break; + } + case '-': + { + match('-'); + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': case '_': + { + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + } + { + _loop814: + do { + if (((LA(1) >= '0' && LA(1) <= '9')) && (LA(2)=='0'||LA(2)=='1'||LA(2)=='2'||LA(2)=='3'||LA(2)=='4'||LA(2)=='5'||LA(2)=='6'||LA(2)=='7'||LA(2)=='8'||LA(2)=='9'||LA(2)=='_')) { + matchRange('0','9'); + } + else if ((LA(1)=='_')) { + match('_'); + } + else { + break _loop814; + } + + } while (true); + } + { + matchRange('0','9'); + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mFLOAT_SUFFIX(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = FLOAT_SUFFIX; + int _saveIndex; + + switch ( LA(1)) { + case 'f': + { + match('f'); + break; + } + case 'F': + { + match('F'); + break; + } + case 'd': + { + match('d'); + break; + } + case 'D': + { + match('D'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + protected final void mBIG_SUFFIX(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = BIG_SUFFIX; + int _saveIndex; + + switch ( LA(1)) { + case 'g': + { + match('g'); + break; + } + case 'G': + { + match('G'); + break; + } + default: + { + throw new NoViableAltForCharException((char)LA(1), getFilename(), getLine(), getColumn()); + } + } + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + public final void mAT(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException { + int _ttype; Token _token=null; int _begin=text.length(); + _ttype = AT; + int _saveIndex; + + match('@'); + if ( _createToken && _token==null && _ttype!=Token.SKIP ) { + _token = makeToken(_ttype); + _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin)); + } + _returnToken = _token; + } + + + private static final long[] mk_tokenSet_0() { + long[] data = new long[2560]; + data[0]=68719476736L; + data[1]=576460745995190270L; + data[3]=-36028797027352577L; + for (int i = 4; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_0 = new BitSet(mk_tokenSet_0()); + private static final long[] mk_tokenSet_1() { + long[] data = new long[2048]; + data[0]=-9217L; + for (int i = 1; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_1 = new BitSet(mk_tokenSet_1()); + private static final long[] mk_tokenSet_2() { + long[] data = new long[2048]; + data[0]=-4398046520321L; + for (int i = 1; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_2 = new BitSet(mk_tokenSet_2()); + private static final long[] mk_tokenSet_3() { + long[] data = new long[2048]; + data[0]=-549755813889L; + for (int i = 1; i<=1023; i++) { data[i]=-1L; } + return data; + } + public static final BitSet _tokenSet_3 = new BitSet(mk_tokenSet_3()); + private static final long[] mk_tokenSet_4() { + long[] data = new long[2048]; + data[0]=-635655169025L; + data[1]=-268435457L; + for (int i = 2; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_4 = new BitSet(mk_tokenSet_4()); + private static final long[] mk_tokenSet_5() { + long[] data = new long[2048]; + data[0]=-17179869185L; + for (int i = 1; i<=1023; i++) { data[i]=-1L; } + return data; + } + public static final BitSet _tokenSet_5 = new BitSet(mk_tokenSet_5()); + private static final long[] mk_tokenSet_6() { + long[] data = new long[2048]; + data[0]=-140737488355329L; + for (int i = 1; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_6 = new BitSet(mk_tokenSet_6()); + private static final long[] mk_tokenSet_7() { + long[] data = new long[2048]; + data[0]=-140806207832065L; + for (int i = 1; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_7 = new BitSet(mk_tokenSet_7()); + private static final long[] mk_tokenSet_8() { + long[] data = new long[2048]; + data[0]=-140806207841281L; + data[1]=-268435457L; + for (int i = 2; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_8 = new BitSet(mk_tokenSet_8()); + private static final long[] mk_tokenSet_9() { + long[] data = new long[2048]; + data[0]=-68719476737L; + for (int i = 1; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_9 = new BitSet(mk_tokenSet_9()); + private static final long[] mk_tokenSet_10() { + long[] data = new long[2560]; + data[1]=576460745995190270L; + data[3]=-36028797027352577L; + for (int i = 4; i<=1022; i++) { data[i]=-1L; } + data[1023]=9223372036854775807L; + return data; + } + public static final BitSet _tokenSet_10 = new BitSet(mk_tokenSet_10()); + private static final long[] mk_tokenSet_11() { + long[] data = new long[1025]; + data[0]=287948901175001088L; + data[1]=543313363070L; + return data; + } + public static final BitSet _tokenSet_11 = new BitSet(mk_tokenSet_11()); + private static final long[] mk_tokenSet_12() { + long[] data = new long[1025]; + data[0]=287948901175001088L; + data[1]=541165879422L; + return data; + } + public static final BitSet _tokenSet_12 = new BitSet(mk_tokenSet_12()); + private static final long[] mk_tokenSet_13() { + long[] data = new long[2048]; + data[0]=-70368744177665L; + for (int i = 1; i<=1023; i++) { data[i]=-1L; } + return data; + } + public static final BitSet _tokenSet_13 = new BitSet(mk_tokenSet_13()); + + } diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyLexer.smap b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyLexer.smap new file mode 100644 index 0000000000..b255b6bf1b --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyLexer.smap @@ -0,0 +1,2786 @@ +SMAP +GroovyLexer.java +G +*S G +*F ++ 0 groovy.g +groovy.g +*L +0:408 +0:414 +0:420 +0:426 +0:432 +0:438 +0:444 +0:450 +0:456 +0:462 +0:468 +0:474 +0:480 +0:488 +0:494 +0:500 +0:504 +0:508 +0:512 +0:516 +0:520 +0:524 +0:528 +0:532 +0:536 +0:540 +0:544 +0:548 +0:552 +0:556 +0:560 +0:564 +0:568 +0:572 +0:576 +0:580 +0:584 +0:588 +0:592 +0:596 +0:600 +0:604 +0:608 +0:612 +0:616 +0:620 +0:624 +0:628 +0:632 +0:636 +0:640 +0:644 +0:648 +0:652 +0:656 +0:660 +0:664 +0:668 +0:672 +0:676 +0:680 +0:684 +0:688 +0:692 +0:696 +0:700 +0:704 +0:708 +0:712 +0:716 +25:3 +25:5 +25:6 +25:8 +25:9 +25:10 +25:11 +25:13 +25:14 +3757:43 +3758:44 +3759:45 +3760:46 +3761:47 +3762:48 +3764:50 +3765:51 +3766:52 +3767:53 +3768:54 +3769:55 +3770:56 +3771:57 +3773:59 +3774:60 +3775:61 +3776:62 +3778:64 +3779:65 +3780:66 +3781:67 +3783:69 +3784:70 +3785:71 +3786:72 +3787:73 +3788:74 +3789:75 +3790:76 +3791:77 +3793:79 +3795:81 +3796:82 +3797:83 +3798:84 +3799:85 +3800:86 +3801:87 +3802:88 +3804:90 +3805:91 +3806:92 +3807:93 +3808:94 +3810:96 +3811:97 +3812:98 +3813:99 +3814:100 +3815:101 +3816:102 +3818:104 +3819:105 +3820:106 +3821:107 +3822:108 +3824:110 +3825:111 +3826:112 +3828:114 +3829:115 +3830:116 +3831:117 +3832:118 +3833:119 +3834:120 +3835:121 +3836:122 +3837:123 +3838:124 +3839:125 +3840:126 +3841:127 +3842:128 +3843:129 +3844:130 +3845:131 +3846:132 +3847:133 +3848:134 +3849:135 +3850:136 +3851:137 +3852:138 +3853:139 +3854:140 +3855:141 +3856:142 +3857:143 +3858:144 +3859:145 +3860:146 +3861:147 +3862:148 +3863:149 +3864:150 +3865:151 +3866:152 +3867:153 +3868:154 +3869:155 +3870:156 +3871:157 +3872:158 +3873:159 +3874:160 +3875:161 +3876:162 +3877:163 +3878:164 +3879:165 +3880:166 +3881:167 +3882:168 +3883:169 +3884:170 +3885:171 +3886:172 +3887:173 +3888:174 +3889:175 +3890:176 +3891:177 +3892:178 +3893:179 +3894:180 +3895:181 +3896:182 +3897:183 +3898:184 +3899:185 +3900:186 +3901:187 +3902:188 +3903:189 +3904:190 +3905:191 +3906:192 +3907:193 +3908:194 +3910:196 +3911:197 +3912:198 +3913:199 +3914:200 +3915:201 +3916:202 +3917:203 +3918:204 +3920:206 +3921:207 +3922:208 +3923:209 +3924:210 +3925:211 +3926:212 +3927:213 +3929:215 +3930:216 +3931:217 +3933:219 +3934:220 +3935:221 +3937:223 +3938:224 +3939:225 +3941:227 +3942:228 +3943:229 +3944:230 +3945:231 +3946:232 +3947:233 +3948:234 +3949:235 +3950:236 +3951:237 +3952:238 +3953:239 +3954:240 +3955:241 +3956:242 +3957:243 +3958:244 +3959:245 +3960:246 +3961:247 +3962:248 +3963:249 +3964:250 +3965:251 +3966:252 +3967:253 +3968:254 +3969:255 +3970:256 +3971:257 +3972:258 +3973:259 +3974:260 +3975:261 +3976:262 +3977:263 +3978:264 +3979:265 +3980:266 +3981:267 +3982:268 +3983:269 +3984:270 +3985:271 +3986:272 +3987:273 +3988:274 +3989:275 +3990:276 +3991:277 +3992:278 +3993:279 +3995:281 +3996:282 +3997:283 +3998:284 +3999:285 +4000:286 +4001:287 +4002:288 +4003:289 +4004:290 +4005:291 +4006:292 +4007:293 +4008:294 +4009:295 +4010:296 +4011:297 +4012:298 +4013:299 +4014:300 +4015:301 +4016:302 +4017:303 +4018:304 +4019:305 +4020:306 +4021:307 +4022:308 +4023:309 +4024:310 +4026:312 +4027:313 +4028:314 +4029:315 +4030:316 +4031:317 +4032:318 +4033:319 +4034:320 +4035:321 +4041:744 +4041:745 +4041:746 +4041:747 +4041:749 +4041:750 +4041:751 +4041:752 +4041:753 +4041:754 +4041:755 +4042:757 +4042:758 +4042:759 +4042:760 +4042:762 +4042:763 +4042:764 +4042:766 +4042:767 +4042:768 +4042:769 +4042:770 +4042:771 +4043:773 +4043:774 +4043:775 +4043:776 +4043:778 +4043:779 +4043:780 +4043:782 +4043:783 +4043:784 +4043:785 +4043:786 +4043:787 +4044:789 +4044:790 +4044:791 +4044:792 +4044:794 +4044:795 +4044:796 +4044:798 +4044:799 +4044:800 +4044:801 +4044:802 +4044:803 +4045:805 +4045:806 +4045:807 +4045:808 +4045:810 +4045:811 +4045:812 +4045:814 +4045:815 +4045:816 +4045:817 +4045:818 +4045:819 +4046:821 +4046:822 +4046:823 +4046:824 +4046:826 +4046:827 +4046:828 +4046:830 +4046:831 +4046:832 +4046:833 +4046:834 +4046:835 +4047:837 +4047:838 +4047:839 +4047:840 +4047:842 +4047:843 +4047:844 +4047:846 +4047:847 +4047:848 +4047:849 +4047:850 +4047:851 +4048:853 +4048:854 +4048:855 +4048:856 +4048:858 +4048:859 +4048:860 +4048:861 +4048:862 +4048:863 +4048:864 +4049:866 +4049:867 +4049:868 +4049:869 +4049:871 +4049:872 +4049:873 +4049:874 +4049:875 +4049:876 +4049:877 +4050:879 +4050:880 +4050:881 +4050:882 +4050:884 +4050:885 +4050:886 +4050:887 +4050:888 +4050:889 +4050:890 +4051:892 +4051:893 +4051:894 +4051:895 +4051:897 +4051:898 +4051:899 +4051:900 +4051:901 +4051:902 +4051:903 +4052:905 +4052:906 +4052:907 +4052:908 +4052:910 +4052:911 +4052:912 +4052:913 +4052:914 +4052:915 +4052:916 +4053:918 +4053:919 +4053:920 +4053:921 +4053:923 +4053:924 +4053:925 +4053:926 +4053:927 +4053:928 +4053:929 +4054:931 +4054:932 +4054:933 +4054:934 +4054:936 +4054:937 +4054:938 +4054:939 +4054:940 +4054:941 +4054:942 +4055:944 +4055:945 +4055:946 +4055:947 +4055:949 +4055:950 +4055:951 +4055:952 +4055:953 +4055:954 +4055:955 +4056:957 +4056:958 +4056:959 +4056:960 +4056:962 +4056:963 +4056:964 +4056:965 +4056:966 +4056:967 +4056:968 +4057:970 +4057:971 +4057:972 +4057:973 +4057:975 +4057:976 +4057:977 +4057:978 +4057:979 +4057:980 +4057:981 +4058:983 +4058:984 +4058:985 +4058:986 +4058:988 +4058:989 +4058:990 +4058:991 +4058:992 +4058:993 +4058:994 +4060:996 +4060:997 +4060:998 +4060:999 +4060:1001 +4060:1002 +4060:1003 +4060:1004 +4060:1005 +4060:1006 +4060:1007 +4062:1009 +4062:1010 +4062:1011 +4062:1012 +4062:1014 +4062:1015 +4062:1016 +4062:1017 +4062:1018 +4062:1019 +4062:1020 +4063:1022 +4063:1023 +4063:1024 +4063:1025 +4063:1027 +4063:1028 +4063:1029 +4063:1030 +4063:1031 +4063:1032 +4063:1033 +4064:1035 +4064:1036 +4064:1037 +4064:1038 +4064:1040 +4064:1041 +4064:1042 +4064:1043 +4064:1044 +4064:1045 +4064:1046 +4065:1048 +4065:1049 +4065:1050 +4065:1051 +4065:1053 +4065:1054 +4065:1055 +4065:1056 +4065:1057 +4065:1058 +4065:1059 +4066:1061 +4066:1062 +4066:1063 +4066:1064 +4066:1066 +4066:1067 +4066:1068 +4066:1069 +4066:1070 +4066:1071 +4066:1072 +4067:1074 +4067:1075 +4067:1076 +4067:1077 +4067:1079 +4067:1080 +4067:1081 +4067:1082 +4067:1083 +4067:1084 +4067:1085 +4068:1087 +4068:1088 +4068:1089 +4068:1090 +4068:1092 +4068:1093 +4068:1094 +4068:1095 +4068:1096 +4068:1097 +4068:1098 +4069:1100 +4069:1101 +4069:1102 +4069:1103 +4069:1105 +4069:1106 +4069:1107 +4069:1108 +4069:1109 +4069:1110 +4069:1111 +4070:1113 +4070:1114 +4070:1115 +4070:1116 +4070:1118 +4070:1119 +4070:1120 +4070:1121 +4070:1122 +4070:1123 +4070:1124 +4071:1126 +4071:1127 +4071:1128 +4071:1129 +4071:1131 +4071:1132 +4071:1133 +4071:1134 +4071:1135 +4071:1136 +4071:1137 +4072:1139 +4072:1140 +4072:1141 +4072:1142 +4072:1144 +4072:1145 +4072:1146 +4072:1147 +4072:1148 +4072:1149 +4072:1150 +4073:1152 +4073:1153 +4073:1154 +4073:1155 +4073:1157 +4073:1158 +4073:1159 +4073:1160 +4073:1161 +4073:1162 +4073:1163 +4074:1165 +4074:1166 +4074:1167 +4074:1168 +4074:1170 +4074:1171 +4074:1172 +4074:1173 +4074:1174 +4074:1175 +4074:1176 +4075:1178 +4075:1179 +4075:1180 +4075:1181 +4075:1183 +4075:1184 +4075:1185 +4075:1186 +4075:1187 +4075:1188 +4075:1189 +4076:1191 +4076:1192 +4076:1193 +4076:1194 +4076:1196 +4076:1197 +4076:1198 +4076:1199 +4076:1200 +4076:1201 +4076:1202 +4077:1204 +4077:1205 +4077:1206 +4077:1207 +4077:1209 +4077:1210 +4077:1211 +4077:1212 +4077:1213 +4077:1214 +4077:1215 +4078:1217 +4078:1218 +4078:1219 +4078:1220 +4078:1222 +4078:1223 +4078:1224 +4078:1225 +4078:1226 +4078:1227 +4078:1228 +4079:1230 +4079:1231 +4079:1232 +4079:1233 +4079:1235 +4079:1236 +4079:1237 +4079:1238 +4079:1239 +4079:1240 +4079:1241 +4080:1243 +4080:1244 +4080:1245 +4080:1246 +4080:1248 +4080:1249 +4080:1250 +4080:1251 +4080:1252 +4080:1253 +4080:1254 +4081:1256 +4081:1257 +4081:1258 +4081:1259 +4081:1261 +4081:1262 +4081:1263 +4081:1264 +4081:1265 +4081:1266 +4081:1267 +4082:1269 +4082:1270 +4082:1271 +4082:1272 +4082:1274 +4082:1275 +4082:1276 +4082:1277 +4082:1278 +4082:1279 +4082:1280 +4083:1282 +4083:1283 +4083:1284 +4083:1285 +4083:1287 +4083:1288 +4083:1289 +4083:1290 +4083:1291 +4083:1292 +4083:1293 +4084:1295 +4084:1296 +4084:1297 +4084:1298 +4084:1300 +4084:1301 +4084:1302 +4084:1303 +4084:1304 +4084:1305 +4084:1306 +4085:1308 +4085:1309 +4085:1310 +4085:1311 +4085:1313 +4085:1314 +4085:1315 +4085:1316 +4085:1317 +4085:1318 +4085:1319 +4086:1321 +4086:1322 +4086:1323 +4086:1324 +4086:1326 +4086:1327 +4086:1328 +4086:1329 +4086:1330 +4086:1331 +4086:1332 +4087:1334 +4087:1335 +4087:1336 +4087:1337 +4087:1339 +4087:1340 +4087:1341 +4087:1342 +4087:1343 +4087:1344 +4087:1345 +4088:1347 +4088:1348 +4088:1349 +4088:1350 +4088:1352 +4088:1353 +4088:1354 +4088:1355 +4088:1356 +4088:1357 +4088:1358 +4089:1360 +4089:1361 +4089:1362 +4089:1363 +4089:1365 +4089:1366 +4089:1367 +4089:1368 +4089:1369 +4089:1370 +4089:1371 +4090:1373 +4090:1374 +4090:1375 +4090:1376 +4090:1378 +4090:1379 +4090:1380 +4090:1381 +4090:1382 +4090:1383 +4090:1384 +4091:1386 +4091:1387 +4091:1388 +4091:1389 +4091:1391 +4091:1392 +4091:1393 +4091:1394 +4091:1395 +4091:1396 +4091:1397 +4093:1399 +4093:1400 +4093:1401 +4093:1402 +4093:1404 +4093:1405 +4093:1406 +4093:1407 +4093:1408 +4093:1409 +4093:1410 +4094:1412 +4094:1413 +4094:1414 +4094:1415 +4094:1417 +4094:1418 +4094:1419 +4094:1420 +4094:1421 +4094:1422 +4094:1423 +4095:1425 +4095:1426 +4095:1427 +4095:1428 +4095:1430 +4095:1431 +4095:1432 +4095:1433 +4095:1434 +4095:1435 +4095:1436 +4096:1438 +4096:1439 +4096:1440 +4096:1441 +4096:1443 +4096:1444 +4096:1445 +4096:1446 +4096:1447 +4096:1448 +4096:1449 +4097:1451 +4097:1452 +4097:1453 +4097:1454 +4097:1456 +4097:1457 +4097:1458 +4097:1459 +4097:1460 +4097:1461 +4097:1462 +4098:1464 +4098:1465 +4098:1466 +4098:1467 +4098:1469 +4098:1470 +4098:1471 +4098:1472 +4098:1473 +4098:1474 +4098:1475 +4099:1477 +4099:1478 +4099:1479 +4099:1480 +4099:1482 +4099:1483 +4099:1484 +4099:1485 +4099:1486 +4099:1487 +4099:1488 +4100:1490 +4100:1491 +4100:1492 +4100:1493 +4100:1495 +4100:1496 +4100:1497 +4100:1498 +4100:1499 +4100:1500 +4100:1501 +4101:1503 +4101:1504 +4101:1505 +4101:1506 +4101:1508 +4101:1509 +4101:1510 +4101:1511 +4101:1512 +4101:1513 +4101:1514 +4102:1516 +4102:1517 +4102:1518 +4102:1519 +4102:1521 +4102:1522 +4102:1523 +4102:1524 +4102:1525 +4102:1526 +4102:1527 +4103:1529 +4103:1530 +4103:1531 +4103:1532 +4103:1534 +4103:1535 +4103:1536 +4103:1537 +4103:1538 +4103:1539 +4103:1540 +4104:1542 +4104:1543 +4104:1544 +4104:1545 +4104:1547 +4104:1548 +4104:1549 +4104:1550 +4104:1551 +4104:1552 +4104:1553 +4105:1555 +4105:1556 +4105:1557 +4105:1558 +4105:1560 +4105:1561 +4105:1562 +4105:1563 +4105:1564 +4105:1565 +4105:1566 +4108:1568 +4108:1569 +4108:1570 +4108:1571 +4108:1600 +4108:1601 +4108:1602 +4108:1603 +4108:1604 +4108:1605 +4113:1574 +4113:1575 +4113:1576 +4113:1580 +4113:1583 +4113:1586 +4113:1589 +4113:1590 +4113:1591 +4113:1592 +4113:1594 +4113:1595 +4113:1596 +4115:1581 +4115:1582 +4116:1584 +4116:1585 +4117:1587 +4117:1588 +4118:1577 +4118:1578 +4118:1579 +4120:1597 +4120:1598 +4124:1607 +4124:1608 +4124:1609 +4124:1610 +4124:1611 +4124:1612 +4124:1641 +4124:1642 +4124:1643 +4124:1644 +4124:1645 +4124:1646 +4129:1619 +4129:1624 +4129:1629 +4129:1630 +4129:1631 +4129:1632 +4130:1615 +4130:1616 +4130:1617 +4130:1618 +4131:1620 +4131:1621 +4131:1622 +4131:1623 +4132:1625 +4132:1626 +4132:1627 +4132:1628 +4134:1635 +4135:1637 +4136:1638 +4142:1648 +4142:1649 +4142:1650 +4142:1651 +4142:1652 +4142:1653 +4142:1676 +4142:1677 +4142:1678 +4142:1679 +4142:1680 +4142:1681 +4147:1658 +4147:1661 +4147:1664 +4147:1665 +4147:1666 +4147:1667 +4148:1656 +4148:1657 +4149:1659 +4149:1660 +4150:1662 +4150:1663 +4152:1670 +4153:1672 +4154:1673 +4162:1683 +4162:1684 +4162:1685 +4162:1686 +4162:1737 +4162:1738 +4162:1739 +4162:1740 +4162:1741 +4162:1742 +4166:1688 +4167:1720 +4167:1722 +4168:1690 +4168:1692 +4168:1693 +4168:1694 +4168:1695 +4168:1696 +4168:1697 +4168:1698 +4168:1701 +4168:1702 +4168:1703 +4168:1706 +4168:1707 +4168:1708 +4168:1709 +4168:1710 +4168:1711 +4168:1712 +4168:1713 +4168:1714 +4168:1715 +4168:1716 +4168:1717 +4168:1718 +4168:1719 +4172:1725 +4172:1726 +4173:1727 +4174:1728 +4175:1729 +4176:1730 +4177:1731 +4178:1732 +4179:1733 +4180:1734 +4185:1744 +4185:1745 +4185:1746 +4185:1747 +4185:1773 +4185:1774 +4185:1775 +4185:1776 +4185:1777 +4185:1778 +4189:1749 +4191:1750 +4191:1751 +4193:1753 +4193:1754 +4193:1755 +4193:1760 +4193:1761 +4193:1762 +4193:1763 +4193:1765 +4193:1766 +4197:1756 +4197:1758 +4200:1767 +4200:1768 +4201:1769 +4202:1770 +4209:1841 +4209:1842 +4209:1843 +4209:1844 +4209:1846 +4209:1847 +4209:1866 +4209:1867 +4209:1868 +4209:1869 +4209:1870 +4209:1871 +4213:1848 +4214:1849 +4214:1850 +4214:1851 +4214:1856 +4214:1857 +4214:1858 +4214:1859 +4214:1861 +4214:1862 +4218:1852 +4218:1854 +4220:1863 +4220:1864 +4225:1780 +4225:1781 +4225:1782 +4225:1783 +4225:1785 +4225:1786 +4225:1834 +4225:1835 +4225:1836 +4225:1837 +4225:1838 +4225:1839 +4229:1787 +4231:1788 +4231:1789 +4233:1791 +4233:1792 +4233:1793 +4233:1813 +4233:1816 +4233:1821 +4233:1822 +4233:1823 +4233:1824 +4233:1826 +4233:1827 +4244:1794 +4244:1795 +4244:1796 +4244:1797 +4244:1798 +4244:1799 +4244:1801 +4244:1802 +4244:1804 +4244:1805 +4244:1806 +4244:1807 +4244:1808 +4244:1809 +4244:1810 +4244:1811 +4244:1812 +4247:1814 +4247:1815 +4249:1817 +4249:1819 +4251:1828 +4253:1829 +4253:1830 +4254:1831 +4260:1873 +4260:1874 +4260:1875 +4260:1876 +4260:1877 +4260:1968 +4260:1994 +4260:2037 +4260:2049 +4260:2050 +4260:2051 +4260:2052 +4260:2053 +4260:2054 +4260:2055 +4260:2056 +4260:2057 +4260:2058 +4260:2059 +4265:1879 +4265:1880 +4265:1881 +4265:1882 +4265:1883 +4265:1884 +4265:1886 +4265:1888 +4265:1889 +4265:1890 +4265:1891 +4265:1892 +4265:1893 +4265:1894 +4265:1895 +4266:1896 +4266:1897 +4266:1898 +4267:1899 +4267:1900 +4267:1901 +4267:1902 +4267:1903 +4267:1904 +4267:1905 +4267:1908 +4267:1909 +4267:1910 +4267:1913 +4267:1914 +4267:1915 +4267:1918 +4267:1919 +4267:1920 +4267:1923 +4267:1955 +4267:1956 +4267:1957 +4267:1958 +4267:1959 +4267:1960 +4267:1961 +4267:1962 +4267:1963 +4267:1964 +4268:1924 +4268:1925 +4268:1926 +4268:1927 +4268:1928 +4268:1929 +4268:1931 +4268:1933 +4268:1934 +4268:1935 +4268:1936 +4268:1937 +4268:1938 +4268:1939 +4268:1940 +4268:1941 +4268:1942 +4268:1946 +4268:1947 +4268:1948 +4268:1949 +4268:1950 +4268:1951 +4268:1952 +4268:1953 +4268:1954 +4270:1965 +4270:1966 +4270:1967 +4271:1995 +4271:1996 +4271:1997 +4271:1998 +4272:1999 +4272:2000 +4273:2002 +4273:2003 +4273:2004 +4273:2005 +4273:2006 +4273:2007 +4273:2008 +4273:2011 +4273:2012 +4273:2013 +4273:2016 +4273:2017 +4273:2018 +4273:2021 +4273:2022 +4273:2023 +4273:2024 +4273:2025 +4273:2026 +4273:2027 +4273:2028 +4273:2029 +4273:2030 +4274:2031 +4274:2032 +4275:2034 +4275:2035 +4275:2036 +4276:1969 +4276:1970 +4276:1971 +4276:1972 +4276:1973 +4276:1974 +4276:1975 +4276:1977 +4276:1979 +4276:1980 +4276:1981 +4276:1982 +4276:1983 +4276:1984 +4276:1985 +4276:1986 +4277:1987 +4277:1988 +4277:1989 +4278:1990 +4279:1991 +4279:1992 +4280:2038 +4280:2039 +4280:2040 +4280:2041 +4281:2042 +4281:2043 +4282:2045 +4283:2046 +4283:2047 +4287:2271 +4287:2272 +4287:2273 +4287:2274 +4287:2275 +4287:2276 +4287:2277 +4287:2278 +4287:2400 +4287:2401 +4287:2402 +4287:2403 +4287:2404 +4287:2405 +4287:2406 +4294:2280 +4294:2281 +4294:2282 +4294:2283 +4294:2299 +4294:2331 +4294:2334 +4294:2335 +4294:2336 +4294:2337 +4294:2338 +4294:2339 +4294:2340 +4296:2284 +4296:2285 +4296:2286 +4296:2289 +4296:2290 +4296:2291 +4296:2294 +4296:2295 +4296:2296 +4296:2332 +4296:2333 +4297:2300 +4297:2301 +4297:2302 +4297:2303 +4297:2304 +4297:2305 +4297:2307 +4297:2309 +4297:2310 +4297:2311 +4297:2312 +4297:2313 +4297:2314 +4297:2315 +4297:2316 +4297:2317 +4297:2318 +4297:2322 +4297:2323 +4297:2324 +4297:2325 +4297:2326 +4297:2327 +4297:2328 +4297:2329 +4297:2330 +4299:2342 +4299:2343 +4299:2344 +4299:2350 +4299:2351 +4299:2352 +4299:2353 +4299:2354 +4299:2355 +4299:2356 +4299:2357 +4299:2358 +4299:2391 +4299:2392 +4299:2393 +4299:2394 +4299:2395 +4300:2346 +4300:2347 +4300:2348 +4300:2349 +4302:2361 +4303:2363 +4304:2364 +4305:2365 +4306:2366 +4308:2371 +4308:2372 +4308:2373 +4308:2374 +4309:2376 +4309:2377 +4309:2378 +4310:2379 +4311:2381 +4312:2382 +4313:2383 +4314:2384 +4315:2385 +4316:2386 +4319:2397 +4319:2398 +4323:2061 +4323:2062 +4323:2063 +4323:2064 +4323:2069 +4323:2070 +4323:2071 +4323:2072 +4323:2073 +4323:2074 +4327:2067 +4330:2408 +4330:2409 +4330:2410 +4330:2411 +4330:2412 +4330:2414 +4330:2415 +4330:2489 +4330:2490 +4330:2491 +4330:2492 +4330:2493 +4330:2494 +4336:2453 +4336:2459 +4336:2483 +4336:2484 +4336:2485 +4336:2486 +4336:2487 +4337:2417 +4337:2418 +4337:2419 +4337:2420 +4338:2421 +4338:2422 +4342:2428 +4342:2432 +4342:2444 +4342:2445 +4342:2446 +4342:2447 +4343:2429 +4343:2430 +4344:2431 +4345:2425 +4345:2426 +4346:2427 +4347:2433 +4347:2434 +4347:2435 +4347:2436 +4348:2437 +4349:2439 +4350:2440 +4351:2441 +4354:2450 +4354:2451 +4356:2460 +4356:2461 +4356:2462 +4356:2463 +4356:2464 +4356:2465 +4356:2466 +4356:2468 +4356:2469 +4356:2471 +4356:2472 +4356:2473 +4356:2474 +4356:2475 +4356:2476 +4356:2477 +4356:2478 +4356:2479 +4356:2480 +4356:2481 +4357:2454 +4357:2455 +4357:2456 +4357:2457 +4361:2609 +4361:2610 +4361:2611 +4361:2612 +4361:2613 +4361:2615 +4361:2616 +4361:2692 +4361:2693 +4361:2694 +4361:2695 +4361:2696 +4361:2697 +4366:2617 +4366:2618 +4366:2619 +4369:2641 +4369:2663 +4369:2667 +4369:2671 +4369:2683 +4369:2684 +4369:2685 +4369:2686 +4369:2687 +4370:2668 +4370:2669 +4371:2670 +4372:2664 +4372:2665 +4373:2666 +4375:2621 +4375:2622 +4375:2623 +4375:2624 +4375:2625 +4375:2626 +4375:2628 +4375:2629 +4375:2631 +4375:2632 +4375:2633 +4375:2634 +4375:2635 +4375:2636 +4375:2637 +4375:2638 +4375:2639 +4376:2640 +4378:2642 +4378:2643 +4378:2644 +4378:2645 +4378:2646 +4378:2647 +4378:2648 +4378:2650 +4378:2651 +4378:2653 +4378:2654 +4378:2655 +4378:2656 +4378:2657 +4378:2658 +4378:2659 +4378:2660 +4378:2661 +4379:2662 +4381:2672 +4381:2673 +4381:2674 +4381:2675 +4382:2676 +4383:2678 +4384:2679 +4385:2680 +4388:2689 +4388:2690 +4392:2538 +4392:2539 +4392:2540 +4392:2541 +4392:2542 +4392:2543 +4392:2544 +4392:2601 +4392:2602 +4392:2603 +4392:2604 +4392:2605 +4392:2606 +4392:2607 +4398:2546 +4398:2547 +4398:2548 +4398:2551 +4398:2554 +4398:2555 +4398:2556 +4398:2557 +4398:2559 +4398:2560 +4400:2552 +4400:2553 +4402:2549 +4402:2550 +4404:2562 +4404:2563 +4404:2564 +4404:2565 +4404:2566 +4404:2567 +4404:2592 +4404:2593 +4404:2594 +4404:2595 +4404:2596 +4405:2568 +4406:2570 +4407:2571 +4408:2572 +4409:2573 +4411:2578 +4411:2579 +4411:2580 +4411:2581 +4411:2582 +4412:2583 +4413:2585 +4414:2586 +4415:2587 +4418:2598 +4418:2599 +4422:2743 +4422:2744 +4422:2745 +4422:2746 +4422:2747 +4422:2748 +4422:2749 +4422:2844 +4422:2845 +4422:2846 +4422:2847 +4422:2848 +4422:2849 +4422:2850 +4428:2751 +4428:2752 +4428:2753 +4428:2773 +4428:2794 +4428:2797 +4428:2800 +4428:2801 +4428:2802 +4428:2803 +4428:2804 +4428:2805 +4428:2806 +4430:2795 +4430:2796 +4432:2754 +4432:2755 +4432:2756 +4432:2757 +4432:2758 +4432:2759 +4432:2761 +4432:2762 +4432:2764 +4432:2765 +4432:2766 +4432:2767 +4432:2768 +4432:2769 +4432:2770 +4432:2771 +4432:2772 +4434:2774 +4434:2775 +4434:2776 +4434:2777 +4434:2778 +4434:2779 +4434:2780 +4434:2782 +4434:2783 +4434:2785 +4434:2786 +4434:2787 +4434:2788 +4434:2789 +4434:2790 +4434:2791 +4434:2792 +4434:2793 +4436:2798 +4436:2799 +4438:2808 +4438:2835 +4438:2836 +4438:2837 +4438:2838 +4438:2839 +4439:2809 +4439:2810 +4439:2811 +4439:2812 +4439:2813 +4440:2814 +4441:2816 +4443:2821 +4443:2822 +4443:2823 +4443:2824 +4443:2825 +4444:2826 +4445:2828 +4446:2829 +4447:2830 +4450:2841 +4450:2842 +4453:2852 +4453:2853 +4453:2854 +4453:2855 +4453:2857 +4453:2858 +4453:2859 +4453:2860 +4453:2862 +4453:2863 +4453:2864 +4453:2865 +4453:2866 +4453:2867 +4455:2869 +4455:2870 +4455:2871 +4455:2872 +4455:2874 +4455:2875 +4455:2876 +4455:2877 +4455:2879 +4455:2880 +4455:2881 +4455:2882 +4455:2883 +4455:2884 +4458:2496 +4458:2497 +4458:2498 +4458:2499 +4458:2507 +4458:2515 +4458:2518 +4458:2523 +4458:2526 +4458:2527 +4458:2528 +4458:2529 +4458:2531 +4458:2532 +4458:2533 +4458:2534 +4458:2535 +4458:2536 +4463:2519 +4463:2521 +4464:2516 +4464:2517 +4465:2501 +4465:2502 +4465:2503 +4465:2504 +4465:2505 +4466:2524 +4466:2525 +4467:2508 +4467:2509 +4467:2510 +4467:2511 +4467:2512 +4467:2513 +4467:2514 +4471:2699 +4471:2700 +4471:2701 +4471:2702 +4471:2704 +4471:2715 +4471:2723 +4471:2726 +4471:2731 +4471:2732 +4471:2733 +4471:2734 +4471:2735 +4471:2736 +4471:2737 +4471:2738 +4471:2739 +4471:2740 +4471:2741 +4476:2727 +4476:2729 +4477:2724 +4477:2725 +4478:2705 +4478:2706 +4478:2707 +4479:2710 +4479:2711 +4479:2712 +4480:2716 +4480:2717 +4480:2718 +4480:2719 +4480:2720 +4480:2721 +4480:2722 +4492:2076 +4492:2077 +4492:2078 +4492:2079 +4492:2230 +4492:2238 +4492:2239 +4492:2240 +4492:2241 +4492:2243 +4492:2244 +4492:2245 +4492:2246 +4492:2247 +4492:2248 +4496:2081 +4496:2082 +4496:2083 +4496:2084 +4497:2086 +4497:2087 +4497:2088 +4497:2089 +4497:2090 +4497:2091 +4497:2224 +4497:2225 +4497:2226 +4497:2227 +4497:2228 +4498:2095 +4498:2096 +4498:2097 +4498:2098 +4498:2099 +4499:2103 +4499:2104 +4499:2105 +4499:2106 +4499:2107 +4500:2111 +4500:2112 +4500:2113 +4500:2114 +4500:2115 +4501:2119 +4501:2120 +4501:2121 +4501:2122 +4501:2123 +4502:2127 +4502:2128 +4502:2129 +4503:2132 +4503:2133 +4503:2134 +4504:2137 +4504:2138 +4504:2139 +4505:2142 +4505:2143 +4505:2144 +4506:2147 +4506:2148 +4506:2150 +4506:2151 +4506:2152 +4506:2153 +4506:2154 +4506:2155 +4506:2156 +4506:2157 +4506:2158 +4506:2160 +4506:2161 +4506:2162 +4506:2163 +4506:2164 +4507:2166 +4507:2167 +4507:2168 +4507:2169 +4508:2170 +4508:2171 +4509:2175 +4509:2176 +4509:2177 +4510:2192 +4510:2194 +4510:2195 +4510:2196 +4510:2197 +4514:2179 +4514:2180 +4515:2184 +4515:2186 +4515:2187 +4515:2188 +4515:2189 +4519:2182 +4519:2183 +4522:2200 +4522:2201 +4523:2205 +4523:2206 +4523:2207 +4524:2211 +4524:2213 +4524:2214 +4524:2215 +4524:2216 +4528:2209 +4528:2210 +4530:2219 +4530:2220 +4532:2231 +4532:2232 +4532:2233 +4532:2234 +4532:2235 +4532:2236 +4532:2237 +4536:2250 +4536:2251 +4536:2252 +4536:2253 +4536:2254 +4536:2255 +4536:2264 +4536:2265 +4536:2266 +4536:2267 +4536:2268 +4536:2269 +4540:2257 +4540:2258 +4541:2260 +4541:2261 +4541:2262 +4547:2886 +4547:2887 +4547:2888 +4547:2889 +4547:2918 +4547:2919 +4547:2920 +4547:2921 +4547:2922 +4547:2923 +4551:2892 +4551:2893 +4551:2894 +4551:2895 +4551:2896 +4551:2897 +4551:2900 +4551:2901 +4551:2902 +4551:2903 +4551:2906 +4551:2907 +4551:2908 +4551:2909 +4551:2912 +4551:2913 +4551:2914 +4551:2915 +4551:2916 +4558:2925 +4558:2926 +4558:2927 +4558:2928 +4558:2931 +4558:2932 +4558:2933 +4558:2934 +4558:2935 +4558:2936 +4562:2930 +4569:2938 +4569:2939 +4569:2940 +4569:2941 +4569:3048 +4569:3049 +4569:3050 +4569:3051 +4569:3052 +4569:3053 +4574:2944 +4574:2946 +4574:2947 +4574:2948 +4574:2949 +4574:2950 +4574:2951 +4574:2952 +4574:2953 +4574:2954 +4574:2957 +4574:2958 +4574:2959 +4574:2960 +4574:2961 +4574:2962 +4574:2963 +4574:2964 +4574:2965 +4574:2968 +4574:2969 +4574:2970 +4574:2973 +4574:2974 +4574:2975 +4574:2976 +4574:2977 +4574:2978 +4574:2979 +4574:2980 +4574:2981 +4574:2982 +4574:2983 +4574:2984 +4574:2985 +4574:2986 +4574:2987 +4574:2988 +4574:2989 +4574:2990 +4574:2991 +4574:2992 +4574:2993 +4574:2994 +4574:2995 +4574:2996 +4574:2997 +4574:2999 +4574:3000 +4574:3001 +4574:3002 +4574:3003 +4574:3004 +4575:3007 +4576:3009 +4577:3010 +4578:3011 +4579:3012 +4580:3013 +4581:3014 +4582:3015 +4583:3016 +4584:3017 +4585:3018 +4586:3019 +4587:3020 +4588:3021 +4589:3022 +4590:3023 +4591:3024 +4592:3025 +4593:3026 +4594:3027 +4595:3028 +4596:3029 +4597:3030 +4598:3031 +4599:3032 +4600:3033 +4601:3034 +4603:3036 +4605:3038 +4606:3039 +4607:3040 +4608:3041 +4609:3042 +4610:3043 +4611:3044 +4612:3045 +4617:3055 +4617:3056 +4617:3057 +4617:3058 +4617:3060 +4617:3116 +4617:3119 +4617:3120 +4617:3121 +4617:3122 +4617:3123 +4617:3124 +4617:3125 +4617:3126 +4617:3127 +4617:3128 +4617:3129 +4621:3061 +4621:3062 +4621:3063 +4621:3064 +4621:3065 +4621:3066 +4621:3067 +4621:3068 +4621:3069 +4621:3072 +4621:3073 +4621:3074 +4621:3075 +4621:3076 +4621:3077 +4621:3078 +4621:3079 +4621:3080 +4621:3083 +4621:3084 +4621:3085 +4621:3086 +4621:3087 +4621:3088 +4621:3089 +4621:3090 +4621:3093 +4621:3094 +4621:3095 +4621:3096 +4621:3097 +4621:3098 +4621:3099 +4621:3100 +4621:3101 +4621:3102 +4621:3105 +4621:3106 +4621:3107 +4621:3108 +4621:3111 +4621:3112 +4621:3113 +4621:3117 +4621:3118 +4626:3131 +4626:3132 +4626:3133 +4626:3134 +4626:3137 +4626:3138 +4626:3139 +4626:3140 +4626:3141 +4626:3142 +4630:3136 +4635:3144 +4635:3145 +4635:3146 +4635:3147 +4635:3158 +4635:3159 +4635:3160 +4635:3161 +4635:3162 +4635:3163 +4639:3149 +4639:3151 +4639:3152 +4639:3153 +4639:3155 +4643:3165 +4643:3166 +4643:3167 +4643:3168 +4643:3186 +4643:3187 +4643:3188 +4643:3189 +4643:3190 +4643:3191 +4647:3170 +4647:3171 +4647:3172 +4647:3173 +4647:3174 +4647:3175 +4647:3176 +4647:3177 +4647:3178 +4647:3179 +4647:3180 +4647:3181 +4647:3183 +4647:3184 +4647:3185 +4651:3193 +4651:3194 +4651:3195 +4651:3196 +4651:3203 +4651:3653 +4651:3654 +4651:3655 +4651:3656 +4651:3657 +4651:3658 +4658:3206 +4658:3207 +4658:3208 +4658:3209 +4658:3210 +4658:3211 +4658:3453 +4658:3454 +4658:3455 +4658:3456 +4658:3457 +4659:3214 +4659:3350 +4659:3393 +4659:3426 +4659:3428 +4659:3429 +4660:3215 +4660:3216 +4660:3218 +4660:3219 +4660:3220 +4660:3221 +4660:3224 +4660:3225 +4660:3226 +4660:3229 +4660:3230 +4660:3231 +4660:3232 +4660:3233 +4661:3235 +4661:3236 +4662:3238 +4663:3257 +4663:3259 +4664:3240 +4664:3241 +4664:3242 +4664:3243 +4664:3244 +4664:3245 +4664:3246 +4664:3247 +4664:3248 +4664:3249 +4664:3250 +4664:3251 +4664:3252 +4664:3254 +4664:3255 +4665:3256 +4669:3264 +4669:3265 +4669:3267 +4669:3268 +4669:3269 +4669:3270 +4669:3273 +4669:3274 +4669:3275 +4669:3278 +4669:3279 +4669:3280 +4669:3281 +4669:3282 +4669:3285 +4669:3286 +4669:3287 +4669:3288 +4669:3291 +4669:3292 +4669:3293 +4669:3296 +4669:3297 +4669:3298 +4669:3299 +4669:3300 +4669:3303 +4669:3304 +4669:3305 +4669:3306 +4669:3307 +4669:3308 +4669:3309 +4669:3310 +4669:3311 +4669:3312 +4669:3313 +4669:3314 +4669:3315 +4669:3316 +4669:3317 +4669:3318 +4669:3320 +4669:3321 +4669:3323 +4669:3324 +4669:3325 +4669:3326 +4669:3329 +4669:3330 +4669:3331 +4669:3334 +4669:3335 +4669:3336 +4669:3337 +4669:3338 +4669:3340 +4669:3342 +4670:3345 +4670:3346 +4673:3351 +4673:3352 +4673:3353 +4673:3354 +4673:3355 +4673:3356 +4673:3358 +4673:3384 +4673:3385 +4673:3386 +4673:3387 +4673:3388 +4673:3389 +4673:3390 +4673:3391 +4674:3360 +4674:3361 +4674:3362 +4674:3363 +4674:3364 +4674:3367 +4674:3368 +4674:3369 +4674:3372 +4674:3373 +4674:3374 +4674:3377 +4674:3378 +4674:3379 +4674:3380 +4674:3381 +4675:3392 +4678:3394 +4678:3396 +4678:3399 +4678:3400 +4678:3401 +4678:3402 +4678:3403 +4678:3404 +4678:3405 +4678:3406 +4678:3407 +4678:3408 +4678:3409 +4678:3410 +4678:3411 +4678:3413 +4678:3414 +4678:3416 +4678:3418 +4678:3420 +4679:3423 +4679:3424 +4681:3433 +4681:3434 +4681:3435 +4681:3436 +4681:3438 +4681:3441 +4681:3442 +4681:3443 +4681:3445 +4681:3448 +4681:3449 +4683:3460 +4683:3461 +4683:3462 +4683:3464 +4683:3465 +4683:3466 +4683:3467 +4683:3470 +4683:3471 +4683:3472 +4683:3475 +4683:3476 +4683:3477 +4683:3478 +4683:3479 +4683:3481 +4683:3482 +4683:3519 +4683:3648 +4683:3650 +4683:3651 +4684:3486 +4684:3487 +4684:3489 +4684:3490 +4684:3491 +4684:3492 +4684:3495 +4684:3496 +4684:3497 +4684:3500 +4684:3501 +4684:3502 +4684:3503 +4684:3504 +4684:3506 +4684:3507 +4685:3511 +4685:3512 +4685:3513 +4685:3514 +4685:3515 +4689:3520 +4689:3521 +4689:3522 +4689:3523 +4689:3524 +4689:3525 +4689:3527 +4689:3528 +4689:3529 +4689:3530 +4689:3531 +4689:3533 +4689:3535 +4689:3536 +4689:3537 +4689:3538 +4689:3541 +4689:3542 +4689:3543 +4689:3544 +4689:3545 +4689:3546 +4689:3547 +4689:3548 +4691:3197 +4691:3198 +4691:3199 +4691:3550 +4691:3551 +4691:3552 +4691:3553 +4691:3554 +4691:3556 +4691:3557 +4691:3558 +4691:3559 +4691:3561 +4691:3565 +4691:3566 +4691:3567 +4691:3568 +4691:3569 +4691:3570 +4691:3571 +4691:3575 +4691:3576 +4691:3577 +4691:3578 +4691:3579 +4691:3580 +4691:3584 +4691:3586 +4691:3587 +4691:3630 +4691:3631 +4691:3632 +4691:3633 +4691:3634 +4692:3200 +4692:3201 +4692:3591 +4692:3592 +4692:3593 +4692:3595 +4692:3596 +4692:3597 +4692:3598 +4692:3599 +4692:3600 +4692:3601 +4692:3605 +4692:3606 +4692:3607 +4692:3608 +4692:3609 +4692:3610 +4692:3614 +4692:3616 +4692:3617 +4693:3202 +4693:3621 +4693:3622 +4693:3623 +4693:3624 +4693:3625 +4693:3626 +4695:3636 +4696:3638 +4697:3639 +4698:3640 +4699:3641 +4700:3642 +4701:3643 +4702:3644 +4703:3645 +4710:3798 +4710:3799 +4710:3800 +4710:3801 +4710:3804 +4710:3805 +4710:3806 +4710:3807 +4710:3808 +4710:3809 +4714:3803 +4719:3660 +4719:3661 +4719:3662 +4719:3663 +4719:3725 +4719:3726 +4719:3727 +4719:3728 +4719:3729 +4719:3730 +4723:3666 +4723:3667 +4723:3668 +4723:3669 +4723:3672 +4723:3673 +4723:3674 +4723:3677 +4723:3678 +4723:3679 +4723:3680 +4723:3681 +4723:3684 +4723:3685 +4723:3686 +4723:3687 +4723:3690 +4723:3691 +4723:3692 +4723:3701 +4723:3702 +4723:3703 +4723:3704 +4723:3705 +4723:3707 +4723:3708 +4723:3709 +4723:3710 +4723:3711 +4723:3712 +4723:3713 +4723:3714 +4723:3715 +4723:3716 +4723:3717 +4723:3718 +4723:3720 +4723:3721 +4723:3723 +4728:3732 +4728:3733 +4728:3734 +4728:3735 +4728:3737 +4728:3758 +4728:3759 +4728:3760 +4728:3761 +4728:3762 +4728:3763 +4728:3764 +4728:3765 +4728:3766 +4728:3767 +4728:3768 +4732:3738 +4732:3739 +4732:3740 +4732:3743 +4732:3744 +4732:3745 +4732:3748 +4732:3749 +4732:3750 +4732:3753 +4732:3754 +4732:3755 +4736:3770 +4736:3771 +4736:3772 +4736:3773 +4736:3775 +4736:3786 +4736:3787 +4736:3788 +4736:3789 +4736:3790 +4736:3791 +4736:3792 +4736:3793 +4736:3794 +4736:3795 +4736:3796 +4740:3776 +4740:3777 +4740:3778 +4740:3781 +4740:3782 +4740:3783 +*E diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyRecognizer.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyRecognizer.java new file mode 100644 index 0000000000..2b6c76600f --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyRecognizer.java @@ -0,0 +1,16031 @@ +// $ANTLR 2.7.7 (20060906): "groovy.g" -> "GroovyRecognizer.java"$ + +package org.codehaus.groovy.antlr.parser; + +import java.io.*; +import java.util.*; + +import groovyjarjarantlr.CommonToken; +import groovyjarjarantlr.InputBuffer; +import groovyjarjarantlr.LexerSharedInputState; +import groovyjarjarantlr.TokenStreamRecognitionException; + +import org.codehaus.groovy.antlr.*; +import org.codehaus.groovy.ast.Comment; + +import groovyjarjarantlr.TokenBuffer; +import groovyjarjarantlr.TokenStreamException; +import groovyjarjarantlr.TokenStreamIOException; +import groovyjarjarantlr.ANTLRException; +import groovyjarjarantlr.LLkParser; +import groovyjarjarantlr.Token; +import groovyjarjarantlr.TokenStream; +import groovyjarjarantlr.RecognitionException; +import groovyjarjarantlr.NoViableAltException; +import groovyjarjarantlr.MismatchedTokenException; +import groovyjarjarantlr.SemanticException; +import groovyjarjarantlr.ParserSharedInputState; +import groovyjarjarantlr.collections.impl.BitSet; +import groovyjarjarantlr.collections.AST; +import java.util.Hashtable; +import groovyjarjarantlr.ASTFactory; +import groovyjarjarantlr.ASTPair; +import groovyjarjarantlr.collections.impl.ASTArray; + +/** JSR-241 Groovy Recognizer. + * + * Run 'java Main [-showtree] directory-full-of-groovy-files' + * + * [The -showtree option pops up a Swing frame that shows + * the AST constructed from the parser.] + * + * Contributing authors: + * John Mitchell johnm@non.net + * Terence Parr parrt@magelang.com + * John Lilley jlilley@empathy.com + * Scott Stanchfield thetick@magelang.com + * Markus Mohnen mohnen@informatik.rwth-aachen.de + * Peter Williams pete.williams@sun.com + * Allan Jacobs Allan.Jacobs@eng.sun.com + * Steve Messick messick@redhills.com + * James Strachan jstrachan@protique.com + * John Pybus john@pybus.org + * John Rose rose00@mac.com + * Jeremy Rayner groovy@ross-rayner.com + * Alex Popescu the.mindstorm@gmail.com + * Martin Kempf mkempf@hsr.ch + * Reto Kleeb rkleeb@hsr.ch + * + * Version 1.00 December 9, 1997 -- initial release + * Version 1.01 December 10, 1997 + * fixed bug in octal def (0..7 not 0..8) + * Version 1.10 August 1998 (parrt) + * added tree construction + * fixed definition of WS,comments for mac,pc,unix newlines + * added unary plus + * Version 1.11 (Nov 20, 1998) + * Added "shutup" option to turn off last ambig warning. + * Fixed inner class def to allow named class defs as statements + * synchronized requires compound not simple statement + * add [] after builtInType DOT class in primaryExpression + * "const" is reserved but not valid..removed from modifiers + * Version 1.12 (Feb 2, 1999) + * Changed LITERAL_xxx to xxx in tree grammar. + * Updated java.g to use tokens {...} now for 2.6.0 (new feature). + * + * Version 1.13 (Apr 23, 1999) + * Didn't have (stat)? for else clause in tree parser. + * Didn't gen ASTs for interface extends. Updated tree parser too. + * Updated to 2.6.0. + * Version 1.14 (Jun 20, 1999) + * Allowed final/abstract on local classes. + * Removed local interfaces from methods + * Put instanceof precedence where it belongs...in relationalExpr + * It also had expr not type as arg; fixed it. + * Missing ! on SEMI in classBlock + * fixed: (expr) + "string" was parsed incorrectly (+ as unary plus). + * fixed: didn't like Object[].class in parser or tree parser + * Version 1.15 (Jun 26, 1999) + * Screwed up rule with instanceof in it. :( Fixed. + * Tree parser didn't like (expr).something; fixed. + * Allowed multiple inheritance in tree grammar. oops. + * Version 1.16 (August 22, 1999) + * Extending an interface built a wacky tree: had extra EXTENDS. + * Tree grammar didn't allow multiple superinterfaces. + * Tree grammar didn't allow empty var initializer: {} + * Version 1.17 (October 12, 1999) + * ESC lexer rule allowed 399 max not 377 max. + * java.tree.g didn't handle the expression of synchronized + * statements. + * Version 1.18 (August 12, 2001) + * Terence updated to Java 2 Version 1.3 by + * observing/combining work of Allan Jacobs and Steve + * Messick. Handles 1.3 src. Summary: + * o primary didn't include boolean.class kind of thing + * o constructor calls parsed explicitly now: + * see explicitConstructorInvocation + * o add strictfp modifier + * o missing objBlock after new expression in tree grammar + * o merged local class definition alternatives, moved after declaration + * o fixed problem with ClassName.super.field + * o reordered some alternatives to make things more efficient + * o long and double constants were not differentiated from int/float + * o whitespace rule was inefficient: matched only one char + * o add an examples directory with some nasty 1.3 cases + * o made Main.java use buffered IO and a Reader for Unicode support + * o supports UNICODE? + * Using Unicode charVocabulary makes code file big, but only + * in the bitsets at the end. I need to make ANTLR generate + * unicode bitsets more efficiently. + * Version 1.19 (April 25, 2002) + * Terence added in nice fixes by John Pybus concerning floating + * constants and problems with super() calls. John did a nice + * reorg of the primary/postfix expression stuff to read better + * and makes f.g.super() parse properly (it was METHOD_CALL not + * a SUPER_CTOR_CALL). Also: + * + * o "finally" clause was a root...made it a child of "try" + * o Added stuff for asserts too for Java 1.4, but *commented out* + * as it is not backward compatible. + * + * Version 1.20 (October 27, 2002) + * + * Terence ended up reorging John Pybus' stuff to + * remove some nondeterminisms and some syntactic predicates. + * Note that the grammar is stricter now; e.g., this(...) must + * be the first statement. + * + * Trinary ?: operator wasn't working as array name: + * (isBig ? bigDigits : digits)[i]; + * + * Checked parser/tree parser on source for + * Resin-2.0.5, jive-2.1.1, jdk 1.3.1, Lucene, antlr 2.7.2a4, + * and the 110k-line jGuru server source. + * + * Version 1.21 (October 17, 2003) + * Fixed lots of problems including: + * Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g + * He found a problem/fix with floating point that start with 0 + * Ray also fixed problem that (int.class) was not recognized. + * Thorsten van Ellen noticed that \n are allowed incorrectly in strings. + * TJP fixed CHAR_LITERAL analogously. + * + * Version 1.21.2 (March, 2003) + * Changes by Matt Quail to support generics (as per JDK1.5/JSR14) + * Notes: + * o We only allow the "extends" keyword and not the "implements" + * keyword, since that's what JSR14 seems to imply. + * o Thanks to Monty Zukowski for his help on the antlr-interest + * mail list. + * o Thanks to Alan Eliasen for testing the grammar over his + * Fink source base + * + * Version 1.22 (July, 2004) + * Changes by Michael Studman to support Java 1.5 language extensions + * Notes: + * o Added support for annotations types + * o Finished off Matt Quail's generics enhancements to support bound type arguments + * o Added support for new for statement syntax + * o Added support for static import syntax + * o Added support for enum types + * o Tested against JDK 1.5 source base and source base of jdigraph project + * o Thanks to Matt Quail for doing the hard part by doing most of the generics work + * + * Version 1.22.1 (July 28, 2004) + * Bug/omission fixes for Java 1.5 language support + * o Fixed tree structure bug with classOrInterface - thanks to Pieter Vangorpto for + * spotting this + * o Fixed bug where incorrect handling of SR and BSR tokens would cause type + * parameters to be recognised as type arguments. + * o Enabled type parameters on constructors, annotations on enum constants + * and package definitions + * o Fixed problems when parsing if ((char.class.equals(c))) {} - solution by Matt Quail at Cenqua + * + * Version 1.22.2 (July 28, 2004) + * Slight refactoring of Java 1.5 language support + * o Refactored for/"foreach" productions so that original literal "for" literal + * is still used but the for sub-clauses vary by token type + * o Fixed bug where type parameter was not included in generic constructor's branch of AST + * + * Version 1.22.3 (August 26, 2004) + * Bug fixes as identified by Michael Stahl; clean up of tabs/spaces + * and other refactorings + * o Fixed typeParameters omission in identPrimary and newStatement + * o Replaced GT reconcilliation code with simple semantic predicate + * o Adapted enum/assert keyword checking support from Michael Stahl's java15 grammar + * o Refactored typeDefinition production and field productions to reduce duplication + * + * Version 1.22.4 (October 21, 2004) + * Small bux fixes + * o Added typeArguments to explicitConstructorInvocation, e.g. new MyParameterised() + * o Added typeArguments to postfixExpression productions for anonymous inner class super + * constructor invocation, e.g. new Outer().super() + * o Fixed bug in array declarations identified by Geoff Roy + * + * Version 1.22.4.g.1 + * o I have taken java.g for Java1.5 from Michael Studman (1.22.4) + * and have applied the groovy.diff from java.g (1.22) by John Rose + * back onto the new root (1.22.4) - Jeremy Rayner (Jan 2005) + * + * Version 1.22.4.g.2 + * o mkempf, rkleeb, Dec 2007 + * o fixed various rules so that they call the correct Create Method + * to make sure that the line information are correct + * + * Based on an original grammar released in the PUBLIC DOMAIN + */ +public class GroovyRecognizer extends groovyjarjarantlr.LLkParser implements GroovyTokenTypes + { + + /** This factory is the correct way to wire together a Groovy parser and lexer. */ + public static GroovyRecognizer make(GroovyLexer lexer) { + GroovyRecognizer parser = new GroovyRecognizer(lexer.plumb()); + // TODO: set up a common error-handling control block, to avoid excessive tangle between these guys + parser.lexer = lexer; + lexer.parser = parser; + parser.getASTFactory().setASTNodeClass(GroovySourceAST.class); + parser.warningList = new ArrayList(); + // GRECLIPSE add + parser.errorList = new ArrayList(); + // GRECLIPSE end + return parser; + } + // Create a scanner that reads from the input stream passed to us... + public static GroovyRecognizer make(InputStream in) { return make(new GroovyLexer(in)); } + public static GroovyRecognizer make(Reader in) { return make(new GroovyLexer(in)); } + public static GroovyRecognizer make(InputBuffer in) { return make(new GroovyLexer(in)); } + public static GroovyRecognizer make(LexerSharedInputState in) { return make(new GroovyLexer(in)); } + + @SuppressWarnings("unused") + private static GroovySourceAST dummyVariableToforceClassLoaderToFindASTClass = new GroovySourceAST(); + + List warningList; + public List getWarningList() { return warningList; } + + // GRECLIPSE add + List errorList; + public List getErrorList() { return errorList; } + + List comments = new ArrayList(); + public List getComments() { return comments; } + // GRECLIPSE end + + GroovyLexer lexer; + public GroovyLexer getLexer() { return lexer; } + public void setFilename(String f) { super.setFilename(f); lexer.setFilename(f); } + + @SuppressWarnings("unused") + private SourceBuffer sourceBuffer; + public void setSourceBuffer(SourceBuffer sourceBuffer) { + this.sourceBuffer = sourceBuffer; + } + + /** Create an AST node with the token type and text passed in, but + * with the same background information as another supplied Token (e.g. line numbers). + * To be used in place of antlr tree construction syntax, + * i.e. #[TOKEN,"text"] becomes create(TOKEN,"text",anotherToken) + * + * todo - change antlr.ASTFactory to do this instead... + */ + public AST create(int type, String txt, AST first) { + AST t = astFactory.create(type,txt); + if ( t != null && first != null) { + // first copy details from first token + t.initialize(first); + // then ensure that type and txt are specific to this new node + t.initialize(type,txt); + } + return t; + } + + private AST attachLast(AST t, Object last) { + if ((t instanceof GroovySourceAST) && (last instanceof SourceInfo)) { + SourceInfo lastInfo = (SourceInfo) last; + GroovySourceAST node = (GroovySourceAST)t; + node.setColumnLast(lastInfo.getColumn()); + node.setLineLast(lastInfo.getLine()); + // This is a good point to call node.setSnippet(), + // but it bulks up the AST too much for production code. + } + return t; + } + + public AST create(int type, String txt, Token first, Token last) { + return attachLast(create(type, txt, astFactory.create(first)), last); + } + + public AST create(int type, String txt, AST first, Token last) { + return attachLast(create(type, txt, first), last); + } + + public AST create(int type, String txt, AST first, AST last) { + return attachLast(create(type, txt, first), last); + } + + // GRECLIPSE add + public AST create2(int type, String txt, Token first, Token last) { + AST ast = create(type, txt, astFactory.create(first)); + if ((ast instanceof GroovySourceAST) && (last instanceof SourceInfo)) { + ((GroovySourceAST) ast).setLineLast(((SourceInfo) last).getLineLast()); + ((GroovySourceAST) ast).setColumnLast(((SourceInfo) last).getColumnLast()); + } + return ast; + } + + public AST missingIdentifier(Token prev, Token next) { + int line, column; + if (!(prev instanceof SourceInfo)) { + line = prev.getLine(); + column = prev.getColumn() + 1; + } else { + line = ((SourceInfo) prev).getLineLast(); + column = ((SourceInfo) prev).getColumnLast(); + } + GroovySourceToken ident = new GroovySourceToken(IDENT); + ident.setText("?"); + ident.setLine(line); + ident.setColumn(column); + ident.setLineLast(line); + ident.setColumnLast(column + 1); + return (AST)astFactory.make( (new ASTArray(1)).add(create(ident.getType(),ident.getText(),ident,next))); + } + + private Stack commentStartPositions = new Stack<>(); + + public void startComment(int line, int column) { + commentStartPositions.push((line << 16) + column); + } + + public void endComment(int type, int line, int column, String text) { + int lineAndColumn = commentStartPositions.pop(); + int startLine = lineAndColumn >>> 16; + int startColumn = lineAndColumn & 0xffff; + if (type == 0) { + Comment comment = Comment.makeSingleLineComment(startLine, startColumn, line, column, text); + comments.add(comment); + } else if (type == 1) { + Comment comment = Comment.makeMultiLineComment(startLine, startColumn, line, column, text); + comments.add(comment); + } + } + // GRECLIPSE end + + /** + * Clones the token + */ + public Token cloneToken(Token t) { + CommonToken clone = new CommonToken(t.getType(),t.getText()); + clone.setLine(t.getLine()); + clone.setColumn(t.getColumn()); + return clone; + } + + + // stuff to adjust ANTLR's tracing machinery + public static boolean tracing = false; // only effective if antlr.Tool is run with -traceParser + public void traceIn(String rname) throws TokenStreamException { + if (!GroovyRecognizer.tracing) return; + super.traceIn(rname); + } + public void traceOut(String rname) throws TokenStreamException { + if (!GroovyRecognizer.tracing) return; + if (returnAST != null) rname += returnAST.toStringList(); + super.traceOut(rname); + } + + // Error handling. This is a funnel through which parser errors go, when the parser can suggest a solution. + public void requireFailed(String problem, String solution) throws SemanticException { + // TODO: Needs more work. + Token lt = null; + int lineNum = Token.badToken.getLine(), colNum = Token.badToken.getColumn(); + try { + lt = LT(1); + if(lt != null) { + lineNum = lt.getLine(); + colNum = lt.getColumn(); + } + } + catch (TokenStreamException ee) { + if(ee instanceof TokenStreamRecognitionException) { + lineNum = ((TokenStreamRecognitionException) ee).recog.getLine(); + colNum = ((TokenStreamRecognitionException) ee).recog.getColumn(); + } + } + throw new SemanticException(problem + ";\n solution: " + solution, + getFilename(), lineNum, colNum); + } + + public void addWarning(String warning, String solution) { + Token lt = null; + try { lt = LT(1); } + catch (TokenStreamException ee) { } + if (lt == null) lt = Token.badToken; + + Map row = new HashMap(); + row.put("warning", warning); + row.put("solution", solution); + row.put("filename", getFilename()); + row.put("line", Integer.valueOf(lt.getLine())); + row.put("column", Integer.valueOf(lt.getColumn())); + // System.out.println(row); + warningList.add(row); + } + + // GRECLIPSE add + /** + * Report a recovered error. + */ + public void reportError(String message) { + Token lt = null; + try { lt = LT(1); } + catch (TokenStreamException e) { } + if (lt == null) lt = Token.badToken; + reportError(message, lt.getLine(), lt.getColumn()); + } + + /** + * Report a recovered error and specify the node. + */ + public void reportError(String message, AST ln) { + reportError(message, ln.getLine(), ln.getColumn()); + } + + /** + * Report a recovered error and specify the token. + */ + public void reportError(String message, Token lt) { + reportError(message, lt.getLine(), lt.getColumn()); + } + + /** + * Report a recovered error and specify the line and column. + */ + public void reportError(String message, int line, int column) { + Map row = new HashMap(); + row.put("error", message); + row.put("filename", getFilename()); + row.put("line", Integer.valueOf(line)); + row.put("column", Integer.valueOf(column)); + errorList.add(row); + } + + /** + * Report a recovered exception. + */ + public void reportError(RecognitionException e) { + Map row = new HashMap(); + row.put("error", e.getMessage()); + row.put("filename", e.getFilename()); + row.put("line", Integer.valueOf(e.getLine())); + row.put("column", Integer.valueOf(e.getColumn())); + errorList.add(row); + } + // GRECLIPSE end + + // Convenience method for checking of expected error syndromes. + private void require(boolean z, String problem, String solution) throws SemanticException { + if (!z) requireFailed(problem, solution); + } + + private boolean matchGenericTypeBrackets(boolean z, String problem, String solution) throws SemanticException { + if (!z) matchGenericTypeBracketsFailed(problem, solution); + return z; + } + + public void matchGenericTypeBracketsFailed(String problem, String solution) throws SemanticException { + Token lt = null; + int lineNum = Token.badToken.getLine(), colNum = Token.badToken.getColumn(); + + try { + lt = LT(1); + if(lt != null) { + lineNum = lt.getLine(); + colNum = lt.getColumn(); + } + } + catch (TokenStreamException ee) { + if(ee instanceof TokenStreamRecognitionException) { + lineNum = ((TokenStreamRecognitionException) ee).recog.getLine(); + colNum = ((TokenStreamRecognitionException) ee).recog.getColumn(); + } + } + + throw new SemanticException(problem + ";\n solution: " + solution, + getFilename(), lineNum, colNum); + } + + // Query a name token to see if it begins with a capital letter. + // This is used to tell the difference (w/o symbol table access) between {String x} and {println x}. + private boolean isUpperCase(Token x) { + if (x == null || x.getType() != IDENT) return false; // cannot happen? + String xtext = x.getText(); + return (xtext.length() > 0 && Character.isUpperCase(xtext.charAt(0))); + } + + private AST currentClass = null; // current enclosing class (for constructor recognition) + // Query a name token to see if it is identical with the current class name. + // This is used to distinguish constructors from other methods. + private boolean isConstructorIdent(Token x) { + if (currentClass == null) return false; + if (currentClass.getType() != IDENT) return false; // cannot happen? + String cname = currentClass.getText(); + + if (x == null || x.getType() != IDENT) return false; // cannot happen? + return cname.equals(x.getText()); + } + + @SuppressWarnings("unused") + private void dumpTree(AST ast, String offset) { + dump(ast, offset); + for (AST node = ast.getFirstChild(); node != null; node = node.getNextSibling()) { + dumpTree(node, offset+"\t"); + } + } + + private void dump(AST node, String offset) { + System.out.println(offset+"Type: " + getTokenName(node) + " text: " + node.getText()); + } + + private String getTokenName(AST node) { + if (node == null) return "null"; + return getTokenName(node.getType()); + } + + // Scratch variable for last 'sep' token. + // Written by the 'sep' rule, read only by immediate callers of 'sep'. + // (Not entirely clean, but better than a million xx=sep occurrences.) + private int sepToken = EOF; + + // Scratch variable for last argument list; tells whether there was a label. + // Written by 'argList' rule, read only by immediate callers of 'argList'. + private boolean argListHasLabels = false; + + // Scratch variable, holds most recently completed pathExpression. + // Read only by immediate callers of 'pathExpression' and 'expression'. + private AST lastPathExpression = null; + + // Inherited attribute pushed into most expression rules. + // If not zero, it means that the left context of the expression + // being parsed is a statement boundary or an initializer sign '='. + // Only such expressions are allowed to reach across newlines + // to pull in an LCURLY and appended block. + private final int LC_STMT = 1, LC_INIT = 2; + + /** + * Counts the number of LT seen in the typeArguments production. + * It is used in semantic predicates to ensure we have seen + * enough closing '>' characters; which actually may have been + * either GT, SR or BSR tokens. + */ + private int ltCounter = 0; + + /* This symbol is used to work around a known ANTLR limitation. + * In a loop with syntactic predicate, ANTLR needs help knowing + * that the loop exit is a second alternative. + * Example usage: ( (LCURLY)=> block | {ANTLR_LOOP_EXIT}? )* + * Probably should be an ANTLR RFE. + */ + ////// Original comment in Java grammar: + // Unfortunately a syntactic predicate can only select one of + // multiple alternatives on the same level, not break out of + // an enclosing loop, which is why this ugly hack (a fake + // empty alternative with always-false semantic predicate) + // is necessary. + @SuppressWarnings("unused") + private static final boolean ANTLR_LOOP_EXIT = false; + +protected GroovyRecognizer(TokenBuffer tokenBuf, int k) { + super(tokenBuf,k); + tokenNames = _tokenNames; + buildTokenTypeASTClassMap(); + astFactory = new ASTFactory(getTokenTypeToASTClassMap()); +} + +public GroovyRecognizer(TokenBuffer tokenBuf) { + this(tokenBuf,2); +} + +protected GroovyRecognizer(TokenStream lexer, int k) { + super(lexer,k); + tokenNames = _tokenNames; + buildTokenTypeASTClassMap(); + astFactory = new ASTFactory(getTokenTypeToASTClassMap()); +} + +public GroovyRecognizer(TokenStream lexer) { + this(lexer,2); +} + +public GroovyRecognizer(ParserSharedInputState state) { + super(state,2); + tokenNames = _tokenNames; + buildTokenTypeASTClassMap(); + astFactory = new ASTFactory(getTokenTypeToASTClassMap()); +} + + public final void compilationUnit() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST compilationUnit_AST = null; + + try { // for error handling + { + switch ( LA(1)) { + case SH_COMMENT: + { + match(SH_COMMENT); + break; + } + case EOF: + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case SEMI: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + { + boolean synPredMatched5 = false; + if (((LA(1)==LITERAL_package||LA(1)==AT) && (_tokenSet_0.member(LA(2))))) { + int _m5 = mark(); + synPredMatched5 = true; + inputState.guessing++; + try { + { + annotationsOpt(); + match(LITERAL_package); + } + } + catch (RecognitionException pe) { + synPredMatched5 = false; + } + rewind(_m5); +inputState.guessing--; + } + if ( synPredMatched5 ) { + packageDefinition(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_1.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + statement(EOF); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + _loop9: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + statement(sepToken); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop9; + } + + } while (true); + } + match(Token.EOF_TYPE); + compilationUnit_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // report the error but don't throw away what we've successfully parsed + reportError(e); + compilationUnit_AST = (AST) currentAST.root; + + } else { + throw e; + } + } + returnAST = compilationUnit_AST; + } + +/** Zero or more insignificant newlines, all gobbled up and thrown away. */ + public final void nls() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST nls_AST = null; + + { + if ((LA(1)==NLS) && (_tokenSet_3.member(LA(2)))) { + match(NLS); + } + else if ((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + returnAST = nls_AST; + } + + public final void annotationsOpt() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationsOpt_AST = null; + Token first = LT(1); + + { + if ((_tokenSet_5.member(LA(1))) && (_tokenSet_6.member(LA(2)))) { + annotationsInternal(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_7.member(LA(1))) && (_tokenSet_8.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + annotationsOpt_AST = (AST)currentAST.root; + annotationsOpt_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(ANNOTATIONS,"ANNOTATIONS",first,LT(1))).add(annotationsOpt_AST)); + currentAST.root = annotationsOpt_AST; + currentAST.child = annotationsOpt_AST!=null &&annotationsOpt_AST.getFirstChild()!=null ? + annotationsOpt_AST.getFirstChild() : annotationsOpt_AST; + currentAST.advanceChildToEnd(); + } + annotationsOpt_AST = (AST)currentAST.root; + returnAST = annotationsOpt_AST; + } + + public final void packageDefinition() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST packageDefinition_AST = null; + AST an_AST = null; + AST id_AST = null; + Token first = LT(1); + + annotationsOpt(); + an_AST = (AST)returnAST; + match(LITERAL_package); + { + switch ( LA(1)) { + case IDENT: + { + identifier(); + id_AST = (AST)returnAST; + break; + } + case EOF: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + packageDefinition_AST = (AST)currentAST.root; + + if (id_AST == null) { + id_AST = missingIdentifier(LT(0), null); + reportError("Invalid package specification", LT(0).getLine(), LT(0).getColumn()-1); + } + packageDefinition_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(PACKAGE_DEF,"package",first,LT(1))).add(an_AST).add(id_AST)); + + currentAST.root = packageDefinition_AST; + currentAST.child = packageDefinition_AST!=null &&packageDefinition_AST.getFirstChild()!=null ? + packageDefinition_AST.getFirstChild() : packageDefinition_AST; + currentAST.advanceChildToEnd(); + } + packageDefinition_AST = (AST)currentAST.root; + returnAST = packageDefinition_AST; + } + +/** A statement is an element of a block. + * Typical statements are declarations (which are scoped to the block) + * and expressions. + */ + public final void statement( + int prevToken + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST statement_AST = null; + AST pfx_AST = null; + AST es_AST = null; + AST ale_AST = null; + AST ifCbs_AST = null; + AST elseCbs_AST = null; + AST while_sce_AST = null; + Token s = null; + AST s_AST = null; + AST while_cbs_AST = null; + AST m_AST = null; + AST switchSce_AST = null; + AST cg_AST = null; + AST synch_sce_AST = null; + AST synch_cs_AST = null; + boolean sce = false; Token first = LT(1); AST casesGroup_AST = null; int start = mark(); + + try { // for error handling + switch ( LA(1)) { + case LITERAL_if: + { + match(LITERAL_if); + match(LPAREN); + assignmentLessExpression(); + ale_AST = (AST)returnAST; + match(RPAREN); + nlsWarn(); + compatibleBodyStatement(); + ifCbs_AST = (AST)returnAST; + { + boolean synPredMatched317 = false; + if (((_tokenSet_9.member(LA(1))) && (_tokenSet_10.member(LA(2))))) { + int _m317 = mark(); + synPredMatched317 = true; + inputState.guessing++; + try { + { + { + switch ( LA(1)) { + case SEMI: + case NLS: + { + sep(); + break; + } + case LITERAL_else: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(LITERAL_else); + } + } + catch (RecognitionException pe) { + synPredMatched317 = false; + } + rewind(_m317); +inputState.guessing--; + } + if ( synPredMatched317 ) { + { + switch ( LA(1)) { + case SEMI: + case NLS: + { + sep(); + break; + } + case LITERAL_else: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(LITERAL_else); + nlsWarn(); + compatibleBodyStatement(); + elseCbs_AST = (AST)returnAST; + } + else if ((_tokenSet_11.member(LA(1))) && (_tokenSet_12.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + statement_AST = (AST)currentAST.root; + statement_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(LITERAL_if,"if",first,LT(1))).add(ale_AST).add(ifCbs_AST).add(elseCbs_AST)); + currentAST.root = statement_AST; + currentAST.child = statement_AST!=null &&statement_AST.getFirstChild()!=null ? + statement_AST.getFirstChild() : statement_AST; + currentAST.advanceChildToEnd(); + } + statement_AST = (AST)currentAST.root; + break; + } + case LITERAL_for: + { + forStatement(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + break; + } + case LITERAL_while: + { + match(LITERAL_while); + match(LPAREN); + sce=strictContextExpression(false); + while_sce_AST = (AST)returnAST; + match(RPAREN); + nlsWarn(); + { + switch ( LA(1)) { + case SEMI: + { + s = LT(1); + s_AST = astFactory.create(s); + match(SEMI); + break; + } + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + compatibleBodyStatement(); + while_cbs_AST = (AST)returnAST; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + statement_AST = (AST)currentAST.root; + + if (s_AST != null) + statement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_while,"Literal_while",first,LT(1))).add(while_sce_AST).add(s_AST)); + else + statement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_while,"Literal_while",first,LT(1))).add(while_sce_AST).add(while_cbs_AST)); + + currentAST.root = statement_AST; + currentAST.child = statement_AST!=null &&statement_AST.getFirstChild()!=null ? + statement_AST.getFirstChild() : statement_AST; + currentAST.advanceChildToEnd(); + } + statement_AST = (AST)currentAST.root; + break; + } + case UNUSED_DO: + { + AST tmp12_AST = null; + tmp12_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp12_AST); + match(UNUSED_DO); + compoundStatement(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + match(LITERAL_while); + match(LPAREN); + strictContextExpression(false); + match(RPAREN); + if ( inputState.guessing==0 ) { + + reportError(new NoViableAltException(first, getFilename())); + + } + statement_AST = (AST)currentAST.root; + break; + } + case LITERAL_switch: + { + match(LITERAL_switch); + match(LPAREN); + sce=strictContextExpression(false); + switchSce_AST = (AST)returnAST; + match(RPAREN); + nlsWarn(); + match(LCURLY); + nls(); + { + _loop323: + do { + if ((LA(1)==LITERAL_default||LA(1)==LITERAL_case)) { + casesGroup(); + cg_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + casesGroup_AST = (AST)astFactory.make( (new ASTArray(3)).add(null).add(casesGroup_AST).add(cg_AST)); + } + } + else { + break _loop323; + } + + } while (true); + } + match(RCURLY); + if ( inputState.guessing==0 ) { + statement_AST = (AST)currentAST.root; + statement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_switch,"switch",first,LT(1))).add(switchSce_AST).add(casesGroup_AST)); + currentAST.root = statement_AST; + currentAST.child = statement_AST!=null &&statement_AST.getFirstChild()!=null ? + statement_AST.getFirstChild() : statement_AST; + currentAST.advanceChildToEnd(); + } + statement_AST = (AST)currentAST.root; + break; + } + case LITERAL_try: + { + tryBlock(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + break; + } + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + { + branchStatement(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + break; + } + default: + boolean synPredMatched304 = false; + if (((_tokenSet_13.member(LA(1))) && (_tokenSet_14.member(LA(2))))) { + int _m304 = mark(); + synPredMatched304 = true; + inputState.guessing++; + try { + { + genericMethodStart(); + } + } + catch (RecognitionException pe) { + synPredMatched304 = false; + } + rewind(_m304); +inputState.guessing--; + } + if ( synPredMatched304 ) { + genericMethod(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + } + else { + boolean synPredMatched306 = false; + if (((_tokenSet_13.member(LA(1))) && (_tokenSet_15.member(LA(2))))) { + int _m306 = mark(); + synPredMatched306 = true; + inputState.guessing++; + try { + { + multipleAssignmentDeclarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched306 = false; + } + rewind(_m306); +inputState.guessing--; + } + if ( synPredMatched306 ) { + multipleAssignmentDeclaration(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + } + else { + boolean synPredMatched308 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_17.member(LA(2))))) { + int _m308 = mark(); + synPredMatched308 = true; + inputState.guessing++; + try { + { + declarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched308 = false; + } + rewind(_m308); +inputState.guessing--; + } + if ( synPredMatched308 ) { + declaration(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + } + else { + boolean synPredMatched310 = false; + if (((LA(1)==IDENT) && (LA(2)==COLON))) { + int _m310 = mark(); + synPredMatched310 = true; + inputState.guessing++; + try { + { + match(IDENT); + match(COLON); + } + } + catch (RecognitionException pe) { + synPredMatched310 = false; + } + rewind(_m310); +inputState.guessing--; + } + if ( synPredMatched310 ) { + statementLabelPrefix(); + pfx_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + statement_AST = (AST)currentAST.root; + statement_AST = pfx_AST; + currentAST.root = statement_AST; + currentAST.child = statement_AST!=null &&statement_AST.getFirstChild()!=null ? + statement_AST.getFirstChild() : statement_AST; + currentAST.advanceChildToEnd(); + } + { + boolean synPredMatched313 = false; + if (((LA(1)==LCURLY) && (_tokenSet_18.member(LA(2))))) { + int _m313 = mark(); + synPredMatched313 = true; + inputState.guessing++; + try { + { + match(LCURLY); + } + } + catch (RecognitionException pe) { + synPredMatched313 = false; + } + rewind(_m313); +inputState.guessing--; + } + if ( synPredMatched313 ) { + openOrClosableBlock(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_19.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + statement(COLON); + astFactory.addASTChild(currentAST, returnAST); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + statement_AST = (AST)currentAST.root; + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + expressionStatement(prevToken); + es_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + } + else { + boolean synPredMatched321 = false; + if (((LA(1)==LITERAL_import||LA(1)==AT) && (_tokenSet_21.member(LA(2))))) { + int _m321 = mark(); + synPredMatched321 = true; + inputState.guessing++; + try { + { + annotationsOpt(); + match(LITERAL_import); + } + } + catch (RecognitionException pe) { + synPredMatched321 = false; + } + rewind(_m321); +inputState.guessing--; + } + if ( synPredMatched321 ) { + importStatement(); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + } + else if ((_tokenSet_22.member(LA(1))) && (_tokenSet_23.member(LA(2)))) { + modifiersOpt(); + m_AST = (AST)returnAST; + typeDefinitionInternal(m_AST); + astFactory.addASTChild(currentAST, returnAST); + statement_AST = (AST)currentAST.root; + } + else if ((LA(1)==LITERAL_synchronized) && (LA(2)==LPAREN)) { + match(LITERAL_synchronized); + match(LPAREN); + sce=strictContextExpression(false); + synch_sce_AST = (AST)returnAST; + match(RPAREN); + nlsWarn(); + compoundStatement(); + synch_cs_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + statement_AST = (AST)currentAST.root; + statement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_synchronized,"synchronized",first,LT(1))).add(synch_sce_AST).add(synch_cs_AST)); + currentAST.root = statement_AST; + currentAST.child = statement_AST!=null &&statement_AST.getFirstChild()!=null ? + statement_AST.getFirstChild() : statement_AST; + currentAST.advanceChildToEnd(); + } + statement_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + }}}}} + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // GRECLIPSE-1048 + // If the pfx_AST is not null (i.e. a label was encountered) then attempt recovery. Basically if the + // NoViableAltException hit a problem and the token it encountered was on the same line as the prefix, + // skip to the end of the line, otherwise assume we can continue from where we are. + if (pfx_AST != null) { + reportError(e); + if (e instanceof NoViableAltException) { + NoViableAltException nvae = (NoViableAltException) e; + if (pfx_AST.getLine() == nvae.token.getLine()) { + consumeUntil(NLS); + } + } + } + // GRECLIPSE-1046 + // Two situations to support: 'if (f.) ' where the 'else' condition is missing. This is now handled + // by a recovery rule in the else clause parsing. And 'if (f.', where even the trailing parenthesis + // is missing, which is dealt with here by noticing the condition exists but ifCbs_AST is null. + // Create a basic if statement and soldier on. + else if (ale_AST != null && ifCbs_AST == null) { + // likely missing close paren + statement_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(LITERAL_if,"if",first,LT(1))).add(ale_AST).add(ifCbs_AST).add(elseCbs_AST)); + } + else { + throw e; + } + + } else { + throw e; + } + } + returnAST = statement_AST; + } + +/** A statement separator is either a semicolon or a significant newline. + * Any number of additional (insignificant) newlines may accompany it. + */ + public final void sep() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST sep_AST = null; + + switch ( LA(1)) { + case SEMI: + { + match(SEMI); + { + _loop600: + do { + if ((LA(1)==NLS) && (_tokenSet_24.member(LA(2)))) { + match(NLS); + } + else { + break _loop600; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + sepToken = SEMI; + } + break; + } + case NLS: + { + match(NLS); + if ( inputState.guessing==0 ) { + sepToken = NLS; + } + { + _loop604: + do { + if ((LA(1)==SEMI) && (_tokenSet_24.member(LA(2)))) { + match(SEMI); + { + _loop603: + do { + if ((LA(1)==NLS) && (_tokenSet_24.member(LA(2)))) { + match(NLS); + } + else { + break _loop603; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + sepToken = SEMI; + } + } + else { + break _loop604; + } + + } while (true); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = sep_AST; + } + +/** A Groovy script or simple expression. Can be anything legal inside {...}. */ + public final void snippetUnit() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST snippetUnit_AST = null; + + nls(); + blockBody(EOF); + astFactory.addASTChild(currentAST, returnAST); + snippetUnit_AST = (AST)currentAST.root; + returnAST = snippetUnit_AST; + } + +/** A block body is a parade of zero or more statements or expressions. */ + public final void blockBody( + int prevToken + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST blockBody_AST = null; + + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + statement(prevToken); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + _loop298: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + statement(sepToken); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop298; + } + + } while (true); + } + blockBody_AST = (AST)currentAST.root; + returnAST = blockBody_AST; + } + + public final void identifier() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST identifier_AST = null; + Token i1 = null; + AST i1_AST = null; + Token d = null; + AST d_AST = null; + Token i2 = null; + AST i2_AST = null; + Token first = LT(1); + + i1 = LT(1); + i1_AST = astFactory.create(i1); + match(IDENT); + { + _loop74: + do { + if ((LA(1)==DOT)) { + d = LT(1); + d_AST = astFactory.create(d); + match(DOT); + nls(); + i2 = LT(1); + i2_AST = astFactory.create(i2); + match(IDENT); + if ( inputState.guessing==0 ) { + i1_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(DOT,".",first,LT(1))).add(i1_AST).add(i2_AST)); + } + } + else { + break _loop74; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + identifier_AST = (AST)currentAST.root; + identifier_AST = i1_AST; + currentAST.root = identifier_AST; + currentAST.child = identifier_AST!=null &&identifier_AST.getFirstChild()!=null ? + identifier_AST.getFirstChild() : identifier_AST; + currentAST.advanceChildToEnd(); + } + identifier_AST = (AST)currentAST.root; + returnAST = identifier_AST; + } + + public final void importStatement() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST importStatement_AST = null; + AST an_AST = null; + AST is_AST = null; + Token first = LT(1); boolean isStatic = false; + + annotationsOpt(); + an_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + match(LITERAL_import); + { + switch ( LA(1)) { + case LITERAL_static: + { + match(LITERAL_static); + if ( inputState.guessing==0 ) { + isStatic=true; + } + break; + } + case EOF: + case IDENT: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case IDENT: + { + identifierStar(); + is_AST = (AST)returnAST; + break; + } + case EOF: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + importStatement_AST = (AST)currentAST.root; + + if (is_AST == null) { + is_AST = missingIdentifier(LT(0), null); + } + if (!isStatic) { + importStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(IMPORT,"import",first,LT(1))).add(an_AST).add(is_AST)); + } else { + importStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(STATIC_IMPORT,"static_import",first,LT(1))).add(an_AST).add(is_AST)); + } + + currentAST.root = importStatement_AST; + currentAST.child = importStatement_AST!=null &&importStatement_AST.getFirstChild()!=null ? + importStatement_AST.getFirstChild() : importStatement_AST; + currentAST.advanceChildToEnd(); + } + importStatement_AST = (AST)currentAST.root; + returnAST = importStatement_AST; + } + + public final void identifierStar() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST identifierStar_AST = null; + Token i1 = null; + AST i1_AST = null; + Token d1 = null; + AST d1_AST = null; + Token i2 = null; + AST i2_AST = null; + Token d2 = null; + AST d2_AST = null; + Token s = null; + AST s_AST = null; + Token alias = null; + AST alias_AST = null; + Token first = LT(1); int start = mark(); + + try { // for error handling + i1 = LT(1); + i1_AST = astFactory.create(i1); + match(IDENT); + { + _loop77: + do { + if ((LA(1)==DOT) && (LA(2)==IDENT||LA(2)==NLS)) { + d1 = LT(1); + d1_AST = astFactory.create(d1); + match(DOT); + nls(); + i2 = LT(1); + i2_AST = astFactory.create(i2); + match(IDENT); + if ( inputState.guessing==0 ) { + i1_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(DOT,".",first,LT(1))).add(i1_AST).add(i2_AST)); + } + } + else { + break _loop77; + } + + } while (true); + } + { + switch ( LA(1)) { + case DOT: + { + d2 = LT(1); + d2_AST = astFactory.create(d2); + match(DOT); + nls(); + s = LT(1); + s_AST = astFactory.create(s); + match(STAR); + if ( inputState.guessing==0 ) { + i1_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(DOT,".",first,LT(1))).add(i1_AST).add(s_AST)); + } + break; + } + case LITERAL_as: + { + match(LITERAL_as); + nls(); + alias = LT(1); + alias_AST = astFactory.create(alias); + match(IDENT); + if ( inputState.guessing==0 ) { + i1_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_as,"as",first,LT(1))).add(i1_AST).add(alias_AST)); + } + break; + } + case EOF: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + identifierStar_AST = (AST)currentAST.root; + identifierStar_AST = i1_AST; + currentAST.root = identifierStar_AST; + currentAST.child = identifierStar_AST!=null &&identifierStar_AST.getFirstChild()!=null ? + identifierStar_AST.getFirstChild() : identifierStar_AST; + currentAST.advanceChildToEnd(); + } + identifierStar_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + reportError("Invalid import", first); + identifierStar_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(DOT,".",first,LT(1))).add(i1_AST).add((AST)astFactory.make( (new ASTArray(1)).add(create(STAR,"*",null))))); + // Give up on this line and just go to the next + rewind(start); + consumeUntil(NLS); + + } else { + throw e; + } + } + returnAST = identifierStar_AST; + } + + protected final void typeDefinitionInternal( + AST mods + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeDefinitionInternal_AST = null; + AST cd_AST = null; + AST td_AST = null; + AST id_AST = null; + AST ed_AST = null; + AST ad_AST = null; + + switch ( LA(1)) { + case LITERAL_class: + { + classDefinition(mods); + cd_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + typeDefinitionInternal_AST = (AST)currentAST.root; + typeDefinitionInternal_AST = cd_AST; + currentAST.root = typeDefinitionInternal_AST; + currentAST.child = typeDefinitionInternal_AST!=null &&typeDefinitionInternal_AST.getFirstChild()!=null ? + typeDefinitionInternal_AST.getFirstChild() : typeDefinitionInternal_AST; + currentAST.advanceChildToEnd(); + } + typeDefinitionInternal_AST = (AST)currentAST.root; + break; + } + case LITERAL_trait: + { + traitDefinition(mods); + td_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + typeDefinitionInternal_AST = (AST)currentAST.root; + typeDefinitionInternal_AST = td_AST; + currentAST.root = typeDefinitionInternal_AST; + currentAST.child = typeDefinitionInternal_AST!=null &&typeDefinitionInternal_AST.getFirstChild()!=null ? + typeDefinitionInternal_AST.getFirstChild() : typeDefinitionInternal_AST; + currentAST.advanceChildToEnd(); + } + typeDefinitionInternal_AST = (AST)currentAST.root; + break; + } + case LITERAL_interface: + { + interfaceDefinition(mods); + id_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + typeDefinitionInternal_AST = (AST)currentAST.root; + typeDefinitionInternal_AST = id_AST; + currentAST.root = typeDefinitionInternal_AST; + currentAST.child = typeDefinitionInternal_AST!=null &&typeDefinitionInternal_AST.getFirstChild()!=null ? + typeDefinitionInternal_AST.getFirstChild() : typeDefinitionInternal_AST; + currentAST.advanceChildToEnd(); + } + typeDefinitionInternal_AST = (AST)currentAST.root; + break; + } + case LITERAL_enum: + { + enumDefinition(mods); + ed_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + typeDefinitionInternal_AST = (AST)currentAST.root; + typeDefinitionInternal_AST = ed_AST; + currentAST.root = typeDefinitionInternal_AST; + currentAST.child = typeDefinitionInternal_AST!=null &&typeDefinitionInternal_AST.getFirstChild()!=null ? + typeDefinitionInternal_AST.getFirstChild() : typeDefinitionInternal_AST; + currentAST.advanceChildToEnd(); + } + typeDefinitionInternal_AST = (AST)currentAST.root; + break; + } + case AT: + { + annotationDefinition(mods); + ad_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + typeDefinitionInternal_AST = (AST)currentAST.root; + typeDefinitionInternal_AST = ad_AST; + currentAST.root = typeDefinitionInternal_AST; + currentAST.child = typeDefinitionInternal_AST!=null &&typeDefinitionInternal_AST.getFirstChild()!=null ? + typeDefinitionInternal_AST.getFirstChild() : typeDefinitionInternal_AST; + currentAST.advanceChildToEnd(); + } + typeDefinitionInternal_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = typeDefinitionInternal_AST; + } + + public final void classDefinition( + AST modifiers + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST classDefinition_AST = null; + AST tp_AST = null; + AST sc_AST = null; + AST ic_AST = null; + AST cb_AST = null; + Token first = cloneToken(LT(1));AST prevCurrentClass = currentClass; + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + } + + match(LITERAL_class); + AST tmp33_AST = null; + tmp33_AST = astFactory.create(LT(1)); + match(IDENT); + nls(); + if ( inputState.guessing==0 ) { + currentClass = tmp33_AST; + } + { + switch ( LA(1)) { + case LT: + { + typeParameters(); + tp_AST = (AST)returnAST; + nls(); + break; + } + case EOF: + case LITERAL_extends: + case LCURLY: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_implements: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + superClassClause(); + sc_AST = (AST)returnAST; + implementsClause(); + ic_AST = (AST)returnAST; + { + switch ( LA(1)) { + case LCURLY: + { + classBlock(); + cb_AST = (AST)returnAST; + break; + } + case EOF: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + classDefinition_AST = (AST)currentAST.root; + + if (cb_AST != null) { + classDefinition_AST = (AST)astFactory.make( (new ASTArray(7)).add(create(CLASS_DEF,"CLASS_DEF",first,LT(1))).add(modifiers).add(tmp33_AST).add(tp_AST).add(sc_AST).add(ic_AST).add(cb_AST)); + } else { + reportError("Malformed class declaration", LT(1)); + classDefinition_AST = (AST)astFactory.make( (new ASTArray(7)).add(create(CLASS_DEF,"CLASS_DEF",first,LT(1))).add(modifiers).add(tmp33_AST).add(tp_AST).add(sc_AST).add(ic_AST).add(null)); + } + + currentAST.root = classDefinition_AST; + currentAST.child = classDefinition_AST!=null &&classDefinition_AST.getFirstChild()!=null ? + classDefinition_AST.getFirstChild() : classDefinition_AST; + currentAST.advanceChildToEnd(); + } + if ( inputState.guessing==0 ) { + currentClass = prevCurrentClass; + } + returnAST = classDefinition_AST; + } + + public final void traitDefinition( + AST modifiers + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST traitDefinition_AST = null; + AST tp_AST = null; + AST sc_AST = null; + AST ic_AST = null; + AST cb_AST = null; + Token first = cloneToken(LT(1));AST prevCurrentClass = currentClass; + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + } + + match(LITERAL_trait); + AST tmp35_AST = null; + tmp35_AST = astFactory.create(LT(1)); + match(IDENT); + nls(); + if ( inputState.guessing==0 ) { + currentClass = tmp35_AST; + } + { + switch ( LA(1)) { + case LT: + { + typeParameters(); + tp_AST = (AST)returnAST; + nls(); + break; + } + case LITERAL_extends: + case LCURLY: + case LITERAL_implements: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + superClassClause(); + sc_AST = (AST)returnAST; + implementsClause(); + ic_AST = (AST)returnAST; + classBlock(); + cb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + traitDefinition_AST = (AST)currentAST.root; + traitDefinition_AST = (AST)astFactory.make( (new ASTArray(7)).add(create(TRAIT_DEF,"TRAIT_DEF",first,LT(1))).add(modifiers).add(tmp35_AST).add(tp_AST).add(sc_AST).add(ic_AST).add(cb_AST)); + currentAST.root = traitDefinition_AST; + currentAST.child = traitDefinition_AST!=null &&traitDefinition_AST.getFirstChild()!=null ? + traitDefinition_AST.getFirstChild() : traitDefinition_AST; + currentAST.advanceChildToEnd(); + } + if ( inputState.guessing==0 ) { + currentClass = prevCurrentClass; + } + returnAST = traitDefinition_AST; + } + + public final void interfaceDefinition( + AST modifiers + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST interfaceDefinition_AST = null; + AST tp_AST = null; + AST ie_AST = null; + AST ib_AST = null; + Token first = cloneToken(LT(1)); + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + } + + match(LITERAL_interface); + AST tmp37_AST = null; + tmp37_AST = astFactory.create(LT(1)); + match(IDENT); + nls(); + { + switch ( LA(1)) { + case LT: + { + typeParameters(); + tp_AST = (AST)returnAST; + nls(); + break; + } + case LITERAL_extends: + case LCURLY: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + interfaceExtends(); + ie_AST = (AST)returnAST; + interfaceBlock(); + ib_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + interfaceDefinition_AST = (AST)currentAST.root; + interfaceDefinition_AST = (AST)astFactory.make( (new ASTArray(6)).add(create(INTERFACE_DEF,"INTERFACE_DEF",first,LT(1))).add(modifiers).add(tmp37_AST).add(tp_AST).add(ie_AST).add(ib_AST)); + currentAST.root = interfaceDefinition_AST; + currentAST.child = interfaceDefinition_AST!=null &&interfaceDefinition_AST.getFirstChild()!=null ? + interfaceDefinition_AST.getFirstChild() : interfaceDefinition_AST; + currentAST.advanceChildToEnd(); + } + returnAST = interfaceDefinition_AST; + } + + public final void enumDefinition( + AST modifiers + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumDefinition_AST = null; + AST ic_AST = null; + AST eb_AST = null; + Token first = cloneToken(LT(1)); AST prevCurrentClass = currentClass; + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + } + + match(LITERAL_enum); + AST tmp39_AST = null; + tmp39_AST = astFactory.create(LT(1)); + match(IDENT); + if ( inputState.guessing==0 ) { + currentClass = tmp39_AST; + } + nls(); + implementsClause(); + ic_AST = (AST)returnAST; + nls(); + enumBlock(); + eb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + enumDefinition_AST = (AST)currentAST.root; + enumDefinition_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(ENUM_DEF,"ENUM_DEF",first,LT(1))).add(modifiers).add(tmp39_AST).add(ic_AST).add(eb_AST)); + currentAST.root = enumDefinition_AST; + currentAST.child = enumDefinition_AST!=null &&enumDefinition_AST.getFirstChild()!=null ? + enumDefinition_AST.getFirstChild() : enumDefinition_AST; + currentAST.advanceChildToEnd(); + } + if ( inputState.guessing==0 ) { + currentClass = prevCurrentClass; + } + returnAST = enumDefinition_AST; + } + + public final void annotationDefinition( + AST modifiers + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationDefinition_AST = null; + AST ab_AST = null; + Token first = cloneToken(LT(1)); + if (modifiers != null) { + first.setLine(modifiers.getLine()); + first.setColumn(modifiers.getColumn()); + } + + AST tmp40_AST = null; + tmp40_AST = astFactory.create(LT(1)); + match(AT); + match(LITERAL_interface); + AST tmp42_AST = null; + tmp42_AST = astFactory.create(LT(1)); + match(IDENT); + nls(); + annotationBlock(); + ab_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + annotationDefinition_AST = (AST)currentAST.root; + annotationDefinition_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(ANNOTATION_DEF,"ANNOTATION_DEF",first,LT(1))).add(modifiers).add(tmp42_AST).add(ab_AST)); + currentAST.root = annotationDefinition_AST; + currentAST.child = annotationDefinition_AST!=null &&annotationDefinition_AST.getFirstChild()!=null ? + annotationDefinition_AST.getFirstChild() : annotationDefinition_AST; + currentAST.advanceChildToEnd(); + } + returnAST = annotationDefinition_AST; + } + +/** A declaration is the creation of a reference or primitive-type variable, + * or (if arguments are present) of a method. + * Generically, this is called a 'variable' definition, even in the case of a class field or method. + * It may start with the modifiers and/or a declaration keyword "def". + * It may also start with the modifiers and a capitalized type name. + *

+ * AST effect: Create a separate Type/Var tree for each var in the var list. + * Must be guarded, as in (declarationStart) => declaration. + */ + public final void declaration() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST declaration_AST = null; + AST m_AST = null; + AST t_AST = null; + AST v_AST = null; + AST t2_AST = null; + AST v2_AST = null; + + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case AT: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + modifiers(); + m_AST = (AST)returnAST; + { + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_26.member(LA(2)))) { + typeSpec(false); + t_AST = (AST)returnAST; + } + else if ((LA(1)==IDENT||LA(1)==STRING_LITERAL) && (_tokenSet_27.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + variableDefinitions(m_AST, t_AST); + v_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + declaration_AST = (AST)currentAST.root; + declaration_AST = v_AST; + currentAST.root = declaration_AST; + currentAST.child = declaration_AST!=null &&declaration_AST.getFirstChild()!=null ? + declaration_AST.getFirstChild() : declaration_AST; + currentAST.advanceChildToEnd(); + } + break; + } + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + typeSpec(false); + t2_AST = (AST)returnAST; + variableDefinitions(null,t2_AST); + v2_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + declaration_AST = (AST)currentAST.root; + declaration_AST = v2_AST; + currentAST.root = declaration_AST; + currentAST.child = declaration_AST!=null &&declaration_AST.getFirstChild()!=null ? + declaration_AST.getFirstChild() : declaration_AST; + currentAST.advanceChildToEnd(); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = declaration_AST; + } + +/** A list of one or more modifier, annotation, or "def". */ + public final void modifiers() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST modifiers_AST = null; + Token first = LT(1); + + modifiersInternal(); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + modifiers_AST = (AST)currentAST.root; + modifiers_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(MODIFIERS,"MODIFIERS",first,LT(1))).add(modifiers_AST)); + currentAST.root = modifiers_AST; + currentAST.child = modifiers_AST!=null &&modifiers_AST.getFirstChild()!=null ? + modifiers_AST.getFirstChild() : modifiers_AST; + currentAST.advanceChildToEnd(); + } + modifiers_AST = (AST)currentAST.root; + returnAST = modifiers_AST; + } + + public final void typeSpec( + boolean addImagNode + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeSpec_AST = null; + + switch ( LA(1)) { + case IDENT: + { + classTypeSpec(addImagNode); + astFactory.addASTChild(currentAST, returnAST); + typeSpec_AST = (AST)currentAST.root; + break; + } + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + builtInTypeSpec(addImagNode); + astFactory.addASTChild(currentAST, returnAST); + typeSpec_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = typeSpec_AST; + } + +/** The tail of a declaration. + * Either v1, v2, ... (with possible initializers) or else m(args){body}. + * The two arguments are the modifier list (if any) and the declaration head (if any). + * The declaration head is the variable type, or (for a method) the return type. + * If it is missing, then the variable type is taken from its initializer (if there is one). + * Otherwise, the variable type defaults to 'any'. + * DECIDE: Method return types default to the type of the method body, as an expression. + */ + public final void variableDefinitions( + AST mods, AST t + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST variableDefinitions_AST = null; + Token id = null; + AST id_AST = null; + Token qid = null; + AST qid_AST = null; + AST param_AST = null; + AST tc_AST = null; + AST mb_AST = null; + Token first = cloneToken(LT(1)); + if (mods != null) { + first.setLine(mods.getLine()); + first.setColumn(mods.getColumn()); + } else if (t != null) { + first.setLine(t.getLine()); + first.setColumn(t.getColumn()); + } + + if ((LA(1)==IDENT) && (_tokenSet_28.member(LA(2)))) { + listOfVariables(mods,t,first); + astFactory.addASTChild(currentAST, returnAST); + variableDefinitions_AST = (AST)currentAST.root; + } + else if ((LA(1)==IDENT||LA(1)==STRING_LITERAL) && (LA(2)==LPAREN)) { + { + switch ( LA(1)) { + case IDENT: + { + id = LT(1); + id_AST = astFactory.create(id); + astFactory.addASTChild(currentAST, id_AST); + match(IDENT); + break; + } + case STRING_LITERAL: + { + qid = LT(1); + qid_AST = astFactory.create(qid); + astFactory.addASTChild(currentAST, qid_AST); + match(STRING_LITERAL); + if ( inputState.guessing==0 ) { + qid_AST.setType(IDENT); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(LPAREN); + parameterDeclarationList(); + param_AST = (AST)returnAST; + match(RPAREN); + { + boolean synPredMatched249 = false; + if (((LA(1)==LITERAL_throws||LA(1)==NLS) && (_tokenSet_29.member(LA(2))))) { + int _m249 = mark(); + synPredMatched249 = true; + inputState.guessing++; + try { + { + nls(); + match(LITERAL_throws); + } + } + catch (RecognitionException pe) { + synPredMatched249 = false; + } + rewind(_m249); +inputState.guessing--; + } + if ( synPredMatched249 ) { + throwsClause(); + tc_AST = (AST)returnAST; + } + else if ((_tokenSet_30.member(LA(1))) && (_tokenSet_12.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + boolean synPredMatched252 = false; + if (((LA(1)==LCURLY||LA(1)==NLS) && (_tokenSet_31.member(LA(2))))) { + int _m252 = mark(); + synPredMatched252 = true; + inputState.guessing++; + try { + { + nls(); + match(LCURLY); + } + } + catch (RecognitionException pe) { + synPredMatched252 = false; + } + rewind(_m252); +inputState.guessing--; + } + if ( synPredMatched252 ) { + { + nlsWarn(); + openBlock(); + mb_AST = (AST)returnAST; + } + } + else if ((_tokenSet_11.member(LA(1))) && (_tokenSet_12.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + variableDefinitions_AST = (AST)currentAST.root; + if (qid_AST != null) id_AST = qid_AST; + variableDefinitions_AST = + (AST)astFactory.make( (new ASTArray(7)).add(create(METHOD_DEF,"METHOD_DEF",first,LT(1))).add(mods).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t))).add(id_AST).add(param_AST).add(tc_AST).add(mb_AST)); + + currentAST.root = variableDefinitions_AST; + currentAST.child = variableDefinitions_AST!=null &&variableDefinitions_AST.getFirstChild()!=null ? + variableDefinitions_AST.getFirstChild() : variableDefinitions_AST; + currentAST.advanceChildToEnd(); + } + variableDefinitions_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = variableDefinitions_AST; + } + + public final void genericMethod() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST genericMethod_AST = null; + AST m_AST = null; + AST p_AST = null; + AST t_AST = null; + AST v_AST = null; + + modifiers(); + m_AST = (AST)returnAST; + typeParameters(); + p_AST = (AST)returnAST; + typeSpec(false); + t_AST = (AST)returnAST; + variableDefinitions(m_AST, t_AST); + v_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + genericMethod_AST = (AST)currentAST.root; + + genericMethod_AST = v_AST; + AST old = v_AST.getFirstChild(); + genericMethod_AST.setFirstChild(p_AST); + p_AST.setNextSibling(old); + + currentAST.root = genericMethod_AST; + currentAST.child = genericMethod_AST!=null &&genericMethod_AST.getFirstChild()!=null ? + genericMethod_AST.getFirstChild() : genericMethod_AST; + currentAST.advanceChildToEnd(); + } + returnAST = genericMethod_AST; + } + + public final void typeParameters() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeParameters_AST = null; + Token first = LT(1);int currentLtLevel = 0; + + if ( inputState.guessing==0 ) { + currentLtLevel = ltCounter; + } + match(LT); + if ( inputState.guessing==0 ) { + ltCounter++; + } + nls(); + typeParameter(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop115: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + typeParameter(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop115; + } + + } while (true); + } + nls(); + { + switch ( LA(1)) { + case GT: + case SR: + case BSR: + { + typeArgumentsOrParametersEnd(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case IDENT: + case STRING_LITERAL: + case LITERAL_extends: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_implements: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if (!(matchGenericTypeBrackets(((currentLtLevel != 0) || ltCounter == currentLtLevel), + "Missing closing bracket '>' for generics types", "Please specify the missing bracket!"))) + throw new SemanticException("matchGenericTypeBrackets(((currentLtLevel != 0) || ltCounter == currentLtLevel),\n \"Missing closing bracket '>' for generics types\", \"Please specify the missing bracket!\")"); + if ( inputState.guessing==0 ) { + typeParameters_AST = (AST)currentAST.root; + typeParameters_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_PARAMETERS,"TYPE_PARAMETERS",first,LT(1))).add(typeParameters_AST)); + currentAST.root = typeParameters_AST; + currentAST.child = typeParameters_AST!=null &&typeParameters_AST.getFirstChild()!=null ? + typeParameters_AST.getFirstChild() : typeParameters_AST; + currentAST.advanceChildToEnd(); + } + typeParameters_AST = (AST)currentAST.root; + returnAST = typeParameters_AST; + } + +/** A declaration with one declarator and no initialization, like a parameterDeclaration. + * Used to parse loops like for (int x in y) (up to the in keyword). + */ + public final void singleDeclarationNoInit() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST singleDeclarationNoInit_AST = null; + AST m_AST = null; + AST t_AST = null; + AST v_AST = null; + AST t2_AST = null; + AST v2_AST = null; + + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case AT: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + modifiers(); + m_AST = (AST)returnAST; + { + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_32.member(LA(2)))) { + typeSpec(false); + t_AST = (AST)returnAST; + } + else if ((LA(1)==IDENT) && (_tokenSet_33.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + singleVariable(m_AST, t_AST); + v_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + singleDeclarationNoInit_AST = (AST)currentAST.root; + singleDeclarationNoInit_AST = v_AST; + currentAST.root = singleDeclarationNoInit_AST; + currentAST.child = singleDeclarationNoInit_AST!=null &&singleDeclarationNoInit_AST.getFirstChild()!=null ? + singleDeclarationNoInit_AST.getFirstChild() : singleDeclarationNoInit_AST; + currentAST.advanceChildToEnd(); + } + break; + } + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + typeSpec(false); + t2_AST = (AST)returnAST; + singleVariable(null,t2_AST); + v2_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + singleDeclarationNoInit_AST = (AST)currentAST.root; + singleDeclarationNoInit_AST = v2_AST; + currentAST.root = singleDeclarationNoInit_AST; + currentAST.child = singleDeclarationNoInit_AST!=null &&singleDeclarationNoInit_AST.getFirstChild()!=null ? + singleDeclarationNoInit_AST.getFirstChild() : singleDeclarationNoInit_AST; + currentAST.advanceChildToEnd(); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = singleDeclarationNoInit_AST; + } + +/** Used in cases where a declaration cannot have commas, or ends with the "in" operator instead of '='. */ + public final void singleVariable( + AST mods, AST t + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST singleVariable_AST = null; + AST id_AST = null; + Token first = LT(1); + + variableName(); + id_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + singleVariable_AST = (AST)currentAST.root; + singleVariable_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1))).add(mods).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t))).add(id_AST)); + currentAST.root = singleVariable_AST; + currentAST.child = singleVariable_AST!=null &&singleVariable_AST.getFirstChild()!=null ? + singleVariable_AST.getFirstChild() : singleVariable_AST; + currentAST.advanceChildToEnd(); + } + returnAST = singleVariable_AST; + } + +/** A declaration with one declarator and optional initialization, like a parameterDeclaration. + * Used to parse declarations used for both binding and effect, in places like argument + * lists and while statements. + */ + public final void singleDeclaration() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST singleDeclaration_AST = null; + AST sd_AST = null; + + singleDeclarationNoInit(); + sd_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + singleDeclaration_AST = (AST)currentAST.root; + singleDeclaration_AST = sd_AST; + currentAST.root = singleDeclaration_AST; + currentAST.child = singleDeclaration_AST!=null &&singleDeclaration_AST.getFirstChild()!=null ? + singleDeclaration_AST.getFirstChild() : singleDeclaration_AST; + currentAST.advanceChildToEnd(); + } + { + switch ( LA(1)) { + case ASSIGN: + { + varInitializer(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case RBRACK: + case COMMA: + case RPAREN: + case SEMI: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + singleDeclaration_AST = (AST)currentAST.root; + returnAST = singleDeclaration_AST; + } + +/** An assignment operator '=' followed by an expression. (Never empty.) */ + public final void varInitializer() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST varInitializer_AST = null; + + try { // for error handling + AST tmp47_AST = null; + tmp47_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp47_AST); + match(ASSIGN); + nls(); + expressionStatementNoCheck(); + astFactory.addASTChild(currentAST, returnAST); + varInitializer_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // if empty assignment was found, produce something compatible with content assist + int index = 0; + if (ASSIGN == LT(index).getType() || ASSIGN == LT(--index).getType()) { + astFactory.addASTChild(currentAST, missingIdentifier(LT(index), LT(index + 1))); + varInitializer_AST = (AST) currentAST.root; + reportError(e); + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = varInitializer_AST; + } + +/** Used only as a lookahead predicate, before diving in and parsing a declaration. + * A declaration can be unambiguously introduced with "def", an annotation or a modifier token like "final". + * It may also be introduced by a simple identifier whose first character is an uppercase letter, + * as in {String x}. A declaration can also be introduced with a built in type like 'int' or 'void'. + * Brackets (array and generic) are allowed, as in {List[] x} or {int[][] y}. + * Anything else is parsed as a statement of some sort (expression or command). + *

+ * (In the absence of explicit method-call parens, we assume a capitalized name is a type name. + * Yes, this is a little hacky. Alternatives are to complicate the declaration or command + * syntaxes, or to have the parser query the symbol table. Parse-time queries are evil. + * And we want both {String x} and {println x}. So we need a syntactic razor-edge to slip + * between 'println' and 'String'.) + */ + public final void declarationStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST declarationStart_AST = null; + + { + int _cnt31=0; + _loop31: + do { + switch ( LA(1)) { + case LITERAL_def: + { + { + match(LITERAL_def); + nls(); + } + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + modifier(); + nls(); + break; + } + case AT: + { + annotation(); + nls(); + break; + } + default: + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_34.member(LA(2)))) { + { + if ((LA(1)==IDENT) && (_tokenSet_35.member(LA(2)))) { + upperCaseIdent(); + } + else if (((LA(1) >= LITERAL_void && LA(1) <= LITERAL_double))) { + builtInType(); + } + else if ((LA(1)==IDENT) && (LA(2)==DOT)) { + qualifiedTypeName(); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + switch ( LA(1)) { + case LT: + { + typeArguments(); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + _loop30: + do { + if ((LA(1)==LBRACK)) { + AST tmp49_AST = null; + tmp49_AST = astFactory.create(LT(1)); + match(LBRACK); + balancedTokens(); + AST tmp50_AST = null; + tmp50_AST = astFactory.create(LT(1)); + match(RBRACK); + } + else { + break _loop30; + } + + } while (true); + } + } + else { + if ( _cnt31>=1 ) { break _loop31; } else {throw new NoViableAltException(LT(1), getFilename());} + } + } + _cnt31++; + } while (true); + } + { + switch ( LA(1)) { + case IDENT: + { + AST tmp51_AST = null; + tmp51_AST = astFactory.create(LT(1)); + match(IDENT); + break; + } + case STRING_LITERAL: + { + AST tmp52_AST = null; + tmp52_AST = astFactory.create(LT(1)); + match(STRING_LITERAL); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + returnAST = declarationStart_AST; + } + + public final void modifier() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST modifier_AST = null; + + switch ( LA(1)) { + case LITERAL_private: + { + AST tmp53_AST = null; + tmp53_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp53_AST); + match(LITERAL_private); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_public: + { + AST tmp54_AST = null; + tmp54_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp54_AST); + match(LITERAL_public); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_protected: + { + AST tmp55_AST = null; + tmp55_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp55_AST); + match(LITERAL_protected); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_static: + { + AST tmp56_AST = null; + tmp56_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp56_AST); + match(LITERAL_static); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_transient: + { + AST tmp57_AST = null; + tmp57_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp57_AST); + match(LITERAL_transient); + modifier_AST = (AST)currentAST.root; + break; + } + case FINAL: + { + AST tmp58_AST = null; + tmp58_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp58_AST); + match(FINAL); + modifier_AST = (AST)currentAST.root; + break; + } + case ABSTRACT: + { + AST tmp59_AST = null; + tmp59_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp59_AST); + match(ABSTRACT); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_native: + { + AST tmp60_AST = null; + tmp60_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp60_AST); + match(LITERAL_native); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_threadsafe: + { + AST tmp61_AST = null; + tmp61_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp61_AST); + match(LITERAL_threadsafe); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_synchronized: + { + AST tmp62_AST = null; + tmp62_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp62_AST); + match(LITERAL_synchronized); + modifier_AST = (AST)currentAST.root; + break; + } + case LITERAL_volatile: + { + AST tmp63_AST = null; + tmp63_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp63_AST); + match(LITERAL_volatile); + modifier_AST = (AST)currentAST.root; + break; + } + case STRICTFP: + { + AST tmp64_AST = null; + tmp64_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp64_AST); + match(STRICTFP); + modifier_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = modifier_AST; + } + + public final void annotation() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotation_AST = null; + AST i_AST = null; + AST args_AST = null; + Token first = LT(1); + + if ((LA(1)==AT) && (LA(2)==IDENT)) { + match(AT); + identifier(); + i_AST = (AST)returnAST; + nls(); + { + if ((LA(1)==LPAREN) && (_tokenSet_36.member(LA(2)))) { + match(LPAREN); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + annotationArguments(); + args_AST = (AST)returnAST; + break; + } + case RPAREN: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(RPAREN); + } + else if ((_tokenSet_37.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + annotation_AST = (AST)currentAST.root; + annotation_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(ANNOTATION,"ANNOTATION",first,LT(1))).add(i_AST).add(args_AST)); + currentAST.root = annotation_AST; + currentAST.child = annotation_AST!=null &&annotation_AST.getFirstChild()!=null ? + annotation_AST.getFirstChild() : annotation_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((LA(1)==AT) && (_tokenSet_37.member(LA(2)))) { + match(AT); + nls(); + if ( inputState.guessing==0 ) { + annotation_AST = (AST)currentAST.root; + annotation_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(ANNOTATION,"ANNOTATION",first,LT(1))).add(missingIdentifier(first,LT(1))).add(null)); + currentAST.root = annotation_AST; + currentAST.child = annotation_AST!=null &&annotation_AST.getFirstChild()!=null ? + annotation_AST.getFirstChild() : annotation_AST; + currentAST.advanceChildToEnd(); + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = annotation_AST; + } + +/** An IDENT token whose spelling is required to start with an uppercase letter. + * In the case of a simple statement {UpperID name} the identifier is taken to be a type name, not a command name. + */ + public final void upperCaseIdent() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST upperCaseIdent_AST = null; + + if (!(isUpperCase(LT(1)))) + throw new SemanticException("isUpperCase(LT(1))"); + AST tmp69_AST = null; + tmp69_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp69_AST); + match(IDENT); + upperCaseIdent_AST = (AST)currentAST.root; + returnAST = upperCaseIdent_AST; + } + + public final void builtInType() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST builtInType_AST = null; + + switch ( LA(1)) { + case LITERAL_void: + { + AST tmp70_AST = null; + tmp70_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp70_AST); + match(LITERAL_void); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_boolean: + { + AST tmp71_AST = null; + tmp71_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp71_AST); + match(LITERAL_boolean); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_byte: + { + AST tmp72_AST = null; + tmp72_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp72_AST); + match(LITERAL_byte); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_char: + { + AST tmp73_AST = null; + tmp73_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp73_AST); + match(LITERAL_char); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_short: + { + AST tmp74_AST = null; + tmp74_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp74_AST); + match(LITERAL_short); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_int: + { + AST tmp75_AST = null; + tmp75_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp75_AST); + match(LITERAL_int); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_float: + { + AST tmp76_AST = null; + tmp76_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp76_AST); + match(LITERAL_float); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_long: + { + AST tmp77_AST = null; + tmp77_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp77_AST); + match(LITERAL_long); + builtInType_AST = (AST)currentAST.root; + break; + } + case LITERAL_double: + { + AST tmp78_AST = null; + tmp78_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp78_AST); + match(LITERAL_double); + builtInType_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = builtInType_AST; + } + + public final void qualifiedTypeName() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST qualifiedTypeName_AST = null; + + AST tmp79_AST = null; + tmp79_AST = astFactory.create(LT(1)); + match(IDENT); + AST tmp80_AST = null; + tmp80_AST = astFactory.create(LT(1)); + match(DOT); + { + _loop38: + do { + if ((LA(1)==IDENT) && (LA(2)==DOT)) { + AST tmp81_AST = null; + tmp81_AST = astFactory.create(LT(1)); + match(IDENT); + AST tmp82_AST = null; + tmp82_AST = astFactory.create(LT(1)); + match(DOT); + } + else { + break _loop38; + } + + } while (true); + } + upperCaseIdent(); + returnAST = qualifiedTypeName_AST; + } + + public final void typeArguments() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeArguments_AST = null; + Token first = LT(1); + int currentLtLevel = 0; + + if ( inputState.guessing==0 ) { + currentLtLevel = ltCounter; + } + match(LT); + if ( inputState.guessing==0 ) { + ltCounter++; + } + nls(); + typeArgument(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop60: + do { + if (((LA(1)==COMMA) && (_tokenSet_38.member(LA(2))))&&(inputState.guessing !=0 || ltCounter == currentLtLevel + 1)) { + match(COMMA); + nls(); + typeArgument(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop60; + } + + } while (true); + } + nls(); + { + if ((_tokenSet_39.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + typeArgumentsOrParametersEnd(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if (!(matchGenericTypeBrackets(((currentLtLevel != 0) || ltCounter == currentLtLevel), + "Missing closing bracket '>' for generics types", "Please specify the missing bracket!"))) + throw new SemanticException("matchGenericTypeBrackets(((currentLtLevel != 0) || ltCounter == currentLtLevel),\n \"Missing closing bracket '>' for generics types\", \"Please specify the missing bracket!\")"); + if ( inputState.guessing==0 ) { + typeArguments_AST = (AST)currentAST.root; + typeArguments_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_ARGUMENTS,"TYPE_ARGUMENTS",first,LT(1))).add(typeArguments_AST)); + currentAST.root = typeArguments_AST; + currentAST.child = typeArguments_AST!=null &&typeArguments_AST.getFirstChild()!=null ? + typeArguments_AST.getFirstChild() : typeArguments_AST; + currentAST.advanceChildToEnd(); + } + typeArguments_AST = (AST)currentAST.root; + returnAST = typeArguments_AST; + } + + public final void balancedTokens() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST balancedTokens_AST = null; + + { + _loop597: + do { + if ((_tokenSet_40.member(LA(1)))) { + balancedBrackets(); + } + else if ((_tokenSet_41.member(LA(1)))) { + { + match(_tokenSet_41); + } + } + else { + break _loop597; + } + + } while (true); + } + returnAST = balancedTokens_AST; + } + +/** + * lookahead predicate for usage of generics in methods + * as parameter for the method. Example: + * static T foo(){} + * must be first after the modifier. + * This rule allows more and does no exact match, but it + * is only a lookahead, not the real rule. + */ + public final void genericMethodStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST genericMethodStart_AST = null; + + { + int _cnt35=0; + _loop35: + do { + switch ( LA(1)) { + case LITERAL_def: + { + match(LITERAL_def); + nls(); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + modifier(); + nls(); + break; + } + case AT: + { + annotation(); + nls(); + break; + } + default: + { + if ( _cnt35>=1 ) { break _loop35; } else {throw new NoViableAltException(LT(1), getFilename());} + } + } + _cnt35++; + } while (true); + } + AST tmp87_AST = null; + tmp87_AST = astFactory.create(LT(1)); + match(LT); + returnAST = genericMethodStart_AST; + } + +/** Used to look ahead for a constructor + */ + public final void constructorStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST constructorStart_AST = null; + Token id = null; + AST id_AST = null; + + modifiersOpt(); + id = LT(1); + id_AST = astFactory.create(id); + match(IDENT); + if (!(isConstructorIdent(id))) + throw new SemanticException("isConstructorIdent(id)"); + nls(); + match(LPAREN); + returnAST = constructorStart_AST; + } + +/** A list of zero or more modifiers, annotations, or "def". */ + public final void modifiersOpt() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST modifiersOpt_AST = null; + Token first = LT(1); + + { + if ((_tokenSet_13.member(LA(1))) && (_tokenSet_42.member(LA(2)))) { + modifiersInternal(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_43.member(LA(1))) && (_tokenSet_44.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + modifiersOpt_AST = (AST)currentAST.root; + modifiersOpt_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(MODIFIERS,"MODIFIERS",first,LT(1))).add(modifiersOpt_AST)); + currentAST.root = modifiersOpt_AST; + currentAST.child = modifiersOpt_AST!=null &&modifiersOpt_AST.getFirstChild()!=null ? + modifiersOpt_AST.getFirstChild() : modifiersOpt_AST; + currentAST.advanceChildToEnd(); + } + modifiersOpt_AST = (AST)currentAST.root; + returnAST = modifiersOpt_AST; + } + +/** Used only as a lookahead predicate for nested type definitions. */ + public final void typeDefinitionStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeDefinitionStart_AST = null; + + modifiersOpt(); + { + switch ( LA(1)) { + case LITERAL_class: + { + match(LITERAL_class); + break; + } + case LITERAL_interface: + { + match(LITERAL_interface); + break; + } + case LITERAL_enum: + { + match(LITERAL_enum); + break; + } + case LITERAL_trait: + { + match(LITERAL_trait); + break; + } + case AT: + { + AST tmp93_AST = null; + tmp93_AST = astFactory.create(LT(1)); + match(AT); + match(LITERAL_interface); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + returnAST = typeDefinitionStart_AST; + } + + public final void classTypeSpec( + boolean addImagNode + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST classTypeSpec_AST = null; + AST ct_AST = null; + Token first = LT(1); + + classOrInterfaceType(false); + ct_AST = (AST)returnAST; + declaratorBrackets(ct_AST); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + classTypeSpec_AST = (AST)currentAST.root; + + if ( addImagNode ) { + classTypeSpec_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(classTypeSpec_AST)); + } + + currentAST.root = classTypeSpec_AST; + currentAST.child = classTypeSpec_AST!=null &&classTypeSpec_AST.getFirstChild()!=null ? + classTypeSpec_AST.getFirstChild() : classTypeSpec_AST; + currentAST.advanceChildToEnd(); + } + classTypeSpec_AST = (AST)currentAST.root; + returnAST = classTypeSpec_AST; + } + + public final void builtInTypeSpec( + boolean addImagNode + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST builtInTypeSpec_AST = null; + AST bt_AST = null; + Token first = LT(1); + + builtInType(); + bt_AST = (AST)returnAST; + declaratorBrackets(bt_AST); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + builtInTypeSpec_AST = (AST)currentAST.root; + + if ( addImagNode ) { + builtInTypeSpec_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(builtInTypeSpec_AST)); + } + + currentAST.root = builtInTypeSpec_AST; + currentAST.child = builtInTypeSpec_AST!=null &&builtInTypeSpec_AST.getFirstChild()!=null ? + builtInTypeSpec_AST.getFirstChild() : builtInTypeSpec_AST; + currentAST.advanceChildToEnd(); + } + builtInTypeSpec_AST = (AST)currentAST.root; + returnAST = builtInTypeSpec_AST; + } + + public final void classOrInterfaceType( + boolean addImagNode + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST classOrInterfaceType_AST = null; + Token i1 = null; + AST i1_AST = null; + Token d = null; + AST d_AST = null; + Token i2 = null; + AST i2_AST = null; + AST ta_AST = null; + Token first = LT(1); + + i1 = LT(1); + i1_AST = astFactory.create(i1); + astFactory.makeASTRoot(currentAST, i1_AST); + match(IDENT); + { + if ((LA(1)==LT) && (_tokenSet_38.member(LA(2)))) { + typeArguments(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((LA(1)==LT) && (LA(2)==GT)) { + typeArgumentsDiamond(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + _loop49: + do { + if ((LA(1)==DOT) && (LA(2)==IDENT)) { + d = LT(1); + d_AST = astFactory.create(d); + match(DOT); + i2 = LT(1); + i2_AST = astFactory.create(i2); + match(IDENT); + { + if ((LA(1)==LT) && (_tokenSet_38.member(LA(2)))) { + typeArguments(); + ta_AST = (AST)returnAST; + } + else if ((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + i1_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(DOT,".",first,LT(1))).add(i1_AST).add(i2_AST).add(ta_AST)); + } + } + else { + break _loop49; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + classOrInterfaceType_AST = (AST)currentAST.root; + + classOrInterfaceType_AST = i1_AST; + if ( addImagNode ) { + classOrInterfaceType_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(classOrInterfaceType_AST)); + } + + currentAST.root = classOrInterfaceType_AST; + currentAST.child = classOrInterfaceType_AST!=null &&classOrInterfaceType_AST.getFirstChild()!=null ? + classOrInterfaceType_AST.getFirstChild() : classOrInterfaceType_AST; + currentAST.advanceChildToEnd(); + } + classOrInterfaceType_AST = (AST)currentAST.root; + returnAST = classOrInterfaceType_AST; + } + +/** After some type names, where zero or more empty bracket pairs are allowed. + * We use ARRAY_DECLARATOR to represent this. + */ + public final void declaratorBrackets( + AST typ + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST declaratorBrackets_AST = null; + + if ( inputState.guessing==0 ) { + declaratorBrackets_AST = (AST)currentAST.root; + declaratorBrackets_AST=typ; + currentAST.root = declaratorBrackets_AST; + currentAST.child = declaratorBrackets_AST!=null &&declaratorBrackets_AST.getFirstChild()!=null ? + declaratorBrackets_AST.getFirstChild() : declaratorBrackets_AST; + currentAST.advanceChildToEnd(); + } + { + _loop264: + do { + if ((LA(1)==LBRACK) && (LA(2)==RBRACK)) { + match(LBRACK); + match(RBRACK); + if ( inputState.guessing==0 ) { + declaratorBrackets_AST = (AST)currentAST.root; + declaratorBrackets_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(ARRAY_DECLARATOR,"[",typ,LT(1))).add(declaratorBrackets_AST)); + currentAST.root = declaratorBrackets_AST; + currentAST.child = declaratorBrackets_AST!=null &&declaratorBrackets_AST.getFirstChild()!=null ? + declaratorBrackets_AST.getFirstChild() : declaratorBrackets_AST; + currentAST.advanceChildToEnd(); + } + } + else { + break _loop264; + } + + } while (true); + } + declaratorBrackets_AST = (AST)currentAST.root; + returnAST = declaratorBrackets_AST; + } + + public final void typeArgumentsDiamond() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeArgumentsDiamond_AST = null; + Token first = LT(1); + + match(LT); + match(GT); + nls(); + if ( inputState.guessing==0 ) { + typeArgumentsDiamond_AST = (AST)currentAST.root; + typeArgumentsDiamond_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_ARGUMENTS,"TYPE_ARGUMENTS",first,LT(1))).add(typeArgumentsDiamond_AST)); + currentAST.root = typeArgumentsDiamond_AST; + currentAST.child = typeArgumentsDiamond_AST!=null &&typeArgumentsDiamond_AST.getFirstChild()!=null ? + typeArgumentsDiamond_AST.getFirstChild() : typeArgumentsDiamond_AST; + currentAST.advanceChildToEnd(); + } + typeArgumentsDiamond_AST = (AST)currentAST.root; + returnAST = typeArgumentsDiamond_AST; + } + + public final void typeArgumentSpec() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeArgumentSpec_AST = null; + + switch ( LA(1)) { + case IDENT: + { + classTypeSpec(true); + astFactory.addASTChild(currentAST, returnAST); + typeArgumentSpec_AST = (AST)currentAST.root; + break; + } + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + builtInTypeArraySpec(true); + astFactory.addASTChild(currentAST, returnAST); + typeArgumentSpec_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = typeArgumentSpec_AST; + } + + public final void builtInTypeArraySpec( + boolean addImagNode + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST builtInTypeArraySpec_AST = null; + AST bt_AST = null; + Token first = LT(1); + + builtInType(); + bt_AST = (AST)returnAST; + { + boolean synPredMatched68 = false; + if (((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2))))) { + int _m68 = mark(); + synPredMatched68 = true; + inputState.guessing++; + try { + { + match(LBRACK); + } + } + catch (RecognitionException pe) { + synPredMatched68 = false; + } + rewind(_m68); +inputState.guessing--; + } + if ( synPredMatched68 ) { + declaratorBrackets(bt_AST); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2)))) { + if ( inputState.guessing==0 ) { + require(false, + "primitive type parameters not allowed here", + "use the corresponding wrapper type, such as Integer for int" + ); + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + builtInTypeArraySpec_AST = (AST)currentAST.root; + + if ( addImagNode ) { + builtInTypeArraySpec_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(builtInTypeArraySpec_AST)); + } + + currentAST.root = builtInTypeArraySpec_AST; + currentAST.child = builtInTypeArraySpec_AST!=null &&builtInTypeArraySpec_AST.getFirstChild()!=null ? + builtInTypeArraySpec_AST.getFirstChild() : builtInTypeArraySpec_AST; + currentAST.advanceChildToEnd(); + } + builtInTypeArraySpec_AST = (AST)currentAST.root; + returnAST = builtInTypeArraySpec_AST; + } + + public final void typeArgument() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeArgument_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + typeArgumentSpec(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case QUESTION: + { + wildcardType(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + typeArgument_AST = (AST)currentAST.root; + typeArgument_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_ARGUMENT,"TYPE_ARGUMENT",first,LT(1))).add(typeArgument_AST)); + currentAST.root = typeArgument_AST; + currentAST.child = typeArgument_AST!=null &&typeArgument_AST.getFirstChild()!=null ? + typeArgument_AST.getFirstChild() : typeArgument_AST; + currentAST.advanceChildToEnd(); + } + typeArgument_AST = (AST)currentAST.root; + returnAST = typeArgument_AST; + } + + public final void wildcardType() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST wildcardType_AST = null; + + AST tmp99_AST = null; + tmp99_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp99_AST); + match(QUESTION); + { + boolean synPredMatched56 = false; + if (((LA(1)==LITERAL_extends||LA(1)==LITERAL_super) && (LA(2)==IDENT||LA(2)==NLS))) { + int _m56 = mark(); + synPredMatched56 = true; + inputState.guessing++; + try { + { + switch ( LA(1)) { + case LITERAL_extends: + { + match(LITERAL_extends); + break; + } + case LITERAL_super: + { + match(LITERAL_super); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + catch (RecognitionException pe) { + synPredMatched56 = false; + } + rewind(_m56); +inputState.guessing--; + } + if ( synPredMatched56 ) { + typeArgumentBounds(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_3.member(LA(1))) && (_tokenSet_4.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + wildcardType_AST = (AST)currentAST.root; + wildcardType_AST.setType(WILDCARD_TYPE); + } + wildcardType_AST = (AST)currentAST.root; + returnAST = wildcardType_AST; + } + + public final void typeArgumentBounds() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeArgumentBounds_AST = null; + Token first = LT(1);boolean isUpperBounds = false; + + { + switch ( LA(1)) { + case LITERAL_extends: + { + match(LITERAL_extends); + if ( inputState.guessing==0 ) { + isUpperBounds=true; + } + break; + } + case LITERAL_super: + { + match(LITERAL_super); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + nls(); + if ( inputState.guessing==0 ) { + typeArgumentBounds_AST = (AST)currentAST.root; + + if (isUpperBounds) + { + typeArgumentBounds_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1))).add(typeArgumentBounds_AST)); + } + else + { + typeArgumentBounds_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_LOWER_BOUNDS,"TYPE_LOWER_BOUNDS",first,LT(1))).add(typeArgumentBounds_AST)); + } + + currentAST.root = typeArgumentBounds_AST; + currentAST.child = typeArgumentBounds_AST!=null &&typeArgumentBounds_AST.getFirstChild()!=null ? + typeArgumentBounds_AST.getFirstChild() : typeArgumentBounds_AST; + currentAST.advanceChildToEnd(); + } + typeArgumentBounds_AST = (AST)currentAST.root; + returnAST = typeArgumentBounds_AST; + } + + protected final void typeArgumentsOrParametersEnd() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeArgumentsOrParametersEnd_AST = null; + + switch ( LA(1)) { + case GT: + { + match(GT); + if ( inputState.guessing==0 ) { + ltCounter-=1; + } + typeArgumentsOrParametersEnd_AST = (AST)currentAST.root; + break; + } + case SR: + { + match(SR); + if ( inputState.guessing==0 ) { + ltCounter-=2; + } + typeArgumentsOrParametersEnd_AST = (AST)currentAST.root; + break; + } + case BSR: + { + match(BSR); + if ( inputState.guessing==0 ) { + ltCounter-=3; + } + typeArgumentsOrParametersEnd_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = typeArgumentsOrParametersEnd_AST; + } + + public final void type() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST type_AST = null; + + switch ( LA(1)) { + case IDENT: + { + classOrInterfaceType(false); + astFactory.addASTChild(currentAST, returnAST); + type_AST = (AST)currentAST.root; + break; + } + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + builtInType(); + astFactory.addASTChild(currentAST, returnAST); + type_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = type_AST; + } + + public final void modifiersInternal() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST modifiersInternal_AST = null; + int seenDef = 0; + + { + int _cnt81=0; + _loop81: + do { + if (((LA(1)==LITERAL_def))&&(seenDef++ == 0)) { + match(LITERAL_def); + nls(); + } + else if ((_tokenSet_45.member(LA(1)))) { + modifier(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + } + else if ((LA(1)==AT) && (LA(2)==LITERAL_interface)) { + if ( inputState.guessing==0 ) { + break; /* go out of the ()+ loop*/ + } + AST tmp106_AST = null; + tmp106_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp106_AST); + match(AT); + AST tmp107_AST = null; + tmp107_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp107_AST); + match(LITERAL_interface); + } + else if ((LA(1)==AT) && (_tokenSet_46.member(LA(2)))) { + annotation(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + } + else { + if ( _cnt81>=1 ) { break _loop81; } else {throw new NoViableAltException(LT(1), getFilename());} + } + + _cnt81++; + } while (true); + } + modifiersInternal_AST = (AST)currentAST.root; + returnAST = modifiersInternal_AST; + } + + public final void annotationArguments() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationArguments_AST = null; + AST v_AST = null; + + if ((_tokenSet_47.member(LA(1))) && (_tokenSet_48.member(LA(2)))) { + annotationMemberValueInitializer(); + v_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + annotationArguments_AST = (AST)currentAST.root; + Token itkn = new Token(IDENT, "value"); + AST i; + i = (AST)astFactory.make( (new ASTArray(1)).add(create(IDENT,"value",itkn,itkn))); + annotationArguments_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",LT(1),LT(1))).add(i).add(v_AST)); + currentAST.root = annotationArguments_AST; + currentAST.child = annotationArguments_AST!=null &&annotationArguments_AST.getFirstChild()!=null ? + annotationArguments_AST.getFirstChild() : annotationArguments_AST; + currentAST.advanceChildToEnd(); + } + annotationArguments_AST = (AST)currentAST.root; + } + else if ((_tokenSet_49.member(LA(1))) && (LA(2)==ASSIGN)) { + annotationMemberValuePairs(); + astFactory.addASTChild(currentAST, returnAST); + annotationArguments_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = annotationArguments_AST; + } + + public final void annotationsInternal() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationsInternal_AST = null; + + { + _loop91: + do { + if ((LA(1)==AT) && (LA(2)==LITERAL_interface)) { + if ( inputState.guessing==0 ) { + break; /* go out of the ()* loop*/ + } + AST tmp108_AST = null; + tmp108_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp108_AST); + match(AT); + AST tmp109_AST = null; + tmp109_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp109_AST); + match(LITERAL_interface); + } + else if ((LA(1)==AT) && (_tokenSet_50.member(LA(2)))) { + annotation(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + } + else { + break _loop91; + } + + } while (true); + } + annotationsInternal_AST = (AST)currentAST.root; + returnAST = annotationsInternal_AST; + } + + public final void annotationMemberValueInitializer() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationMemberValueInitializer_AST = null; + + switch ( LA(1)) { + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + conditionalExpression(0); + astFactory.addASTChild(currentAST, returnAST); + annotationMemberValueInitializer_AST = (AST)currentAST.root; + break; + } + case AT: + { + annotation(); + astFactory.addASTChild(currentAST, returnAST); + annotationMemberValueInitializer_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = annotationMemberValueInitializer_AST; + } + + public final void annotationMemberValuePairs() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationMemberValuePairs_AST = null; + + annotationMemberValuePair(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop97: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + annotationMemberValuePair(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop97; + } + + } while (true); + } + annotationMemberValuePairs_AST = (AST)currentAST.root; + returnAST = annotationMemberValuePairs_AST; + } + + public final void annotationMemberValuePair() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationMemberValuePair_AST = null; + AST i_AST = null; + AST v_AST = null; + Token first = LT(1); + + try { // for error handling + annotationIdent(); + i_AST = (AST)returnAST; + match(ASSIGN); + nls(); + { + switch ( LA(1)) { + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + annotationMemberValueInitializer(); + v_AST = (AST)returnAST; + break; + } + case COMMA: + case RPAREN: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + annotationMemberValuePair_AST = (AST)currentAST.root; + annotationMemberValuePair_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",first,LT(1))).add(i_AST).add(v_AST)); + currentAST.root = annotationMemberValuePair_AST; + currentAST.child = annotationMemberValuePair_AST!=null &&annotationMemberValuePair_AST.getFirstChild()!=null ? + annotationMemberValuePair_AST.getFirstChild() : annotationMemberValuePair_AST; + currentAST.advanceChildToEnd(); + } + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // finish invalid member-value pair if the closing parenthesis is next + if (LT(1).getType() == RPAREN) { + reportError(e); + if (i_AST == null) { + i_AST = missingIdentifier(first, LT(1)); + } + annotationMemberValuePair_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",first,LT(1))).add(i_AST).add(v_AST)); + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = annotationMemberValuePair_AST; + } + + public final void annotationIdent() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationIdent_AST = null; + + switch ( LA(1)) { + case IDENT: + { + AST tmp112_AST = null; + tmp112_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp112_AST); + match(IDENT); + annotationIdent_AST = (AST)currentAST.root; + break; + } + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + { + keywordPropertyNames(); + astFactory.addASTChild(currentAST, returnAST); + annotationIdent_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = annotationIdent_AST; + } + + public final void keywordPropertyNames() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST keywordPropertyNames_AST = null; + + { + switch ( LA(1)) { + case LITERAL_as: + { + AST tmp113_AST = null; + tmp113_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp113_AST); + match(LITERAL_as); + break; + } + case LITERAL_assert: + { + AST tmp114_AST = null; + tmp114_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp114_AST); + match(LITERAL_assert); + break; + } + case LITERAL_break: + { + AST tmp115_AST = null; + tmp115_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp115_AST); + match(LITERAL_break); + break; + } + case LITERAL_case: + { + AST tmp116_AST = null; + tmp116_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp116_AST); + match(LITERAL_case); + break; + } + case LITERAL_catch: + { + AST tmp117_AST = null; + tmp117_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp117_AST); + match(LITERAL_catch); + break; + } + case LITERAL_class: + { + AST tmp118_AST = null; + tmp118_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp118_AST); + match(LITERAL_class); + break; + } + case UNUSED_CONST: + { + AST tmp119_AST = null; + tmp119_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp119_AST); + match(UNUSED_CONST); + break; + } + case LITERAL_continue: + { + AST tmp120_AST = null; + tmp120_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp120_AST); + match(LITERAL_continue); + break; + } + case LITERAL_def: + { + AST tmp121_AST = null; + tmp121_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp121_AST); + match(LITERAL_def); + break; + } + case LITERAL_default: + { + AST tmp122_AST = null; + tmp122_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp122_AST); + match(LITERAL_default); + break; + } + case UNUSED_DO: + { + AST tmp123_AST = null; + tmp123_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp123_AST); + match(UNUSED_DO); + break; + } + case LITERAL_else: + { + AST tmp124_AST = null; + tmp124_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp124_AST); + match(LITERAL_else); + break; + } + case LITERAL_enum: + { + AST tmp125_AST = null; + tmp125_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp125_AST); + match(LITERAL_enum); + break; + } + case LITERAL_extends: + { + AST tmp126_AST = null; + tmp126_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp126_AST); + match(LITERAL_extends); + break; + } + case LITERAL_false: + { + AST tmp127_AST = null; + tmp127_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp127_AST); + match(LITERAL_false); + break; + } + case LITERAL_finally: + { + AST tmp128_AST = null; + tmp128_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp128_AST); + match(LITERAL_finally); + break; + } + case LITERAL_for: + { + AST tmp129_AST = null; + tmp129_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp129_AST); + match(LITERAL_for); + break; + } + case UNUSED_GOTO: + { + AST tmp130_AST = null; + tmp130_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp130_AST); + match(UNUSED_GOTO); + break; + } + case LITERAL_if: + { + AST tmp131_AST = null; + tmp131_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp131_AST); + match(LITERAL_if); + break; + } + case LITERAL_implements: + { + AST tmp132_AST = null; + tmp132_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp132_AST); + match(LITERAL_implements); + break; + } + case LITERAL_import: + { + AST tmp133_AST = null; + tmp133_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp133_AST); + match(LITERAL_import); + break; + } + case LITERAL_in: + { + AST tmp134_AST = null; + tmp134_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp134_AST); + match(LITERAL_in); + break; + } + case LITERAL_instanceof: + { + AST tmp135_AST = null; + tmp135_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp135_AST); + match(LITERAL_instanceof); + break; + } + case LITERAL_interface: + { + AST tmp136_AST = null; + tmp136_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp136_AST); + match(LITERAL_interface); + break; + } + case LITERAL_new: + { + AST tmp137_AST = null; + tmp137_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp137_AST); + match(LITERAL_new); + break; + } + case LITERAL_null: + { + AST tmp138_AST = null; + tmp138_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp138_AST); + match(LITERAL_null); + break; + } + case LITERAL_package: + { + AST tmp139_AST = null; + tmp139_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp139_AST); + match(LITERAL_package); + break; + } + case LITERAL_return: + { + AST tmp140_AST = null; + tmp140_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp140_AST); + match(LITERAL_return); + break; + } + case LITERAL_super: + { + AST tmp141_AST = null; + tmp141_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp141_AST); + match(LITERAL_super); + break; + } + case LITERAL_switch: + { + AST tmp142_AST = null; + tmp142_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp142_AST); + match(LITERAL_switch); + break; + } + case LITERAL_this: + { + AST tmp143_AST = null; + tmp143_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp143_AST); + match(LITERAL_this); + break; + } + case LITERAL_throw: + { + AST tmp144_AST = null; + tmp144_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp144_AST); + match(LITERAL_throw); + break; + } + case LITERAL_throws: + { + AST tmp145_AST = null; + tmp145_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp145_AST); + match(LITERAL_throws); + break; + } + case LITERAL_trait: + { + AST tmp146_AST = null; + tmp146_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp146_AST); + match(LITERAL_trait); + break; + } + case LITERAL_true: + { + AST tmp147_AST = null; + tmp147_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp147_AST); + match(LITERAL_true); + break; + } + case LITERAL_try: + { + AST tmp148_AST = null; + tmp148_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp148_AST); + match(LITERAL_try); + break; + } + case LITERAL_while: + { + AST tmp149_AST = null; + tmp149_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp149_AST); + match(LITERAL_while); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + modifier(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + builtInType(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + keywordPropertyNames_AST = (AST)currentAST.root; + keywordPropertyNames_AST.setType(IDENT); + } + keywordPropertyNames_AST = (AST)currentAST.root; + returnAST = keywordPropertyNames_AST; + } + + public final void conditionalExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST conditionalExpression_AST = null; + + logicalOrExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + boolean synPredMatched461 = false; + if (((LA(1)==ELVIS_OPERATOR||LA(1)==NLS) && (_tokenSet_51.member(LA(2))))) { + int _m461 = mark(); + synPredMatched461 = true; + inputState.guessing++; + try { + { + nls(); + match(ELVIS_OPERATOR); + } + } + catch (RecognitionException pe) { + synPredMatched461 = false; + } + rewind(_m461); +inputState.guessing--; + } + if ( synPredMatched461 ) { + nls(); + AST tmp150_AST = null; + tmp150_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp150_AST); + match(ELVIS_OPERATOR); + nls(); + conditionalExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + boolean synPredMatched463 = false; + if (((LA(1)==QUESTION||LA(1)==NLS) && (_tokenSet_52.member(LA(2))))) { + int _m463 = mark(); + synPredMatched463 = true; + inputState.guessing++; + try { + { + nls(); + match(QUESTION); + } + } + catch (RecognitionException pe) { + synPredMatched463 = false; + } + rewind(_m463); +inputState.guessing--; + } + if ( synPredMatched463 ) { + try { // for error handling + nls(); + AST tmp151_AST = null; + tmp151_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp151_AST); + match(QUESTION); + nls(); + assignmentExpression(0); + astFactory.addASTChild(currentAST, returnAST); + nls(); + match(COLON); + nls(); + conditionalExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // keep AST if recognition failed at or after ':' + if (currentAST.root.getNumberOfChildren() > 1) { + reportError(e); + } else { + throw e; + } + + } else { + throw e; + } + } + } + else if ((_tokenSet_53.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + conditionalExpression_AST = (AST)currentAST.root; + returnAST = conditionalExpression_AST; + } + + public final void superClassClause() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST superClassClause_AST = null; + AST c_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case LITERAL_extends: + { + match(LITERAL_extends); + nls(); + classOrInterfaceType(false); + c_AST = (AST)returnAST; + nls(); + break; + } + case EOF: + case LCURLY: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_implements: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + superClassClause_AST = (AST)currentAST.root; + superClassClause_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1))).add(c_AST)); + currentAST.root = superClassClause_AST; + currentAST.child = superClassClause_AST!=null &&superClassClause_AST.getFirstChild()!=null ? + superClassClause_AST.getFirstChild() : superClassClause_AST; + currentAST.advanceChildToEnd(); + } + returnAST = superClassClause_AST; + } + + public final void implementsClause() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST implementsClause_AST = null; + Token i = null; + AST i_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case LITERAL_implements: + { + i = LT(1); + i_AST = astFactory.create(i); + match(LITERAL_implements); + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + { + _loop198: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop198; + } + + } while (true); + } + nls(); + break; + } + case EOF: + case LCURLY: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + implementsClause_AST = (AST)currentAST.root; + implementsClause_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE",first,LT(1))).add(implementsClause_AST)); + currentAST.root = implementsClause_AST; + currentAST.child = implementsClause_AST!=null &&implementsClause_AST.getFirstChild()!=null ? + implementsClause_AST.getFirstChild() : implementsClause_AST; + currentAST.advanceChildToEnd(); + } + implementsClause_AST = (AST)currentAST.root; + returnAST = implementsClause_AST; + } + + public final void classBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST classBlock_AST = null; + Token first = LT(1); + + try { // for error handling + match(LCURLY); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + classField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + _loop127: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + classField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop127; + } + + } while (true); + } + match(RCURLY); + if ( inputState.guessing==0 ) { + classBlock_AST = (AST)currentAST.root; + classBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(OBJBLOCK,"OBJBLOCK",first,LT(1))).add(classBlock_AST)); + currentAST.root = classBlock_AST; + currentAST.child = classBlock_AST!=null &&classBlock_AST.getFirstChild()!=null ? + classBlock_AST.getFirstChild() : classBlock_AST; + currentAST.advanceChildToEnd(); + } + classBlock_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + if (errorList.isEmpty()) { + // dirty hack to avoid having trouble with cascading problems + classBlock_AST = (AST) currentAST.root; + } + reportError(e); + classBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(OBJBLOCK,"OBJBLOCK",first,LT(1))).add(classBlock_AST)); + currentAST.root = classBlock_AST; + currentAST.child = classBlock_AST != null && classBlock_AST.getFirstChild() != null ? classBlock_AST.getFirstChild() : classBlock_AST; + currentAST.advanceChildToEnd(); + + } else { + throw e; + } + } + returnAST = classBlock_AST; + } + + public final void interfaceExtends() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST interfaceExtends_AST = null; + Token e = null; + AST e_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case LITERAL_extends: + { + e = LT(1); + e_AST = astFactory.create(e); + match(LITERAL_extends); + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + { + _loop194: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop194; + } + + } while (true); + } + nls(); + break; + } + case LCURLY: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + interfaceExtends_AST = (AST)currentAST.root; + interfaceExtends_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1))).add(interfaceExtends_AST)); + currentAST.root = interfaceExtends_AST; + currentAST.child = interfaceExtends_AST!=null &&interfaceExtends_AST.getFirstChild()!=null ? + interfaceExtends_AST.getFirstChild() : interfaceExtends_AST; + currentAST.advanceChildToEnd(); + } + interfaceExtends_AST = (AST)currentAST.root; + returnAST = interfaceExtends_AST; + } + + public final void interfaceBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST interfaceBlock_AST = null; + Token first = LT(1); + + match(LCURLY); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + interfaceField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + _loop132: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + interfaceField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop132; + } + + } while (true); + } + match(RCURLY); + if ( inputState.guessing==0 ) { + interfaceBlock_AST = (AST)currentAST.root; + interfaceBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(OBJBLOCK,"OBJBLOCK",first,LT(1))).add(interfaceBlock_AST)); + currentAST.root = interfaceBlock_AST; + currentAST.child = interfaceBlock_AST!=null &&interfaceBlock_AST.getFirstChild()!=null ? + interfaceBlock_AST.getFirstChild() : interfaceBlock_AST; + currentAST.advanceChildToEnd(); + } + interfaceBlock_AST = (AST)currentAST.root; + returnAST = interfaceBlock_AST; + } + + public final void enumBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumBlock_AST = null; + Token first = LT(1); + + match(LCURLY); + nls(); + { + boolean synPredMatched141 = false; + if (((LA(1)==IDENT||LA(1)==AT) && (_tokenSet_55.member(LA(2))))) { + int _m141 = mark(); + synPredMatched141 = true; + inputState.guessing++; + try { + { + enumConstantsStart(); + } + } + catch (RecognitionException pe) { + synPredMatched141 = false; + } + rewind(_m141); +inputState.guessing--; + } + if ( synPredMatched141 ) { + enumConstants(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_56.member(LA(1))) && (_tokenSet_57.member(LA(2)))) { + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + classField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + _loop145: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + classField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop145; + } + + } while (true); + } + match(RCURLY); + if ( inputState.guessing==0 ) { + enumBlock_AST = (AST)currentAST.root; + enumBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(OBJBLOCK,"OBJBLOCK",first,LT(1))).add(enumBlock_AST)); + currentAST.root = enumBlock_AST; + currentAST.child = enumBlock_AST!=null &&enumBlock_AST.getFirstChild()!=null ? + enumBlock_AST.getFirstChild() : enumBlock_AST; + currentAST.advanceChildToEnd(); + } + enumBlock_AST = (AST)currentAST.root; + returnAST = enumBlock_AST; + } + + public final void annotationBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationBlock_AST = null; + Token first = LT(1); + + match(LCURLY); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + annotationField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + _loop137: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + annotationField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop137; + } + + } while (true); + } + match(RCURLY); + if ( inputState.guessing==0 ) { + annotationBlock_AST = (AST)currentAST.root; + annotationBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(OBJBLOCK,"OBJBLOCK",first,LT(1))).add(annotationBlock_AST)); + currentAST.root = annotationBlock_AST; + currentAST.child = annotationBlock_AST!=null &&annotationBlock_AST.getFirstChild()!=null ? + annotationBlock_AST.getFirstChild() : annotationBlock_AST; + currentAST.advanceChildToEnd(); + } + annotationBlock_AST = (AST)currentAST.root; + returnAST = annotationBlock_AST; + } + + public final void typeParameter() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeParameter_AST = null; + Token id = null; + AST id_AST = null; + Token first = LT(1); + + { + id = LT(1); + id_AST = astFactory.create(id); + astFactory.addASTChild(currentAST, id_AST); + match(IDENT); + } + { + if ((LA(1)==LITERAL_extends) && (LA(2)==IDENT||LA(2)==NLS)) { + typeParameterBounds(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_58.member(LA(1))) && (_tokenSet_12.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + typeParameter_AST = (AST)currentAST.root; + typeParameter_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_PARAMETER,"TYPE_PARAMETER",first,LT(1))).add(typeParameter_AST)); + currentAST.root = typeParameter_AST; + currentAST.child = typeParameter_AST!=null &&typeParameter_AST.getFirstChild()!=null ? + typeParameter_AST.getFirstChild() : typeParameter_AST; + currentAST.advanceChildToEnd(); + } + typeParameter_AST = (AST)currentAST.root; + returnAST = typeParameter_AST; + } + + public final void typeParameterBounds() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeParameterBounds_AST = null; + Token first = LT(1); + + match(LITERAL_extends); + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + { + _loop122: + do { + if ((LA(1)==BAND)) { + match(BAND); + nls(); + classOrInterfaceType(true); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop122; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + typeParameterBounds_AST = (AST)currentAST.root; + typeParameterBounds_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1))).add(typeParameterBounds_AST)); + currentAST.root = typeParameterBounds_AST; + currentAST.child = typeParameterBounds_AST!=null &&typeParameterBounds_AST.getFirstChild()!=null ? + typeParameterBounds_AST.getFirstChild() : typeParameterBounds_AST; + currentAST.advanceChildToEnd(); + } + typeParameterBounds_AST = (AST)currentAST.root; + returnAST = typeParameterBounds_AST; + } + + public final void classField() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST classField_AST = null; + AST mc_AST = null; + AST ctor_AST = null; + AST dg_AST = null; + AST mad_AST = null; + AST dd_AST = null; + AST mods_AST = null; + AST td_AST = null; + AST s3_AST = null; + AST s4_AST = null; + Token first = LT(1); + + try { // for error handling + boolean synPredMatched201 = false; + if (((_tokenSet_59.member(LA(1))) && (_tokenSet_60.member(LA(2))))) { + int _m201 = mark(); + synPredMatched201 = true; + inputState.guessing++; + try { + { + constructorStart(); + } + } + catch (RecognitionException pe) { + synPredMatched201 = false; + } + rewind(_m201); +inputState.guessing--; + } + if ( synPredMatched201 ) { + modifiersOpt(); + mc_AST = (AST)returnAST; + constructorDefinition(mc_AST); + ctor_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = ctor_AST; + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched203 = false; + if (((_tokenSet_13.member(LA(1))) && (_tokenSet_14.member(LA(2))))) { + int _m203 = mark(); + synPredMatched203 = true; + inputState.guessing++; + try { + { + genericMethodStart(); + } + } + catch (RecognitionException pe) { + synPredMatched203 = false; + } + rewind(_m203); +inputState.guessing--; + } + if ( synPredMatched203 ) { + genericMethod(); + dg_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = dg_AST; + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched205 = false; + if (((_tokenSet_13.member(LA(1))) && (_tokenSet_15.member(LA(2))))) { + int _m205 = mark(); + synPredMatched205 = true; + inputState.guessing++; + try { + { + multipleAssignmentDeclarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched205 = false; + } + rewind(_m205); +inputState.guessing--; + } + if ( synPredMatched205 ) { + multipleAssignmentDeclaration(); + mad_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = mad_AST; + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched207 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_17.member(LA(2))))) { + int _m207 = mark(); + synPredMatched207 = true; + inputState.guessing++; + try { + { + declarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched207 = false; + } + rewind(_m207); +inputState.guessing--; + } + if ( synPredMatched207 ) { + declaration(); + dd_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = dd_AST; + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched209 = false; + if (((_tokenSet_22.member(LA(1))) && (_tokenSet_23.member(LA(2))))) { + int _m209 = mark(); + synPredMatched209 = true; + inputState.guessing++; + try { + { + typeDefinitionStart(); + } + } + catch (RecognitionException pe) { + synPredMatched209 = false; + } + rewind(_m209); +inputState.guessing--; + } + if ( synPredMatched209 ) { + modifiersOpt(); + mods_AST = (AST)returnAST; + { + typeDefinitionInternal(mods_AST); + td_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = td_AST; + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + } + else if ((LA(1)==LITERAL_static) && (LA(2)==LCURLY||LA(2)==NLS)) { + match(LITERAL_static); + nls(); + compoundStatement(); + s3_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(STATIC_INIT,"STATIC_INIT",first,LT(1))).add(s3_AST)); + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((LA(1)==LCURLY)) { + compoundStatement(); + s4_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + classField_AST = (AST)currentAST.root; + classField_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1))).add(s4_AST)); + currentAST.root = classField_AST; + currentAST.child = classField_AST!=null &&classField_AST.getFirstChild()!=null ? + classField_AST.getFirstChild() : classField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + }}}} + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // GRECLIPSE-494: "class C {\n def m(){}\n thing\n static main(args){}\n }" + if (LA(1) == IDENT) { + reportError(e); + // create a variable definition for "thing" in hopes that subsequent class members can still be parsed + classField_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1))).add(null).add(create(TYPE,"java.lang.Object",LT(1),LT(2))).add(create(IDENT,first.getText(),LT(1),LT(2)))); + consumeUntil(NLS); + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = classField_AST; + } + + public final void interfaceField() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST interfaceField_AST = null; + AST d_AST = null; + AST dg_AST = null; + AST mods_AST = null; + AST td_AST = null; + + boolean synPredMatched213 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_17.member(LA(2))))) { + int _m213 = mark(); + synPredMatched213 = true; + inputState.guessing++; + try { + { + declarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched213 = false; + } + rewind(_m213); +inputState.guessing--; + } + if ( synPredMatched213 ) { + declaration(); + d_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + interfaceField_AST = (AST)currentAST.root; + interfaceField_AST = d_AST; + currentAST.root = interfaceField_AST; + currentAST.child = interfaceField_AST!=null &&interfaceField_AST.getFirstChild()!=null ? + interfaceField_AST.getFirstChild() : interfaceField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched215 = false; + if (((_tokenSet_13.member(LA(1))) && (_tokenSet_14.member(LA(2))))) { + int _m215 = mark(); + synPredMatched215 = true; + inputState.guessing++; + try { + { + genericMethodStart(); + } + } + catch (RecognitionException pe) { + synPredMatched215 = false; + } + rewind(_m215); +inputState.guessing--; + } + if ( synPredMatched215 ) { + genericMethod(); + dg_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + interfaceField_AST = (AST)currentAST.root; + interfaceField_AST = dg_AST; + currentAST.root = interfaceField_AST; + currentAST.child = interfaceField_AST!=null &&interfaceField_AST.getFirstChild()!=null ? + interfaceField_AST.getFirstChild() : interfaceField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched217 = false; + if (((_tokenSet_22.member(LA(1))) && (_tokenSet_23.member(LA(2))))) { + int _m217 = mark(); + synPredMatched217 = true; + inputState.guessing++; + try { + { + typeDefinitionStart(); + } + } + catch (RecognitionException pe) { + synPredMatched217 = false; + } + rewind(_m217); +inputState.guessing--; + } + if ( synPredMatched217 ) { + modifiersOpt(); + mods_AST = (AST)returnAST; + { + typeDefinitionInternal(mods_AST); + td_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + interfaceField_AST = (AST)currentAST.root; + interfaceField_AST = td_AST; + currentAST.root = interfaceField_AST; + currentAST.child = interfaceField_AST!=null &&interfaceField_AST.getFirstChild()!=null ? + interfaceField_AST.getFirstChild() : interfaceField_AST; + currentAST.advanceChildToEnd(); + } + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + }} + returnAST = interfaceField_AST; + } + + public final void annotationField() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST annotationField_AST = null; + AST mods_AST = null; + AST td_AST = null; + AST t_AST = null; + Token i = null; + AST i_AST = null; + AST amvi_AST = null; + AST v_AST = null; + Token first = LT(1); + + modifiersOpt(); + mods_AST = (AST)returnAST; + { + switch ( LA(1)) { + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + { + typeDefinitionInternal(mods_AST); + td_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + annotationField_AST = (AST)currentAST.root; + annotationField_AST = td_AST; + currentAST.root = annotationField_AST; + currentAST.child = annotationField_AST!=null &&annotationField_AST.getFirstChild()!=null ? + annotationField_AST.getFirstChild() : annotationField_AST; + currentAST.advanceChildToEnd(); + } + break; + } + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + typeSpec(false); + t_AST = (AST)returnAST; + { + boolean synPredMatched165 = false; + if (((LA(1)==IDENT) && (LA(2)==LPAREN))) { + int _m165 = mark(); + synPredMatched165 = true; + inputState.guessing++; + try { + { + match(IDENT); + match(LPAREN); + } + } + catch (RecognitionException pe) { + synPredMatched165 = false; + } + rewind(_m165); +inputState.guessing--; + } + if ( synPredMatched165 ) { + i = LT(1); + i_AST = astFactory.create(i); + match(IDENT); + match(LPAREN); + match(RPAREN); + { + switch ( LA(1)) { + case LITERAL_default: + { + match(LITERAL_default); + nls(); + annotationMemberValueInitializer(); + amvi_AST = (AST)returnAST; + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + annotationField_AST = (AST)currentAST.root; + annotationField_AST = + (AST)astFactory.make( (new ASTArray(5)).add(create(ANNOTATION_FIELD_DEF,"ANNOTATION_FIELD_DEF",first,LT(1))).add(mods_AST).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t_AST))).add(i_AST).add(amvi_AST)); + currentAST.root = annotationField_AST; + currentAST.child = annotationField_AST!=null &&annotationField_AST.getFirstChild()!=null ? + annotationField_AST.getFirstChild() : annotationField_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((LA(1)==IDENT||LA(1)==STRING_LITERAL) && (_tokenSet_61.member(LA(2)))) { + variableDefinitions(mods_AST,t_AST); + v_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + annotationField_AST = (AST)currentAST.root; + annotationField_AST = v_AST; + currentAST.root = annotationField_AST; + currentAST.child = annotationField_AST!=null &&annotationField_AST.getFirstChild()!=null ? + annotationField_AST.getFirstChild() : annotationField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + returnAST = annotationField_AST; + } + +/** Guard for enumConstants. */ + public final void enumConstantsStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumConstantsStart_AST = null; + + annotationsOpt(); + astFactory.addASTChild(currentAST, returnAST); + AST tmp170_AST = null; + tmp170_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp170_AST); + match(IDENT); + { + switch ( LA(1)) { + case LCURLY: + { + AST tmp171_AST = null; + tmp171_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp171_AST); + match(LCURLY); + break; + } + case LPAREN: + { + AST tmp172_AST = null; + tmp172_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp172_AST); + match(LPAREN); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case AT: + case COMMA: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case RCURLY: + case SEMI: + case NLS: + { + nls(); + astFactory.addASTChild(currentAST, returnAST); + { + switch ( LA(1)) { + case SEMI: + { + AST tmp173_AST = null; + tmp173_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp173_AST); + match(SEMI); + break; + } + case COMMA: + { + AST tmp174_AST = null; + tmp174_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp174_AST); + match(COMMA); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + declarationStart(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + { + AST tmp175_AST = null; + tmp175_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp175_AST); + match(RCURLY); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + enumConstantsStart_AST = (AST)currentAST.root; + returnAST = enumConstantsStart_AST; + } + +/** Comma-separated list of one or more enum constant definitions. */ + public final void enumConstants() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumConstants_AST = null; + + enumConstant(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop160: + do { + boolean synPredMatched153 = false; + if (((_tokenSet_62.member(LA(1))) && (_tokenSet_63.member(LA(2))))) { + int _m153 = mark(); + synPredMatched153 = true; + inputState.guessing++; + try { + { + nls(); + { + switch ( LA(1)) { + case SEMI: + { + match(SEMI); + break; + } + case RCURLY: + { + match(RCURLY); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + classField(); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + } + catch (RecognitionException pe) { + synPredMatched153 = false; + } + rewind(_m153); +inputState.guessing--; + } + if ( synPredMatched153 ) { + if ( inputState.guessing==0 ) { + break; /* leave ()* loop */ + } + } + else if ((LA(1)==COMMA||LA(1)==NLS) && (_tokenSet_64.member(LA(2)))) { + nls(); + match(COMMA); + { + boolean synPredMatched156 = false; + if (((_tokenSet_65.member(LA(1))) && (_tokenSet_55.member(LA(2))))) { + int _m156 = mark(); + synPredMatched156 = true; + inputState.guessing++; + try { + { + nls(); + annotationsOpt(); + match(IDENT); + } + } + catch (RecognitionException pe) { + synPredMatched156 = false; + } + rewind(_m156); +inputState.guessing--; + } + if ( synPredMatched156 ) { + nls(); + enumConstant(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + boolean synPredMatched159 = false; + if (((_tokenSet_62.member(LA(1))) && (_tokenSet_63.member(LA(2))))) { + int _m159 = mark(); + synPredMatched159 = true; + inputState.guessing++; + try { + { + nls(); + { + switch ( LA(1)) { + case SEMI: + { + match(SEMI); + break; + } + case RCURLY: + { + match(RCURLY); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + classField(); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + } + catch (RecognitionException pe) { + synPredMatched159 = false; + } + rewind(_m159); +inputState.guessing--; + } + if ( synPredMatched159 ) { + if ( inputState.guessing==0 ) { + break; /* leave ()* loop */ + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop160; + } + + } while (true); + } + enumConstants_AST = (AST)currentAST.root; + returnAST = enumConstants_AST; + } + + public final void enumConstant() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumConstant_AST = null; + AST an_AST = null; + Token i = null; + AST i_AST = null; + AST a_AST = null; + AST b_AST = null; + Token first = LT(1); + + annotationsOpt(); + an_AST = (AST)returnAST; + i = LT(1); + i_AST = astFactory.create(i); + match(IDENT); + { + switch ( LA(1)) { + case LPAREN: + { + match(LPAREN); + argList(); + a_AST = (AST)returnAST; + match(RPAREN); + break; + } + case COMMA: + case LCURLY: + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case LCURLY: + { + enumConstantBlock(); + b_AST = (AST)returnAST; + break; + } + case COMMA: + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + enumConstant_AST = (AST)currentAST.root; + enumConstant_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(ENUM_CONSTANT_DEF,"ENUM_CONSTANT_DEF",first,LT(1))).add(an_AST).add(i_AST).add(a_AST).add(b_AST)); + currentAST.root = enumConstant_AST; + currentAST.child = enumConstant_AST!=null &&enumConstant_AST.getFirstChild()!=null ? + enumConstant_AST.getFirstChild() : enumConstant_AST; + currentAST.advanceChildToEnd(); + } + returnAST = enumConstant_AST; + } + + public final void argList() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST argList_AST = null; + + Token first = LT(1); + Token lastComma = null; + int hls=0, hls2=0; + boolean hasClosureList=false; + boolean trailingComma=false; + boolean sce=false; + + + try { // for error handling + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case STAR: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + hls=argument(); + astFactory.addASTChild(currentAST, returnAST); + { + switch ( LA(1)) { + case SEMI: + { + { + { + int _cnt565=0; + _loop565: + do { + if ((LA(1)==SEMI)) { + match(SEMI); + if ( inputState.guessing==0 ) { + hasClosureList=true; + } + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + sce=strictContextExpression(true); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RBRACK: + case RPAREN: + case SEMI: + { + if ( inputState.guessing==0 ) { + astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + if ( _cnt565>=1 ) { break _loop565; } else {throw new NoViableAltException(LT(1), getFilename());} + } + + _cnt565++; + } while (true); + } + if ( inputState.guessing==0 ) { + argList_AST = (AST)currentAST.root; + argList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1))).add(argList_AST)); + currentAST.root = argList_AST; + currentAST.child = argList_AST!=null &&argList_AST.getFirstChild()!=null ? + argList_AST.getFirstChild() : argList_AST; + currentAST.advanceChildToEnd(); + } + } + break; + } + case RBRACK: + case COMMA: + case RPAREN: + { + { + { + _loop571: + do { + if ((LA(1)==COMMA)) { + if ( inputState.guessing==0 ) { + lastComma = LT(1); + } + match(COMMA); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case STAR: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + { + hls2=argument(); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + hls |= hls2; + } + } + break; + } + case RBRACK: + case COMMA: + case RPAREN: + { + { + if ( inputState.guessing==0 ) { + if (trailingComma) throw new NoViableAltException(lastComma, getFilename()); + trailingComma=true; + + } + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop571; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + argList_AST = (AST)currentAST.root; + argList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(ELIST,"ELIST",first,LT(1))).add(argList_AST)); + currentAST.root = argList_AST; + currentAST.child = argList_AST!=null &&argList_AST.getFirstChild()!=null ? + argList_AST.getFirstChild() : argList_AST; + currentAST.advanceChildToEnd(); + } + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + break; + } + case RBRACK: + case RPAREN: + { + { + if ( inputState.guessing==0 ) { + argList_AST = (AST)currentAST.root; + argList_AST = create(ELIST,"ELIST",first,LT(1)); + currentAST.root = argList_AST; + currentAST.child = argList_AST!=null &&argList_AST.getFirstChild()!=null ? + argList_AST.getFirstChild() : argList_AST; + currentAST.advanceChildToEnd(); + } + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + argListHasLabels = (hls&1)!=0; + } + argList_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // in case of missing right paren "method(obj.exp", complete arglist + if (currentAST != null && !hasClosureList) { + argList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(ELIST,"ELIST",first,LT(1))).add(currentAST.root)); + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = argList_AST; + } + + public final void enumConstantBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumConstantBlock_AST = null; + Token first = LT(1); + + match(LCURLY); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + enumConstantField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + _loop174: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + { + enumConstantField(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop174; + } + + } while (true); + } + match(RCURLY); + if ( inputState.guessing==0 ) { + enumConstantBlock_AST = (AST)currentAST.root; + enumConstantBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(OBJBLOCK,"OBJBLOCK",first,LT(1))).add(enumConstantBlock_AST)); + currentAST.root = enumConstantBlock_AST; + currentAST.child = enumConstantBlock_AST!=null &&enumConstantBlock_AST.getFirstChild()!=null ? + enumConstantBlock_AST.getFirstChild() : enumConstantBlock_AST; + currentAST.advanceChildToEnd(); + } + enumConstantBlock_AST = (AST)currentAST.root; + returnAST = enumConstantBlock_AST; + } + + public final void enumConstantField() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumConstantField_AST = null; + AST mods_AST = null; + AST td_AST = null; + AST m1_AST = null; + AST tp1_AST = null; + AST t1_AST = null; + AST e1_AST = null; + AST m2_AST = null; + AST tp2_AST = null; + AST t2_AST = null; + AST e2_AST = null; + AST cs_AST = null; + Token first = LT(1); + + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case LT: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + { + boolean synPredMatched178 = false; + if (((_tokenSet_22.member(LA(1))) && (_tokenSet_23.member(LA(2))))) { + int _m178 = mark(); + synPredMatched178 = true; + inputState.guessing++; + try { + { + typeDefinitionStart(); + } + } + catch (RecognitionException pe) { + synPredMatched178 = false; + } + rewind(_m178); +inputState.guessing--; + } + if ( synPredMatched178 ) { + modifiersOpt(); + mods_AST = (AST)returnAST; + typeDefinitionInternal(mods_AST); + td_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + enumConstantField_AST = (AST)currentAST.root; + enumConstantField_AST = td_AST; + currentAST.root = enumConstantField_AST; + currentAST.child = enumConstantField_AST!=null &&enumConstantField_AST.getFirstChild()!=null ? + enumConstantField_AST.getFirstChild() : enumConstantField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + boolean synPredMatched180 = false; + if (((_tokenSet_13.member(LA(1))) && (_tokenSet_66.member(LA(2))))) { + int _m180 = mark(); + synPredMatched180 = true; + inputState.guessing++; + try { + { + modifiers(); + } + } + catch (RecognitionException pe) { + synPredMatched180 = false; + } + rewind(_m180); +inputState.guessing--; + } + if ( synPredMatched180 ) { + modifiers(); + m1_AST = (AST)returnAST; + { + switch ( LA(1)) { + case LT: + { + typeParameters(); + tp1_AST = (AST)returnAST; + break; + } + case IDENT: + case STRING_LITERAL: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_26.member(LA(2)))) { + typeSpec(false); + t1_AST = (AST)returnAST; + } + else if ((LA(1)==IDENT||LA(1)==STRING_LITERAL) && (_tokenSet_61.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + enumConstantFieldInternal(m1_AST, tp1_AST, t1_AST, first); + e1_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + enumConstantField_AST = (AST)currentAST.root; + enumConstantField_AST = e1_AST; + currentAST.root = enumConstantField_AST; + currentAST.child = enumConstantField_AST!=null &&enumConstantField_AST.getFirstChild()!=null ? + enumConstantField_AST.getFirstChild() : enumConstantField_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((_tokenSet_67.member(LA(1))) && (_tokenSet_17.member(LA(2)))) { + modifiersOpt(); + m2_AST = (AST)returnAST; + { + switch ( LA(1)) { + case LT: + { + typeParameters(); + tp2_AST = (AST)returnAST; + break; + } + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + typeSpec(false); + t2_AST = (AST)returnAST; + enumConstantFieldInternal(m2_AST, tp2_AST, t2_AST, first); + e2_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + enumConstantField_AST = (AST)currentAST.root; + enumConstantField_AST = e2_AST; + currentAST.root = enumConstantField_AST; + currentAST.child = enumConstantField_AST!=null &&enumConstantField_AST.getFirstChild()!=null ? + enumConstantField_AST.getFirstChild() : enumConstantField_AST; + currentAST.advanceChildToEnd(); + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + break; + } + case LCURLY: + { + compoundStatement(); + cs_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + enumConstantField_AST = (AST)currentAST.root; + enumConstantField_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1))).add(cs_AST)); + currentAST.root = enumConstantField_AST; + currentAST.child = enumConstantField_AST!=null &&enumConstantField_AST.getFirstChild()!=null ? + enumConstantField_AST.getFirstChild() : enumConstantField_AST; + currentAST.advanceChildToEnd(); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = enumConstantField_AST; + } + + protected final void enumConstantFieldInternal( + AST mods, AST tp, AST t, Token first + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST enumConstantFieldInternal_AST = null; + AST param_AST = null; + AST tc_AST = null; + AST s2_AST = null; + AST v_AST = null; + + boolean synPredMatched186 = false; + if (((LA(1)==IDENT) && (LA(2)==LPAREN))) { + int _m186 = mark(); + synPredMatched186 = true; + inputState.guessing++; + try { + { + match(IDENT); + match(LPAREN); + } + } + catch (RecognitionException pe) { + synPredMatched186 = false; + } + rewind(_m186); +inputState.guessing--; + } + if ( synPredMatched186 ) { + AST tmp183_AST = null; + tmp183_AST = astFactory.create(LT(1)); + match(IDENT); + match(LPAREN); + parameterDeclarationList(); + param_AST = (AST)returnAST; + match(RPAREN); + { + boolean synPredMatched189 = false; + if (((LA(1)==LITERAL_throws||LA(1)==NLS) && (_tokenSet_29.member(LA(2))))) { + int _m189 = mark(); + synPredMatched189 = true; + inputState.guessing++; + try { + { + nls(); + match(LITERAL_throws); + } + } + catch (RecognitionException pe) { + synPredMatched189 = false; + } + rewind(_m189); +inputState.guessing--; + } + if ( synPredMatched189 ) { + throwsClause(); + tc_AST = (AST)returnAST; + } + else if ((_tokenSet_68.member(LA(1))) && (_tokenSet_69.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + switch ( LA(1)) { + case LCURLY: + { + compoundStatement(); + s2_AST = (AST)returnAST; + break; + } + case RCURLY: + case SEMI: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + enumConstantFieldInternal_AST = (AST)currentAST.root; + + enumConstantFieldInternal_AST = (AST)astFactory.make( (new ASTArray(7)).add(create(METHOD_DEF,"METHOD_DEF",first,LT(1))).add(mods).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t))).add(tmp183_AST).add(param_AST).add(tc_AST).add(s2_AST)); + if (tp != null) { + AST old = enumConstantFieldInternal_AST.getFirstChild(); + enumConstantFieldInternal_AST.setFirstChild(tp); + tp.setNextSibling(old); + } + + currentAST.root = enumConstantFieldInternal_AST; + currentAST.child = enumConstantFieldInternal_AST!=null &&enumConstantFieldInternal_AST.getFirstChild()!=null ? + enumConstantFieldInternal_AST.getFirstChild() : enumConstantFieldInternal_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((LA(1)==IDENT||LA(1)==STRING_LITERAL) && (_tokenSet_61.member(LA(2)))) { + variableDefinitions(mods,t); + v_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + enumConstantFieldInternal_AST = (AST)currentAST.root; + enumConstantFieldInternal_AST = v_AST; + currentAST.root = enumConstantFieldInternal_AST; + currentAST.child = enumConstantFieldInternal_AST!=null &&enumConstantFieldInternal_AST.getFirstChild()!=null ? + enumConstantFieldInternal_AST.getFirstChild() : enumConstantFieldInternal_AST; + currentAST.advanceChildToEnd(); + } + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = enumConstantFieldInternal_AST; + } + + public final void compoundStatement() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST compoundStatement_AST = null; + + openBlock(); + astFactory.addASTChild(currentAST, returnAST); + compoundStatement_AST = (AST)currentAST.root; + returnAST = compoundStatement_AST; + } + +/** A list of zero or more formal parameters. + * If a parameter is variable length (e.g. String... myArg) it should be + * to the right of any other parameters of the same kind. + * General form: (req, ..., opt, ..., [rest], key, ..., [restKeys], [block] + * This must be sorted out after parsing, since the various declaration forms + * are impossible to tell apart without backtracking. + */ + public final void parameterDeclarationList() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST parameterDeclarationList_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case FINAL: + case LITERAL_def: + case IDENT: + case AT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case TRIPLE_DOT: + { + parameterDeclaration(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop272: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + parameterDeclaration(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop272; + } + + } while (true); + } + break; + } + case RPAREN: + case CLOSABLE_BLOCK_OP: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + parameterDeclarationList_AST = (AST)currentAST.root; + parameterDeclarationList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(PARAMETERS,"PARAMETERS",first,LT(1))).add(parameterDeclarationList_AST)); + currentAST.root = parameterDeclarationList_AST; + currentAST.child = parameterDeclarationList_AST!=null &¶meterDeclarationList_AST.getFirstChild()!=null ? + parameterDeclarationList_AST.getFirstChild() : parameterDeclarationList_AST; + currentAST.advanceChildToEnd(); + } + parameterDeclarationList_AST = (AST)currentAST.root; + returnAST = parameterDeclarationList_AST; + } + + public final void throwsClause() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST throwsClause_AST = null; + + nls(); + AST tmp187_AST = null; + tmp187_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp187_AST); + match(LITERAL_throws); + nls(); + identifier(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop268: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + identifier(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop268; + } + + } while (true); + } + throwsClause_AST = (AST)currentAST.root; + returnAST = throwsClause_AST; + } + +/** I've split out constructors separately; we could maybe integrate back into variableDefinitions + * later on if we maybe simplified 'def' to be a type declaration? + */ + public final void constructorDefinition( + AST mods + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST constructorDefinition_AST = null; + Token id = null; + AST id_AST = null; + AST param_AST = null; + AST tc_AST = null; + AST cb_AST = null; + Token first = cloneToken(LT(1)); + if (mods != null) { + first.setLine(mods.getLine()); + first.setColumn(mods.getColumn()); + } + + id = LT(1); + id_AST = astFactory.create(id); + astFactory.addASTChild(currentAST, id_AST); + match(IDENT); + match(LPAREN); + parameterDeclarationList(); + param_AST = (AST)returnAST; + match(RPAREN); + { + boolean synPredMatched257 = false; + if (((LA(1)==LITERAL_throws||LA(1)==NLS) && (_tokenSet_29.member(LA(2))))) { + int _m257 = mark(); + synPredMatched257 = true; + inputState.guessing++; + try { + { + nls(); + match(LITERAL_throws); + } + } + catch (RecognitionException pe) { + synPredMatched257 = false; + } + rewind(_m257); +inputState.guessing--; + } + if ( synPredMatched257 ) { + throwsClause(); + tc_AST = (AST)returnAST; + } + else if ((LA(1)==LCURLY||LA(1)==NLS) && (_tokenSet_70.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + nlsWarn(); + if ( inputState.guessing==0 ) { + isConstructorIdent(id); + } + constructorBody(); + cb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + constructorDefinition_AST = (AST)currentAST.root; + constructorDefinition_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(CTOR_IDENT,"CTOR_IDENT",first,LT(1))).add(mods).add(param_AST).add(tc_AST).add(cb_AST)); + + currentAST.root = constructorDefinition_AST; + currentAST.child = constructorDefinition_AST!=null &&constructorDefinition_AST.getFirstChild()!=null ? + constructorDefinition_AST.getFirstChild() : constructorDefinition_AST; + currentAST.advanceChildToEnd(); + } + constructorDefinition_AST = (AST)currentAST.root; + returnAST = constructorDefinition_AST; + } + + public final void multipleAssignmentDeclarationStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST multipleAssignmentDeclarationStart_AST = null; + + { + _loop232: + do { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + { + modifier(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case AT: + { + annotation(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + break _loop232; + } + } + } while (true); + } + AST tmp191_AST = null; + tmp191_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp191_AST); + match(LITERAL_def); + nls(); + astFactory.addASTChild(currentAST, returnAST); + AST tmp192_AST = null; + tmp192_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp192_AST); + match(LPAREN); + multipleAssignmentDeclarationStart_AST = (AST)currentAST.root; + returnAST = multipleAssignmentDeclarationStart_AST; + } + + public final void multipleAssignmentDeclaration() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST multipleAssignmentDeclaration_AST = null; + AST mods_AST = null; + AST t_AST = null; + Token first = cloneToken(LT(1)); + + modifiers(); + mods_AST = (AST)returnAST; + { + switch ( LA(1)) { + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + typeSpec(false); + t_AST = (AST)returnAST; + break; + } + case LPAREN: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + AST tmp193_AST = null; + tmp193_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp193_AST); + match(LPAREN); + nls(); + typeNamePairs(mods_AST,first); + astFactory.addASTChild(currentAST, returnAST); + match(RPAREN); + AST tmp195_AST = null; + tmp195_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp195_AST); + match(ASSIGN); + nls(); + { + boolean synPredMatched244 = false; + if (((LA(1)==LPAREN) && (LA(2)==IDENT||LA(2)==NLS))) { + int _m244 = mark(); + synPredMatched244 = true; + inputState.guessing++; + try { + { + match(LPAREN); + nls(); + match(IDENT); + { + _loop243: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + match(IDENT); + } + else { + break _loop243; + } + + } while (true); + } + match(RPAREN); + match(ASSIGN); + } + } + catch (RecognitionException pe) { + synPredMatched244 = false; + } + rewind(_m244); +inputState.guessing--; + } + if ( synPredMatched244 ) { + multipleAssignment(0); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + assignmentExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + multipleAssignmentDeclaration_AST = (AST)currentAST.root; + multipleAssignmentDeclaration_AST=(AST)astFactory.make( (new ASTArray(4)).add(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1))).add(mods_AST).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t_AST))).add(multipleAssignmentDeclaration_AST)); + currentAST.root = multipleAssignmentDeclaration_AST; + currentAST.child = multipleAssignmentDeclaration_AST!=null &&multipleAssignmentDeclaration_AST.getFirstChild()!=null ? + multipleAssignmentDeclaration_AST.getFirstChild() : multipleAssignmentDeclaration_AST; + currentAST.advanceChildToEnd(); + } + multipleAssignmentDeclaration_AST = (AST)currentAST.root; + returnAST = multipleAssignmentDeclaration_AST; + } + + public final void constructorBody() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST constructorBody_AST = null; + AST eci_AST = null; + AST bb1_AST = null; + AST bb2_AST = null; + Token first = LT(1); + + match(LCURLY); + nls(); + { + boolean synPredMatched222 = false; + if (((_tokenSet_71.member(LA(1))) && (_tokenSet_72.member(LA(2))))) { + int _m222 = mark(); + synPredMatched222 = true; + inputState.guessing++; + try { + { + explicitConstructorInvocation(); + } + } + catch (RecognitionException pe) { + synPredMatched222 = false; + } + rewind(_m222); +inputState.guessing--; + } + if ( synPredMatched222 ) { + explicitConstructorInvocation(); + eci_AST = (AST)returnAST; + { + switch ( LA(1)) { + case SEMI: + case NLS: + { + sep(); + blockBody(sepToken); + bb1_AST = (AST)returnAST; + break; + } + case RCURLY: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else if ((_tokenSet_31.member(LA(1))) && (_tokenSet_73.member(LA(2)))) { + blockBody(EOF); + bb2_AST = (AST)returnAST; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RCURLY); + if ( inputState.guessing==0 ) { + constructorBody_AST = (AST)currentAST.root; + if (eci_AST != null) + constructorBody_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(SLIST,"{",first,LT(1))).add(eci_AST).add(bb1_AST)); + else + constructorBody_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(SLIST,"{",first,LT(1))).add(bb2_AST)); + currentAST.root = constructorBody_AST; + currentAST.child = constructorBody_AST!=null &&constructorBody_AST.getFirstChild()!=null ? + constructorBody_AST.getFirstChild() : constructorBody_AST; + currentAST.advanceChildToEnd(); + } + constructorBody_AST = (AST)currentAST.root; + returnAST = constructorBody_AST; + } + +/** Catch obvious constructor calls, but not the expr.super(...) calls */ + public final void explicitConstructorInvocation() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST explicitConstructorInvocation_AST = null; + Token lp1 = null; + AST lp1_AST = null; + Token lp2 = null; + AST lp2_AST = null; + + { + switch ( LA(1)) { + case LT: + { + typeArguments(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case LITERAL_super: + case LITERAL_this: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case LITERAL_this: + { + match(LITERAL_this); + lp1 = LT(1); + lp1_AST = astFactory.create(lp1); + astFactory.makeASTRoot(currentAST, lp1_AST); + match(LPAREN); + argList(); + astFactory.addASTChild(currentAST, returnAST); + match(RPAREN); + if ( inputState.guessing==0 ) { + lp1_AST.setType(CTOR_CALL); + } + break; + } + case LITERAL_super: + { + match(LITERAL_super); + lp2 = LT(1); + lp2_AST = astFactory.create(lp2); + astFactory.makeASTRoot(currentAST, lp2_AST); + match(LPAREN); + argList(); + astFactory.addASTChild(currentAST, returnAST); + match(RPAREN); + if ( inputState.guessing==0 ) { + lp2_AST.setType(SUPER_CTOR_CALL); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + explicitConstructorInvocation_AST = (AST)currentAST.root; + returnAST = explicitConstructorInvocation_AST; + } + + public final void listOfVariables( + AST mods, AST t, Token first + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST listOfVariables_AST = null; + + variableDeclarator(getASTFactory().dupTree(mods), + getASTFactory().dupTree(t),first); + astFactory.addASTChild(currentAST, returnAST); + { + _loop229: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + if ( inputState.guessing==0 ) { + first = LT(1); + } + variableDeclarator(getASTFactory().dupTree(mods), + getASTFactory().dupTree(t),first); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop229; + } + + } while (true); + } + listOfVariables_AST = (AST)currentAST.root; + returnAST = listOfVariables_AST; + } + +/** Declaration of a variable. This can be a class/instance variable, + * or a local variable in a method + * It can also include possible initialization. + */ + public final void variableDeclarator( + AST mods, AST t,Token first + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST variableDeclarator_AST = null; + AST id_AST = null; + AST v_AST = null; + + variableName(); + id_AST = (AST)returnAST; + { + switch ( LA(1)) { + case ASSIGN: + { + varInitializer(); + v_AST = (AST)returnAST; + break; + } + case EOF: + case COMMA: + case RPAREN: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + variableDeclarator_AST = (AST)currentAST.root; + variableDeclarator_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1))).add(mods).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t))).add(id_AST).add(v_AST)); + currentAST.root = variableDeclarator_AST; + currentAST.child = variableDeclarator_AST!=null &&variableDeclarator_AST.getFirstChild()!=null ? + variableDeclarator_AST.getFirstChild() : variableDeclarator_AST; + currentAST.advanceChildToEnd(); + } + returnAST = variableDeclarator_AST; + } + + public final void typeNamePairs( + AST mods, Token first + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST typeNamePairs_AST = null; + AST t_AST = null; + AST tn_AST = null; + + { + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_32.member(LA(2)))) { + typeSpec(false); + t_AST = (AST)returnAST; + } + else if ((LA(1)==IDENT) && (LA(2)==COMMA||LA(2)==RPAREN)) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + singleVariable(getASTFactory().dupTree(mods),t_AST); + astFactory.addASTChild(currentAST, returnAST); + { + _loop237: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + if ( inputState.guessing==0 ) { + first = LT(1); + } + { + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_32.member(LA(2)))) { + typeSpec(false); + tn_AST = (AST)returnAST; + } + else if ((LA(1)==IDENT) && (LA(2)==COMMA||LA(2)==RPAREN)) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + singleVariable(getASTFactory().dupTree(mods),tn_AST); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop237; + } + + } while (true); + } + typeNamePairs_AST = (AST)currentAST.root; + returnAST = typeNamePairs_AST; + } + + public final void multipleAssignment( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST multipleAssignment_AST = null; + Token first = cloneToken(LT(1)); + + AST tmp204_AST = null; + tmp204_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp204_AST); + match(LPAREN); + nls(); + listOfVariables(null,null,first); + astFactory.addASTChild(currentAST, returnAST); + match(RPAREN); + AST tmp206_AST = null; + tmp206_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp206_AST); + match(ASSIGN); + nls(); + { + boolean synPredMatched419 = false; + if (((LA(1)==LPAREN) && (LA(2)==IDENT||LA(2)==NLS))) { + int _m419 = mark(); + synPredMatched419 = true; + inputState.guessing++; + try { + { + match(LPAREN); + nls(); + match(IDENT); + { + _loop418: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + match(IDENT); + } + else { + break _loop418; + } + + } while (true); + } + match(RPAREN); + match(ASSIGN); + } + } + catch (RecognitionException pe) { + synPredMatched419 = false; + } + rewind(_m419); +inputState.guessing--; + } + if ( synPredMatched419 ) { + multipleAssignment(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + assignmentExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + multipleAssignment_AST = (AST)currentAST.root; + returnAST = multipleAssignment_AST; + } + + public final void assignmentExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST assignmentExpression_AST = null; + + conditionalExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + switch ( LA(1)) { + case ASSIGN: + case PLUS_ASSIGN: + case MINUS_ASSIGN: + case STAR_ASSIGN: + case DIV_ASSIGN: + case MOD_ASSIGN: + case SR_ASSIGN: + case BSR_ASSIGN: + case SL_ASSIGN: + case BAND_ASSIGN: + case BXOR_ASSIGN: + case BOR_ASSIGN: + case STAR_STAR_ASSIGN: + { + { + switch ( LA(1)) { + case ASSIGN: + { + AST tmp207_AST = null; + tmp207_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp207_AST); + match(ASSIGN); + break; + } + case PLUS_ASSIGN: + { + AST tmp208_AST = null; + tmp208_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp208_AST); + match(PLUS_ASSIGN); + break; + } + case MINUS_ASSIGN: + { + AST tmp209_AST = null; + tmp209_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp209_AST); + match(MINUS_ASSIGN); + break; + } + case STAR_ASSIGN: + { + AST tmp210_AST = null; + tmp210_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp210_AST); + match(STAR_ASSIGN); + break; + } + case DIV_ASSIGN: + { + AST tmp211_AST = null; + tmp211_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp211_AST); + match(DIV_ASSIGN); + break; + } + case MOD_ASSIGN: + { + AST tmp212_AST = null; + tmp212_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp212_AST); + match(MOD_ASSIGN); + break; + } + case SR_ASSIGN: + { + AST tmp213_AST = null; + tmp213_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp213_AST); + match(SR_ASSIGN); + break; + } + case BSR_ASSIGN: + { + AST tmp214_AST = null; + tmp214_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp214_AST); + match(BSR_ASSIGN); + break; + } + case SL_ASSIGN: + { + AST tmp215_AST = null; + tmp215_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp215_AST); + match(SL_ASSIGN); + break; + } + case BAND_ASSIGN: + { + AST tmp216_AST = null; + tmp216_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp216_AST); + match(BAND_ASSIGN); + break; + } + case BXOR_ASSIGN: + { + AST tmp217_AST = null; + tmp217_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp217_AST); + match(BXOR_ASSIGN); + break; + } + case BOR_ASSIGN: + { + AST tmp218_AST = null; + tmp218_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp218_AST); + match(BOR_ASSIGN); + break; + } + case STAR_STAR_ASSIGN: + { + AST tmp219_AST = null; + tmp219_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp219_AST); + match(STAR_STAR_ASSIGN); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + expressionStatementNoCheck(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case RBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case LITERAL_extends: + case LITERAL_super: + case COMMA: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case RPAREN: + case LCURLY: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case CLOSABLE_BLOCK_OP: + case COLON: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + assignmentExpression_AST = (AST)currentAST.root; + returnAST = assignmentExpression_AST; + } + +/** Zero or more insignificant newlines, all gobbled up and thrown away, + * but a warning message is left for the user, if there was a newline. + */ + public final void nlsWarn() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST nlsWarn_AST = null; + + { + boolean synPredMatched610 = false; + if (((_tokenSet_74.member(LA(1))) && (_tokenSet_2.member(LA(2))))) { + int _m610 = mark(); + synPredMatched610 = true; + inputState.guessing++; + try { + { + match(NLS); + } + } + catch (RecognitionException pe) { + synPredMatched610 = false; + } + rewind(_m610); +inputState.guessing--; + } + if ( synPredMatched610 ) { + if ( inputState.guessing==0 ) { + addWarning( + "A newline at this point does not follow the Groovy Coding Conventions.", + "Keep this statement on one line, or use curly braces to break across multiple lines." + ); + } + } + else if ((_tokenSet_74.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + nls(); + returnAST = nlsWarn_AST; + } + +/** An open block is not allowed to have closure arguments. */ + public final void openBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST openBlock_AST = null; + AST bb_AST = null; + Token first = LT(1); int start = mark(); + + try { // for error handling + match(LCURLY); + nls(); + blockBody(EOF); + bb_AST = (AST)returnAST; + match(RCURLY); + if ( inputState.guessing==0 ) { + openBlock_AST = (AST)currentAST.root; + openBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(SLIST,"{",first,LT(1))).add(bb_AST)); + currentAST.root = openBlock_AST; + currentAST.child = openBlock_AST!=null &&openBlock_AST.getFirstChild()!=null ? + openBlock_AST.getFirstChild() : openBlock_AST; + currentAST.advanceChildToEnd(); + } + openBlock_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + int end = mark(); + // rewind to the first token on the same line as opening '{' (aka first) + rewind(start); + while (LT(0) != null && LT(0).getLine() == first.getLine()) { + rewind(mark() - 1); + } + // advance through all tokens that have greater indentation + int col = LT(1).getColumn(); + do { + consume(); + } while (LT(1).getColumn() > col && LT(1).getType() != EOF); // TODO: skip 'case', 'default', comments? and statement labels -- they may be in same column as first token + + // if a closing '}' was found in the proper position, create a basic block + if (LT(1).getColumn() == col && LT(1).getType() == RCURLY) { + match(RCURLY); + reportError(e); + openBlock_AST = (AST)astFactory.make( (new ASTArray(1)).add(create(SLIST,"{",first,LT(1)))); + } else { + rewind(end); + throw e; + } + + } else { + throw e; + } + } + returnAST = openBlock_AST; + } + + public final void variableName() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST variableName_AST = null; + + AST tmp222_AST = null; + tmp222_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp222_AST); + match(IDENT); + variableName_AST = (AST)currentAST.root; + returnAST = variableName_AST; + } + + public final void expressionStatementNoCheck() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST expressionStatementNoCheck_AST = null; + AST head_AST = null; + AST cmd_AST = null; + boolean isPathExpr = true; + + expression(LC_STMT); + head_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + isPathExpr = (head_AST == lastPathExpression); + } + { + if (((_tokenSet_75.member(LA(1))) && (_tokenSet_54.member(LA(2))))&&(LA(1)!=LITERAL_else && isPathExpr /*&& #head.getType()==METHOD_CALL*/)) { + commandArgumentsGreedy(head_AST); + cmd_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + expressionStatementNoCheck_AST = (AST)currentAST.root; + + expressionStatementNoCheck_AST = cmd_AST; + + currentAST.root = expressionStatementNoCheck_AST; + currentAST.child = expressionStatementNoCheck_AST!=null &&expressionStatementNoCheck_AST.getFirstChild()!=null ? + expressionStatementNoCheck_AST.getFirstChild() : expressionStatementNoCheck_AST; + currentAST.advanceChildToEnd(); + } + } + else if ((_tokenSet_75.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + expressionStatementNoCheck_AST = (AST)currentAST.root; + returnAST = expressionStatementNoCheck_AST; + } + +/** A formal parameter for a method or closure. */ + public final void parameterDeclaration() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST parameterDeclaration_AST = null; + AST pm_AST = null; + AST t_AST = null; + Token id = null; + AST id_AST = null; + AST exp_AST = null; + Token first = LT(1);boolean spreadParam = false; + + parameterModifiersOpt(); + pm_AST = (AST)returnAST; + { + if ((_tokenSet_25.member(LA(1))) && (_tokenSet_76.member(LA(2)))) { + typeSpec(false); + t_AST = (AST)returnAST; + } + else if ((LA(1)==IDENT||LA(1)==TRIPLE_DOT) && (_tokenSet_77.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + switch ( LA(1)) { + case TRIPLE_DOT: + { + match(TRIPLE_DOT); + if ( inputState.guessing==0 ) { + spreadParam = true; + } + break; + } + case IDENT: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + id = LT(1); + id_AST = astFactory.create(id); + match(IDENT); + { + switch ( LA(1)) { + case ASSIGN: + { + varInitializer(); + exp_AST = (AST)returnAST; + break; + } + case COMMA: + case RPAREN: + case CLOSABLE_BLOCK_OP: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + parameterDeclaration_AST = (AST)currentAST.root; + + if (spreadParam) { + parameterDeclaration_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1))).add(pm_AST).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t_AST))).add(id_AST).add(exp_AST)); + } else { + parameterDeclaration_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1))).add(pm_AST).add((AST)astFactory.make( (new ASTArray(2)).add(create(TYPE,"TYPE",first,LT(1))).add(t_AST))).add(id_AST).add(exp_AST)); + } + + currentAST.root = parameterDeclaration_AST; + currentAST.child = parameterDeclaration_AST!=null &¶meterDeclaration_AST.getFirstChild()!=null ? + parameterDeclaration_AST.getFirstChild() : parameterDeclaration_AST; + currentAST.advanceChildToEnd(); + } + returnAST = parameterDeclaration_AST; + } + + public final void parameterModifiersOpt() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST parameterModifiersOpt_AST = null; + Token first = LT(1);int seenDef = 0; + + { + _loop286: + do { + switch ( LA(1)) { + case FINAL: + { + AST tmp224_AST = null; + tmp224_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp224_AST); + match(FINAL); + nls(); + break; + } + case AT: + { + annotation(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + break; + } + default: + if (((LA(1)==LITERAL_def))&&(seenDef++ == 0)) { + match(LITERAL_def); + nls(); + } + else { + break _loop286; + } + } + } while (true); + } + if ( inputState.guessing==0 ) { + parameterModifiersOpt_AST = (AST)currentAST.root; + parameterModifiersOpt_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(MODIFIERS,"MODIFIERS",first,LT(1))).add(parameterModifiersOpt_AST)); + currentAST.root = parameterModifiersOpt_AST; + currentAST.child = parameterModifiersOpt_AST!=null &¶meterModifiersOpt_AST.getFirstChild()!=null ? + parameterModifiersOpt_AST.getFirstChild() : parameterModifiersOpt_AST; + currentAST.advanceChildToEnd(); + } + parameterModifiersOpt_AST = (AST)currentAST.root; + returnAST = parameterModifiersOpt_AST; + } + + public final void multicatch_types() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST multicatch_types_AST = null; + Token first = LT(1); + + nls(); + classOrInterfaceType(false); + astFactory.addASTChild(currentAST, returnAST); + { + _loop279: + do { + if ((LA(1)==BOR)) { + match(BOR); + nls(); + classOrInterfaceType(false); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop279; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + multicatch_types_AST = (AST)currentAST.root; + multicatch_types_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(MULTICATCH_TYPES,"MULTICATCH_TYPES",first,LT(1))).add(multicatch_types_AST)); + currentAST.root = multicatch_types_AST; + currentAST.child = multicatch_types_AST!=null &&multicatch_types_AST.getFirstChild()!=null ? + multicatch_types_AST.getFirstChild() : multicatch_types_AST; + currentAST.advanceChildToEnd(); + } + multicatch_types_AST = (AST)currentAST.root; + returnAST = multicatch_types_AST; + } + + public final void multicatch() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST multicatch_AST = null; + AST m_AST = null; + Token id = null; + AST id_AST = null; + Token first = LT(1); + + try { // for error handling + nls(); + { + switch ( LA(1)) { + case FINAL: + { + AST tmp227_AST = null; + tmp227_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp227_AST); + match(FINAL); + break; + } + case LITERAL_def: + case IDENT: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case LITERAL_def: + { + AST tmp228_AST = null; + tmp228_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp228_AST); + match(LITERAL_def); + break; + } + case IDENT: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + if ((LA(1)==IDENT||LA(1)==NLS) && (_tokenSet_78.member(LA(2)))) { + multicatch_types(); + m_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + } + else if ((LA(1)==IDENT) && (LA(2)==RPAREN)) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + id = LT(1); + id_AST = astFactory.create(id); + match(IDENT); + if ( inputState.guessing==0 ) { + multicatch_AST = (AST)currentAST.root; + + multicatch_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(MULTICATCH,"MULTICATCH",first,LT(1))).add(m_AST).add(id_AST)); + + currentAST.root = multicatch_AST; + currentAST.child = multicatch_AST!=null &&multicatch_AST.getFirstChild()!=null ? + multicatch_AST.getFirstChild() : multicatch_AST; + currentAST.advanceChildToEnd(); + } + multicatch_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + if (m_AST != null && LT(1).getType() == RPAREN) { + reportError(e); + id_AST = missingIdentifier(first, LT(1)); + multicatch_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(MULTICATCH,"MULTICATCH",first,LT(1))).add(m_AST).add(id_AST)); + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = multicatch_AST; + } + +/** Closure parameters are exactly like method parameters, + * except that they are not enclosed in parentheses, but rather + * are prepended to the front of a block, just after the brace. + * They are separated from the closure body by a CLOSABLE_BLOCK_OP token '->'. + */ + public final void closableBlockParamsOpt( + boolean addImplicit + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST closableBlockParamsOpt_AST = null; + + boolean synPredMatched289 = false; + if (((_tokenSet_79.member(LA(1))) && (_tokenSet_80.member(LA(2))))) { + int _m289 = mark(); + synPredMatched289 = true; + inputState.guessing++; + try { + { + parameterDeclarationList(); + nls(); + match(CLOSABLE_BLOCK_OP); + } + } + catch (RecognitionException pe) { + synPredMatched289 = false; + } + rewind(_m289); +inputState.guessing--; + } + if ( synPredMatched289 ) { + parameterDeclarationList(); + astFactory.addASTChild(currentAST, returnAST); + nls(); + match(CLOSABLE_BLOCK_OP); + nls(); + closableBlockParamsOpt_AST = (AST)currentAST.root; + } + else if (((_tokenSet_31.member(LA(1))) && (_tokenSet_54.member(LA(2))))&&(addImplicit)) { + implicitParameters(); + astFactory.addASTChild(currentAST, returnAST); + closableBlockParamsOpt_AST = (AST)currentAST.root; + } + else if ((_tokenSet_31.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + closableBlockParamsOpt_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = closableBlockParamsOpt_AST; + } + +/** A block known to be a closure, but which omits its arguments, is given this placeholder. + * A subsequent pass is responsible for deciding if there is an implicit 'it' parameter, + * or if the parameter list should be empty. + */ + public final void implicitParameters() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST implicitParameters_AST = null; + Token first = LT(1); + + if ( inputState.guessing==0 ) { + implicitParameters_AST = (AST)currentAST.root; + implicitParameters_AST = (AST)astFactory.make( (new ASTArray(1)).add(create(IMPLICIT_PARAMETERS,"IMPLICIT_PARAMETERS",first,LT(1)))); + currentAST.root = implicitParameters_AST; + currentAST.child = implicitParameters_AST!=null &&implicitParameters_AST.getFirstChild()!=null ? + implicitParameters_AST.getFirstChild() : implicitParameters_AST; + currentAST.advanceChildToEnd(); + } + implicitParameters_AST = (AST)currentAST.root; + returnAST = implicitParameters_AST; + } + +/** Lookahead to check whether a block begins with explicit closure arguments. */ + public final void closableBlockParamsStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST closableBlockParamsStart_AST = null; + + nls(); + parameterDeclarationList(); + nls(); + AST tmp230_AST = null; + tmp230_AST = astFactory.create(LT(1)); + match(CLOSABLE_BLOCK_OP); + returnAST = closableBlockParamsStart_AST; + } + +/** Simple names, as in {x|...}, are completely equivalent to {(def x)|...}. Build the right AST. */ + public final void closableBlockParam() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST closableBlockParam_AST = null; + Token id = null; + AST id_AST = null; + Token first = LT(1); + + id = LT(1); + id_AST = astFactory.create(id); + match(IDENT); + if ( inputState.guessing==0 ) { + closableBlockParam_AST = (AST)currentAST.root; + closableBlockParam_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1))).add((AST)astFactory.make( (new ASTArray(1)).add(create(MODIFIERS,"MODIFIERS",first,LT(1))))).add((AST)astFactory.make( (new ASTArray(1)).add(create(TYPE,"TYPE",first,LT(1))))).add(id_AST)); + currentAST.root = closableBlockParam_AST; + currentAST.child = closableBlockParam_AST!=null &&closableBlockParam_AST.getFirstChild()!=null ? + closableBlockParam_AST.getFirstChild() : closableBlockParam_AST; + currentAST.advanceChildToEnd(); + } + returnAST = closableBlockParam_AST; + } + +/** A block which is known to be a closure, even if it has no apparent arguments. + * A block inside an expression or after a method call is always assumed to be a closure. + * Only labeled, unparameterized blocks which occur directly as substatements are kept open. + */ + public final void closableBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST closableBlock_AST = null; + AST cbp_AST = null; + AST bb_AST = null; + Token first = LT(1); + + match(LCURLY); + nls(); + closableBlockParamsOpt(true); + cbp_AST = (AST)returnAST; + blockBody(EOF); + bb_AST = (AST)returnAST; + match(RCURLY); + if ( inputState.guessing==0 ) { + closableBlock_AST = (AST)currentAST.root; + closableBlock_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(CLOSABLE_BLOCK,"{",first,LT(1))).add(cbp_AST).add(bb_AST)); + currentAST.root = closableBlock_AST; + currentAST.child = closableBlock_AST!=null &&closableBlock_AST.getFirstChild()!=null ? + closableBlock_AST.getFirstChild() : closableBlock_AST; + currentAST.advanceChildToEnd(); + } + closableBlock_AST = (AST)currentAST.root; + returnAST = closableBlock_AST; + } + +/** A sub-block of a block can be either open or closable. + * It is closable if and only if there are explicit closure arguments. + * Compare this to a block which is appended to a method call, + * which is given closure arguments, even if they are not explicit in the code. + */ + public final void openOrClosableBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST openOrClosableBlock_AST = null; + AST cp_AST = null; + AST bb_AST = null; + Token first = LT(1); + + match(LCURLY); + nls(); + closableBlockParamsOpt(false); + cp_AST = (AST)returnAST; + blockBody(EOF); + bb_AST = (AST)returnAST; + match(RCURLY); + if ( inputState.guessing==0 ) { + openOrClosableBlock_AST = (AST)currentAST.root; + + if (cp_AST == null) openOrClosableBlock_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(SLIST,"{",first,LT(1))).add(bb_AST)); + else openOrClosableBlock_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(CLOSABLE_BLOCK,"{",first,LT(1))).add(cp_AST).add(bb_AST)); + + currentAST.root = openOrClosableBlock_AST; + currentAST.child = openOrClosableBlock_AST!=null &&openOrClosableBlock_AST.getFirstChild()!=null ? + openOrClosableBlock_AST.getFirstChild() : openOrClosableBlock_AST; + currentAST.advanceChildToEnd(); + } + openOrClosableBlock_AST = (AST)currentAST.root; + returnAST = openOrClosableBlock_AST; + } + +/** A labeled statement, consisting of a vanilla identifier followed by a colon. */ + public final void statementLabelPrefix() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST statementLabelPrefix_AST = null; + Token c = null; + AST c_AST = null; + + AST tmp235_AST = null; + tmp235_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp235_AST); + match(IDENT); + c = LT(1); + c_AST = astFactory.create(c); + astFactory.makeASTRoot(currentAST, c_AST); + match(COLON); + if ( inputState.guessing==0 ) { + c_AST.setType(LABELED_STAT); + } + nls(); + statementLabelPrefix_AST = (AST)currentAST.root; + returnAST = statementLabelPrefix_AST; + } + +/** An expression statement can be any general expression. + *

+ * An expression statement can also be a command, + * which is a simple method call in which the outermost parentheses are omitted. + *

+ * Certain "suspicious" looking forms are flagged for the user to disambiguate. + */ + public final void expressionStatement( + int prevToken + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST expressionStatement_AST = null; + AST esn_AST = null; + Token first = LT(1); + + { + boolean synPredMatched355 = false; + if (((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2))))) { + int _m355 = mark(); + synPredMatched355 = true; + inputState.guessing++; + try { + { + suspiciousExpressionStatementStart(); + } + } + catch (RecognitionException pe) { + synPredMatched355 = false; + } + rewind(_m355); +inputState.guessing--; + } + if ( synPredMatched355 ) { + checkSuspiciousExpressionStatement(prevToken); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + expressionStatementNoCheck(); + esn_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + expressionStatement_AST = (AST)currentAST.root; + expressionStatement_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXPR,"EXPR",first,LT(1))).add(esn_AST)); + currentAST.root = expressionStatement_AST; + currentAST.child = expressionStatement_AST!=null &&expressionStatement_AST.getFirstChild()!=null ? + expressionStatement_AST.getFirstChild() : expressionStatement_AST; + currentAST.advanceChildToEnd(); + } + expressionStatement_AST = (AST)currentAST.root; + returnAST = expressionStatement_AST; + } + + public final void assignmentLessExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST assignmentLessExpression_AST = null; + Token first = LT(1); + + { + conditionalExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + if ( inputState.guessing==0 ) { + assignmentLessExpression_AST = (AST)currentAST.root; + assignmentLessExpression_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXPR,"EXPR",first,LT(1))).add(assignmentLessExpression_AST)); + currentAST.root = assignmentLessExpression_AST; + currentAST.child = assignmentLessExpression_AST!=null &&assignmentLessExpression_AST.getFirstChild()!=null ? + assignmentLessExpression_AST.getFirstChild() : assignmentLessExpression_AST; + currentAST.advanceChildToEnd(); + } + assignmentLessExpression_AST = (AST)currentAST.root; + returnAST = assignmentLessExpression_AST; + } + +/** In Java, "if", "while", and "for" statements can take random, non-braced statements as their bodies. + * Support this practice, even though it isn't very Groovy. + */ + public final void compatibleBodyStatement() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST compatibleBodyStatement_AST = null; + AST de_AST = null; + Token first = LT(1); + + try { // for error handling + boolean synPredMatched341 = false; + if (((LA(1)==LCURLY) && (_tokenSet_31.member(LA(2))))) { + int _m341 = mark(); + synPredMatched341 = true; + inputState.guessing++; + try { + { + match(LCURLY); + } + } + catch (RecognitionException pe) { + synPredMatched341 = false; + } + rewind(_m341); +inputState.guessing--; + } + if ( synPredMatched341 ) { + compoundStatement(); + astFactory.addASTChild(currentAST, returnAST); + compatibleBodyStatement_AST = (AST)currentAST.root; + } + else { + boolean synPredMatched344 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_17.member(LA(2))))) { + int _m344 = mark(); + synPredMatched344 = true; + inputState.guessing++; + try { + { + declarationStart(); + { + switch ( LA(1)) { + case ASSIGN: + { + varInitializer(); + break; + } + case COMMA: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(COMMA); + } + } + catch (RecognitionException pe) { + synPredMatched344 = false; + } + rewind(_m344); +inputState.guessing--; + } + if ( synPredMatched344 ) { + declaration(); + de_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + compatibleBodyStatement_AST = (AST)currentAST.root; + compatibleBodyStatement_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(SLIST,"CBSLIST",first,LT(1))).add(de_AST)); + currentAST.root = compatibleBodyStatement_AST; + currentAST.child = compatibleBodyStatement_AST!=null &&compatibleBodyStatement_AST.getFirstChild()!=null ? + compatibleBodyStatement_AST.getFirstChild() : compatibleBodyStatement_AST; + currentAST.advanceChildToEnd(); + } + compatibleBodyStatement_AST = (AST)currentAST.root; + } + else if ((_tokenSet_19.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + statement(EOF); + astFactory.addASTChild(currentAST, returnAST); + compatibleBodyStatement_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // GRECLIPSE-1046 + reportError(e); + + } else { + throw e; + } + } + returnAST = compatibleBodyStatement_AST; + } + + public final void forStatement() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST forStatement_AST = null; + AST cl_AST = null; + AST fic_AST = null; + Token s = null; + AST s_AST = null; + AST forCbs_AST = null; + Token first = LT(1); + + match(LITERAL_for); + match(LPAREN); + { + boolean synPredMatched328 = false; + if (((_tokenSet_81.member(LA(1))) && (_tokenSet_82.member(LA(2))))) { + int _m328 = mark(); + synPredMatched328 = true; + inputState.guessing++; + try { + { + switch ( LA(1)) { + case SEMI: + { + match(SEMI); + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + { + strictContextExpression(true); + match(SEMI); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + catch (RecognitionException pe) { + synPredMatched328 = false; + } + rewind(_m328); +inputState.guessing--; + } + if ( synPredMatched328 ) { + closureList(); + cl_AST = (AST)returnAST; + } + else if ((_tokenSet_16.member(LA(1))) && (_tokenSet_83.member(LA(2)))) { + forInClause(); + fic_AST = (AST)returnAST; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + match(RPAREN); + nls(); + { + switch ( LA(1)) { + case SEMI: + { + s = LT(1); + s_AST = astFactory.create(s); + match(SEMI); + break; + } + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + compatibleBodyStatement(); + forCbs_AST = (AST)returnAST; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + forStatement_AST = (AST)currentAST.root; + + if (cl_AST != null) { + if (s_AST != null) + forStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_for,"for",first,LT(1))).add(cl_AST).add(s_AST)); + else + forStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_for,"for",first,LT(1))).add(cl_AST).add(forCbs_AST)); + } else { + if (s_AST != null) + forStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_for,"for",first,LT(1))).add(fic_AST).add(s_AST)); + else + forStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_for,"for",first,LT(1))).add(fic_AST).add(forCbs_AST)); + } + + currentAST.root = forStatement_AST; + currentAST.child = forStatement_AST!=null &&forStatement_AST.getFirstChild()!=null ? + forStatement_AST.getFirstChild() : forStatement_AST; + currentAST.advanceChildToEnd(); + } + forStatement_AST = (AST)currentAST.root; + returnAST = forStatement_AST; + } + +/** Things that can show up as expressions, but only in strict + * contexts like inside parentheses, argument lists, and list constructors. + */ + public final boolean strictContextExpression( + boolean allowDeclaration + ) throws RecognitionException, TokenStreamException { + boolean hasDeclaration=false; + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST strictContextExpression_AST = null; + Token first = LT(1); + + { + boolean synPredMatched544 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_84.member(LA(2))))) { + int _m544 = mark(); + synPredMatched544 = true; + inputState.guessing++; + try { + { + if (!(allowDeclaration)) + throw new SemanticException("allowDeclaration"); + declarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched544 = false; + } + rewind(_m544); +inputState.guessing--; + } + if ( synPredMatched544 ) { + if ( inputState.guessing==0 ) { + hasDeclaration=true; + } + singleDeclaration(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + expression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else if (((LA(1) >= LITERAL_return && LA(1) <= LITERAL_assert))) { + branchStatement(); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((LA(1)==AT) && (_tokenSet_85.member(LA(2)))) { + annotation(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + strictContextExpression_AST = (AST)currentAST.root; + strictContextExpression_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXPR,"EXPR",first,LT(1))).add(strictContextExpression_AST)); + currentAST.root = strictContextExpression_AST; + currentAST.child = strictContextExpression_AST!=null &&strictContextExpression_AST.getFirstChild()!=null ? + strictContextExpression_AST.getFirstChild() : strictContextExpression_AST; + currentAST.advanceChildToEnd(); + } + strictContextExpression_AST = (AST)currentAST.root; + returnAST = strictContextExpression_AST; + return hasDeclaration; + } + + public final void casesGroup() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST casesGroup_AST = null; + Token first = LT(1); + + { + int _cnt368=0; + _loop368: + do { + if ((LA(1)==LITERAL_default||LA(1)==LITERAL_case)) { + aCase(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + if ( _cnt368>=1 ) { break _loop368; } else {throw new NoViableAltException(LT(1), getFilename());} + } + + _cnt368++; + } while (true); + } + caseSList(); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + casesGroup_AST = (AST)currentAST.root; + casesGroup_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(CASE_GROUP,"CASE_GROUP",first,LT(1))).add(casesGroup_AST)); + currentAST.root = casesGroup_AST; + currentAST.child = casesGroup_AST!=null &&casesGroup_AST.getFirstChild()!=null ? + casesGroup_AST.getFirstChild() : casesGroup_AST; + currentAST.advanceChildToEnd(); + } + casesGroup_AST = (AST)currentAST.root; + returnAST = casesGroup_AST; + } + + public final void tryBlock() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST tryBlock_AST = null; + AST tryCs_AST = null; + AST h_AST = null; + AST fc_AST = null; + Token first = LT(1);List catchNodes = new ArrayList();AST newHandler_AST = null; + + match(LITERAL_try); + nlsWarn(); + compoundStatement(); + tryCs_AST = (AST)returnAST; + { + _loop385: + do { + if (((LA(1)==LITERAL_catch||LA(1)==NLS) && (LA(2)==LPAREN||LA(2)==LITERAL_catch))&&(!(LA(1) == NLS && LA(2) == LPAREN))) { + nls(); + handler(); + h_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + newHandler_AST = (AST)astFactory.make( (new ASTArray(3)).add(null).add(newHandler_AST).add(h_AST)); + } + } + else { + break _loop385; + } + + } while (true); + } + { + if ((LA(1)==LITERAL_finally||LA(1)==NLS) && (_tokenSet_86.member(LA(2)))) { + nls(); + finallyClause(); + fc_AST = (AST)returnAST; + } + else if ((_tokenSet_11.member(LA(1))) && (_tokenSet_12.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + tryBlock_AST = (AST)currentAST.root; + tryBlock_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(LITERAL_try,"try",first,LT(1))).add(tryCs_AST).add(newHandler_AST).add(fc_AST)); + currentAST.root = tryBlock_AST; + currentAST.child = tryBlock_AST!=null &&tryBlock_AST.getFirstChild()!=null ? + tryBlock_AST.getFirstChild() : tryBlock_AST; + currentAST.advanceChildToEnd(); + } + tryBlock_AST = (AST)currentAST.root; + returnAST = tryBlock_AST; + } + +/** In Groovy, return, break, continue, throw, and assert can be used in a parenthesized expression context. + * Example: println (x || (return)); println assert x, "won't print a false value!" + * If an optional expression is missing, its value is void (this coerces to null when a value is required). + */ + public final void branchStatement() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST branchStatement_AST = null; + AST returnE_AST = null; + Token breakI = null; + AST breakI_AST = null; + Token contI = null; + AST contI_AST = null; + AST throwE_AST = null; + AST assertAle_AST = null; + AST assertE_AST = null; + Token first = LT(1); + + switch ( LA(1)) { + case LITERAL_return: + { + match(LITERAL_return); + { + switch ( LA(1)) { + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + expression(0); + returnE_AST = (AST)returnAST; + break; + } + case EOF: + case RBRACK: + case COMMA: + case RPAREN: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + branchStatement_AST = (AST)currentAST.root; + branchStatement_AST = (AST)astFactory.make( (new ASTArray(2)).add(create2(LITERAL_return,"return",first,LT(0))).add(returnE_AST)); + currentAST.root = branchStatement_AST; + currentAST.child = branchStatement_AST!=null &&branchStatement_AST.getFirstChild()!=null ? + branchStatement_AST.getFirstChild() : branchStatement_AST; + currentAST.advanceChildToEnd(); + } + branchStatement_AST = (AST)currentAST.root; + break; + } + case LITERAL_break: + { + match(LITERAL_break); + { + switch ( LA(1)) { + case IDENT: + { + breakI = LT(1); + breakI_AST = astFactory.create(breakI); + match(IDENT); + break; + } + case EOF: + case RBRACK: + case COMMA: + case RPAREN: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + branchStatement_AST = (AST)currentAST.root; + branchStatement_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(LITERAL_break,"break",first,LT(1))).add(breakI_AST)); + currentAST.root = branchStatement_AST; + currentAST.child = branchStatement_AST!=null &&branchStatement_AST.getFirstChild()!=null ? + branchStatement_AST.getFirstChild() : branchStatement_AST; + currentAST.advanceChildToEnd(); + } + branchStatement_AST = (AST)currentAST.root; + break; + } + case LITERAL_continue: + { + match(LITERAL_continue); + { + switch ( LA(1)) { + case IDENT: + { + contI = LT(1); + contI_AST = astFactory.create(contI); + match(IDENT); + break; + } + case EOF: + case RBRACK: + case COMMA: + case RPAREN: + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_else: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + branchStatement_AST = (AST)currentAST.root; + branchStatement_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(LITERAL_continue,"continue",first,LT(1))).add(contI_AST)); + currentAST.root = branchStatement_AST; + currentAST.child = branchStatement_AST!=null &&branchStatement_AST.getFirstChild()!=null ? + branchStatement_AST.getFirstChild() : branchStatement_AST; + currentAST.advanceChildToEnd(); + } + branchStatement_AST = (AST)currentAST.root; + break; + } + case LITERAL_throw: + { + match(LITERAL_throw); + expression(0); + throwE_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + branchStatement_AST = (AST)currentAST.root; + branchStatement_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(LITERAL_throw,"throw",first,LT(1))).add(throwE_AST)); + currentAST.root = branchStatement_AST; + currentAST.child = branchStatement_AST!=null &&branchStatement_AST.getFirstChild()!=null ? + branchStatement_AST.getFirstChild() : branchStatement_AST; + currentAST.advanceChildToEnd(); + } + branchStatement_AST = (AST)currentAST.root; + break; + } + case LITERAL_assert: + { + match(LITERAL_assert); + assignmentLessExpression(); + assertAle_AST = (AST)returnAST; + { + if ((LA(1)==COMMA||LA(1)==COLON) && (_tokenSet_87.member(LA(2)))) { + { + switch ( LA(1)) { + case COMMA: + { + match(COMMA); + nls(); + break; + } + case COLON: + { + match(COLON); + nls(); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + expression(0); + assertE_AST = (AST)returnAST; + } + else if ((_tokenSet_88.member(LA(1))) && (_tokenSet_12.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + branchStatement_AST = (AST)currentAST.root; + branchStatement_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_assert,"assert",first,LT(1))).add(assertAle_AST).add(assertE_AST)); + currentAST.root = branchStatement_AST; + currentAST.child = branchStatement_AST!=null &&branchStatement_AST.getFirstChild()!=null ? + branchStatement_AST.getFirstChild() : branchStatement_AST; + currentAST.advanceChildToEnd(); + } + branchStatement_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = branchStatement_AST; + } + + public final void closureList() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST closureList_AST = null; + Token first = LT(1); boolean sce=false; + + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + sce=strictContextExpression(true); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case SEMI: + { + if ( inputState.guessing==0 ) { + astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + int _cnt333=0; + _loop333: + do { + if ((LA(1)==SEMI) && (_tokenSet_89.member(LA(2)))) { + match(SEMI); + sce=strictContextExpression(true); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((LA(1)==SEMI) && (LA(2)==RPAREN||LA(2)==SEMI)) { + match(SEMI); + if ( inputState.guessing==0 ) { + astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); + } + } + else { + if ( _cnt333>=1 ) { break _loop333; } else {throw new NoViableAltException(LT(1), getFilename());} + } + + _cnt333++; + } while (true); + } + if ( inputState.guessing==0 ) { + closureList_AST = (AST)currentAST.root; + closureList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1))).add(closureList_AST)); + currentAST.root = closureList_AST; + currentAST.child = closureList_AST!=null &&closureList_AST.getFirstChild()!=null ? + closureList_AST.getFirstChild() : closureList_AST; + currentAST.advanceChildToEnd(); + } + closureList_AST = (AST)currentAST.root; + returnAST = closureList_AST; + } + + public final void forInClause() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST forInClause_AST = null; + AST decl_AST = null; + Token i = null; + AST i_AST = null; + Token c = null; + AST c_AST = null; + + { + boolean synPredMatched337 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_84.member(LA(2))))) { + int _m337 = mark(); + synPredMatched337 = true; + inputState.guessing++; + try { + { + declarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched337 = false; + } + rewind(_m337); +inputState.guessing--; + } + if ( synPredMatched337 ) { + singleDeclarationNoInit(); + decl_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + } + else if ((LA(1)==IDENT) && (LA(2)==COLON||LA(2)==LITERAL_in)) { + AST tmp249_AST = null; + tmp249_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp249_AST); + match(IDENT); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + switch ( LA(1)) { + case LITERAL_in: + { + i = LT(1); + i_AST = astFactory.create(i); + astFactory.makeASTRoot(currentAST, i_AST); + match(LITERAL_in); + if ( inputState.guessing==0 ) { + i_AST.setType(FOR_IN_ITERABLE); + } + shiftExpression(0); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case COLON: + { + if ( inputState.guessing==0 ) { + addWarning( + "A colon at this point is legal Java but not recommended in Groovy.", + "Use the 'in' keyword." + ); + require(decl_AST != null, + "Java-style for-each statement requires a type declaration." + , + "Use the 'in' keyword, as for (x in y) {...}" + ); + + } + c = LT(1); + c_AST = astFactory.create(c); + astFactory.makeASTRoot(currentAST, c_AST); + match(COLON); + if ( inputState.guessing==0 ) { + c_AST.setType(FOR_IN_ITERABLE); + } + expression(0); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + forInClause_AST = (AST)currentAST.root; + returnAST = forInClause_AST; + } + + public final void shiftExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST shiftExpression_AST = null; + + additiveExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop495: + do { + if ((_tokenSet_90.member(LA(1)))) { + { + switch ( LA(1)) { + case SR: + case BSR: + case SL: + { + { + switch ( LA(1)) { + case SL: + { + AST tmp250_AST = null; + tmp250_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp250_AST); + match(SL); + break; + } + case SR: + { + AST tmp251_AST = null; + tmp251_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp251_AST); + match(SR); + break; + } + case BSR: + { + AST tmp252_AST = null; + tmp252_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp252_AST); + match(BSR); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + break; + } + case RANGE_INCLUSIVE: + { + AST tmp253_AST = null; + tmp253_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp253_AST); + match(RANGE_INCLUSIVE); + break; + } + case RANGE_EXCLUSIVE: + { + AST tmp254_AST = null; + tmp254_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp254_AST); + match(RANGE_EXCLUSIVE); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + additiveExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop495; + } + + } while (true); + } + shiftExpression_AST = (AST)currentAST.root; + returnAST = shiftExpression_AST; + } + + public final void expression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST expression_AST = null; + AST m_AST = null; + + boolean synPredMatched413 = false; + if (((LA(1)==LPAREN) && (LA(2)==IDENT||LA(2)==NLS))) { + int _m413 = mark(); + synPredMatched413 = true; + inputState.guessing++; + try { + { + match(LPAREN); + nls(); + match(IDENT); + { + _loop412: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + match(IDENT); + } + else { + break _loop412; + } + + } while (true); + } + match(RPAREN); + match(ASSIGN); + } + } + catch (RecognitionException pe) { + synPredMatched413 = false; + } + rewind(_m413); +inputState.guessing--; + } + if ( synPredMatched413 ) { + multipleAssignment(lc_stmt); + m_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + expression_AST = (AST)currentAST.root; + expression_AST=m_AST; + currentAST.root = expression_AST; + currentAST.child = expression_AST!=null &&expression_AST.getFirstChild()!=null ? + expression_AST.getFirstChild() : expression_AST; + currentAST.advanceChildToEnd(); + } + expression_AST = (AST)currentAST.root; + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + assignmentExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + expression_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = expression_AST; + } + +/** Lookahead for suspicious statement warnings and errors. */ + public final void suspiciousExpressionStatementStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST suspiciousExpressionStatementStart_AST = null; + + { + switch ( LA(1)) { + case PLUS: + case MINUS: + { + { + switch ( LA(1)) { + case PLUS: + { + AST tmp255_AST = null; + tmp255_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp255_AST); + match(PLUS); + break; + } + case MINUS: + { + AST tmp256_AST = null; + tmp256_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp256_AST); + match(MINUS); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + break; + } + case LBRACK: + case LPAREN: + case LCURLY: + { + { + switch ( LA(1)) { + case LBRACK: + { + AST tmp257_AST = null; + tmp257_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp257_AST); + match(LBRACK); + break; + } + case LPAREN: + { + AST tmp258_AST = null; + tmp258_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp258_AST); + match(LPAREN); + break; + } + case LCURLY: + { + AST tmp259_AST = null; + tmp259_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp259_AST); + match(LCURLY); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + suspiciousExpressionStatementStart_AST = (AST)currentAST.root; + returnAST = suspiciousExpressionStatementStart_AST; + } + +/** + * If two statements are separated by newline (not SEMI), the second had + * better not look like the latter half of an expression. If it does, issue a warning. + *

+ * Also, if the expression starts with a closure, it needs to + * have an explicit parameter list, in order to avoid the appearance of a + * compound statement. This is a hard error. + *

+ * These rules are different from Java's "dumb expression" restriction. + * Unlike Java, Groovy blocks can end with arbitrary (even dumb) expressions, + * as a consequence of optional 'return' and 'continue' tokens. + *

+ * To make the programmer's intention clear, a leading closure must have an + * explicit parameter list, and must not follow a previous statement separated + * only by newlines. + */ + public final void checkSuspiciousExpressionStatement( + int prevToken + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST checkSuspiciousExpressionStatement_AST = null; + + boolean synPredMatched360 = false; + if (((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2))))) { + int _m360 = mark(); + synPredMatched360 = true; + inputState.guessing++; + try { + { + if ((_tokenSet_91.member(LA(1)))) { + matchNot(LCURLY); + } + else if ((LA(1)==LCURLY)) { + match(LCURLY); + closableBlockParamsStart(); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + } + catch (RecognitionException pe) { + synPredMatched360 = false; + } + rewind(_m360); +inputState.guessing--; + } + if ( synPredMatched360 ) { + { + if (((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2))))&&(prevToken == NLS)) { + if ( inputState.guessing==0 ) { + addWarning( + "Expression statement looks like it may continue a previous statement", + "Either remove the previous newline, or add an explicit semicolon ';'."); + + } + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + checkSuspiciousExpressionStatement_AST = (AST)currentAST.root; + } + else if (((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2))))&&(prevToken == NLS)) { + if ( inputState.guessing==0 ) { + require(false, + "Ambiguous expression could be a parameterless closure expression, "+ + "an isolated open code block, or it may continue a previous statement", + "Add an explicit parameter list, e.g. {it -> ...}, or force it to be treated "+ + "as an open block by giving it a label, e.g. L:{...}, "+ + "and also either remove the previous newline, or add an explicit semicolon ';'" + ); + + } + checkSuspiciousExpressionStatement_AST = (AST)currentAST.root; + } + else if (((_tokenSet_20.member(LA(1))) && (_tokenSet_2.member(LA(2))))&&(prevToken != NLS)) { + if ( inputState.guessing==0 ) { + require(false, + "Ambiguous expression could be either a parameterless closure expression or "+ + "an isolated open code block", + "Add an explicit closure parameter list, e.g. {it -> ...}, or force it to "+ + "be treated as an open block by giving it a label, e.g. L:{...}"); + + } + checkSuspiciousExpressionStatement_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = checkSuspiciousExpressionStatement_AST; + } + + public final void commandArgumentsGreedy( + AST head + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST commandArgumentsGreedy_AST = null; + AST first_AST = null; + AST pre_AST = null; + AST pc_AST = null; + AST ca_AST = null; + + AST prev = head; + + + { + boolean synPredMatched395 = false; + if (((_tokenSet_92.member(LA(1))) && (_tokenSet_3.member(LA(2))))) { + int _m395 = mark(); + synPredMatched395 = true; + inputState.guessing++; + try { + { + if (!(prev==null || prev.getType()!=METHOD_CALL)) + throw new SemanticException("prev==null || prev.getType()!=METHOD_CALL"); + commandArgument(); + } + } + catch (RecognitionException pe) { + synPredMatched395 = false; + } + rewind(_m395); +inputState.guessing--; + } + if ( synPredMatched395 ) { + { + commandArguments(head); + first_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prev = first_AST; + } + } + } + else if ((_tokenSet_75.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + { + _loop404: + do { + if ((_tokenSet_93.member(LA(1))) && (_tokenSet_94.member(LA(2)))) { + primaryExpression(); + pre_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prev = (AST)astFactory.make( (new ASTArray(3)).add(create(DOT,".",prev)).add(prev).add(pre_AST)); + } + { + boolean synPredMatched401 = false; + if (((_tokenSet_95.member(LA(1))) && (_tokenSet_94.member(LA(2))))) { + int _m401 = mark(); + synPredMatched401 = true; + inputState.guessing++; + try { + { + pathElementStart(); + } + } + catch (RecognitionException pe) { + synPredMatched401 = false; + } + rewind(_m401); +inputState.guessing--; + } + if ( synPredMatched401 ) { + { + pathChain(LC_STMT,prev); + pc_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prev = pc_AST; + } + } + } + else if ((_tokenSet_92.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + { + commandArguments(prev); + ca_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prev = ca_AST; + } + } + } + else if ((_tokenSet_75.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + } + else { + break _loop404; + } + + } while (true); + } + } + if ( inputState.guessing==0 ) { + commandArgumentsGreedy_AST = (AST)currentAST.root; + commandArgumentsGreedy_AST = prev; + currentAST.root = commandArgumentsGreedy_AST; + currentAST.child = commandArgumentsGreedy_AST!=null &&commandArgumentsGreedy_AST.getFirstChild()!=null ? + commandArgumentsGreedy_AST.getFirstChild() : commandArgumentsGreedy_AST; + currentAST.advanceChildToEnd(); + } + commandArgumentsGreedy_AST = (AST)currentAST.root; + returnAST = commandArgumentsGreedy_AST; + } + + public final void aCase() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST aCase_AST = null; + + { + switch ( LA(1)) { + case LITERAL_case: + { + AST tmp260_AST = null; + tmp260_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp260_AST); + match(LITERAL_case); + expression(0); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case LITERAL_default: + { + AST tmp261_AST = null; + tmp261_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp261_AST); + match(LITERAL_default); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(COLON); + nls(); + aCase_AST = (AST)currentAST.root; + returnAST = aCase_AST; + } + + public final void caseSList() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST caseSList_AST = null; + Token first = LT(1); + + statement(COLON); + astFactory.addASTChild(currentAST, returnAST); + { + _loop374: + do { + if ((LA(1)==SEMI||LA(1)==NLS)) { + sep(); + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case UNUSED_DO: + case STRICTFP: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_if: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_try: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + statement(sepToken); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RCURLY: + case SEMI: + case LITERAL_default: + case LITERAL_case: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop374; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + caseSList_AST = (AST)currentAST.root; + caseSList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(SLIST,"SLIST",first,LT(1))).add(caseSList_AST)); + currentAST.root = caseSList_AST; + currentAST.child = caseSList_AST!=null &&caseSList_AST.getFirstChild()!=null ? + caseSList_AST.getFirstChild() : caseSList_AST; + currentAST.advanceChildToEnd(); + } + caseSList_AST = (AST)currentAST.root; + returnAST = caseSList_AST; + } + + public final void forInit() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST forInit_AST = null; + Token first = LT(1); + + boolean synPredMatched377 = false; + if (((_tokenSet_16.member(LA(1))) && (_tokenSet_17.member(LA(2))))) { + int _m377 = mark(); + synPredMatched377 = true; + inputState.guessing++; + try { + { + declarationStart(); + } + } + catch (RecognitionException pe) { + synPredMatched377 = false; + } + rewind(_m377); +inputState.guessing--; + } + if ( synPredMatched377 ) { + declaration(); + astFactory.addASTChild(currentAST, returnAST); + forInit_AST = (AST)currentAST.root; + } + else if ((_tokenSet_96.member(LA(1))) && (_tokenSet_97.member(LA(2)))) { + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + controlExpressionList(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + forInit_AST = (AST)currentAST.root; + forInit_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(FOR_INIT,"FOR_INIT",first,LT(1))).add(forInit_AST)); + currentAST.root = forInit_AST; + currentAST.child = forInit_AST!=null &&forInit_AST.getFirstChild()!=null ? + forInit_AST.getFirstChild() : forInit_AST; + currentAST.advanceChildToEnd(); + } + forInit_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = forInit_AST; + } + + public final void controlExpressionList() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST controlExpressionList_AST = null; + Token first = LT(1); boolean sce=false; + + sce=strictContextExpression(false); + astFactory.addASTChild(currentAST, returnAST); + { + _loop422: + do { + if ((LA(1)==COMMA)) { + match(COMMA); + nls(); + sce=strictContextExpression(false); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop422; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + controlExpressionList_AST = (AST)currentAST.root; + controlExpressionList_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(ELIST,"ELIST",first,LT(1))).add(controlExpressionList_AST)); + currentAST.root = controlExpressionList_AST; + currentAST.child = controlExpressionList_AST!=null &&controlExpressionList_AST.getFirstChild()!=null ? + controlExpressionList_AST.getFirstChild() : controlExpressionList_AST; + currentAST.advanceChildToEnd(); + } + controlExpressionList_AST = (AST)currentAST.root; + returnAST = controlExpressionList_AST; + } + + public final void forCond() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST forCond_AST = null; + Token first = LT(1); boolean sce=false; + + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + sce=strictContextExpression(false); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + forCond_AST = (AST)currentAST.root; + forCond_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(FOR_CONDITION,"FOR_CONDITION",first,LT(1))).add(forCond_AST)); + currentAST.root = forCond_AST; + currentAST.child = forCond_AST!=null &&forCond_AST.getFirstChild()!=null ? + forCond_AST.getFirstChild() : forCond_AST; + currentAST.advanceChildToEnd(); + } + forCond_AST = (AST)currentAST.root; + returnAST = forCond_AST; + } + + public final void forIter() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST forIter_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + controlExpressionList(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case EOF: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + forIter_AST = (AST)currentAST.root; + forIter_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(FOR_ITERATOR,"FOR_ITERATOR",first,LT(1))).add(forIter_AST)); + currentAST.root = forIter_AST; + currentAST.child = forIter_AST!=null &&forIter_AST.getFirstChild()!=null ? + forIter_AST.getFirstChild() : forIter_AST; + currentAST.advanceChildToEnd(); + } + forIter_AST = (AST)currentAST.root; + returnAST = forIter_AST; + } + + public final void handler() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST handler_AST = null; + AST pd_AST = null; + AST handlerCs_AST = null; + Token first = LT(1); + + match(LITERAL_catch); + match(LPAREN); + multicatch(); + pd_AST = (AST)returnAST; + match(RPAREN); + nlsWarn(); + compoundStatement(); + handlerCs_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + handler_AST = (AST)currentAST.root; + handler_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_catch,"catch",first,LT(1))).add(pd_AST).add(handlerCs_AST)); + currentAST.root = handler_AST; + currentAST.child = handler_AST!=null &&handler_AST.getFirstChild()!=null ? + handler_AST.getFirstChild() : handler_AST; + currentAST.advanceChildToEnd(); + } + handler_AST = (AST)currentAST.root; + returnAST = handler_AST; + } + + public final void finallyClause() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST finallyClause_AST = null; + AST finallyCs_AST = null; + Token first = LT(1); + + match(LITERAL_finally); + nlsWarn(); + compoundStatement(); + finallyCs_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + finallyClause_AST = (AST)currentAST.root; + finallyClause_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(LITERAL_finally,"finally",first,LT(1))).add(finallyCs_AST)); + currentAST.root = finallyClause_AST; + currentAST.child = finallyClause_AST!=null &&finallyClause_AST.getFirstChild()!=null ? + finallyClause_AST.getFirstChild() : finallyClause_AST; + currentAST.advanceChildToEnd(); + } + finallyClause_AST = (AST)currentAST.root; + returnAST = finallyClause_AST; + } + +/** A member name (x.y) or element name (x[y]) can serve as a command name, + * which may be followed by a list of arguments. + * Unlike parenthesized arguments, these must be plain expressions, + * without labels or spread operators. + */ + public final void commandArguments( + AST head + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST commandArguments_AST = null; + + Token first = LT(1); + + + try { // for error handling + commandArgument(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop391: + do { + if ((LA(1)==COMMA) && (_tokenSet_98.member(LA(2)))) { + match(COMMA); + nls(); + commandArgument(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop391; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + commandArguments_AST = (AST)currentAST.root; + + AST elist = (AST)astFactory.make( (new ASTArray(2)).add(create(ELIST,"ELIST",first,LT(1))).add(commandArguments_AST)); + AST headid = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"",first,LT(1))).add(head).add(elist)); + commandArguments_AST = headid; + + currentAST.root = commandArguments_AST; + currentAST.child = commandArguments_AST!=null &&commandArguments_AST.getFirstChild()!=null ? + commandArguments_AST.getFirstChild() : commandArguments_AST; + currentAST.advanceChildToEnd(); + } + commandArguments_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // GRECLIPSE-1192 + // Do we need better recognition of the specific problem here? + // (if so, see the label recovery for GRECLIPSE-1048) + reportError(e); + + } else { + throw e; + } + } + returnAST = commandArguments_AST; + } + + public final void commandArgument() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST commandArgument_AST = null; + Token c = null; + AST c_AST = null; + + boolean synPredMatched407 = false; + if (((_tokenSet_99.member(LA(1))) && (_tokenSet_100.member(LA(2))))) { + int _m407 = mark(); + synPredMatched407 = true; + inputState.guessing++; + try { + { + argumentLabel(); + match(COLON); + nls(); + } + } + catch (RecognitionException pe) { + synPredMatched407 = false; + } + rewind(_m407); +inputState.guessing--; + } + if ( synPredMatched407 ) { + { + argumentLabel(); + astFactory.addASTChild(currentAST, returnAST); + c = LT(1); + c_AST = astFactory.create(c); + astFactory.makeASTRoot(currentAST, c_AST); + match(COLON); + nls(); + expression(0); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + c_AST.setType(LABELED_ARG); + } + } + commandArgument_AST = (AST)currentAST.root; + } + else if ((_tokenSet_20.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + expression(0); + astFactory.addASTChild(currentAST, returnAST); + commandArgument_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = commandArgument_AST; + } + + public final void primaryExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST primaryExpression_AST = null; + AST pe_AST = null; + Token first = LT(1); + + switch ( LA(1)) { + case IDENT: + { + AST tmp269_AST = null; + tmp269_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp269_AST); + match(IDENT); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case STRING_LITERAL: + case LITERAL_false: + case LITERAL_null: + case LITERAL_true: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + constant(); + astFactory.addASTChild(currentAST, returnAST); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LITERAL_new: + { + newExpression(); + astFactory.addASTChild(currentAST, returnAST); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LITERAL_this: + { + AST tmp270_AST = null; + tmp270_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp270_AST); + match(LITERAL_this); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LITERAL_super: + { + AST tmp271_AST = null; + tmp271_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp271_AST); + match(LITERAL_super); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LPAREN: + { + parenthesizedExpression(); + pe_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + primaryExpression_AST = (AST)currentAST.root; + primaryExpression_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXPR,"EXPR",first,LT(1))).add(pe_AST)); + currentAST.root = primaryExpression_AST; + currentAST.child = primaryExpression_AST!=null &&primaryExpression_AST.getFirstChild()!=null ? + primaryExpression_AST.getFirstChild() : primaryExpression_AST; + currentAST.advanceChildToEnd(); + } + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LCURLY: + { + closableBlockConstructorExpression(); + astFactory.addASTChild(currentAST, returnAST); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LBRACK: + { + listOrMapConstructorExpression(); + astFactory.addASTChild(currentAST, returnAST); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case STRING_CTOR_START: + { + stringConstructorExpression(); + astFactory.addASTChild(currentAST, returnAST); + primaryExpression_AST = (AST)currentAST.root; + break; + } + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + builtInType(); + astFactory.addASTChild(currentAST, returnAST); + primaryExpression_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = primaryExpression_AST; + } + + public final void pathElementStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST pathElementStart_AST = null; + + switch ( LA(1)) { + case DOT: + case SPREAD_DOT: + case OPTIONAL_DOT: + case MEMBER_POINTER: + case NLS: + { + { + nls(); + { + switch ( LA(1)) { + case DOT: + { + AST tmp272_AST = null; + tmp272_AST = astFactory.create(LT(1)); + match(DOT); + break; + } + case SPREAD_DOT: + { + AST tmp273_AST = null; + tmp273_AST = astFactory.create(LT(1)); + match(SPREAD_DOT); + break; + } + case OPTIONAL_DOT: + { + AST tmp274_AST = null; + tmp274_AST = astFactory.create(LT(1)); + match(OPTIONAL_DOT); + break; + } + case MEMBER_POINTER: + { + AST tmp275_AST = null; + tmp275_AST = astFactory.create(LT(1)); + match(MEMBER_POINTER); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + break; + } + case LBRACK: + { + AST tmp276_AST = null; + tmp276_AST = astFactory.create(LT(1)); + match(LBRACK); + break; + } + case LPAREN: + { + AST tmp277_AST = null; + tmp277_AST = astFactory.create(LT(1)); + match(LPAREN); + break; + } + case LCURLY: + { + AST tmp278_AST = null; + tmp278_AST = astFactory.create(LT(1)); + match(LCURLY); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = pathElementStart_AST; + } + + public final void pathChain( + int lc_stmt, AST prefix + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST pathChain_AST = null; + AST pe_AST = null; + AST apb_AST = null; + + { + int _cnt429=0; + _loop429: + do { + boolean synPredMatched426 = false; + if (((_tokenSet_95.member(LA(1))) && (_tokenSet_94.member(LA(2))))) { + int _m426 = mark(); + synPredMatched426 = true; + inputState.guessing++; + try { + { + pathElementStart(); + } + } + catch (RecognitionException pe) { + synPredMatched426 = false; + } + rewind(_m426); +inputState.guessing--; + } + if ( synPredMatched426 ) { + nls(); + pathElement(prefix); + pe_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prefix = pe_AST; + } + } + else { + boolean synPredMatched428 = false; + if ((((LA(1)==LCURLY||LA(1)==NLS) && (_tokenSet_18.member(LA(2))))&&(lc_stmt == LC_STMT || lc_stmt == LC_INIT))) { + int _m428 = mark(); + synPredMatched428 = true; + inputState.guessing++; + try { + { + nls(); + match(LCURLY); + } + } + catch (RecognitionException pe) { + synPredMatched428 = false; + } + rewind(_m428); +inputState.guessing--; + } + if ( synPredMatched428 ) { + nlsWarn(); + appendedBlock(prefix); + apb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prefix = apb_AST; + } + } + else { + if ( _cnt429>=1 ) { break _loop429; } else {throw new NoViableAltException(LT(1), getFilename());} + } + } + _cnt429++; + } while (true); + } + if ( inputState.guessing==0 ) { + pathChain_AST = (AST)currentAST.root; + pathChain_AST = prefix; + currentAST.root = pathChain_AST; + currentAST.child = pathChain_AST!=null &&pathChain_AST.getFirstChild()!=null ? + pathChain_AST.getFirstChild() : pathChain_AST; + currentAST.advanceChildToEnd(); + } + pathChain_AST = (AST)currentAST.root; + returnAST = pathChain_AST; + } + +/** A label for an argument is of the form a:b, 'a':b, "a":b, (a):b, etc.. + * The labels in (a:b), ('a':b), and ("a":b) are in all ways equivalent, + * except that the quotes allow more spellings. + * Equivalent dynamically computed labels are (('a'):b) and ("${'a'}":b) + * but not ((a):b) or "$a":b, since the latter cases evaluate (a) as a normal identifier. + * Bottom line: If you want a truly variable label, use parens and say ((a):b). + */ + public final void argumentLabel() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST argumentLabel_AST = null; + Token id = null; + AST id_AST = null; + AST kw_AST = null; + + boolean synPredMatched580 = false; + if (((LA(1)==IDENT) && (LA(2)==COLON))) { + int _m580 = mark(); + synPredMatched580 = true; + inputState.guessing++; + try { + { + match(IDENT); + } + } + catch (RecognitionException pe) { + synPredMatched580 = false; + } + rewind(_m580); +inputState.guessing--; + } + if ( synPredMatched580 ) { + id = LT(1); + id_AST = astFactory.create(id); + astFactory.addASTChild(currentAST, id_AST); + match(IDENT); + if ( inputState.guessing==0 ) { + id_AST.setType(STRING_LITERAL); + } + argumentLabel_AST = (AST)currentAST.root; + } + else { + boolean synPredMatched582 = false; + if (((_tokenSet_101.member(LA(1))) && (LA(2)==COLON))) { + int _m582 = mark(); + synPredMatched582 = true; + inputState.guessing++; + try { + { + keywordPropertyNames(); + } + } + catch (RecognitionException pe) { + synPredMatched582 = false; + } + rewind(_m582); +inputState.guessing--; + } + if ( synPredMatched582 ) { + keywordPropertyNames(); + kw_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + kw_AST.setType(STRING_LITERAL); + } + argumentLabel_AST = (AST)currentAST.root; + } + else if ((_tokenSet_93.member(LA(1))) && (_tokenSet_100.member(LA(2)))) { + primaryExpression(); + astFactory.addASTChild(currentAST, returnAST); + argumentLabel_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = argumentLabel_AST; + } + + public final void pathElement( + AST prefix + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST pathElement_AST = null; + AST ta_AST = null; + AST np_AST = null; + AST mca_AST = null; + AST apb_AST = null; + AST ipa_AST = null; + Token operator = LT(1); + + switch ( LA(1)) { + case DOT: + case SPREAD_DOT: + case OPTIONAL_DOT: + case MEMBER_POINTER: + case NLS: + { + if ( inputState.guessing==0 ) { + pathElement_AST = (AST)currentAST.root; + pathElement_AST = prefix; + currentAST.root = pathElement_AST; + currentAST.child = pathElement_AST!=null &&pathElement_AST.getFirstChild()!=null ? + pathElement_AST.getFirstChild() : pathElement_AST; + currentAST.advanceChildToEnd(); + } + { + nls(); + { + switch ( LA(1)) { + case SPREAD_DOT: + { + match(SPREAD_DOT); + break; + } + case OPTIONAL_DOT: + { + match(OPTIONAL_DOT); + break; + } + case MEMBER_POINTER: + { + match(MEMBER_POINTER); + break; + } + case DOT: + { + match(DOT); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + nls(); + { + if ((LA(1)==LT) && (_tokenSet_38.member(LA(2)))) { + typeArguments(); + ta_AST = (AST)returnAST; + } + else if ((_tokenSet_102.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + { + if ((_tokenSet_103.member(LA(1))) && (_tokenSet_102.member(LA(2)))) { + namePart(); + np_AST = (AST)returnAST; + } + else if ((_tokenSet_104.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + pathElement_AST = (AST)currentAST.root; + + if (np_AST == null) { + GroovySourceToken ident = new GroovySourceToken(IDENT); + ident.setLine(((SourceInfo) LT(0)).getLineLast()); + ident.setColumn(((SourceInfo) LT(0)).getColumnLast()); + ident.setLineLast(((SourceInfo) LT(0)).getLineLast()); + ident.setColumnLast(((SourceInfo) LT(0)).getColumnLast()); + np_AST = (AST)astFactory.make( (new ASTArray(1)).add(create(ident.getType(),ident.getText(),ident,null))); + reportError(new NoViableAltException(LT(1), getFilename())); + } + pathElement_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(operator.getType(),operator.getText(),prefix,LT(1))).add(prefix).add(ta_AST).add(np_AST)); + + currentAST.root = pathElement_AST; + currentAST.child = pathElement_AST!=null &&pathElement_AST.getFirstChild()!=null ? + pathElement_AST.getFirstChild() : pathElement_AST; + currentAST.advanceChildToEnd(); + } + pathElement_AST = (AST)currentAST.root; + break; + } + case LPAREN: + { + methodCallArgs(prefix); + mca_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + pathElement_AST = (AST)currentAST.root; + pathElement_AST = mca_AST; + currentAST.root = pathElement_AST; + currentAST.child = pathElement_AST!=null &&pathElement_AST.getFirstChild()!=null ? + pathElement_AST.getFirstChild() : pathElement_AST; + currentAST.advanceChildToEnd(); + } + pathElement_AST = (AST)currentAST.root; + break; + } + case LCURLY: + { + appendedBlock(prefix); + apb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + pathElement_AST = (AST)currentAST.root; + pathElement_AST = apb_AST; + currentAST.root = pathElement_AST; + currentAST.child = pathElement_AST!=null &&pathElement_AST.getFirstChild()!=null ? + pathElement_AST.getFirstChild() : pathElement_AST; + currentAST.advanceChildToEnd(); + } + pathElement_AST = (AST)currentAST.root; + break; + } + case LBRACK: + { + indexPropertyArgs(prefix); + ipa_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + pathElement_AST = (AST)currentAST.root; + pathElement_AST = ipa_AST; + currentAST.root = pathElement_AST; + currentAST.child = pathElement_AST!=null &&pathElement_AST.getFirstChild()!=null ? + pathElement_AST.getFirstChild() : pathElement_AST; + currentAST.advanceChildToEnd(); + } + pathElement_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = pathElement_AST; + } + +/** An appended block follows any expression. + * If the expression is not a method call, it is given an empty argument list. + */ + public final void appendedBlock( + AST callee + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST appendedBlock_AST = null; + AST cb_AST = null; + + closableBlock(); + cb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + appendedBlock_AST = (AST)currentAST.root; + + // If the callee is itself a call, flatten the AST. + if (callee != null && callee.getType() == METHOD_CALL) { + appendedBlock_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"(",callee,LT(1))).add(callee.getFirstChild()).add(cb_AST)); + } else { + appendedBlock_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"{",callee,LT(1))).add(callee).add(cb_AST)); + } + + currentAST.root = appendedBlock_AST; + currentAST.child = appendedBlock_AST!=null &&appendedBlock_AST.getFirstChild()!=null ? + appendedBlock_AST.getFirstChild() : appendedBlock_AST; + currentAST.advanceChildToEnd(); + } + appendedBlock_AST = (AST)currentAST.root; + returnAST = appendedBlock_AST; + } + +/** A "path expression" is a name or other primary, possibly qualified by various + * forms of dot, and/or followed by various kinds of brackets. + * It can be used for value or assigned to, or else further qualified, indexed, or called. + * It is called a "path" because it looks like a linear path through a data structure. + * Examples: x.y, x?.y, x*.y, x.@y; x[], x[y], x[y,z]; x(), x(y), x(y,z); x{s}; a.b[n].c(x).d{s} + * (Compare to a C lvalue, or LeftHandSide in the JLS section 15.26.) + * General expressions are built up from path expressions, using operators like '+' and '='. + */ + public final void pathExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST pathExpression_AST = null; + AST pre_AST = null; + AST pe_AST = null; + AST apb_AST = null; + AST prefix = null; + + primaryExpression(); + pre_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prefix = pre_AST; + } + { + _loop436: + do { + boolean synPredMatched433 = false; + if (((_tokenSet_95.member(LA(1))) && (_tokenSet_3.member(LA(2))))) { + int _m433 = mark(); + synPredMatched433 = true; + inputState.guessing++; + try { + { + pathElementStart(); + } + } + catch (RecognitionException pe) { + synPredMatched433 = false; + } + rewind(_m433); +inputState.guessing--; + } + if ( synPredMatched433 ) { + nls(); + pathElement(prefix); + pe_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prefix = pe_AST; + } + } + else { + boolean synPredMatched435 = false; + if ((((LA(1)==LCURLY||LA(1)==NLS) && (_tokenSet_18.member(LA(2))))&&(lc_stmt == LC_STMT || lc_stmt == LC_INIT))) { + int _m435 = mark(); + synPredMatched435 = true; + inputState.guessing++; + try { + { + nls(); + match(LCURLY); + } + } + catch (RecognitionException pe) { + synPredMatched435 = false; + } + rewind(_m435); +inputState.guessing--; + } + if ( synPredMatched435 ) { + nlsWarn(); + appendedBlock(prefix); + apb_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + prefix = apb_AST; + } + } + else { + break _loop436; + } + } + } while (true); + } + if ( inputState.guessing==0 ) { + pathExpression_AST = (AST)currentAST.root; + + pathExpression_AST = prefix; + lastPathExpression = pathExpression_AST; + + currentAST.root = pathExpression_AST; + currentAST.child = pathExpression_AST!=null &&pathExpression_AST.getFirstChild()!=null ? + pathExpression_AST.getFirstChild() : pathExpression_AST; + currentAST.advanceChildToEnd(); + } + pathExpression_AST = (AST)currentAST.root; + returnAST = pathExpression_AST; + } + +/** This is the grammar for what can follow a dot: x.a, x.@a, x.&a, x.'a', etc. + * Note: typeArguments is handled by the caller of namePart. + */ + public final void namePart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST namePart_AST = null; + Token ats = null; + AST ats_AST = null; + Token sl = null; + AST sl_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case AT: + { + ats = LT(1); + ats_AST = astFactory.create(ats); + astFactory.makeASTRoot(currentAST, ats_AST); + match(AT); + if ( inputState.guessing==0 ) { + ats_AST.setType(SELECT_SLOT); + } + break; + } + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case STRING_CTOR_START: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case IDENT: + { + AST tmp283_AST = null; + tmp283_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp283_AST); + match(IDENT); + break; + } + case STRING_LITERAL: + { + sl = LT(1); + sl_AST = astFactory.create(sl); + astFactory.addASTChild(currentAST, sl_AST); + match(STRING_LITERAL); + if ( inputState.guessing==0 ) { + sl_AST.setType(IDENT); + } + break; + } + case LPAREN: + case STRING_CTOR_START: + { + dynamicMemberName(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case LCURLY: + { + openBlock(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + { + keywordPropertyNames(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + namePart_AST = (AST)currentAST.root; + returnAST = namePart_AST; + } + +/** An expression may be followed by one or both of (...) and {...}. + * Note: If either is (...) or {...} present, it is a method call. + * The {...} is appended to the argument list, and matches a formal of type Closure. + * If there is no method member, a property (or field) is used instead, and must itself be callable. + *

+ * If the methodCallArgs are absent, it is a property reference. + * If there is no property, it is treated as a field reference, but never a method reference. + *

+ * Arguments in the (...) can be labeled, and the appended block can be labeled also. + * If there is a mix of unlabeled and labeled arguments, + * all the labeled arguments must follow the unlabeled arguments, + * except that the closure (labeled or not) is always a separate final argument. + * Labeled arguments are collected up and passed as a single argument to a formal of type Map. + *

+ * Therefore, f(x,y, a:p, b:q) {s} is equivalent in all ways to f(x,y, [a:p,b:q], {s}). + * Spread arguments of sequence type count as unlabeled arguments, + * while spread arguments of map type count as labeled arguments. + * (This distinction must sometimes be checked dynamically.) + * + * A plain unlabeled argument is allowed to match a trailing Map or Closure argument: + * f(x, a:p) {s} === f(*[ x, [a:p], {s} ]) + */ + public final void methodCallArgs( + AST callee + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST methodCallArgs_AST = null; + AST al_AST = null; + + try { // for error handling + match(LPAREN); + argList(); + al_AST = (AST)returnAST; + match(RPAREN); + if ( inputState.guessing==0 ) { + methodCallArgs_AST = (AST)currentAST.root; + if (callee != null && callee.getFirstChild() != null) { + //method call like obj.method() + methodCallArgs_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"(",callee.getFirstChild(),LT(1))).add(callee).add(al_AST)); + } else { + //method call like method() or new Expr(), in the latter case "callee" is null + methodCallArgs_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"(",callee,LT(1))).add(callee).add(al_AST)); + } + + currentAST.root = methodCallArgs_AST; + currentAST.child = methodCallArgs_AST!=null &&methodCallArgs_AST.getFirstChild()!=null ? + methodCallArgs_AST.getFirstChild() : methodCallArgs_AST; + currentAST.advanceChildToEnd(); + } + methodCallArgs_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + if (al_AST != null) { + reportError(e); + // copy of the block above - lets build it (assuming that all that was missing was the RPAREN) + if (callee != null && callee.getFirstChild() != null) { + // method call like obj.method() + methodCallArgs_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"(",callee.getFirstChild(),LT(1))).add(callee).add(al_AST)); + } else { + // method call like method() or new Expr(), in the latter case "callee" is null + methodCallArgs_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(METHOD_CALL,"(",callee,LT(1))).add(callee).add(al_AST)); + } + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = methodCallArgs_AST; + } + +/** An expression may be followed by [...]. + * Unlike Java, these brackets may contain a general argument list, + * which is passed to the array element operator, which can make of it what it wants. + * The brackets may also be empty, as in T[]. This is how Groovy names array types. + *

Returned AST is [INDEX_OP, indexee, ELIST]. + */ + public final void indexPropertyArgs( + AST indexee + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST indexPropertyArgs_AST = null; + Token lb = null; + AST lb_AST = null; + AST al_AST = null; + + lb = LT(1); + lb_AST = astFactory.create(lb); + astFactory.addASTChild(currentAST, lb_AST); + match(LBRACK); + argList(); + al_AST = (AST)returnAST; + match(RBRACK); + if ( inputState.guessing==0 ) { + indexPropertyArgs_AST = (AST)currentAST.root; + if (indexee != null && indexee.getFirstChild() != null) { + //expression like obj.index[] + indexPropertyArgs_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(INDEX_OP,"INDEX_OP",indexee.getFirstChild(),LT(1))).add(lb_AST).add(indexee).add(al_AST)); + } else { + //expression like obj[] + indexPropertyArgs_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(INDEX_OP,"INDEX_OP",indexee,LT(1))).add(lb_AST).add(indexee).add(al_AST)); + } + + currentAST.root = indexPropertyArgs_AST; + currentAST.child = indexPropertyArgs_AST!=null &&indexPropertyArgs_AST.getFirstChild()!=null ? + indexPropertyArgs_AST.getFirstChild() : indexPropertyArgs_AST; + currentAST.advanceChildToEnd(); + } + indexPropertyArgs_AST = (AST)currentAST.root; + returnAST = indexPropertyArgs_AST; + } + +/** If a dot is followed by a parenthesized or quoted expression, the member is computed dynamically, + * and the member selection is done only at runtime. This forces a statically unchecked member access. + */ + public final void dynamicMemberName() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST dynamicMemberName_AST = null; + AST pe_AST = null; + Token first = LT(1); + + { + switch ( LA(1)) { + case LPAREN: + { + parenthesizedExpression(); + pe_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + dynamicMemberName_AST = (AST)currentAST.root; + dynamicMemberName_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(EXPR,"EXPR",first,LT(1))).add(pe_AST)); + currentAST.root = dynamicMemberName_AST; + currentAST.child = dynamicMemberName_AST!=null &&dynamicMemberName_AST.getFirstChild()!=null ? + dynamicMemberName_AST.getFirstChild() : dynamicMemberName_AST; + currentAST.advanceChildToEnd(); + } + break; + } + case STRING_CTOR_START: + { + stringConstructorExpression(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + if ( inputState.guessing==0 ) { + dynamicMemberName_AST = (AST)currentAST.root; + dynamicMemberName_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(DYNAMIC_MEMBER,"DYNAMIC_MEMBER",first,LT(1))).add(dynamicMemberName_AST)); + currentAST.root = dynamicMemberName_AST; + currentAST.child = dynamicMemberName_AST!=null &&dynamicMemberName_AST.getFirstChild()!=null ? + dynamicMemberName_AST.getFirstChild() : dynamicMemberName_AST; + currentAST.advanceChildToEnd(); + } + dynamicMemberName_AST = (AST)currentAST.root; + returnAST = dynamicMemberName_AST; + } + + public final void parenthesizedExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST parenthesizedExpression_AST = null; + Token first = LT(1); + Token declaration = null; + boolean hasClosureList=false; + boolean firstContainsDeclaration=false; + boolean sce=false; + + + try { // for error handling + match(LPAREN); + if ( inputState.guessing==0 ) { + declaration=LT(1); + } + firstContainsDeclaration=strictContextExpression(true); + astFactory.addASTChild(currentAST, returnAST); + { + _loop540: + do { + if ((LA(1)==SEMI)) { + match(SEMI); + if ( inputState.guessing==0 ) { + hasClosureList=true; + } + { + switch ( LA(1)) { + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + sce=strictContextExpression(true); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RPAREN: + case SEMI: + { + if ( inputState.guessing==0 ) { + astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else { + break _loop540; + } + + } while (true); + } + if ( inputState.guessing==0 ) { + + if (firstContainsDeclaration && !hasClosureList) + throw new NoViableAltException(declaration, getFilename()); + + } + match(RPAREN); + if ( inputState.guessing==0 ) { + parenthesizedExpression_AST = (AST)currentAST.root; + + if (hasClosureList) { + parenthesizedExpression_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1))).add(parenthesizedExpression_AST)); + } + + currentAST.root = parenthesizedExpression_AST; + currentAST.child = parenthesizedExpression_AST!=null &&parenthesizedExpression_AST.getFirstChild()!=null ? + parenthesizedExpression_AST.getFirstChild() : parenthesizedExpression_AST; + currentAST.advanceChildToEnd(); + } + parenthesizedExpression_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + // GRECLIPSE-1213 - missing closing paren + reportError(e); + parenthesizedExpression_AST = (AST) currentAST.root; + + } else { + throw e; + } + } + returnAST = parenthesizedExpression_AST; + } + + public final void stringConstructorExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST stringConstructorExpression_AST = null; + Token cs = null; + AST cs_AST = null; + Token cm = null; + AST cm_AST = null; + Token ce = null; + AST ce_AST = null; + Token first = LT(1); + + cs = LT(1); + cs_AST = astFactory.create(cs); + astFactory.addASTChild(currentAST, cs_AST); + match(STRING_CTOR_START); + if ( inputState.guessing==0 ) { + cs_AST.setType(STRING_LITERAL); + } + stringConstructorValuePart(); + astFactory.addASTChild(currentAST, returnAST); + { + _loop550: + do { + if ((LA(1)==STRING_CTOR_MIDDLE)) { + cm = LT(1); + cm_AST = astFactory.create(cm); + astFactory.addASTChild(currentAST, cm_AST); + match(STRING_CTOR_MIDDLE); + if ( inputState.guessing==0 ) { + cm_AST.setType(STRING_LITERAL); + } + stringConstructorValuePart(); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop550; + } + + } while (true); + } + ce = LT(1); + ce_AST = astFactory.create(ce); + astFactory.addASTChild(currentAST, ce_AST); + match(STRING_CTOR_END); + if ( inputState.guessing==0 ) { + stringConstructorExpression_AST = (AST)currentAST.root; + ce_AST.setType(STRING_LITERAL); + stringConstructorExpression_AST = + (AST)astFactory.make( (new ASTArray(2)).add(create(STRING_CONSTRUCTOR,"STRING_CONSTRUCTOR",first,LT(1))).add(stringConstructorExpression_AST)); + + currentAST.root = stringConstructorExpression_AST; + currentAST.child = stringConstructorExpression_AST!=null &&stringConstructorExpression_AST.getFirstChild()!=null ? + stringConstructorExpression_AST.getFirstChild() : stringConstructorExpression_AST; + currentAST.advanceChildToEnd(); + } + stringConstructorExpression_AST = (AST)currentAST.root; + returnAST = stringConstructorExpression_AST; + } + + public final void logicalOrExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST logicalOrExpression_AST = null; + + logicalAndExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop466: + do { + if ((LA(1)==LOR)) { + AST tmp290_AST = null; + tmp290_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp290_AST); + match(LOR); + nls(); + logicalAndExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop466; + } + + } while (true); + } + logicalOrExpression_AST = (AST)currentAST.root; + returnAST = logicalOrExpression_AST; + } + + public final void logicalAndExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST logicalAndExpression_AST = null; + + inclusiveOrExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop469: + do { + if ((LA(1)==LAND)) { + AST tmp291_AST = null; + tmp291_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp291_AST); + match(LAND); + nls(); + inclusiveOrExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop469; + } + + } while (true); + } + logicalAndExpression_AST = (AST)currentAST.root; + returnAST = logicalAndExpression_AST; + } + + public final void inclusiveOrExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST inclusiveOrExpression_AST = null; + + exclusiveOrExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop472: + do { + if ((LA(1)==BOR)) { + AST tmp292_AST = null; + tmp292_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp292_AST); + match(BOR); + nls(); + exclusiveOrExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop472; + } + + } while (true); + } + inclusiveOrExpression_AST = (AST)currentAST.root; + returnAST = inclusiveOrExpression_AST; + } + + public final void exclusiveOrExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST exclusiveOrExpression_AST = null; + + andExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop475: + do { + if ((LA(1)==BXOR)) { + AST tmp293_AST = null; + tmp293_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp293_AST); + match(BXOR); + nls(); + andExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop475; + } + + } while (true); + } + exclusiveOrExpression_AST = (AST)currentAST.root; + returnAST = exclusiveOrExpression_AST; + } + + public final void andExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST andExpression_AST = null; + + regexExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop478: + do { + if ((LA(1)==BAND)) { + AST tmp294_AST = null; + tmp294_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp294_AST); + match(BAND); + nls(); + regexExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop478; + } + + } while (true); + } + andExpression_AST = (AST)currentAST.root; + returnAST = andExpression_AST; + } + + public final void regexExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST regexExpression_AST = null; + + equalityExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop482: + do { + if ((LA(1)==REGEX_FIND||LA(1)==REGEX_MATCH)) { + { + switch ( LA(1)) { + case REGEX_FIND: + { + AST tmp295_AST = null; + tmp295_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp295_AST); + match(REGEX_FIND); + break; + } + case REGEX_MATCH: + { + AST tmp296_AST = null; + tmp296_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp296_AST); + match(REGEX_MATCH); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + equalityExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop482; + } + + } while (true); + } + regexExpression_AST = (AST)currentAST.root; + returnAST = regexExpression_AST; + } + + public final void equalityExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST equalityExpression_AST = null; + + relationalExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop486: + do { + if (((LA(1) >= NOT_EQUAL && LA(1) <= COMPARE_TO))) { + { + switch ( LA(1)) { + case NOT_EQUAL: + { + AST tmp297_AST = null; + tmp297_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp297_AST); + match(NOT_EQUAL); + break; + } + case EQUAL: + { + AST tmp298_AST = null; + tmp298_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp298_AST); + match(EQUAL); + break; + } + case IDENTICAL: + { + AST tmp299_AST = null; + tmp299_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp299_AST); + match(IDENTICAL); + break; + } + case NOT_IDENTICAL: + { + AST tmp300_AST = null; + tmp300_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp300_AST); + match(NOT_IDENTICAL); + break; + } + case COMPARE_TO: + { + AST tmp301_AST = null; + tmp301_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp301_AST); + match(COMPARE_TO); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + relationalExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop486; + } + + } while (true); + } + equalityExpression_AST = (AST)currentAST.root; + returnAST = equalityExpression_AST; + } + + public final void relationalExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST relationalExpression_AST = null; + + shiftExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + if ((_tokenSet_105.member(LA(1))) && (_tokenSet_87.member(LA(2)))) { + { + { + switch ( LA(1)) { + case LT: + { + AST tmp302_AST = null; + tmp302_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp302_AST); + match(LT); + break; + } + case GT: + { + AST tmp303_AST = null; + tmp303_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp303_AST); + match(GT); + break; + } + case LE: + { + AST tmp304_AST = null; + tmp304_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp304_AST); + match(LE); + break; + } + case GE: + { + AST tmp305_AST = null; + tmp305_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp305_AST); + match(GE); + break; + } + case LITERAL_in: + { + AST tmp306_AST = null; + tmp306_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp306_AST); + match(LITERAL_in); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + shiftExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + } + else if ((LA(1)==LITERAL_instanceof) && (_tokenSet_106.member(LA(2)))) { + AST tmp307_AST = null; + tmp307_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp307_AST); + match(LITERAL_instanceof); + nls(); + typeSpec(true); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((LA(1)==LITERAL_as) && (_tokenSet_106.member(LA(2)))) { + AST tmp308_AST = null; + tmp308_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp308_AST); + match(LITERAL_as); + nls(); + typeSpec(true); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_107.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + relationalExpression_AST = (AST)currentAST.root; + returnAST = relationalExpression_AST; + } + + public final void additiveExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST additiveExpression_AST = null; + + multiplicativeExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop499: + do { + if ((LA(1)==PLUS||LA(1)==MINUS) && (_tokenSet_87.member(LA(2)))) { + { + switch ( LA(1)) { + case PLUS: + { + AST tmp309_AST = null; + tmp309_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp309_AST); + match(PLUS); + break; + } + case MINUS: + { + AST tmp310_AST = null; + tmp310_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp310_AST); + match(MINUS); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + multiplicativeExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop499; + } + + } while (true); + } + additiveExpression_AST = (AST)currentAST.root; + returnAST = additiveExpression_AST; + } + + public final void multiplicativeExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST multiplicativeExpression_AST = null; + + switch ( LA(1)) { + case INC: + { + { + AST tmp311_AST = null; + tmp311_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp311_AST); + match(INC); + nls(); + powerExpressionNotPlusMinus(0); + astFactory.addASTChild(currentAST, returnAST); + { + _loop504: + do { + if ((_tokenSet_108.member(LA(1)))) { + { + switch ( LA(1)) { + case STAR: + { + AST tmp312_AST = null; + tmp312_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp312_AST); + match(STAR); + break; + } + case DIV: + { + AST tmp313_AST = null; + tmp313_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp313_AST); + match(DIV); + break; + } + case MOD: + { + AST tmp314_AST = null; + tmp314_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp314_AST); + match(MOD); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + powerExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop504; + } + + } while (true); + } + } + multiplicativeExpression_AST = (AST)currentAST.root; + break; + } + case DEC: + { + { + AST tmp315_AST = null; + tmp315_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp315_AST); + match(DEC); + nls(); + powerExpressionNotPlusMinus(0); + astFactory.addASTChild(currentAST, returnAST); + { + _loop508: + do { + if ((_tokenSet_108.member(LA(1)))) { + { + switch ( LA(1)) { + case STAR: + { + AST tmp316_AST = null; + tmp316_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp316_AST); + match(STAR); + break; + } + case DIV: + { + AST tmp317_AST = null; + tmp317_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp317_AST); + match(DIV); + break; + } + case MOD: + { + AST tmp318_AST = null; + tmp318_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp318_AST); + match(MOD); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + powerExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop508; + } + + } while (true); + } + } + multiplicativeExpression_AST = (AST)currentAST.root; + break; + } + case MINUS: + { + { + AST tmp319_AST = null; + tmp319_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp319_AST); + match(MINUS); + if ( inputState.guessing==0 ) { + tmp319_AST.setType(UNARY_MINUS); + } + nls(); + powerExpressionNotPlusMinus(0); + astFactory.addASTChild(currentAST, returnAST); + { + _loop512: + do { + if ((_tokenSet_108.member(LA(1)))) { + { + switch ( LA(1)) { + case STAR: + { + AST tmp320_AST = null; + tmp320_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp320_AST); + match(STAR); + break; + } + case DIV: + { + AST tmp321_AST = null; + tmp321_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp321_AST); + match(DIV); + break; + } + case MOD: + { + AST tmp322_AST = null; + tmp322_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp322_AST); + match(MOD); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + powerExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop512; + } + + } while (true); + } + } + multiplicativeExpression_AST = (AST)currentAST.root; + break; + } + case PLUS: + { + { + AST tmp323_AST = null; + tmp323_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp323_AST); + match(PLUS); + if ( inputState.guessing==0 ) { + tmp323_AST.setType(UNARY_PLUS); + } + nls(); + powerExpressionNotPlusMinus(0); + astFactory.addASTChild(currentAST, returnAST); + { + _loop516: + do { + if ((_tokenSet_108.member(LA(1)))) { + { + switch ( LA(1)) { + case STAR: + { + AST tmp324_AST = null; + tmp324_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp324_AST); + match(STAR); + break; + } + case DIV: + { + AST tmp325_AST = null; + tmp325_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp325_AST); + match(DIV); + break; + } + case MOD: + { + AST tmp326_AST = null; + tmp326_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp326_AST); + match(MOD); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + powerExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop516; + } + + } while (true); + } + } + multiplicativeExpression_AST = (AST)currentAST.root; + break; + } + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + { + powerExpressionNotPlusMinus(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop520: + do { + if ((_tokenSet_108.member(LA(1)))) { + { + switch ( LA(1)) { + case STAR: + { + AST tmp327_AST = null; + tmp327_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp327_AST); + match(STAR); + break; + } + case DIV: + { + AST tmp328_AST = null; + tmp328_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp328_AST); + match(DIV); + break; + } + case MOD: + { + AST tmp329_AST = null; + tmp329_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp329_AST); + match(MOD); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + nls(); + powerExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop520; + } + + } while (true); + } + } + multiplicativeExpression_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = multiplicativeExpression_AST; + } + + public final void powerExpressionNotPlusMinus( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST powerExpressionNotPlusMinus_AST = null; + + unaryExpressionNotPlusMinus(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop527: + do { + if ((LA(1)==STAR_STAR)) { + AST tmp330_AST = null; + tmp330_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp330_AST); + match(STAR_STAR); + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop527; + } + + } while (true); + } + powerExpressionNotPlusMinus_AST = (AST)currentAST.root; + returnAST = powerExpressionNotPlusMinus_AST; + } + + public final void powerExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST powerExpression_AST = null; + + unaryExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + _loop524: + do { + if ((LA(1)==STAR_STAR)) { + AST tmp331_AST = null; + tmp331_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp331_AST); + match(STAR_STAR); + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + break _loop524; + } + + } while (true); + } + powerExpression_AST = (AST)currentAST.root; + returnAST = powerExpression_AST; + } + + public final void unaryExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST unaryExpression_AST = null; + + switch ( LA(1)) { + case INC: + { + AST tmp332_AST = null; + tmp332_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp332_AST); + match(INC); + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + unaryExpression_AST = (AST)currentAST.root; + break; + } + case DEC: + { + AST tmp333_AST = null; + tmp333_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp333_AST); + match(DEC); + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + unaryExpression_AST = (AST)currentAST.root; + break; + } + case MINUS: + { + AST tmp334_AST = null; + tmp334_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp334_AST); + match(MINUS); + if ( inputState.guessing==0 ) { + tmp334_AST.setType(UNARY_MINUS); + } + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + unaryExpression_AST = (AST)currentAST.root; + break; + } + case PLUS: + { + AST tmp335_AST = null; + tmp335_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp335_AST); + match(PLUS); + if ( inputState.guessing==0 ) { + tmp335_AST.setType(UNARY_PLUS); + } + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + unaryExpression_AST = (AST)currentAST.root; + break; + } + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + unaryExpressionNotPlusMinus(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + unaryExpression_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = unaryExpression_AST; + } + + public final void unaryExpressionNotPlusMinus( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST unaryExpressionNotPlusMinus_AST = null; + Token lpb = null; + AST lpb_AST = null; + Token lp = null; + AST lp_AST = null; + + switch ( LA(1)) { + case BNOT: + { + AST tmp336_AST = null; + tmp336_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp336_AST); + match(BNOT); + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + unaryExpressionNotPlusMinus_AST = (AST)currentAST.root; + break; + } + case LNOT: + { + AST tmp337_AST = null; + tmp337_AST = astFactory.create(LT(1)); + astFactory.makeASTRoot(currentAST, tmp337_AST); + match(LNOT); + nls(); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + unaryExpressionNotPlusMinus_AST = (AST)currentAST.root; + break; + } + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + { + boolean synPredMatched531 = false; + if (((LA(1)==LPAREN) && ((LA(2) >= LITERAL_void && LA(2) <= LITERAL_double)))) { + int _m531 = mark(); + synPredMatched531 = true; + inputState.guessing++; + try { + { + match(LPAREN); + builtInTypeSpec(true); + match(RPAREN); + unaryExpression(0); + } + } + catch (RecognitionException pe) { + synPredMatched531 = false; + } + rewind(_m531); +inputState.guessing--; + } + if ( synPredMatched531 ) { + lpb = LT(1); + lpb_AST = astFactory.create(lpb); + astFactory.makeASTRoot(currentAST, lpb_AST); + match(LPAREN); + if ( inputState.guessing==0 ) { + lpb_AST.setType(TYPECAST); + } + builtInTypeSpec(true); + astFactory.addASTChild(currentAST, returnAST); + match(RPAREN); + unaryExpression(0); + astFactory.addASTChild(currentAST, returnAST); + } + else { + boolean synPredMatched533 = false; + if (((LA(1)==LPAREN) && (LA(2)==IDENT))) { + int _m533 = mark(); + synPredMatched533 = true; + inputState.guessing++; + try { + { + match(LPAREN); + classTypeSpec(true); + match(RPAREN); + unaryExpressionNotPlusMinus(0); + } + } + catch (RecognitionException pe) { + synPredMatched533 = false; + } + rewind(_m533); +inputState.guessing--; + } + if ( synPredMatched533 ) { + lp = LT(1); + lp_AST = astFactory.create(lp); + astFactory.makeASTRoot(currentAST, lp_AST); + match(LPAREN); + if ( inputState.guessing==0 ) { + lp_AST.setType(TYPECAST); + } + classTypeSpec(true); + astFactory.addASTChild(currentAST, returnAST); + match(RPAREN); + unaryExpressionNotPlusMinus(0); + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_93.member(LA(1))) && (_tokenSet_3.member(LA(2)))) { + postfixExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + unaryExpressionNotPlusMinus_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = unaryExpressionNotPlusMinus_AST; + } + + public final void postfixExpression( + int lc_stmt + ) throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST postfixExpression_AST = null; + Token in = null; + AST in_AST = null; + Token de = null; + AST de_AST = null; + + pathExpression(lc_stmt); + astFactory.addASTChild(currentAST, returnAST); + { + if ((LA(1)==INC) && (_tokenSet_109.member(LA(2)))) { + in = LT(1); + in_AST = astFactory.create(in); + astFactory.makeASTRoot(currentAST, in_AST); + match(INC); + if ( inputState.guessing==0 ) { + in_AST.setType(POST_INC); + } + } + else if ((LA(1)==DEC) && (_tokenSet_109.member(LA(2)))) { + de = LT(1); + de_AST = astFactory.create(de); + astFactory.makeASTRoot(currentAST, de_AST); + match(DEC); + if ( inputState.guessing==0 ) { + de_AST.setType(POST_DEC); + } + } + else if ((_tokenSet_109.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + postfixExpression_AST = (AST)currentAST.root; + returnAST = postfixExpression_AST; + } + +/** Numeric, string, regexp, boolean, or null constant. */ + public final void constant() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST constant_AST = null; + + switch ( LA(1)) { + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + constantNumber(); + astFactory.addASTChild(currentAST, returnAST); + constant_AST = (AST)currentAST.root; + break; + } + case STRING_LITERAL: + { + AST tmp340_AST = null; + tmp340_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp340_AST); + match(STRING_LITERAL); + constant_AST = (AST)currentAST.root; + break; + } + case LITERAL_true: + { + AST tmp341_AST = null; + tmp341_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp341_AST); + match(LITERAL_true); + constant_AST = (AST)currentAST.root; + break; + } + case LITERAL_false: + { + AST tmp342_AST = null; + tmp342_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp342_AST); + match(LITERAL_false); + constant_AST = (AST)currentAST.root; + break; + } + case LITERAL_null: + { + AST tmp343_AST = null; + tmp343_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp343_AST); + match(LITERAL_null); + constant_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = constant_AST; + } + +/** object instantiation. + * Trees are built as illustrated by the following input/tree pairs: + * + * new T() + * + * new + * | + * T -- ELIST + * | + * arg1 -- arg2 -- .. -- argn + * + * new int[] + * + * new + * | + * int -- ARRAY_DECLARATOR + * + * new int[] {1,2} + * + * new + * | + * int -- ARRAY_DECLARATOR -- ARRAY_INIT + * | + * EXPR -- EXPR + * | | + * 1 2 + * + * new int[3] + * new + * | + * int -- ARRAY_DECLARATOR + * | + * EXPR + * | + * 3 + * + * new int[1][2] + * + * new + * | + * int -- ARRAY_DECLARATOR + * | + * ARRAY_DECLARATOR -- EXPR + * | | + * EXPR 1 + * | + * 2 + * + */ + public final void newExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST newExpression_AST = null; + AST ta_AST = null; + AST t_AST = null; + AST mca_AST = null; + AST cb_AST = null; + AST ad_AST = null; + Token first = LT(1); int start = mark(); + + try { // for error handling + match(LITERAL_new); + nls(); + { + switch ( LA(1)) { + case LT: + { + typeArguments(); + ta_AST = (AST)returnAST; + break; + } + case LBRACK: + case IDENT: + case LPAREN: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case IDENT: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + { + type(); + t_AST = (AST)returnAST; + break; + } + case LBRACK: + case LPAREN: + case NLS: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + { + switch ( LA(1)) { + case LPAREN: + case NLS: + { + nls(); + methodCallArgs(null); + mca_AST = (AST)returnAST; + { + if ((LA(1)==LCURLY) && (_tokenSet_56.member(LA(2)))) { + classBlock(); + cb_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + } + else if ((_tokenSet_104.member(LA(1))) && (_tokenSet_54.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + if ( inputState.guessing==0 ) { + newExpression_AST = (AST)currentAST.root; + mca_AST = mca_AST.getFirstChild(); + newExpression_AST = (AST)astFactory.make( (new ASTArray(5)).add(create(LITERAL_new,"new",first,LT(1))).add(ta_AST).add(t_AST).add(mca_AST).add(cb_AST)); + currentAST.root = newExpression_AST; + currentAST.child = newExpression_AST!=null &&newExpression_AST.getFirstChild()!=null ? + newExpression_AST.getFirstChild() : newExpression_AST; + currentAST.advanceChildToEnd(); + } + break; + } + case LBRACK: + { + newArrayDeclarator(); + ad_AST = (AST)returnAST; + if ( inputState.guessing==0 ) { + newExpression_AST = (AST)currentAST.root; + newExpression_AST = (AST)astFactory.make( (new ASTArray(4)).add(create(LITERAL_new,"new",first,LT(1))).add(ta_AST).add(t_AST).add(ad_AST)); + currentAST.root = newExpression_AST; + currentAST.child = newExpression_AST!=null &&newExpression_AST.getFirstChild()!=null ? + newExpression_AST.getFirstChild() : newExpression_AST; + currentAST.advanceChildToEnd(); + } + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + newExpression_AST = (AST)currentAST.root; + } + catch (RecognitionException e) { + if (inputState.guessing==0) { + + if (t_AST == null) { + reportError("missing type for constructor call", first); + newExpression_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_new,"new",first,LT(1))).add(ta_AST).add(null)); + // probably others to include - or make this the default? + if (e instanceof MismatchedTokenException || e instanceof NoViableAltException) { + rewind(start); + consumeUntil(NLS); + } + } else if (mca_AST == null && ad_AST == null) { + reportError("expecting '(' or '[' after type name to continue new expression", t_AST); + newExpression_AST = (AST)astFactory.make( (new ASTArray(3)).add(create(LITERAL_new,"new",first,LT(1))).add(ta_AST).add(t_AST)); + if (e instanceof MismatchedTokenException) { + rewind(start); + consume(); + consumeUntil(NLS); + } + } else { + throw e; + } + + } else { + throw e; + } + } + returnAST = newExpression_AST; + } + + public final void closableBlockConstructorExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST closableBlockConstructorExpression_AST = null; + + closableBlock(); + astFactory.addASTChild(currentAST, returnAST); + closableBlockConstructorExpression_AST = (AST)currentAST.root; + returnAST = closableBlockConstructorExpression_AST; + } + +/** + * A list constructor is a argument list enclosed in square brackets, without labels. + * Any argument can be decorated with a spread operator (*x), but not a label (a:x). + * Examples: [], [1], [1,2], [1,*l1,2], [*l1,*l2]. + * (The l1, l2 must be a sequence or null.) + *

+ * A map constructor is an argument list enclosed in square brackets, with labels everywhere, + * except on spread arguments, which stand for whole maps spliced in. + * A colon alone between the brackets also forces the expression to be an empty map constructor. + * Examples: [:], [a:1], [a:1,b:2], [a:1,*:m1,b:2], [*:m1,*:m2] + * (The m1, m2 must be a map or null.) + * Values associated with identical keys overwrite from left to right: + * [a:1,a:2] === [a:2] + *

+ * Some malformed constructor expressions are not detected in the parser, but in a post-pass. + * Bad examples: [1,b:2], [a:1,2], [:1]. + * (Note that method call arguments, by contrast, can be a mix of keyworded and non-keyworded arguments.) + */ + public final void listOrMapConstructorExpression() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST listOrMapConstructorExpression_AST = null; + Token lcon = null; + AST lcon_AST = null; + AST args_AST = null; + Token emcon = null; + AST emcon_AST = null; + boolean hasLabels = false; + + if ((LA(1)==LBRACK) && (_tokenSet_110.member(LA(2)))) { + lcon = LT(1); + lcon_AST = astFactory.create(lcon); + match(LBRACK); + argList(); + args_AST = (AST)returnAST; + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + hasLabels |= argListHasLabels; + } + match(RBRACK); + if ( inputState.guessing==0 ) { + listOrMapConstructorExpression_AST = (AST)currentAST.root; + int type = hasLabels ? MAP_CONSTRUCTOR : LIST_CONSTRUCTOR; + listOrMapConstructorExpression_AST = (AST)astFactory.make( (new ASTArray(2)).add(create(type,"[",lcon_AST,LT(1))).add(args_AST)); + + currentAST.root = listOrMapConstructorExpression_AST; + currentAST.child = listOrMapConstructorExpression_AST!=null &&listOrMapConstructorExpression_AST.getFirstChild()!=null ? + listOrMapConstructorExpression_AST.getFirstChild() : listOrMapConstructorExpression_AST; + currentAST.advanceChildToEnd(); + } + listOrMapConstructorExpression_AST = (AST)currentAST.root; + } + else if ((LA(1)==LBRACK) && (LA(2)==COLON)) { + emcon = LT(1); + emcon_AST = astFactory.create(emcon); + astFactory.makeASTRoot(currentAST, emcon_AST); + match(LBRACK); + match(COLON); + match(RBRACK); + if ( inputState.guessing==0 ) { + emcon_AST.setType(MAP_CONSTRUCTOR); + } + listOrMapConstructorExpression_AST = (AST)currentAST.root; + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + returnAST = listOrMapConstructorExpression_AST; + } + + public final void stringConstructorValuePart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST stringConstructorValuePart_AST = null; + + { + switch ( LA(1)) { + case IDENT: + { + identifier(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case LITERAL_this: + { + AST tmp348_AST = null; + tmp348_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp348_AST); + match(LITERAL_this); + break; + } + case LITERAL_super: + { + AST tmp349_AST = null; + tmp349_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp349_AST); + match(LITERAL_super); + break; + } + case LCURLY: + { + openOrClosableBlock(); + astFactory.addASTChild(currentAST, returnAST); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + stringConstructorValuePart_AST = (AST)currentAST.root; + returnAST = stringConstructorValuePart_AST; + } + + public final void newArrayDeclarator() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST newArrayDeclarator_AST = null; + Token lb = null; + AST lb_AST = null; + + { + int _cnt590=0; + _loop590: + do { + if ((LA(1)==LBRACK) && (_tokenSet_111.member(LA(2)))) { + lb = LT(1); + lb_AST = astFactory.create(lb); + astFactory.makeASTRoot(currentAST, lb_AST); + match(LBRACK); + if ( inputState.guessing==0 ) { + lb_AST.setType(ARRAY_DECLARATOR); + } + { + switch ( LA(1)) { + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LCURLY: + case LITERAL_this: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + expression(0); + astFactory.addASTChild(currentAST, returnAST); + break; + } + case RBRACK: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + match(RBRACK); + } + else { + if ( _cnt590>=1 ) { break _loop590; } else {throw new NoViableAltException(LT(1), getFilename());} + } + + _cnt590++; + } while (true); + } + newArrayDeclarator_AST = (AST)currentAST.root; + returnAST = newArrayDeclarator_AST; + } + +/** A single argument in (...) or [...]. Corresponds to to a method or closure parameter. + * May be labeled. May be modified by the spread operator '*' ('*:' for keywords). + */ + public final byte argument() throws RecognitionException, TokenStreamException { + byte hasLabelOrSpread = 0; + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST argument_AST = null; + Token c = null; + AST c_AST = null; + Token sp = null; + AST sp_AST = null; + boolean sce=false; + + { + boolean synPredMatched576 = false; + if (((_tokenSet_99.member(LA(1))) && (_tokenSet_100.member(LA(2))))) { + int _m576 = mark(); + synPredMatched576 = true; + inputState.guessing++; + try { + { + argumentLabelStart(); + } + } + catch (RecognitionException pe) { + synPredMatched576 = false; + } + rewind(_m576); +inputState.guessing--; + } + if ( synPredMatched576 ) { + argumentLabel(); + astFactory.addASTChild(currentAST, returnAST); + c = LT(1); + c_AST = astFactory.create(c); + astFactory.makeASTRoot(currentAST, c_AST); + match(COLON); + if ( inputState.guessing==0 ) { + c_AST.setType(LABELED_ARG); + } + if ( inputState.guessing==0 ) { + hasLabelOrSpread |= 1; + } + } + else if ((LA(1)==STAR)) { + sp = LT(1); + sp_AST = astFactory.create(sp); + astFactory.makeASTRoot(currentAST, sp_AST); + match(STAR); + if ( inputState.guessing==0 ) { + sp_AST.setType(SPREAD_ARG); + } + if ( inputState.guessing==0 ) { + hasLabelOrSpread |= 2; + } + { + switch ( LA(1)) { + case COLON: + { + match(COLON); + if ( inputState.guessing==0 ) { + sp_AST.setType(SPREAD_MAP_ARG); + } + if ( inputState.guessing==0 ) { + hasLabelOrSpread |= 1; + } + break; + } + case FINAL: + case ABSTRACT: + case STRICTFP: + case LITERAL_static: + case LITERAL_def: + case LBRACK: + case IDENT: + case STRING_LITERAL: + case LPAREN: + case AT: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LCURLY: + case LITERAL_this: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case PLUS: + case MINUS: + case LITERAL_false: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + case INC: + case DEC: + case BNOT: + case LNOT: + case STRING_CTOR_START: + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + } + else if ((_tokenSet_89.member(LA(1))) && (_tokenSet_112.member(LA(2)))) { + } + else { + throw new NoViableAltException(LT(1), getFilename()); + } + + } + sce=strictContextExpression(true); + astFactory.addASTChild(currentAST, returnAST); + if ( inputState.guessing==0 ) { + + require(LA(1) != COLON, + "illegal colon after argument expression", + "a complex label expression before a colon must be parenthesized"); + + } + argument_AST = (AST)currentAST.root; + returnAST = argument_AST; + return hasLabelOrSpread; + } + +/** For lookahead only. Fast approximate parse of an argumentLabel followed by a colon. */ + public final void argumentLabelStart() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST argumentLabelStart_AST = null; + + { + switch ( LA(1)) { + case IDENT: + { + AST tmp352_AST = null; + tmp352_AST = astFactory.create(LT(1)); + match(IDENT); + break; + } + case FINAL: + case ABSTRACT: + case UNUSED_GOTO: + case UNUSED_CONST: + case UNUSED_DO: + case STRICTFP: + case LITERAL_package: + case LITERAL_import: + case LITERAL_static: + case LITERAL_def: + case LITERAL_class: + case LITERAL_interface: + case LITERAL_enum: + case LITERAL_trait: + case LITERAL_extends: + case LITERAL_super: + case LITERAL_void: + case LITERAL_boolean: + case LITERAL_byte: + case LITERAL_char: + case LITERAL_short: + case LITERAL_int: + case LITERAL_float: + case LITERAL_long: + case LITERAL_double: + case LITERAL_as: + case LITERAL_private: + case LITERAL_public: + case LITERAL_protected: + case LITERAL_transient: + case LITERAL_native: + case LITERAL_threadsafe: + case LITERAL_synchronized: + case LITERAL_volatile: + case LITERAL_default: + case LITERAL_throws: + case LITERAL_implements: + case LITERAL_this: + case LITERAL_if: + case LITERAL_else: + case LITERAL_while: + case LITERAL_switch: + case LITERAL_for: + case LITERAL_in: + case LITERAL_return: + case LITERAL_break: + case LITERAL_continue: + case LITERAL_throw: + case LITERAL_assert: + case LITERAL_case: + case LITERAL_try: + case LITERAL_finally: + case LITERAL_catch: + case LITERAL_false: + case LITERAL_instanceof: + case LITERAL_new: + case LITERAL_null: + case LITERAL_true: + { + keywordPropertyNames(); + break; + } + case NUM_INT: + case NUM_FLOAT: + case NUM_LONG: + case NUM_DOUBLE: + case NUM_BIG_INT: + case NUM_BIG_DECIMAL: + { + constantNumber(); + break; + } + case STRING_LITERAL: + { + AST tmp353_AST = null; + tmp353_AST = astFactory.create(LT(1)); + match(STRING_LITERAL); + break; + } + case LBRACK: + case LPAREN: + case LCURLY: + case STRING_CTOR_START: + { + balancedBrackets(); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + } + AST tmp354_AST = null; + tmp354_AST = astFactory.create(LT(1)); + match(COLON); + returnAST = argumentLabelStart_AST; + } + +/** Numeric constant. */ + public final void constantNumber() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST constantNumber_AST = null; + + switch ( LA(1)) { + case NUM_INT: + { + AST tmp355_AST = null; + tmp355_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp355_AST); + match(NUM_INT); + constantNumber_AST = (AST)currentAST.root; + break; + } + case NUM_FLOAT: + { + AST tmp356_AST = null; + tmp356_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp356_AST); + match(NUM_FLOAT); + constantNumber_AST = (AST)currentAST.root; + break; + } + case NUM_LONG: + { + AST tmp357_AST = null; + tmp357_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp357_AST); + match(NUM_LONG); + constantNumber_AST = (AST)currentAST.root; + break; + } + case NUM_DOUBLE: + { + AST tmp358_AST = null; + tmp358_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp358_AST); + match(NUM_DOUBLE); + constantNumber_AST = (AST)currentAST.root; + break; + } + case NUM_BIG_INT: + { + AST tmp359_AST = null; + tmp359_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp359_AST); + match(NUM_BIG_INT); + constantNumber_AST = (AST)currentAST.root; + break; + } + case NUM_BIG_DECIMAL: + { + AST tmp360_AST = null; + tmp360_AST = astFactory.create(LT(1)); + astFactory.addASTChild(currentAST, tmp360_AST); + match(NUM_BIG_DECIMAL); + constantNumber_AST = (AST)currentAST.root; + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = constantNumber_AST; + } + +/** Fast lookahead across balanced brackets of all sorts. */ + public final void balancedBrackets() throws RecognitionException, TokenStreamException { + + returnAST = null; + ASTPair currentAST = new ASTPair(); + AST balancedBrackets_AST = null; + + switch ( LA(1)) { + case LPAREN: + { + AST tmp361_AST = null; + tmp361_AST = astFactory.create(LT(1)); + match(LPAREN); + balancedTokens(); + AST tmp362_AST = null; + tmp362_AST = astFactory.create(LT(1)); + match(RPAREN); + break; + } + case LBRACK: + { + AST tmp363_AST = null; + tmp363_AST = astFactory.create(LT(1)); + match(LBRACK); + balancedTokens(); + AST tmp364_AST = null; + tmp364_AST = astFactory.create(LT(1)); + match(RBRACK); + break; + } + case LCURLY: + { + AST tmp365_AST = null; + tmp365_AST = astFactory.create(LT(1)); + match(LCURLY); + balancedTokens(); + AST tmp366_AST = null; + tmp366_AST = astFactory.create(LT(1)); + match(RCURLY); + break; + } + case STRING_CTOR_START: + { + AST tmp367_AST = null; + tmp367_AST = astFactory.create(LT(1)); + match(STRING_CTOR_START); + balancedTokens(); + AST tmp368_AST = null; + tmp368_AST = astFactory.create(LT(1)); + match(STRING_CTOR_END); + break; + } + default: + { + throw new NoViableAltException(LT(1), getFilename()); + } + } + returnAST = balancedBrackets_AST; + } + + + public static final String[] _tokenNames = { + "<0>", + "EOF", + "<2>", + "NULL_TREE_LOOKAHEAD", + "BLOCK", + "MODIFIERS", + "OBJBLOCK", + "SLIST", + "METHOD_DEF", + "VARIABLE_DEF", + "INSTANCE_INIT", + "STATIC_INIT", + "TYPE", + "CLASS_DEF", + "INTERFACE_DEF", + "TRAIT_DEF", + "PACKAGE_DEF", + "ARRAY_DECLARATOR", + "EXTENDS_CLAUSE", + "IMPLEMENTS_CLAUSE", + "PARAMETERS", + "PARAMETER_DEF", + "LABELED_STAT", + "TYPECAST", + "INDEX_OP", + "POST_INC", + "POST_DEC", + "METHOD_CALL", + "EXPR", + "IMPORT", + "UNARY_MINUS", + "UNARY_PLUS", + "CASE_GROUP", + "ELIST", + "FOR_INIT", + "FOR_CONDITION", + "FOR_ITERATOR", + "EMPTY_STAT", + "\"final\"", + "\"abstract\"", + "\"goto\"", + "\"const\"", + "\"do\"", + "\"strictfp\"", + "SUPER_CTOR_CALL", + "CTOR_CALL", + "CTOR_IDENT", + "VARIABLE_PARAMETER_DEF", + "STRING_CONSTRUCTOR", + "STRING_CTOR_MIDDLE", + "CLOSABLE_BLOCK", + "IMPLICIT_PARAMETERS", + "SELECT_SLOT", + "DYNAMIC_MEMBER", + "LABELED_ARG", + "SPREAD_ARG", + "SPREAD_MAP_ARG", + "LIST_CONSTRUCTOR", + "MAP_CONSTRUCTOR", + "FOR_IN_ITERABLE", + "STATIC_IMPORT", + "ENUM_DEF", + "ENUM_CONSTANT_DEF", + "FOR_EACH_CLAUSE", + "ANNOTATION_DEF", + "ANNOTATIONS", + "ANNOTATION", + "ANNOTATION_MEMBER_VALUE_PAIR", + "ANNOTATION_FIELD_DEF", + "ANNOTATION_ARRAY_INIT", + "TYPE_ARGUMENTS", + "TYPE_ARGUMENT", + "TYPE_PARAMETERS", + "TYPE_PARAMETER", + "WILDCARD_TYPE", + "TYPE_UPPER_BOUNDS", + "TYPE_LOWER_BOUNDS", + "CLOSURE_LIST", + "MULTICATCH", + "MULTICATCH_TYPES", + "a script header", + "\"package\"", + "\"import\"", + "\"static\"", + "\"def\"", + "'['", + "']'", + "an identifier", + "a string literal", + "'<'", + "'.'", + "'('", + "\"class\"", + "\"interface\"", + "\"enum\"", + "\"trait\"", + "'@'", + "'?'", + "\"extends\"", + "\"super\"", + "'>'", + "','", + "'>>'", + "'>>>'", + "\"void\"", + "\"boolean\"", + "\"byte\"", + "\"char\"", + "\"short\"", + "\"int\"", + "\"float\"", + "\"long\"", + "\"double\"", + "'*'", + "\"as\"", + "\"private\"", + "\"public\"", + "\"protected\"", + "\"transient\"", + "\"native\"", + "\"threadsafe\"", + "\"synchronized\"", + "\"volatile\"", + "')'", + "'='", + "'&'", + "'{'", + "'}'", + "';'", + "\"default\"", + "\"throws\"", + "\"implements\"", + "\"this\"", + "'...'", + "'|'", + "'->'", + "':'", + "\"if\"", + "\"else\"", + "\"while\"", + "\"switch\"", + "\"for\"", + "\"in\"", + "\"return\"", + "\"break\"", + "\"continue\"", + "\"throw\"", + "\"assert\"", + "'+'", + "'-'", + "\"case\"", + "\"try\"", + "\"finally\"", + "\"catch\"", + "'*.'", + "'?.'", + "'.&'", + "\"false\"", + "\"instanceof\"", + "\"new\"", + "\"null\"", + "\"true\"", + "'+='", + "'-='", + "'*='", + "'/='", + "'%='", + "'>>='", + "'>>>='", + "'<<='", + "'&='", + "'^='", + "'|='", + "'**='", + "'?:'", + "'||'", + "'&&'", + "'^'", + "'=~'", + "'==~'", + "'!='", + "'=='", + "'==='", + "'!=='", + "'<=>'", + "'<='", + "'>='", + "'<<'", + "'..'", + "'..<'", + "'++'", + "'/'", + "'%'", + "'--'", + "'**'", + "'~'", + "'!'", + "STRING_CTOR_START", + "a string literal end", + "a numeric literal", + "NUM_FLOAT", + "NUM_LONG", + "NUM_DOUBLE", + "NUM_BIG_INT", + "NUM_BIG_DECIMAL", + "some newlines, whitespace or comments", + "'$'", + "whitespace", + "a newline", + "a newline", + "a single line comment", + "a multi-line comment", + "a string character", + "a multiline regular expression literal", + "a multiline dollar escaping regular expression literal", + "a multiline regular expression literal end", + "a multiline dollar escaping regular expression literal end", + "ESCAPED_SLASH", + "ESCAPED_DOLLAR", + "a multiline regular expression character", + "a multiline dollar escaping regular expression character", + "an escape sequence", + "a newline inside a string", + "a hexadecimal digit", + "a character", + "a letter", + "a digit", + "a sequence of digits and underscores, bordered by digits", + "a sequence of digits and underscores with maybe underscore starting", + "an exponent", + "a float or double suffix", + "a big decimal suffix" + }; + + protected void buildTokenTypeASTClassMap() { + tokenTypeToASTClassMap=null; + }; + + private static final long[] mk_tokenSet_0() { + long[] data = new long[8]; + data[0]=2L; + data[1]=4840357888L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_0 = new BitSet(mk_tokenSet_0()); + private static final long[] mk_tokenSet_1() { + long[] data = new long[8]; + data[0]=14018773254146L; + data[1]=5186456864203472896L; + data[2]=4611686034009209361L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_1 = new BitSet(mk_tokenSet_1()); + private static final long[] mk_tokenSet_2() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-576460889742508032L; + data[2]=-1L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_2 = new BitSet(mk_tokenSet_2()); + private static final long[] mk_tokenSet_3() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-131072L; + data[2]=-1L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_3 = new BitSet(mk_tokenSet_3()); + private static final long[] mk_tokenSet_4() { + long[] data = new long[16]; + data[0]=-14L; + for (int i = 1; i<=2; i++) { data[i]=-1L; } + data[3]=1099511627775L; + return data; + } + public static final BitSet _tokenSet_4 = new BitSet(mk_tokenSet_4()); + private static final long[] mk_tokenSet_5() { + long[] data = { 0L, 4303749120L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_5 = new BitSet(mk_tokenSet_5()); + private static final long[] mk_tokenSet_6() { + long[] data = new long[8]; + data[0]=9620726743042L; + data[1]=-4036915073080492032L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_6 = new BitSet(mk_tokenSet_6()); + private static final long[] mk_tokenSet_7() { + long[] data = { 0L, 8781824L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_7 = new BitSet(mk_tokenSet_7()); + private static final long[] mk_tokenSet_8() { + long[] data = new long[8]; + data[0]=9620726743042L; + data[1]=-4036915073617756160L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_8 = new BitSet(mk_tokenSet_8()); + private static final long[] mk_tokenSet_9() { + long[] data = new long[8]; + data[2]=1025L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_9 = new BitSet(mk_tokenSet_9()); + private static final long[] mk_tokenSet_10() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=5186456864203472896L; + data[2]=4611686034009210385L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_10 = new BitSet(mk_tokenSet_10()); + private static final long[] mk_tokenSet_11() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-9223372036854775808L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_11 = new BitSet(mk_tokenSet_11()); + private static final long[] mk_tokenSet_12() { + long[] data = new long[8]; + data[0]=580267261558786L; + data[1]=-131072L; + data[2]=-33L; + data[3]=16383L; + return data; + } + public static final BitSet _tokenSet_12 = new BitSet(mk_tokenSet_12()); + private static final long[] mk_tokenSet_13() { + long[] data = { 9620726743040L, 574208956786278400L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_13 = new BitSet(mk_tokenSet_13()); + private static final long[] mk_tokenSet_14() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574208957365092352L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_14 = new BitSet(mk_tokenSet_14()); + private static final long[] mk_tokenSet_15() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770807907549184L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_15 = new BitSet(mk_tokenSet_15()); + private static final long[] mk_tokenSet_16() { + long[] data = { 9620726743040L, 574770807236460544L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_16 = new BitSet(mk_tokenSet_16()); + private static final long[] mk_tokenSet_17() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770807892869120L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_17 = new BitSet(mk_tokenSet_17()); + private static final long[] mk_tokenSet_18() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=-4036915172651302912L; + data[2]=4611686034009209521L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_18 = new BitSet(mk_tokenSet_18()); + private static final long[] mk_tokenSet_19() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=5186456864203472896L; + data[2]=4611686034009209360L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_19 = new BitSet(mk_tokenSet_19()); + private static final long[] mk_tokenSet_20() { + long[] data = new long[8]; + data[1]=4612247903390400512L; + data[2]=4611686033999790096L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_20 = new BitSet(mk_tokenSet_20()); + private static final long[] mk_tokenSet_21() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-9223372032013762560L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_21 = new BitSet(mk_tokenSet_21()); + private static final long[] mk_tokenSet_22() { + long[] data = { 9620726743040L, 574208960812810240L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_22 = new BitSet(mk_tokenSet_22()); + private static final long[] mk_tokenSet_23() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574208960821198848L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_23 = new BitSet(mk_tokenSet_23()); + private static final long[] mk_tokenSet_24() { + long[] data = new long[8]; + data[0]=14018773254146L; + data[1]=-4036915172617748480L; + data[2]=4611686034013404691L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_24 = new BitSet(mk_tokenSet_24()); + private static final long[] mk_tokenSet_25() { + long[] data = { 0L, 561850450182144L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_25 = new BitSet(mk_tokenSet_25()); + private static final long[] mk_tokenSet_26() { + long[] data = { 0L, 127926272L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_26 = new BitSet(mk_tokenSet_26()); + private static final long[] mk_tokenSet_27() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-8070450394674757632L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_27 = new BitSet(mk_tokenSet_27()); + private static final long[] mk_tokenSet_28() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-8070450394808975360L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_28 = new BitSet(mk_tokenSet_28()); + private static final long[] mk_tokenSet_29() { + long[] data = new long[8]; + data[1]=8388608L; + data[2]=4L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_29 = new BitSet(mk_tokenSet_29()); + private static final long[] mk_tokenSet_30() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-4611686018427387904L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_30 = new BitSet(mk_tokenSet_30()); + private static final long[] mk_tokenSet_31() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=-4036915172651302912L; + data[2]=4611686034009209361L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_31 = new BitSet(mk_tokenSet_31()); + private static final long[] mk_tokenSet_32() { + long[] data = { 0L, 111149056L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_32 = new BitSet(mk_tokenSet_32()); + private static final long[] mk_tokenSet_33() { + long[] data = { 2L, 1729382394353418240L, 16641L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_33 = new BitSet(mk_tokenSet_33()); + private static final long[] mk_tokenSet_34() { + long[] data = { 9620726743040L, 574770807355998208L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_34 = new BitSet(mk_tokenSet_34()); + private static final long[] mk_tokenSet_35() { + long[] data = { 9620726743040L, 574770807288889344L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_35 = new BitSet(mk_tokenSet_35()); + private static final long[] mk_tokenSet_36() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=5764043533593739264L; + data[2]=4611686035137494558L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_36 = new BitSet(mk_tokenSet_36()); + private static final long[] mk_tokenSet_37() { + long[] data = new long[8]; + data[0]=9620726743042L; + data[1]=-8072140335660269568L; + data[2]=33L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_37 = new BitSet(mk_tokenSet_37()); + private static final long[] mk_tokenSet_38() { + long[] data = new long[8]; + data[1]=561859040116736L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_38 = new BitSet(mk_tokenSet_38()); + private static final long[] mk_tokenSet_39() { + long[] data = { 0L, 893353197568L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_39 = new BitSet(mk_tokenSet_39()); + private static final long[] mk_tokenSet_40() { + long[] data = new long[8]; + data[1]=4611686018563702784L; + data[3]=32L; + return data; + } + public static final BitSet _tokenSet_40 = new BitSet(mk_tokenSet_40()); + private static final long[] mk_tokenSet_41() { + long[] data = new long[16]; + data[0]=-16L; + data[1]=4035225265983455231L; + data[2]=-1L; + data[3]=1099511627679L; + return data; + } + public static final BitSet _tokenSet_41 = new BitSet(mk_tokenSet_41()); + private static final long[] mk_tokenSet_42() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770811296546816L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_42 = new BitSet(mk_tokenSet_42()); + private static final long[] mk_tokenSet_43() { + long[] data = { 0L, 561858805235712L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_43 = new BitSet(mk_tokenSet_43()); + private static final long[] mk_tokenSet_44() { + long[] data = new long[8]; + data[0]=2L; + data[1]=799014912L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_44 = new BitSet(mk_tokenSet_44()); + private static final long[] mk_tokenSet_45() { + long[] data = { 9620726743040L, 574208952490262528L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_45 = new BitSet(mk_tokenSet_45()); + private static final long[] mk_tokenSet_46() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770811447541760L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_46 = new BitSet(mk_tokenSet_46()); + private static final long[] mk_tokenSet_47() { + long[] data = new long[8]; + data[1]=4612247907685367808L; + data[2]=4611686033999790096L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_47 = new BitSet(mk_tokenSet_47()); + private static final long[] mk_tokenSet_48() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=-1152921642045931520L; + data[2]=-70351564308481L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_48 = new BitSet(mk_tokenSet_48()); + private static final long[] mk_tokenSet_49() { + long[] data = { 17317308137472L, 575896758414868480L, 16706960926L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_49 = new BitSet(mk_tokenSet_49()); + private static final long[] mk_tokenSet_50() { + long[] data = new long[8]; + data[1]=4303749120L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_50 = new BitSet(mk_tokenSet_50()); + private static final long[] mk_tokenSet_51() { + long[] data = new long[8]; + data[1]=4612247903390400512L; + data[2]=4611756402743967760L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_51 = new BitSet(mk_tokenSet_51()); + private static final long[] mk_tokenSet_52() { + long[] data = new long[8]; + data[1]=4612247911980335104L; + data[2]=4611686033999790096L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_52 = new BitSet(mk_tokenSet_52()); + private static final long[] mk_tokenSet_53() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-2306406865506009088L; + data[2]=4611756386701803423L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_53 = new BitSet(mk_tokenSet_53()); + private static final long[] mk_tokenSet_54() { + long[] data = new long[8]; + data[0]=580267261558786L; + data[1]=-131072L; + data[2]=-1L; + data[3]=16383L; + return data; + } + public static final BitSet _tokenSet_54 = new BitSet(mk_tokenSet_54()); + private static final long[] mk_tokenSet_55() { + long[] data = new long[8]; + data[1]=-4611685876013989888L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_55 = new BitSet(mk_tokenSet_55()); + private static final long[] mk_tokenSet_56() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=-4036915207164395520L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_56 = new BitSet(mk_tokenSet_56()); + private static final long[] mk_tokenSet_57() { + long[] data = new long[8]; + data[0]=14018773254146L; + data[1]=-4036915172550639616L; + data[2]=4611686034013404691L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_57 = new BitSet(mk_tokenSet_57()); + private static final long[] mk_tokenSet_58() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-4611123119988408320L; + data[2]=4195339L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_58 = new BitSet(mk_tokenSet_58()); + private static final long[] mk_tokenSet_59() { + long[] data = { 9620726743040L, 574208956794667008L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_59 = new BitSet(mk_tokenSet_59()); + private static final long[] mk_tokenSet_60() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574208957465755648L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_60 = new BitSet(mk_tokenSet_60()); + private static final long[] mk_tokenSet_61() { + long[] data = new long[8]; + data[1]=-8070450394674757632L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_61 = new BitSet(mk_tokenSet_61()); + private static final long[] mk_tokenSet_62() { + long[] data = new long[8]; + data[1]=-9223371899415822336L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_62 = new BitSet(mk_tokenSet_62()); + private static final long[] mk_tokenSet_63() { + long[] data = new long[8]; + data[0]=9620726743042L; + data[1]=-4036915069725442048L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_63 = new BitSet(mk_tokenSet_63()); + private static final long[] mk_tokenSet_64() { + long[] data = new long[8]; + data[1]=-9223371895112466432L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_64 = new BitSet(mk_tokenSet_64()); + private static final long[] mk_tokenSet_65() { + long[] data = new long[8]; + data[1]=4303355904L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_65 = new BitSet(mk_tokenSet_65()); + private static final long[] mk_tokenSet_66() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770807823663104L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_66 = new BitSet(mk_tokenSet_66()); + private static final long[] mk_tokenSet_67() { + long[] data = { 9620726743040L, 574770807270014976L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_67 = new BitSet(mk_tokenSet_67()); + private static final long[] mk_tokenSet_68() { + long[] data = new long[8]; + data[1]=-4611686018427387904L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_68 = new BitSet(mk_tokenSet_68()); + private static final long[] mk_tokenSet_69() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=-4036915035178795008L; + data[2]=4611686034009209361L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_69 = new BitSet(mk_tokenSet_69()); + private static final long[] mk_tokenSet_70() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=-4036915172617748480L; + data[2]=4611686034009209361L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_70 = new BitSet(mk_tokenSet_70()); + private static final long[] mk_tokenSet_71() { + long[] data = { 0L, 34393292800L, 16L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_71 = new BitSet(mk_tokenSet_71()); + private static final long[] mk_tokenSet_72() { + long[] data = new long[8]; + data[1]=561859174334464L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_72 = new BitSet(mk_tokenSet_72()); + private static final long[] mk_tokenSet_73() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=-576460889742508032L; + data[2]=-1L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_73 = new BitSet(mk_tokenSet_73()); + private static final long[] mk_tokenSet_74() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=5186456864203472896L; + data[2]=4611686034009209361L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_74 = new BitSet(mk_tokenSet_74()); + private static final long[] mk_tokenSet_75() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-3459328370112856064L; + data[2]=4611686035137494943L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_75 = new BitSet(mk_tokenSet_75()); + private static final long[] mk_tokenSet_76() { + long[] data = { 0L, 111149056L, 32L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_76 = new BitSet(mk_tokenSet_76()); + private static final long[] mk_tokenSet_77() { + long[] data = new long[8]; + data[1]=1729382394357612544L; + data[2]=128L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_77 = new BitSet(mk_tokenSet_77()); + private static final long[] mk_tokenSet_78() { + long[] data = { 0L, 109051904L, 64L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_78 = new BitSet(mk_tokenSet_78()); + private static final long[] mk_tokenSet_79() { + long[] data = new long[8]; + data[0]=274877906944L; + data[1]=561854746198016L; + data[2]=160L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_79 = new BitSet(mk_tokenSet_79()); + private static final long[] mk_tokenSet_80() { + long[] data = new long[8]; + data[0]=14018773254144L; + data[1]=-2883993530504839168L; + data[2]=4611686034009209521L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_80 = new BitSet(mk_tokenSet_80()); + private static final long[] mk_tokenSet_81() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=5186456860176678912L; + data[2]=4611686034000805905L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_81 = new BitSet(mk_tokenSet_81()); + private static final long[] mk_tokenSet_82() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=-137439084544L; + data[2]=-1L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_82 = new BitSet(mk_tokenSet_82()); + private static final long[] mk_tokenSet_83() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770807876091904L; + data[2]=16640L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_83 = new BitSet(mk_tokenSet_83()); + private static final long[] mk_tokenSet_84() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=574770807876091904L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_84 = new BitSet(mk_tokenSet_84()); + private static final long[] mk_tokenSet_85() { + long[] data = new long[8]; + data[0]=2L; + data[1]=576460889754959872L; + data[2]=1L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_85 = new BitSet(mk_tokenSet_85()); + private static final long[] mk_tokenSet_86() { + long[] data = new long[8]; + data[1]=4611686018427387904L; + data[2]=16777216L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_86 = new BitSet(mk_tokenSet_86()); + private static final long[] mk_tokenSet_87() { + long[] data = new long[8]; + data[1]=4612247903390400512L; + data[2]=4611686033999790096L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_87 = new BitSet(mk_tokenSet_87()); + private static final long[] mk_tokenSet_88() { + long[] data = new long[8]; + data[0]=2L; + data[1]=-8646911147108204544L; + data[2]=4195331L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_88 = new BitSet(mk_tokenSet_88()); + private static final long[] mk_tokenSet_89() { + long[] data = new long[8]; + data[0]=9620726743040L; + data[1]=5186456860176678912L; + data[2]=4611686034000805904L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_89 = new BitSet(mk_tokenSet_89()); + private static final long[] mk_tokenSet_90() { + long[] data = { 0L, 824633720832L, 4035225266123964416L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_90 = new BitSet(mk_tokenSet_90()); + private static final long[] mk_tokenSet_91() { + long[] data = new long[8]; + data[0]=-16L; + data[1]=-4611686018427387905L; + data[2]=-1L; + data[3]=1099511627775L; + return data; + } + public static final BitSet _tokenSet_91 = new BitSet(mk_tokenSet_91()); + private static final long[] mk_tokenSet_92() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=5187582776995348480L; + data[2]=4611686035137494558L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_92 = new BitSet(mk_tokenSet_92()); + private static final long[] mk_tokenSet_93() { + long[] data = new long[8]; + data[1]=4612247903390400512L; + data[2]=15569256464L; + data[3]=8096L; + return data; + } + public static final BitSet _tokenSet_93 = new BitSet(mk_tokenSet_93()); + private static final long[] mk_tokenSet_94() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-3458765415763804160L; + data[2]=4611686035607257023L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_94 = new BitSet(mk_tokenSet_94()); + private static final long[] mk_tokenSet_95() { + long[] data = new long[8]; + data[1]=4611686018630811648L; + data[2]=469762048L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_95 = new BitSet(mk_tokenSet_95()); + private static final long[] mk_tokenSet_96() { + long[] data = new long[8]; + data[0]=9620726743042L; + data[1]=5186456860176678912L; + data[2]=4611686034000805904L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_96 = new BitSet(mk_tokenSet_96()); + private static final long[] mk_tokenSet_97() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-576460752303554560L; + data[2]=-1L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_97 = new BitSet(mk_tokenSet_97()); + private static final long[] mk_tokenSet_98() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=5187582776995348480L; + data[2]=4611686035137494558L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_98 = new BitSet(mk_tokenSet_98()); + private static final long[] mk_tokenSet_99() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=5187582776995348480L; + data[2]=16706960926L; + data[3]=8096L; + return data; + } + public static final BitSet _tokenSet_99 = new BitSet(mk_tokenSet_99()); + private static final long[] mk_tokenSet_100() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=-4035226305573289984L; + data[2]=4611686035137494975L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_100 = new BitSet(mk_tokenSet_100()); + private static final long[] mk_tokenSet_101() { + long[] data = { 17317308137472L, 575896758406479872L, 16706960926L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_101 = new BitSet(mk_tokenSet_101()); + private static final long[] mk_tokenSet_102() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-131072L; + data[2]=-33L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_102 = new BitSet(mk_tokenSet_102()); + private static final long[] mk_tokenSet_103() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=5187582781288218624L; + data[2]=16706960926L; + data[3]=32L; + return data; + } + public static final BitSet _tokenSet_103 = new BitSet(mk_tokenSet_103()); + private static final long[] mk_tokenSet_104() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-4295098368L; + data[2]=-33L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_104 = new BitSet(mk_tokenSet_104()); + private static final long[] mk_tokenSet_105() { + long[] data = { 0L, 68753031168L, 432345564227584000L, 0L, 0L, 0L}; + return data; + } + public static final BitSet _tokenSet_105 = new BitSet(mk_tokenSet_105()); + private static final long[] mk_tokenSet_106() { + long[] data = new long[8]; + data[1]=561850450182144L; + data[3]=8192L; + return data; + } + public static final BitSet _tokenSet_106 = new BitSet(mk_tokenSet_106()); + private static final long[] mk_tokenSet_107() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-563847702380544L; + data[2]=4755801206033481695L; + data[3]=16314L; + return data; + } + public static final BitSet _tokenSet_107 = new BitSet(mk_tokenSet_107()); + private static final long[] mk_tokenSet_108() { + long[] data = new long[8]; + data[1]=562949953421312L; + data[2]=-9223372036854775808L; + data[3]=1L; + return data; + } + public static final BitSet _tokenSet_108 = new BitSet(mk_tokenSet_108()); + private static final long[] mk_tokenSet_109() { + long[] data = new long[8]; + data[0]=17317308137474L; + data[1]=-4362207232L; + data[2]=-469762081L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_109 = new BitSet(mk_tokenSet_109()); + private static final long[] mk_tokenSet_110() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=5188145731247931392L; + data[2]=4611686035137494558L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_110 = new BitSet(mk_tokenSet_110()); + private static final long[] mk_tokenSet_111() { + long[] data = new long[8]; + data[1]=4612247903394594816L; + data[2]=4611686033999790096L; + data[3]=8122L; + return data; + } + public static final BitSet _tokenSet_111 = new BitSet(mk_tokenSet_111()); + private static final long[] mk_tokenSet_112() { + long[] data = new long[8]; + data[0]=17317308137472L; + data[1]=-131072L; + data[2]=-1L; + data[3]=16319L; + return data; + } + public static final BitSet _tokenSet_112 = new BitSet(mk_tokenSet_112()); + + } diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyRecognizer.smap b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyRecognizer.smap new file mode 100644 index 0000000000..810a49c1fb --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyRecognizer.smap @@ -0,0 +1,11564 @@ +SMAP +GroovyRecognizer.java +G +*S G +*F ++ 0 groovy.g +groovy.g +*L +25:3 +25:5 +25:6 +25:8 +25:9 +25:10 +25:11 +25:13 +25:14 +256:220 +257:221 +258:222 +259:223 +260:224 +261:225 +262:226 +263:227 +264:228 +265:229 +266:230 +267:231 +268:232 +269:233 +270:234 +271:235 +272:236 +273:237 +275:239 +276:240 +278:242 +279:243 +281:245 +282:246 +283:247 +285:249 +286:250 +287:251 +289:253 +290:254 +291:255 +293:257 +294:258 +295:259 +296:260 +297:261 +299:263 +300:264 +301:265 +302:266 +303:267 +304:268 +305:269 +306:270 +307:271 +308:272 +309:273 +310:274 +311:275 +312:276 +313:277 +314:278 +315:279 +317:281 +318:282 +319:283 +320:284 +321:285 +322:286 +323:287 +324:288 +325:289 +326:290 +327:291 +329:293 +330:294 +331:295 +333:297 +334:298 +335:299 +337:301 +338:302 +339:303 +341:305 +342:306 +343:307 +344:308 +345:309 +346:310 +347:311 +348:312 +349:313 +351:315 +352:316 +353:317 +354:318 +355:319 +356:320 +357:321 +358:322 +359:323 +360:324 +361:325 +362:326 +363:327 +364:328 +365:329 +366:330 +367:331 +369:333 +371:335 +372:336 +373:337 +375:339 +376:340 +377:341 +378:342 +379:343 +380:344 +381:345 +382:346 +383:347 +384:348 +385:349 +386:350 +387:351 +389:353 +390:354 +391:355 +392:356 +393:357 +394:358 +395:359 +396:360 +397:361 +400:364 +401:365 +402:366 +403:367 +404:368 +405:369 +406:370 +407:371 +408:372 +409:373 +410:374 +412:376 +413:377 +414:378 +415:379 +416:380 +417:381 +418:382 +419:383 +420:384 +421:385 +422:386 +423:387 +424:388 +425:389 +426:390 +427:391 +428:392 +429:393 +430:394 +431:395 +432:396 +434:398 +435:399 +436:400 +437:401 +438:402 +440:404 +441:405 +442:406 +443:407 +444:408 +445:409 +446:410 +447:411 +448:412 +450:414 +451:415 +452:416 +453:417 +454:418 +455:419 +456:420 +457:421 +458:422 +459:423 +460:424 +462:426 +463:427 +464:428 +465:429 +466:430 +467:431 +469:433 +470:434 +471:435 +472:436 +473:437 +474:438 +476:440 +477:441 +478:442 +479:443 +480:444 +481:445 +482:446 +483:447 +484:448 +485:449 +486:450 +488:452 +489:453 +490:454 +491:455 +492:456 +493:457 +494:458 +495:459 +496:460 +497:461 +498:462 +499:463 +501:465 +502:466 +503:467 +504:468 +506:470 +507:471 +508:472 +509:473 +511:475 +512:476 +513:477 +515:479 +516:480 +517:481 +518:482 +519:483 +520:484 +521:485 +522:486 +523:487 +524:488 +525:489 +526:490 +527:491 +529:493 +530:494 +531:495 +533:497 +534:498 +535:499 +536:500 +537:501 +538:502 +539:503 +541:505 +542:506 +543:507 +544:508 +545:509 +546:510 +547:511 +549:513 +550:514 +551:515 +553:517 +554:518 +555:519 +556:520 +557:521 +558:522 +559:523 +561:525 +562:526 +563:527 +565:529 +566:530 +567:531 +568:532 +570:534 +571:535 +572:536 +573:537 +575:539 +576:540 +577:541 +579:543 +580:544 +581:545 +583:547 +584:548 +585:549 +586:550 +587:551 +588:552 +590:554 +591:555 +592:556 +593:557 +594:558 +595:559 +596:560 +598:562 +599:563 +600:564 +601:565 +602:566 +603:567 +604:568 +605:569 +606:570 +607:571 +608:572 +609:573 +610:574 +611:575 +616:606 +616:608 +616:609 +616:610 +616:612 +616:904 +616:916 +616:917 +619:614 +619:615 +619:616 +619:617 +619:690 +619:691 +619:692 +619:693 +619:694 +622:696 +625:698 +625:699 +625:700 +625:701 +625:702 +625:703 +625:705 +625:706 +625:708 +625:709 +625:710 +625:711 +625:712 +625:713 +625:714 +625:715 +625:716 +625:717 +625:718 +625:802 +625:803 +625:804 +625:805 +626:719 +626:721 +626:722 +626:723 +626:724 +626:725 +626:726 +626:727 +626:728 +626:729 +626:730 +626:731 +626:732 +626:733 +626:734 +626:735 +626:736 +626:737 +626:738 +626:739 +626:740 +626:741 +626:742 +626:743 +626:744 +626:745 +626:746 +626:747 +626:748 +626:749 +626:750 +626:751 +626:752 +626:753 +626:754 +626:755 +626:756 +626:757 +626:758 +626:759 +626:760 +626:761 +626:762 +626:763 +626:764 +626:765 +626:766 +626:767 +626:768 +626:769 +626:770 +626:771 +626:772 +626:773 +626:774 +626:775 +626:776 +626:777 +626:778 +626:779 +626:780 +626:781 +626:782 +626:783 +626:784 +626:785 +626:786 +626:787 +626:796 +626:797 +626:798 +626:799 +626:800 +631:808 +631:809 +631:810 +631:811 +631:812 +631:814 +631:815 +631:816 +631:817 +631:818 +631:819 +631:820 +631:821 +631:822 +631:823 +631:824 +631:825 +631:826 +631:827 +631:828 +631:829 +631:830 +631:831 +631:832 +631:833 +631:834 +631:835 +631:836 +631:837 +631:838 +631:839 +631:840 +631:841 +631:842 +631:843 +631:844 +631:845 +631:846 +631:847 +631:848 +631:849 +631:850 +631:851 +631:852 +631:853 +631:854 +631:855 +631:856 +631:857 +631:858 +631:859 +631:860 +631:861 +631:862 +631:863 +631:864 +631:865 +631:866 +631:867 +631:868 +631:869 +631:870 +631:871 +631:872 +631:873 +631:874 +631:875 +631:876 +631:877 +631:878 +631:879 +631:880 +631:889 +631:890 +631:891 +631:892 +631:893 +631:895 +631:896 +631:897 +631:898 +631:900 +631:901 +632:902 +634:903 +635:905 +635:906 +635:912 +635:913 +635:914 +635:915 +636:908 +637:909 +638:910 +644:1624 +644:1625 +644:1627 +644:1628 +644:1629 +644:1635 +644:1636 +645:1631 +645:1632 +645:1633 +645:1634 +649:971 +649:973 +649:974 +649:975 +649:978 +649:1018 +649:1019 +654:976 +654:977 +654:980 +654:981 +654:982 +654:984 +654:985 +654:986 +654:987 +654:988 +654:997 +654:998 +654:999 +654:1000 +654:1001 +655:1003 +655:1004 +655:1012 +655:1014 +655:1015 +656:1006 +656:1017 +657:1007 +658:1008 +659:1009 +660:1010 +666:1878 +666:1880 +666:1881 +666:1882 +666:1885 +666:1960 +666:1961 +674:1883 +674:1884 +674:1887 +674:1888 +674:1889 +674:1890 +674:1892 +674:1893 +674:1894 +674:1895 +674:1896 +674:1897 +674:1912 +674:1913 +674:1914 +674:1915 +674:1916 +674:1919 +674:1920 +674:1921 +674:1922 +674:1923 +674:1936 +674:1937 +674:1938 +674:1939 +674:1940 +675:1942 +675:1943 +675:1954 +675:1956 +675:1957 +676:1945 +676:1959 +677:1946 +678:1947 +679:1948 +680:1949 +681:1950 +682:1951 +683:1952 +689:2077 +689:2078 +689:2079 +689:2081 +689:2082 +689:2083 +689:2090 +689:2171 +689:2172 +689:2173 +689:2174 +689:2175 +689:2176 +689:2177 +690:2084 +690:2091 +690:2092 +690:2093 +690:2094 +690:2095 +691:2096 +691:2097 +691:2098 +691:2099 +691:2101 +691:2102 +692:2085 +692:2104 +692:2107 +692:2108 +692:2109 +692:2110 +692:2111 +693:2112 +693:2113 +693:2114 +693:2115 +693:2117 +693:2118 +694:2086 +694:2120 +694:2123 +694:2124 +694:2125 +694:2126 +694:2127 +695:2128 +695:2129 +695:2130 +695:2131 +695:2133 +695:2134 +696:2087 +696:2136 +696:2139 +696:2140 +696:2141 +696:2142 +696:2143 +697:2144 +697:2145 +697:2146 +697:2147 +697:2149 +697:2150 +698:2088 +698:2152 +698:2155 +698:2156 +698:2157 +698:2158 +698:2159 +699:2160 +699:2161 +699:2162 +699:2163 +699:2165 +699:2166 +700:2168 +711:2480 +711:2489 +711:2491 +711:2492 +711:2493 +711:2500 +711:2567 +711:2568 +711:2569 +711:2570 +711:2571 +711:2572 +711:2573 +712:2481 +713:2482 +714:2483 +714:2494 +714:2501 +714:2502 +714:2503 +714:2504 +714:2505 +714:2506 +714:2507 +714:2508 +714:2509 +714:2510 +714:2511 +714:2512 +714:2513 +714:2514 +714:2515 +714:2516 +714:2517 +715:2484 +715:2495 +715:2519 +715:2520 +715:2521 +715:2522 +715:2524 +715:2525 +715:2526 +715:2527 +716:2485 +716:2496 +716:2530 +716:2531 +717:2486 +717:2532 +717:2533 +717:2534 +717:2535 +717:2537 +717:2538 +718:2487 +719:2488 +720:2497 +720:2542 +720:2543 +720:2544 +720:2545 +720:2546 +720:2547 +720:2548 +720:2549 +720:2550 +720:2551 +720:2552 +720:2553 +720:2554 +721:2498 +721:2555 +721:2556 +722:2557 +722:2558 +722:2559 +722:2560 +722:2562 +722:2563 +725:2785 +725:2787 +725:2788 +725:2789 +725:2816 +725:2817 +728:2790 +728:2795 +728:2796 +729:2791 +729:2797 +729:2798 +730:2792 +730:2799 +730:2800 +731:2793 +731:2801 +731:2802 +732:2803 +732:2804 +732:2811 +732:2813 +732:2814 +733:2806 +734:2807 +735:2808 +736:2809 +743:2907 +743:2910 +743:2912 +743:2913 +743:2914 +743:2921 +743:2988 +743:2989 +743:2990 +743:2991 +743:2992 +743:2993 +743:2994 +744:2908 +745:2909 +746:2915 +746:2922 +746:2923 +746:2924 +746:2925 +746:2926 +746:2927 +746:2928 +746:2929 +746:2930 +746:2931 +746:2932 +746:2933 +746:2934 +746:2935 +746:2936 +746:2937 +746:2938 +747:2916 +747:2940 +747:2941 +747:2942 +747:2943 +747:2945 +747:2946 +747:2947 +747:2948 +748:2917 +748:2951 +748:2952 +749:2953 +749:2954 +749:2955 +749:2956 +749:2958 +749:2959 +752:2918 +752:2963 +752:2964 +752:2965 +752:2966 +752:2967 +752:2968 +752:2969 +752:2970 +752:2971 +752:2972 +752:2973 +752:2974 +752:2975 +753:2919 +753:2976 +753:2977 +754:2978 +754:2979 +754:2980 +754:2981 +754:2983 +754:2984 +761:3020 +761:3024 +761:3026 +761:3027 +761:3028 +761:3064 +761:3065 +762:3021 +762:3029 +762:3031 +762:3032 +763:3022 +763:3033 +763:3034 +763:3035 +763:3036 +763:3038 +763:3039 +764:3023 +764:3042 +764:3043 +764:3044 +764:3045 +764:3046 +764:3057 +764:3058 +764:3059 +764:3060 +764:3061 +765:3063 +780:3104 +780:3117 +780:3119 +780:3120 +780:3121 +780:3266 +780:3267 +781:3105 +781:3124 +781:3125 +781:3126 +781:3127 +781:3128 +781:3129 +781:3131 +781:3132 +781:3159 +781:3236 +781:3237 +781:3238 +781:3239 +781:3240 +781:3241 +781:3242 +781:3243 +782:3106 +782:3136 +782:3137 +782:3138 +782:3139 +782:3140 +782:3141 +782:3142 +782:3143 +782:3144 +782:3145 +782:3146 +782:3147 +782:3148 +782:3149 +782:3150 +783:3107 +783:3153 +783:3154 +783:3155 +783:3156 +784:3108 +784:3160 +784:3162 +784:3163 +784:3164 +784:3167 +784:3170 +784:3171 +784:3172 +784:3173 +785:3109 +785:3165 +785:3166 +786:3110 +786:3168 +786:3169 +787:3111 +787:3177 +787:3178 +787:3179 +787:3180 +787:3212 +787:3213 +787:3214 +787:3215 +787:3216 +787:3218 +787:3219 +787:3220 +787:3221 +787:3222 +787:3223 +787:3224 +787:3225 +787:3226 +787:3227 +787:3228 +787:3229 +787:3230 +787:3231 +787:3232 +787:3234 +787:3235 +788:3112 +789:3113 +789:3245 +789:3246 +789:3247 +789:3248 +789:3249 +789:3250 +789:3253 +789:3254 +789:3255 +789:3256 +789:3257 +789:3260 +789:3261 +789:3262 +789:3263 +789:3264 +790:3114 +791:3115 +792:3116 +800:3775 +800:3783 +800:3785 +800:3786 +800:3787 +800:3834 +800:3835 +801:3776 +801:3790 +801:3791 +801:3792 +801:3793 +801:3794 +801:3795 +801:3796 +801:3797 +801:3823 +801:3824 +801:3825 +801:3826 +801:3827 +801:3828 +801:3829 +801:3830 +802:3777 +802:3800 +802:3801 +802:3802 +802:3803 +802:3804 +802:3805 +802:3806 +802:3807 +802:3808 +802:3809 +802:3810 +802:3811 +802:3812 +802:3813 +802:3814 +803:3778 +803:3817 +803:3818 +803:3819 +803:3820 +804:3779 +804:3831 +804:3832 +804:3833 +805:3780 +806:3781 +807:3655 +807:3657 +807:3658 +807:3659 +807:3685 +807:3686 +807:3782 +809:3661 +809:3662 +809:3663 +809:3664 +809:3665 +809:3666 +809:3667 +809:3668 +809:3669 +809:3670 +809:3671 +809:3672 +809:3673 +809:3674 +809:3675 +809:3676 +809:3677 +809:3678 +809:3679 +809:3680 +809:3682 +809:3683 +809:3684 +814:3837 +814:3839 +814:3841 +814:3842 +814:3843 +814:3855 +814:3856 +815:3838 +816:3844 +816:3845 +816:3847 +816:3848 +816:3849 +816:3850 +816:3851 +816:3852 +816:3853 +816:3854 +822:3890 +822:3891 +822:3893 +822:3894 +822:3895 +822:3934 +822:3935 +823:3897 +823:3899 +823:3900 +823:3901 +823:3902 +823:3905 +823:3906 +823:3907 +823:3910 +823:3911 +823:3912 +823:3915 +823:3916 +823:3917 +823:3920 +823:3921 +823:3922 +823:3923 +823:3924 +823:3925 +823:3928 +823:3929 +823:3930 +823:3931 +823:3932 +829:3540 +829:3543 +829:3545 +829:3546 +829:3547 +829:3549 +829:3550 +829:3556 +829:3557 +830:3541 +831:3542 +831:3551 +831:3552 +831:3553 +831:3554 +833:3555 +837:2597 +837:2598 +837:2599 +837:2601 +837:2602 +837:2603 +837:2605 +837:2628 +837:2629 +837:2630 +837:2631 +837:2632 +837:2633 +837:2634 +838:2606 +838:2607 +838:2608 +838:2609 +838:2610 +839:2613 +839:2614 +839:2615 +839:2616 +839:2617 +839:2618 +839:2619 +839:2620 +839:2621 +839:2622 +839:2623 +839:2624 +839:2625 +848:3937 +848:3938 +848:3939 +848:3941 +848:3942 +848:3943 +848:3945 +848:3964 +848:3965 +849:3944 +849:3947 +849:3948 +850:3949 +850:3950 +851:3951 +851:3952 +851:3958 +851:3960 +851:3961 +852:3954 +852:3963 +853:3955 +854:3956 +859:3997 +859:3998 +859:3999 +859:4001 +859:4002 +859:4003 +859:4011 +859:4079 +859:4080 +860:4004 +860:4005 +860:4013 +860:4014 +860:4015 +860:4016 +860:4018 +860:4019 +860:4020 +860:4021 +860:4022 +860:4023 +860:4024 +860:4025 +860:4027 +860:4028 +860:4029 +860:4030 +862:4033 +862:4034 +862:4035 +862:4058 +862:4059 +862:4060 +862:4061 +862:4063 +862:4064 +863:4006 +863:4007 +863:4036 +863:4037 +863:4038 +863:4039 +864:4008 +864:4009 +864:4010 +864:4040 +864:4041 +864:4042 +864:4044 +864:4045 +864:4046 +864:4047 +864:4049 +864:4050 +864:4051 +864:4052 +865:4055 +865:4056 +867:4065 +867:4066 +867:4073 +867:4075 +867:4076 +868:4068 +868:4078 +869:4069 +870:4070 +871:4071 +876:4148 +876:4150 +876:4151 +876:4152 +876:4154 +876:4177 +876:4178 +876:4179 +876:4180 +876:4181 +876:4182 +876:4183 +877:4155 +877:4156 +877:4157 +877:4158 +877:4159 +878:4162 +878:4163 +878:4164 +878:4165 +878:4166 +878:4167 +878:4168 +878:4169 +878:4170 +878:4171 +878:4172 +878:4173 +878:4174 +882:4247 +882:4249 +882:4250 +882:4251 +882:4252 +882:4292 +882:4293 +883:4255 +883:4256 +883:4257 +883:4258 +883:4259 +883:4260 +883:4261 +883:4262 +883:4263 +883:4264 +883:4265 +883:4266 +883:4267 +883:4268 +883:4277 +883:4278 +883:4279 +883:4280 +883:4281 +884:4271 +884:4272 +884:4273 +884:4274 +886:4283 +886:4284 +886:4285 +886:4286 +886:4288 +886:4289 +887:4291 +890:4295 +890:4297 +890:4298 +890:4299 +890:4353 +890:4354 +891:4301 +891:4302 +891:4303 +891:4304 +892:4306 +892:4307 +892:4308 +892:4309 +892:4310 +892:4311 +892:4313 +892:4314 +892:4315 +892:4316 +892:4319 +892:4320 +892:4321 +892:4324 +892:4325 +892:4326 +892:4327 +892:4328 +892:4330 +892:4331 +892:4332 +892:4333 +892:4334 +892:4335 +892:4336 +892:4337 +892:4338 +892:4339 +892:4340 +892:4342 +892:4343 +892:4344 +892:4345 +893:4348 +893:4349 +893:4350 +896:4126 +896:4128 +896:4129 +896:4130 +896:4131 +896:4145 +896:4146 +896:4352 +898:4133 +898:4134 +898:4135 +899:4136 +899:4137 +899:4138 +899:4139 +899:4141 +899:4142 +900:4144 +903:3688 +903:3690 +903:3691 +903:3692 +903:3693 +903:3746 +903:3747 +904:3694 +907:3696 +907:3697 +908:3699 +908:3700 +908:3701 +908:3703 +909:3704 +909:3705 +910:3706 +910:3707 +910:3708 +910:3714 +910:3715 +910:3716 +910:3717 +910:3719 +910:3720 +912:3709 +912:3710 +912:3711 +912:3712 +912:3713 +914:3721 +915:3726 +915:3728 +915:3729 +915:3730 +915:3731 +918:3723 +918:3724 +918:3725 +923:3734 +923:3735 +923:3736 +926:3737 +926:3738 +926:3739 +926:3740 +926:3742 +926:3743 +927:3745 +931:4409 +931:4411 +931:4412 +931:4413 +931:4415 +931:4443 +931:4444 +931:4445 +931:4446 +931:4447 +931:4448 +931:4449 +932:4416 +932:4417 +932:4418 +932:4419 +932:4420 +933:4425 +933:4426 +933:4427 +933:4428 +933:4429 +934:4434 +934:4435 +934:4436 +934:4437 +934:4438 +935:4422 +936:4431 +937:4440 +938:4356 +938:4358 +938:4359 +938:4360 +938:4361 +938:4406 +938:4407 +941:4364 +941:4365 +941:4366 +941:4367 +941:4368 +941:4369 +941:4373 +941:4374 +941:4375 +941:4378 +941:4379 +941:4380 +941:4381 +941:4382 +941:4384 +941:4385 +941:4386 +941:4387 +942:4388 +942:4389 +942:4400 +942:4402 +942:4403 +943:4391 +943:4405 +944:4392 +945:4393 +946:4394 +947:4395 +948:4396 +949:4397 +950:4398 +955:4185 +955:4186 +955:4187 +955:4189 +955:4190 +955:4191 +955:4193 +955:4244 +955:4245 +956:4192 +956:4195 +956:4196 +957:4198 +957:4199 +957:4200 +957:4201 +957:4202 +957:4203 +957:4205 +957:4207 +957:4208 +957:4209 +957:4210 +957:4211 +957:4212 +957:4213 +957:4214 +957:4217 +957:4225 +957:4226 +957:4227 +957:4228 +958:4215 +958:4216 +959:4218 +959:4219 +959:4220 +960:4221 +961:4222 +962:4223 +964:4231 +964:4232 +964:4238 +964:4240 +964:4241 +965:4234 +965:4243 +966:4235 +967:4236 +973:3967 +973:3968 +973:3969 +973:3971 +973:3972 +973:3973 +973:3975 +973:3994 +973:3995 +974:3974 +974:3977 +974:3978 +975:3979 +975:3980 +976:3981 +976:3982 +976:3988 +976:3990 +976:3991 +977:3984 +977:3993 +978:3985 +979:3986 +985:4451 +985:4453 +985:4454 +985:4455 +985:4457 +985:4480 +985:4481 +985:4482 +985:4483 +985:4484 +985:4485 +985:4486 +986:4458 +986:4459 +986:4460 +986:4461 +986:4462 +987:4465 +987:4466 +987:4467 +987:4468 +987:4469 +987:4470 +987:4471 +987:4472 +987:4473 +987:4474 +987:4475 +987:4476 +987:4477 +991:3559 +991:3561 +991:3562 +991:3563 +991:3565 +991:3647 +991:3648 +991:3649 +991:3650 +991:3651 +991:3652 +991:3653 +992:3566 +992:3567 +992:3568 +992:3569 +992:3570 +992:3571 +992:3572 +993:3575 +993:3576 +993:3577 +993:3578 +993:3579 +993:3580 +993:3581 +994:3584 +994:3585 +994:3586 +994:3587 +994:3588 +994:3589 +994:3590 +995:3593 +995:3594 +995:3595 +995:3596 +995:3597 +995:3598 +995:3599 +996:3602 +996:3603 +996:3604 +996:3605 +996:3606 +996:3607 +996:3608 +997:3611 +997:3612 +997:3613 +997:3614 +997:3615 +997:3616 +997:3617 +998:3620 +998:3621 +998:3622 +998:3623 +998:3624 +998:3625 +998:3626 +999:3629 +999:3630 +999:3631 +999:3632 +999:3633 +999:3634 +999:3635 +1000:3638 +1000:3639 +1000:3640 +1000:3641 +1000:3642 +1000:3643 +1000:3644 +1005:1829 +1005:1831 +1005:1832 +1005:1833 +1005:1840 +1005:1875 +1005:1876 +1006:1834 +1006:1835 +1006:1842 +1006:1843 +1006:1844 +1007:1845 +1007:1846 +1007:1847 +1007:1859 +1007:1860 +1007:1861 +1007:1862 +1007:1864 +1007:1865 +1008:1836 +1008:1837 +1008:1838 +1008:1839 +1008:1848 +1008:1849 +1008:1850 +1008:1851 +1008:1852 +1008:1853 +1008:1854 +1008:1855 +1009:1856 +1009:1857 +1011:1866 +1011:1867 +1011:1868 +1011:1869 +1011:1871 +1011:1872 +1012:1874 +1014:1963 +1014:1965 +1014:1966 +1014:1967 +1014:1980 +1014:1982 +1014:2060 +1014:2074 +1014:2075 +1015:1968 +1015:1969 +1015:1983 +1015:1984 +1015:1985 +1016:1986 +1016:1987 +1016:1988 +1016:2000 +1016:2001 +1016:2002 +1016:2003 +1016:2005 +1016:2006 +1017:1970 +1017:1971 +1017:1972 +1017:1973 +1017:1989 +1017:1990 +1017:1991 +1017:1992 +1017:1993 +1017:1994 +1017:1995 +1017:1996 +1018:1997 +1018:1998 +1020:1974 +1020:1975 +1020:1976 +1020:1977 +1020:2008 +1020:2009 +1020:2010 +1020:2011 +1020:2012 +1020:2013 +1020:2014 +1020:2015 +1020:2016 +1020:2017 +1020:2045 +1020:2046 +1020:2047 +1020:2048 +1020:2049 +1021:2018 +1021:2019 +1022:1978 +1022:1979 +1022:2023 +1022:2024 +1022:2025 +1022:2026 +1022:2027 +1022:2028 +1022:2029 +1023:2030 +1023:2031 +1025:2051 +1025:2052 +1025:2053 +1025:2054 +1025:2056 +1025:2057 +1026:2059 +1034:2061 +1034:2062 +1034:2070 +1034:2071 +1034:2072 +1034:2073 +1035:2064 +1036:2065 +1037:2066 +1038:2067 +1039:2068 +1044:4488 +1044:4490 +1044:4491 +1044:4492 +1044:4493 +1044:4534 +1044:4535 +1047:4496 +1047:4497 +1047:4498 +1047:4502 +1047:4507 +1047:4520 +1047:4525 +1047:4526 +1047:4527 +1047:4528 +1047:4530 +1047:4531 +1047:4532 +1047:4533 +1056:4499 +1056:4500 +1056:4501 +1059:4503 +1059:4504 +1059:4505 +1059:4506 +1061:4508 +1061:4509 +1061:4510 +1062:4512 +1062:4513 +1062:4514 +1062:4515 +1062:4516 +1062:4517 +1062:4518 +1062:4519 +1064:4521 +1064:4522 +1064:4523 +1064:4524 +1069:2575 +1069:2576 +1069:2578 +1069:2579 +1069:2580 +1069:2581 +1069:2594 +1069:2595 +1070:2583 +1070:2584 +1071:2585 +1071:2586 +1071:2587 +1071:2588 +1071:2590 +1071:2591 +1072:2593 +1075:3858 +1075:3859 +1075:3861 +1075:3862 +1075:3863 +1075:3864 +1075:3887 +1075:3888 +1076:3870 +1076:3872 +1076:3873 +1076:3874 +1076:3875 +1079:3867 +1079:3868 +1079:3869 +1081:3878 +1081:3879 +1081:3880 +1081:3881 +1081:3883 +1081:3884 +1082:3886 +1085:3269 +1085:3271 +1085:3272 +1085:3273 +1085:3275 +1085:3384 +1085:3385 +1085:3386 +1085:3387 +1085:3388 +1085:3389 +1085:3390 +1086:3276 +1086:3277 +1086:3278 +1086:3279 +1086:3280 +1086:3281 +1086:3282 +1087:3285 +1087:3286 +1087:3287 +1087:3288 +1087:3289 +1087:3290 +1087:3291 +1088:3294 +1088:3295 +1088:3296 +1088:3297 +1088:3298 +1088:3299 +1088:3300 +1089:3303 +1089:3304 +1089:3305 +1089:3306 +1089:3307 +1089:3308 +1089:3309 +1090:3312 +1090:3313 +1090:3314 +1090:3315 +1090:3316 +1090:3317 +1090:3318 +1091:3321 +1091:3322 +1091:3323 +1091:3324 +1091:3325 +1091:3326 +1091:3327 +1092:3330 +1092:3331 +1092:3332 +1092:3333 +1092:3334 +1092:3335 +1092:3336 +1093:3339 +1093:3340 +1093:3341 +1093:3342 +1093:3343 +1093:3344 +1093:3345 +1094:3348 +1094:3349 +1094:3350 +1094:3351 +1094:3352 +1094:3353 +1094:3354 +1095:3357 +1095:3358 +1095:3359 +1095:3360 +1095:3361 +1095:3362 +1095:3363 +1096:3366 +1096:3367 +1096:3368 +1096:3369 +1096:3370 +1096:3371 +1096:3372 +1097:3375 +1097:3376 +1097:3377 +1097:3378 +1097:3379 +1097:3380 +1097:3381 +1100:3392 +1100:3394 +1100:3395 +1100:3396 +1100:3399 +1100:3520 +1100:3532 +1100:3533 +1100:3534 +1100:3535 +1100:3537 +1100:3538 +1101:3397 +1101:3398 +1101:3401 +1101:3402 +1101:3403 +1101:3404 +1101:3405 +1101:3407 +1101:3408 +1101:3410 +1101:3411 +1101:3412 +1101:3413 +1101:3414 +1101:3415 +1101:3416 +1101:3417 +1101:3418 +1101:3419 +1101:3420 +1101:3421 +1101:3422 +1101:3423 +1101:3424 +1101:3425 +1101:3426 +1101:3427 +1101:3428 +1101:3429 +1101:3430 +1101:3431 +1101:3432 +1101:3433 +1101:3434 +1101:3435 +1101:3436 +1101:3437 +1101:3438 +1101:3439 +1101:3440 +1101:3441 +1101:3442 +1101:3443 +1101:3444 +1101:3445 +1101:3446 +1101:3447 +1101:3448 +1101:3449 +1101:3450 +1101:3451 +1101:3452 +1101:3453 +1101:3454 +1101:3455 +1101:3456 +1101:3457 +1101:3458 +1101:3459 +1101:3460 +1101:3461 +1101:3462 +1101:3463 +1101:3464 +1101:3465 +1101:3466 +1101:3467 +1101:3468 +1101:3469 +1101:3470 +1101:3471 +1101:3472 +1101:3473 +1101:3474 +1101:3475 +1101:3476 +1101:3477 +1101:3478 +1101:3479 +1101:3480 +1101:3481 +1101:3482 +1101:3483 +1101:3484 +1101:3485 +1101:3486 +1101:3487 +1101:3488 +1101:3489 +1101:3490 +1101:3497 +1101:3498 +1101:3499 +1101:3500 +1101:3501 +1101:3503 +1101:3504 +1101:3506 +1101:3507 +1101:3508 +1101:3509 +1102:3512 +1102:3513 +1102:3514 +1102:3515 +1102:3517 +1102:3518 +1104:3521 +1104:3522 +1104:3523 +1105:3524 +1105:3525 +1105:3526 +1105:3527 +1105:3529 +1105:3530 +1109:4573 +1109:4575 +1109:4576 +1109:4577 +1109:4607 +1109:4608 +1110:4579 +1110:4580 +1110:4581 +1110:4594 +1110:4599 +1110:4600 +1110:4601 +1110:4602 +1110:4604 +1110:4605 +1110:4606 +1112:4582 +1112:4583 +1112:4584 +1113:4586 +1113:4587 +1113:4588 +1113:4589 +1113:4590 +1113:4591 +1113:4592 +1113:4593 +1115:4595 +1115:4596 +1115:4597 +1115:4598 +1118:940 +1118:942 +1118:943 +1118:944 +1118:945 +1118:968 +1118:969 +1119:951 +1119:953 +1119:954 +1119:955 +1119:956 +1122:948 +1122:949 +1122:950 +1124:959 +1124:960 +1124:961 +1124:962 +1124:964 +1124:965 +1125:967 +1127:4537 +1127:4539 +1127:4540 +1127:4541 +1127:4560 +1127:4565 +1127:4566 +1127:4567 +1127:4568 +1127:4570 +1127:4571 +1128:4542 +1128:4544 +1128:4545 +1128:4546 +1128:4547 +1129:4548 +1129:4549 +1129:4550 +1129:4554 +1129:4556 +1129:4557 +1130:4551 +1130:4559 +1131:4552 +1132:4553 +1133:4561 +1133:4562 +1133:4563 +1133:4564 +1136:4671 +1136:4673 +1136:4674 +1136:4675 +1136:4695 +1136:4696 +1137:4677 +1137:4678 +1137:4679 +1137:4680 +1137:4681 +1137:4682 +1137:4683 +1137:4684 +1137:4685 +1137:4686 +1137:4687 +1137:4688 +1137:4689 +1137:4690 +1137:4692 +1137:4693 +1137:4694 +1140:4698 +1140:4700 +1140:4701 +1140:4702 +1140:4705 +1140:4707 +1140:4772 +1140:4791 +1140:4792 +1143:4703 +1143:4704 +1143:4708 +1143:4709 +1143:4710 +1143:4711 +1143:4713 +1143:4714 +1143:4715 +1143:4716 +1143:4717 +1143:4718 +1143:4719 +1143:4720 +1143:4721 +1143:4722 +1143:4723 +1143:4724 +1143:4725 +1143:4726 +1143:4727 +1143:4728 +1143:4729 +1143:4730 +1143:4731 +1143:4732 +1143:4733 +1143:4734 +1143:4735 +1143:4736 +1143:4737 +1143:4738 +1143:4739 +1143:4740 +1143:4741 +1143:4742 +1143:4743 +1143:4744 +1143:4745 +1143:4746 +1143:4747 +1143:4748 +1143:4749 +1143:4750 +1143:4758 +1143:4759 +1143:4760 +1143:4761 +1143:4762 +1144:4764 +1144:4765 +1144:4766 +1144:4767 +1144:4769 +1144:4770 +1147:4773 +1147:4774 +1147:4787 +1147:4788 +1147:4789 +1147:4790 +1148:4776 +1149:4777 +1150:4778 +1151:4779 +1152:4780 +1153:4781 +1154:4782 +1155:4783 +1156:4784 +1157:4785 +1162:4794 +1162:4796 +1162:4797 +1162:4798 +1162:4800 +1162:4874 +1162:4875 +1162:4876 +1162:4877 +1162:4878 +1162:4879 +1162:4880 +1163:4801 +1163:4802 +1163:4803 +1163:4804 +1163:4805 +1163:4806 +1164:4810 +1164:4811 +1164:4812 +1164:4813 +1164:4814 +1164:4815 +1164:4816 +1164:4817 +1164:4818 +1164:4819 +1164:4820 +1164:4821 +1164:4822 +1164:4823 +1164:4824 +1164:4825 +1164:4826 +1164:4827 +1164:4828 +1164:4829 +1164:4830 +1164:4831 +1164:4832 +1164:4833 +1164:4834 +1164:4835 +1164:4836 +1164:4837 +1164:4838 +1164:4839 +1164:4840 +1164:4841 +1164:4842 +1164:4843 +1164:4844 +1164:4845 +1164:4846 +1164:4847 +1164:4848 +1164:4849 +1164:4850 +1164:4851 +1164:4852 +1164:4853 +1164:4854 +1164:4855 +1164:4856 +1164:4857 +1164:4858 +1164:4859 +1164:4860 +1164:4861 +1164:4862 +1164:4863 +1164:4864 +1164:4865 +1164:4866 +1164:4867 +1164:4868 +1164:4869 +1164:4870 +1164:4871 +1165:4807 +1167:4610 +1167:4612 +1167:4613 +1167:4614 +1167:4616 +1167:4663 +1167:4664 +1167:4665 +1167:4666 +1167:4667 +1167:4668 +1167:4669 +1168:4617 +1168:4618 +1168:4619 +1168:4620 +1168:4621 +1168:4622 +1168:4623 +1168:4624 +1168:4625 +1168:4626 +1168:4627 +1168:4628 +1168:4629 +1168:4630 +1168:4631 +1168:4632 +1168:4633 +1168:4634 +1168:4635 +1168:4636 +1168:4637 +1168:4638 +1168:4639 +1168:4640 +1168:4641 +1168:4642 +1168:4643 +1168:4644 +1168:4645 +1168:4646 +1168:4647 +1168:4648 +1168:4649 +1168:4650 +1168:4651 +1168:4652 +1168:4653 +1168:4656 +1168:4657 +1168:4658 +1168:4659 +1168:4660 +1200:5329 +1200:5331 +1200:5332 +1200:5333 +1200:5335 +1200:5374 +1200:5375 +1203:5334 +1203:5338 +1203:5339 +1203:5340 +1203:5341 +1203:5342 +1203:5343 +1203:5344 +1203:5345 +1203:5360 +1203:5361 +1203:5362 +1203:5363 +1203:5364 +1204:5366 +1204:5367 +1204:5368 +1204:5369 +1204:5371 +1204:5372 +1208:2179 +1208:2180 +1208:2181 +1208:2183 +1208:2184 +1208:2185 +1208:2190 +1208:2278 +1208:2279 +1209:2191 +1210:2192 +1211:2193 +1212:2194 +1214:2196 +1214:2197 +1214:2198 +1214:2199 +1214:2200 +1215:2201 +1215:2202 +1217:2186 +1217:2205 +1217:2206 +1217:2207 +1217:2208 +1217:2209 +1217:2210 +1217:2226 +1217:2227 +1217:2228 +1217:2229 +1217:2230 +1219:2187 +1219:2232 +1219:2233 +1221:2188 +1221:2234 +1221:2235 +1234:2189 +1234:2237 +1234:2238 +1234:2239 +1234:2240 +1234:2241 +1234:2254 +1234:2255 +1234:2256 +1234:2257 +1234:2258 +1235:2260 +1235:2261 +1235:2270 +1235:2272 +1235:2273 +1236:2263 +1237:2264 +1238:2265 +1239:2266 +1240:2267 +1241:2268 +1244:2275 +1244:2276 +1248:2281 +1248:2282 +1248:2283 +1248:2285 +1248:2286 +1248:2287 +1248:2292 +1248:2344 +1248:2345 +1249:2293 +1250:2294 +1251:2295 +1252:2296 +1254:2298 +1254:2299 +1254:2300 +1254:2301 +1254:2302 +1255:2303 +1255:2304 +1257:2288 +1257:2307 +1257:2308 +1257:2309 +1257:2310 +1257:2311 +1257:2312 +1257:2321 +1257:2322 +1257:2323 +1257:2324 +1257:2325 +1259:2289 +1259:2327 +1259:2328 +1261:2290 +1261:2329 +1261:2330 +1263:2291 +1263:2331 +1263:2332 +1264:2333 +1264:2334 +1264:2335 +1264:2336 +1264:2338 +1264:2339 +1266:2341 +1266:2342 +1270:2347 +1270:2348 +1270:2349 +1270:2351 +1270:2352 +1270:2353 +1270:2357 +1270:2400 +1270:2401 +1271:2358 +1272:2359 +1273:2360 +1274:2361 +1275:2363 +1275:2364 +1275:2365 +1275:2366 +1275:2367 +1277:2354 +1277:2369 +1277:2370 +1277:2371 +1277:2372 +1277:2373 +1277:2374 +1277:2382 +1277:2383 +1277:2384 +1277:2385 +1277:2386 +1279:2355 +1279:2388 +1279:2389 +1281:2356 +1281:2390 +1281:2391 +1282:2392 +1282:2393 +1282:2394 +1282:2395 +1282:2397 +1282:2398 +1286:2403 +1286:2404 +1286:2405 +1286:2407 +1286:2408 +1286:2409 +1286:2412 +1286:2442 +1286:2443 +1287:2413 +1288:2414 +1289:2415 +1290:2416 +1291:2418 +1291:2419 +1291:2420 +1291:2421 +1292:2422 +1292:2423 +1293:2425 +1295:2410 +1295:2426 +1295:2427 +1296:2428 +1298:2411 +1298:2429 +1298:2430 +1299:2431 +1299:2432 +1299:2433 +1299:2434 +1299:2436 +1299:2437 +1301:2439 +1301:2440 +1304:2445 +1304:2446 +1304:2447 +1304:2449 +1304:2450 +1304:2451 +1304:2453 +1304:2477 +1304:2478 +1305:2454 +1306:2455 +1307:2456 +1308:2457 +1309:2459 +1309:2460 +1309:2461 +1309:2462 +1309:2463 +1309:2464 +1309:2465 +1309:2466 +1311:2452 +1311:2467 +1311:2468 +1312:2469 +1312:2470 +1312:2471 +1312:2472 +1312:2474 +1312:2475 +1316:2819 +1316:2821 +1316:2822 +1316:2823 +1316:2824 +1316:2904 +1316:2905 +1319:2826 +1319:2827 +1320:2829 +1320:2830 +1320:2831 +1320:2833 +1321:2834 +1321:2835 +1321:2836 +1321:2837 +1321:2838 +1321:2839 +1321:2840 +1321:2841 +1321:2842 +1321:2843 +1321:2844 +1321:2845 +1321:2846 +1321:2847 +1321:2849 +1321:2850 +1322:2851 +1323:2853 +1323:2854 +1323:2855 +1323:2856 +1323:2857 +1323:2858 +1323:2859 +1323:2886 +1323:2887 +1323:2888 +1323:2889 +1323:2890 +1327:2892 +1327:2893 +1327:2894 +1330:2895 +1330:2896 +1330:2897 +1330:2898 +1330:2900 +1330:2901 +1331:2903 +1333:6062 +1333:6064 +1333:6065 +1333:6066 +1333:6069 +1333:6098 +1333:6099 +1336:6067 +1336:6068 +1336:6072 +1336:6073 +1336:6074 +1336:6075 +1336:6078 +1336:6079 +1336:6080 +1336:6081 +1336:6083 +1336:6084 +1336:6085 +1336:6086 +1337:6089 +1337:6090 +1337:6091 +1337:6092 +1337:6094 +1337:6095 +1338:6097 +1340:6101 +1340:6103 +1340:6104 +1340:6105 +1340:6106 +1340:6136 +1340:6137 +1342:6108 +1342:6109 +1342:6110 +1342:6111 +1343:6112 +1343:6113 +1343:6114 +1343:6115 +1343:6116 +1343:6117 +1343:6118 +1343:6119 +1343:6120 +1343:6121 +1343:6122 +1343:6123 +1343:6125 +1343:6126 +1344:6127 +1344:6128 +1344:6129 +1344:6130 +1344:6132 +1344:6133 +1345:6135 +1348:5443 +1348:5445 +1348:5446 +1348:5447 +1348:5448 +1348:5450 +1348:5570 +1348:5588 +1348:5589 +1349:5451 +1350:5453 +1350:5454 +1350:5455 +1350:5456 +1350:5457 +1350:5458 +1350:5459 +1350:5460 +1350:5461 +1350:5462 +1350:5463 +1350:5464 +1350:5465 +1350:5466 +1350:5467 +1350:5468 +1350:5469 +1350:5470 +1350:5471 +1350:5472 +1350:5473 +1350:5474 +1350:5475 +1350:5476 +1350:5477 +1350:5478 +1350:5479 +1350:5480 +1350:5481 +1350:5482 +1350:5483 +1350:5484 +1350:5485 +1350:5494 +1350:5495 +1350:5496 +1350:5497 +1350:5498 +1350:5500 +1350:5501 +1350:5502 +1350:5503 +1350:5504 +1350:5506 +1350:5507 +1350:5508 +1350:5509 +1350:5510 +1350:5511 +1350:5512 +1350:5513 +1350:5514 +1350:5515 +1350:5516 +1350:5517 +1350:5518 +1350:5519 +1350:5520 +1350:5521 +1350:5522 +1350:5523 +1350:5524 +1350:5525 +1350:5526 +1350:5527 +1350:5528 +1350:5529 +1350:5530 +1350:5531 +1350:5532 +1350:5533 +1350:5534 +1350:5535 +1350:5536 +1350:5537 +1350:5538 +1350:5547 +1350:5548 +1350:5549 +1350:5550 +1350:5551 +1350:5553 +1350:5554 +1350:5555 +1350:5556 +1350:5558 +1350:5559 +1351:5560 +1352:5561 +1352:5562 +1352:5563 +1352:5564 +1352:5566 +1352:5567 +1353:5569 +1356:5571 +1356:5572 +1356:5584 +1356:5585 +1356:5586 +1356:5587 +1357:5574 +1358:5575 +1359:5576 +1360:5577 +1361:5578 +1362:5579 +1363:5580 +1364:5581 +1365:5582 +1371:5650 +1371:5652 +1371:5653 +1371:5654 +1371:5655 +1371:5774 +1371:5775 +1372:5657 +1373:5659 +1373:5660 +1373:5661 +1373:5662 +1373:5663 +1373:5664 +1373:5665 +1373:5666 +1373:5667 +1373:5668 +1373:5669 +1373:5670 +1373:5671 +1373:5672 +1373:5673 +1373:5674 +1373:5675 +1373:5676 +1373:5677 +1373:5678 +1373:5679 +1373:5680 +1373:5681 +1373:5682 +1373:5683 +1373:5684 +1373:5685 +1373:5686 +1373:5687 +1373:5688 +1373:5689 +1373:5690 +1373:5699 +1373:5700 +1373:5701 +1373:5702 +1373:5703 +1373:5705 +1373:5706 +1373:5707 +1373:5708 +1373:5709 +1373:5711 +1373:5712 +1373:5713 +1373:5714 +1373:5715 +1373:5716 +1373:5717 +1373:5718 +1373:5719 +1373:5720 +1373:5721 +1373:5722 +1373:5723 +1373:5724 +1373:5725 +1373:5726 +1373:5727 +1373:5728 +1373:5729 +1373:5730 +1373:5731 +1373:5732 +1373:5733 +1373:5734 +1373:5735 +1373:5736 +1373:5737 +1373:5738 +1373:5739 +1373:5740 +1373:5741 +1373:5742 +1373:5751 +1373:5752 +1373:5753 +1373:5754 +1373:5755 +1373:5757 +1373:5758 +1373:5759 +1373:5760 +1373:5762 +1373:5763 +1374:5764 +1375:5765 +1375:5766 +1375:5767 +1375:5768 +1375:5770 +1375:5771 +1376:5773 +1380:5935 +1380:5937 +1380:5938 +1380:5939 +1380:5940 +1380:6059 +1380:6060 +1381:5942 +1382:5944 +1382:5945 +1382:5946 +1382:5947 +1382:5948 +1382:5949 +1382:5950 +1382:5951 +1382:5952 +1382:5953 +1382:5954 +1382:5955 +1382:5956 +1382:5957 +1382:5958 +1382:5959 +1382:5960 +1382:5961 +1382:5962 +1382:5963 +1382:5964 +1382:5965 +1382:5966 +1382:5967 +1382:5968 +1382:5969 +1382:5970 +1382:5971 +1382:5972 +1382:5973 +1382:5974 +1382:5975 +1382:5984 +1382:5985 +1382:5986 +1382:5987 +1382:5988 +1382:5990 +1382:5991 +1382:5992 +1382:5993 +1382:5994 +1382:5996 +1382:5997 +1382:5998 +1382:5999 +1382:6000 +1382:6001 +1382:6002 +1382:6003 +1382:6004 +1382:6005 +1382:6006 +1382:6007 +1382:6008 +1382:6009 +1382:6010 +1382:6011 +1382:6012 +1382:6013 +1382:6014 +1382:6015 +1382:6016 +1382:6017 +1382:6018 +1382:6019 +1382:6020 +1382:6021 +1382:6022 +1382:6023 +1382:6024 +1382:6025 +1382:6026 +1382:6027 +1382:6036 +1382:6037 +1382:6038 +1382:6039 +1382:6040 +1382:6042 +1382:6043 +1382:6044 +1382:6045 +1382:6047 +1382:6048 +1383:6049 +1384:6050 +1384:6051 +1384:6052 +1384:6053 +1384:6055 +1384:6056 +1385:6058 +1389:5777 +1389:5779 +1389:5780 +1389:5781 +1389:5782 +1389:5932 +1389:5933 +1390:5784 +1390:5785 +1391:5806 +1391:5856 +1391:5857 +1391:5858 +1391:5859 +1396:5787 +1396:5788 +1396:5789 +1396:5790 +1396:5791 +1396:5792 +1396:5794 +1396:5796 +1396:5797 +1396:5798 +1396:5799 +1396:5800 +1396:5801 +1396:5802 +1396:5803 +1396:5804 +1396:5805 +1397:5807 +1397:5809 +1397:5810 +1397:5811 +1397:5812 +1397:5813 +1397:5814 +1397:5815 +1397:5816 +1397:5817 +1397:5818 +1397:5819 +1397:5820 +1397:5821 +1397:5822 +1397:5823 +1397:5824 +1397:5825 +1397:5826 +1397:5827 +1397:5828 +1397:5829 +1397:5830 +1397:5831 +1397:5832 +1397:5833 +1397:5834 +1397:5835 +1397:5836 +1397:5837 +1397:5838 +1397:5839 +1397:5840 +1397:5841 +1397:5850 +1397:5851 +1397:5852 +1397:5853 +1397:5854 +1399:5862 +1399:5863 +1399:5864 +1399:5865 +1399:5866 +1399:5868 +1399:5869 +1399:5870 +1399:5871 +1399:5872 +1399:5873 +1399:5874 +1399:5875 +1399:5876 +1399:5877 +1399:5878 +1399:5879 +1399:5880 +1399:5881 +1399:5882 +1399:5883 +1399:5884 +1399:5885 +1399:5886 +1399:5887 +1399:5888 +1399:5889 +1399:5890 +1399:5891 +1399:5892 +1399:5893 +1399:5894 +1399:5895 +1399:5896 +1399:5897 +1399:5898 +1399:5899 +1399:5900 +1399:5909 +1399:5910 +1399:5911 +1399:5912 +1399:5913 +1399:5915 +1399:5916 +1399:5917 +1399:5918 +1399:5920 +1399:5921 +1400:5922 +1401:5923 +1401:5924 +1401:5925 +1401:5926 +1401:5928 +1401:5929 +1402:5931 +1405:6597 +1405:6598 +1405:6600 +1405:6601 +1405:6602 +1405:6729 +1405:6730 +1406:6604 +1406:6605 +1406:6606 +1406:6607 +1406:6608 +1406:6609 +1406:6611 +1406:6612 +1406:6613 +1406:6614 +1406:6615 +1406:6616 +1406:6617 +1406:6620 +1406:6621 +1406:6622 +1406:6623 +1406:6624 +1406:6625 +1406:6628 +1406:6629 +1406:6630 +1406:6631 +1406:6632 +1406:6633 +1406:6634 +1406:6635 +1406:6636 +1406:6637 +1406:6638 +1406:6639 +1406:6640 +1406:6641 +1406:6642 +1406:6643 +1406:6644 +1406:6645 +1406:6646 +1406:6647 +1406:6648 +1406:6649 +1406:6650 +1406:6651 +1406:6652 +1406:6653 +1406:6654 +1406:6655 +1406:6656 +1406:6657 +1406:6658 +1406:6660 +1406:6661 +1406:6662 +1406:6663 +1406:6664 +1406:6665 +1406:6666 +1406:6669 +1406:6670 +1406:6671 +1406:6672 +1406:6673 +1406:6674 +1406:6677 +1406:6678 +1406:6679 +1406:6680 +1406:6681 +1406:6682 +1406:6683 +1406:6684 +1406:6685 +1406:6686 +1406:6687 +1406:6688 +1406:6689 +1406:6690 +1406:6691 +1406:6692 +1406:6693 +1406:6694 +1406:6695 +1406:6696 +1406:6697 +1406:6698 +1406:6699 +1406:6700 +1406:6701 +1406:6702 +1406:6703 +1406:6706 +1406:6707 +1406:6708 +1406:6709 +1406:6710 +1406:6711 +1406:6714 +1406:6715 +1406:6716 +1406:6717 +1406:6718 +1406:6722 +1406:6723 +1406:6724 +1406:6725 +1406:6726 +1407:6728 +1410:6732 +1410:6733 +1410:6735 +1410:6736 +1410:6737 +1410:6929 +1410:6930 +1412:6739 +1412:6740 +1413:6741 +1413:6742 +1413:6743 +1413:6815 +1413:6921 +1413:6922 +1413:6923 +1413:6924 +1413:6926 +1413:6927 +1413:6928 +1414:6744 +1414:6745 +1414:6746 +1414:6747 +1414:6748 +1414:6749 +1414:6751 +1414:6753 +1414:6754 +1414:6755 +1414:6756 +1414:6759 +1414:6760 +1414:6761 +1414:6764 +1414:6765 +1414:6766 +1414:6767 +1414:6768 +1414:6769 +1414:6770 +1414:6771 +1414:6772 +1414:6773 +1414:6774 +1414:6775 +1414:6776 +1414:6777 +1414:6778 +1414:6779 +1414:6780 +1414:6781 +1414:6782 +1414:6783 +1414:6784 +1414:6785 +1414:6786 +1414:6787 +1414:6788 +1414:6789 +1414:6790 +1414:6791 +1414:6792 +1414:6793 +1414:6794 +1414:6797 +1414:6798 +1414:6799 +1414:6800 +1414:6801 +1414:6804 +1414:6805 +1414:6806 +1414:6807 +1414:6808 +1414:6809 +1414:6810 +1414:6811 +1414:6812 +1414:6813 +1415:6816 +1415:6817 +1415:6818 +1416:6842 +1416:6915 +1416:6916 +1416:6917 +1416:6918 +1416:6919 +1417:6820 +1417:6821 +1417:6822 +1417:6823 +1417:6824 +1417:6825 +1417:6827 +1417:6828 +1417:6829 +1417:6831 +1417:6832 +1417:6833 +1417:6834 +1417:6835 +1417:6836 +1417:6837 +1417:6838 +1417:6839 +1417:6840 +1417:6841 +1419:6843 +1419:6844 +1419:6845 +1419:6846 +1419:6847 +1419:6848 +1419:6849 +1419:6851 +1419:6853 +1419:6854 +1419:6855 +1419:6856 +1419:6859 +1419:6860 +1419:6861 +1419:6864 +1419:6865 +1419:6866 +1419:6867 +1419:6868 +1419:6869 +1419:6870 +1419:6871 +1419:6872 +1419:6873 +1419:6874 +1419:6875 +1419:6876 +1419:6877 +1419:6878 +1419:6879 +1419:6880 +1419:6881 +1419:6882 +1419:6883 +1419:6884 +1419:6885 +1419:6886 +1419:6887 +1419:6888 +1419:6889 +1419:6890 +1419:6891 +1419:6892 +1419:6893 +1419:6894 +1419:6897 +1419:6898 +1419:6899 +1419:6900 +1419:6901 +1419:6904 +1419:6905 +1419:6906 +1419:6907 +1419:6908 +1419:6909 +1419:6910 +1419:6911 +1419:6912 +1419:6913 +1425:6464 +1425:6466 +1425:6467 +1425:6468 +1425:6476 +1425:6594 +1425:6595 +1426:6469 +1426:6478 +1426:6479 +1427:6470 +1427:6481 +1427:6482 +1427:6483 +1427:6484 +1427:6485 +1427:6486 +1427:6487 +1427:6488 +1427:6489 +1427:6588 +1427:6589 +1427:6590 +1427:6591 +1427:6592 +1428:6490 +1428:6491 +1428:6492 +1428:6493 +1428:6495 +1428:6496 +1429:6471 +1429:6500 +1429:6501 +1429:6502 +1429:6503 +1429:6504 +1429:6505 +1429:6506 +1429:6507 +1429:6508 +1429:6509 +1429:6510 +1429:6511 +1429:6512 +1430:6568 +1430:6580 +1430:6581 +1430:6582 +1430:6583 +1434:6514 +1434:6515 +1434:6516 +1434:6517 +1434:6518 +1434:6519 +1434:6521 +1434:6522 +1434:6524 +1434:6525 +1434:6526 +1434:6527 +1434:6528 +1434:6529 +1434:6530 +1434:6531 +1435:6472 +1435:6473 +1435:6532 +1435:6533 +1435:6534 +1436:6535 +1436:6536 +1440:6474 +1440:6538 +1440:6539 +1440:6540 +1440:6541 +1440:6542 +1440:6543 +1440:6544 +1440:6553 +1440:6554 +1440:6555 +1440:6556 +1440:6557 +1442:6559 +1442:6560 +1442:6561 +1442:6563 +1442:6565 +1442:6566 +1443:6562 +1448:6475 +1448:6569 +1448:6570 +1448:6571 +1449:6572 +1449:6573 +1449:6574 +1449:6575 +1449:6577 +1449:6578 +1456:6932 +1456:6934 +1456:6935 +1456:6936 +1456:6942 +1456:7002 +1456:7003 +1457:6937 +1457:6944 +1457:6945 +1458:6938 +1458:6939 +1458:6946 +1458:6947 +1458:6948 +1459:6950 +1459:6951 +1459:6952 +1459:6953 +1459:6967 +1459:6968 +1459:6969 +1459:6970 +1459:6971 +1460:6940 +1460:6954 +1460:6955 +1461:6956 +1463:6941 +1463:6974 +1463:6975 +1463:6976 +1463:6977 +1463:6978 +1463:6988 +1463:6989 +1463:6990 +1463:6991 +1463:6992 +1464:6994 +1464:6995 +1464:6996 +1464:6997 +1464:6999 +1464:7000 +1468:7401 +1468:7403 +1468:7404 +1468:7405 +1468:7406 +1468:7529 +1468:7530 +1469:7408 +1470:7410 +1470:7411 +1470:7412 +1470:7413 +1470:7414 +1470:7415 +1470:7416 +1470:7417 +1470:7418 +1470:7419 +1470:7420 +1470:7421 +1470:7422 +1470:7423 +1470:7424 +1470:7425 +1470:7426 +1470:7427 +1470:7428 +1470:7429 +1470:7430 +1470:7431 +1470:7432 +1470:7433 +1470:7434 +1470:7435 +1470:7436 +1470:7437 +1470:7438 +1470:7439 +1470:7440 +1470:7441 +1470:7442 +1470:7443 +1470:7452 +1470:7453 +1470:7454 +1470:7455 +1470:7456 +1470:7458 +1470:7459 +1470:7460 +1470:7461 +1470:7462 +1470:7464 +1470:7465 +1470:7466 +1470:7467 +1470:7468 +1470:7469 +1470:7470 +1470:7471 +1470:7472 +1470:7473 +1470:7474 +1470:7475 +1470:7476 +1470:7477 +1470:7478 +1470:7479 +1470:7480 +1470:7481 +1470:7482 +1470:7483 +1470:7484 +1470:7485 +1470:7486 +1470:7487 +1470:7488 +1470:7489 +1470:7490 +1470:7491 +1470:7492 +1470:7493 +1470:7494 +1470:7495 +1470:7496 +1470:7497 +1470:7506 +1470:7507 +1470:7508 +1470:7509 +1470:7510 +1470:7512 +1470:7513 +1470:7514 +1470:7515 +1470:7517 +1470:7518 +1471:7519 +1472:7520 +1472:7521 +1472:7522 +1472:7523 +1472:7525 +1472:7526 +1473:7528 +1480:7532 +1480:7534 +1480:7535 +1480:7536 +1480:7548 +1480:7550 +1480:7747 +1480:7748 +1480:7749 +1480:7750 +1480:7751 +1480:7752 +1480:7753 +1481:7551 +1481:7552 +1481:7553 +1481:7554 +1481:7555 +1481:7556 +1481:7557 +1481:7558 +1481:7559 +1481:7560 +1481:7561 +1481:7562 +1481:7563 +1481:7564 +1481:7565 +1481:7566 +1481:7567 +1481:7568 +1481:7569 +1481:7570 +1481:7571 +1481:7572 +1481:7573 +1481:7574 +1481:7575 +1481:7576 +1481:7577 +1481:7578 +1481:7579 +1481:7580 +1481:7611 +1481:7682 +1481:7725 +1481:7726 +1481:7727 +1481:7728 +1481:7729 +1482:7582 +1482:7583 +1482:7584 +1482:7585 +1482:7586 +1482:7587 +1482:7589 +1482:7591 +1482:7592 +1482:7593 +1482:7594 +1482:7595 +1482:7596 +1482:7597 +1482:7598 +1483:7537 +1483:7599 +1483:7600 +1484:7538 +1484:7601 +1484:7602 +1485:7603 +1485:7604 +1485:7605 +1485:7606 +1485:7608 +1485:7609 +1487:7612 +1487:7613 +1487:7614 +1487:7615 +1487:7616 +1487:7617 +1487:7618 +1487:7620 +1487:7622 +1487:7623 +1487:7624 +1487:7625 +1487:7626 +1487:7627 +1487:7628 +1487:7629 +1488:7539 +1488:7630 +1488:7631 +1489:7540 +1489:7541 +1489:7633 +1489:7634 +1489:7635 +1489:7636 +1489:7637 +1489:7654 +1489:7655 +1489:7656 +1489:7657 +1489:7658 +1489:7661 +1489:7662 +1489:7663 +1489:7664 +1489:7666 +1489:7667 +1489:7668 +1489:7669 +1490:7542 +1490:7672 +1490:7673 +1491:7674 +1491:7675 +1491:7676 +1491:7677 +1491:7679 +1491:7680 +1493:7543 +1493:7683 +1493:7684 +1493:7685 +1494:7544 +1494:7545 +1494:7687 +1494:7688 +1494:7689 +1494:7690 +1494:7691 +1494:7707 +1494:7708 +1494:7709 +1494:7710 +1494:7711 +1494:7713 +1494:7714 +1495:7546 +1495:7715 +1495:7716 +1496:7717 +1496:7718 +1496:7719 +1496:7720 +1496:7722 +1496:7723 +1498:7547 +1498:7733 +1498:7734 +1498:7735 +1498:7736 +1499:7737 +1499:7738 +1499:7739 +1499:7740 +1499:7742 +1499:7743 +1502:7755 +1502:7756 +1502:7757 +1502:7759 +1502:7760 +1502:7761 +1502:7856 +1502:7868 +1502:7869 +1502:7870 +1502:7871 +1502:7873 +1502:7874 +1505:7767 +1505:7768 +1505:7769 +1505:7770 +1505:7771 +1505:7772 +1505:7774 +1505:7775 +1505:7777 +1505:7778 +1505:7779 +1505:7780 +1505:7781 +1505:7782 +1505:7783 +1505:7784 +1506:7785 +1506:7786 +1506:7787 +1509:7762 +1509:7788 +1509:7789 +1509:7790 +1509:7791 +1512:7763 +1512:7793 +1512:7794 +1512:7795 +1512:7796 +1512:7797 +1512:7798 +1512:7800 +1512:7801 +1512:7803 +1512:7804 +1512:7805 +1512:7806 +1512:7807 +1512:7808 +1512:7809 +1512:7810 +1512:7811 +1512:7812 +1512:7813 +1512:7815 +1512:7816 +1512:7817 +1512:7818 +1514:7764 +1514:7822 +1514:7823 +1514:7824 +1514:7825 +1514:7826 +1514:7835 +1514:7836 +1514:7837 +1514:7838 +1514:7839 +1515:7841 +1515:7842 +1515:7851 +1515:7853 +1515:7854 +1516:7844 +1517:7845 +1518:7846 +1519:7847 +1520:7848 +1521:7849 +1530:7765 +1530:7857 +1530:7858 +1530:7859 +1531:7860 +1531:7861 +1531:7862 +1531:7863 +1531:7865 +1531:7866 +1535:5591 +1535:5593 +1535:5594 +1535:5595 +1535:5598 +1535:5647 +1535:5648 +1536:5601 +1536:5632 +1536:5633 +1536:5634 +1536:5635 +1536:5636 +1537:5596 +1537:5597 +1537:5602 +1537:5603 +1537:5604 +1537:5605 +1537:5606 +1537:5607 +1538:5608 +1538:5609 +1538:5610 +1538:5611 +1538:5612 +1538:5613 +1538:5614 +1538:5615 +1538:5616 +1538:5617 +1538:5618 +1538:5619 +1538:5620 +1538:5621 +1538:5623 +1538:5624 +1538:5625 +1540:5638 +1540:5639 +1540:5640 +1540:5641 +1540:5643 +1540:5644 +1541:5646 +1545:5377 +1545:5379 +1545:5380 +1545:5381 +1545:5384 +1545:5440 +1545:5441 +1546:5387 +1546:5425 +1546:5426 +1546:5427 +1546:5428 +1546:5429 +1547:5382 +1547:5383 +1547:5388 +1547:5389 +1547:5390 +1547:5391 +1547:5392 +1547:5393 +1548:5394 +1548:5395 +1548:5396 +1548:5397 +1548:5398 +1548:5399 +1548:5400 +1548:5401 +1548:5402 +1548:5403 +1548:5404 +1548:5405 +1548:5406 +1548:5407 +1548:5409 +1548:5410 +1548:5411 +1550:5431 +1550:5432 +1550:5433 +1550:5434 +1550:5436 +1550:5437 +1551:5439 +1555:6139 +1555:6141 +1555:6142 +1555:6143 +1555:6153 +1555:6155 +1555:6185 +1555:6214 +1555:6243 +1555:6272 +1555:6305 +1555:6319 +1555:6331 +1555:6332 +1555:6333 +1555:6334 +1555:6335 +1555:6336 +1555:6354 +1555:6355 +1557:6156 +1557:6157 +1557:6158 +1557:6159 +1557:6160 +1557:6161 +1557:6163 +1557:6165 +1557:6166 +1557:6167 +1557:6168 +1557:6169 +1557:6170 +1557:6171 +1557:6172 +1558:6144 +1558:6145 +1558:6173 +1558:6174 +1558:6175 +1558:6176 +1559:6177 +1559:6178 +1559:6179 +1559:6180 +1559:6182 +1559:6183 +1561:6186 +1561:6187 +1561:6188 +1561:6189 +1561:6190 +1561:6191 +1561:6192 +1561:6194 +1561:6196 +1561:6197 +1561:6198 +1561:6199 +1561:6200 +1561:6201 +1561:6202 +1561:6203 +1562:6146 +1562:6204 +1562:6205 +1563:6206 +1563:6207 +1563:6208 +1563:6209 +1563:6211 +1563:6212 +1565:6215 +1565:6216 +1565:6217 +1565:6218 +1565:6219 +1565:6220 +1565:6221 +1565:6223 +1565:6225 +1565:6226 +1565:6227 +1565:6228 +1565:6229 +1565:6230 +1565:6231 +1565:6232 +1566:6147 +1566:6233 +1566:6234 +1567:6235 +1567:6236 +1567:6237 +1567:6238 +1567:6240 +1567:6241 +1569:6244 +1569:6245 +1569:6246 +1569:6247 +1569:6248 +1569:6249 +1569:6250 +1569:6252 +1569:6254 +1569:6255 +1569:6256 +1569:6257 +1569:6258 +1569:6259 +1569:6260 +1569:6261 +1570:6148 +1570:6262 +1570:6263 +1571:6264 +1571:6265 +1571:6266 +1571:6267 +1571:6269 +1571:6270 +1574:6273 +1574:6274 +1574:6275 +1574:6276 +1574:6277 +1574:6278 +1574:6279 +1574:6281 +1574:6283 +1574:6284 +1574:6285 +1574:6286 +1574:6287 +1574:6288 +1574:6289 +1574:6290 +1575:6149 +1575:6291 +1575:6292 +1576:6150 +1576:6294 +1576:6295 +1577:6296 +1577:6297 +1577:6298 +1577:6299 +1577:6301 +1577:6302 +1581:6151 +1581:6306 +1581:6307 +1581:6308 +1581:6309 +1581:6310 +1582:6311 +1582:6312 +1582:6313 +1582:6314 +1582:6316 +1582:6317 +1585:6152 +1585:6320 +1585:6321 +1585:6322 +1586:6323 +1586:6324 +1586:6325 +1586:6326 +1586:6328 +1586:6329 +1590:6337 +1590:6338 +1590:6350 +1590:6351 +1590:6352 +1590:6353 +1591:6340 +1592:6341 +1593:6342 +1594:6343 +1595:6344 +1596:6345 +1597:6346 +1598:6347 +1599:6348 +1605:6357 +1605:6359 +1605:6360 +1605:6361 +1605:6394 +1605:6423 +1605:6456 +1605:6457 +1605:6458 +1605:6459 +1605:6460 +1605:6461 +1605:6462 +1607:6367 +1607:6368 +1607:6369 +1607:6370 +1607:6371 +1607:6372 +1607:6374 +1607:6376 +1607:6377 +1607:6378 +1607:6379 +1607:6380 +1607:6381 +1607:6382 +1607:6383 +1608:6362 +1608:6384 +1608:6385 +1609:6386 +1609:6387 +1609:6388 +1609:6389 +1609:6391 +1609:6392 +1611:6395 +1611:6396 +1611:6397 +1611:6398 +1611:6399 +1611:6400 +1611:6401 +1611:6403 +1611:6405 +1611:6406 +1611:6407 +1611:6408 +1611:6409 +1611:6410 +1611:6411 +1611:6412 +1612:6363 +1612:6413 +1612:6414 +1613:6415 +1613:6416 +1613:6417 +1613:6418 +1613:6420 +1613:6421 +1616:6424 +1616:6425 +1616:6426 +1616:6427 +1616:6428 +1616:6429 +1616:6430 +1616:6432 +1616:6434 +1616:6435 +1616:6436 +1616:6437 +1616:6438 +1616:6439 +1616:6440 +1616:6441 +1617:6364 +1617:6442 +1617:6443 +1618:6365 +1618:6445 +1618:6446 +1619:6447 +1619:6448 +1619:6449 +1619:6450 +1619:6452 +1619:6453 +1623:8241 +1623:8243 +1623:8244 +1623:8245 +1623:8249 +1623:8316 +1623:8317 +1624:8251 +1624:8252 +1625:8254 +1625:8255 +1625:8256 +1625:8257 +1625:8258 +1625:8259 +1625:8261 +1625:8263 +1625:8264 +1625:8265 +1625:8266 +1625:8267 +1625:8268 +1625:8269 +1625:8270 +1625:8293 +1625:8297 +1625:8298 +1625:8299 +1625:8300 +1626:8246 +1626:8247 +1626:8271 +1626:8272 +1626:8274 +1626:8275 +1626:8276 +1626:8277 +1626:8278 +1626:8279 +1626:8280 +1626:8287 +1626:8288 +1626:8289 +1626:8290 +1626:8291 +1627:8248 +1627:8294 +1627:8295 +1627:8296 +1629:8303 +1630:8304 +1630:8305 +1630:8306 +1630:8310 +1630:8312 +1630:8313 +1631:8307 +1631:8315 +1632:8308 +1633:8309 +1638:8319 +1638:8320 +1638:8322 +1638:8323 +1638:8324 +1638:8388 +1638:8389 +1639:8331 +1639:8332 +1639:8333 +1639:8334 +1639:8335 +1639:8343 +1639:8344 +1639:8345 +1639:8346 +1639:8347 +1640:8325 +1640:8326 +1640:8350 +1640:8351 +1640:8352 +1640:8353 +1640:8354 +1640:8355 +1640:8356 +1640:8357 +1640:8358 +1640:8359 +1640:8360 +1640:8381 +1640:8382 +1640:8383 +1640:8384 +1640:8385 +1641:8361 +1641:8362 +1641:8387 +1642:8327 +1642:8328 +1642:8366 +1642:8367 +1642:8368 +1642:8369 +1642:8370 +1642:8371 +1642:8372 +1642:8373 +1642:8374 +1642:8375 +1643:8376 +1643:8377 +1647:8391 +1647:8392 +1647:8393 +1647:8395 +1647:8396 +1647:8397 +1647:8422 +1647:8423 +1649:8399 +1649:8400 +1649:8401 +1650:8400 +1651:8402 +1651:8403 +1651:8404 +1651:8405 +1651:8406 +1651:8407 +1651:8414 +1651:8415 +1651:8416 +1651:8417 +1651:8419 +1651:8420 +1651:8421 +1652:8408 +1652:8409 +1653:8411 +1653:8412 +1653:8413 +1654:8412 +1658:8072 +1658:8074 +1658:8075 +1658:8076 +1658:8127 +1658:8128 +1660:8078 +1660:8079 +1660:8080 +1660:8081 +1660:8082 +1660:8083 +1660:8084 +1660:8085 +1660:8086 +1660:8087 +1660:8088 +1660:8089 +1660:8090 +1660:8091 +1660:8092 +1660:8093 +1660:8094 +1660:8095 +1660:8096 +1660:8097 +1660:8098 +1660:8101 +1660:8102 +1660:8103 +1660:8104 +1660:8105 +1660:8106 +1660:8109 +1660:8110 +1660:8111 +1660:8112 +1660:8113 +1660:8114 +1660:8115 +1660:8116 +1660:8117 +1660:8118 +1660:8119 +1660:8120 +1660:8121 +1660:8122 +1660:8123 +1660:8124 +1660:8125 +1662:8126 +1663:8478 +1663:8479 +1663:8480 +1663:8482 +1663:8483 +1663:8484 +1663:8533 +1663:8534 +1665:8485 +1665:8489 +1665:8490 +1665:8491 +1665:8492 +1665:8494 +1665:8495 +1665:8496 +1665:8497 +1666:8500 +1666:8501 +1667:8502 +1667:8503 +1667:8504 +1667:8505 +1667:8506 +1667:8507 +1667:8525 +1667:8526 +1667:8527 +1667:8528 +1667:8530 +1667:8531 +1667:8532 +1668:8508 +1668:8509 +1669:8486 +1669:8512 +1669:8513 +1669:8514 +1669:8515 +1669:8517 +1669:8518 +1669:8519 +1669:8520 +1670:8523 +1670:8524 +1674:8130 +1674:8132 +1674:8133 +1674:8134 +1674:8137 +1674:8238 +1674:8239 +1676:8135 +1676:8139 +1676:8140 +1677:8136 +1677:8142 +1677:8143 +1677:8144 +1677:8145 +1677:8146 +1677:8147 +1677:8148 +1677:8149 +1677:8150 +1677:8151 +1677:8152 +1677:8153 +1677:8154 +1677:8155 +1677:8162 +1677:8163 +1677:8164 +1677:8165 +1677:8166 +1678:8168 +1678:8169 +1678:8170 +1678:8171 +1678:8172 +1678:8173 +1678:8174 +1678:8175 +1679:8176 +1679:8177 +1679:8178 +1679:8179 +1679:8180 +1680:8219 +1680:8223 +1680:8224 +1680:8225 +1680:8226 +1681:8182 +1681:8183 +1681:8184 +1681:8185 +1681:8186 +1681:8187 +1681:8189 +1681:8190 +1681:8191 +1681:8192 +1681:8193 +1681:8194 +1681:8195 +1681:8196 +1681:8197 +1681:8198 +1681:8199 +1681:8200 +1681:8201 +1681:8202 +1681:8204 +1681:8205 +1681:8206 +1681:8207 +1681:8209 +1681:8210 +1681:8211 +1681:8212 +1681:8213 +1681:8214 +1681:8215 +1681:8216 +1681:8217 +1681:8218 +1682:8220 +1682:8221 +1682:8222 +1684:8229 +1684:8230 +1684:8231 +1684:8232 +1684:8234 +1684:8235 +1685:8237 +1695:2636 +1695:2644 +1695:2645 +1695:2646 +1695:2648 +1695:2649 +1695:2650 +1695:2658 +1695:2671 +1695:2777 +1695:2778 +1695:2779 +1695:2780 +1695:2782 +1695:2783 +1696:2637 +1696:2659 +1697:2638 +1697:2660 +1698:2639 +1698:2661 +1699:2640 +1699:2662 +1700:2641 +1700:2663 +1701:2642 +1701:2664 +1702:2643 +1702:2665 +1704:2667 +1704:2668 +1704:2669 +1704:2670 +1708:2651 +1708:2652 +1708:2672 +1708:2674 +1708:2675 +1708:2676 +1708:2677 +1708:2678 +1708:2679 +1708:2680 +1708:2694 +1708:2695 +1708:2696 +1708:2697 +1708:2698 +1709:2653 +1709:2654 +1709:2683 +1709:2684 +1709:2685 +1709:2686 +1709:2687 +1709:2688 +1709:2689 +1709:2690 +1713:2655 +1713:2700 +1713:2701 +1713:2702 +1713:2703 +1719:2656 +1719:2705 +1719:2706 +1719:2707 +1719:2708 +1719:2709 +1719:2710 +1719:2712 +1719:2713 +1719:2715 +1719:2716 +1719:2717 +1719:2718 +1719:2719 +1719:2720 +1719:2721 +1719:2722 +1719:2723 +1719:2724 +1719:2725 +1719:2727 +1719:2728 +1719:2729 +1719:2730 +1727:2657 +1727:2734 +1727:2735 +1727:2736 +1727:2737 +1727:2738 +1727:2739 +1727:2741 +1727:2742 +1727:2744 +1727:2745 +1727:2746 +1727:2747 +1727:2748 +1727:2749 +1727:2750 +1727:2751 +1727:2753 +1727:2754 +1727:2755 +1727:2757 +1727:2759 +1727:2760 +1727:2761 +1727:2762 +1729:2765 +1729:2766 +1729:2767 +1729:2771 +1729:2773 +1729:2774 +1730:2768 +1730:2776 +1731:2769 +1739:7995 +1739:7998 +1739:7999 +1739:8000 +1739:8002 +1739:8003 +1739:8004 +1739:8010 +1739:8069 +1739:8070 +1740:7996 +1740:8011 +1741:7997 +1741:8012 +1742:8013 +1743:8014 +1745:8005 +1745:8006 +1745:8016 +1745:8017 +1745:8018 +1745:8019 +1748:8007 +1748:8020 +1748:8021 +1748:8022 +1748:8023 +1754:8008 +1754:8025 +1754:8026 +1754:8027 +1754:8028 +1754:8029 +1754:8030 +1754:8032 +1754:8033 +1754:8035 +1754:8036 +1754:8037 +1754:8038 +1754:8039 +1754:8040 +1754:8041 +1754:8042 +1754:8043 +1754:8044 +1754:8045 +1754:8047 +1754:8048 +1754:8049 +1754:8050 +1754:8053 +1759:8054 +1759:8055 +1761:8009 +1761:8057 +1761:8058 +1762:8059 +1762:8060 +1762:8061 +1762:8063 +1762:8065 +1762:8066 +1763:8068 +1770:8425 +1770:8429 +1770:8430 +1770:8431 +1770:8433 +1770:8434 +1770:8435 +1770:8475 +1770:8476 +1771:8426 +1772:8427 +1772:8436 +1772:8439 +1772:8440 +1773:8428 +1774:8437 +1774:8442 +1774:8443 +1774:8444 +1774:8445 +1774:8446 +1774:8461 +1774:8462 +1774:8463 +1774:8464 +1774:8465 +1775:8467 +1775:8468 +1775:8469 +1775:8470 +1775:8472 +1775:8473 +1779:2996 +1779:2997 +1779:2998 +1779:2999 +1779:3001 +1779:3002 +1779:3003 +1779:3005 +1779:3017 +1779:3018 +1781:3004 +1781:3007 +1781:3008 +1782:3009 +1782:3010 +1782:3011 +1782:3012 +1782:3014 +1782:3015 +1785:8953 +1785:8955 +1785:8956 +1785:8957 +1785:8964 +1785:8965 +1786:8959 +1786:8960 +1786:8961 +1786:8962 +1788:8963 +1792:4082 +1792:4085 +1792:4086 +1792:4087 +1792:4089 +1792:4090 +1792:4091 +1792:4123 +1792:4124 +1793:4083 +1793:4093 +1793:4094 +1793:4095 +1793:4096 +1793:4098 +1793:4099 +1794:4084 +1794:4101 +1794:4102 +1794:4103 +1794:4115 +1794:4116 +1794:4117 +1794:4118 +1794:4120 +1794:4121 +1794:4122 +1797:4104 +1797:4105 +1798:4106 +1799:4107 +1799:4108 +1799:4109 +1799:4110 +1799:4112 +1799:4113 +1805:3067 +1805:3068 +1805:3070 +1805:3071 +1805:3072 +1805:3074 +1805:3083 +1805:3101 +1805:3102 +1806:3075 +1806:3076 +1806:3077 +1806:3078 +1806:3079 +1806:3080 +1806:3081 +1806:3082 +1811:3084 +1811:3085 +1811:3097 +1811:3098 +1811:3099 +1811:3100 +1812:3087 +1813:3088 +1814:3089 +1815:3090 +1816:3091 +1817:3092 +1818:3093 +1819:3094 +1820:3095 +1857:7962 +1857:7964 +1857:7965 +1857:7966 +1857:7992 +1857:7993 +1858:7968 +1858:7969 +1858:7970 +1858:7971 +1858:7972 +1858:7973 +1858:7974 +1858:7975 +1858:7976 +1858:7977 +1858:7978 +1858:7979 +1858:7980 +1858:7981 +1858:7982 +1858:7983 +1858:7984 +1858:7985 +1858:7986 +1858:7987 +1858:7989 +1858:7990 +1858:7991 +1868:7888 +1868:7895 +1868:7897 +1868:7898 +1868:7899 +1868:7900 +1868:7959 +1868:7960 +1869:7889 +1870:7890 +1870:7903 +1870:7944 +1870:7945 +1870:7946 +1870:7947 +1870:7948 +1871:7891 +1871:7904 +1871:7905 +1871:7906 +1871:7907 +1871:7908 +1871:7909 +1871:7910 +1871:7911 +1871:7912 +1871:7913 +1871:7914 +1871:7915 +1871:7916 +1871:7917 +1871:7918 +1871:7919 +1871:7920 +1872:7892 +1872:7921 +1872:7922 +1872:7923 +1872:7924 +1872:7925 +1872:7926 +1872:7929 +1872:7930 +1872:7931 +1872:7932 +1872:7934 +1872:7935 +1873:7893 +1873:7927 +1873:7928 +1874:7894 +1876:7950 +1876:7951 +1876:7952 +1876:7953 +1876:7955 +1876:7956 +1877:7958 +1881:9008 +1881:9009 +1881:9011 +1881:9012 +1881:9013 +1881:9019 +1881:9093 +1881:9094 +1884:9014 +1884:9021 +1884:9022 +1885:9027 +1885:9029 +1885:9030 +1885:9031 +1885:9032 +1886:9015 +1886:9024 +1886:9025 +1886:9026 +1892:9036 +1892:9037 +1892:9038 +1892:9039 +1892:9040 +1892:9041 +1892:9049 +1892:9050 +1892:9051 +1892:9052 +1892:9053 +1894:9016 +1894:9017 +1894:9055 +1894:9056 +1894:9057 +1897:9018 +1897:9059 +1897:9060 +1897:9061 +1897:9062 +1897:9063 +1897:9073 +1897:9074 +1897:9075 +1897:9076 +1897:9077 +1900:9079 +1900:9080 +1900:9088 +1900:9090 +1900:9091 +1901:9082 +1902:9083 +1903:9084 +1904:9085 +1905:9086 +1911:9146 +1911:9148 +1911:9149 +1911:9150 +1911:9151 +1911:9180 +1911:9181 +1914:9153 +1915:9154 +1915:9155 +1916:9156 +1916:9157 +1916:9158 +1916:9164 +1916:9165 +1916:9166 +1916:9167 +1916:9169 +1916:9170 +1917:9159 +1917:9160 +1917:9161 +1917:9162 +1917:9163 +1920:9171 +1920:9172 +1920:9173 +1920:9174 +1920:9176 +1920:9177 +1921:9179 +1923:9183 +1923:9185 +1923:9186 +1923:9187 +1923:9191 +1923:9193 +1923:9265 +1923:9281 +1923:9282 +1925:9188 +1925:9189 +1925:9190 +1925:9194 +1925:9196 +1925:9197 +1925:9198 +1925:9199 +1925:9200 +1925:9201 +1925:9202 +1925:9211 +1925:9212 +1925:9213 +1925:9214 +1925:9215 +1925:9218 +1925:9219 +1925:9220 +1925:9221 +1925:9222 +1925:9223 +1925:9224 +1925:9232 +1925:9233 +1925:9234 +1925:9235 +1925:9236 +1925:9239 +1925:9240 +1925:9241 +1925:9242 +1925:9243 +1925:9245 +1925:9246 +1925:9247 +1925:9248 +1925:9251 +1925:9252 +1925:9253 +1926:9254 +1926:9255 +1926:9259 +1926:9261 +1926:9262 +1927:9257 +1927:9264 +1931:9266 +1931:9267 +1931:9277 +1931:9278 +1931:9279 +1931:9280 +1932:9269 +1933:9270 +1934:9271 +1935:9272 +1936:9273 +1937:9274 +1938:9275 +1953:9096 +1953:9098 +1953:9099 +1953:9100 +1953:9101 +1953:9143 +1953:9144 +1956:9103 +1956:9104 +1956:9105 +1956:9106 +1956:9123 +1956:9127 +1956:9128 +1956:9129 +1956:9130 +1956:9131 +1956:9132 +1956:9133 +1957:9124 +1957:9125 +1957:9126 +1958:9107 +1958:9108 +1958:9109 +1958:9110 +1958:9111 +1958:9112 +1958:9113 +1959:9116 +1959:9117 +1959:9118 +1959:9119 +1959:9120 +1961:9134 +1961:9135 +1961:9136 +1961:9137 +1961:9139 +1961:9140 +1962:9142 +1970:9284 +1970:9289 +1970:9290 +1970:9291 +1970:9293 +1970:9294 +1970:9295 +1970:9322 +1970:9327 +1970:9330 +1970:9331 +1970:9332 +1970:9333 +1970:9335 +1970:9336 +1971:9285 +1971:9297 +1971:9298 +1971:9299 +1971:9300 +1971:9301 +1971:9302 +1971:9304 +1971:9305 +1971:9306 +1971:9308 +1971:9309 +1971:9310 +1971:9311 +1971:9312 +1971:9313 +1971:9314 +1971:9315 +1971:9329 +1972:9286 +1972:9316 +1972:9317 +1972:9318 +1972:9319 +1972:9320 +1973:9287 +1974:9288 +1974:9321 +1974:9323 +1974:9324 +1974:9325 +1974:9326 +1980:9361 +1980:9362 +1980:9364 +1980:9365 +1980:9366 +1980:9374 +1980:9375 +1982:9368 +1982:9369 +1982:9370 +1982:9371 +1982:9372 +1982:9373 +1986:9377 +1986:9378 +1986:9380 +1986:9381 +1986:9382 +1986:9385 +1986:9398 +1986:9399 +1987:9383 +1987:9384 +1987:9387 +1987:9388 +1987:9389 +1988:9390 +1988:9391 +1988:9392 +1988:9393 +1988:9395 +1988:9396 +2003:7876 +2003:7878 +2003:7879 +2003:7880 +2003:7885 +2003:7886 +2004:7882 +2004:7883 +2004:7884 +2008:8896 +2008:8897 +2008:8899 +2008:8900 +2008:8901 +2008:8903 +2008:8905 +2008:8920 +2008:8950 +2008:8951 +2009:8906 +2009:8907 +2011:8902 +2011:8908 +2011:8909 +2012:8910 +2013:8911 +2013:8912 +2013:8913 +2013:8914 +2013:8916 +2013:8917 +2014:8919 +2017:8921 +2017:8922 +2017:8946 +2017:8947 +2017:8948 +2017:8949 +2018:8924 +2019:8925 +2020:8926 +2021:8927 +2022:8928 +2023:8929 +2024:8930 +2025:8931 +2026:8932 +2027:8933 +2028:8934 +2030:8936 +2031:8937 +2032:8938 +2033:8939 +2034:8940 +2035:8941 +2036:8942 +2037:8943 +2038:8944 +2044:1638 +2044:1639 +2044:1640 +2044:1641 +2044:1643 +2044:1644 +2044:1645 +2044:1826 +2044:1827 +2046:1648 +2046:1649 +2046:1650 +2046:1651 +2046:1652 +2046:1653 +2046:1654 +2046:1655 +2046:1656 +2046:1657 +2046:1658 +2046:1659 +2046:1660 +2046:1661 +2046:1662 +2046:1663 +2046:1664 +2046:1665 +2046:1666 +2046:1667 +2046:1668 +2046:1669 +2046:1670 +2046:1671 +2046:1672 +2046:1673 +2046:1674 +2046:1675 +2046:1676 +2046:1677 +2046:1678 +2046:1679 +2046:1680 +2046:1681 +2046:1682 +2046:1683 +2046:1684 +2046:1685 +2046:1686 +2046:1687 +2046:1688 +2046:1689 +2046:1690 +2046:1691 +2046:1692 +2046:1693 +2046:1694 +2046:1695 +2046:1696 +2046:1697 +2046:1698 +2046:1699 +2046:1700 +2046:1701 +2046:1702 +2046:1703 +2046:1704 +2046:1705 +2046:1706 +2046:1707 +2046:1708 +2046:1709 +2046:1710 +2046:1711 +2046:1712 +2046:1713 +2046:1714 +2046:1724 +2046:1725 +2046:1726 +2046:1727 +2046:1728 +2046:1730 +2046:1731 +2046:1732 +2046:1733 +2046:1734 +2046:1736 +2046:1737 +2046:1738 +2046:1739 +2046:1740 +2046:1741 +2046:1742 +2046:1743 +2046:1744 +2046:1745 +2046:1746 +2046:1747 +2046:1748 +2046:1749 +2046:1750 +2046:1751 +2046:1752 +2046:1753 +2046:1754 +2046:1755 +2046:1756 +2046:1757 +2046:1758 +2046:1759 +2046:1760 +2046:1761 +2046:1762 +2046:1763 +2046:1764 +2046:1765 +2046:1766 +2046:1767 +2046:1768 +2046:1769 +2046:1770 +2046:1771 +2046:1772 +2046:1773 +2046:1774 +2046:1775 +2046:1776 +2046:1777 +2046:1778 +2046:1779 +2046:1780 +2046:1781 +2046:1782 +2046:1783 +2046:1784 +2046:1785 +2046:1786 +2046:1787 +2046:1788 +2046:1789 +2046:1790 +2046:1791 +2046:1792 +2046:1793 +2046:1794 +2046:1795 +2046:1796 +2046:1797 +2046:1798 +2046:1799 +2046:1800 +2046:1801 +2046:1802 +2046:1812 +2046:1813 +2046:1814 +2046:1815 +2046:1816 +2046:1818 +2046:1819 +2046:1820 +2046:1821 +2046:1823 +2046:1824 +2046:1825 +2053:9401 +2053:9405 +2053:9407 +2053:9408 +2053:9409 +2053:9412 +2053:9430 +2053:9431 +2054:9402 +2054:9414 +2054:9415 +2055:9403 +2055:9410 +2055:9416 +2055:9417 +2056:9404 +2056:9411 +2056:9418 +2056:9419 +2057:9420 +2058:9421 +2058:9422 +2058:9423 +2058:9424 +2058:9426 +2058:9427 +2059:9429 +2065:9338 +2065:9342 +2065:9344 +2065:9345 +2065:9346 +2065:9347 +2065:9358 +2065:9359 +2066:9339 +2066:9349 +2066:9350 +2066:9351 +2066:9352 +2066:9354 +2066:9355 +2067:9340 +2067:9357 +2068:9341 +2074:9433 +2074:9438 +2074:9440 +2074:9441 +2074:9442 +2074:9445 +2074:9466 +2074:9467 +2075:9434 +2075:9447 +2075:9448 +2076:9435 +2076:9443 +2076:9449 +2076:9450 +2077:9436 +2077:9444 +2077:9451 +2077:9452 +2078:9437 +2078:9453 +2079:9454 +2079:9455 +2079:9460 +2079:9462 +2079:9463 +2080:9457 +2080:9465 +2081:9458 +2089:1021 +2089:1025 +2089:1026 +2089:1027 +2089:1029 +2089:1030 +2089:1031 +2089:1046 +2089:1048 +2089:1049 +2089:1328 +2089:1349 +2089:1371 +2089:1393 +2089:1454 +2089:1460 +2089:1483 +2089:1490 +2089:1509 +2089:1510 +2089:1511 +2089:1512 +2089:1513 +2089:1514 +2089:1548 +2089:1549 +2090:1022 +2091:1023 +2092:1024 +2093:1329 +2093:1330 +2093:1331 +2093:1332 +2093:1333 +2093:1334 +2093:1336 +2093:1338 +2093:1339 +2093:1340 +2093:1341 +2093:1342 +2093:1343 +2093:1344 +2093:1345 +2094:1346 +2094:1347 +2094:1348 +2096:1350 +2096:1351 +2096:1352 +2096:1353 +2096:1354 +2096:1355 +2096:1356 +2096:1358 +2096:1360 +2096:1361 +2096:1362 +2096:1363 +2096:1364 +2096:1365 +2096:1366 +2096:1367 +2097:1368 +2097:1369 +2097:1370 +2103:1372 +2103:1373 +2103:1374 +2103:1375 +2103:1376 +2103:1377 +2103:1378 +2103:1380 +2103:1382 +2103:1383 +2103:1384 +2103:1385 +2103:1386 +2103:1387 +2103:1388 +2103:1389 +2104:1390 +2104:1391 +2104:1392 +2109:1394 +2109:1395 +2109:1396 +2109:1397 +2109:1398 +2109:1399 +2109:1400 +2109:1402 +2109:1403 +2109:1405 +2109:1406 +2109:1407 +2109:1408 +2109:1409 +2109:1410 +2109:1411 +2109:1412 +2110:1032 +2110:1413 +2110:1414 +2111:1415 +2111:1416 +2111:1417 +2111:1418 +2111:1420 +2111:1421 +2112:1424 +2112:1425 +2112:1426 +2112:1427 +2112:1428 +2112:1429 +2112:1431 +2112:1433 +2112:1434 +2112:1435 +2112:1436 +2112:1437 +2112:1438 +2112:1439 +2112:1440 +2112:1441 +2112:1442 +2112:1443 +2112:1447 +2112:1448 +2112:1449 +2112:1450 +2113:1444 +2113:1445 +2113:1446 +2113:1453 +2120:1033 +2120:1455 +2120:1456 +2120:1457 +2120:1458 +2120:1459 +2124:1034 +2124:1035 +2124:1050 +2124:1051 +2124:1052 +2124:1053 +2124:1054 +2124:1055 +2124:1056 +2124:1057 +2124:1058 +2124:1059 +2125:1118 +2125:1120 +2125:1121 +2125:1122 +2125:1123 +2133:1061 +2133:1062 +2133:1063 +2133:1064 +2133:1065 +2133:1066 +2133:1069 +2133:1070 +2133:1071 +2133:1072 +2133:1073 +2133:1080 +2133:1081 +2133:1082 +2133:1083 +2133:1084 +2133:1086 +2133:1088 +2133:1089 +2133:1090 +2133:1091 +2133:1092 +2133:1093 +2133:1094 +2133:1095 +2134:1097 +2134:1098 +2134:1099 +2134:1100 +2134:1101 +2134:1108 +2134:1109 +2134:1110 +2134:1111 +2134:1112 +2135:1036 +2135:1114 +2135:1115 +2135:1116 +2135:1117 +2137:1126 +2137:1127 +2137:1128 +2137:1129 +2137:1131 +2137:1132 +2138:1134 +2140:1137 +2140:1138 +2140:1139 +2140:1140 +2140:1141 +2143:1037 +2143:1144 +2143:1145 +2143:1146 +2143:1147 +2143:1148 +2143:1149 +2143:1150 +2143:1151 +2144:1038 +2144:1039 +2144:1040 +2144:1153 +2144:1154 +2144:1155 +2144:1156 +2144:1157 +2144:1158 +2144:1161 +2144:1162 +2144:1163 +2144:1164 +2144:1165 +2144:1166 +2144:1167 +2144:1168 +2144:1169 +2144:1170 +2144:1171 +2144:1172 +2144:1173 +2144:1174 +2144:1175 +2144:1176 +2144:1177 +2144:1178 +2144:1179 +2144:1180 +2144:1181 +2144:1182 +2144:1183 +2144:1184 +2144:1185 +2144:1186 +2144:1187 +2144:1188 +2144:1189 +2144:1190 +2144:1191 +2144:1192 +2144:1193 +2144:1194 +2144:1195 +2144:1196 +2144:1197 +2144:1198 +2144:1199 +2144:1200 +2144:1201 +2144:1202 +2144:1203 +2144:1204 +2144:1205 +2144:1206 +2144:1207 +2144:1208 +2144:1209 +2144:1210 +2144:1211 +2144:1212 +2144:1213 +2144:1214 +2144:1215 +2144:1216 +2144:1217 +2144:1218 +2144:1219 +2144:1220 +2144:1221 +2144:1222 +2144:1223 +2144:1224 +2144:1225 +2144:1226 +2144:1229 +2144:1230 +2144:1231 +2144:1232 +2144:1233 +2145:1235 +2145:1236 +2145:1243 +2145:1245 +2145:1246 +2146:1238 +2146:1248 +2147:1239 +2148:1240 +2149:1241 +2157:1251 +2157:1252 +2157:1253 +2157:1254 +2157:1255 +2157:1256 +2157:1257 +2157:1258 +2157:1259 +2157:1260 +2157:1261 +2157:1262 +2157:1263 +2158:1264 +2159:1266 +2163:1269 +2164:1461 +2164:1462 +2164:1463 +2164:1464 +2164:1465 +2164:1466 +2164:1467 +2164:1469 +2164:1470 +2164:1472 +2164:1473 +2164:1474 +2164:1475 +2164:1476 +2164:1477 +2164:1478 +2164:1479 +2164:1480 +2164:1481 +2164:1482 +2167:1041 +2167:1484 +2167:1485 +2167:1486 +2167:1487 +2167:1488 +2167:1489 +2170:1042 +2170:1272 +2170:1273 +2170:1274 +2170:1275 +2170:1276 +2170:1277 +2170:1278 +2170:1279 +2170:1280 +2170:1281 +2171:1043 +2171:1282 +2171:1283 +2171:1284 +2171:1285 +2171:1286 +2171:1287 +2171:1291 +2171:1292 +2171:1293 +2171:1294 +2171:1296 +2171:1297 +2173:1288 +2173:1289 +2174:1298 +2175:1299 +2175:1300 +2175:1301 +2175:1302 +2175:1304 +2175:1305 +2176:1307 +2178:1310 +2178:1311 +2178:1312 +2178:1313 +2178:1314 +2181:1044 +2181:1045 +2181:1491 +2181:1492 +2181:1493 +2181:1494 +2181:1495 +2181:1496 +2181:1497 +2181:1498 +2181:1499 +2182:1500 +2182:1501 +2182:1502 +2182:1503 +2182:1505 +2182:1506 +2183:1508 +2189:1317 +2189:1318 +2189:1319 +2189:1320 +2189:1321 +2189:1322 +2189:1323 +2189:1324 +2189:1325 +2193:1515 +2193:1516 +2193:1544 +2193:1545 +2193:1546 +2193:1547 +2194:1518 +2195:1519 +2196:1520 +2197:1521 +2198:1522 +2199:1523 +2200:1524 +2201:1525 +2202:1526 +2203:1527 +2204:1528 +2205:1529 +2206:1530 +2207:1531 +2208:1532 +2209:1533 +2210:1534 +2211:1535 +2212:1536 +2213:1537 +2214:1538 +2215:1539 +2216:1540 +2217:1541 +2218:1542 +2223:9682 +2223:9684 +2223:9685 +2223:9686 +2223:9692 +2223:9901 +2223:9902 +2224:9694 +2225:9695 +2226:9687 +2226:9697 +2226:9698 +2226:9699 +2226:9700 +2226:9701 +2226:9702 +2226:9704 +2226:9705 +2226:9706 +2226:9707 +2226:9710 +2226:9711 +2226:9712 +2226:9713 +2226:9714 +2226:9715 +2226:9716 +2226:9717 +2226:9718 +2226:9719 +2226:9720 +2226:9721 +2226:9722 +2226:9723 +2226:9724 +2226:9725 +2226:9726 +2226:9727 +2226:9728 +2226:9729 +2226:9730 +2226:9731 +2226:9732 +2226:9733 +2226:9734 +2226:9735 +2226:9736 +2226:9737 +2226:9738 +2226:9739 +2226:9740 +2226:9741 +2226:9742 +2226:9743 +2226:9744 +2226:9745 +2226:9746 +2226:9747 +2226:9748 +2226:9749 +2226:9750 +2226:9751 +2226:9752 +2226:9753 +2226:9754 +2226:9755 +2226:9756 +2226:9757 +2226:9758 +2226:9759 +2226:9760 +2226:9761 +2226:9762 +2226:9764 +2226:9765 +2226:9769 +2226:9770 +2226:9771 +2226:9772 +2226:9773 +2226:9775 +2226:9776 +2226:9777 +2226:9778 +2226:9779 +2226:9780 +2226:9781 +2226:9782 +2226:9783 +2226:9784 +2226:9785 +2226:9789 +2226:9790 +2226:9791 +2226:9792 +2233:9688 +2233:9786 +2233:9787 +2233:9788 +2235:9795 +2235:9796 +2236:9689 +2236:9690 +2236:9691 +2236:9798 +2236:9799 +2236:9800 +2236:9801 +2236:9802 +2236:9803 +2236:9806 +2236:9807 +2236:9808 +2236:9809 +2236:9810 +2236:9811 +2236:9812 +2236:9813 +2236:9814 +2236:9815 +2236:9816 +2236:9817 +2236:9818 +2236:9819 +2236:9820 +2236:9821 +2236:9822 +2236:9823 +2236:9824 +2236:9825 +2236:9826 +2236:9827 +2236:9828 +2236:9829 +2236:9830 +2236:9831 +2236:9832 +2236:9833 +2236:9834 +2236:9835 +2236:9836 +2236:9837 +2236:9838 +2236:9839 +2236:9840 +2236:9841 +2236:9842 +2236:9843 +2236:9844 +2236:9845 +2236:9846 +2236:9847 +2236:9848 +2236:9849 +2236:9850 +2236:9851 +2236:9852 +2236:9853 +2236:9854 +2236:9855 +2236:9856 +2236:9857 +2236:9858 +2236:9859 +2236:9860 +2236:9861 +2236:9862 +2236:9863 +2236:9864 +2236:9865 +2236:9866 +2236:9867 +2236:9868 +2236:9869 +2236:9870 +2236:9871 +2236:9874 +2236:9875 +2236:9876 +2236:9877 +2236:9878 +2237:9880 +2237:9881 +2237:9895 +2237:9897 +2237:9898 +2238:9883 +2238:9900 +2239:9884 +2240:9885 +2241:9886 +2242:9887 +2243:9888 +2244:9889 +2245:9890 +2246:9891 +2247:9892 +2248:9893 +2253:10314 +2253:10316 +2253:10317 +2253:10318 +2253:10319 +2253:10424 +2253:10425 +2257:10322 +2257:10323 +2257:10324 +2257:10325 +2257:10326 +2257:10327 +2257:10328 +2257:10329 +2257:10330 +2257:10331 +2257:10332 +2257:10333 +2257:10334 +2257:10335 +2257:10336 +2257:10337 +2257:10338 +2257:10339 +2257:10340 +2257:10341 +2257:10342 +2257:10343 +2257:10344 +2257:10345 +2257:10346 +2257:10347 +2257:10348 +2257:10349 +2257:10350 +2257:10351 +2257:10352 +2257:10353 +2257:10354 +2257:10355 +2257:10356 +2257:10357 +2257:10358 +2257:10359 +2257:10360 +2257:10361 +2257:10362 +2257:10363 +2257:10364 +2257:10365 +2257:10366 +2257:10367 +2257:10368 +2257:10369 +2257:10370 +2257:10371 +2257:10372 +2257:10373 +2257:10374 +2257:10375 +2257:10376 +2257:10377 +2257:10387 +2257:10388 +2257:10389 +2257:10390 +2257:10391 +2258:10380 +2258:10381 +2258:10382 +2258:10383 +2260:10394 +2260:10395 +2260:10396 +2260:10401 +2260:10407 +2260:10408 +2260:10409 +2260:10410 +2260:10412 +2260:10413 +2260:10414 +2261:10397 +2261:10398 +2261:10399 +2261:10400 +2262:10402 +2262:10403 +2262:10404 +2262:10405 +2264:10415 +2264:10416 +2264:10417 +2264:10418 +2264:10420 +2264:10421 +2265:10423 +2275:10427 +2275:10429 +2275:10430 +2275:10431 +2275:10518 +2275:10519 +2276:10439 +2276:10440 +2276:10441 +2276:10442 +2276:10443 +2276:10444 +2276:10446 +2276:10448 +2276:10449 +2276:10450 +2276:10451 +2276:10452 +2276:10453 +2276:10454 +2276:10455 +2276:10459 +2276:10465 +2276:10466 +2276:10467 +2276:10468 +2277:10432 +2277:10456 +2277:10457 +2277:10458 +2278:10460 +2278:10461 +2278:10462 +2278:10463 +2278:10464 +2280:10472 +2280:10511 +2280:10512 +2280:10513 +2280:10514 +2280:10515 +2281:10433 +2281:10434 +2281:10473 +2281:10474 +2281:10475 +2281:10476 +2281:10477 +2281:10478 +2281:10479 +2281:10480 +2281:10517 +2282:10482 +2282:10483 +2284:10486 +2284:10487 +2284:10488 +2284:10489 +2285:10490 +2286:10491 +2287:10492 +2288:10493 +2289:10494 +2290:10495 +2291:10496 +2292:10497 +2294:10435 +2294:10436 +2294:10500 +2294:10501 +2294:10502 +2294:10503 +2294:10504 +2294:10505 +2295:10507 +2295:10508 +2302:9577 +2302:9580 +2302:9582 +2302:9583 +2302:9584 +2302:9586 +2302:9588 +2302:9609 +2302:9658 +2302:9663 +2302:9664 +2302:9665 +2302:9666 +2302:9667 +2302:9668 +2302:9679 +2302:9680 +2303:9578 +2303:9589 +2303:9590 +2303:9591 +2303:9592 +2303:9593 +2303:9594 +2303:9596 +2303:9598 +2303:9599 +2303:9600 +2303:9601 +2303:9602 +2303:9603 +2303:9604 +2303:9605 +2304:9579 +2304:9606 +2304:9607 +2304:9608 +2306:9610 +2306:9611 +2306:9612 +2306:9613 +2306:9614 +2306:9615 +2306:9616 +2306:9618 +2306:9620 +2306:9621 +2306:9622 +2306:9623 +2306:9630 +2306:9631 +2306:9632 +2306:9633 +2306:9634 +2306:9636 +2306:9638 +2306:9639 +2306:9640 +2306:9641 +2306:9642 +2306:9643 +2306:9644 +2306:9645 +2307:9585 +2307:9646 +2307:9647 +2307:9648 +2308:9649 +2308:9650 +2308:9651 +2308:9652 +2308:9654 +2308:9655 +2309:9657 +2310:9659 +2310:9660 +2310:9661 +2310:9662 +2313:9669 +2313:9670 +2313:9675 +2313:9676 +2313:9677 +2313:9678 +2314:9672 +2315:9673 +2324:10065 +2324:10069 +2324:10071 +2324:10072 +2324:10073 +2324:10082 +2324:10084 +2324:10306 +2324:10307 +2324:10308 +2324:10309 +2324:10310 +2324:10311 +2324:10312 +2325:10066 +2326:10067 +2327:10068 +2327:10085 +2327:10086 +2327:10087 +2328:10074 +2328:10089 +2328:10090 +2328:10091 +2328:10092 +2328:10093 +2328:10094 +2328:10095 +2328:10096 +2328:10097 +2328:10098 +2328:10099 +2328:10100 +2328:10101 +2328:10102 +2328:10103 +2328:10104 +2328:10105 +2328:10106 +2328:10107 +2328:10108 +2328:10109 +2328:10110 +2328:10111 +2328:10112 +2328:10113 +2328:10114 +2328:10115 +2328:10116 +2328:10117 +2328:10118 +2328:10119 +2328:10120 +2328:10121 +2328:10122 +2328:10123 +2328:10124 +2328:10125 +2328:10141 +2328:10142 +2328:10143 +2328:10144 +2328:10145 +2331:10147 +2331:10148 +2331:10149 +2331:10150 +2331:10152 +2331:10153 +2332:10155 +2336:10158 +2336:10159 +2336:10160 +2337:10075 +2337:10076 +2337:10162 +2337:10163 +2337:10164 +2337:10165 +2337:10166 +2337:10167 +2337:10183 +2337:10184 +2337:10185 +2337:10186 +2337:10187 +2338:10189 +2338:10190 +2338:10191 +2338:10192 +2338:10194 +2338:10195 +2339:10197 +2340:10200 +2340:10201 +2340:10202 +2341:10077 +2341:10078 +2341:10204 +2341:10205 +2341:10206 +2341:10207 +2341:10208 +2341:10209 +2341:10225 +2341:10226 +2341:10227 +2341:10228 +2341:10229 +2342:10231 +2342:10232 +2342:10233 +2342:10234 +2342:10236 +2342:10237 +2343:10239 +2345:10079 +2345:10242 +2345:10243 +2345:10244 +2345:10245 +2345:10246 +2346:10247 +2346:10248 +2346:10249 +2346:10250 +2346:10252 +2346:10253 +2347:10255 +2350:10080 +2350:10258 +2350:10259 +2350:10260 +2350:10261 +2350:10262 +2351:10287 +2351:10289 +2351:10290 +2351:10291 +2351:10292 +2352:10264 +2352:10266 +2352:10267 +2352:10268 +2352:10269 +2352:10270 +2352:10279 +2352:10280 +2352:10281 +2352:10282 +2352:10283 +2353:10273 +2353:10274 +2353:10275 +2353:10276 +2355:10081 +2355:10285 +2355:10286 +2357:10295 +2357:10296 +2357:10297 +2357:10298 +2357:10300 +2357:10301 +2358:10303 +2362:9469 +2362:9470 +2362:9472 +2362:9473 +2362:9474 +2362:9491 +2362:9492 +2363:9475 +2363:9476 +2363:9478 +2363:9479 +2363:9480 +2363:9481 +2363:9482 +2363:9483 +2363:9484 +2363:9485 +2363:9486 +2363:9487 +2363:9489 +2365:9490 +2375:9494 +2375:9501 +2375:9502 +2375:9503 +2375:9505 +2375:9506 +2375:9507 +2375:9509 +2375:9551 +2375:9552 +2376:9495 +2377:9496 +2378:9497 +2378:9512 +2378:9513 +2378:9514 +2378:9515 +2378:9516 +2378:9517 +2378:9519 +2378:9521 +2378:9522 +2378:9523 +2378:9524 +2378:9525 +2378:9526 +2378:9527 +2378:9528 +2378:9531 +2378:9533 +2378:9534 +2378:9535 +2378:9536 +2379:9498 +2379:9529 +2379:9530 +2380:9499 +2381:9500 +2381:9508 +2381:9539 +2381:9540 +2381:9541 +2382:9542 +2382:9543 +2382:9544 +2382:9545 +2382:9547 +2382:9548 +2383:9550 +2385:8967 +2385:8969 +2385:8970 +2385:8971 +2385:8974 +2385:9005 +2385:9006 +2389:8972 +2389:8976 +2389:8977 +2389:8978 +2390:8979 +2390:8980 +2391:8996 +2391:8998 +2391:8999 +2391:9000 +2391:9001 +2392:9004 +2394:8973 +2394:8983 +2394:8984 +2394:8985 +2395:8986 +2395:8987 +2395:8991 +2395:8993 +2395:8994 +2396:8989 +2417:10766 +2417:10782 +2417:10783 +2417:10784 +2417:10786 +2417:10787 +2417:10788 +2417:10834 +2417:10847 +2417:10858 +2417:10859 +2417:10860 +2417:10861 +2417:10863 +2417:10864 +2418:10767 +2419:10768 +2419:10790 +2419:10791 +2419:10792 +2419:10793 +2419:10794 +2419:10795 +2419:10797 +2419:10798 +2419:10799 +2419:10800 +2419:10801 +2419:10802 +2419:10803 +2419:10804 +2419:10805 +2419:10806 +2419:10809 +2419:10810 +2419:10811 +2419:10812 +2419:10813 +2419:10814 +2419:10815 +2419:10816 +2420:10769 +2421:10770 +2421:10825 +2421:10827 +2421:10828 +2421:10829 +2421:10830 +2422:10771 +2422:10818 +2422:10819 +2422:10820 +2422:10833 +2423:10772 +2423:10821 +2424:10773 +2424:10822 +2425:10774 +2426:10775 +2427:10776 +2428:10777 +2429:10778 +2430:10779 +2431:10780 +2432:10781 +2435:10835 +2435:10836 +2435:10837 +2436:10838 +2437:10839 +2438:10840 +2439:10841 +2440:10842 +2441:10843 +2445:10846 +2449:10848 +2449:10849 +2449:10850 +2450:10851 +2451:10852 +2452:10853 +2453:10854 +2457:10857 +2458:10680 +2458:10681 +2458:10683 +2458:10684 +2458:10685 +2458:10763 +2458:10764 +2460:10688 +2460:10689 +2460:10690 +2460:10691 +2460:10693 +2460:10694 +2460:10695 +2460:10696 +2460:10697 +2460:10698 +2460:10699 +2460:10702 +2460:10703 +2460:10704 +2460:10705 +2460:10706 +2460:10707 +2460:10710 +2460:10711 +2460:10712 +2460:10713 +2460:10714 +2460:10756 +2460:10757 +2460:10758 +2460:10759 +2460:10760 +2461:10718 +2461:10719 +2461:10720 +2461:10721 +2461:10723 +2461:10724 +2461:10725 +2461:10726 +2461:10727 +2461:10728 +2461:10729 +2461:10732 +2461:10733 +2461:10734 +2461:10735 +2461:10736 +2461:10737 +2461:10740 +2461:10741 +2461:10742 +2461:10743 +2461:10744 +2461:10745 +2461:10748 +2461:10749 +2461:10750 +2461:10751 +2461:10752 +2461:10762 +2467:9973 +2467:9975 +2467:9976 +2467:9977 +2467:9978 +2467:10006 +2467:10007 +2468:9981 +2468:9982 +2468:9983 +2468:9987 +2468:9988 +2468:9989 +2468:9990 +2468:9992 +2468:9993 +2468:9994 +2476:9984 +2476:9985 +2476:9986 +2478:9995 +2478:9996 +2479:9997 +2479:9998 +2479:9999 +2479:10000 +2479:10002 +2479:10003 +2480:10005 +2482:10988 +2482:10990 +2482:10991 +2482:10992 +2482:11023 +2482:11024 +2483:10995 +2483:10996 +2483:10997 +2483:10998 +2483:10999 +2483:11000 +2483:11001 +2483:11002 +2483:11003 +2483:11006 +2483:11007 +2483:11008 +2483:11009 +2483:11010 +2483:11011 +2483:11014 +2483:11015 +2483:11016 +2483:11017 +2483:11018 +2483:11020 +2483:11021 +2485:11022 +2486:11026 +2486:11028 +2486:11029 +2486:11030 +2486:11031 +2486:11140 +2486:11141 +2487:11033 +2487:11034 +2487:11035 +2487:11036 +2487:11037 +2487:11038 +2487:11039 +2487:11041 +2487:11042 +2487:11043 +2487:11044 +2487:11045 +2487:11046 +2487:11047 +2487:11048 +2487:11049 +2487:11050 +2487:11051 +2487:11052 +2487:11053 +2487:11054 +2487:11055 +2487:11056 +2487:11057 +2487:11058 +2487:11059 +2487:11060 +2487:11061 +2487:11062 +2487:11063 +2487:11064 +2487:11065 +2487:11066 +2487:11067 +2487:11068 +2487:11069 +2487:11070 +2487:11071 +2487:11072 +2487:11073 +2487:11074 +2487:11075 +2487:11076 +2487:11077 +2487:11078 +2487:11079 +2487:11080 +2487:11081 +2487:11082 +2487:11083 +2487:11084 +2487:11085 +2487:11086 +2487:11087 +2487:11088 +2487:11089 +2487:11090 +2487:11091 +2487:11092 +2487:11093 +2487:11094 +2487:11095 +2487:11096 +2487:11097 +2487:11098 +2487:11099 +2487:11100 +2487:11101 +2487:11102 +2487:11103 +2487:11104 +2487:11105 +2487:11106 +2487:11107 +2487:11118 +2487:11119 +2487:11120 +2487:11121 +2487:11122 +2487:11124 +2487:11125 +2487:11126 +2487:11127 +2487:11129 +2487:11130 +2488:11131 +2488:11132 +2488:11133 +2488:11134 +2488:11136 +2488:11137 +2489:11139 +2492:11143 +2492:11145 +2492:11146 +2492:11147 +2492:11148 +2492:11170 +2492:11250 +2492:11251 +2492:11252 +2492:11253 +2492:11255 +2492:11256 +2494:11150 +2494:11151 +2494:11152 +2494:11153 +2494:11154 +2494:11155 +2494:11157 +2494:11159 +2494:11160 +2494:11161 +2494:11162 +2494:11163 +2494:11164 +2494:11165 +2494:11166 +2494:11167 +2494:11168 +2494:11169 +2496:11171 +2496:11173 +2496:11174 +2496:11175 +2496:11176 +2496:11177 +2496:11178 +2496:11179 +2496:11180 +2496:11181 +2496:11182 +2496:11183 +2496:11184 +2496:11185 +2496:11186 +2496:11187 +2496:11188 +2496:11189 +2496:11190 +2496:11191 +2496:11192 +2496:11193 +2496:11194 +2496:11195 +2496:11196 +2496:11197 +2496:11198 +2496:11199 +2496:11200 +2496:11201 +2496:11202 +2496:11203 +2496:11204 +2496:11205 +2496:11206 +2496:11207 +2496:11208 +2496:11209 +2496:11210 +2496:11211 +2496:11212 +2496:11213 +2496:11214 +2496:11215 +2496:11216 +2496:11217 +2496:11218 +2496:11219 +2496:11220 +2496:11221 +2496:11222 +2496:11223 +2496:11224 +2496:11225 +2496:11226 +2496:11227 +2496:11228 +2496:11235 +2496:11236 +2496:11237 +2496:11238 +2496:11239 +2497:11241 +2497:11242 +2497:11243 +2497:11244 +2497:11246 +2497:11247 +2498:11249 +2500:11294 +2500:11296 +2500:11297 +2500:11298 +2500:11299 +2500:11379 +2500:11380 +2501:11302 +2501:11303 +2501:11304 +2501:11305 +2501:11306 +2501:11307 +2501:11308 +2501:11309 +2501:11310 +2501:11311 +2501:11312 +2501:11313 +2501:11314 +2501:11315 +2501:11316 +2501:11317 +2501:11318 +2501:11319 +2501:11320 +2501:11321 +2501:11322 +2501:11323 +2501:11324 +2501:11325 +2501:11326 +2501:11327 +2501:11328 +2501:11329 +2501:11330 +2501:11331 +2501:11332 +2501:11333 +2501:11334 +2501:11335 +2501:11336 +2501:11337 +2501:11338 +2501:11339 +2501:11340 +2501:11341 +2501:11342 +2501:11343 +2501:11344 +2501:11345 +2501:11346 +2501:11347 +2501:11348 +2501:11349 +2501:11350 +2501:11351 +2501:11352 +2501:11353 +2501:11354 +2501:11355 +2501:11356 +2501:11357 +2501:11364 +2501:11365 +2501:11366 +2501:11367 +2501:11368 +2502:11370 +2502:11371 +2502:11372 +2502:11373 +2502:11375 +2502:11376 +2503:11378 +2505:11382 +2505:11384 +2505:11385 +2505:11386 +2505:11387 +2505:11467 +2505:11468 +2506:11390 +2506:11391 +2506:11392 +2506:11393 +2506:11394 +2506:11395 +2506:11396 +2506:11397 +2506:11398 +2506:11399 +2506:11400 +2506:11401 +2506:11402 +2506:11403 +2506:11404 +2506:11405 +2506:11406 +2506:11407 +2506:11408 +2506:11409 +2506:11410 +2506:11411 +2506:11412 +2506:11413 +2506:11414 +2506:11415 +2506:11416 +2506:11417 +2506:11418 +2506:11419 +2506:11420 +2506:11421 +2506:11422 +2506:11423 +2506:11424 +2506:11425 +2506:11426 +2506:11427 +2506:11428 +2506:11429 +2506:11430 +2506:11431 +2506:11432 +2506:11433 +2506:11434 +2506:11435 +2506:11436 +2506:11437 +2506:11438 +2506:11439 +2506:11440 +2506:11441 +2506:11442 +2506:11443 +2506:11444 +2506:11445 +2506:11452 +2506:11453 +2506:11454 +2506:11455 +2506:11456 +2507:11458 +2507:11459 +2507:11460 +2507:11461 +2507:11463 +2507:11464 +2508:11466 +2511:10009 +2511:10011 +2511:10012 +2511:10013 +2511:10017 +2511:10062 +2511:10063 +2512:10014 +2512:10019 +2512:10020 +2512:10021 +2512:10022 +2513:10015 +2513:10023 +2513:10024 +2513:10025 +2513:10026 +2513:10027 +2513:10028 +2513:10029 +2513:10033 +2513:10034 +2513:10035 +2513:10036 +2513:10038 +2513:10039 +2516:10030 +2516:10031 +2517:10016 +2517:10041 +2517:10042 +2517:10043 +2517:10044 +2517:10045 +2517:10047 +2517:10048 +2517:10049 +2517:10050 +2519:10053 +2519:10054 +2519:10055 +2519:10056 +2519:10058 +2519:10059 +2520:10061 +2522:11499 +2522:11501 +2522:11502 +2522:11503 +2522:11505 +2522:11520 +2522:11521 +2523:11504 +2523:11507 +2523:11508 +2523:11509 +2523:11510 +2524:11511 +2524:11512 +2524:11513 +2524:11514 +2524:11516 +2524:11517 +2525:11519 +2528:11470 +2528:11472 +2528:11473 +2528:11474 +2528:11477 +2528:11496 +2528:11497 +2529:11475 +2529:11476 +2529:11479 +2529:11480 +2529:11481 +2529:11482 +2529:11483 +2529:11484 +2529:11485 +2529:11486 +2530:11487 +2530:11488 +2530:11489 +2530:11490 +2530:11492 +2530:11493 +2531:11495 +2538:11523 +2538:11528 +2538:11529 +2538:11530 +2538:11532 +2538:11533 +2538:11534 +2538:11539 +2538:11570 +2538:11583 +2538:11584 +2539:11524 +2539:11536 +2540:11525 +2541:11526 +2542:11527 +2543:11540 +2543:11541 +2543:11542 +2543:11543 +2543:11544 +2543:11545 +2543:11546 +2543:11547 +2543:11548 +2543:11549 +2543:11550 +2543:11551 +2543:11552 +2543:11553 +2543:11555 +2543:11556 +2552:11557 +2552:11558 +2552:11564 +2552:11566 +2552:11567 +2553:11560 +2553:11569 +2554:11561 +2555:11562 +2559:11571 +2559:11572 +2559:11579 +2559:11580 +2559:11581 +2559:11582 +2560:11574 +2561:11575 +2562:11576 +2563:11577 +2568:10866 +2568:10867 +2568:10868 +2568:10870 +2568:10871 +2568:10872 +2568:10985 +2568:10986 +2569:10878 +2575:10882 +2575:10883 +2575:10884 +2575:10885 +2575:10886 +2575:10887 +2575:10889 +2575:10890 +2575:10891 +2575:10893 +2575:10894 +2575:10895 +2575:10896 +2575:10897 +2575:10898 +2575:10899 +2575:10900 +2575:10908 +2575:10910 +2575:10911 +2575:10912 +2575:10913 +2576:10873 +2576:10902 +2576:10903 +2577:10904 +2577:10905 +2584:10917 +2584:10918 +2584:10919 +2584:10968 +2584:10969 +2584:10970 +2584:10971 +2584:10973 +2584:10974 +2586:10874 +2586:10920 +2586:10921 +2586:10922 +2587:10923 +2587:10924 +2602:10951 +2602:10960 +2602:10962 +2602:10963 +2602:10964 +2602:10965 +2603:10927 +2603:10928 +2603:10929 +2603:10930 +2603:10931 +2603:10932 +2603:10934 +2603:10936 +2603:10937 +2603:10938 +2603:10939 +2603:10940 +2603:10941 +2603:10942 +2603:10943 +2605:10875 +2605:10945 +2605:10946 +2606:10947 +2606:10948 +2609:10876 +2609:10952 +2609:10954 +2609:10955 +2610:10956 +2610:10957 +2614:10976 +2614:10977 +2614:10978 +2614:10979 +2614:10981 +2614:10982 +2615:10984 +2617:11586 +2617:11588 +2617:11589 +2617:11590 +2617:11628 +2617:11633 +2617:11634 +2617:11635 +2617:11636 +2617:11638 +2617:11639 +2619:11594 +2619:11595 +2619:11596 +2619:11597 +2619:11598 +2619:11599 +2619:11601 +2619:11602 +2619:11603 +2619:11605 +2619:11606 +2619:11607 +2619:11608 +2619:11609 +2619:11610 +2619:11611 +2619:11612 +2620:11591 +2620:11592 +2620:11614 +2620:11615 +2620:11616 +2620:11617 +2620:11618 +2620:11619 +2620:11620 +2620:11621 +2620:11622 +2620:11623 +2620:11624 +2622:11629 +2622:11630 +2622:11631 +2622:11632 +2624:11627 +2668:10611 +2668:10612 +2668:10613 +2668:10615 +2668:10616 +2668:10617 +2668:10667 +2668:10672 +2668:10673 +2668:10674 +2668:10675 +2668:10677 +2668:10678 +2674:10620 +2674:10621 +2674:10622 +2674:10623 +2674:10624 +2674:10625 +2674:10627 +2674:10628 +2674:10629 +2674:10630 +2674:10631 +2674:10632 +2674:10633 +2674:10634 +2674:10635 +2674:10636 +2674:10637 +2674:10638 +2674:10639 +2674:10640 +2674:10642 +2674:10643 +2674:10644 +2674:10645 +2674:10647 +2674:10648 +2674:10649 +2674:10650 +2674:10651 +2674:10652 +2674:10653 +2674:10654 +2675:10618 +2675:10655 +2675:10656 +2675:10657 +2675:10658 +2675:10659 +2675:10660 +2675:10661 +2675:10663 +2675:10664 +2676:10666 +2676:10668 +2676:10669 +2676:10670 +2676:10671 +2679:8536 +2679:8537 +2679:8538 +2679:8540 +2679:8541 +2679:8542 +2679:8543 +2679:8607 +2679:8608 +2680:8545 +2680:8546 +2680:8547 +2680:8548 +2680:8549 +2680:8550 +2680:8551 +2680:8552 +2681:8553 +2681:8554 +2681:8555 +2681:8556 +2681:8557 +2682:8596 +2682:8600 +2682:8601 +2682:8602 +2682:8603 +2683:8559 +2683:8560 +2683:8561 +2683:8562 +2683:8563 +2683:8564 +2683:8566 +2683:8567 +2683:8568 +2683:8569 +2683:8570 +2683:8571 +2683:8572 +2683:8573 +2683:8574 +2683:8575 +2683:8576 +2683:8577 +2683:8578 +2683:8579 +2683:8581 +2683:8582 +2683:8583 +2683:8584 +2683:8586 +2683:8587 +2683:8588 +2683:8589 +2683:8590 +2683:8591 +2683:8592 +2683:8593 +2683:8594 +2683:8595 +2683:8606 +2684:8597 +2684:8598 +2684:8599 +2692:11258 +2692:11260 +2692:11261 +2692:11262 +2692:11263 +2692:11291 +2692:11292 +2693:11265 +2693:11266 +2693:11267 +2693:11268 +2693:11269 +2693:11270 +2693:11271 +2693:11272 +2693:11273 +2693:11274 +2693:11275 +2693:11276 +2693:11277 +2693:11278 +2693:11280 +2693:11281 +2694:11282 +2694:11283 +2694:11284 +2694:11285 +2694:11287 +2694:11288 +2695:11290 +2697:11842 +2697:11843 +2697:11844 +2697:11846 +2697:11847 +2697:11848 +2697:11922 +2697:11923 +2699:11853 +2699:11854 +2699:11855 +2699:11879 +2699:11905 +2699:11906 +2699:11907 +2699:11908 +2699:11909 +2699:11910 +2699:11911 +2699:11912 +2713:11856 +2713:11857 +2713:11858 +2713:11859 +2713:11860 +2713:11861 +2713:11863 +2713:11865 +2713:11866 +2713:11867 +2713:11868 +2713:11869 +2713:11870 +2713:11871 +2713:11872 +2714:11873 +2715:11849 +2715:11874 +2715:11875 +2716:11876 +2716:11877 +2719:11880 +2719:11881 +2719:11882 +2719:11883 +2719:11884 +2719:11885 +2719:11886 +2719:11888 +2719:11889 +2719:11891 +2719:11892 +2719:11893 +2719:11894 +2719:11895 +2719:11896 +2719:11897 +2719:11898 +2720:11899 +2721:11850 +2721:11900 +2721:11901 +2722:11902 +2722:11903 +2725:11913 +2725:11914 +2725:11915 +2725:11916 +2725:11918 +2725:11919 +2726:11921 +2736:12198 +2736:12206 +2736:12207 +2736:12208 +2736:12210 +2736:12211 +2736:12212 +2736:12216 +2736:12294 +2736:12295 +2737:12199 +2738:12200 +2739:12201 +2739:12213 +2739:12218 +2739:12219 +2740:12202 +2740:12220 +2740:12221 +2741:12203 +2741:12223 +2741:12224 +2741:12225 +2741:12249 +2741:12275 +2741:12276 +2741:12277 +2741:12278 +2741:12279 +2741:12280 +2741:12281 +2742:12204 +2743:12205 +2755:12226 +2755:12227 +2755:12228 +2755:12229 +2755:12230 +2755:12231 +2755:12233 +2755:12235 +2755:12236 +2755:12237 +2755:12238 +2755:12239 +2755:12240 +2755:12241 +2755:12242 +2756:12243 +2757:12214 +2757:12244 +2757:12245 +2758:12246 +2758:12247 +2761:12250 +2761:12251 +2761:12252 +2761:12253 +2761:12254 +2761:12255 +2761:12256 +2761:12258 +2761:12259 +2761:12261 +2761:12262 +2761:12263 +2761:12264 +2761:12265 +2761:12266 +2761:12267 +2761:12268 +2762:12269 +2763:12215 +2763:12270 +2763:12271 +2764:12272 +2764:12273 +2766:12282 +2766:12283 +2766:12288 +2766:12290 +2766:12291 +2767:12285 +2767:12293 +2768:12286 +2772:12005 +2772:12006 +2772:12007 +2772:12009 +2772:12010 +2772:12011 +2772:12017 +2772:12019 +2772:12157 +2772:12158 +2772:12159 +2772:12160 +2772:12161 +2772:12162 +2772:12163 +2775:12020 +2775:12021 +2775:12022 +2775:12023 +2775:12024 +2775:12025 +2775:12026 +2775:12027 +2775:12028 +2775:12029 +2775:12031 +2775:12032 +2776:12035 +2777:12037 +2777:12038 +2777:12039 +2777:12040 +2777:12058 +2777:12059 +2777:12060 +2777:12061 +2777:12062 +2779:12043 +2779:12044 +2779:12045 +2781:12048 +2781:12049 +2781:12050 +2783:12053 +2783:12054 +2783:12055 +2785:12065 +2786:12012 +2786:12067 +2786:12068 +2786:12069 +2786:12070 +2786:12072 +2786:12073 +2786:12074 +2786:12075 +2790:12013 +2790:12079 +2790:12080 +2790:12081 +2790:12082 +2790:12084 +2790:12085 +2790:12086 +2790:12087 +2791:12090 +2791:12091 +2791:12104 +2791:12106 +2791:12107 +2792:12093 +2792:12109 +2793:12094 +2794:12095 +2795:12096 +2796:12097 +2797:12098 +2798:12099 +2799:12100 +2800:12101 +2801:12102 +2805:12014 +2805:12112 +2805:12113 +2805:12114 +2805:12115 +2806:12116 +2806:12117 +2806:12118 +2806:12119 +2806:12121 +2806:12122 +2807:12124 +2809:12015 +2809:12127 +2809:12128 +2809:12129 +2809:12130 +2810:12131 +2810:12132 +2810:12133 +2810:12134 +2810:12136 +2810:12137 +2811:12139 +2815:12016 +2815:12142 +2815:12143 +2815:12144 +2815:12145 +2816:12146 +2816:12147 +2816:12148 +2816:12149 +2816:12151 +2816:12152 +2817:12154 +2819:11759 +2819:11761 +2819:11762 +2819:11763 +2819:11765 +2819:11834 +2819:11835 +2819:11836 +2819:11837 +2819:11838 +2819:11839 +2819:11840 +2820:11766 +2820:11767 +2820:11768 +2820:11769 +2820:11770 +2820:11771 +2820:11773 +2820:11775 +2820:11776 +2820:11777 +2820:11778 +2820:11779 +2820:11780 +2820:11804 +2820:11805 +2820:11806 +2820:11807 +2820:11808 +2821:11783 +2821:11784 +2821:11785 +2821:11786 +2821:11787 +2822:11790 +2822:11791 +2822:11792 +2822:11793 +2822:11794 +2823:11797 +2823:11798 +2823:11799 +2823:11800 +2823:11801 +2824:11813 +2824:11814 +2824:11815 +2824:11816 +2824:11817 +2825:11820 +2825:11821 +2825:11822 +2825:11823 +2825:11824 +2826:11827 +2826:11828 +2826:11829 +2826:11830 +2826:11831 +2832:12297 +2832:12300 +2832:12302 +2832:12303 +2832:12304 +2832:12309 +2832:12500 +2832:12501 +2833:12298 +2834:12299 +2834:12305 +2834:12306 +2834:12312 +2834:12313 +2834:12314 +2834:12315 +2834:12316 +2834:12317 +2834:12318 +2834:12319 +2834:12320 +2834:12390 +2834:12391 +2834:12392 +2834:12393 +2834:12394 +2837:12397 +2837:12398 +2837:12399 +2837:12400 +2837:12401 +2837:12402 +2837:12403 +2837:12493 +2837:12494 +2837:12495 +2837:12496 +2837:12497 +2838:12307 +2838:12308 +2838:12406 +2838:12407 +2838:12408 +2838:12409 +2838:12410 +2838:12411 +2838:12412 +2838:12413 +2838:12499 +2840:12417 +2840:12418 +2840:12419 +2840:12420 +2840:12421 +2842:12424 +2842:12425 +2842:12426 +2842:12427 +2848:12430 +2848:12431 +2848:12432 +2848:12433 +2848:12434 +2848:12435 +2848:12436 +2848:12437 +2848:12438 +2848:12439 +2848:12440 +2848:12441 +2848:12442 +2848:12443 +2848:12444 +2848:12445 +2848:12446 +2848:12447 +2848:12448 +2848:12449 +2848:12450 +2848:12451 +2848:12452 +2848:12453 +2848:12454 +2848:12455 +2848:12456 +2848:12457 +2848:12458 +2848:12459 +2848:12460 +2848:12461 +2848:12462 +2848:12463 +2848:12464 +2848:12465 +2848:12466 +2848:12467 +2848:12468 +2848:12469 +2848:12470 +2848:12471 +2848:12472 +2848:12473 +2848:12474 +2848:12475 +2848:12476 +2848:12477 +2848:12478 +2848:12479 +2848:12480 +2848:12481 +2848:12482 +2848:12483 +2848:12484 +2848:12485 +2848:12486 +2848:12487 +2848:12488 +2848:12489 +2848:12490 +2858:4882 +2858:4884 +2858:4885 +2858:4886 +2858:5228 +2858:5229 +2859:4889 +2859:5217 +2859:5218 +2859:5219 +2859:5220 +2859:5221 +2860:4890 +2860:4891 +2860:4892 +2860:4893 +2860:4894 +2860:4895 +2861:4898 +2861:4899 +2861:4900 +2861:4901 +2861:4902 +2861:4903 +2862:4906 +2862:4907 +2862:4908 +2862:4909 +2862:4910 +2862:4911 +2863:4914 +2863:4915 +2863:4916 +2863:4917 +2863:4918 +2863:4919 +2864:4922 +2864:4923 +2864:4924 +2864:4925 +2864:4926 +2864:4927 +2865:4930 +2865:4931 +2865:4932 +2865:4933 +2865:4934 +2865:4935 +2866:4938 +2866:4939 +2866:4940 +2866:4941 +2866:4942 +2866:4943 +2867:4946 +2867:4947 +2867:4948 +2867:4949 +2867:4950 +2867:4951 +2868:4954 +2868:4955 +2868:4956 +2868:4957 +2868:4958 +2868:4959 +2869:4962 +2869:4963 +2869:4964 +2869:4965 +2869:4966 +2869:4967 +2870:4970 +2870:4971 +2870:4972 +2870:4973 +2870:4974 +2870:4975 +2871:4978 +2871:4979 +2871:4980 +2871:4981 +2871:4982 +2871:4983 +2872:4986 +2872:4987 +2872:4988 +2872:4989 +2872:4990 +2872:4991 +2873:4994 +2873:4995 +2873:4996 +2873:4997 +2873:4998 +2873:4999 +2874:5002 +2874:5003 +2874:5004 +2874:5005 +2874:5006 +2874:5007 +2875:5010 +2875:5011 +2875:5012 +2875:5013 +2875:5014 +2875:5015 +2876:5018 +2876:5019 +2876:5020 +2876:5021 +2876:5022 +2876:5023 +2877:5026 +2877:5027 +2877:5028 +2877:5029 +2877:5030 +2877:5031 +2878:5034 +2878:5035 +2878:5036 +2878:5037 +2878:5038 +2878:5039 +2879:5042 +2879:5043 +2879:5044 +2879:5045 +2879:5046 +2879:5047 +2880:5050 +2880:5051 +2880:5052 +2880:5053 +2880:5054 +2880:5055 +2881:5058 +2881:5059 +2881:5060 +2881:5061 +2881:5062 +2881:5063 +2882:5066 +2882:5067 +2882:5068 +2882:5069 +2882:5070 +2882:5071 +2883:5074 +2883:5075 +2883:5076 +2883:5077 +2883:5078 +2883:5079 +2884:5082 +2884:5083 +2884:5084 +2884:5085 +2884:5086 +2884:5087 +2885:5090 +2885:5091 +2885:5092 +2885:5093 +2885:5094 +2885:5095 +2886:5098 +2886:5099 +2886:5100 +2886:5101 +2886:5102 +2886:5103 +2887:5106 +2887:5107 +2887:5108 +2887:5109 +2887:5110 +2887:5111 +2888:5114 +2888:5115 +2888:5116 +2888:5117 +2888:5118 +2888:5119 +2889:5122 +2889:5123 +2889:5124 +2889:5125 +2889:5126 +2889:5127 +2890:5130 +2890:5131 +2890:5132 +2890:5133 +2890:5134 +2890:5135 +2891:5138 +2891:5139 +2891:5140 +2891:5141 +2891:5142 +2891:5143 +2892:5146 +2892:5147 +2892:5148 +2892:5149 +2892:5150 +2892:5151 +2893:5154 +2893:5155 +2893:5156 +2893:5157 +2893:5158 +2893:5159 +2894:5162 +2894:5163 +2894:5164 +2894:5165 +2894:5166 +2894:5167 +2895:5170 +2895:5171 +2895:5172 +2895:5173 +2895:5174 +2895:5175 +2896:5178 +2896:5179 +2896:5180 +2896:5181 +2896:5182 +2896:5183 +2897:5186 +2897:5187 +2897:5188 +2897:5189 +2897:5190 +2897:5191 +2897:5192 +2897:5193 +2897:5194 +2897:5195 +2897:5196 +2897:5197 +2897:5198 +2897:5199 +2897:5200 +2898:5203 +2898:5204 +2898:5205 +2898:5206 +2898:5207 +2898:5208 +2898:5209 +2898:5210 +2898:5211 +2898:5212 +2898:5213 +2898:5214 +2900:5223 +2900:5224 +2900:5225 +2903:5227 +2906:12623 +2906:12626 +2906:12628 +2906:12629 +2906:12630 +2906:12632 +2906:12671 +2906:12672 +2907:12624 +2907:12631 +2907:12635 +2907:12636 +2907:12637 +2907:12638 +2907:12639 +2907:12656 +2907:12657 +2907:12658 +2907:12659 +2907:12660 +2908:12625 +2908:12640 +2908:12641 +2908:12642 +2908:12643 +2908:12645 +2908:12646 +2909:12650 +2909:12651 +2909:12652 +2909:12653 +2911:12662 +2911:12663 +2911:12664 +2911:12665 +2911:12667 +2911:12668 +2912:12670 +2940:12503 +2940:12525 +2940:12526 +2940:12527 +2940:12529 +2940:12530 +2940:12531 +2940:12534 +2940:12555 +2940:12577 +2940:12578 +2941:12504 +2942:12505 +2942:12535 +2943:12506 +2943:12532 +2943:12536 +2943:12537 +2944:12507 +2944:12538 +2945:12508 +2945:12539 +2945:12540 +2945:12541 +2945:12549 +2945:12551 +2945:12552 +2946:12509 +2946:12542 +2946:12554 +2947:12510 +2947:12543 +2948:12511 +2948:12544 +2949:12512 +2949:12545 +2950:12513 +2950:12546 +2951:12514 +2951:12547 +2952:12515 +2953:12516 +2954:12517 +2955:12518 +2955:12556 +2955:12557 +2955:12573 +2955:12574 +2955:12575 +2955:12576 +2956:12519 +2956:12559 +2957:12520 +2957:12560 +2958:12521 +2958:12561 +2959:12522 +2959:12562 +2960:12523 +2960:12563 +2961:12524 +2961:12564 +2962:12565 +2963:12566 +2964:12567 +2965:12568 +2966:12569 +2967:12570 +2968:12571 +2976:12165 +2976:12168 +2976:12169 +2976:12170 +2976:12172 +2976:12173 +2976:12174 +2976:12195 +2976:12196 +2977:12166 +2978:12167 +2982:12175 +2982:12177 +2982:12178 +2983:12179 +2983:12180 +2983:12189 +2983:12191 +2983:12192 +2984:12182 +2984:12194 +2985:12183 +2986:12184 +2987:12185 +2988:12186 +2989:12187 +3000:12580 +3000:12586 +3000:12587 +3000:12588 +3000:12590 +3000:12591 +3000:12592 +3000:12620 +3000:12621 +3001:12581 +3002:12582 +3002:12593 +3002:12594 +3002:12597 +3002:12598 +3002:12599 +3002:12600 +3003:12583 +3003:12595 +3003:12601 +3003:12602 +3004:12584 +3004:12603 +3005:12585 +3005:12604 +3005:12605 +3005:12606 +3005:12614 +3005:12616 +3005:12617 +3006:12607 +3006:12619 +3007:12608 +3008:12609 +3009:12610 +3010:12611 +3011:12612 +3016:8610 +3016:8611 +3016:8612 +3016:8614 +3016:8615 +3016:8616 +3016:8848 +3016:8849 +3017:8618 +3017:8619 +3018:8621 +3018:8841 +3018:8842 +3018:8843 +3018:8844 +3018:8845 +3019:8622 +3019:8623 +3019:8624 +3019:8625 +3019:8626 +3019:8627 +3019:8628 +3019:8629 +3019:8630 +3019:8631 +3019:8632 +3019:8633 +3019:8634 +3019:8635 +3019:8637 +3019:8638 +3019:8639 +3019:8640 +3019:8641 +3019:8642 +3019:8643 +3019:8742 +3019:8743 +3019:8744 +3019:8745 +3019:8746 +3019:8847 +3020:8646 +3020:8647 +3020:8648 +3020:8649 +3020:8650 +3020:8651 +3021:8654 +3021:8655 +3021:8656 +3021:8657 +3021:8658 +3021:8659 +3022:8662 +3022:8663 +3022:8664 +3022:8665 +3022:8666 +3022:8667 +3023:8670 +3023:8671 +3023:8672 +3023:8673 +3023:8674 +3023:8675 +3024:8678 +3024:8679 +3024:8680 +3024:8681 +3024:8682 +3024:8683 +3025:8686 +3025:8687 +3025:8688 +3025:8689 +3025:8690 +3025:8691 +3026:8694 +3026:8695 +3026:8696 +3026:8697 +3026:8698 +3026:8699 +3027:8702 +3027:8703 +3027:8704 +3027:8705 +3027:8706 +3027:8707 +3028:8710 +3028:8711 +3028:8712 +3028:8713 +3028:8714 +3028:8715 +3029:8718 +3029:8719 +3029:8720 +3029:8721 +3029:8722 +3029:8723 +3030:8726 +3030:8727 +3030:8728 +3030:8729 +3030:8730 +3030:8731 +3031:8734 +3031:8735 +3031:8736 +3031:8737 +3031:8738 +3031:8739 +3034:8748 +3035:8749 +3035:8750 +3042:5231 +3042:5232 +3042:5233 +3042:5235 +3042:5236 +3042:5237 +3042:5326 +3042:5327 +3043:5239 +3043:5240 +3044:5268 +3044:5317 +3044:5319 +3044:5320 +3044:5321 +3044:5322 +3044:5323 +3045:5242 +3045:5243 +3045:5244 +3045:5245 +3045:5246 +3045:5247 +3045:5249 +3045:5250 +3045:5252 +3045:5253 +3045:5254 +3045:5255 +3045:5256 +3045:5257 +3045:5258 +3045:5259 +3045:5260 +3045:5261 +3045:5262 +3045:5263 +3045:5264 +3045:5265 +3045:5266 +3045:5267 +3045:5325 +3046:5269 +3046:5270 +3046:5271 +3046:5272 +3046:5273 +3046:5274 +3046:5275 +3046:5277 +3046:5278 +3046:5280 +3046:5281 +3046:5282 +3046:5283 +3046:5284 +3046:5285 +3046:5286 +3046:5287 +3046:5288 +3046:5289 +3046:5290 +3046:5291 +3046:5292 +3046:5293 +3046:5294 +3046:5295 +3046:5296 +3046:5297 +3046:5298 +3046:5299 +3046:5300 +3046:5301 +3049:5303 +3049:5304 +3049:5313 +3049:5314 +3049:5315 +3049:5316 +3050:5306 +3051:5307 +3052:5308 +3053:5309 +3054:5310 +3055:5311 +3063:12877 +3063:12878 +3063:12879 +3063:12881 +3063:12882 +3063:12883 +3063:12906 +3063:12907 +3064:12885 +3064:12886 +3064:12887 +3064:12888 +3064:12889 +3064:12890 +3064:12891 +3064:12892 +3064:12893 +3064:12894 +3064:12895 +3064:12896 +3064:12897 +3064:12898 +3064:12899 +3064:12900 +3064:12901 +3064:12903 +3064:12904 +3064:12905 +3069:12909 +3069:12910 +3069:12911 +3069:12913 +3069:12914 +3069:12915 +3069:12938 +3069:12939 +3070:12917 +3070:12918 +3070:12919 +3070:12920 +3070:12921 +3070:12922 +3070:12923 +3070:12924 +3070:12925 +3070:12926 +3070:12927 +3070:12928 +3070:12929 +3070:12930 +3070:12931 +3070:12932 +3070:12933 +3070:12935 +3070:12936 +3070:12937 +3074:12941 +3074:12942 +3074:12943 +3074:12945 +3074:12946 +3074:12947 +3074:12970 +3074:12971 +3075:12949 +3075:12950 +3075:12951 +3075:12952 +3075:12953 +3075:12954 +3075:12955 +3075:12956 +3075:12957 +3075:12958 +3075:12959 +3075:12960 +3075:12961 +3075:12962 +3075:12963 +3075:12964 +3075:12965 +3075:12967 +3075:12968 +3075:12969 +3080:12973 +3080:12974 +3080:12975 +3080:12977 +3080:12978 +3080:12979 +3080:13002 +3080:13003 +3081:12981 +3081:12982 +3081:12983 +3081:12984 +3081:12985 +3081:12986 +3081:12987 +3081:12988 +3081:12989 +3081:12990 +3081:12991 +3081:12992 +3081:12993 +3081:12994 +3081:12995 +3081:12996 +3081:12997 +3081:12999 +3081:13000 +3081:13001 +3086:13005 +3086:13006 +3086:13007 +3086:13009 +3086:13010 +3086:13011 +3086:13034 +3086:13035 +3087:13013 +3087:13014 +3087:13015 +3087:13016 +3087:13017 +3087:13018 +3087:13019 +3087:13020 +3087:13021 +3087:13022 +3087:13023 +3087:13024 +3087:13025 +3087:13026 +3087:13027 +3087:13028 +3087:13029 +3087:13031 +3087:13032 +3087:13033 +3093:13037 +3093:13038 +3093:13039 +3093:13041 +3093:13042 +3093:13043 +3093:13086 +3093:13087 +3094:13045 +3094:13046 +3094:13047 +3094:13048 +3094:13049 +3094:13050 +3094:13052 +3094:13053 +3094:13054 +3094:13055 +3094:13056 +3094:13057 +3094:13058 +3094:13061 +3094:13062 +3094:13063 +3094:13064 +3094:13065 +3094:13066 +3094:13069 +3094:13070 +3094:13071 +3094:13072 +3094:13073 +3094:13075 +3094:13076 +3094:13077 +3094:13078 +3094:13079 +3094:13080 +3094:13081 +3094:13083 +3094:13084 +3094:13085 +3098:13089 +3098:13090 +3098:13091 +3098:13093 +3098:13094 +3098:13095 +3098:13162 +3098:13163 +3099:13097 +3099:13098 +3099:13099 +3099:13100 +3099:13101 +3099:13102 +3099:13104 +3099:13105 +3099:13106 +3099:13107 +3099:13108 +3099:13109 +3099:13110 +3099:13113 +3099:13114 +3099:13115 +3099:13116 +3099:13117 +3099:13118 +3099:13121 +3099:13122 +3099:13123 +3099:13124 +3099:13125 +3099:13126 +3099:13129 +3099:13130 +3099:13131 +3099:13132 +3099:13133 +3099:13134 +3099:13137 +3099:13138 +3099:13139 +3099:13140 +3099:13141 +3099:13142 +3099:13145 +3099:13146 +3099:13147 +3099:13148 +3099:13149 +3099:13151 +3099:13152 +3099:13153 +3099:13154 +3099:13155 +3099:13156 +3099:13157 +3099:13159 +3099:13160 +3099:13161 +3103:13165 +3103:13166 +3103:13167 +3103:13169 +3103:13170 +3103:13171 +3103:13257 +3103:13258 +3104:13173 +3104:13174 +3105:13176 +3105:13230 +3105:13239 +3105:13248 +3105:13250 +3105:13251 +3105:13252 +3105:13253 +3106:13179 +3106:13180 +3106:13181 +3106:13182 +3106:13183 +3106:13184 +3106:13185 +3106:13220 +3106:13221 +3106:13222 +3106:13223 +3106:13224 +3106:13256 +3107:13188 +3107:13189 +3107:13190 +3107:13191 +3107:13192 +3107:13193 +3108:13196 +3108:13197 +3108:13198 +3108:13199 +3108:13200 +3108:13201 +3109:13204 +3109:13205 +3109:13206 +3109:13207 +3109:13208 +3109:13209 +3110:13212 +3110:13213 +3110:13214 +3110:13215 +3110:13216 +3110:13217 +3112:13226 +3113:13227 +3113:13228 +3116:13231 +3116:13232 +3116:13233 +3116:13234 +3116:13235 +3116:13236 +3116:13237 +3116:13238 +3117:13240 +3117:13241 +3117:13242 +3117:13243 +3117:13244 +3117:13245 +3117:13246 +3117:13247 +3124:10521 +3124:10522 +3124:10523 +3124:10525 +3124:10526 +3124:10527 +3124:10608 +3124:10609 +3125:10529 +3125:10530 +3126:10531 +3126:10532 +3126:10533 +3126:10600 +3126:10601 +3126:10602 +3126:10603 +3126:10605 +3126:10606 +3126:10607 +3127:10534 +3127:10536 +3127:10537 +3127:10538 +3127:10539 +3127:10540 +3127:10542 +3127:10543 +3127:10544 +3127:10545 +3127:10546 +3127:10547 +3127:10548 +3127:10551 +3127:10552 +3127:10553 +3127:10554 +3127:10555 +3127:10556 +3127:10559 +3127:10560 +3127:10561 +3127:10562 +3127:10563 +3127:10564 +3127:10567 +3127:10568 +3127:10569 +3127:10570 +3127:10571 +3127:10591 +3127:10592 +3127:10593 +3127:10594 +3127:10595 +3128:10575 +3128:10576 +3128:10577 +3128:10578 +3128:10579 +3128:10580 +3129:10583 +3129:10584 +3129:10585 +3129:10586 +3129:10587 +3129:10588 +3131:10597 +3132:10598 +3132:10599 +3138:13260 +3138:13261 +3138:13262 +3138:13264 +3138:13265 +3138:13266 +3138:13309 +3138:13310 +3139:13268 +3139:13269 +3140:13270 +3140:13271 +3140:13272 +3140:13301 +3140:13302 +3140:13303 +3140:13304 +3140:13306 +3140:13307 +3140:13308 +3143:13273 +3143:13275 +3143:13276 +3143:13277 +3143:13278 +3143:13279 +3143:13280 +3143:13281 +3143:13284 +3143:13285 +3143:13286 +3143:13287 +3143:13288 +3143:13289 +3143:13292 +3143:13293 +3143:13294 +3143:13295 +3143:13296 +3143:13298 +3144:13299 +3144:13300 +3150:13312 +3150:13313 +3150:13314 +3150:13316 +3150:13317 +3150:13318 +3150:13320 +3150:13650 +3150:13651 +3150:13652 +3150:13653 +3150:13654 +3150:13655 +3150:13656 +3151:13321 +3151:13322 +3151:13324 +3151:13325 +3151:13326 +3151:13327 +3151:13328 +3151:13329 +3151:13330 +3151:13331 +3151:13332 +3151:13333 +3151:13334 +3151:13336 +3151:13337 +3151:13338 +3151:13339 +3151:13340 +3151:13341 +3151:13342 +3151:13345 +3151:13346 +3151:13347 +3151:13348 +3151:13349 +3151:13350 +3151:13353 +3151:13354 +3151:13355 +3151:13356 +3151:13357 +3151:13358 +3151:13361 +3151:13362 +3151:13363 +3151:13364 +3151:13365 +3151:13367 +3151:13368 +3151:13369 +3151:13370 +3151:13371 +3151:13372 +3151:13373 +3151:13375 +3151:13376 +3152:13378 +3152:13381 +3152:13382 +3152:13384 +3152:13385 +3152:13386 +3152:13387 +3152:13388 +3152:13389 +3152:13390 +3152:13391 +3152:13392 +3152:13393 +3152:13394 +3152:13396 +3152:13397 +3152:13398 +3152:13399 +3152:13400 +3152:13401 +3152:13402 +3152:13405 +3152:13406 +3152:13407 +3152:13408 +3152:13409 +3152:13410 +3152:13413 +3152:13414 +3152:13415 +3152:13416 +3152:13417 +3152:13418 +3152:13421 +3152:13422 +3152:13423 +3152:13424 +3152:13425 +3152:13427 +3152:13428 +3152:13429 +3152:13430 +3152:13431 +3152:13432 +3152:13433 +3152:13435 +3152:13436 +3153:13438 +3153:13441 +3153:13442 +3153:13444 +3153:13445 +3153:13446 +3153:13447 +3153:13448 +3153:13449 +3153:13451 +3153:13452 +3153:13453 +3153:13454 +3153:13455 +3153:13456 +3153:13457 +3153:13459 +3153:13460 +3153:13461 +3153:13462 +3153:13463 +3153:13464 +3153:13465 +3153:13468 +3153:13469 +3153:13470 +3153:13471 +3153:13472 +3153:13473 +3153:13476 +3153:13477 +3153:13478 +3153:13479 +3153:13480 +3153:13481 +3153:13484 +3153:13485 +3153:13486 +3153:13487 +3153:13488 +3153:13490 +3153:13491 +3153:13492 +3153:13493 +3153:13494 +3153:13495 +3153:13496 +3153:13498 +3153:13499 +3154:13501 +3154:13504 +3154:13505 +3154:13507 +3154:13508 +3154:13509 +3154:13510 +3154:13511 +3154:13512 +3154:13514 +3154:13515 +3154:13516 +3154:13517 +3154:13518 +3154:13519 +3154:13520 +3154:13522 +3154:13523 +3154:13524 +3154:13525 +3154:13526 +3154:13527 +3154:13528 +3154:13531 +3154:13532 +3154:13533 +3154:13534 +3154:13535 +3154:13536 +3154:13539 +3154:13540 +3154:13541 +3154:13542 +3154:13543 +3154:13544 +3154:13547 +3154:13548 +3154:13549 +3154:13550 +3154:13551 +3154:13553 +3154:13554 +3154:13555 +3154:13556 +3154:13557 +3154:13558 +3154:13559 +3154:13561 +3154:13562 +3155:13564 +3155:13567 +3155:13568 +3155:13569 +3155:13570 +3155:13571 +3155:13572 +3155:13573 +3155:13574 +3155:13575 +3155:13576 +3155:13577 +3155:13578 +3155:13579 +3155:13580 +3155:13581 +3155:13582 +3155:13583 +3155:13584 +3155:13585 +3155:13586 +3155:13587 +3155:13588 +3155:13589 +3155:13590 +3155:13591 +3155:13592 +3155:13593 +3155:13594 +3155:13595 +3155:13596 +3155:13598 +3155:13599 +3155:13600 +3155:13601 +3155:13602 +3155:13603 +3155:13605 +3155:13606 +3155:13607 +3155:13608 +3155:13609 +3155:13610 +3155:13611 +3155:13614 +3155:13615 +3155:13616 +3155:13617 +3155:13618 +3155:13619 +3155:13622 +3155:13623 +3155:13624 +3155:13625 +3155:13626 +3155:13627 +3155:13630 +3155:13631 +3155:13632 +3155:13633 +3155:13634 +3155:13636 +3155:13637 +3155:13638 +3155:13639 +3155:13640 +3155:13641 +3155:13642 +3155:13644 +3155:13645 +3156:13647 +3159:13722 +3159:13723 +3159:13724 +3159:13726 +3159:13727 +3159:13728 +3159:13730 +3159:13820 +3159:13821 +3159:13822 +3159:13823 +3159:13824 +3159:13825 +3159:13826 +3160:13731 +3160:13732 +3160:13733 +3160:13734 +3160:13735 +3160:13736 +3160:13737 +3160:13738 +3160:13739 +3160:13740 +3161:13743 +3161:13744 +3161:13745 +3161:13746 +3161:13747 +3161:13748 +3161:13749 +3161:13750 +3161:13751 +3161:13752 +3162:13755 +3162:13756 +3162:13757 +3162:13758 +3162:13759 +3162:13760 +3162:13761 +3162:13762 +3162:13764 +3162:13765 +3162:13766 +3162:13767 +3163:13770 +3163:13771 +3163:13772 +3163:13773 +3163:13774 +3163:13775 +3163:13776 +3163:13777 +3163:13779 +3163:13780 +3163:13781 +3163:13782 +3164:13785 +3164:13786 +3164:13787 +3164:13788 +3164:13789 +3164:13790 +3164:13791 +3164:13792 +3164:13793 +3164:13794 +3164:13795 +3164:13796 +3164:13797 +3164:13798 +3164:13799 +3164:13800 +3164:13801 +3164:13802 +3164:13803 +3164:13804 +3164:13805 +3164:13806 +3164:13807 +3164:13808 +3164:13809 +3164:13810 +3164:13811 +3164:13812 +3164:13813 +3164:13814 +3164:13815 +3164:13816 +3164:13817 +3168:13690 +3168:13691 +3168:13692 +3168:13694 +3168:13695 +3168:13696 +3168:13719 +3168:13720 +3169:13698 +3169:13699 +3169:13700 +3169:13701 +3169:13702 +3169:13703 +3169:13704 +3169:13705 +3169:13706 +3169:13707 +3169:13708 +3169:13709 +3169:13710 +3169:13711 +3169:13712 +3169:13713 +3169:13714 +3169:13716 +3169:13717 +3169:13718 +3176:13658 +3176:13659 +3176:13660 +3176:13662 +3176:13663 +3176:13664 +3176:13687 +3176:13688 +3177:13666 +3177:13667 +3177:13668 +3177:13669 +3177:13670 +3177:13671 +3177:13672 +3177:13673 +3177:13674 +3177:13675 +3177:13676 +3177:13677 +3177:13678 +3177:13679 +3177:13680 +3177:13681 +3177:13682 +3177:13684 +3177:13685 +3177:13686 +3181:13828 +3181:13829 +3181:13830 +3181:13832 +3181:13833 +3181:13834 +3181:13840 +3181:13973 +3181:13974 +3181:13975 +3181:13976 +3181:13977 +3181:13978 +3181:13979 +3182:13841 +3182:13842 +3182:13843 +3182:13844 +3182:13845 +3182:13846 +3182:13847 +3182:13848 +3182:13849 +3182:13850 +3183:13853 +3183:13854 +3183:13855 +3183:13856 +3183:13857 +3183:13858 +3183:13859 +3183:13860 +3183:13861 +3183:13862 +3184:13865 +3184:13866 +3184:13867 +3184:13868 +3184:13869 +3184:13870 +3184:13871 +3184:13872 +3184:13873 +3184:13874 +3184:13875 +3184:13876 +3184:13877 +3184:13878 +3184:13879 +3184:13880 +3184:13881 +3184:13882 +3184:13883 +3184:13884 +3184:13885 +3184:13886 +3184:13887 +3184:13888 +3184:13889 +3184:13890 +3184:13891 +3184:13892 +3184:13926 +3184:13960 +3184:13964 +3184:13965 +3184:13966 +3184:13967 +3184:13968 +3185:13970 +3193:13894 +3193:13895 +3193:13896 +3193:13897 +3193:13898 +3193:13899 +3193:13901 +3193:13902 +3193:13903 +3193:13904 +3193:13906 +3193:13907 +3193:13908 +3193:13909 +3193:13910 +3193:13911 +3193:13912 +3193:13913 +3194:13835 +3194:13836 +3194:13914 +3194:13915 +3194:13916 +3194:13917 +3194:13918 +3194:13919 +3194:13921 +3194:13922 +3194:13923 +3195:13924 +3195:13925 +3202:13927 +3202:13928 +3202:13929 +3202:13930 +3202:13931 +3202:13932 +3202:13933 +3202:13935 +3202:13936 +3202:13937 +3202:13938 +3202:13940 +3202:13941 +3202:13942 +3202:13943 +3202:13944 +3202:13945 +3202:13946 +3202:13947 +3203:13837 +3203:13838 +3203:13948 +3203:13949 +3203:13950 +3203:13951 +3203:13952 +3203:13953 +3203:13955 +3203:13956 +3203:13957 +3204:13958 +3204:13959 +3206:13961 +3206:13962 +3206:13963 +3211:13981 +3211:13982 +3211:13983 +3211:13985 +3211:13986 +3211:13987 +3211:14022 +3211:14023 +3213:13993 +3213:13994 +3214:14004 +3214:14013 +3214:14015 +3214:14016 +3214:14017 +3214:14018 +3215:14021 +3218:13988 +3218:13989 +3218:13996 +3218:13997 +3218:13998 +3218:13999 +3218:14000 +3218:14001 +3218:14002 +3219:13990 +3219:13991 +3219:14005 +3219:14006 +3219:14007 +3219:14008 +3219:14009 +3219:14010 +3219:14011 +3226:11641 +3226:11643 +3226:11644 +3226:11645 +3226:11647 +3226:11649 +3226:11751 +3226:11752 +3226:11753 +3226:11754 +3226:11755 +3226:11756 +3226:11757 +3227:11650 +3227:11651 +3227:11652 +3227:11653 +3227:11654 +3227:11655 +3228:11659 +3228:11660 +3228:11661 +3228:11662 +3228:11663 +3228:11664 +3228:11665 +3228:11666 +3228:11667 +3228:11668 +3228:11669 +3228:11670 +3228:11671 +3228:11672 +3229:11656 +3229:11675 +3229:11676 +3229:11677 +3229:11678 +3229:11679 +3230:11682 +3230:11683 +3230:11684 +3230:11685 +3230:11686 +3230:11687 +3230:11688 +3231:11691 +3231:11692 +3231:11693 +3231:11694 +3231:11695 +3231:11696 +3231:11697 +3232:11646 +3232:11700 +3232:11701 +3232:11702 +3232:11703 +3233:11704 +3233:11705 +3233:11706 +3233:11707 +3233:11709 +3233:11710 +3234:11712 +3234:11715 +3234:11716 +3234:11717 +3234:11718 +3234:11719 +3235:11722 +3235:11723 +3235:11724 +3235:11725 +3235:11726 +3236:11729 +3236:11730 +3236:11731 +3236:11732 +3236:11733 +3237:11736 +3237:11737 +3237:11738 +3237:11739 +3237:11740 +3237:11741 +3237:11742 +3237:11743 +3237:11744 +3237:11745 +3237:11746 +3237:11747 +3237:11748 +3243:12674 +3243:12676 +3243:12677 +3243:12678 +3243:12679 +3243:12686 +3243:12801 +3243:12813 +3243:12814 +3244:12680 +3245:12681 +3246:12682 +3247:12683 +3250:12687 +3251:12688 +3251:12689 +3252:12691 +3252:12692 +3253:12693 +3253:12694 +3253:12695 +3253:12696 +3253:12697 +3253:12774 +3253:12775 +3253:12776 +3253:12777 +3253:12779 +3253:12780 +3254:12698 +3254:12699 +3255:12702 +3255:12703 +3255:12704 +3255:12705 +3255:12706 +3255:12707 +3255:12708 +3255:12709 +3255:12710 +3255:12711 +3255:12712 +3255:12713 +3255:12714 +3255:12715 +3255:12716 +3255:12717 +3255:12718 +3255:12719 +3255:12720 +3255:12721 +3255:12722 +3255:12723 +3255:12724 +3255:12725 +3255:12726 +3255:12727 +3255:12728 +3255:12729 +3255:12730 +3255:12731 +3255:12732 +3255:12733 +3255:12734 +3255:12735 +3255:12736 +3255:12737 +3255:12738 +3255:12739 +3255:12740 +3255:12741 +3255:12742 +3255:12743 +3255:12744 +3255:12745 +3255:12746 +3255:12747 +3255:12748 +3255:12749 +3255:12750 +3255:12751 +3255:12752 +3255:12753 +3255:12754 +3255:12755 +3255:12756 +3255:12757 +3255:12760 +3255:12761 +3255:12762 +3255:12763 +3255:12764 +3255:12768 +3255:12769 +3255:12770 +3255:12771 +3255:12772 +3263:12781 +3264:12783 +3265:12784 +3267:12787 +3268:12788 +3268:12789 +3268:12795 +3268:12797 +3268:12798 +3269:12791 +3269:12800 +3270:12792 +3271:12793 +3275:12802 +3275:12803 +3275:12809 +3275:12810 +3275:12811 +3275:12812 +3276:12805 +3277:12806 +3278:12807 +3286:9904 +3286:9907 +3286:9908 +3286:9909 +3286:9910 +3286:9912 +3286:9913 +3286:9914 +3286:9915 +3286:9969 +3286:9970 +3286:9971 +3287:9905 +3288:9906 +3290:9918 +3290:9919 +3290:9920 +3290:9921 +3290:9922 +3290:9923 +3290:9925 +3290:9926 +3290:9927 +3290:9929 +3290:9930 +3290:9931 +3290:9932 +3290:9933 +3290:9934 +3290:9935 +3290:9936 +3290:9942 +3290:9946 +3290:9950 +3290:9954 +3290:9955 +3290:9956 +3290:9957 +3291:9937 +3291:9938 +3291:9940 +3291:9941 +3292:9943 +3292:9944 +3292:9945 +3293:9947 +3293:9948 +3293:9949 +3294:9951 +3294:9952 +3294:9953 +3297:9960 +3297:9961 +3297:9962 +3297:9963 +3297:9965 +3297:9966 +3298:9968 +3300:9554 +3300:9556 +3300:9557 +3300:9558 +3300:9559 +3300:9574 +3300:9575 +3302:9562 +3302:9563 +3305:9565 +3305:9566 +3305:9567 +3305:9568 +3305:9570 +3305:9571 +3306:9573 +3309:14296 +3309:14298 +3309:14299 +3309:14300 +3309:14305 +3309:14306 +3310:14302 +3310:14303 +3310:14304 +3314:12816 +3314:12818 +3314:12819 +3314:12820 +3314:12827 +3314:12874 +3314:12875 +3315:12821 +3315:12822 +3315:12829 +3315:12830 +3315:12831 +3315:12832 +3316:12833 +3316:12834 +3318:12836 +3318:12837 +3320:12823 +3320:12824 +3320:12838 +3320:12839 +3320:12840 +3320:12841 +3320:12842 +3320:12843 +3320:12844 +3320:12845 +3320:12851 +3320:12852 +3320:12853 +3320:12854 +3320:12856 +3320:12857 +3321:12846 +3321:12847 +3322:12849 +3322:12850 +3325:12825 +3325:12826 +3325:12858 +3325:12859 +3325:12860 +3325:12861 +3326:12862 +3326:12863 +3326:12864 +3326:12868 +3326:12870 +3326:12871 +3327:12865 +3327:12873 +3328:12866 +3332:14380 +3332:14382 +3332:14383 +3332:14384 +3332:14423 +3332:14424 +3334:14387 +3334:14388 +3334:14389 +3334:14390 +3334:14391 +3334:14416 +3334:14417 +3334:14418 +3334:14419 +3334:14420 +3335:14394 +3335:14395 +3335:14396 +3335:14397 +3335:14398 +3335:14399 +3335:14402 +3335:14403 +3335:14404 +3335:14405 +3335:14406 +3335:14407 +3335:14422 +3336:14410 +3336:14411 +3336:14412 +3336:14413 +3360:14308 +3360:14326 +3360:14328 +3360:14329 +3360:14330 +3360:14336 +3360:14360 +3360:14372 +3360:14373 +3360:14374 +3360:14375 +3360:14377 +3360:14378 +3361:14309 +3362:14310 +3362:14331 +3362:14332 +3362:14338 +3362:14339 +3362:14340 +3362:14341 +3363:14311 +3363:14333 +3363:14342 +3363:14343 +3363:14344 +3363:14345 +3363:14346 +3364:14312 +3364:14348 +3365:14313 +3365:14349 +3365:14350 +3365:14351 +3365:14354 +3365:14356 +3365:14357 +3366:14314 +3366:14352 +3366:14359 +3367:14315 +3368:14316 +3369:14317 +3370:14318 +3370:14334 +3370:14335 +3370:14361 +3370:14362 +3370:14363 +3370:14364 +3370:14365 +3370:14366 +3370:14367 +3370:14368 +3370:14369 +3371:14319 +3372:14320 +3373:14321 +3373:14371 +3374:14322 +3375:14323 +3376:14324 +3377:14325 +3477:14089 +3477:14138 +3477:14140 +3477:14141 +3477:14142 +3477:14148 +3477:14150 +3477:14265 +3477:14293 +3477:14294 +3478:14090 +3478:14143 +3478:14144 +3478:14151 +3478:14152 +3478:14154 +3478:14155 +3478:14156 +3478:14157 +3478:14158 +3478:14177 +3478:14178 +3478:14179 +3478:14180 +3478:14181 +3478:14184 +3478:14185 +3478:14186 +3478:14187 +3478:14188 +3478:14189 +3478:14190 +3478:14191 +3478:14192 +3478:14193 +3478:14194 +3478:14195 +3478:14196 +3478:14197 +3478:14206 +3478:14207 +3478:14208 +3478:14209 +3478:14210 +3479:14091 +3479:14213 +3479:14214 +3479:14215 +3479:14216 +3479:14217 +3479:14258 +3479:14259 +3479:14260 +3479:14261 +3479:14262 +3480:14092 +3480:14145 +3480:14218 +3480:14219 +3480:14264 +3481:14093 +3482:14094 +3482:14225 +3482:14227 +3482:14228 +3482:14229 +3482:14230 +3483:14095 +3484:14096 +3484:14146 +3484:14221 +3484:14222 +3484:14223 +3484:14224 +3485:14097 +3486:14098 +3487:14099 +3487:14233 +3487:14234 +3487:14235 +3487:14237 +3487:14239 +3487:14240 +3488:14100 +3488:14236 +3489:14101 +3490:14102 +3491:14103 +3492:14104 +3493:14105 +3494:14106 +3495:14107 +3496:14108 +3497:14109 +3497:14147 +3497:14244 +3497:14245 +3497:14246 +3497:14247 +3498:14110 +3499:14111 +3500:14112 +3500:14248 +3500:14249 +3500:14250 +3500:14251 +3500:14253 +3500:14254 +3501:14113 +3502:14114 +3503:14115 +3504:14116 +3505:14117 +3506:14118 +3506:14266 +3506:14267 +3506:14289 +3506:14290 +3506:14291 +3506:14292 +3507:14119 +3507:14269 +3508:14120 +3508:14270 +3509:14121 +3509:14271 +3510:14122 +3510:14272 +3511:14123 +3511:14273 +3512:14124 +3512:14274 +3513:14125 +3513:14275 +3514:14126 +3514:14276 +3515:14127 +3515:14277 +3516:14128 +3516:14278 +3517:14129 +3517:14279 +3518:14130 +3518:14280 +3519:14131 +3519:14281 +3520:14132 +3520:14282 +3521:14133 +3521:14283 +3522:14134 +3522:14284 +3523:14135 +3523:14285 +3524:14136 +3524:14286 +3525:14137 +3525:14287 +3530:7005 +3530:7007 +3530:7008 +3530:7009 +3530:7019 +3530:7383 +3530:7398 +3530:7399 +3531:7011 +3532:7012 +3533:7013 +3534:7014 +3535:7015 +3536:7016 +3542:7021 +3542:7022 +3542:7023 +3542:7024 +3542:7025 +3542:7026 +3542:7027 +3542:7028 +3542:7029 +3542:7030 +3542:7031 +3542:7032 +3542:7033 +3542:7034 +3542:7035 +3542:7036 +3542:7037 +3542:7038 +3542:7039 +3542:7040 +3542:7041 +3542:7042 +3542:7043 +3542:7044 +3542:7045 +3542:7046 +3542:7047 +3542:7048 +3542:7049 +3542:7050 +3542:7051 +3542:7052 +3542:7053 +3542:7054 +3542:7055 +3542:7056 +3542:7057 +3542:7058 +3542:7059 +3542:7060 +3542:7061 +3542:7062 +3542:7063 +3542:7064 +3542:7065 +3542:7066 +3542:7067 +3542:7068 +3542:7069 +3542:7070 +3542:7071 +3542:7072 +3542:7073 +3542:7074 +3542:7075 +3542:7076 +3542:7077 +3542:7078 +3542:7079 +3542:7080 +3542:7081 +3542:7082 +3542:7083 +3542:7084 +3542:7085 +3542:7086 +3542:7087 +3542:7088 +3542:7089 +3542:7090 +3542:7091 +3542:7092 +3542:7093 +3542:7094 +3542:7095 +3542:7096 +3542:7097 +3542:7098 +3542:7099 +3542:7100 +3542:7101 +3542:7102 +3542:7373 +3542:7374 +3542:7375 +3542:7376 +3542:7377 +3543:7104 +3543:7105 +3543:7106 +3543:7350 +3543:7351 +3543:7352 +3543:7353 +3543:7354 +3544:7109 +3544:7110 +3544:7111 +3544:7191 +3544:7192 +3544:7193 +3544:7194 +3544:7196 +3544:7197 +3544:7198 +3545:7112 +3545:7113 +3545:7114 +3545:7115 +3546:7118 +3546:7185 +3546:7186 +3546:7187 +3546:7188 +3546:7189 +3547:7119 +3547:7120 +3547:7121 +3547:7122 +3547:7123 +3547:7124 +3547:7125 +3547:7126 +3547:7127 +3547:7128 +3547:7129 +3547:7130 +3547:7131 +3547:7132 +3547:7133 +3547:7134 +3547:7135 +3547:7136 +3547:7137 +3547:7138 +3547:7139 +3547:7140 +3547:7141 +3547:7142 +3547:7143 +3547:7144 +3547:7145 +3547:7146 +3547:7147 +3547:7148 +3547:7149 +3547:7150 +3547:7151 +3547:7152 +3547:7153 +3547:7154 +3547:7155 +3547:7156 +3547:7157 +3547:7158 +3547:7159 +3547:7160 +3547:7161 +3547:7162 +3547:7163 +3547:7164 +3547:7165 +3547:7166 +3547:7167 +3547:7168 +3547:7169 +3547:7170 +3547:7171 +3547:7172 +3547:7173 +3548:7176 +3548:7177 +3548:7178 +3548:7179 +3548:7180 +3548:7181 +3551:7199 +3551:7200 +3551:7201 +3551:7202 +3551:7204 +3551:7205 +3552:7210 +3552:7211 +3552:7212 +3552:7213 +3553:7215 +3553:7216 +3553:7217 +3553:7218 +3553:7219 +3553:7220 +3553:7332 +3553:7333 +3553:7334 +3553:7335 +3553:7337 +3553:7338 +3554:7222 +3555:7224 +3555:7326 +3555:7327 +3555:7328 +3555:7329 +3555:7330 +3556:7225 +3556:7226 +3556:7227 +3556:7228 +3556:7229 +3556:7230 +3556:7231 +3556:7232 +3556:7233 +3556:7234 +3556:7235 +3556:7236 +3556:7237 +3556:7238 +3556:7239 +3556:7240 +3556:7241 +3556:7242 +3556:7243 +3556:7244 +3556:7245 +3556:7246 +3556:7247 +3556:7248 +3556:7249 +3556:7250 +3556:7251 +3556:7252 +3556:7253 +3556:7254 +3556:7255 +3556:7256 +3556:7257 +3556:7258 +3556:7259 +3556:7260 +3556:7261 +3556:7262 +3556:7263 +3556:7264 +3556:7265 +3556:7266 +3556:7267 +3556:7268 +3556:7269 +3556:7270 +3556:7271 +3556:7272 +3556:7273 +3556:7274 +3556:7275 +3556:7276 +3556:7277 +3556:7278 +3556:7279 +3556:7280 +3556:7281 +3556:7282 +3556:7283 +3556:7284 +3556:7285 +3556:7286 +3556:7287 +3556:7288 +3556:7289 +3556:7290 +3556:7291 +3556:7292 +3556:7293 +3556:7294 +3556:7295 +3556:7296 +3556:7297 +3556:7298 +3556:7299 +3556:7300 +3556:7301 +3556:7302 +3556:7303 +3556:7305 +3556:7306 +3556:7307 +3556:7308 +3558:7313 +3558:7314 +3558:7315 +3558:7316 +3559:7318 +3559:7319 +3560:7320 +3566:7339 +3566:7340 +3566:7341 +3566:7342 +3566:7344 +3566:7345 +3568:7358 +3568:7359 +3568:7360 +3569:7362 +3569:7363 +3569:7364 +3569:7365 +3569:7367 +3569:7368 +3572:7379 +3572:7380 +3575:7382 +3575:7384 +3575:7385 +3575:7394 +3575:7395 +3575:7396 +3575:7397 +3576:7387 +3577:7388 +3578:7389 +3579:7390 +3580:7391 +3581:7392 +3589:14509 +3589:14512 +3589:14513 +3589:14515 +3589:14516 +3589:14517 +3589:14522 +3589:14658 +3589:14659 +3589:14660 +3590:14510 +3591:14511 +3595:14525 +3595:14526 +3595:14527 +3595:14528 +3595:14529 +3595:14530 +3595:14532 +3595:14534 +3595:14535 +3595:14536 +3595:14537 +3595:14538 +3595:14539 +3595:14540 +3595:14541 +3595:14554 +3595:14640 +3595:14642 +3595:14643 +3595:14644 +3595:14645 +3596:14518 +3596:14519 +3596:14542 +3596:14543 +3596:14544 +3596:14545 +3596:14546 +3596:14547 +3596:14548 +3596:14549 +3598:14551 +3598:14552 +3601:14520 +3601:14521 +3601:14555 +3601:14556 +3601:14557 +3601:14558 +3601:14559 +3601:14560 +3601:14561 +3602:14563 +3602:14564 +3604:14567 +3604:14634 +3604:14635 +3604:14636 +3604:14637 +3604:14638 +3605:14568 +3605:14569 +3605:14570 +3605:14571 +3605:14572 +3606:14574 +3606:14575 +3610:14648 +3610:14649 +3611:14650 +3612:14652 +3613:14653 +3614:14654 +3618:14657 +3625:11925 +3625:11932 +3625:11934 +3625:11935 +3625:11936 +3625:11966 +3625:11992 +3625:11997 +3625:11998 +3625:11999 +3625:12000 +3625:12001 +3625:12002 +3625:12003 +3626:11926 +3626:11941 +3626:11942 +3626:11943 +3626:11944 +3626:11945 +3626:11946 +3626:11948 +3626:11950 +3626:11951 +3626:11952 +3626:11953 +3626:11954 +3626:11955 +3626:11956 +3626:11957 +3627:11927 +3627:11937 +3627:11938 +3627:11958 +3627:11959 +3627:11960 +3627:11961 +3627:11962 +3627:11963 +3628:11928 +3628:11967 +3628:11968 +3628:11969 +3628:11970 +3628:11971 +3628:11972 +3628:11973 +3628:11975 +3628:11977 +3628:11978 +3628:11979 +3628:11980 +3628:11981 +3628:11982 +3628:11983 +3628:11984 +3629:11929 +3629:11939 +3629:11985 +3629:11986 +3629:11987 +3629:11988 +3629:11989 +3630:11930 +3630:11965 +3630:11993 +3630:11994 +3630:11995 +3630:11996 +3631:11931 +3632:11991 +3634:14662 +3634:14663 +3634:14665 +3634:14666 +3634:14667 +3634:14774 +3634:14775 +3636:14670 +3636:14765 +3636:14766 +3636:14767 +3636:14768 +3636:14769 +3637:14671 +3637:14672 +3637:14673 +3637:14674 +3637:14675 +3637:14678 +3637:14679 +3637:14680 +3637:14681 +3637:14682 +3637:14683 +3637:14684 +3637:14685 +3637:14686 +3637:14687 +3637:14688 +3637:14689 +3637:14690 +3637:14691 +3637:14692 +3637:14693 +3637:14694 +3637:14695 +3637:14696 +3637:14697 +3637:14698 +3637:14699 +3637:14700 +3637:14701 +3637:14702 +3637:14703 +3637:14704 +3637:14705 +3637:14706 +3637:14707 +3637:14708 +3637:14709 +3637:14710 +3637:14711 +3637:14712 +3637:14713 +3637:14714 +3637:14715 +3637:14716 +3637:14717 +3637:14718 +3637:14719 +3637:14720 +3637:14721 +3637:14722 +3637:14723 +3637:14724 +3637:14725 +3637:14726 +3637:14727 +3637:14728 +3637:14729 +3637:14730 +3637:14731 +3637:14732 +3637:14733 +3637:14734 +3637:14735 +3637:14736 +3637:14737 +3638:14740 +3638:14741 +3638:14742 +3638:14743 +3638:14744 +3638:14745 +3638:14746 +3638:14747 +3638:14750 +3638:14751 +3638:14752 +3638:14753 +3638:14754 +3639:14757 +3639:14758 +3639:14759 +3639:14760 +3639:14761 +3639:14762 +3641:14771 +3641:14772 +3641:14773 +3644:14426 +3644:14428 +3644:14429 +3644:14430 +3644:14506 +3644:14507 +3645:14435 +3645:14436 +3645:14437 +3645:14497 +3645:14498 +3645:14499 +3645:14500 +3645:14502 +3645:14503 +3645:14504 +3645:14505 +3655:14431 +3655:14432 +3655:14438 +3655:14439 +3655:14440 +3655:14441 +3655:14442 +3655:14443 +3655:14444 +3656:14447 +3656:14448 +3656:14449 +3656:14450 +3656:14451 +3656:14452 +3656:14453 +3656:14454 +3656:14455 +3656:14456 +3656:14457 +3656:14458 +3656:14459 +3656:14460 +3656:14461 +3656:14462 +3656:14463 +3656:14464 +3656:14465 +3656:14466 +3656:14467 +3656:14468 +3656:14469 +3656:14470 +3656:14471 +3656:14472 +3656:14473 +3656:14474 +3656:14475 +3656:14476 +3656:14477 +3656:14478 +3656:14479 +3656:14480 +3656:14481 +3656:14482 +3656:14483 +3656:14490 +3656:14491 +3656:14492 +3656:14493 +3656:14494 +3657:14496 +3662:14025 +3662:14026 +3662:14028 +3662:14029 +3662:14030 +3662:14032 +3662:14081 +3662:14082 +3662:14083 +3662:14084 +3662:14085 +3662:14086 +3662:14087 +3663:14033 +3663:14034 +3663:14035 +3663:14036 +3663:14037 +3663:14038 +3663:14039 +3663:14040 +3663:14041 +3663:14042 +3664:14045 +3664:14046 +3664:14047 +3664:14048 +3664:14049 +3664:14050 +3665:14054 +3665:14055 +3665:14056 +3665:14057 +3665:14058 +3665:14059 +3665:14060 +3666:14051 +3666:14063 +3666:14064 +3666:14065 +3666:14066 +3666:14067 +3666:14068 +3666:14069 +3667:14072 +3667:14073 +3667:14074 +3667:14075 +3667:14076 +3667:14077 +3667:14078 +3671:14777 +3671:14778 +3671:14780 +3671:14781 +3671:14782 +3671:14784 +3671:14839 +3671:14840 +3671:14841 +3671:14842 +3671:14843 +3671:14844 +3671:14845 +3672:14785 +3672:14786 +3672:14787 +3672:14788 +3672:14789 +3672:14790 +3673:14794 +3673:14795 +3673:14796 +3673:14797 +3673:14798 +3673:14799 +3674:14791 +3674:14803 +3674:14804 +3674:14805 +3674:14806 +3674:14807 +3674:14808 +3675:14800 +3675:14812 +3675:14813 +3675:14814 +3675:14815 +3675:14816 +3675:14817 +3676:14809 +3676:14821 +3676:14822 +3676:14823 +3676:14824 +3676:14825 +3676:14826 +3677:14818 +3677:14830 +3677:14831 +3677:14832 +3677:14833 +3677:14834 +3677:14835 +3678:14827 +3679:14836 +3681:14847 +3681:14848 +3681:14850 +3681:14851 +3681:14852 +3681:14854 +3681:14899 +3681:14900 +3681:14901 +3681:14902 +3681:14903 +3681:14904 +3681:14905 +3682:14855 +3682:14856 +3682:14857 +3682:14858 +3682:14859 +3682:14860 +3682:14861 +3682:14862 +3682:14863 +3683:14866 +3683:14867 +3683:14868 +3683:14869 +3683:14870 +3683:14871 +3683:14872 +3683:14873 +3683:14874 +3684:14877 +3684:14878 +3684:14879 +3684:14880 +3684:14881 +3684:14882 +3684:14883 +3684:14884 +3684:14885 +3685:14888 +3685:14889 +3685:14890 +3685:14891 +3685:14892 +3685:14893 +3685:14894 +3685:14895 +3685:14896 +3688:3749 +3688:3751 +3688:3752 +3688:3753 +3688:3772 +3688:3773 +3689:3755 +3689:3756 +3689:3757 +3689:3758 +3689:3759 +3689:3760 +3689:3765 +3689:3766 +3689:3767 +3689:3768 +3689:3770 +3689:3771 +3690:3761 +3690:3763 +3700:1551 +3700:1554 +3700:1556 +3700:1557 +3700:1558 +3700:1560 +3700:1616 +3700:1617 +3700:1618 +3700:1619 +3700:1620 +3700:1621 +3700:1622 +3701:1552 +3701:1561 +3701:1562 +3701:1563 +3702:1553 +3702:1564 +3702:1565 +3702:1566 +3702:1567 +3702:1568 +3702:1569 +3702:1570 +3702:1571 +3702:1572 +3702:1574 +3702:1575 +3703:1576 +3703:1577 +3704:1581 +3704:1582 +3704:1583 +3705:1584 +3705:1585 +3706:1587 +3706:1588 +3706:1589 +3706:1607 +3706:1608 +3706:1609 +3706:1610 +3706:1612 +3706:1613 +3708:1590 +3708:1591 +3709:1592 +3709:1593 +3709:1594 +3709:1595 +3709:1596 +3709:1597 +3709:1598 +3709:1599 +3709:1600 +3709:1602 +3709:1603 +3710:1604 +3710:1605 +3715:919 +3715:920 +3715:922 +3715:923 +3715:924 +3715:937 +3715:938 +3717:927 +3717:928 +3717:929 +3717:931 +3717:932 +3717:933 +3717:934 +3728:8851 +3728:8854 +3728:8856 +3728:8857 +3728:8858 +3728:8893 +3728:8894 +3729:8852 +3730:8853 +3730:8861 +3730:8862 +3730:8863 +3730:8864 +3730:8865 +3730:8866 +3730:8868 +3730:8870 +3730:8871 +3730:8872 +3730:8873 +3730:8874 +3730:8875 +3730:8876 +3730:8877 +3730:8884 +3730:8886 +3730:8887 +3730:8888 +3730:8889 +3731:8878 +3731:8879 +3732:8880 +3733:8881 +3734:8882 +3736:8892 +*E diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyTokenTypes.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyTokenTypes.java new file mode 100644 index 0000000000..f32822cb64 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyTokenTypes.java @@ -0,0 +1,247 @@ +// $ANTLR 2.7.7 (20060906): "groovy.g" -> "GroovyRecognizer.java"$ + +package org.codehaus.groovy.antlr.parser; + +import java.io.*; +import java.util.*; + +import groovyjarjarantlr.CommonToken; +import groovyjarjarantlr.InputBuffer; +import groovyjarjarantlr.LexerSharedInputState; +import groovyjarjarantlr.TokenStreamRecognitionException; + +import org.codehaus.groovy.antlr.*; +import org.codehaus.groovy.ast.Comment; + +public interface GroovyTokenTypes { + int EOF = 1; + int NULL_TREE_LOOKAHEAD = 3; + int BLOCK = 4; + int MODIFIERS = 5; + int OBJBLOCK = 6; + int SLIST = 7; + int METHOD_DEF = 8; + int VARIABLE_DEF = 9; + int INSTANCE_INIT = 10; + int STATIC_INIT = 11; + int TYPE = 12; + int CLASS_DEF = 13; + int INTERFACE_DEF = 14; + int TRAIT_DEF = 15; + int PACKAGE_DEF = 16; + int ARRAY_DECLARATOR = 17; + int EXTENDS_CLAUSE = 18; + int IMPLEMENTS_CLAUSE = 19; + int PARAMETERS = 20; + int PARAMETER_DEF = 21; + int LABELED_STAT = 22; + int TYPECAST = 23; + int INDEX_OP = 24; + int POST_INC = 25; + int POST_DEC = 26; + int METHOD_CALL = 27; + int EXPR = 28; + int IMPORT = 29; + int UNARY_MINUS = 30; + int UNARY_PLUS = 31; + int CASE_GROUP = 32; + int ELIST = 33; + int FOR_INIT = 34; + int FOR_CONDITION = 35; + int FOR_ITERATOR = 36; + int EMPTY_STAT = 37; + int FINAL = 38; + int ABSTRACT = 39; + int UNUSED_GOTO = 40; + int UNUSED_CONST = 41; + int UNUSED_DO = 42; + int STRICTFP = 43; + int SUPER_CTOR_CALL = 44; + int CTOR_CALL = 45; + int CTOR_IDENT = 46; + int VARIABLE_PARAMETER_DEF = 47; + int STRING_CONSTRUCTOR = 48; + int STRING_CTOR_MIDDLE = 49; + int CLOSABLE_BLOCK = 50; + int IMPLICIT_PARAMETERS = 51; + int SELECT_SLOT = 52; + int DYNAMIC_MEMBER = 53; + int LABELED_ARG = 54; + int SPREAD_ARG = 55; + int SPREAD_MAP_ARG = 56; + int LIST_CONSTRUCTOR = 57; + int MAP_CONSTRUCTOR = 58; + int FOR_IN_ITERABLE = 59; + int STATIC_IMPORT = 60; + int ENUM_DEF = 61; + int ENUM_CONSTANT_DEF = 62; + int FOR_EACH_CLAUSE = 63; + int ANNOTATION_DEF = 64; + int ANNOTATIONS = 65; + int ANNOTATION = 66; + int ANNOTATION_MEMBER_VALUE_PAIR = 67; + int ANNOTATION_FIELD_DEF = 68; + int ANNOTATION_ARRAY_INIT = 69; + int TYPE_ARGUMENTS = 70; + int TYPE_ARGUMENT = 71; + int TYPE_PARAMETERS = 72; + int TYPE_PARAMETER = 73; + int WILDCARD_TYPE = 74; + int TYPE_UPPER_BOUNDS = 75; + int TYPE_LOWER_BOUNDS = 76; + int CLOSURE_LIST = 77; + int MULTICATCH = 78; + int MULTICATCH_TYPES = 79; + int SH_COMMENT = 80; + int LITERAL_package = 81; + int LITERAL_import = 82; + int LITERAL_static = 83; + int LITERAL_def = 84; + int LBRACK = 85; + int RBRACK = 86; + int IDENT = 87; + int STRING_LITERAL = 88; + int LT = 89; + int DOT = 90; + int LPAREN = 91; + int LITERAL_class = 92; + int LITERAL_interface = 93; + int LITERAL_enum = 94; + int LITERAL_trait = 95; + int AT = 96; + int QUESTION = 97; + int LITERAL_extends = 98; + int LITERAL_super = 99; + int GT = 100; + int COMMA = 101; + int SR = 102; + int BSR = 103; + int LITERAL_void = 104; + int LITERAL_boolean = 105; + int LITERAL_byte = 106; + int LITERAL_char = 107; + int LITERAL_short = 108; + int LITERAL_int = 109; + int LITERAL_float = 110; + int LITERAL_long = 111; + int LITERAL_double = 112; + int STAR = 113; + int LITERAL_as = 114; + int LITERAL_private = 115; + int LITERAL_public = 116; + int LITERAL_protected = 117; + int LITERAL_transient = 118; + int LITERAL_native = 119; + int LITERAL_threadsafe = 120; + int LITERAL_synchronized = 121; + int LITERAL_volatile = 122; + int RPAREN = 123; + int ASSIGN = 124; + int BAND = 125; + int LCURLY = 126; + int RCURLY = 127; + int SEMI = 128; + int LITERAL_default = 129; + int LITERAL_throws = 130; + int LITERAL_implements = 131; + int LITERAL_this = 132; + int TRIPLE_DOT = 133; + int BOR = 134; + int CLOSABLE_BLOCK_OP = 135; + int COLON = 136; + int LITERAL_if = 137; + int LITERAL_else = 138; + int LITERAL_while = 139; + int LITERAL_switch = 140; + int LITERAL_for = 141; + int LITERAL_in = 142; + int LITERAL_return = 143; + int LITERAL_break = 144; + int LITERAL_continue = 145; + int LITERAL_throw = 146; + int LITERAL_assert = 147; + int PLUS = 148; + int MINUS = 149; + int LITERAL_case = 150; + int LITERAL_try = 151; + int LITERAL_finally = 152; + int LITERAL_catch = 153; + int SPREAD_DOT = 154; + int OPTIONAL_DOT = 155; + int MEMBER_POINTER = 156; + int LITERAL_false = 157; + int LITERAL_instanceof = 158; + int LITERAL_new = 159; + int LITERAL_null = 160; + int LITERAL_true = 161; + int PLUS_ASSIGN = 162; + int MINUS_ASSIGN = 163; + int STAR_ASSIGN = 164; + int DIV_ASSIGN = 165; + int MOD_ASSIGN = 166; + int SR_ASSIGN = 167; + int BSR_ASSIGN = 168; + int SL_ASSIGN = 169; + int BAND_ASSIGN = 170; + int BXOR_ASSIGN = 171; + int BOR_ASSIGN = 172; + int STAR_STAR_ASSIGN = 173; + int ELVIS_OPERATOR = 174; + int LOR = 175; + int LAND = 176; + int BXOR = 177; + int REGEX_FIND = 178; + int REGEX_MATCH = 179; + int NOT_EQUAL = 180; + int EQUAL = 181; + int IDENTICAL = 182; + int NOT_IDENTICAL = 183; + int COMPARE_TO = 184; + int LE = 185; + int GE = 186; + int SL = 187; + int RANGE_INCLUSIVE = 188; + int RANGE_EXCLUSIVE = 189; + int INC = 190; + int DIV = 191; + int MOD = 192; + int DEC = 193; + int STAR_STAR = 194; + int BNOT = 195; + int LNOT = 196; + int STRING_CTOR_START = 197; + int STRING_CTOR_END = 198; + int NUM_INT = 199; + int NUM_FLOAT = 200; + int NUM_LONG = 201; + int NUM_DOUBLE = 202; + int NUM_BIG_INT = 203; + int NUM_BIG_DECIMAL = 204; + int NLS = 205; + int DOLLAR = 206; + int WS = 207; + int ONE_NL = 208; + int ONE_NL_KEEP = 209; + int SL_COMMENT = 210; + int ML_COMMENT = 211; + int STRING_CH = 212; + int REGEXP_LITERAL = 213; + int DOLLAR_REGEXP_LITERAL = 214; + int REGEXP_CTOR_END = 215; + int DOLLAR_REGEXP_CTOR_END = 216; + int ESCAPED_SLASH = 217; + int ESCAPED_DOLLAR = 218; + int REGEXP_SYMBOL = 219; + int DOLLAR_REGEXP_SYMBOL = 220; + int ESC = 221; + int STRING_NL = 222; + int HEX_DIGIT = 223; + int VOCAB = 224; + int LETTER = 225; + int DIGIT = 226; + int DIGITS_WITH_UNDERSCORE = 227; + int DIGITS_WITH_UNDERSCORE_OPT = 228; + int EXPONENT = 229; + int FLOAT_SUFFIX = 230; + int BIG_SUFFIX = 231; +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyTokenTypes.txt b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyTokenTypes.txt new file mode 100644 index 0000000000..2ba8641d52 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/antlr/parser/GroovyTokenTypes.txt @@ -0,0 +1,230 @@ +// $ANTLR 2.7.7 (20060906): groovy.g -> GroovyTokenTypes.txt$ +Groovy // output token vocab name +BLOCK=4 +MODIFIERS=5 +OBJBLOCK=6 +SLIST=7 +METHOD_DEF=8 +VARIABLE_DEF=9 +INSTANCE_INIT=10 +STATIC_INIT=11 +TYPE=12 +CLASS_DEF=13 +INTERFACE_DEF=14 +TRAIT_DEF=15 +PACKAGE_DEF=16 +ARRAY_DECLARATOR=17 +EXTENDS_CLAUSE=18 +IMPLEMENTS_CLAUSE=19 +PARAMETERS=20 +PARAMETER_DEF=21 +LABELED_STAT=22 +TYPECAST=23 +INDEX_OP=24 +POST_INC=25 +POST_DEC=26 +METHOD_CALL=27 +EXPR=28 +IMPORT=29 +UNARY_MINUS=30 +UNARY_PLUS=31 +CASE_GROUP=32 +ELIST=33 +FOR_INIT=34 +FOR_CONDITION=35 +FOR_ITERATOR=36 +EMPTY_STAT=37 +FINAL="final"=38 +ABSTRACT="abstract"=39 +UNUSED_GOTO="goto"=40 +UNUSED_CONST="const"=41 +UNUSED_DO="do"=42 +STRICTFP="strictfp"=43 +SUPER_CTOR_CALL=44 +CTOR_CALL=45 +CTOR_IDENT=46 +VARIABLE_PARAMETER_DEF=47 +STRING_CONSTRUCTOR=48 +STRING_CTOR_MIDDLE=49 +CLOSABLE_BLOCK=50 +IMPLICIT_PARAMETERS=51 +SELECT_SLOT=52 +DYNAMIC_MEMBER=53 +LABELED_ARG=54 +SPREAD_ARG=55 +SPREAD_MAP_ARG=56 +LIST_CONSTRUCTOR=57 +MAP_CONSTRUCTOR=58 +FOR_IN_ITERABLE=59 +STATIC_IMPORT=60 +ENUM_DEF=61 +ENUM_CONSTANT_DEF=62 +FOR_EACH_CLAUSE=63 +ANNOTATION_DEF=64 +ANNOTATIONS=65 +ANNOTATION=66 +ANNOTATION_MEMBER_VALUE_PAIR=67 +ANNOTATION_FIELD_DEF=68 +ANNOTATION_ARRAY_INIT=69 +TYPE_ARGUMENTS=70 +TYPE_ARGUMENT=71 +TYPE_PARAMETERS=72 +TYPE_PARAMETER=73 +WILDCARD_TYPE=74 +TYPE_UPPER_BOUNDS=75 +TYPE_LOWER_BOUNDS=76 +CLOSURE_LIST=77 +MULTICATCH=78 +MULTICATCH_TYPES=79 +SH_COMMENT("a script header")=80 +LITERAL_package="package"=81 +LITERAL_import="import"=82 +LITERAL_static="static"=83 +LITERAL_def="def"=84 +LBRACK("'['")=85 +RBRACK("']'")=86 +IDENT("an identifier")=87 +STRING_LITERAL("a string literal")=88 +LT("'<'")=89 +DOT("'.'")=90 +LPAREN("'('")=91 +LITERAL_class="class"=92 +LITERAL_interface="interface"=93 +LITERAL_enum="enum"=94 +LITERAL_trait="trait"=95 +AT("'@'")=96 +QUESTION("'?'")=97 +LITERAL_extends="extends"=98 +LITERAL_super="super"=99 +GT("'>'")=100 +COMMA("','")=101 +SR("'>>'")=102 +BSR("'>>>'")=103 +LITERAL_void="void"=104 +LITERAL_boolean="boolean"=105 +LITERAL_byte="byte"=106 +LITERAL_char="char"=107 +LITERAL_short="short"=108 +LITERAL_int="int"=109 +LITERAL_float="float"=110 +LITERAL_long="long"=111 +LITERAL_double="double"=112 +STAR("'*'")=113 +LITERAL_as="as"=114 +LITERAL_private="private"=115 +LITERAL_public="public"=116 +LITERAL_protected="protected"=117 +LITERAL_transient="transient"=118 +LITERAL_native="native"=119 +LITERAL_threadsafe="threadsafe"=120 +LITERAL_synchronized="synchronized"=121 +LITERAL_volatile="volatile"=122 +RPAREN("')'")=123 +ASSIGN("'='")=124 +BAND("'&'")=125 +LCURLY("'{'")=126 +RCURLY("'}'")=127 +SEMI("';'")=128 +LITERAL_default="default"=129 +LITERAL_throws="throws"=130 +LITERAL_implements="implements"=131 +LITERAL_this="this"=132 +TRIPLE_DOT("'...'")=133 +BOR("'|'")=134 +CLOSABLE_BLOCK_OP("'->'")=135 +COLON("':'")=136 +LITERAL_if="if"=137 +LITERAL_else="else"=138 +LITERAL_while="while"=139 +LITERAL_switch="switch"=140 +LITERAL_for="for"=141 +LITERAL_in="in"=142 +LITERAL_return="return"=143 +LITERAL_break="break"=144 +LITERAL_continue="continue"=145 +LITERAL_throw="throw"=146 +LITERAL_assert="assert"=147 +PLUS("'+'")=148 +MINUS("'-'")=149 +LITERAL_case="case"=150 +LITERAL_try="try"=151 +LITERAL_finally="finally"=152 +LITERAL_catch="catch"=153 +SPREAD_DOT("'*.'")=154 +OPTIONAL_DOT("'?.'")=155 +MEMBER_POINTER("'.&'")=156 +LITERAL_false="false"=157 +LITERAL_instanceof="instanceof"=158 +LITERAL_new="new"=159 +LITERAL_null="null"=160 +LITERAL_true="true"=161 +PLUS_ASSIGN("'+='")=162 +MINUS_ASSIGN("'-='")=163 +STAR_ASSIGN("'*='")=164 +DIV_ASSIGN("'/='")=165 +MOD_ASSIGN("'%='")=166 +SR_ASSIGN("'>>='")=167 +BSR_ASSIGN("'>>>='")=168 +SL_ASSIGN("'<<='")=169 +BAND_ASSIGN("'&='")=170 +BXOR_ASSIGN("'^='")=171 +BOR_ASSIGN("'|='")=172 +STAR_STAR_ASSIGN("'**='")=173 +ELVIS_OPERATOR("'?:'")=174 +LOR("'||'")=175 +LAND("'&&'")=176 +BXOR("'^'")=177 +REGEX_FIND("'=~'")=178 +REGEX_MATCH("'==~'")=179 +NOT_EQUAL("'!='")=180 +EQUAL("'=='")=181 +IDENTICAL("'==='")=182 +NOT_IDENTICAL("'!=='")=183 +COMPARE_TO("'<=>'")=184 +LE("'<='")=185 +GE("'>='")=186 +SL("'<<'")=187 +RANGE_INCLUSIVE("'..'")=188 +RANGE_EXCLUSIVE("'..<'")=189 +INC("'++'")=190 +DIV("'/'")=191 +MOD("'%'")=192 +DEC("'--'")=193 +STAR_STAR("'**'")=194 +BNOT("'~'")=195 +LNOT("'!'")=196 +STRING_CTOR_START=197 +STRING_CTOR_END("a string literal end")=198 +NUM_INT("a numeric literal")=199 +NUM_FLOAT=200 +NUM_LONG=201 +NUM_DOUBLE=202 +NUM_BIG_INT=203 +NUM_BIG_DECIMAL=204 +NLS("some newlines, whitespace or comments")=205 +DOLLAR("'$'")=206 +WS("whitespace")=207 +ONE_NL("a newline")=208 +ONE_NL_KEEP("a newline")=209 +SL_COMMENT("a single line comment")=210 +ML_COMMENT("a multi-line comment")=211 +STRING_CH("a string character")=212 +REGEXP_LITERAL("a multiline regular expression literal")=213 +DOLLAR_REGEXP_LITERAL("a multiline dollar escaping regular expression literal")=214 +REGEXP_CTOR_END("a multiline regular expression literal end")=215 +DOLLAR_REGEXP_CTOR_END("a multiline dollar escaping regular expression literal end")=216 +ESCAPED_SLASH=217 +ESCAPED_DOLLAR=218 +REGEXP_SYMBOL("a multiline regular expression character")=219 +DOLLAR_REGEXP_SYMBOL("a multiline dollar escaping regular expression character")=220 +ESC("an escape sequence")=221 +STRING_NL("a newline inside a string")=222 +HEX_DIGIT("a hexadecimal digit")=223 +VOCAB("a character")=224 +LETTER("a letter")=225 +DIGIT("a digit")=226 +DIGITS_WITH_UNDERSCORE("a sequence of digits and underscores, bordered by digits")=227 +DIGITS_WITH_UNDERSCORE_OPT("a sequence of digits and underscores with maybe underscore starting")=228 +EXPONENT("an exponent")=229 +FLOAT_SUFFIX("a float or double suffix")=230 +BIG_SUFFIX("a big decimal suffix")=231 diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ASTNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ASTNode.java new file mode 100644 index 0000000000..6b7e8beb84 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ASTNode.java @@ -0,0 +1,197 @@ +/* + * 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.ast; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.util.ListHashMap; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Base class for any AST node. This class supports basic information used in all nodes of the AST: + *

    + *
  • line and column number information. Usually a node represents a certain + * area in a text file determined by a starting position and an ending position. + * For nodes that do not represent this, this information will be -1. A node can + * also be configured in its line/col information using another node through + * setSourcePosition(otherNode).
  • + *
  • every node can store meta data. A phase operation or transform can use + * this to transport arbitrary information to another phase operation or + * transform. The only requirement is that the other phase operation or transform + * runs after the part storing the information. If the information transport is + * done it is strongly recommended to remove that meta data.
  • + *
  • a text representation of this node trough getText(). This was in the + * past used for assertion messages. Since the usage of power asserts this + * method will not be called for this purpose anymore and might be removed in + * future versions of Groovy
  • + *
+ * + * @author James Strachan + * @author Jochen "blackdrag" Theodorou + */ +public class ASTNode implements NodeMetaDataHandler { + + private int lineNumber = -1; + private int columnNumber = -1; + private int lastLineNumber = -1; + private int lastColumnNumber = -1; + // GRECLIPSE add + private int start = 0; + private int end = 0; + // GRECLIPSE end + private Map metaDataMap = null; + private NodeMetaDataHandlerHelper helper = new NodeMetaDataHandlerHelper(this); + + public void visit(GroovyCodeVisitor visitor) { + throw new RuntimeException("No visit() method implemented for class: " + getClass().getName()); + } + + public String getText() { + return ""; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public int getColumnNumber() { + return columnNumber; + } + + public void setColumnNumber(int columnNumber) { + this.columnNumber = columnNumber; + } + + public int getLastLineNumber() { + return lastLineNumber; + } + + public void setLastLineNumber(int lastLineNumber) { + this.lastLineNumber = lastLineNumber; + } + + public int getLastColumnNumber() { + return lastColumnNumber; + } + + public void setLastColumnNumber(int lastColumnNumber) { + this.lastColumnNumber = lastColumnNumber; + } + + // GRECLIPSE add + public int getStart() { + return start; + } + public void setStart(int start) { + this.start = start; + } + public int getEnd() { + return end; + } + public void setEnd(int end) { + this.end = end; + } + public int getLength() { + return end >= 0 && start >= 0 ? end - start : -1; + } + // GRECLIPSE end + + /** + * Sets the source position using another ASTNode. + * The sourcePosition consists of a line/column pair for + * the start and a line/column pair for the end of the + * expression or statement + * + * @param node - the node used to configure the position information + */ + public void setSourcePosition(ASTNode node) { + this.columnNumber = node.getColumnNumber(); + this.lastLineNumber = node.getLastLineNumber(); + this.lastColumnNumber = node.getLastColumnNumber(); + this.lineNumber = node.getLineNumber(); + // GRECLIPSE add + this.start = node.getStart(); + this.end = node.getEnd(); + // GRECLIPSE end + } + + /** + * Copies all node meta data from the other node to this one + * @param other - the other node + */ + public void copyNodeMetaData(ASTNode other) { + copyNodeMetaData((NodeMetaDataHandler) other); + } + + @Override + public T getNodeMetaData(Object key) { + return helper.getNodeMetaData(key); + } + + @Override + public void copyNodeMetaData(NodeMetaDataHandler other) { + helper.copyNodeMetaData(other); + } + + @Override + public void setNodeMetaData(Object key, Object value) { + helper.setNodeMetaData(key, value); + } + + @Override + public Object putNodeMetaData(Object key, Object value) { + return helper.putNodeMetaData(key, value); + } + + @Override + public void removeNodeMetaData(Object key) { + helper.removeNodeMetaData(key); + } + + @Override + public Map getNodeMetaData() { + return helper.getNodeMetaData(); + } + + @Override + public ListHashMap getMetaDataMap() { + return (ListHashMap) metaDataMap; + } + + @Override + public void setMetaDataMap(Map metaDataMap) { + this.metaDataMap = metaDataMap; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return Objects.hash(lineNumber, columnNumber, lastLineNumber, lastColumnNumber); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/AnnotatedNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/AnnotatedNode.java new file mode 100644 index 0000000000..534cbe7f65 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/AnnotatedNode.java @@ -0,0 +1,144 @@ +/* + * 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.ast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Base class for any AST node which is capable of being annotated + * + * @author James Strachan + */ +public class AnnotatedNode extends ASTNode { + private List annotations = Collections.emptyList(); + private boolean synthetic; + ClassNode declaringClass; + private boolean hasNoRealSourcePositionFlag; + // GRECLIPSE add + private int nameEnd; + private int nameStart; + // GRECLIPSE end + + public AnnotatedNode() { + } + + public List getAnnotations() { + return annotations; + } + + public List getAnnotations(ClassNode type) { + List ret = new ArrayList(annotations.size()); + for (AnnotationNode node: annotations) { + if (type.equals(node.getClassNode())) ret.add(node); + } + return ret; + } + + public void addAnnotation(AnnotationNode value) { + checkInit(); + annotations.add(value); + } + + private void checkInit() { + if (annotations == Collections.EMPTY_LIST) + annotations = new ArrayList(3); + } + + public void addAnnotations(List annotations) { + for (AnnotationNode node : annotations) { + addAnnotation(node); + } + } + + /** + * returns true if this node is added by the compiler. + * NOTE: + * This method has nothing to do with the synthetic flag + * for fields, methods or classes. + * @return true if this node is added by the compiler + */ + public boolean isSynthetic() { + return synthetic; + } + + /** + * sets this node as a node added by the compiler. + * NOTE: + * This method has nothing to do with the synthetic flag + * for fields, methods or classes. + * @param synthetic - if true this node is marked as + * added by the compiler + */ + public void setSynthetic(boolean synthetic) { + this.synthetic = synthetic; + } + + public ClassNode getDeclaringClass() { + return declaringClass; + } + + /** + * @param declaringClass - The declaringClass to set. + */ + public void setDeclaringClass(ClassNode declaringClass) { + this.declaringClass = declaringClass; + } + + /** + * Currently only ever returns true for default constructors + * added by the compiler. See GROOVY-4161. + */ + public boolean hasNoRealSourcePosition() { + return hasNoRealSourcePositionFlag; + } + + public void setHasNoRealSourcePosition(boolean value) { + this.hasNoRealSourcePositionFlag = value; + } + + // GRECLIPSE add + public int getNameStart() { + return nameStart; + } + + public void setNameStart(int nameStart) { + this.nameStart = nameStart; + } + + public int getNameEnd() { + return nameEnd; + } + + public void setNameEnd(int nameEnd) { + this.nameEnd = nameEnd; + } + + @Override + public void setSourcePosition(ASTNode node) { + super.setSourcePosition(node); + if (node instanceof AnnotatedNode) { + AnnotatedNode aNode = (AnnotatedNode) node; + setNameStart(aNode.getNameStart()); + setNameEnd(aNode.getNameEnd()); + } + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java new file mode 100644 index 0000000000..604508f02a --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java @@ -0,0 +1,321 @@ +/* + * 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.ast; + +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.AssertStatement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.DoWhileStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.SynchronizedStatement; +import org.codehaus.groovy.ast.stmt.ThrowStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.PreciseSyntaxException; +import org.codehaus.groovy.transform.ErrorCollecting; + +import java.util.List; +import java.util.Set; + +public abstract class ClassCodeVisitorSupport extends CodeVisitorSupport implements ErrorCollecting, GroovyClassVisitor { + + public void visitClass(ClassNode node) { + visitAnnotations(node); + visitPackage(node.getPackage()); + visitImports(node.getModule()); + node.visitContents(this); + visitObjectInitializerStatements(node); + } + + protected void visitObjectInitializerStatements(ClassNode node) { + for (Statement element : node.getObjectInitializerStatements()) { + element.visit(this); + } + } + + public void visitPackage(PackageNode node) { + if (node != null) { + visitAnnotations(node); + node.visit(this); + } + } + + public void visitImports(ModuleNode node) { + if (node != null) { + for (ImportNode importNode : node.getImports()) { + visitAnnotations(importNode); + importNode.visit(this); + } + for (ImportNode importStarNode : node.getStarImports()) { + visitAnnotations(importStarNode); + importStarNode.visit(this); + } + for (ImportNode importStaticNode : node.getStaticImports().values()) { + visitAnnotations(importStaticNode); + importStaticNode.visit(this); + } + for (ImportNode importStaticStarNode : node.getStaticStarImports().values()) { + visitAnnotations(importStaticStarNode); + importStaticStarNode.visit(this); + } + } + } + + public void visitAnnotations(AnnotatedNode node) { + List annotations = node.getAnnotations(); + // GRECLIPSE edit + if (!annotations.isEmpty()) + visitAnnotations(annotations); + // GRECLIPSE end + } + + // GRECLIPSE add + protected void visitAnnotations(Iterable nodes) { + for (AnnotationNode node : nodes) { + // skip built-in properties + if (node.isBuiltIn()) continue; + + Set originals = + node.getNodeMetaData("AnnotationCollector"); + if (originals != null && !originals.isEmpty()) { + visitAnnotations(originals); + } + visitAnnotation(node); + } + } + + protected void visitAnnotation(AnnotationNode node) { + for (Expression expr : node.getMembers().values()) { + expr.visit(this); + } + } + + public void visitConstantExpression(ConstantExpression expression) { + // check for inlined constant (see ResolveVisitor.transformInlineConstants) + Expression original = expression.getNodeMetaData(ORIGINAL_EXPRESSION); + if (original != null) { + original.visit(this); + } + } + + /** + * Returns the original source expression (in case of constant inlining) or + * the input expression. + * + * @see org.codehaus.groovy.control.ResolveVisitor#transformInlineConstants + * @see org.codehaus.groovy.control.ResolveVisitor#cloneConstantExpression + */ + public static final Expression getNonInlinedExpression(Expression expression) { + Expression original = expression.getNodeMetaData(ORIGINAL_EXPRESSION); + return original != null ? original : expression; + } + + public static final String ORIGINAL_EXPRESSION = "OriginalExpression"; + // GRECLIPSE end + + public void visitBlockStatement(BlockStatement block) { + visitStatement(block); + super.visitBlockStatement(block); + } + + protected void visitClassCodeContainer(Statement code) { + if (code != null) code.visit(this); + } + + public void visitDeclarationExpression(DeclarationExpression expression) { + visitAnnotations(expression); + super.visitDeclarationExpression(expression); + } + + protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { + visitAnnotations(node); + visitClassCodeContainer(node.getCode()); + for (Parameter param : node.getParameters()) { + visitAnnotations(param); + } + } + + public void visitConstructor(ConstructorNode node) { + visitConstructorOrMethod(node, true); + } + + public void visitMethod(MethodNode node) { + visitConstructorOrMethod(node, false); + } + + public void visitField(FieldNode node) { + visitAnnotations(node); + Expression init = node.getInitialExpression(); + if (init != null) init.visit(this); + } + + public void visitProperty(PropertyNode node) { + visitAnnotations(node); + Statement statement = node.getGetterBlock(); + visitClassCodeContainer(statement); + + statement = node.getSetterBlock(); + visitClassCodeContainer(statement); + + Expression init = node.getInitialExpression(); + if (init != null) init.visit(this); + } + + public void addError(String msg, ASTNode expr) { + // GRECLIPSE add + int line = expr.getLineNumber(); + int col = expr.getColumnNumber(); + int start = expr.getStart(); + int end = expr.getEnd() - 1; + if (expr instanceof ClassNode) { + // assume we have a class declaration + ClassNode cn = (ClassNode) expr; + if (cn.getNameEnd() > 0) { + start = cn.getNameStart(); + end = cn.getNameEnd(); + } else if (cn.getComponentType() != null) { + // avoid extra whitespace after closing ] + end -= 1; + } + } else if (expr instanceof DeclarationExpression) { + // assume that we just want to underline the variable declaration + DeclarationExpression decl = (DeclarationExpression) expr; + Expression lhs = decl.getLeftExpression(); + start = lhs.getStart(); + // avoid extra space before = if a variable + end = lhs instanceof VariableExpression ? start + lhs.getText().length() -1: lhs.getEnd() -1; + } + // GRECLIPSE end + SourceUnit source = getSourceUnit(); + source.getErrorCollector().addErrorAndContinue( + // GRECLIPSE edit + //new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), source) + new SyntaxErrorMessage(new PreciseSyntaxException(msg + '\n', line, col, start, end), source) + // GRECLIPSE end + ); + } + + // GRECLIPSE add + protected void addTypeError(String msg, ClassNode expr) { + SourceUnit source = getSourceUnit(); + source.getErrorCollector().addErrorAndContinue( + new SyntaxErrorMessage(new PreciseSyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getNameStart(), expr.getNameEnd()), source) + ); + } + // GRECLIPSE end + + // GRECLIPSE edit + //protected abstract SourceUnit getSourceUnit(); + protected SourceUnit getSourceUnit() { + return null; + } + // GRECLIPSE end + + protected void visitStatement(Statement statement) { + } + + public void visitAssertStatement(AssertStatement statement) { + visitStatement(statement); + super.visitAssertStatement(statement); + } + + public void visitBreakStatement(BreakStatement statement) { + visitStatement(statement); + super.visitBreakStatement(statement); + } + + public void visitCaseStatement(CaseStatement statement) { + visitStatement(statement); + super.visitCaseStatement(statement); + } + + public void visitCatchStatement(CatchStatement statement) { + visitStatement(statement); + super.visitCatchStatement(statement); + } + + public void visitContinueStatement(ContinueStatement statement) { + visitStatement(statement); + super.visitContinueStatement(statement); + } + + public void visitDoWhileLoop(DoWhileStatement loop) { + visitStatement(loop); + super.visitDoWhileLoop(loop); + } + + public void visitExpressionStatement(ExpressionStatement statement) { + visitStatement(statement); + super.visitExpressionStatement(statement); + } + + public void visitForLoop(ForStatement forLoop) { + visitStatement(forLoop); + super.visitForLoop(forLoop); + } + + public void visitIfElse(IfStatement ifElse) { + visitStatement(ifElse); + super.visitIfElse(ifElse); + } + + public void visitReturnStatement(ReturnStatement statement) { + visitStatement(statement); + super.visitReturnStatement(statement); + } + + public void visitSwitch(SwitchStatement statement) { + visitStatement(statement); + super.visitSwitch(statement); + } + + public void visitSynchronizedStatement(SynchronizedStatement statement) { + visitStatement(statement); + super.visitSynchronizedStatement(statement); + } + + public void visitThrowStatement(ThrowStatement statement) { + visitStatement(statement); + super.visitThrowStatement(statement); + } + + public void visitTryCatchFinally(TryCatchStatement statement) { + visitStatement(statement); + super.visitTryCatchFinally(statement); + } + + public void visitWhileLoop(WhileStatement loop) { + visitStatement(loop); + super.visitWhileLoop(loop); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassHelper.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassHelper.java new file mode 100644 index 0000000000..2421e8a896 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassHelper.java @@ -0,0 +1,485 @@ +/* + * 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.ast; + +import groovy.lang.Binding; +import groovy.lang.Closure; +import groovy.lang.GString; +import groovy.lang.GroovyInterceptable; +import groovy.lang.GroovyObject; +import groovy.lang.GroovyObjectSupport; +import groovy.lang.MetaClass; +import groovy.lang.Range; +import groovy.lang.Reference; +import groovy.lang.Script; +import org.apache.groovy.util.Maps; +import org.codehaus.groovy.classgen.asm.util.TypeDescriptionUtil; +import org.codehaus.groovy.runtime.GeneratedClosure; +import org.codehaus.groovy.runtime.GeneratedLambda; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport; +import org.codehaus.groovy.transform.trait.Traits; +import org.codehaus.groovy.util.ManagedConcurrentMap; +import org.codehaus.groovy.util.ReferenceBundle; +import org.codehaus.groovy.vmplugin.VMPluginFactory; +import groovyjarjarasm.asm.Opcodes; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.ref.SoftReference; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * This class is a Helper for ClassNode and classes handling ClassNodes. + * It does contain a set of predefined ClassNodes for the most used + * types and some code for cached ClassNode creation and basic + * ClassNode handling + */ +public class ClassHelper { + + private static final Class[] classes = new Class[]{ + Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, + Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE, + Closure.class, GString.class, List.class, Map.class, Range.class, + Pattern.class, Script.class, String.class, Boolean.class, + Character.class, Byte.class, Short.class, Integer.class, Long.class, + Double.class, Float.class, BigDecimal.class, BigInteger.class, + Number.class, Void.class, Reference.class, Class.class, MetaClass.class, + Iterator.class, GeneratedClosure.class, GeneratedLambda.class, GroovyObjectSupport.class + }; + + private static final String[] primitiveClassNames = new String[]{ + "", "boolean", "char", "byte", "short", + "int", "long", "double", "float", "void" + }; + + public static final ClassNode + DYNAMIC_TYPE = makeCached(Object.class), OBJECT_TYPE = DYNAMIC_TYPE, + VOID_TYPE = makeCached(Void.TYPE), + CLOSURE_TYPE = makeCached(Closure.class), + GSTRING_TYPE = makeCached(GString.class), LIST_TYPE = makeWithoutCaching(List.class), + MAP_TYPE = makeWithoutCaching(Map.class), RANGE_TYPE = makeCached(Range.class), + PATTERN_TYPE = makeCached(Pattern.class), STRING_TYPE = makeCached(String.class), + SCRIPT_TYPE = makeCached(Script.class), REFERENCE_TYPE = makeWithoutCaching(Reference.class), + BINDING_TYPE = makeCached(Binding.class), + + boolean_TYPE = makeCached(boolean.class), char_TYPE = makeCached(char.class), + byte_TYPE = makeCached(byte.class), int_TYPE = makeCached(int.class), + long_TYPE = makeCached(long.class), short_TYPE = makeCached(short.class), + double_TYPE = makeCached(double.class), float_TYPE = makeCached(float.class), + Byte_TYPE = makeCached(Byte.class), Short_TYPE = makeCached(Short.class), + Integer_TYPE = makeCached(Integer.class), Long_TYPE = makeCached(Long.class), + Character_TYPE = makeCached(Character.class), Float_TYPE = makeCached(Float.class), + Double_TYPE = makeCached(Double.class), Boolean_TYPE = makeCached(Boolean.class), + BigInteger_TYPE = makeCached(java.math.BigInteger.class), + BigDecimal_TYPE = makeCached(java.math.BigDecimal.class), + Number_TYPE = makeCached(Number.class), + + void_WRAPPER_TYPE = makeCached(Void.class), METACLASS_TYPE = makeCached(MetaClass.class), + Iterator_TYPE = makeCached(Iterator.class), + + Enum_Type = makeWithoutCaching(Enum.class), + Annotation_TYPE = makeCached(Annotation.class), + ELEMENT_TYPE_TYPE = makeCached(ElementType.class), + +// FunctionalInterface_Type = ClassHelper.makeCached(FunctionalInterface.class), + + // uncached constants. + CLASS_Type = makeWithoutCaching(Class.class), COMPARABLE_TYPE = makeWithoutCaching(Comparable.class), + GENERATED_CLOSURE_Type = makeWithoutCaching(GeneratedClosure.class), + GENERATED_LAMBDA_TYPE = makeWithoutCaching(GeneratedLambda.class), + GROOVY_OBJECT_SUPPORT_TYPE = makeWithoutCaching(GroovyObjectSupport.class), + GROOVY_OBJECT_TYPE = makeWithoutCaching(GroovyObject.class), + GROOVY_INTERCEPTABLE_TYPE = makeWithoutCaching(GroovyInterceptable.class); + + private static final ClassNode[] types = new ClassNode[]{ + OBJECT_TYPE, + boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE, + int_TYPE, long_TYPE, double_TYPE, float_TYPE, + VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE, + LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE, + SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE, + Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE, + Double_TYPE, Float_TYPE, BigDecimal_TYPE, BigInteger_TYPE, + Number_TYPE, + void_WRAPPER_TYPE, REFERENCE_TYPE, CLASS_Type, METACLASS_TYPE, + Iterator_TYPE, GENERATED_CLOSURE_Type, GENERATED_LAMBDA_TYPE, GROOVY_OBJECT_SUPPORT_TYPE, + GROOVY_OBJECT_TYPE, GROOVY_INTERCEPTABLE_TYPE, Enum_Type, Annotation_TYPE + }; + + private static final int ABSTRACT_STATIC_PRIVATE = + Modifier.ABSTRACT | Modifier.PRIVATE | Modifier.STATIC; + private static final int VISIBILITY = 5; // public|protected + + protected static final ClassNode[] EMPTY_TYPE_ARRAY = {}; + + public static final String OBJECT = "java.lang.Object"; + + public static ClassNode makeCached(Class c) { + final SoftReference classNodeSoftReference = ClassHelperCache.classCache.get(c); + ClassNode classNode; + if (classNodeSoftReference == null || (classNode = classNodeSoftReference.get()) == null) { + // GRECLIPSE edit + classNode = new ImmutableClassNode(c); + // GRECLIPSE end + ClassHelperCache.classCache.put(c, new SoftReference(classNode)); + + VMPluginFactory.getPlugin().setAdditionalClassInformation(classNode); + } + + return classNode; + } + + /** + * Creates an array of ClassNodes using an array of classes. + * For each of the given classes a new ClassNode will be + * created + * + * @param classes an array of classes used to create the ClassNodes + * @return an array of ClassNodes + * @see #make(Class) + */ + public static ClassNode[] make(Class[] classes) { + ClassNode[] cns = new ClassNode[classes.length]; + for (int i = 0; i < cns.length; i++) { + cns[i] = make(classes[i]); + } + + return cns; + } + + /** + * Creates a ClassNode using a given class. + * A new ClassNode object is only created if the class + * is not one of the predefined ones + * + * @param c class used to created the ClassNode + * @return ClassNode instance created from the given class + */ + public static ClassNode make(Class c) { + return make(c, true); + } + + public static ClassNode make(Class c, boolean includeGenerics) { + for (int i = 0; i < classes.length; i++) { + if (c == classes[i]) return types[i]; + } + if (c.isArray()) { + ClassNode cn = make(c.getComponentType(), includeGenerics); + return cn.makeArray(); + } + return makeWithoutCaching(c, includeGenerics); + } + + public static ClassNode makeWithoutCaching(Class c) { + return makeWithoutCaching(c, true); + } + + public static ClassNode makeWithoutCaching(Class c, boolean includeGenerics) { + if (c.isArray()) { + ClassNode cn = makeWithoutCaching(c.getComponentType(), includeGenerics); + return cn.makeArray(); + } + + final ClassNode cached = makeCached(c); + if (includeGenerics) { + return cached; + } else { + ClassNode t = makeWithoutCaching(c.getName()); + t.setRedirect(cached); + return t; + } + } + + + /** + * Creates a ClassNode using a given class. + * Unlike make(String) this method will not use the cache + * to create the ClassNode. This means the ClassNode created + * from this method using the same name will have a different + * reference + * + * @param name of the class the ClassNode is representing + * @see #make(String) + */ + public static ClassNode makeWithoutCaching(String name) { + ClassNode cn = new ClassNode(name, Opcodes.ACC_PUBLIC, OBJECT_TYPE); + cn.isPrimaryNode = false; + return cn; + } + + /** + * Creates a ClassNode using a given class. + * If the name is one of the predefined ClassNodes then the + * corresponding ClassNode instance will be returned. If the + * name is null or of length 0 the dynamic type is returned + * + * @param name of the class the ClassNode is representing + */ + public static ClassNode make(String name) { + if (name == null || name.length() == 0) return DYNAMIC_TYPE; + + for (int i = 0; i < primitiveClassNames.length; i++) { + if (primitiveClassNames[i].equals(name)) return types[i]; + } + + for (int i = 0; i < classes.length; i++) { + String cname = classes[i].getName(); + if (name.equals(cname)) return types[i]; + } + return makeWithoutCaching(name); + } + + private static final Map PRIMITIVE_TYPE_TO_WRAPPER_TYPE_MAP = Maps.of( + boolean_TYPE, Boolean_TYPE, + byte_TYPE, Byte_TYPE, + char_TYPE, Character_TYPE, + short_TYPE, Short_TYPE, + int_TYPE, Integer_TYPE, + long_TYPE, Long_TYPE, + float_TYPE, Float_TYPE, + double_TYPE, Double_TYPE, + VOID_TYPE, void_WRAPPER_TYPE + ); + + /** + * Creates a ClassNode containing the wrapper of a ClassNode + * of primitive type. Any ClassNode representing a primitive + * type should be created using the predefined types used in + * class. The method will check the parameter for known + * references of ClassNode representing a primitive type. If + * Reference is found, then a ClassNode will be contained that + * represents the wrapper class. For example for boolean, the + * wrapper class is java.lang.Boolean. + *

+ * If the parameter is no primitive type, the redirected + * ClassNode will be returned + * + * @param cn the ClassNode containing a possible primitive type + * @see #make(Class) + * @see #make(String) + */ + public static ClassNode getWrapper(ClassNode cn) { + cn = cn.redirect(); + if (!isPrimitiveType(cn)) return cn; + + ClassNode result = PRIMITIVE_TYPE_TO_WRAPPER_TYPE_MAP.get(cn); + + if (null != result) { + return result; + } + + return cn; + } + + private static final Map WRAPPER_TYPE_TO_PRIMITIVE_TYPE_MAP = Maps.inverse(PRIMITIVE_TYPE_TO_WRAPPER_TYPE_MAP); + + public static ClassNode getUnwrapper(ClassNode cn) { + cn = cn.redirect(); + if (isPrimitiveType(cn)) return cn; + + ClassNode result = WRAPPER_TYPE_TO_PRIMITIVE_TYPE_MAP.get(cn); + + if (null != result) { + return result; + } + + return cn; + } + + + /** + * Test to determine if a ClassNode is a primitive type. + * Note: this only works for ClassNodes created using a + * predefined ClassNode + * + * @param cn the ClassNode containing a possible primitive type + * @return true if the ClassNode is a primitive type + * @see #make(Class) + * @see #make(String) + */ + public static boolean isPrimitiveType(ClassNode cn) { + return TypeDescriptionUtil.isPrimitiveType(cn); + } + + /** + * Test to determine if a ClassNode is a type belongs to the list of types which + * are allowed to initialize constants directly in bytecode instead of using <cinit> + *

+ * Note: this only works for ClassNodes created using a + * predefined ClassNode + * + * @param cn the ClassNode to be tested + * @return true if the ClassNode is of int, float, long, double or String type + * @see #make(Class) + * @see #make(String) + */ + public static boolean isStaticConstantInitializerType(ClassNode cn) { + return cn == int_TYPE || + cn == float_TYPE || + cn == long_TYPE || + cn == double_TYPE || + cn == STRING_TYPE || + // the next items require conversion to int when initializing + cn == byte_TYPE || + cn == char_TYPE || + cn == short_TYPE; + } + + public static boolean isNumberType(ClassNode cn) { + return cn == Byte_TYPE || + cn == Short_TYPE || + cn == Integer_TYPE || + cn == Long_TYPE || + cn == Float_TYPE || + cn == Double_TYPE || + cn == byte_TYPE || + cn == short_TYPE || + cn == int_TYPE || + cn == long_TYPE || + cn == float_TYPE || + cn == double_TYPE; + } + + public static ClassNode makeReference() { + return REFERENCE_TYPE.getPlainNodeReference(); + } + + public static boolean isCachedType(ClassNode type) { + for (ClassNode cachedType : types) { + if (cachedType == type) return true; + } + return false; + } + + static class ClassHelperCache { + static ManagedConcurrentMap> classCache = new ManagedConcurrentMap>(ReferenceBundle.getWeakBundle()); + } + + public static boolean isSAMType(ClassNode type) { + return findSAM(type) != null; + } + + public static boolean isFunctionalInterface(ClassNode type) { + // Functional interface must be an interface at first, or the following exception will occur: + // java.lang.invoke.LambdaConversionException: Functional interface SamCallable is not an interface + return type.isInterface() && isSAMType(type); + } + + /** + * Returns the single abstract method of a class node, if it is a SAM type, or null otherwise. + * + * @param type a type for which to search for a single abstract method + * @return the method node if type is a SAM type, null otherwise + */ + public static MethodNode findSAM(ClassNode type) { + if (!Modifier.isAbstract(type.getModifiers())) return null; + if (type.isInterface()) { + List methods; + if (type.isInterface()) { + // e.g. BinaryOperator extends BiFunction, BinaryOperator contains no abstract method, but it is really a SAM + methods = type.redirect().getAllDeclaredMethods(); + } else { + methods = type.getMethods(); + } + + MethodNode found = null; + for (MethodNode mi : methods) { + // ignore methods, that are not abstract and from Object + if (!Modifier.isAbstract(mi.getModifiers())) continue; + // ignore trait methods which have a default implementation + if (Traits.hasDefaultImplementation(mi)) continue; + if (mi.getDeclaringClass().equals(OBJECT_TYPE)) continue; + if (OBJECT_TYPE.getDeclaredMethod(mi.getName(), mi.getParameters()) != null) continue; + + // we have two methods, so no SAM + if (found != null) return null; + found = mi; + } + return found; + + } else { + + List methods = type.getAbstractMethods(); + MethodNode found = null; + if (methods != null) { + for (MethodNode mi : methods) { + if (!hasUsableImplementation(type, mi)) { + if (found != null) return null; + found = mi; + } + } + } + return found; + } + } + + private static boolean hasUsableImplementation(ClassNode c, MethodNode m) { + if (c == m.getDeclaringClass()) return false; + MethodNode found = c.getDeclaredMethod(m.getName(), m.getParameters()); + if (found == null) return false; + int asp = found.getModifiers() & ABSTRACT_STATIC_PRIVATE; + int visible = found.getModifiers() & VISIBILITY; + if (visible != 0 && asp == 0) return true; + if (c.equals(OBJECT_TYPE)) return false; + return hasUsableImplementation(c.getSuperClass(), m); + } + + /** + * Returns a super class or interface for a given class depending on a given target. + * If the target is no super class or interface, then null will be returned. + * For a non-primitive array type, returns an array of the componentType's super class + * or interface if the target is also an array. + * + * @param clazz the start class + * @param goalClazz the goal class + * @return the next super class or interface + */ + public static ClassNode getNextSuperClass(ClassNode clazz, ClassNode goalClazz) { + if (clazz.isArray()) { + if (!goalClazz.isArray()) return null; + ClassNode cn = getNextSuperClass(clazz.getComponentType(), goalClazz.getComponentType()); + if (cn != null) cn = cn.makeArray(); + return cn; + } + + if (!goalClazz.isInterface()) { + if (clazz.isInterface()) { + if (OBJECT_TYPE.equals(clazz)) return null; + return OBJECT_TYPE; + } else { + return clazz.getUnresolvedSuperClass(); + } + } + + ClassNode[] interfaces = clazz.getUnresolvedInterfaces(); + for (ClassNode anInterface : interfaces) { + if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(anInterface, goalClazz)) { + return anInterface; + } + } + //none of the interfaces here match, so continue with super class + return clazz.getUnresolvedSuperClass(); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassNode.java new file mode 100644 index 0000000000..6e820f6a91 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ClassNode.java @@ -0,0 +1,1645 @@ +/* + * 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.ast; + +import groovy.lang.groovydoc.Groovydoc; +import groovy.lang.groovydoc.GroovydocHolder; +import org.apache.groovy.ast.tools.ClassNodeUtils; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.tools.ParameterUtils; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.transform.ASTTransformation; +import org.codehaus.groovy.transform.GroovyASTTransformation; +import org.codehaus.groovy.vmplugin.VMPluginFactory; +import groovyjarjarasm.asm.Opcodes; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +/** + * Represents a class in the AST. + *

+ * A ClassNode should be created using the methods in ClassHelper. + * This ClassNode may be used to represent a class declaration or + * any other type. This class uses a proxy mechanism allowing to + * create a class for a plain name at AST creation time. In another + * phase of the compiler the real ClassNode for the plain name may be + * found. To avoid the need of exchanging this ClassNode with an + * instance of the correct ClassNode the correct ClassNode is set as + * redirect. Most method calls are then redirected to that ClassNode. + *

+ * There are three types of ClassNodes: + *

    + *
  1. Primary ClassNodes:
    + * A primary ClassNode is one where we have a source representation + * which is to be compiled by Groovy and which we have an AST for. + * The groovy compiler will output one class for each such ClassNode + * that passes through AsmBytecodeGenerator... not more, not less. + * That means for example Closures become such ClassNodes too at + * some point. + *
  2. ClassNodes create through different sources (typically created + * from a java.lang.reflect.Class object):
    + * The compiler will not output classes from these, the methods + * usually do not contain bodies. These kind of ClassNodes will be + * used in different checks, but not checks that work on the method + * bodies. For example if such a ClassNode is a super class to a primary + * ClassNode, then the abstract method test and others will be done + * with data based on these. Theoretically it is also possible to mix both + * (1 and 2) kind of classes in a hierarchy, but this probably works only + * in the newest Groovy versions. Such ClassNodes normally have to + * isResolved() returning true without having a redirect.In the Groovy + * compiler the only version of this, that exists, is a ClassNode created + * through a Class instance + *
  3. Labels:
    + * ClassNodes created through ClassHelper.makeWithoutCaching. They + * are place holders, its redirect points to the real structure, which can + * be a label too, but following all redirects it should end with a ClassNode + * from one of the other two categories. If ResolveVisitor finds such a + * node, it tries to set the redirects. Any such label created after + * ResolveVisitor has done its work needs to have a redirect pointing to + * case 1 or 2. If not the compiler may react strange... this can be considered + * as a kind of dangling pointer. + *
+ * Note: the redirect mechanism is only allowed for classes + * that are not primary ClassNodes. Typically this is done for classes + * created by name only. The redirect itself can be any type of ClassNode. + *

+ * To describe generic type signature see {@link #getGenericsTypes()} and + * {@link #setGenericsTypes(GenericsType[])}. These methods are not proxied, + * they describe the type signature used at the point of declaration or the + * type signatures provided by the class. If the type signatures provided + * by the class are needed, then a call to {@link #redirect()} will help. + * + * @see org.codehaus.groovy.ast.ClassHelper + */ +public class ClassNode extends AnnotatedNode implements Opcodes, GroovydocHolder { + // GRECLIPSE private->package + static class MapOfLists { + // GRECLIPSE private->protected + protected Map> map; + + public List get(Object key) { + return map == null ? null : map.get(key); + } + + public List getNotNull(Object key) { + List ret = get(key); + if (ret==null) ret = Collections.emptyList(); + return ret; + } + + public void put(Object key, MethodNode value) { + if (map == null) { + map = new LinkedHashMap>(); + } + if (map.containsKey(key)) { + get(key).add(value); + } else { + List list = new ArrayList(2); + list.add(value); + map.put(key, list); + } + } + + public void remove(Object key, MethodNode value) { + get(key).remove(value); + } + } + + public static final ClassNode[] EMPTY_ARRAY = new ClassNode[0]; + public static final ClassNode THIS = new ClassNode(Object.class); + public static final ClassNode SUPER = new ClassNode(Object.class); + + private String name; + private int modifiers; + private boolean syntheticPublic; + private ClassNode[] interfaces; + private MixinNode[] mixins; + private List constructors; + private List objectInitializers; + // GRECLIPSE private->protected + protected MapOfLists methods; + private List methodsList; + private LinkedList fields; + private List properties; + private Map fieldIndex; + private ModuleNode module; + private CompileUnit compileUnit; + private boolean staticClass = false; + private boolean scriptBody = false; + private boolean script; + private ClassNode superClass; + protected boolean isPrimaryNode; + protected List innerClasses; + + // GRECLIPSE add + private int bitflags = 0x0000; + private static final int BIT_INCONSISTENT_HIERARCHY = 0x0001; + + public boolean hasInconsistentHierarchy() { + return ((redirect().bitflags) & BIT_INCONSISTENT_HIERARCHY) != 0; + } + + public void setHasInconsistentHierarchy(boolean b) { + if (b) { + redirect().bitflags |= BIT_INCONSISTENT_HIERARCHY; + } else { + redirect().bitflags &= ~BIT_INCONSISTENT_HIERARCHY; + } + } + // GRECLIPSE end + + /** + * The ASTTransformations to be applied to the Class + */ + private Map, Set>> transformInstances; + + // use this to synchronize access for the lazy init + protected final Object lazyInitLock = new Object(); + + // clazz!=null when resolved + protected Class clazz; + // only false when this classNode is constructed from a class + // GRECLIPSE private->protected + protected volatile boolean lazyInitDone=true; + // not null if if the ClassNode is an array + // GRECLIPSE private->protected + protected ClassNode componentType = null; + // if not null this instance is handled as proxy + // for the redirect + // GRECLIPSE private->protected + protected ClassNode redirect=null; + // flag if the classes or its members are annotated + private boolean annotated; + + // type spec for generics + // GRECLIPSE private->protected + protected GenericsType[] genericsTypes=null; + private boolean usesGenerics=false; + + // if set to true the name getGenericsTypes consists + // of 1 element describing the name of the placeholder + private boolean placeholder; + + /** + * Returns the ClassNode this ClassNode is redirecting to. + */ + public ClassNode redirect() { + if (redirect==null) return this; + return redirect.redirect(); + } + + /** + * Sets this instance as proxy for the given ClassNode. + * @param cn the class to redirect to. If set to null the redirect will be removed + */ + public void setRedirect(ClassNode cn) { + if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+")."); + if (cn!=null) cn = cn.redirect(); + if (cn==this) return; + redirect = cn; + } + + /** + * Returns a ClassNode representing an array of the class + * represented by this ClassNode + */ + public ClassNode makeArray() { + if (redirect!=null) { + ClassNode res = redirect().makeArray(); + res.componentType = this; + return res; + } + ClassNode cn; + if (clazz!=null) { + Class ret = Array.newInstance(clazz,0).getClass(); + // don't use the ClassHelper here! + cn = new ClassNode(ret,this); + } else { + cn = new ClassNode(this); + } + return cn; + } + + /** + * @return true if this instance is a primary ClassNode + */ + public boolean isPrimaryClassNode() { + return redirect().isPrimaryNode || (componentType != null && componentType.isPrimaryClassNode()); + } + + /* + * Constructor used by makeArray() if no real class is available + */ + // GRECLIPSE private->public + public ClassNode(ClassNode componentType) { + // GRECLIPSE edit + this(/*componentType.getName()+"[]"*/computeArrayName(componentType), ACC_PUBLIC, ClassHelper.OBJECT_TYPE); + // GRECLIPSE end + this.componentType = componentType.redirect(); + isPrimaryNode=false; + } + + // GRECLIPSE add + /** + * For a given component type compute the right 'name'. Rules are as follows: + *

    + *
  • primitive component types: result is a name like "[I" or "[Z" + *
  • array component types: follow the pattern for the component, if it starts '[' add another leading. if it ends with '[]' then do that + *
  • reference types: Create [Lcom.foo.Bar; - this isn't quite right really as it should have '/' in... + *
+ */ + public static String computeArrayName(ClassNode componentType) { + String n = componentType.getName(); + if (componentType.isPrimitive()) { + int len = n.length(); + if (len == 7) { + return "[Z"; //boolean + } else if (len == 6) { + return "[D"; //double + } else if (len == 5) { + if (n.charAt(0) == 'f') { + return "[F"; //float + } else { + return "[S"; //short + } + } else if (len == 4) { + switch (n.charAt(0)) { + case 'b': return "[B"; //byte + case 'c': return "[C"; //char + default: return "[J"; //long + } + } else { + return "[I"; //int + } + } else if (componentType.isArray()) { + // follow the pattern: + if (n.charAt(0) == '[') { + return new StringBuilder("[").append(n).toString(); + } else { + return new StringBuilder(n).append("[]").toString(); + } + } else { + // reference type: + return new StringBuilder("[L").append(componentType.getName()).append(";").toString(); + } + } + // GRECLIPSE end + + /* + * Constructor used by makeArray() if a real class is available + */ + // GRECLIPSE private->public + public ClassNode(Class c, ClassNode componentType) { + this(c); + this.componentType = componentType; + isPrimaryNode=false; + } + + /** + * Creates a ClassNode from a real class. The resulting + * ClassNode will not be a primary ClassNode. + */ + public ClassNode(Class c) { + this(c.getName(), c.getModifiers(), null, null, MixinNode.EMPTY_ARRAY); + clazz=c; + lazyInitDone=false; + CompileUnit cu = getCompileUnit(); + if (cu!=null) cu.addClass(this); + isPrimaryNode=false; + } + + /** + * The complete class structure will be initialized only when really + * needed to avoid having too many objects during compilation + */ + // GRECLIPSE private->public + public void lazyClassInit() { + if (lazyInitDone) return; + synchronized (lazyInitLock) { + if (redirect!=null) { + throw new GroovyBugError("lazyClassInit called on a proxy ClassNode, that must not happen."+ + "A redirect() call is missing somewhere!"); + } + if (lazyInitDone) return; + VMPluginFactory.getPlugin().configureClassNode(compileUnit,this); + lazyInitDone = true; + } + } + + // added to track the enclosing method for local inner classes + private MethodNode enclosingMethod = null; + + public MethodNode getEnclosingMethod() { + return redirect().enclosingMethod; + } + + public void setEnclosingMethod(MethodNode enclosingMethod) { + redirect().enclosingMethod = enclosingMethod; + } + + /** + * Indicates that this class has been "promoted" to public by + * Groovy when in fact there was no public modifier explicitly + * in the source code. I.e. it remembers that it has applied + * Groovy's "public classes by default" rule.This property is + * typically only of interest to AST transform writers. + * + * @return true if this class is public but had no explicit public modifier + */ + public boolean isSyntheticPublic() { + return syntheticPublic; + } + + public void setSyntheticPublic(boolean syntheticPublic) { + this.syntheticPublic = syntheticPublic; + } + + /** + * @param name is the full name of the class + * @param modifiers the modifiers, + * @param superClass the base class name - use "java.lang.Object" if no direct + * base class + * @see org.objectweb.asm.Opcodes + */ + public ClassNode(String name, int modifiers, ClassNode superClass) { + this(name, modifiers, superClass, EMPTY_ARRAY, MixinNode.EMPTY_ARRAY); + } + + /** + * @param name is the full name of the class + * @param modifiers the modifiers, + * @param superClass the base class name - use "java.lang.Object" if no direct + * base class + * @param interfaces the interfaces for this class + * @param mixins the mixins for this class + * @see org.objectweb.asm.Opcodes + */ + public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) { + this.name = name; + this.modifiers = modifiers; + this.superClass = superClass; + this.interfaces = interfaces; + this.mixins = mixins; + isPrimaryNode = true; + if (superClass!=null) { + usesGenerics = superClass.isUsingGenerics(); + } + if (!usesGenerics && interfaces!=null) { + for (ClassNode anInterface : interfaces) { + usesGenerics = usesGenerics || anInterface.isUsingGenerics(); + if (usesGenerics) break; + } + } + this.methods = new MapOfLists(); + this.methodsList = Collections.emptyList(); + } + + /** + * Sets the superclass of this ClassNode + */ + public void setSuperClass(ClassNode superClass) { + redirect().superClass = superClass; + } + + /** + * @return the list of FieldNode's associated with this ClassNode + */ + public List getFields() { + if (redirect!=null) return redirect().getFields(); + lazyClassInit(); + if (fields == null) + fields = new LinkedList(); + return fields; + } + + /** + * @return the array of interfaces which this ClassNode implements + */ + public ClassNode[] getInterfaces() { + // GRECLIPSE add + if (hasInconsistentHierarchy()) return EMPTY_ARRAY; + // GRECLIPSE end + if (redirect!=null) return redirect().getInterfaces(); + lazyClassInit(); + return interfaces; + } + + public void setInterfaces(ClassNode[] interfaces) { + if (redirect!=null) { + redirect().setInterfaces(interfaces); + } else { + this.interfaces = interfaces; + } + } + + /** + * @return the array of mixins associated with this ClassNode + */ + public MixinNode[] getMixins() { + return redirect().mixins; + } + + public void setMixins(MixinNode[] mixins) { + redirect().mixins = mixins; + } + + /** + * @return the list of methods associated with this ClassNode + */ + public List getMethods() { + if (redirect!=null) return redirect().getMethods(); + lazyClassInit(); + return methodsList; + } + + /** + * @return the list of abstract methods associated with this + * ClassNode or null if there are no such methods + */ + public List getAbstractMethods() { + List result = new ArrayList(3); + for (MethodNode method : getDeclaredMethodsMap().values()) { + if (method.isAbstract()) { + result.add(method); + } + } + + if (result.isEmpty()) { + return null; + } else { + return result; + } + } + + public List getAllDeclaredMethods() { + return new ArrayList(getDeclaredMethodsMap().values()); + } + + public Set getAllInterfaces() { + Set res = new LinkedHashSet(); + getAllInterfaces(res); + return res; + } + + private void getAllInterfaces(Set res) { + if (isInterface()) + res.add(this); + + for (ClassNode anInterface : getInterfaces()) { + res.add(anInterface); + anInterface.getAllInterfaces(res); + } + } + + public Map getDeclaredMethodsMap() { + Map result = ClassNodeUtils.getDeclaredMethodsFromSuper(this); + ClassNodeUtils.addDeclaredMethodsFromInterfaces(this, result); + + // And add in the methods implemented in this class. + for (MethodNode method : getMethods()) { + String sig = method.getTypeDescriptor(); + result.put(sig, method); + } + return result; + } + + public String getName() { + return redirect().name; + } + + public String getUnresolvedName() { + return name; + } + + public String setName(String name) { + return redirect().name=name; + } + + public int getModifiers() { + return redirect().modifiers; + } + + public void setModifiers(int modifiers) { + redirect().modifiers = modifiers; + } + + public List getProperties() { + final ClassNode r = redirect(); + // GRECLIPSE add + if (r != this) return r.getProperties(); + // GRECLIPSE end + if (r.properties == null) + r.properties = new ArrayList(); + return r.properties; + } + + public List getDeclaredConstructors() { + if (redirect != null) return redirect().getDeclaredConstructors(); + lazyClassInit(); + if (constructors == null) + constructors = new ArrayList(); + return constructors; + } + + /** + * Finds a constructor matching the given parameters in this class. + * + * @return the constructor matching the given parameters or null + */ + public ConstructorNode getDeclaredConstructor(Parameter[] parameters) { + for (ConstructorNode method : getDeclaredConstructors()) { + if (parametersEqual(method.getParameters(), parameters)) { + return method; + } + } + return null; + } + + public void removeConstructor(ConstructorNode node) { + redirect().constructors.remove(node); + } + + public ModuleNode getModule() { + return redirect().module; + } + + public PackageNode getPackage() { + return getModule() == null ? null : getModule().getPackage(); + } + + public void setModule(ModuleNode module) { + redirect().module = module; + if (module != null) { + redirect().compileUnit = module.getUnit(); + } + } + + public void addField(FieldNode node) { + addField(node, false); + } + + public void addFieldFirst(FieldNode node) { + addField(node, true); + } + + private void addField(FieldNode node, boolean isFirst) { + final ClassNode r = redirect(); + node.setDeclaringClass(r); + node.setOwner(r); + if (r.fields == null) + r.fields = new LinkedList<>(); + if (r.fieldIndex == null) + r.fieldIndex = new LinkedHashMap<>(); + + if (isFirst) + r.fields.addFirst(node); + else + r.fields.add(node); + + r.fieldIndex.put(node.getName(), node); + } + + public Map getFieldIndex() { + return fieldIndex; + } + + public void addProperty(PropertyNode node) { + // GRECLIPSE add + getProperties().add(node); + // GRECLIPSE end + node.setDeclaringClass(redirect()); + FieldNode field = node.getField(); + addField(field); + /* GRECLIPSE edit + final ClassNode r = redirect(); + if (r.properties == null) + r.properties = new ArrayList(); + r.properties.add(node); + */ + } + + public PropertyNode addProperty(String name, + int modifiers, + ClassNode type, + Expression initialValueExpression, + Statement getterBlock, + Statement setterBlock) { + for (PropertyNode pn : getProperties()) { + if (pn.getName().equals(name)) { + if (pn.getInitialExpression() == null && initialValueExpression != null) + pn.getField().setInitialValueExpression(initialValueExpression); + + if (pn.getGetterBlock() == null && getterBlock != null) + pn.setGetterBlock(getterBlock); + + if (pn.getSetterBlock() == null && setterBlock != null) + pn.setSetterBlock(setterBlock); + + return pn; + } + } + PropertyNode node = + new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock); + addProperty(node); + return node; + } + + public boolean hasProperty(String name) { + return getProperty(name) != null; + } + + public PropertyNode getProperty(String name) { + for (PropertyNode pn : getProperties()) { + if (pn.getName().equals(name)) return pn; + } + return null; + } + + public void addConstructor(ConstructorNode node) { + node.setDeclaringClass(this); + final ClassNode r = redirect(); + if (r.constructors == null) + r.constructors = new ArrayList(); + r.constructors.add(node); + } + + public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) { + ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code); + addConstructor(node); + return node; + } + + public void addMethod(MethodNode node) { + node.setDeclaringClass(this); + ClassNode base = redirect(); + if (base.methodsList.isEmpty()) { + base.methodsList = new ArrayList(); + } + base.methodsList.add(node); + base.methods.put(node.getName(), node); + } + + public void removeMethod(MethodNode node) { + ClassNode base = redirect(); + if (!base.methodsList.isEmpty()) { + base.methodsList.remove(node); + } + base.methods.remove(node.getName(), node); + } + + /** + * If a method with the given name and parameters is already defined then it is returned + * otherwise the given method is added to this node. This method is useful for + * default method adding like getProperty() or invokeMethod() where there may already + * be a method defined in a class and so the default implementations should not be added + * if already present. + */ + public MethodNode addMethod(String name, + int modifiers, + ClassNode returnType, + Parameter[] parameters, + ClassNode[] exceptions, + Statement code) { + MethodNode other = getDeclaredMethod(name, parameters); + // let's not add duplicate methods + if (other != null) { + return other; + } + MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code); + addMethod(node); + return node; + } + + /** + * @see #getDeclaredMethod(String, Parameter[]) + */ + public boolean hasDeclaredMethod(String name, Parameter[] parameters) { + MethodNode other = getDeclaredMethod(name, parameters); + return other != null; + } + + /** + * @see #getMethod(String, Parameter[]) + */ + public boolean hasMethod(String name, Parameter[] parameters) { + MethodNode other = getMethod(name, parameters); + return other != null; + } + + /** + * Adds a synthetic method as part of the compilation process + */ + public MethodNode addSyntheticMethod(String name, + int modifiers, + ClassNode returnType, + Parameter[] parameters, + ClassNode[] exceptions, + Statement code) { + MethodNode answer = addMethod(name, modifiers|ACC_SYNTHETIC, returnType, parameters, exceptions, code); + answer.setSynthetic(true); + return answer; + } + + public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) { + FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue); + addField(node); + return node; + } + + public FieldNode addFieldFirst(String name, int modifiers, ClassNode type, Expression initialValue) { + FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue); + addFieldFirst(node); + return node; + } + + public void addInterface(ClassNode type) { + // let's check if it already implements an interface + boolean skip = false; + ClassNode[] interfaces = redirect().interfaces; + for (ClassNode existing : interfaces) { + if (type.equals(existing)) { + skip = true; + break; + } + } + if (!skip) { + ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1]; + System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length); + newInterfaces[interfaces.length] = type; + redirect().interfaces = newInterfaces; + } + } + + public boolean equals(Object o) { + if (redirect!=null) return redirect().equals(o); + if (!(o instanceof ClassNode)) return false; + ClassNode cn = (ClassNode) o; + return (cn.getText().equals(getText())); + } + + public int hashCode() { + if (redirect!=null) return redirect().hashCode(); + return getName().hashCode(); + } + + public void addMixin(MixinNode mixin) { + // let's check if it already uses a mixin + MixinNode[] mixins = redirect().mixins; + boolean skip = false; + for (MixinNode existing : mixins) { + if (mixin.equals(existing)) { + skip = true; + break; + } + } + if (!skip) { + MixinNode[] newMixins = new MixinNode[mixins.length + 1]; + System.arraycopy(mixins, 0, newMixins, 0, mixins.length); + newMixins[mixins.length] = mixin; + redirect().mixins = newMixins; + } + } + + /** + * Finds a field matching the given name in this class. + * + * @param name the name of the field of interest + * @return the method matching the given name and parameters or null + */ + public FieldNode getDeclaredField(String name) { + if (redirect != null) return redirect().getDeclaredField(name); + + lazyClassInit(); + return fieldIndex == null ? null : fieldIndex.get(name); + } + + /** + * Finds a field matching the given name in this class or a parent class. + * + * @param name the name of the field of interest + * @return the method matching the given name and parameters or null + */ + public FieldNode getField(String name) { + ClassNode node = this; + while (node != null) { + FieldNode fn = node.getDeclaredField(name); + if (fn != null) return fn; + node = node.getSuperClass(); + } + return null; + } + + /** + * @return the field node on the outer class or null if this is not an + * inner class + */ + public FieldNode getOuterField(String name) { + return null; + } + + /** + * Helper method to avoid casting to inner class + */ + public ClassNode getOuterClass() { + return null; + } + + /** + * Adds a statement to the object initializer. + * + * @param statements the statement to be added + */ + public void addObjectInitializerStatements(Statement statements) { + getObjectInitializerStatements().add(statements); + } + + public List getObjectInitializerStatements() { + if (objectInitializers == null) + objectInitializers = new LinkedList(); + return objectInitializers; + } + + private MethodNode getOrAddStaticConstructorNode() { + MethodNode method = null; + List declaredMethods = getDeclaredMethods(""); + if (declaredMethods.isEmpty()) { + method = + addMethod("", ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); + method.setSynthetic(true); + } + else { + method = (MethodNode) declaredMethods.get(0); + } + return method; + } + + public void addStaticInitializerStatements(List staticStatements, boolean fieldInit) { + MethodNode method = getOrAddStaticConstructorNode(); + BlockStatement block = null; + Statement statement = method.getCode(); + if (statement == null) { + block = new BlockStatement(); + } + else if (statement instanceof BlockStatement) { + block = (BlockStatement) statement; + } + else { + block = new BlockStatement(); + block.addStatement(statement); + } + + // while anything inside a static initializer block is appended + // we don't want to append in the case we have a initialization + // expression of a static field. In that case we want to add + // before the other statements + if (!fieldInit) { + block.addStatements(staticStatements); + } else { + List blockStatements = block.getStatements(); + staticStatements.addAll(blockStatements); + blockStatements.clear(); + blockStatements.addAll(staticStatements); + } + } + + public void positionStmtsAfterEnumInitStmts(List staticFieldStatements) { + MethodNode method = getOrAddStaticConstructorNode(); + Statement statement = method.getCode(); + if (statement instanceof BlockStatement) { + BlockStatement block = (BlockStatement) statement; + // add given statements for explicitly declared static fields just after enum-special fields + // are found - the $VALUES binary expression marks the end of such fields. + List blockStatements = block.getStatements(); + ListIterator litr = blockStatements.listIterator(); + while (litr.hasNext()) { + Statement stmt = litr.next(); + if (stmt instanceof ExpressionStatement && + ((ExpressionStatement) stmt).getExpression() instanceof BinaryExpression) { + BinaryExpression bExp = (BinaryExpression) ((ExpressionStatement) stmt).getExpression(); + if (bExp.getLeftExpression() instanceof FieldExpression) { + FieldExpression fExp = (FieldExpression) bExp.getLeftExpression(); + if (fExp.getFieldName().equals("$VALUES")) { + for (Statement tmpStmt : staticFieldStatements) { + litr.add(tmpStmt); + } + } + } + } + } + } + } + + /** + * This methods returns a list of all methods of the given name + * defined in the current class + * @return the method list + * @see #getMethods(String) + */ + public List getDeclaredMethods(String name) { + if (redirect!=null) return redirect().getDeclaredMethods(name); + lazyClassInit(); + return methods.getNotNull(name); + } + + /** + * This methods creates a list of all methods with this name of the + * current class and of all super classes + * @return the methods list + * @see #getDeclaredMethods(String) + */ + public List getMethods(String name) { + List answer = new ArrayList(); + ClassNode node = this; + while (node != null) { + answer.addAll(node.getDeclaredMethods(name)); + node = node.getSuperClass(); + } + return answer; + } + + /** + * Finds a method matching the given name and parameters in this class. + * + * @return the method matching the given name and parameters or null + */ + public MethodNode getDeclaredMethod(String name, Parameter[] parameters) { + for (MethodNode method : getDeclaredMethods(name)) { + if (parametersEqual(method.getParameters(), parameters)) { + return method; + } + } + return null; + } + + /** + * Finds a method matching the given name and parameters in this class + * or any parent class. + * + * @return the method matching the given name and parameters or null + */ + public MethodNode getMethod(String name, Parameter[] parameters) { + for (MethodNode method : getMethods(name)) { + if (parametersEqual(method.getParameters(), parameters)) { + return method; + } + } + return null; + } + + /** + * @param type the ClassNode of interest + * @return true if this node is derived from the given ClassNode + */ + public boolean isDerivedFrom(ClassNode type) { + if (this.equals(ClassHelper.VOID_TYPE)) { + return type.equals(ClassHelper.VOID_TYPE); + } + if (type.equals(ClassHelper.OBJECT_TYPE)) return true; + ClassNode node = this; + while (node != null) { + if (type.equals(node)) { + return true; + } + node = node.getSuperClass(); + } + return false; + } + + /** + * @return true if this class is derived from a groovy object + * i.e. it implements GroovyObject + */ + public boolean isDerivedFromGroovyObject() { + return implementsInterface(ClassHelper.GROOVY_OBJECT_TYPE); + } + + /** + * @param classNode the class node for the interface + * @return true if this class or any base class implements the given interface + */ + public boolean implementsInterface(ClassNode classNode) { + ClassNode node = redirect(); + do { + if (node.declaresInterface(classNode)) { + return true; + } + node = node.getSuperClass(); + } + while (node != null); + return false; + } + + /** + * @param classNode the class node for the interface + * @return true if this class declares that it implements the given interface + * or if one of its interfaces extends directly or indirectly the interface + * + * NOTE: Doesn't consider an interface to implement itself. + * I think this is intended to be called on ClassNodes representing + * classes, not interfaces. + * + */ + public boolean declaresInterface(ClassNode classNode) { + ClassNode[] interfaces = redirect().getInterfaces(); + for (ClassNode cn : interfaces) { + if (cn.equals(classNode)) return true; + } + for (ClassNode cn : interfaces) { + if (cn.declaresInterface(classNode)) return true; + } + return false; + } + + /** + * @return the ClassNode of the super class of this type + */ + public ClassNode getSuperClass() { + if (!lazyInitDone && !isResolved()) { + throw new GroovyBugError("ClassNode#getSuperClass for "+getName()+" called before class resolving"); + } + // GRECLIPSE add + if (hasInconsistentHierarchy()) { + return ClassHelper.OBJECT_TYPE; + } + // GRECLIPSE end + ClassNode sn = redirect().getUnresolvedSuperClass(); + if (sn!=null) sn=sn.redirect(); + return sn; + } + + public ClassNode getUnresolvedSuperClass() { + return getUnresolvedSuperClass(true); + } + + public ClassNode getUnresolvedSuperClass(boolean useRedirect) { + // GRECLIPSE add + if (hasInconsistentHierarchy()) { + return ClassHelper.OBJECT_TYPE; + } + // GRECLIPSE end + if (!useRedirect) return superClass; + if (redirect != null) return redirect().getUnresolvedSuperClass(true); + lazyClassInit(); + return superClass; + } + + public void setUnresolvedSuperClass(ClassNode sn) { + superClass = sn; + } + + public ClassNode [] getUnresolvedInterfaces() { + return getUnresolvedInterfaces(true); + } + + public ClassNode [] getUnresolvedInterfaces(boolean useRedirect) { + // GRECLIPSE add + if (hasInconsistentHierarchy()) { + return EMPTY_ARRAY; + } + // GRECLIPSE end + if (!useRedirect) return interfaces; + if (redirect != null) return redirect().getUnresolvedInterfaces(true); + lazyClassInit(); + return interfaces; + } + + public CompileUnit getCompileUnit() { + if (redirect!=null) return redirect().getCompileUnit(); + if (compileUnit == null && module != null) { + compileUnit = module.getUnit(); + } + return compileUnit; + } + + protected void setCompileUnit(CompileUnit cu) { + if (redirect!=null) redirect().setCompileUnit(cu); + if (compileUnit!= null) compileUnit = cu; + } + + /** + * @return true if the two arrays are of the same size and have the same contents + */ + protected boolean parametersEqual(Parameter[] a, Parameter[] b) { + return ParameterUtils.parametersEqual(a, b); + } + + /** + * @return the package name of this class + */ + public String getPackageName() { + int idx = getName().lastIndexOf('.'); + if (idx > 0) { + return getName().substring(0, idx); + } + return null; + } + + public String getNameWithoutPackage() { + int idx = getName().lastIndexOf('.'); + if (idx > 0) { + return getName().substring(idx + 1); + } + return getName(); + } + + public void visitContents(GroovyClassVisitor visitor) { + // now let's visit the contents of the class + for (PropertyNode pn : getProperties()) { + visitor.visitProperty(pn); + } + + for (FieldNode fn : getFields()) { + visitor.visitField(fn); + } + + for (ConstructorNode cn : getDeclaredConstructors()) { + visitor.visitConstructor(cn); + } + + for (MethodNode mn : getMethods()) { + visitor.visitMethod(mn); + } + } + + public MethodNode getGetterMethod(String getterName) { + return getGetterMethod(getterName, true); + } + + public MethodNode getGetterMethod(String getterName, boolean searchSuperClasses) { + MethodNode getterMethod = null; + boolean booleanReturnOnly = getterName.startsWith("is"); + for (MethodNode method : getDeclaredMethods(getterName)) { + if (getterName.equals(method.getName()) + && ClassHelper.VOID_TYPE!=method.getReturnType() + && method.getParameters().length == 0 + && (!booleanReturnOnly || ClassHelper.Boolean_TYPE.equals(ClassHelper.getWrapper(method.getReturnType())))) { + // GROOVY-7363: There can be multiple matches for a getter returning a generic parameter type, due to + // the generation of a bridge method. The real getter is really the non-bridge, non-synthetic one as it + // has the most specific and exact return type of the two. Picking the bridge method results in loss of + // type information, as it down-casts the return type to the lower bound of the generic parameter. + if (getterMethod == null || getterMethod.isSynthetic()) { + getterMethod = method; + } + } + } + if (getterMethod != null) return getterMethod; + if (searchSuperClasses) { + ClassNode parent = getSuperClass(); + if (parent != null) return parent.getGetterMethod(getterName); + } + return null; + } + + public MethodNode getSetterMethod(String setterName) { + return getSetterMethod(setterName, true); + } + + public MethodNode getSetterMethod(String setterName, boolean voidOnly) { + for (MethodNode method : getDeclaredMethods(setterName)) { + if (setterName.equals(method.getName()) + && (!voidOnly || ClassHelper.VOID_TYPE==method.getReturnType()) + && method.getParameters().length == 1) { + return method; + } + } + ClassNode parent = getSuperClass(); + if (parent!=null) return parent.getSetterMethod(setterName, voidOnly); + return null; + } + + /** + * Is this class declared in a static method (such as a closure / inner class declared in a static method) + */ + public boolean isStaticClass() { + return redirect().staticClass; + } + + public void setStaticClass(boolean staticClass) { + redirect().staticClass = staticClass; + } + + /** + * @return Returns true if this inner class or closure was declared inside a script body + */ + public boolean isScriptBody() { + return redirect().scriptBody; + } + + public void setScriptBody(boolean scriptBody) { + redirect().scriptBody = scriptBody; + } + + public boolean isScript() { + return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE); + } + + public void setScript(boolean script) { + redirect().script = script; + } + + public String toString() { + return toString(true); + } + + public String toString(boolean showRedirect) { + if (isArray()) { + return componentType.toString(showRedirect)+"[]"; + } + StringBuilder ret = new StringBuilder(getName()); + if (placeholder) ret = new StringBuilder(getUnresolvedName()); + if (!placeholder && genericsTypes != null) { + ret.append(" <"); + for (int i = 0; i < genericsTypes.length; i++) { + if (i != 0) ret.append(", "); + GenericsType genericsType = genericsTypes[i]; + ret.append(genericTypeAsString(genericsType)); + } + ret.append(">"); + } + if (redirect != null && showRedirect) { + ret.append(" -> ").append(redirect().toString()); + } + return ret.toString(); + } + + /** + * This exists to avoid a recursive definition of toString. The default toString + * in GenericsType calls ClassNode.toString(), which calls GenericsType.toString(), etc. + * @param genericsType + * @return the string representing the generic type + */ + private String genericTypeAsString(GenericsType genericsType) { + StringBuilder ret = new StringBuilder(genericsType.getName()); + if (genericsType.getUpperBounds() != null) { + ret.append(" extends "); + for (int i = 0; i < genericsType.getUpperBounds().length; i++) { + ClassNode classNode = genericsType.getUpperBounds()[i]; + if (classNode.equals(this)) { + ret.append(classNode.getName()); + } else { + ret.append(classNode.toString(false)); + } + if (i + 1 < genericsType.getUpperBounds().length) ret.append(" & "); + } + } else if (genericsType.getLowerBound() !=null) { + ClassNode classNode = genericsType.getLowerBound(); + if (classNode.equals(this)) { + ret.append(" super ").append(classNode.getName()); + } else { + ret.append(" super ").append(classNode); + } + } + return ret.toString(); + } + + /** + * Returns true if the given method has a possibly matching instance method with the given name and arguments. + * + * @param name the name of the method of interest + * @param arguments the arguments to match against + * @return true if a matching method was found + */ + public boolean hasPossibleMethod(String name, Expression arguments) { + int count = 0; + + if (arguments instanceof TupleExpression) { + TupleExpression tuple = (TupleExpression) arguments; + // TODO this won't strictly be true when using list expansion in argument calls + count = tuple.getExpressions().size(); + } + ClassNode node = this; + do { + for (MethodNode method : getMethods(name)) { + if (method.getParameters().length == count && !method.isStatic()) { + return true; + } + } + node = node.getSuperClass(); + } + while (node != null); + return false; + } + + public MethodNode tryFindPossibleMethod(String name, Expression arguments) { + int count = 0; + + if (arguments instanceof TupleExpression) { + TupleExpression tuple = (TupleExpression) arguments; + // TODO this won't strictly be true when using list expansion in argument calls + count = tuple.getExpressions().size(); + } else + return null; + + MethodNode res = null; + ClassNode node = this; + TupleExpression args = (TupleExpression) arguments; + do { + for (MethodNode method : node.getMethods(name)) { + if (method.getParameters().length == count) { + boolean match = true; + for (int i = 0; i != count; ++i) + if (!args.getType().isDerivedFrom(method.getParameters()[i].getType())) { + match = false; + break; + } + + if (match) { + if (res == null) + res = method; + else { + if (res.getParameters().length != count) + return null; + if (node.equals(this)) + return null; + + match = true; + for (int i = 0; i != count; ++i) + if (!res.getParameters()[i].getType().equals(method.getParameters()[i].getType())) { + match = false; + break; + } + if (!match) + return null; + } + } + } + } + node = node.getSuperClass(); + } + while (node != null); + + return res; + } + + /** + * Returns true if the given method has a possibly matching static method with the given name and arguments. + * + * @param name the name of the method of interest + * @param arguments the arguments to match against + * @return true if a matching method was found + */ + public boolean hasPossibleStaticMethod(String name, Expression arguments) { + return ClassNodeUtils.hasPossibleStaticMethod(this, name, arguments, false); + } + + public boolean isInterface() { + return (getModifiers() & ACC_INTERFACE) != 0; + } + + public boolean isAbstract() { + return (getModifiers() & ACC_ABSTRACT) != 0; + } + + public boolean isResolved() { + if (clazz != null) return true; + if (redirect != null) return redirect.isResolved(); + return componentType != null && componentType.isResolved(); + } + + // GRECLIPSE hack -- rework (remove?) this if it behaves as an approach + // enables the redirect to be a JDTClassNode and satisfy 'isResolved()' + public boolean isReallyResolved() { + return false; + } + // GRECLIPSE end + + public boolean isArray() { + return componentType != null; + } + + public ClassNode getComponentType() { + return componentType; + } + + /** + * Returns the concrete class this classnode relates to. However, this method + * is inherently unsafe as it may return null depending on the compile phase you are + * using. AST transformations should never use this method directly, but rather obtain + * a new class node using {@link #getPlainNodeReference()}. + * @return the class this classnode relates to. May return null. + */ + public Class getTypeClass() { + if (clazz != null) return clazz; + if (redirect != null) return redirect.getTypeClass(); + + ClassNode component = redirect().componentType; + if (component!=null && component.isResolved()){ + return Array.newInstance(component.getTypeClass(), 0).getClass(); + } + // GRECLIPSE add + if (redirect().getClass().getName().endsWith("JDTClassNode")) { + return redirect().getTypeClass(); + } + // GRECLIPSE end + throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set "); + } + + public boolean hasPackageName(){ + return redirect().name.indexOf('.')>0; + } + + /** + * Marks if the current class uses annotations or not + * @param flag + */ + public void setAnnotated(boolean flag) { + this.annotated = flag; + } + + public boolean isAnnotated() { + return this.annotated; + } + + public GenericsType[] getGenericsTypes() { + return genericsTypes; + } + + public void setGenericsTypes(GenericsType[] genericsTypes) { + usesGenerics = usesGenerics || genericsTypes!=null; + this.genericsTypes = genericsTypes; + } + + public void setGenericsPlaceHolder(boolean b) { + usesGenerics = usesGenerics || b; + placeholder = b; + } + + public boolean isGenericsPlaceHolder() { + return placeholder; + } + + public boolean isUsingGenerics() { + return usesGenerics; + } + + public void setUsingGenerics(boolean b) { + usesGenerics = b; + } + + public ClassNode getPlainNodeReference() { + if (ClassHelper.isPrimitiveType(this)) return this; + ClassNode n = new ClassNode(name, modifiers, superClass, null, null); + n.isPrimaryNode = false; + n.setRedirect(redirect()); + if (isArray()) { + n.componentType = redirect().getComponentType(); + } + return n; + } + + public boolean isAnnotationDefinition() { + return redirect().isPrimaryNode && + isInterface() && + (getModifiers() & ACC_ANNOTATION) != 0; + } + + public List getAnnotations() { + if (redirect!=null) return redirect.getAnnotations(); + lazyClassInit(); + return super.getAnnotations(); + } + + public List getAnnotations(ClassNode type) { + if (redirect!=null) return redirect.getAnnotations(type); + lazyClassInit(); + return super.getAnnotations(type); + } + + public void addTransform(Class transform, ASTNode node) { + GroovyASTTransformation annotation = transform.getAnnotation(GroovyASTTransformation.class); + if (annotation == null) return; + + Set nodes = getTransformInstances().get(annotation.phase()).get(transform); + if (nodes == null) { + nodes = new LinkedHashSet(); + getTransformInstances().get(annotation.phase()).put(transform, nodes); + } + nodes.add(node); + } + + public Map, Set> getTransforms(CompilePhase phase) { + return getTransformInstances().get(phase); + } + + public void renameField(String oldName, String newName) { + ClassNode r = redirect(); + if (r.fieldIndex == null) + r.fieldIndex = new LinkedHashMap(); + final Map index = r.fieldIndex; + index.put(newName, index.remove(oldName)); + } + + public void removeField(String oldName) { + ClassNode r = redirect(); + if (r.fieldIndex == null) + r.fieldIndex = new LinkedHashMap(); + final Map index = r.fieldIndex; + r.fields.remove(index.get(oldName)); + index.remove(oldName); + } + + public boolean isEnum() { + return (getModifiers() & ACC_ENUM) != 0; + } + + /** + * @return iterator of inner classes defined inside this one + */ + public Iterator getInnerClasses() { + return (innerClasses == null ? Collections.emptyList() : innerClasses).iterator(); + } + + // GRECLIPSE add + public void forgetInnerClass(InnerClassNode icn) { + if (innerClasses != null) { + innerClasses.remove(icn); // GRECLIPSE-1167 + } + } + + public String getClassInternalName() { + return (isRedirectNode() ? redirect().getClassInternalName() : null); + } + + public boolean hasClass() { + return (redirect().clazz != null); + } + + public boolean isPrimitive() { + return (clazz != null && clazz.isPrimitive()); + } + + /** + * @return true if this classnode might have inners, conservatively it says yes if it is unsure. + */ + public boolean mightHaveInners() { + ClassNode r = redirect(); + if (r.hasClass()) { + return true; + } + return (r.innerClasses != null && !r.innerClasses.isEmpty()); + } + // GRECLIPSE end + + private Map, Set>> getTransformInstances() { + if(transformInstances == null){ + transformInstances = new EnumMap, Set>>(CompilePhase.class); + for (CompilePhase phase : CompilePhase.values()) { + transformInstances.put(phase, new LinkedHashMap, Set>()); + } + } + return transformInstances; + } + + public boolean isRedirectNode() { + return redirect!=null; + } + + @Override + public String getText() { + return getName(); + } + + @Override + public Groovydoc getGroovydoc() { + return this.getNodeMetaData(DOC_COMMENT); + } + + @Override + public ClassNode getInstance() { + return this; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/CodeVisitorSupport.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/CodeVisitorSupport.java new file mode 100644 index 0000000000..eb77c9acf4 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/CodeVisitorSupport.java @@ -0,0 +1,358 @@ +/* + * 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.ast; + +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ArrayExpression; +import org.codehaus.groovy.ast.expr.AttributeExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; +import org.codehaus.groovy.ast.expr.EmptyExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.LambdaExpression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.MethodPointerExpression; +import org.codehaus.groovy.ast.expr.NotExpression; +import org.codehaus.groovy.ast.expr.PostfixExpression; +import org.codehaus.groovy.ast.expr.PrefixExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.SpreadExpression; +import org.codehaus.groovy.ast.expr.SpreadMapExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.TernaryExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.UnaryMinusExpression; +import org.codehaus.groovy.ast.expr.UnaryPlusExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.AssertStatement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.DoWhileStatement; +import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.SynchronizedStatement; +import org.codehaus.groovy.ast.stmt.ThrowStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.classgen.BytecodeExpression; + +import java.util.List; + +/** + * Abstract base class for any GroovyCodeVisitor which by default + * just walks the code and expression tree + * + * @author James Strachan + */ +public abstract class CodeVisitorSupport implements GroovyCodeVisitor { + + public void visitBlockStatement(BlockStatement block) { + for (Statement statement : block.getStatements()) { + statement.visit(this); + } + } + + public void visitForLoop(ForStatement forLoop) { + forLoop.getCollectionExpression().visit(this); + forLoop.getLoopBlock().visit(this); + } + + public void visitWhileLoop(WhileStatement loop) { + loop.getBooleanExpression().visit(this); + loop.getLoopBlock().visit(this); + } + + public void visitDoWhileLoop(DoWhileStatement loop) { + loop.getLoopBlock().visit(this); + loop.getBooleanExpression().visit(this); + } + + public void visitIfElse(IfStatement ifElse) { + ifElse.getBooleanExpression().visit(this); + ifElse.getIfBlock().visit(this); + + Statement elseBlock = ifElse.getElseBlock(); + if (elseBlock instanceof EmptyStatement) { + // dispatching to EmptyStatement will not call back visitor, + // must call our visitEmptyStatement explicitly + visitEmptyStatement((EmptyStatement) elseBlock); + } else { + elseBlock.visit(this); + } + } + + public void visitExpressionStatement(ExpressionStatement statement) { + statement.getExpression().visit(this); + } + + public void visitReturnStatement(ReturnStatement statement) { + statement.getExpression().visit(this); + } + + public void visitAssertStatement(AssertStatement statement) { + statement.getBooleanExpression().visit(this); + statement.getMessageExpression().visit(this); + } + + public void visitTryCatchFinally(TryCatchStatement statement) { + statement.getTryStatement().visit(this); + for (CatchStatement catchStatement : statement.getCatchStatements()) { + catchStatement.visit(this); + } + Statement finallyStatement = statement.getFinallyStatement(); + if (finallyStatement instanceof EmptyStatement) { + // dispatching to EmptyStatement will not call back visitor, + // must call our visitEmptyStatement explicitly + visitEmptyStatement((EmptyStatement) finallyStatement); + } else { + finallyStatement.visit(this); + } + } + + protected void visitEmptyStatement(EmptyStatement statement) { + // noop + } + + public void visitSwitch(SwitchStatement statement) { + statement.getExpression().visit(this); + for (CaseStatement caseStatement : statement.getCaseStatements()) { + caseStatement.visit(this); + } + statement.getDefaultStatement().visit(this); + } + + public void visitCaseStatement(CaseStatement statement) { + statement.getExpression().visit(this); + statement.getCode().visit(this); + } + + public void visitBreakStatement(BreakStatement statement) { + } + + public void visitContinueStatement(ContinueStatement statement) { + } + + public void visitSynchronizedStatement(SynchronizedStatement statement) { + statement.getExpression().visit(this); + statement.getCode().visit(this); + } + + public void visitThrowStatement(ThrowStatement statement) { + statement.getExpression().visit(this); + } + + public void visitMethodCallExpression(MethodCallExpression call) { + call.getObjectExpression().visit(this); + call.getMethod().visit(this); + call.getArguments().visit(this); + } + + public void visitStaticMethodCallExpression(StaticMethodCallExpression call) { + call.getArguments().visit(this); + } + + public void visitConstructorCallExpression(ConstructorCallExpression call) { + call.getArguments().visit(this); + } + + public void visitBinaryExpression(BinaryExpression expression) { + expression.getLeftExpression().visit(this); + expression.getRightExpression().visit(this); + } + + public void visitTernaryExpression(TernaryExpression expression) { + expression.getBooleanExpression().visit(this); + expression.getTrueExpression().visit(this); + expression.getFalseExpression().visit(this); + } + + public void visitShortTernaryExpression(ElvisOperatorExpression expression) { + visitTernaryExpression(expression); + } + + public void visitPostfixExpression(PostfixExpression expression) { + expression.getExpression().visit(this); + } + + public void visitPrefixExpression(PrefixExpression expression) { + expression.getExpression().visit(this); + } + + public void visitBooleanExpression(BooleanExpression expression) { + expression.getExpression().visit(this); + } + + public void visitNotExpression(NotExpression expression) { + expression.getExpression().visit(this); + } + + public void visitClosureExpression(ClosureExpression expression) { + expression.getCode().visit(this); + } + + public void visitLambdaExpression(LambdaExpression expression) { + visitClosureExpression(expression); + } + + public void visitTupleExpression(TupleExpression expression) { + visitListOfExpressions(expression.getExpressions()); + } + + public void visitListExpression(ListExpression expression) { + visitListOfExpressions(expression.getExpressions()); + } + + public void visitArrayExpression(ArrayExpression expression) { + visitListOfExpressions(expression.getExpressions()); + visitListOfExpressions(expression.getSizeExpression()); + } + + public void visitMapExpression(MapExpression expression) { + visitListOfExpressions(expression.getMapEntryExpressions()); + + } + + public void visitMapEntryExpression(MapEntryExpression expression) { + expression.getKeyExpression().visit(this); + expression.getValueExpression().visit(this); + + } + + public void visitRangeExpression(RangeExpression expression) { + expression.getFrom().visit(this); + expression.getTo().visit(this); + } + + public void visitSpreadExpression(SpreadExpression expression) { + expression.getExpression().visit(this); + } + + public void visitSpreadMapExpression(SpreadMapExpression expression) { + expression.getExpression().visit(this); + } + + public void visitMethodPointerExpression(MethodPointerExpression expression) { + expression.getExpression().visit(this); + expression.getMethodName().visit(this); + } + + public void visitUnaryMinusExpression(UnaryMinusExpression expression) { + expression.getExpression().visit(this); + } + + public void visitUnaryPlusExpression(UnaryPlusExpression expression) { + expression.getExpression().visit(this); + } + + public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) { + expression.getExpression().visit(this); + } + + public void visitCastExpression(CastExpression expression) { + expression.getExpression().visit(this); + } + + public void visitConstantExpression(ConstantExpression expression) { + } + + public void visitClassExpression(ClassExpression expression) { + } + + public void visitVariableExpression(VariableExpression expression) { + } + + public void visitDeclarationExpression(DeclarationExpression expression) { + visitBinaryExpression(expression); + } + + public void visitPropertyExpression(PropertyExpression expression) { + expression.getObjectExpression().visit(this); + expression.getProperty().visit(this); + } + + public void visitAttributeExpression(AttributeExpression expression) { + expression.getObjectExpression().visit(this); + expression.getProperty().visit(this); + } + + public void visitFieldExpression(FieldExpression expression) { + } + + public void visitGStringExpression(GStringExpression expression) { + visitListOfExpressions(expression.getStrings()); + visitListOfExpressions(expression.getValues()); + } + + protected void visitListOfExpressions(List list) { + if (list == null) return; + for (Expression expression : list) { + // GRECLIPSE edit + //if (expression instanceof SpreadExpression) { + // Expression spread = ((SpreadExpression) expression).getExpression(); + // spread.visit(this); + //} else { + expression.visit(this); + //} + // GRECLIPSE end + } + } + + public void visitCatchStatement(CatchStatement statement) { + statement.getCode().visit(this); + } + + public void visitArgumentlistExpression(ArgumentListExpression ale) { + visitTupleExpression(ale); + } + + public void visitClosureListExpression(ClosureListExpression cle) { + visitListOfExpressions(cle.getExpressions()); + } + + public void visitBytecodeExpression(BytecodeExpression cle) { + } + + // GRECLIPSE add + public void visitEmptyExpression(EmptyExpression expression) { + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/Comment.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/Comment.java new file mode 100644 index 0000000000..de3fa1cbf9 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/Comment.java @@ -0,0 +1,236 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.ast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a comment in groovy source. Subtypes are single line or multi line. + * Contains factory methods called from the parser (GroovyRecognizer) that build the + * comment subtypes. + */ +public abstract class Comment { + + protected static final boolean debug = false; + + protected static final int BLOCK = 0; // text surrounded by /* .. */ + protected static final int LINE = 1; // text prefixed with // + protected static final int JAVADOC = 2; // text surrounded by /** .. */ + + protected String comment; + private int kind; + + // set when the comment is associated with a source element + public boolean usedUp = false; + + // Start/Ends for line/columns + // Lines are from 1..N + // Columns are from 1..N + public int sline, scol, eline, ecol; + + public Comment(int kind, int sline, int scol, int eline, int ecol, String string) { + this.kind = kind; + this.sline = sline; + this.scol = scol; + this.eline = eline; + this.ecol = ecol; + this.comment = string; + } + + public int getLastLine() { + return eline; + } + + public static Comment makeSingleLineComment(int sline, int scol, int eline, int ecol, String string) { + return new SingleLineComment(sline, scol, eline, ecol, string); + } + + public static Comment makeMultiLineComment(int sline, int scol, int eline, int ecol, String string) { + return new MultiLineComment(sline, scol, eline, ecol, string); + } + + public abstract List getPositionsOf(String taskTag, String taskPriority, int[] lineseps, boolean caseSensitive); + + /** + * Return the positions (offsets) that JDT wants to see. Special rules here! For a javadoc comment + * both offsets are positive. For a line comment '//' both are negative. For a block comment only the + * end is negative. + */ + public int[] getPositions(int[] lineseps) { + int offsetToStartLine = (sline == 1 ? 0 : lineseps[sline - 2] + 1); + int start = offsetToStartLine + (scol - 1); + int offsetToEndLine = (eline == 1 ? 0 : lineseps[eline - 2] + 1); + int end = offsetToEndLine + (ecol - 1); + if (kind == LINE) { + return new int[]{-start, -end}; + } else if (kind == BLOCK) { + return new int[]{start, -end}; + } else { // JAVADOC + return new int[]{start, end}; + } + } + + public String toString() { + return comment; + } + + protected boolean isValidStartLocationForTask(String text, int index, String taskTag) { + int tagLen = taskTag.length(); + if (comment.charAt(index - 1) == '@') { + return false; + } + + // ensure tag is not leaded with letter if tag starts with a letter + if (Character.isJavaIdentifierStart(comment.charAt(index))) { + if (Character.isJavaIdentifierPart(comment.charAt(index - 1))) { + return false; + } + } + + // ensure tag is not followed with letter if tag finishes with a + // letter + if ((index + tagLen) < comment.length() && Character.isJavaIdentifierStart(comment.charAt(index + tagLen - 1))) { + if (Character.isJavaIdentifierPart(comment.charAt(index + tagLen))) { + return false; + } + } + return true; + } + + protected int findTaskTag(String text, String tag, boolean caseSensitive, int fromIndex) { + if (caseSensitive) { + return text.indexOf(tag, fromIndex); + } else { + int taglen = tag.length(); + String lcTag = tag.toLowerCase(); + char firstChar = lcTag.charAt(0); + for (int p = fromIndex, max = text.length() - tag.length() + 1; p < max; p++) { + if (Character.toLowerCase(text.charAt(p)) == firstChar) { + // possible match + boolean matched = true; + for (int t = 1; t < taglen; t++) { + if (Character.toLowerCase(text.charAt(p + t)) != lcTag.charAt(t)) { + matched = false; + break; + } + } + if (matched) { + return p; + } + } + } + return -1; + } + } + + public boolean isJavadoc() { + return kind == JAVADOC; + } +} + +/** + * Represents a single line comment of the form '// blahblahblah' + */ +class SingleLineComment extends Comment { + + public SingleLineComment(int sline, int scol, int eline, int ecol, String string) { + super(LINE, sline, scol, eline, ecol, string); + if (debug) { + System.out.println("Lexer found SL comment: [" + string + "] at L" + sline + "C" + scol + ">L" + eline + "C" + ecol); + } + } + + public List getPositionsOf(String taskTag, String taskPriority, int[] lineseps, boolean caseSensitive) { + int i = findTaskTag(comment, taskTag, caseSensitive, 0); + if (debug) { + System.out.println("searching slc: [" + comment + "] for '" + taskTag + "' " + i); + } + if (i == -1) { + return Collections.emptyList(); + } + List tasks = new ArrayList<>(); + while (i != -1) { + if (isValidStartLocationForTask(comment, i, taskTag)) { + int offsetToLineStart = (sline == 1 ? 0 : lineseps[sline - 2] + 1); + int taskTagStart = offsetToLineStart + (scol - 1) + i; + int taskEnd = offsetToLineStart + ecol - 2; + TaskEntry taskEntry = new TaskEntry(taskTagStart, taskEnd, taskTag, taskPriority, comment, offsetToLineStart + scol - 1); + if (debug) { + System.out.println("Built task entry " + taskEntry.toString()); + } + tasks.add(taskEntry); + } + i = findTaskTag(comment, taskTag, caseSensitive, i + taskTag.length()); + } + return tasks; + } +} + +/** + * Represents a multi line comment of the form '/ blahblahblah /' + */ +class MultiLineComment extends Comment { + + public MultiLineComment(int sline, int scol, int eline, int ecol, String string) { + super(string.charAt(2) == '*' ? JAVADOC : BLOCK, sline, scol, eline, ecol, string); + if (debug) { + System.out.println("Lexer found ML comment: [" + string + "] at L" + sline + "C" + scol + ">L" + eline + "C" + ecol); + } + } + + @Override + public List getPositionsOf(String taskTag, String taskPriority, int[] lineseps, boolean caseSensitive) { + int i = findTaskTag(comment, taskTag, caseSensitive, 0); + if (debug) { + System.out.println("searching mlc: [" + comment + "] for '" + taskTag + "' " + i); + } + if (i == -1) { + return Collections.emptyList(); + } + List taskPositions = new ArrayList<>(); + while (i != -1) { + + if (isValidStartLocationForTask(comment, i, taskTag)) { + int offsetToCommentStart = (sline == 1 ? 0 : lineseps[sline - 2] + 1) + scol - 1; + + int taskTagStart = offsetToCommentStart + i; + int taskEnd = taskTagStart; + // find the end (end of comment or end of line) + while (true) { + int pos = taskEnd - offsetToCommentStart; + char ch = comment.charAt(pos); + if (ch == '\n' || ch == '\r') { + break; + } + if ((pos + 2) > comment.length()) { + taskEnd--; + break; + } + taskEnd++; + } + TaskEntry taskEntry = new TaskEntry(taskTagStart, taskEnd - 1, taskTag, taskPriority, comment, offsetToCommentStart); + if (debug) { + System.out.println("Built task entry " + taskEntry.toString()); + } + taskPositions.add(taskEntry); + } + i = findTaskTag(comment, taskTag, caseSensitive, i + taskTag.length()); + } + return taskPositions; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/CompileUnit.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/CompileUnit.java new file mode 100644 index 0000000000..a3ecb72fbd --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/CompileUnit.java @@ -0,0 +1,201 @@ +/* + * 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.ast; + +import groovy.lang.GroovyClassLoader; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.SyntaxException; + +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Represents the entire contents of a compilation step which consists of one or more + * {@link ModuleNode} instances. There's one instance of this that's shared by all modules and + * classes compiled during a single invocation of the compiler. + *

+ * It's attached to MethodNodes and ClassNodes and is used to find fully qualified names of classes, + * resolve imports, and that sort of thing. + * + * @author James Strachan + */ +public class CompileUnit { + + private final List modules = new ArrayList(); + private final Map classes = new HashMap(); + // GRECLIPSE add + private List sortedClasses; + public void setSortedClasses(List sortedClasses) { this.sortedClasses = sortedClasses; } + public List getSortedClasses() { return sortedClasses == null ? null : Collections.unmodifiableList(sortedClasses); } + // GRECLIPSE end + private final CompilerConfiguration config; + private final GroovyClassLoader classLoader; + private final CodeSource codeSource; + private final Map classesToCompile = new HashMap(); + private final Map classNameToSource = new HashMap(); + private final Map generatedInnerClasses = new HashMap(); + + public CompileUnit(GroovyClassLoader classLoader, CompilerConfiguration config) { + this(classLoader, null, config); + } + + public CompileUnit(GroovyClassLoader classLoader, CodeSource codeSource, CompilerConfiguration config) { + this.classLoader = classLoader; + this.config = config; + this.codeSource = codeSource; + } + + public List getModules() { + return modules; + } + + public void addModule(ModuleNode node) { + // node==null means a compilation error prevented + // groovy from building an ast + if (node == null) return; + modules.add(node); + node.setUnit(this); + addClasses(node.getClasses()); + } + + /** + * @return the ClassNode for the given qualified name or returns null if + * the name does not exist in the current compilation unit + * (ignoring the .class files on the classpath) + */ + public ClassNode getClass(String name) { + ClassNode cn = classes.get(name); + if (cn != null) return cn; + return classesToCompile.get(name); + } + + /** + * @return a list of all the classes in each module in the compilation unit + */ + public List getClasses() { + List answer = new ArrayList(); + for (ModuleNode module : modules) { + answer.addAll(module.getClasses()); + } + return answer; + } + + public CompilerConfiguration getConfig() { + return config; + } + + public GroovyClassLoader getClassLoader() { + return classLoader; + } + + public CodeSource getCodeSource() { + return codeSource; + } + + /** + * Appends all of the fully qualified class names in this + * module into the given map + */ + void addClasses(List classList) { + for (ClassNode node : classList) { + addClass(node); + } + } + + /** + * Adds a class to the unit. + */ + public void addClass(ClassNode node) { + node = node.redirect(); + String name = node.getName(); + ClassNode stored = classes.get(name); + if (stored != null && stored != node) { + // we have a duplicate class! + // One possibility for this is, that we declared a script and a + // class in the same file and named the class like the file + SourceUnit nodeSource = node.getModule().getContext(); + SourceUnit storedSource = stored.getModule().getContext(); + String txt = "Invalid duplicate class definition of class " + node.getName() + " : "; + if (nodeSource == storedSource) { + // same class in same source + txt += "The source " + nodeSource.getName() + " contains at least two definitions of the class " + node.getName() + ".\n"; + if (node.isScriptBody() || stored.isScriptBody()) { + txt += "One of the classes is an explicit generated class using the class statement, the other is a class generated from" + + " the script body based on the file name. Solutions are to change the file name or to change the class name.\n"; + } + } else { + txt += "The sources " + nodeSource.getName() + " and " + storedSource.getName() + " each contain a class with the name " + node.getName() + ".\n"; + } + nodeSource.getErrorCollector().addErrorAndContinue( + new SyntaxErrorMessage(new SyntaxException(txt, node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), nodeSource) + ); + } + classes.put(name, node); + // GRECLIPSE add + sortedClasses = null; + // GRECLIPSE end + + if (classesToCompile.containsKey(name)) { + ClassNode cn = classesToCompile.get(name); + cn.setRedirect(node); + classesToCompile.remove(name); + } + } + + /** + * this method actually does not compile a class. It's only + * a marker that this type has to be compiled by the CompilationUnit + * at the end of a parse step no node should be be left. + */ + public void addClassNodeToCompile(ClassNode node, SourceUnit location) { + classesToCompile.put(node.getName(), node); + classNameToSource.put(node.getName(), location); + } + + public SourceUnit getScriptSourceLocation(String className) { + return classNameToSource.get(className); + } + + public boolean hasClassNodeToCompile() { + return !classesToCompile.isEmpty(); + } + + public Iterator iterateClassNodeToCompile() { + return classesToCompile.keySet().iterator(); + } + + public InnerClassNode getGeneratedInnerClass(String name) { + return generatedInnerClasses.get(name); + } + + public void addGeneratedInnerClass(InnerClassNode icn) { + generatedInnerClasses.put(icn.getName(), icn); + } + + public Map getGeneratedInnerClasses() { + return Collections.unmodifiableMap(generatedInnerClasses); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/GenericsType.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/GenericsType.java new file mode 100644 index 0000000000..b36f7748ac --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/GenericsType.java @@ -0,0 +1,536 @@ +/* + * 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.ast; + +import org.codehaus.groovy.ast.tools.GenericsUtils; +import org.codehaus.groovy.ast.tools.WideningCategories; + +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.codehaus.groovy.ast.ClassHelper.GROOVY_OBJECT_TYPE; + +/** + * This class is used to describe generic type signatures for ClassNodes. + * + * @author Jochen Theodorou + * @see ClassNode + */ +public class GenericsType extends ASTNode { + public static final GenericsType[] EMPTY_ARRAY = new GenericsType[0]; + + // GRECLIPSE edit + private /*final*/ ClassNode[] upperBounds; + private /*final*/ ClassNode lowerBound; + // GRECLIPSE end + private ClassNode type; + private String name; + private boolean placeholder; + private boolean resolved; + private boolean wildcard; + + public GenericsType(ClassNode type, ClassNode[] upperBounds, ClassNode lowerBound) { + this.type = type; + this.name = type.isGenericsPlaceHolder() ? type.getUnresolvedName() : type.getName(); + this.upperBounds = upperBounds; + this.lowerBound = lowerBound; + placeholder = type.isGenericsPlaceHolder(); + resolved = false; + } + + public GenericsType(ClassNode basicType) { + this(basicType, null, null); + } + + // GRECLIPSE add + public GenericsType() { + } + + public String toDetailsString() { + StringBuilder sb = new StringBuilder(); + sb.append("GenericsType[name=").append(name).append(",placeholder=").append(placeholder); + sb.append(",resolved=").append(resolved).append(",wildcard=").append(wildcard); + sb.append(",type=").append(type); + if (lowerBound != null) { + sb.append(",lowerBound=").append(lowerBound); + } + if (upperBounds != null) { + sb.append(",upperBounds=["); + for (int i = 0, n = upperBounds.length; i < n; i += 1) { + if (i > 0) sb.append(','); + sb.append(upperBounds[i]); + } + } + sb.append("]]"); + sb.append(getClass().getName()); + return sb.toString(); + } + // GRECLIPSE end + + public ClassNode getType() { + return type; + } + + public void setType(ClassNode type) { + this.type = type; + } + + public String toString() { + Set visited = new HashSet(); + return toString(visited); + } + + private String toString(Set visited) { + if (placeholder) visited.add(name); + StringBuilder ret = new StringBuilder(wildcard ? "?" : ((type == null || placeholder) ? name : genericsBounds(type, visited))); + if (upperBounds != null) { + if (placeholder && upperBounds.length==1 && !upperBounds[0].isGenericsPlaceHolder() && upperBounds[0].getName().equals("java.lang.Object")) { + // T extends Object should just be printed as T + } else { + ret.append(" extends "); + for (int i = 0; i < upperBounds.length; i++) { + ret.append(genericsBounds(upperBounds[i], visited)); + if (i + 1 < upperBounds.length) ret.append(" & "); + } + } + } else if (lowerBound != null) { + ret.append(" super ").append(genericsBounds(lowerBound, visited)); + } + return ret.toString(); + } + + private String nameOf(ClassNode theType) { + StringBuilder ret = new StringBuilder(); + if (theType.isArray()) { + ret.append(nameOf(theType.getComponentType())); + ret.append("[]"); + } else { + ret.append(theType.getName()); + } + return ret.toString(); + } + + private String genericsBounds(ClassNode theType, Set visited) { + + StringBuilder ret = new StringBuilder(); + + if (theType.isArray()) { + ret.append(nameOf(theType)); + } else if (theType.redirect() instanceof InnerClassNode) { + InnerClassNode innerClassNode = (InnerClassNode) theType.redirect(); + String parentClassNodeName = innerClassNode.getOuterClass().getName(); + if (Modifier.isStatic(innerClassNode.getModifiers()) || innerClassNode.isInterface()) { + ret.append(innerClassNode.getOuterClass().getName()); + } else { + ret.append(genericsBounds(innerClassNode.getOuterClass(), new HashSet())); + } + ret.append("."); + String typeName = theType.getName(); + ret.append(typeName.substring(parentClassNodeName.length() + 1)); + } else { + ret.append(theType.getName()); + } + + GenericsType[] genericsTypes = theType.getGenericsTypes(); + if (genericsTypes == null || genericsTypes.length == 0) + return ret.toString(); + + // TODO instead of catching Object here stop it from being placed into type in first place + if (genericsTypes.length == 1 && genericsTypes[0].isPlaceholder() && theType.getName().equals("java.lang.Object")) { + return genericsTypes[0].getName(); + } + + ret.append("<"); + for (int i = 0; i < genericsTypes.length; i++) { + if (i != 0) ret.append(", "); + + GenericsType type = genericsTypes[i]; + if (type.isPlaceholder() && visited.contains(type.getName())) { + ret.append(type.getName()); + } + else { + ret.append(type.toString(visited)); + } + } + ret.append(">"); + + return ret.toString(); + } + + public ClassNode[] getUpperBounds() { + return upperBounds; + } + + public String getName() { + return name; + } + + public boolean isPlaceholder() { + return placeholder; + } + + public void setPlaceholder(boolean placeholder) { + this.placeholder = placeholder; + type.setGenericsPlaceHolder(placeholder); + } + + public boolean isResolved() { + return resolved || placeholder; + } + + public void setResolved(boolean res) { + resolved = res; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isWildcard() { + return wildcard; + } + + public void setWildcard(boolean wildcard) { + this.wildcard = wildcard; + } + + public ClassNode getLowerBound() { + return lowerBound; + } + + // GRECLIPSE add + public void setLowerBound(ClassNode bound) { + this.lowerBound = bound; + } + public void setUpperBounds(ClassNode[] bounds) { + this.upperBounds = bounds; + } + // GRECLIPSE end + + /** + * Tells if the provided class node is compatible with this generic type definition + * @param classNode the class node to be checked + * @return true if the class node is compatible with this generics type definition + */ + public boolean isCompatibleWith(ClassNode classNode) { + return new GenericsTypeMatcher().matches(classNode); + } + + /** + * Implements generics type comparison. + */ + private class GenericsTypeMatcher { + + public boolean implementsInterfaceOrIsSubclassOf(ClassNode type, ClassNode superOrInterface) { + boolean result = type.equals(superOrInterface) + || type.isDerivedFrom(superOrInterface) + || type.implementsInterface(superOrInterface); + if (result) { + return true; + } + if (GROOVY_OBJECT_TYPE.equals(superOrInterface) && type.getCompileUnit()!=null) { + // type is being compiled so it will implement GroovyObject later + return true; + } + if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) { + WideningCategories.LowestUpperBoundClassNode cn = (WideningCategories.LowestUpperBoundClassNode) superOrInterface; + result = implementsInterfaceOrIsSubclassOf(type, cn.getSuperClass()); + if (result) { + for (ClassNode interfaceNode : cn.getInterfaces()) { + result = implementsInterfaceOrIsSubclassOf(type,interfaceNode); + if (!result) break; + } + } + if (result) return true; + } + if (type.isArray() && superOrInterface.isArray()) { + return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType()); + } + return false; + } + + /** + * Compares this generics type with the one represented by the provided class node. If the provided + * classnode is compatible with the generics specification, returns true. Otherwise, returns false. + * The check is complete, meaning that we also check "nested" generics. + * @param classNode the classnode to be checked + * @return true iff the classnode is compatible with this generics specification + */ + public boolean matches(ClassNode classNode) { + GenericsType[] genericsTypes = classNode.getGenericsTypes(); + // diamond always matches + if (genericsTypes!=null && genericsTypes.length==0) return true; + if (classNode.isGenericsPlaceHolder()) { + // if the classnode we compare to is a generics placeholder (like ) then we + // only need to check that the names are equal + if (genericsTypes==null) return true; + if (isWildcard()) { + if (lowerBound!=null) return genericsTypes[0].getName().equals(lowerBound.getUnresolvedName()); + if (upperBounds!=null) { + for (ClassNode upperBound : upperBounds) { + String name = upperBound.getGenericsTypes()[0].getName(); + if (genericsTypes[0].getName().equals(name)) return true; + } + return false; + } + } + return genericsTypes[0].getName().equals(name); + } + if (wildcard || placeholder) { + // if the current generics spec is a wildcard spec or a placeholder spec + // then we must check upper and lower bounds + if (upperBounds != null) { + // check that the provided classnode is a subclass of all provided upper bounds + boolean upIsOk = true; + for (int i = 0, upperBoundsLength = upperBounds.length; i < upperBoundsLength && upIsOk; i++) { + final ClassNode upperBound = upperBounds[i]; + upIsOk = implementsInterfaceOrIsSubclassOf(classNode, upperBound); + } + // if the provided classnode is a subclass of the upper bound + // then check that the generic types supplied by the class node are compatible with + // this generics specification + // for example, we could have the spec saying List but provided classnode + // saying List + upIsOk = upIsOk && checkGenerics(classNode); + return upIsOk; + } + if (lowerBound != null) { + // if a lower bound is declared, then we must perform the same checks that for an upper bound + // but with reversed arguments + return implementsInterfaceOrIsSubclassOf(lowerBound, classNode) && checkGenerics(classNode); + } + // If there are no bounds, the generic type is basically Object, and everything is compatible. + return true; + } + // if this is not a generics placeholder, first compare that types represent the same type + if ((type!=null && !type.equals(classNode))) { + return false; + } + // last, we could have the spec saying List and a classnode saying List so + // we must check that generics are compatible. + // The null check is normally not required but done to prevent from NPEs + return type == null || compareGenericsWithBound(classNode, type); + } + + /** + * Iterates over each generics bound of this generics specification, and checks + * that the generics defined by the bound are compatible with the generics specified + * by the type. + * @param classNode the classnode the bounds should be compared with + * @return true if generics from bounds are compatible + */ + private boolean checkGenerics(final ClassNode classNode) { + if (upperBounds!=null) { + for (ClassNode upperBound : upperBounds) { + if (!compareGenericsWithBound(classNode, upperBound)) return false; + } + } + if (lowerBound!=null) { + if (!lowerBound.redirect().isUsingGenerics()) { + if (!compareGenericsWithBound(classNode, lowerBound)) return false; + } + } + return true; + } + + /** + * Given a parameterized type (List<String> for example), checks that its + * generic types are compatible with those from a bound. + * @param classNode the classnode from which we will compare generics types + * @param bound the bound to which the types will be compared + * @return true if generics are compatible + */ + private boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) { + if (classNode==null) return false; + if (!bound.isUsingGenerics() || (classNode.getGenericsTypes()==null && classNode.redirect().getGenericsTypes()!=null)) { + // if the bound is not using generics, there's nothing to compare with + return true; + } + if (!classNode.equals(bound)) { + // the class nodes are on different types + // in this situation, we must choose the correct execution path : either the bound + // is an interface and we must find the implementing interface from the classnode + // to compare their parameterized generics, or the bound is a regular class and we + // must compare the bound with a superclass + if (bound.isInterface()) { + Set interfaces = classNode.getAllInterfaces(); + // iterate over all interfaces to check if any corresponds to the bound we are + // comparing to + for (ClassNode anInterface : interfaces) { + if (anInterface.equals(bound)) { + // when we obtain an interface, the types represented by the interface + // class node are not parameterized. This means that we must create a + // new class node with the parameterized types that the current class node + // has defined. + ClassNode node = GenericsUtils.parameterizeType(classNode, anInterface); + return compareGenericsWithBound(node, bound); + } + } + } + if (bound instanceof WideningCategories.LowestUpperBoundClassNode) { + // another special case here, where the bound is a "virtual" type + // we must then check the superclass and the interfaces + boolean success = compareGenericsWithBound(classNode, bound.getSuperClass()); + if (success) { + ClassNode[] interfaces = bound.getInterfaces(); + for (ClassNode anInterface : interfaces) { + success &= compareGenericsWithBound(classNode, anInterface); + if (!success) break; + } + if (success) return true; + } + } + return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound); + } + GenericsType[] cnTypes = classNode.getGenericsTypes(); + if (cnTypes==null && classNode.isRedirectNode()) cnTypes=classNode.redirect().getGenericsTypes(); + if (cnTypes==null) { + // may happen if generic type is Foo and classnode is Foo -> Foo + return true; + } + GenericsType[] redirectBoundGenericTypes = bound.redirect().getGenericsTypes(); + Map classNodePlaceholders = GenericsUtils.extractPlaceholders(classNode); + Map boundPlaceHolders = GenericsUtils.extractPlaceholders(bound); + boolean match = true; + for (int i = 0; redirectBoundGenericTypes!=null && i < redirectBoundGenericTypes.length && match; i++) { + GenericsType redirectBoundType = redirectBoundGenericTypes[i]; + GenericsType classNodeType = cnTypes[i]; + if (classNodeType.isPlaceholder()) { + String name = classNodeType.getName(); + if (redirectBoundType.isPlaceholder()) { + match = name.equals(redirectBoundType.getName()); + if (!match) { + GenericsType genericsType = boundPlaceHolders.get(redirectBoundType.getName()); + match = false; + if (genericsType!=null) { + if (genericsType.isPlaceholder()) { + match = true; + } else if (genericsType.isWildcard()) { + if (genericsType.getUpperBounds()!=null) { + for (ClassNode up : genericsType.getUpperBounds()) { + match |= redirectBoundType.isCompatibleWith(up); + } + if (genericsType.getLowerBound()!=null) { + match |= redirectBoundType.isCompatibleWith(genericsType.getLowerBound()); + } + } + } + } + } + } else { + if (classNodePlaceholders.containsKey(name)) classNodeType=classNodePlaceholders.get(name); + match = classNodeType.isCompatibleWith(redirectBoundType.getType()); + } + } else { + if (redirectBoundType.isPlaceholder()) { + if (classNodeType.isPlaceholder()) { + match = classNodeType.getName().equals(redirectBoundType.getName()); + } else { + String name = redirectBoundType.getName(); + if (boundPlaceHolders.containsKey(name)) { + redirectBoundType = boundPlaceHolders.get(name); + boolean wildcard = redirectBoundType.isWildcard(); + boolean placeholder = redirectBoundType.isPlaceholder(); + if (placeholder || wildcard) { + // placeholder aliases, like Map -> Map +// redirectBoundType = classNodePlaceholders.get(name); + if (wildcard) { + // ex: Comparable <=> Comparable + if (redirectBoundType.lowerBound!=null) { + GenericsType gt = new GenericsType(redirectBoundType.lowerBound); + if (gt.isPlaceholder()) { + // check for recursive generic typedef, like in + // > + if (classNodePlaceholders.containsKey(gt.getName())) { + gt = classNodePlaceholders.get(gt.getName()); + } + } + match = implementsInterfaceOrIsSubclassOf(gt.getType(), classNodeType.getType()); + } + if (match && redirectBoundType.upperBounds!=null) { + for (ClassNode upperBound : redirectBoundType.upperBounds) { + GenericsType gt = new GenericsType(upperBound); + if (gt.isPlaceholder()) { + // check for recursive generic typedef, like in + // > + if (classNodePlaceholders.containsKey(gt.getName())) { + gt = classNodePlaceholders.get(gt.getName()); + } + } + match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType()) + || classNodeType.isCompatibleWith(gt.getType()); // workaround for GROOVY-6095 + if (!match) break; + } + } + return match; + } else if (classNodePlaceholders.containsKey(name)) { + redirectBoundType = classNodePlaceholders.get(name); + } + } + } + match = redirectBoundType.isCompatibleWith(classNodeType.getType()); + } + } else { + // todo: the check for isWildcard should be replaced with a more complete check + match = redirectBoundType.isWildcard() || classNodeType.isCompatibleWith(redirectBoundType.getType()); + } + } + } + return match; + } + } + + /** + * If you have a class which extends a class using generics, returns the superclass with parameterized types. For + * example, if you have: + * class MyList<T> extends LinkedList<T> + * def list = new MyList<String> + * + * then the parameterized superclass for MyList<String> is LinkedList<String> + * @param classNode the class for which we want to return the parameterized superclass + * @return the parameterized superclass + */ + private static ClassNode getParameterizedSuperClass(ClassNode classNode) { + if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null; + ClassNode superClass = classNode.getUnresolvedSuperClass(); + if (superClass==null) { + return ClassHelper.OBJECT_TYPE; + } + if (!classNode.isUsingGenerics() || !superClass.isUsingGenerics()) return superClass; + GenericsType[] genericsTypes = classNode.getGenericsTypes(); + GenericsType[] redirectGenericTypes = classNode.redirect().getGenericsTypes(); + superClass = superClass.getPlainNodeReference(); + if (genericsTypes==null || redirectGenericTypes==null || superClass.getGenericsTypes()==null) return superClass; + for (int i = 0, genericsTypesLength = genericsTypes.length; i < genericsTypesLength; i++) { + if (redirectGenericTypes[i].isPlaceholder()) { + final GenericsType genericsType = genericsTypes[i]; + GenericsType[] superGenericTypes = superClass.getGenericsTypes(); + for (int j = 0, superGenericTypesLength = superGenericTypes.length; j < superGenericTypesLength; j++) { + final GenericsType superGenericType = superGenericTypes[j]; + if (superGenericType.isPlaceholder() && superGenericType.getName().equals(redirectGenericTypes[i].getName())) { + superGenericTypes[j] = genericsType; + } + } + } + } + return superClass; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/GroovyCodeVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/GroovyCodeVisitor.java new file mode 100644 index 0000000000..bfc091a6b4 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/GroovyCodeVisitor.java @@ -0,0 +1,198 @@ +/* + * 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.ast; + +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ArrayExpression; +import org.codehaus.groovy.ast.expr.AttributeExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; +import org.codehaus.groovy.ast.expr.EmptyExpression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.LambdaExpression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.MethodPointerExpression; +import org.codehaus.groovy.ast.expr.NotExpression; +import org.codehaus.groovy.ast.expr.PostfixExpression; +import org.codehaus.groovy.ast.expr.PrefixExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.SpreadExpression; +import org.codehaus.groovy.ast.expr.SpreadMapExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.TernaryExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.UnaryMinusExpression; +import org.codehaus.groovy.ast.expr.UnaryPlusExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.AssertStatement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.DoWhileStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.SynchronizedStatement; +import org.codehaus.groovy.ast.stmt.ThrowStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.classgen.BytecodeExpression; + +/** + * An implementation of the visitor pattern for working with ASTNodes + * + * @author James Strachan + */ + +public interface GroovyCodeVisitor { + + // statements + + //------------------------------------------------------------------------- + + void visitBlockStatement(BlockStatement statement); + + void visitForLoop(ForStatement forLoop); + + void visitWhileLoop(WhileStatement loop); + + void visitDoWhileLoop(DoWhileStatement loop); + + void visitIfElse(IfStatement ifElse); + + void visitExpressionStatement(ExpressionStatement statement); + + void visitReturnStatement(ReturnStatement statement); + + void visitAssertStatement(AssertStatement statement); + + void visitTryCatchFinally(TryCatchStatement finally1); + + void visitSwitch(SwitchStatement statement); + + void visitCaseStatement(CaseStatement statement); + + void visitBreakStatement(BreakStatement statement); + + void visitContinueStatement(ContinueStatement statement); + + void visitThrowStatement(ThrowStatement statement); + + void visitSynchronizedStatement(SynchronizedStatement statement); + + void visitCatchStatement(CatchStatement statement); + + // expressions + + //------------------------------------------------------------------------- + + void visitMethodCallExpression(MethodCallExpression call); + + void visitStaticMethodCallExpression(StaticMethodCallExpression expression); + + void visitConstructorCallExpression(ConstructorCallExpression expression); + + void visitTernaryExpression(TernaryExpression expression); + + void visitShortTernaryExpression(ElvisOperatorExpression expression); + + void visitBinaryExpression(BinaryExpression expression); + + void visitPrefixExpression(PrefixExpression expression); + + void visitPostfixExpression(PostfixExpression expression); + + void visitBooleanExpression(BooleanExpression expression); + + void visitClosureExpression(ClosureExpression expression); + + void visitLambdaExpression(LambdaExpression expression); + + void visitTupleExpression(TupleExpression expression); + + void visitMapExpression(MapExpression expression); + + void visitMapEntryExpression(MapEntryExpression expression); + + void visitListExpression(ListExpression expression); + + void visitRangeExpression(RangeExpression expression); + + void visitPropertyExpression(PropertyExpression expression); + + void visitAttributeExpression(AttributeExpression attributeExpression); + + void visitFieldExpression(FieldExpression expression); + + void visitMethodPointerExpression(MethodPointerExpression expression); + + void visitConstantExpression(ConstantExpression expression); + + void visitClassExpression(ClassExpression expression); + + void visitVariableExpression(VariableExpression expression); + + void visitDeclarationExpression(DeclarationExpression expression); + + void visitGStringExpression(GStringExpression expression); + + void visitArrayExpression(ArrayExpression expression); + + void visitSpreadExpression(SpreadExpression expression); + + void visitSpreadMapExpression(SpreadMapExpression expression); + + void visitNotExpression(NotExpression expression); + + void visitUnaryMinusExpression(UnaryMinusExpression expression); + + void visitUnaryPlusExpression(UnaryPlusExpression expression); + + void visitBitwiseNegationExpression(BitwiseNegationExpression expression); + + void visitCastExpression(CastExpression expression); + + void visitArgumentlistExpression(ArgumentListExpression expression); + + void visitClosureListExpression(ClosureListExpression closureListExpression); + + void visitBytecodeExpression(BytecodeExpression expression); + + // GRECLIPSE add + void visitEmptyExpression(EmptyExpression expression); + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ImmutableClassNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ImmutableClassNode.java new file mode 100644 index 0000000000..dc978116e9 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ImmutableClassNode.java @@ -0,0 +1,224 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.ast; + +import java.util.Collections; +import java.util.List; + +import org.codehaus.groovy.GroovyBugError; + +/** + * A {@link ClassNode} where the {@link GenericsType} information is immutable. + * Provides extra safety in the IDE. + */ +public class ImmutableClassNode extends ClassNode { + + private volatile boolean genericsInitialized; + private volatile boolean writeProtected; + + public ImmutableClassNode(Class c) { + super(c); + } + + // ASTNode overrides: + + @Override + public void setColumnNumber(int n) {} + + @Override + public void setLastColumnNumber(int n) {} + + @Override + public void setLastLineNumber(int n) {} + + @Override + public void setLineNumber(int n) {} + + @Override + public void setNodeMetaData(Object k, Object v) {} + + @Override + public Object putNodeMetaData(Object k, Object v) { + return getNodeMetaData(k); + } + + @Override + public void setSourcePosition(ASTNode n) {} + + @Override + public void setStart(int i) {} + + @Override + public void setEnd(int i) {} + + // AnnotatedNode overrides: + + @Override + public void setNameStart(int i) {} + + @Override + public void setNameEnd(int i) {} + + @Override + public void setDeclaringClass(ClassNode cn) {} + + @Override + public void setHasNoRealSourcePosition(boolean b) {} + + @Override + public void setSynthetic(boolean b) {} + + // ClassNode overrides: + + @Override + public List getDeclaredMethods(String name) { + if (lazyInitDone && !writeProtected) { + synchronized (methods) { + if (!writeProtected) { + writeProtected = true; + if (methods.map == null || + methods.map.isEmpty()) { + methods.map = Collections.emptyMap(); + } else { + for (Object key : methods.map.keySet()) { + List list = methods.get(key); + methods.map.put(key, Collections.unmodifiableList(list)); + } + methods.map = Collections.unmodifiableMap(methods.map); + } + } + } + } + return super.getDeclaredMethods(name); + } + + @Override + public void setAnnotated(boolean b) {} + + @Override + protected void setCompileUnit(CompileUnit cu) {} + + @Override + public void setEnclosingMethod(MethodNode mn) {} + + @Override + public void setGenericsPlaceHolder(boolean b) {} + + @Override + public void setHasInconsistentHierarchy(boolean b) {} + + //public void setInterfaces(ClassNode[] cn) {} + + @Override + public void setModifiers(int bf) {} + + @Override + public void setModule(ModuleNode mn) {} + + @Override + public String setName(String s) { + return getName(); + } + + //public void setRedirect(ClassNode cn) {} + + @Override + public void setSuperClass(ClassNode cn) {} + + @Override + public void setScript(boolean b) {} + + @Override + public void setScriptBody(boolean b) {} + + @Override + public void setStaticClass(boolean b) {} + + @Override + public void setSyntheticPublic(boolean b) {} + + //public void setUnresolvedSuperClass(ClassNode cn) {} + + @Override + public void setUsingGenerics(boolean b) {} + + @Override + public void setGenericsTypes(GenericsType[] genericsTypes) { + if (genericsInitialized && genericsTypes != this.genericsTypes) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + getName()); + } + if (genericsTypes != null) { + GenericsType[] immutable = new GenericsType[genericsTypes.length]; + for (int i = 0, n = genericsTypes.length; i < n; i += 1) { + immutable[i] = new ImmutableGenericsType(genericsTypes[i], getName()); + } + genericsTypes = immutable; + } + super.setGenericsTypes(genericsTypes); + genericsInitialized = true; + } + + static class ImmutableGenericsType extends GenericsType { + + ImmutableGenericsType(GenericsType delegate, String typeName) { + this.typeName = typeName; + super.setType(delegate.getType()); + super.setName(delegate.getName()); + super.setPlaceholder(delegate.isPlaceholder()); + super.setResolved(delegate.isResolved()); + super.setWildcard(delegate.isWildcard()); + super.setLowerBound(delegate.getLowerBound()); + super.setUpperBounds(delegate.getUpperBounds()); + } + + private final String typeName; + + @Override + public void setType(ClassNode type) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + + @Override + public void setPlaceholder(boolean placeholder) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + + @Override + public void setResolved(boolean res) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + + @Override + public void setName(String name) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + + @Override + public void setWildcard(boolean wildcard) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + + @Override + public void setUpperBounds(ClassNode[] bounds) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + + @Override + public void setLowerBound(ClassNode bound) { + throw new GroovyBugError("Attempt to change an immutable Groovy class: " + typeName); + } + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ImportNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ImportNode.java new file mode 100644 index 0000000000..6545e5d6f4 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ImportNode.java @@ -0,0 +1,216 @@ +/* + * 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.ast; + +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import groovyjarjarasm.asm.Opcodes; + +/** + * Represents an import statement of a single class + * + * @author Jochen Theodorou + * @author Paul King + * @author James Strachan + */ +public class ImportNode extends AnnotatedNode implements Opcodes { + + private final ClassNode type; + private final String alias; + private final String fieldName; + // TODO use PackageNode instead here? + private final String packageName; + private final boolean isStar; + private final boolean isStatic; + + /** + * Represent an import of an entire package, i.e. import package.Classname + * + * @param type the referenced class + * @param alias optional alias + */ + public ImportNode(ClassNode type, String alias) { + this.type = type; + this.alias = alias; + this.isStar = false; + this.isStatic = false; + this.packageName = null; + this.fieldName = null; + } + + /** + * Represent an import of an entire package, i.e. import package.* + * + * @param packageName the name of the package + */ + public ImportNode(String packageName) { + this.type = null; + this.alias = null; + this.isStar = true; + this.isStatic = false; + this.packageName = packageName; + this.fieldName = null; + } + + /** + * Represent a static import of a Class, i.e. import static package.Classname.* + * + * @param type the referenced class + */ + public ImportNode(ClassNode type) { + this.type = type; + this.alias = null; + this.isStar = true; + this.isStatic = true; + this.packageName = null; + this.fieldName = null; + } + + /** + * Represent a static import of a field or method, i.e. import static package.Classname.name + * + * @param type the referenced class + * @param fieldName the field name + * @param alias optional alias + */ + public ImportNode(ClassNode type, String fieldName, String alias) { + this.type = type; + this.alias = alias; + this.isStar = false; + this.isStatic = true; + this.packageName = null; + this.fieldName = fieldName; + } + + /** + * @return the text display of this import + */ + public String getText() { + String typeName = getClassName(); + if (isStar && !isStatic) { + return "import " + packageName + "*"; + } + if (isStar) { + return "import static " + typeName + ".*"; + } + if (isStatic) { + if (alias != null && !alias.isEmpty() && !alias.equals(fieldName)) { + return "import static " + typeName + "." + fieldName + " as " + alias; + } + return "import static " + typeName + "." + fieldName; + } + if (alias == null || alias.isEmpty()) { + return "import " + typeName; + } + return "import " + typeName + " as " + alias; + } + + public String getPackageName() { + return packageName; + } + + public String getFieldName() { + return fieldName; + } + + public boolean isStar() { + return isStar; + } + + public boolean isStatic() { + return isStatic; + } + + public String getAlias() { + return alias; + } + + public ClassNode getType() { + return type; + } + + public String getClassName() { + return type == null ? null : type.getName(); + } + + public void visit(GroovyCodeVisitor visitor) { + } + + // GRECLIPSE add + private boolean unresolvable; + private ConstantExpression aliasExpr; + private ConstantExpression fieldNameExpr; + + public void markAsUnresolvable() { + unresolvable = true; + } + + public boolean isUnresolvable() { + return unresolvable; + } + + public ConstantExpression getAliasExpr() { + return aliasExpr; + } + + public void setAliasExpr(ConstantExpression aliasExpr) { + this.aliasExpr = aliasExpr; + } + + public ConstantExpression getFieldNameExpr() { + return fieldNameExpr; + } + + public void setFieldNameExpr(ConstantExpression fieldNameExpr) { + this.fieldNameExpr = fieldNameExpr; + } + + // getType().getStart() is unreliable because ClassNode instances are reused for well-known types + public int getTypeStart() { + int start = -1; + if (type != null) { + if (type.getEnd() > 0) { + start = type.getStart(); + } else { + ClassExpression expr = getNodeMetaData(ClassExpression.class); + if (expr != null) start = expr.getStart(); + } + } + return start; + } + + // getType().getEnd() is unreliable because ClassNode instances are reused for well-known types + public int getTypeEnd() { + int end = -1; + if (type != null) { + if (type.getEnd() > 0) { + end = type.getEnd(); + } else { + ClassExpression expr = getNodeMetaData(ClassExpression.class); + if (expr != null) end = expr.getEnd(); + } + } + return end; + } + + public String toString() { + return super.toString() + '[' + getText() + ']'; + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/MethodNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/MethodNode.java new file mode 100644 index 0000000000..cd965a6ee3 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/MethodNode.java @@ -0,0 +1,312 @@ +/* + * 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.ast; + +import groovy.lang.groovydoc.Groovydoc; +import groovy.lang.groovydoc.GroovydocHolder; +import org.apache.groovy.ast.tools.MethodNodeUtils; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import groovyjarjarasm.asm.Opcodes; + +import java.util.List; + +/** + * Represents a method declaration + * + * @author James Strachan + * @author Hamlet D'Arcy + */ +public class MethodNode extends AnnotatedNode implements Opcodes, GroovydocHolder { + + public static final String SCRIPT_BODY_METHOD_KEY = "org.codehaus.groovy.ast.MethodNode.isScriptBody"; + private final String name; + private int modifiers; + private boolean syntheticPublic; + private ClassNode returnType; + private Parameter[] parameters; + private boolean hasDefaultValue = false; + private Statement code; + private boolean dynamicReturnType; + private VariableScope variableScope; + private final ClassNode[] exceptions; + private final boolean staticConstructor; + + // type spec for generics + private GenericsType[] genericsTypes = null; + private boolean hasDefault; + + // cached data + String typeDescriptor; + + public MethodNode(String name, int modifiers, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) { + this.name = name; + this.modifiers = modifiers; + this.code = code; + setReturnType(returnType); + VariableScope scope = new VariableScope(); + setVariableScope(scope); + setParameters(parameters); + this.hasDefault = false; + this.exceptions = exceptions; + this.staticConstructor = (name != null && name.equals("")); + } + + /** + * The type descriptor for a method node is a string containing the name of the method, its return type, + * and its parameter types in a canonical form. For simplicity, we use the format of a Java declaration + * without parameter names or generics. + * + * @return the type descriptor + */ + public String getTypeDescriptor() { + if (typeDescriptor == null) { + typeDescriptor = MethodNodeUtils.methodDescriptor(this); + } + return typeDescriptor; + } + + private void invalidateCachedData() { + typeDescriptor = null; + } + + public boolean isVoidMethod() { + return returnType == ClassHelper.VOID_TYPE; + } + + public Statement getCode() { + return code; + } + + public void setCode(Statement code) { + this.code = code; + } + + public int getModifiers() { + return modifiers; + } + + public void setModifiers(int modifiers) { + invalidateCachedData(); + this.modifiers = modifiers; + } + + public String getName() { + return name; + } + + public Parameter[] getParameters() { + return parameters; + } + + public void setParameters(Parameter[] parameters) { + invalidateCachedData(); + VariableScope scope = new VariableScope(); + this.parameters = parameters; + if (parameters != null && parameters.length > 0) { + for (Parameter para : parameters) { + if (para.hasInitialExpression()) { + this.hasDefaultValue = true; + } + para.setInStaticContext(isStatic()); + scope.putDeclaredVariable(para); + } + } + setVariableScope(scope); + } + + public ClassNode getReturnType() { + return returnType; + } + + public VariableScope getVariableScope() { + return variableScope; + } + + public void setVariableScope(VariableScope variableScope) { + this.variableScope = variableScope; + variableScope.setInStaticContext(isStatic()); + } + + public boolean isDynamicReturnType() { + return dynamicReturnType; + } + + public boolean isAbstract() { + return (modifiers & ACC_ABSTRACT) != 0; + } + + public boolean isStatic() { + return (modifiers & ACC_STATIC) != 0; + } + + public boolean isPublic() { + return (modifiers & ACC_PUBLIC) != 0; + } + + public boolean isPrivate() { + return (modifiers & ACC_PRIVATE) != 0; + } + + public boolean isFinal() { + return (modifiers & ACC_FINAL) != 0; + } + + public boolean isProtected() { + return (modifiers & ACC_PROTECTED) != 0; + } + + public boolean hasDefaultValue() { + return this.hasDefaultValue; + } + + /** + * @return true if this method is the run method from a script + */ + public boolean isScriptBody() { + // GRECLIPSE edit + //return getNodeMetaData(SCRIPT_BODY_METHOD_KEY) != null; + return getDeclaringClass() != null && + getDeclaringClass().isScript() && + getName().equals("run") && + (parameters == null || parameters.length == 0) && + (returnType != null && returnType.getName().equals("java.lang.Object")); + // GRECLIPSE: end + } + + /** + * Set the metadata flag for this method to indicate that it is a script body implementation. + * @see ModuleNode createStatementsClass(). + */ + public void setIsScriptBody() { + setNodeMetaData(SCRIPT_BODY_METHOD_KEY, true); + } + + public String toString() { + return "MethodNode@" + hashCode() + "[" + getDeclaringClass().getName() + "#" + getTypeDescriptor() + "]"; + } + + public void setReturnType(ClassNode returnType) { + invalidateCachedData(); + dynamicReturnType |= ClassHelper.DYNAMIC_TYPE == returnType; + this.returnType = returnType; + if (returnType == null) this.returnType = ClassHelper.OBJECT_TYPE; + } + + public ClassNode[] getExceptions() { + return exceptions; + } + + public Statement getFirstStatement() { + if (code == null) return null; + Statement first = code; + while (first instanceof BlockStatement) { + List list = ((BlockStatement) first).getStatements(); + if (list.isEmpty()) { + first = null; + } else { + first = list.get(0); + } + } + return first; + } + + public GenericsType[] getGenericsTypes() { + return genericsTypes; + } + + public void setGenericsTypes(GenericsType[] genericsTypes) { + invalidateCachedData(); + this.genericsTypes = genericsTypes; + } + + public void setAnnotationDefault(boolean b) { + this.hasDefault = b; + } + + public boolean hasAnnotationDefault() { + return hasDefault; + } + + public boolean isStaticConstructor() { + return staticConstructor; + } + + /** + * Indicates that this method has been "promoted" to public by + * Groovy when in fact there was no public modifier explicitly + * in the source code. I.e. it remembers that it has applied + * Groovy's "public methods by default" rule. This property is + * typically only of interest to AST transform writers. + * + * @return true if this class is public but had no explicit public modifier + */ + public boolean isSyntheticPublic() { + return syntheticPublic; + } + + public void setSyntheticPublic(boolean syntheticPublic) { + this.syntheticPublic = syntheticPublic; + } + + /** + * Provides a nicely formatted string of the method definition. For simplicity, generic types on some of the elements + * are not displayed. + * @return + * string form of node with some generic elements suppressed + */ + @Override + public String getText() { + String retType = AstToTextHelper.getClassText(returnType); + String exceptionTypes = AstToTextHelper.getThrowsClauseText(exceptions); + String parms = AstToTextHelper.getParametersText(parameters); + return AstToTextHelper.getModifiersText(modifiers) + " " + retType + " " + name + "(" + parms + ") " + exceptionTypes + " { ... }"; + } + + @Override + public Groovydoc getGroovydoc() { + return this.getNodeMetaData(DOC_COMMENT); + } + + @Override + public MethodNode getInstance() { + return this; + } + + // GRECLIPSE add + /** + * When default parameters are involved, this field will be + * the original method without any default parameters applied + */ + private MethodNode original = this; + + /** + * @return When default parameters are involved, this method returns the {@link MethodNode} + * where no default parameters have been applied. Otherwise returns this. Never + * returns null. + */ + public MethodNode getOriginal() { + return original; + } + + public void setOriginal(MethodNode original) { + this.original = original; + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ModuleNode.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ModuleNode.java new file mode 100644 index 0000000000..77b0b8cf0e --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/ModuleNode.java @@ -0,0 +1,539 @@ +/* + * 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.ast; + +import groovy.lang.Binding; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.codehaus.groovy.transform.BaseScriptASTTransformation; +import groovyjarjarasm.asm.Opcodes; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Represents a module, which consists typically of a class declaration + * but could include some imports, some statements and multiple classes + * intermixed with statements like scripts in Python or Ruby + * + * @author Jochen Theodorou + * @author Paul King + * @author James Strachan + */ +public class ModuleNode extends ASTNode implements Opcodes { + + private final BlockStatement statementBlock = new BlockStatement(); + List classes = new LinkedList(); + private final List methods = new ArrayList(); + private final Map imports = new HashMap(); + // GRECLIPSE add + private final List rawImports = new ArrayList(); + // GRECLIPSE end + private final List starImports = new ArrayList(); + private final Map staticImports = new LinkedHashMap(); + private final Map staticStarImports = new LinkedHashMap(); + private CompileUnit unit; + private PackageNode packageNode; + private String description; + private boolean createClassForStatements = true; + private transient SourceUnit context; + private boolean importsResolved = false; + private ClassNode scriptDummy; + private String mainClassName = null; + private final Parameter[] SCRIPT_CONTEXT_CTOR = {new Parameter(ClassHelper.BINDING_TYPE, "context")}; + + public ModuleNode (SourceUnit context ) { + this.context = context; + } + + public ModuleNode (CompileUnit unit) { + this.unit = unit; + } + + public BlockStatement getStatementBlock() { + return statementBlock; + } + + public List getMethods() { + return methods; + } + + public List getClasses() { + if (createClassForStatements && (!statementBlock.isEmpty() || !methods.isEmpty() || isPackageInfo())) { + ClassNode mainClass = createStatementsClass(); + mainClassName = mainClass.getName(); + createClassForStatements = false; + classes.add(0, mainClass); + mainClass.setModule(this); + addToCompileUnit(mainClass); + } + return classes; + } + + // GRECLIPSE add + private boolean encounteredUnrecoverableError; + + public void setEncounteredUnrecoverableError(boolean b) { + encounteredUnrecoverableError = b; + } + + /** + * @return {@code true} if a syntax error was encountered that prevented correct construction of the AST + */ + public boolean encounteredUnrecoverableError() { + return encounteredUnrecoverableError; + } + // GRECLIPSE end + + private boolean isPackageInfo() { + // GRECLIPSE edit + //return context != null && context.getName() != null && context.getName().endsWith("package-info.groovy"); + if (knowIfPackageInfo == 0) { + if (context != null && context.getName() != null && context.getName().endsWith("package-info.groovy")) { + knowIfPackageInfo = 1; + } else { + knowIfPackageInfo = 2; + } + } + return (knowIfPackageInfo == 1); + } + private int knowIfPackageInfo; // 0=dontknow 1=yes, 2=no + // GRECLIPSE end + + public List getImports() { + // GRECLIPSE edit + //return new ArrayList(imports.values()); + return Collections.unmodifiableList(rawImports); + // GRECLIPSE end + } + + public List getStarImports() { + return starImports; + } + + /** + * @param alias the name of interest + * @return the class node for the given alias or null if none is available + */ + public ClassNode getImportType(String alias) { + ImportNode importNode = imports.get(alias); + return importNode == null ? null : importNode.getType(); + } + + /** + * @param alias the name of interest + * @return the import node for the given alias or null if none is available + */ + public ImportNode getImport(String alias) { + return imports.get(alias); + } + + public void addImport(String alias, ClassNode type) { + addImport(alias, type, new ArrayList()); + } + + public void addImport(String alias, ClassNode type, List annotations) { + ImportNode importNode = new ImportNode(type, alias); + // GRECLIPSE add + // configure sloc from the type's sloc + // note: sloc configuration is done more precisely in AntlrParserPlugin.importDef() + // but we need to handle calls to this method separately from importDef() + if (type != null) { + importNode.setSourcePosition(type); + // adjust to beginning of line + importNode.setColumnNumber(1); + if (type.getColumnNumber() > 0) { + importNode.setStart(type.getStart() - type.getColumnNumber() + 1); + } + } + rawImports.add(importNode); + // GRECLIPSE end + imports.put(alias, importNode); + importNode.addAnnotations(annotations); + storeLastAddedImportNode(importNode); + } + + public void addStarImport(String packageName) { + addStarImport(packageName, Collections.EMPTY_LIST); + } + + public void addStarImport(String packageName, List annotations) { + ImportNode importNode = new ImportNode(packageName); + importNode.addAnnotations(annotations); + starImports.add(importNode); + storeLastAddedImportNode(importNode); + } + + public void addStatement(Statement node) { + statementBlock.addStatement(node); + } + + public void addClass(ClassNode node) { + if(classes.isEmpty()) mainClassName = node.getName(); + classes.add(node); + node.setModule(this); + addToCompileUnit(node); + } + + private void addToCompileUnit(ClassNode node) { + // register the new class with the compile unit + if (unit != null) { + unit.addClass(node); + } + } + + public void addMethod(MethodNode node) { + methods.add(node); + } + + public void visit(GroovyCodeVisitor visitor) { + } + + public String getPackageName() { + return packageNode == null ? null : packageNode.getName(); + } + + public PackageNode getPackage() { + return packageNode; + } + + // TODO don't allow override? + public void setPackage(PackageNode packageNode) { + this.packageNode = packageNode; + } + + // TODO don't allow override? + public void setPackageName(String packageName) { + this.packageNode = new PackageNode(packageName); + } + + public boolean hasPackageName(){ + return packageNode != null && packageNode.getName() != null; + } + + public boolean hasPackage(){ + return this.packageNode != null; + } + + public SourceUnit getContext() { + return context; + } + + /** + * @return the underlying character stream description + */ + public String getDescription() { + if( context != null ) + { + return context.getName(); + } + else + { + return this.description; + } + } + + public void setDescription(String description) { + this.description = description; + } + + public CompileUnit getUnit() { + return unit; + } + + void setUnit(CompileUnit unit) { + this.unit = unit; + } + + public ClassNode getScriptClassDummy() { + if (scriptDummy!=null) { + setScriptBaseClassFromConfig(scriptDummy); + return scriptDummy; + } + + String name = getPackageName(); + if (name == null) { + name = ""; + } + // now let's use the file name to determine the class name + if (getDescription() == null) { + throw new RuntimeException("Cannot generate main(String[]) class for statements when we have no file description"); + } + name += GeneratorContext.encodeAsValidClassName(extractClassFromFileDescription()); + + ClassNode classNode; + if (isPackageInfo()) { + classNode = new ClassNode(name, ACC_ABSTRACT | ACC_INTERFACE, ClassHelper.OBJECT_TYPE); + } else { + classNode = new ClassNode(name, ACC_PUBLIC, ClassHelper.SCRIPT_TYPE); + setScriptBaseClassFromConfig(classNode); + classNode.setScript(true); + classNode.setScriptBody(true); + } + + scriptDummy = classNode; + return classNode; + } + + private void setScriptBaseClassFromConfig(ClassNode cn) { + String baseClassName = null; + if (unit != null) { + baseClassName = unit.getConfig().getScriptBaseClass(); + } else if (context != null) { + baseClassName = context.getConfiguration().getScriptBaseClass(); + } + if (baseClassName != null) { + if (!cn.getSuperClass().getName().equals(baseClassName)) { + cn.setSuperClass(ClassHelper.make(baseClassName)); + AnnotationNode annotationNode = new AnnotationNode(BaseScriptASTTransformation.MY_TYPE); + cn.addAnnotation(annotationNode); + } + } + } + + protected ClassNode createStatementsClass() { + ClassNode classNode = getScriptClassDummy(); + if (classNode.getName().endsWith("package-info")) { + return classNode; + } + + handleMainMethodIfPresent(methods); + + // return new Foo(new ShellContext(args)).run() + classNode.addMethod( + new MethodNode( + "main", + ACC_PUBLIC | ACC_STATIC, + ClassHelper.VOID_TYPE, + new Parameter[] { new Parameter(ClassHelper.STRING_TYPE.makeArray(), "args")}, + ClassNode.EMPTY_ARRAY, + new ExpressionStatement( + new MethodCallExpression( + new ClassExpression(ClassHelper.make(InvokerHelper.class)), + "runScript", + new ArgumentListExpression( + new ClassExpression(classNode), + new VariableExpression("args")))))); + + MethodNode methodNode = new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementBlock); + methodNode.setIsScriptBody(); + classNode.addMethod(methodNode); + + classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); + + Statement stmt; + // A script's contextual constructor should call it's super class' contextual constructor, if it has one. + // In practice this will always be true because currently this visitor is run before the AST transformations + // (like @BaseScript) that could change this. But this is cautious and anticipates possible compiler changes. + if (classNode.getSuperClass().getDeclaredConstructor(SCRIPT_CONTEXT_CTOR) != null) { + stmt = new ExpressionStatement( + new ConstructorCallExpression(ClassNode.SUPER, + new ArgumentListExpression( + new VariableExpression("context")))); + } else { + // Fallback for non-standard base "script" classes with no context (Binding) constructor. + stmt = new ExpressionStatement( + new MethodCallExpression( + new VariableExpression("super"), + "setBinding", + new ArgumentListExpression( + new VariableExpression("context")))); + } + + classNode.addConstructor( + ACC_PUBLIC, + new Parameter[] { new Parameter(ClassHelper.make(Binding.class), "context")}, + ClassNode.EMPTY_ARRAY, + stmt); + + for (MethodNode node : methods) { + int modifiers = node.getModifiers(); + if ((modifiers & ACC_ABSTRACT) != 0) { + throw new RuntimeException( + "Cannot use abstract methods in a script, they are only available inside classes. Method: " + + node.getName()); + } + // br: the old logic seems to add static to all def f().... in a script, which makes enclosing + // inner classes (including closures) in a def function difficult. Comment it out. + node.setModifiers(modifiers /*| ACC_STATIC*/); + + classNode.addMethod(node); + } + return classNode; + } + + /* + * If a main method is provided by user, account for it under run() as scripts generate their own 'main' so they can run. + */ + private void handleMainMethodIfPresent(List methods) { + boolean found = false; + for (Iterator iter = methods.iterator(); iter.hasNext();) { + MethodNode node = (MethodNode) iter.next(); + if(node.getName().equals("main")) { + if (node.isStatic() && node.getParameters().length == 1) { + boolean retTypeMatches, argTypeMatches; + ClassNode argType = node.getParameters()[0].getType(); + ClassNode retType = node.getReturnType(); + + argTypeMatches = (argType.equals(ClassHelper.OBJECT_TYPE) || argType.getName().contains("String[]")); + retTypeMatches = (retType == ClassHelper.VOID_TYPE || retType == ClassHelper.OBJECT_TYPE); + + if(retTypeMatches && argTypeMatches) { + if(found) { + throw new RuntimeException("Repetitive main method found."); + } else { + found = true; + } + // if script has both loose statements as well as main(), then main() is ignored + if(statementBlock.isEmpty()) { + addStatement(node.getCode()); + } + iter.remove(); + } + } + } + } + } + + protected String extractClassFromFileDescription() { + String answer = getDescription(); + try { + URI uri = new URI(answer); + String path = uri.getPath(); + String schemeSpecific = uri.getSchemeSpecificPart(); + if (path!=null) { + answer = path; + } else if (schemeSpecific!=null) { + answer = schemeSpecific; + } + } catch (URISyntaxException e) {} + // let's strip off everything after the last '.' + int slashIdx = answer.lastIndexOf('/'); + int separatorIdx = answer.lastIndexOf(File.separatorChar); + int dotIdx = answer.lastIndexOf('.'); + if (dotIdx > 0 && dotIdx > Math.max(slashIdx, separatorIdx)) { + answer = answer.substring(0, dotIdx); + } + // new let's strip everything up to and including the path separators + if (slashIdx >= 0) { + answer = answer.substring(slashIdx + 1); + } + // recalculate in case we have already done some stripping + separatorIdx = answer.lastIndexOf(File.separatorChar); + if (separatorIdx >= 0) { + answer = answer.substring(separatorIdx + 1); + } + return answer; + } + + public boolean isEmpty() { + return classes.isEmpty() && statementBlock.getStatements().isEmpty(); + } + + public void sortClasses(){ + if (isEmpty()) return; + List classes = getClasses(); + LinkedList sorted = new LinkedList(); + int level=1; + while (!classes.isEmpty()) { + for (Iterator cni = classes.iterator(); cni.hasNext();) { + ClassNode cn = cni.next(); + ClassNode sn = cn; + for (int i=0; sn!=null && i getStaticImports() { + return staticImports; + } + + public Map getStaticStarImports() { + return staticStarImports; + } + + public void addStaticImport(ClassNode type, String fieldName, String alias) { + addStaticImport(type, fieldName, alias, new ArrayList()); + } + + public void addStaticImport(ClassNode type, String fieldName, String alias, List annotations) { + ImportNode node = new ImportNode(type, fieldName, alias); + node.addAnnotations(annotations); + // GRECLIPSE edit + //staticImports.put(alias, node); + ImportNode prev = staticImports.put(alias, node); + if (prev != null) staticImports.put(prev.toString(), prev); + // GRECLIPSE end + storeLastAddedImportNode(node); + } + + public void addStaticStarImport(String name, ClassNode type) { + addStaticStarImport(name, type, new ArrayList()); + } + + public void addStaticStarImport(String name, ClassNode type, List annotations) { + ImportNode node = new ImportNode(type); + node.addAnnotations(annotations); + staticStarImports.put(name, node); + storeLastAddedImportNode(node); + } + + // This method only exists as a workaround for GROOVY-6094 + // In order to keep binary compatibility + private void storeLastAddedImportNode(final ImportNode node) { + if (getNodeMetaData(ImportNode.class)==ImportNode.class) { + putNodeMetaData(ImportNode.class, node); + } + } + + public String getMainClassName() { + return mainClassName; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/Parameter.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/Parameter.java new file mode 100644 index 0000000000..16c86e0dd6 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/Parameter.java @@ -0,0 +1,136 @@ +/* + * 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.ast; + +import org.codehaus.groovy.ast.expr.Expression; + + +/** + * Represents a parameter on a constructor or method call. The type name is + * optional - it defaults to java.lang.Object if unknown. + * + * @author James Strachan + */ +public class Parameter extends AnnotatedNode implements Variable { + + public static final Parameter[] EMPTY_ARRAY = {}; + + private ClassNode type; + private final String name; + private boolean dynamicTyped; + private Expression defaultValue; + private boolean hasDefaultValue; + private boolean inStaticContext; + private boolean closureShare=false; + private int modifiers; + private ClassNode originType=ClassHelper.DYNAMIC_TYPE; + + public Parameter(ClassNode type, String name) { + // GRECLIPSE add + if (type == null) { + // Recovery related: something else went wrong that prevented type being determined (STS-3822) + type = ClassHelper.OBJECT_TYPE; + } + // GRECLIPSE end + this.name = name; + this.setType(type); + this.originType = type; + this.hasDefaultValue = false; + } + + public Parameter(ClassNode type, String name, Expression defaultValue) { + this(type,name); + this.defaultValue = defaultValue; + this.hasDefaultValue = defaultValue != null; + } + + public String toString() { + return super.toString() + "[name:" + name + ((type == null) ? "" : " type: " + type.getName()) + ", hasDefaultValue: " + this.hasInitialExpression() + "]"; + } + + public String getName() { + return name; + } + + public ClassNode getType() { + return type; + } + + public void setType(ClassNode type) { + this.type = type; + dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE; + } + + public boolean hasInitialExpression() { + return this.hasDefaultValue; + } + + /** + * @return the default value expression for this parameter or null if + * no default value is specified + */ + public Expression getInitialExpression() { + return defaultValue; + } + + public void setInitialExpression(Expression init) { + defaultValue = init; + hasDefaultValue = defaultValue != null; + } + + public boolean isInStaticContext() { + return inStaticContext; + } + + public void setInStaticContext(boolean inStaticContext) { + this.inStaticContext = inStaticContext; + } + + public boolean isDynamicTyped() { + return dynamicTyped; + } + + public boolean isClosureSharedVariable() { + return closureShare; + } + + public void setClosureSharedVariable(boolean inClosure) { + closureShare = inClosure; + } + + public int getModifiers() { + return modifiers; + } + + public ClassNode getOriginType() { + return originType; + } + + public void setOriginType(ClassNode cn) { + originType = cn; + } + + public void setModifiers(int modifiers) { + this.modifiers = modifiers; + } + + public Expression getDefaultValue() { + return defaultValue; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/TaskEntry.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/TaskEntry.java new file mode 100644 index 0000000000..7654228937 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/TaskEntry.java @@ -0,0 +1,69 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.ast; + +/** + * Represents a single task entry. If it is adjacent to another (and so should + * share the same message), the isAdjacentTo field will be set. + * + * @author Andy Clement + */ +public class TaskEntry { + + public int start; + private int end; + public String taskTag; + public String taskPriority; + public TaskEntry isAdjacentTo; + private String commentText; + private int offsetToStartOfCommentTextInFile; + + public TaskEntry(int startOffset, int endOffset, String taskTag, String taskPriority, String commentText, int offsetToStartOfCommentTextInFile) { + this.start = startOffset; + this.end = endOffset; + this.taskTag = taskTag; + this.taskPriority = taskPriority; + this.commentText = commentText; + this.offsetToStartOfCommentTextInFile = offsetToStartOfCommentTextInFile; + } + + public int getEnd() { + if (isAdjacentTo != null) { + return isAdjacentTo.getEnd(); + } + return end; + } + + public void setEnd(int end) { + this.end = end; + } + + public String getText() { + // start/end are within the whole file + // offsetToStartOfCommentTextInFile gives the offset to the start of the comment within the file + if (isAdjacentTo != null) { + return isAdjacentTo.getText(); + } else { + int commentStartIndex = start - offsetToStartOfCommentTextInFile + taskTag.length(); + int commentEndIndex = end - offsetToStartOfCommentTextInFile + 1; + return commentText.substring(commentStartIndex, commentEndIndex).trim(); + } + } + + public String toString() { + return "Task:" + taskTag + "[" + getText() + "] " + start + " > " + end + "(" + getEnd() + ")"; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/ClassExpression.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/ClassExpression.java new file mode 100644 index 0000000000..54b489b207 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/ClassExpression.java @@ -0,0 +1,61 @@ +/* + * 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.ast.expr; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents access to a Java/Groovy class in an expression, such + * as when invoking a static method or accessing a static type + * + * @author James Strachan + */ +public class ClassExpression extends Expression { + + public ClassExpression(ClassNode type) { + super.setType(type); + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitClassExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + return this; + } + + public String getText() { + return getType().getName(); + } + + // GRECLIPSE add + public void setSourcePosition(ASTNode node) { + super.setSourcePosition(node); + // propagate source position + if (getType().getEnd() <= 0) + getType().setSourcePosition(this); + } + // GRECLIPSE end + + public String toString() { + return super.toString() + "[type: " + getType().getName() + "]"; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/ConstantExpression.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/ConstantExpression.java new file mode 100644 index 0000000000..5aba8f1e2f --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/ConstantExpression.java @@ -0,0 +1,227 @@ +/* + * 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.ast.expr; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents a constant expression such as null, true, false + * + * @author James Strachan + */ +public class ConstantExpression extends Expression { + // The following fields are only used internally; every occurrence of a user-defined expression of the same kind + // has its own instance so as to preserve line information. Consequently, to test for such an expression, don't + // compare against the field but call isXXXExpression() instead. + public static final ConstantExpression NULL = new StaticConstantExpression(null); + public static final ConstantExpression TRUE = new StaticConstantExpression(Boolean.TRUE); + public static final ConstantExpression FALSE = new StaticConstantExpression(Boolean.FALSE); + public static final ConstantExpression EMPTY_STRING = new StaticConstantExpression(""); + public static final ConstantExpression PRIM_TRUE = new StaticConstantExpression(Boolean.TRUE, true); + public static final ConstantExpression PRIM_FALSE = new StaticConstantExpression(Boolean.FALSE, true); + + // the following fields are only used internally; there are no user-defined expressions of the same kind + public static final ConstantExpression VOID = new StaticConstantExpression(Void.class); + public static final ConstantExpression EMPTY_EXPRESSION = new StaticConstantExpression(null); + + private final Object value; + private String constantName; + + public ConstantExpression(Object value) { + this(value,false); + } + + public ConstantExpression(Object value, boolean keepPrimitive) { + this.value = value; + if (value != null) { + if (keepPrimitive) { + if (value instanceof Integer) { + setType(ClassHelper.int_TYPE); + } else if (value instanceof Long) { + setType(ClassHelper.long_TYPE); + } else if (value instanceof Boolean) { + setType(ClassHelper.boolean_TYPE); + } else if (value instanceof Double) { + setType(ClassHelper.double_TYPE); + } else if (value instanceof Float) { + setType(ClassHelper.float_TYPE); + } else if (value instanceof Character) { + setType(ClassHelper.char_TYPE); + } else { + setType(ClassHelper.make(value.getClass())); + } + //TODO: more cases here + } else { + setType(ClassHelper.make(value.getClass())); + } + } + } + + public String toString() { + return "ConstantExpression[" + value + "]"; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitConstantExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + return this; + } + + /** + * @return the value of this constant expression + */ + public Object getValue() { + return value; + } + + public String getText() { + return (value == null) ? "null" : value.toString(); + } + + public String getConstantName() { + return constantName; + } + + public void setConstantName(String constantName) { + this.constantName = constantName; + } + + public boolean isNullExpression() { + return value == null; + } + + public boolean isTrueExpression() { + return Boolean.TRUE.equals(value); + } + + public boolean isFalseExpression() { + return Boolean.FALSE.equals(value); + } + + public boolean isEmptyStringExpression() { + return "".equals(value); + } +} + +// GROOVY add +class StaticConstantExpression extends ConstantExpression { + + public StaticConstantExpression(Object value) { + super(value); + } + + public StaticConstantExpression(Object value, boolean keepPrimitive) { + super(value, keepPrimitive); + } + + // ASTNode overrides: + + @Override + public void setColumnNumber(int n) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setLastColumnNumber(int n) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setLastLineNumber(int n) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setLineNumber(int n) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setNodeMetaData(Object k, Object v) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public Object putNodeMetaData(Object k, Object v) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setSourcePosition(ASTNode n) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setStart(int i) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setEnd(int i) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + // AnnotatedNode overrides: + + @Override + public void setNameStart(int i) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setNameEnd(int i) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setDeclaringClass(ClassNode cn) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setHasNoRealSourcePosition(boolean b) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + @Override + public void setSynthetic(boolean b) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + + // ConstantExpression overrides: + + public void setType(ClassNode t) { + if (getType() == ClassHelper.DYNAMIC_TYPE) { + super.setType(t); + } else { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } + } + + public void setConstantName(String constantName) { + throw new GroovyBugError("Attempt to change static constant expression: " + getText()); + } +} +// GROOVY end diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/EmptyExpression.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/EmptyExpression.java new file mode 100644 index 0000000000..944a870e82 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/EmptyExpression.java @@ -0,0 +1,140 @@ +/* + * 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.ast.expr; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.NodeMetaDataHandler; + +import java.util.List; +import java.util.Map; + +/** + * This class is a place holder for an empty expression. + * Empty expression are used in closures lists like (;). During + * class Generation this expression should be either ignored or + * replace with a null value. + * + * @see org.codehaus.groovy.ast.stmt.EmptyStatement + */ +public class EmptyExpression extends Expression { + public static final EmptyExpression INSTANCE = new EmptyExpression(); + + /** + * use EmptyExpression.INSTANCE instead + */ + @Deprecated + public EmptyExpression() {} + + public Expression transformExpression(ExpressionTransformer transformer) { + return this; + } + + public void visit(GroovyCodeVisitor visitor) { + // GRECLIPSE add + visitor.visitEmptyExpression(this); + // GRECLIPSE end + } + + @Override + public void setType(ClassNode t) { + throw createUnsupportedOperationException(); + } + + @Override + public void addAnnotation(AnnotationNode value) { + throw createUnsupportedOperationException(); + } + + @Override + public void addAnnotations(List annotations) { + throw createUnsupportedOperationException(); + } + + @Override + public void setSynthetic(boolean synthetic) { + throw createUnsupportedOperationException(); + } + + @Override + public void setDeclaringClass(ClassNode declaringClass) { + throw createUnsupportedOperationException(); + } + + @Override + public void setHasNoRealSourcePosition(boolean value) { + throw createUnsupportedOperationException(); + } + + @Override + public void setLineNumber(int lineNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setColumnNumber(int columnNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setLastLineNumber(int lastLineNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setLastColumnNumber(int lastColumnNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setSourcePosition(ASTNode node) { + throw createUnsupportedOperationException(); + } + + @Override + public void copyNodeMetaData(NodeMetaDataHandler other) { + throw createUnsupportedOperationException(); + } + + @Override + public void setNodeMetaData(Object key, Object value) { + throw createUnsupportedOperationException(); + } + + @Override + public Object putNodeMetaData(Object key, Object value) { + throw createUnsupportedOperationException(); + } + + @Override + public void removeNodeMetaData(Object key) { + throw createUnsupportedOperationException(); + } + + @Override + public void setMetaDataMap(Map metaDataMap) { + throw createUnsupportedOperationException(); + } + + private UnsupportedOperationException createUnsupportedOperationException() { + return new UnsupportedOperationException("EmptyExpression.INSTANCE is immutable"); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/MethodCallExpression.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/MethodCallExpression.java new file mode 100644 index 0000000000..49526122ce --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/expr/MethodCallExpression.java @@ -0,0 +1,233 @@ +/* + * 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.ast.expr; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.MethodNode; + +/** + * A method call on an object or class + * + * @author James Strachan + */ +public class MethodCallExpression extends Expression implements MethodCall { + + private Expression objectExpression; + private Expression method; + private Expression arguments; + private boolean spreadSafe = false; + private boolean safe = false; + private boolean implicitThis; + + // type spec for generics + private GenericsType[] genericsTypes = null; + private boolean usesGenerics = false; + + private MethodNode target; + + public static final Expression NO_ARGUMENTS = new TupleExpression(); + + public MethodCallExpression(Expression objectExpression, String method, Expression arguments) { + this(objectExpression,new ConstantExpression(method),arguments); + } + + public MethodCallExpression(Expression objectExpression, Expression method, Expression arguments) { + this.objectExpression = objectExpression; + this.method = method; + if (!(arguments instanceof TupleExpression)){ + this.arguments = new TupleExpression(arguments); + } else { + this.arguments = arguments; + } + //TODO: set correct type here + // if setting type and a methodcall is the last expression in a method, + // then the method will return null if the method itself is not void too! + // (in bytecode after call: aconst_null, areturn) + this.setType(ClassHelper.DYNAMIC_TYPE); + this.setImplicitThis(true); + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitMethodCallExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + MethodCallExpression answer = + new MethodCallExpression(transformer.transform(objectExpression), transformer.transform(method), transformer.transform(arguments)); + answer.setSafe(safe); + answer.setSpreadSafe(spreadSafe); + answer.setImplicitThis(implicitThis); + answer.setGenericsTypes(genericsTypes); + answer.setSourcePosition(this); + answer.setMethodTarget(target); + answer.copyNodeMetaData(this); + return answer; + } + + public Expression getArguments() { + return arguments; + } + + public void setArguments(Expression arguments) { + if (!(arguments instanceof TupleExpression)){ + this.arguments = new TupleExpression(arguments); + } else { + this.arguments = arguments; + } + } + + public Expression getMethod() { + return method; + } + + public void setMethod(Expression method) { + this.method = method; + } + + public ASTNode getReceiver() { + return getObjectExpression(); + } + + /** + * This method returns the method name as String if it is no dynamic + * calculated method name, but a constant. + */ + public String getMethodAsString() { + if (! (method instanceof ConstantExpression)) return null; + ConstantExpression constant = (ConstantExpression) method; + return constant.getText(); + } + + public void setObjectExpression(Expression objectExpression) { + this.objectExpression = objectExpression; + } + + public Expression getObjectExpression() { + return objectExpression; + } + + public String getText() { + String object = objectExpression.getText(); + String meth = method.getText(); + String args = arguments.getText(); + String spread = spreadSafe ? "*" : ""; + String dereference = safe ? "?" : ""; + return object + spread + dereference + "." + meth + args; + } + + /** + * @return is this a safe method call, i.e. if true then if the source object is null + * then this method call will return null rather than throwing a null pointer exception + */ + public boolean isSafe() { + return safe; + } + + public void setSafe(boolean safe) { + this.safe = safe; + } + + public boolean isSpreadSafe() { + return spreadSafe; + } + + public void setSpreadSafe(boolean value) { + spreadSafe = value; + } + + /** + * @return true if no object expression was specified otherwise if + * some expression was specified for the object on which to evaluate + * the method then return false + */ + public boolean isImplicitThis() { + return implicitThis; + } + + public void setImplicitThis(boolean implicitThis) { + this.implicitThis = implicitThis; + } + + public String toString() { + return super.toString() + + "[object: " + + objectExpression + + " method: " + + method + + " arguments: " + + arguments + + "]"; + } + + public GenericsType[] getGenericsTypes() { + return genericsTypes; + } + + public void setGenericsTypes(GenericsType[] genericsTypes) { + usesGenerics = usesGenerics || genericsTypes != null; + this.genericsTypes = genericsTypes; + } + + public boolean isUsingGenerics() { + return usesGenerics; + } + + /** + * Sets a method call target for a direct method call. + * WARNING: A method call made this way will run outside of the MOP! + * @param mn the target as MethodNode, mn==null means no target + */ + public void setMethodTarget(MethodNode mn) { + this.target = mn; + if (mn!=null) { + setType(target.getReturnType()); + } else { + setType(ClassHelper.OBJECT_TYPE); + } + } + + /** + * @return the target as method node if set + */ + public MethodNode getMethodTarget() { + return target; + } + + // GRECLIPSE add -- GROOVY-8002 + public void setSourcePosition(ASTNode node) { + super.setSourcePosition(node); + if (node instanceof MethodCall) { + if (node instanceof MethodCallExpression) { + method.setSourcePosition(((MethodCallExpression) node).getMethod()); + } else { + method.setSourcePosition(node); + method.setEnd(method.getStart() + getMethodAsString().length()); + } + if (arguments != null) { + arguments.setSourcePosition(((MethodCall) node).getArguments()); + } + } else if (node instanceof PropertyExpression) { + method.setSourcePosition(((PropertyExpression) node).getProperty()); + } + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/stmt/EmptyStatement.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/stmt/EmptyStatement.java new file mode 100644 index 0000000000..2203091651 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/ast/stmt/EmptyStatement.java @@ -0,0 +1,107 @@ +/* + * 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.ast.stmt; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.NodeMetaDataHandler; + +import java.util.Map; + +/** + * Represents an empty statement. + */ +public class EmptyStatement extends Statement { + // GRECLIPSE edit + public static final EmptyStatement INSTANCE = new EmptyStatement() { + @Override + public void setStatementLabel(String label) { + throw createUnsupportedOperationException(); + } + + @Override + public void addStatementLabel(String label) { + throw createUnsupportedOperationException(); + } + + @Override + public void setLineNumber(int lineNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setColumnNumber(int columnNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setLastLineNumber(int lastLineNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setLastColumnNumber(int lastColumnNumber) { + throw createUnsupportedOperationException(); + } + + @Override + public void setSourcePosition(ASTNode node) { + throw createUnsupportedOperationException(); + } + + @Override + public void copyNodeMetaData(NodeMetaDataHandler other) { + throw createUnsupportedOperationException(); + } + + @Override + public void setNodeMetaData(Object key, Object value) { + throw createUnsupportedOperationException(); + } + + @Override + public Object putNodeMetaData(Object key, Object value) { + throw createUnsupportedOperationException(); + } + + @Override + public void removeNodeMetaData(Object key) { + throw createUnsupportedOperationException(); + } + + @Override + public void setMetaDataMap(Map metaDataMap) { + throw createUnsupportedOperationException(); + } + + private UnsupportedOperationException createUnsupportedOperationException() { + return new UnsupportedOperationException("EmptyStatement.INSTANCE is immutable"); + } + }; + // GRECLIPSE end + + @Override + public void visit(GroovyCodeVisitor visitor) { + } + + @Override + public boolean isEmpty() { + return true; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/AnnotationVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/AnnotationVisitor.java new file mode 100644 index 0000000000..63eeeddec3 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/AnnotationVisitor.java @@ -0,0 +1,360 @@ +/* + * 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; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.control.ErrorCollector; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.vmplugin.VMPluginFactory; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; + +/** + * An Annotation visitor responsible for: + *

    + *
  • reading annotation metadata (@Retention, @Target, attribute types)
  • + *
  • verify that an AnnotationNode conforms to annotation meta
  • + *
  • enhancing an AnnotationNode AST to reflect real annotation meta
  • + *
+ */ +public class AnnotationVisitor { + private final SourceUnit source; + private final ErrorCollector errorCollector; + private AnnotationNode annotation; + private ClassNode reportClass; + + public AnnotationVisitor(SourceUnit source, ErrorCollector errorCollector) { + this.source = source; + this.errorCollector = errorCollector; + } + + public void setReportClass(ClassNode cn) { + reportClass = cn; + } + + public AnnotationNode visit(AnnotationNode node) { + this.annotation = node; + this.reportClass = node.getClassNode(); + + if (!isValidAnnotationClass(node.getClassNode())) { + addError("class " + node.getClassNode().getName() + " is not an annotation"); + return node; + } + + // check if values have been passed for all annotation attributes that don't have defaults + if (!checkIfMandatoryAnnotationValuesPassed(node)) { + return node; + } + + // if enum constants have been used, check if they are all valid + if (!checkIfValidEnumConstsAreUsed(node)) { + return node; + } + + Map attributes = node.getMembers(); + for (Map.Entry entry : attributes.entrySet()) { + String attrName = entry.getKey(); + Expression attrExpr = transformInlineConstants(entry.getValue()); + entry.setValue(attrExpr); + ClassNode attrType = getAttributeType(node, attrName); + visitExpression(attrName, attrExpr, attrType); + } + VMPluginFactory.getPlugin().configureAnnotation(node); + return this.annotation; + } + + private boolean checkIfValidEnumConstsAreUsed(AnnotationNode node) { + Map attributes = node.getMembers(); + for (Map.Entry entry : attributes.entrySet()) { + if (!validateEnumConstant(entry.getValue())) + return false; + } + return true; + } + + private boolean validateEnumConstant(Expression exp) { + if (exp instanceof PropertyExpression) { + PropertyExpression pe = (PropertyExpression) exp; + String name = pe.getPropertyAsString(); + if (pe.getObjectExpression() instanceof ClassExpression && name != null) { + ClassExpression ce = (ClassExpression) pe.getObjectExpression(); + ClassNode type = ce.getType(); + if (type.isEnum()) { + boolean ok = false; + try { + FieldNode enumField = type.getDeclaredField(name); + ok = enumField != null && enumField.getType().equals(type); + } catch(Exception ex) { + // ignore + } + if(!ok) { + addError("No enum const " + type.getName() + "." + name, pe); + return false; + } + } + } + } + return true; + } + + private Expression transformInlineConstants(Expression exp) { + if (exp instanceof PropertyExpression) { + PropertyExpression pe = (PropertyExpression) exp; + if (pe.getObjectExpression() instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) pe.getObjectExpression(); + ClassNode type = ce.getType(); + if (type.isEnum() || !type.isResolved()) + return exp; + + try { + // GRECLIPSE add + if (type.hasClass()) { + // GRECLIPSE end + Field field = type.getTypeClass().getField(pe.getPropertyAsString()); + if (field != null && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { + return new ConstantExpression(field.get(null)); + } + // GRECLIPSE add + } else { + FieldNode fieldNode = type.getField(pe.getPropertyAsString()); + if (fieldNode != null && Modifier.isStatic(fieldNode.getModifiers()) && Modifier.isFinal(fieldNode.getModifiers())) { + Expression e = fieldNode.getInitialExpression(); + return e; + } + } + // GRECLIPSE end + } catch(Exception e) { + // ignore, leave property expression in place and we'll report later + } + } + } else if (exp instanceof ListExpression) { + ListExpression le = (ListExpression) exp; + ListExpression result = new ListExpression(); + for (Expression e : le.getExpressions()) { + result.addExpression(transformInlineConstants(e)); + } + // GRECLIPSE edd + result.setSourcePosition(exp); + // GRECLIPSE end + return result; + } + return exp; + } + + private boolean checkIfMandatoryAnnotationValuesPassed(AnnotationNode node) { + boolean ok = true; + /* GRECLIPSE edit -- temp hack; can't rely on getCode() + Map attributes = node.getMembers(); + ClassNode classNode = node.getClassNode(); + for (MethodNode mn : classNode.getMethods()) { + String methodName = mn.getName(); + // if the annotation attribute has a default, getCode() returns a ReturnStatement with the default value + if (mn.getCode() == null && !attributes.containsKey(methodName)) { + addError("No explicit/default value found for annotation attribute '" + methodName + "'", node); + ok = false; + } + } + */ + return ok; + } + + private ClassNode getAttributeType(AnnotationNode node, String attrName) { + ClassNode classNode = node.getClassNode(); + List methods = classNode.getMethods(attrName); + // if size is >1, then the method was overwritten or something, we ignore that + // if it is an error, we have to test it at another place. But size==0 is + // an error, because it means that no such attribute exists. + if (methods.isEmpty()) { + addError("'" + attrName + "'is not part of the annotation " + classNode, node); + return ClassHelper.OBJECT_TYPE; + } + MethodNode method = (MethodNode) methods.get(0); + return method.getReturnType(); + } + + private static boolean isValidAnnotationClass(ClassNode node) { + return node.implementsInterface(ClassHelper.Annotation_TYPE); + } + + protected void visitExpression(String attrName, Expression attrExp, ClassNode attrType) { + if (attrType.isArray()) { + // check needed as @Test(attr = {"elem"}) passes through the parser + if (attrExp instanceof ListExpression) { + ListExpression le = (ListExpression) attrExp; + visitListExpression(attrName, le, attrType.getComponentType()); + } else if (attrExp instanceof ClosureExpression) { + addError("Annotation list attributes must use Groovy notation [el1, el2]", attrExp); + } else { + // treat like a singleton list as per Java + ListExpression listExp = new ListExpression(); + listExp.addExpression(attrExp); + // GRECLIPSE add + listExp.setSourcePosition(ClassCodeVisitorSupport.getNonInlinedExpression(attrExp)); + // GRECLIPSE end + if (annotation != null) { + annotation.setMember(attrName, listExp); + } + visitExpression(attrName, listExp, attrType); + } + } else if (ClassHelper.isPrimitiveType(attrType)) { + visitConstantExpression(attrName, getConstantExpression(attrExp, attrType), ClassHelper.getWrapper(attrType)); + } else if (ClassHelper.STRING_TYPE.equals(attrType)) { + visitConstantExpression(attrName, getConstantExpression(attrExp, attrType), ClassHelper.STRING_TYPE); + } else if (ClassHelper.CLASS_Type.equals(attrType)) { + if (!(attrExp instanceof ClassExpression || attrExp instanceof ClosureExpression)) { + addError("Only classes and closures can be used for attribute '" + attrName + "'", attrExp); + } + } else if (attrType.isDerivedFrom(ClassHelper.Enum_Type)) { + if (attrExp instanceof PropertyExpression) { + visitEnumExpression(attrName, (PropertyExpression) attrExp, attrType); + } else { + addError("Expected enum value for attribute " + attrName, attrExp); + } + } else if (isValidAnnotationClass(attrType)) { + if (attrExp instanceof AnnotationConstantExpression) { + visitAnnotationExpression(attrName, (AnnotationConstantExpression) attrExp, attrType); + } else { + addError("Expected annotation of type '" + attrType.getName() + "' for attribute " + attrName, attrExp); + } + } else { + addError("Unexpected type " + attrType.getName(), attrExp); + } + } + + public void checkReturnType(ClassNode attrType, ASTNode node) { + if (attrType.isArray()) { + checkReturnType(attrType.getComponentType(), node); + } else if (ClassHelper.isPrimitiveType(attrType)) { + } else if (ClassHelper.STRING_TYPE.equals(attrType)) { + } else if (ClassHelper.CLASS_Type.equals(attrType)) { + } else if (attrType.isDerivedFrom(ClassHelper.Enum_Type)) { + } else if (isValidAnnotationClass(attrType)) { + } else { + addError("Unexpected return type " + attrType.getName(), node); + } + } + + private ConstantExpression getConstantExpression(Expression exp, ClassNode attrType) { + if (exp instanceof ConstantExpression) { + return (ConstantExpression) exp; + } + String base = "Expected '" + exp.getText() + "' to be an inline constant of type " + attrType.getName(); + if (exp instanceof PropertyExpression) { + addError(base + " not a property expression", exp); + } else if (exp instanceof VariableExpression && ((VariableExpression)exp).getAccessedVariable() instanceof FieldNode) { + addError(base + " not a field expression", exp); + } else { + addError(base, exp); + } + return ConstantExpression.EMPTY_EXPRESSION; + } + + /** + * @param attrName the name + * @param expression the expression + * @param attrType the type + */ + protected void visitAnnotationExpression(String attrName, AnnotationConstantExpression expression, ClassNode attrType) { + AnnotationNode annotationNode = (AnnotationNode) expression.getValue(); + AnnotationVisitor visitor = new AnnotationVisitor(this.source, this.errorCollector); + // TODO track Deprecated usage and give a warning? + visitor.visit(annotationNode); + } + + protected void visitListExpression(String attrName, ListExpression listExpr, ClassNode elementType) { + for (Expression expression : listExpr.getExpressions()) { + visitExpression(attrName, expression, elementType); + } + } + + protected void visitConstantExpression(String attrName, ConstantExpression constExpr, ClassNode attrType) { + ClassNode constType = constExpr.getType(); + ClassNode wrapperType = ClassHelper.getWrapper(constType); + if (!hasCompatibleType(attrType, wrapperType)) { + addError("Attribute '" + attrName + "' should have type '" + attrType.getName() + + "'; but found type '" + constType.getName() + "'", constExpr); + } + } + + private static boolean hasCompatibleType(ClassNode attrType, ClassNode wrapperType) { + return wrapperType.isDerivedFrom(ClassHelper.getWrapper(attrType)); + } + + protected void visitEnumExpression(String attrName, PropertyExpression propExpr, ClassNode attrType) { + if (!propExpr.getObjectExpression().getType().isDerivedFrom(attrType)) { + addError("Attribute '" + attrName + "' should have type '" + attrType.getName() + "' (Enum), but found " + + propExpr.getObjectExpression().getType().getName(), + propExpr); + } + } + + protected void addError(String msg) { + addError(msg, this.annotation); + } + + protected void addError(String msg, ASTNode expr) { + this.errorCollector.addErrorAndContinue( + new SyntaxErrorMessage(new SyntaxException(msg + " in @" + this.reportClass.getName() + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), this.source) + ); + } + + public void checkCircularReference(ClassNode searchClass, ClassNode attrType, Expression startExp) { + if (!isValidAnnotationClass(attrType)) return; + if (!(startExp instanceof AnnotationConstantExpression)) { + addError("Found '" + startExp.getText() + "' when expecting an Annotation Constant", startExp); + return; + } + AnnotationConstantExpression ace = (AnnotationConstantExpression) startExp; + AnnotationNode annotationNode = (AnnotationNode) ace.getValue(); + if (annotationNode.getClassNode().equals(searchClass)) { + addError("Circular reference discovered in " + searchClass.getName(), startExp); + return; + } + ClassNode cn = annotationNode.getClassNode(); + for (MethodNode method : cn.getMethods()) { + if (method.getReturnType().equals(searchClass)) { + addError("Circular reference discovered in " + cn.getName(), startExp); + } + ReturnStatement code = (ReturnStatement) method.getCode(); + if (code == null) continue; + checkCircularReference(searchClass, method.getReturnType(), code.getExpression()); + } + } + +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/ClassCompletionVerifier.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/ClassCompletionVerifier.java new file mode 100644 index 0000000000..a26b629443 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/ClassCompletionVerifier.java @@ -0,0 +1,750 @@ +/* + * 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; + +import org.apache.groovy.ast.tools.ClassNodeUtils; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.tools.GeneralUtils; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.MetaClassHelper; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.trait.Traits; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import static java.lang.reflect.Modifier.isAbstract; +import static java.lang.reflect.Modifier.isFinal; +import static java.lang.reflect.Modifier.isInterface; +import static java.lang.reflect.Modifier.isNative; +import static java.lang.reflect.Modifier.isPrivate; +import static java.lang.reflect.Modifier.isStatic; +import static java.lang.reflect.Modifier.isStrict; +import static java.lang.reflect.Modifier.isSynchronized; +import static java.lang.reflect.Modifier.isTransient; +import static java.lang.reflect.Modifier.isVolatile; +import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE; +import static groovyjarjarasm.asm.Opcodes.ACC_ABSTRACT; +import static groovyjarjarasm.asm.Opcodes.ACC_FINAL; +import static groovyjarjarasm.asm.Opcodes.ACC_INTERFACE; +import static groovyjarjarasm.asm.Opcodes.ACC_NATIVE; +import static groovyjarjarasm.asm.Opcodes.ACC_PRIVATE; +import static groovyjarjarasm.asm.Opcodes.ACC_PROTECTED; +import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC; +import static groovyjarjarasm.asm.Opcodes.ACC_STATIC; +import static groovyjarjarasm.asm.Opcodes.ACC_STRICT; +import static groovyjarjarasm.asm.Opcodes.ACC_SYNCHRONIZED; +import static groovyjarjarasm.asm.Opcodes.ACC_TRANSIENT; +import static groovyjarjarasm.asm.Opcodes.ACC_VOLATILE; + +/** + * Checks that a class satisfies various conditions including: + *
    + *
  • Incorrect class or method access modifiers
  • + *
  • No abstract methods appear in a non-abstract class
  • + *
  • Existence and correct visibility for inherited members
  • + *
  • Invalid attempts to override final members
  • + *
+ */ +public class ClassCompletionVerifier extends ClassCodeVisitorSupport { + private static final String[] INVALID_NAME_CHARS = {".", ":", "/", ";", "[", "<", ">"}; + // the groovy.compiler.strictNames system property is experimental and may change default value or be removed in a future version of Groovy + private final boolean strictNames = Boolean.parseBoolean(System.getProperty("groovy.compiler.strictNames", "false")); + private ClassNode currentClass; + private final SourceUnit source; + private boolean inConstructor = false; + private boolean inStaticConstructor = false; + + public ClassCompletionVerifier(SourceUnit source) { + this.source = source; + } + + public ClassNode getClassNode() { + return currentClass; + } + + public void visitClass(ClassNode node) { + ClassNode oldClass = currentClass; + currentClass = node; + checkImplementsAndExtends(node); + if (source != null && !source.getErrorCollector().hasErrors()) { + checkClassForIncorrectModifiers(node); + checkInterfaceMethodVisibility(node); + checkAbstractMethodVisibility(node); + checkClassForOverwritingFinal(node); + checkMethodsForIncorrectModifiers(node); + checkMethodsForIncorrectName(node); + checkMethodsForWeakerAccess(node); + checkMethodsForOverridingFinal(node); + checkNoAbstractMethodsNonabstractClass(node); + checkClassExtendsAllSelfTypes(node); + checkNoStaticMethodWithSameSignatureAsNonStatic(node); + checkGenericsUsage(node, node.getUnresolvedInterfaces()); + checkGenericsUsage(node, node.getUnresolvedSuperClass()); + } + super.visitClass(node); + currentClass = oldClass; + } + + private void checkNoStaticMethodWithSameSignatureAsNonStatic(final ClassNode node) { + ClassNode parent = node.getSuperClass(); + Map result; + // start with methods from the parent if any + if (parent != null) { + result = parent.getDeclaredMethodsMap(); + } else { + result = new HashMap(); + } + // add in unimplemented abstract methods from the interfaces + ClassNodeUtils.addDeclaredMethodsFromInterfaces(node, result); + for (MethodNode methodNode : node.getMethods()) { + MethodNode mn = result.get(methodNode.getTypeDescriptor()); + if (mn != null && (mn.isStatic() ^ methodNode.isStatic()) && !methodNode.isStaticConstructor()) { + if (!mn.isAbstract()) continue; + ClassNode declaringClass = mn.getDeclaringClass(); + ClassNode cn = declaringClass.getOuterClass(); + if (cn == null && declaringClass.isResolved()) { + // in case of a precompiled class, the outerclass is unknown + Class typeClass = declaringClass.getTypeClass(); + typeClass = typeClass.getEnclosingClass(); + if (typeClass != null) { + cn = ClassHelper.make(typeClass); + } + } + if (cn == null || !Traits.isTrait(cn)) { + ASTNode errorNode = methodNode; + String name = mn.getName(); + if (errorNode.getLineNumber() == -1) { + // try to get a better error message location based on the property + for (PropertyNode propertyNode : node.getProperties()) { + if (name.startsWith("set") || name.startsWith("get") || name.startsWith("is")) { + String propName = Verifier.capitalize(propertyNode.getField().getName()); + String shortName = name.substring(name.startsWith("is") ? 2 : 3); + if (propName.equals(shortName)) { + errorNode = propertyNode; + break; + } + } + } + } + addError("The " + getDescription(methodNode) + " is already defined in " + getDescription(node) + + ". You cannot have both a static and an instance method with the same signature", errorNode); + } + } + result.put(methodNode.getTypeDescriptor(), methodNode); + } + } + + private void checkInterfaceMethodVisibility(ClassNode node) { + if (!node.isInterface()) return; + for (MethodNode method : node.getMethods()) { + if (method.isPrivate()) { + addError("Method '" + method.getName() + "' is private but should be public in " + getDescription(currentClass) + ".", method); + } else if (method.isProtected()) { + addError("Method '" + method.getName() + "' is protected but should be public in " + getDescription(currentClass) + ".", method); + } + } + } + + private void checkAbstractMethodVisibility(ClassNode node) { + // we only do check abstract classes (including enums), no interfaces or non-abstract classes + if (!isAbstract(node.getModifiers()) || isInterface(node.getModifiers())) return; + + List abstractMethods = node.getAbstractMethods(); + if (abstractMethods == null || abstractMethods.isEmpty()) return; + + for (MethodNode method : abstractMethods) { + if (method.isPrivate()) { + addError("Method '" + method.getName() + "' from " + getDescription(node) + + " must not be private as it is declared as an abstract method.", method); + } + } + } + + private void checkNoAbstractMethodsNonabstractClass(ClassNode node) { + if (isAbstract(node.getModifiers())) return; + List abstractMethods = node.getAbstractMethods(); + if (abstractMethods == null) return; + for (MethodNode method : abstractMethods) { + MethodNode sameArgsMethod = node.getMethod(method.getName(), method.getParameters()); + if (sameArgsMethod==null || method.getReturnType().equals(sameArgsMethod.getReturnType())) { + // GRECLIPSE addError->addTypeError + addTypeError("Can't have an abstract method in a non-abstract class." + + " The " + getDescription(node) + " must be declared abstract or" + + " the " + getDescription(method) + " must be implemented.", node); + } else { + addError("Abstract "+getDescription(method)+" is not implemented but a " + + "method of the same name but different return type is defined: "+ + (sameArgsMethod.isStatic()?"static ":"")+ + getDescription(sameArgsMethod), method + ); + } + } + } + + private void checkClassExtendsAllSelfTypes(ClassNode node) { + int modifiers = node.getModifiers(); + if (!isInterface(modifiers)) { + for (ClassNode anInterface : GeneralUtils.getInterfacesAndSuperInterfaces(node)) { + if (Traits.isTrait(anInterface)) { + LinkedHashSet selfTypes = new LinkedHashSet(); + for (ClassNode type : Traits.collectSelfTypes(anInterface, selfTypes, true, false)) { + if (type.isInterface() && !node.implementsInterface(type)) { + addError(getDescription(node) + + " implements " + getDescription(anInterface) + + " but does not implement self type " + getDescription(type), + anInterface); + } else if (!type.isInterface() && !node.isDerivedFrom(type)) { + addError(getDescription(node) + + " implements " + getDescription(anInterface) + + " but does not extend self type " + getDescription(type), + anInterface); + } + } + } + } + } + } + + private void checkClassForIncorrectModifiers(ClassNode node) { + checkClassForAbstractAndFinal(node); + checkClassForOtherModifiers(node); + } + + private void checkClassForAbstractAndFinal(ClassNode node) { + if (!isAbstract(node.getModifiers())) return; + if (!isFinal(node.getModifiers())) return; + if (node.isInterface()) { + addError("The " + getDescription(node) + " must not be final. It is by definition abstract.", node); + } else { + addError("The " + getDescription(node) + " must not be both final and abstract.", node); + } + } + + private void checkClassForOtherModifiers(ClassNode node) { + checkClassForModifier(node, isTransient(node.getModifiers()), "transient"); + checkClassForModifier(node, isVolatile(node.getModifiers()), "volatile"); + checkClassForModifier(node, isNative(node.getModifiers()), "native"); + if (!(node instanceof InnerClassNode)) { + checkClassForModifier(node, isStatic(node.getModifiers()), "static"); + checkClassForModifier(node, isPrivate(node.getModifiers()), "private"); + } + // don't check synchronized here as it overlaps with ACC_SUPER + } + + private void checkMethodForModifier(MethodNode node, boolean condition, String modifierName) { + if (!condition) return; + addError("The " + getDescription(node) + " has an incorrect modifier " + modifierName + ".", node); + } + + private void checkClassForModifier(ClassNode node, boolean condition, String modifierName) { + if (!condition) return; + addError("The " + getDescription(node) + " has an incorrect modifier " + modifierName + ".", node); + } + + private static String getDescription(ClassNode node) { + return (node.isInterface() ? (Traits.isTrait(node)?"trait":"interface") : "class") + " '" + node.getName() + "'"; + } + + private static String getDescription(MethodNode node) { + return "method '" + node.getTypeDescriptor() + "'"; + } + + private static String getDescription(FieldNode node) { + return "field '" + node.getName() + "'"; + } + + private static String getDescription(Parameter node) { + return "parameter '" + node.getName() + "'"; + } + + private void checkAbstractDeclaration(MethodNode methodNode) { + if (!methodNode.isAbstract()) return; + if (isAbstract(currentClass.getModifiers())) return; + addError("Can't have an abstract method in a non-abstract class." + + " The " + getDescription(currentClass) + " must be declared abstract or the method '" + + methodNode.getTypeDescriptor() + "' must not be abstract.", methodNode); + } + + private void checkClassForOverwritingFinal(ClassNode cn) { + ClassNode superCN = cn.getSuperClass(); + if (superCN == null) return; + if (!isFinal(superCN.getModifiers())) return; + String msg = "You are not allowed to overwrite the final " + getDescription(superCN) + "."; + addError(msg, cn); + } + + private void checkImplementsAndExtends(ClassNode node) { + ClassNode cn = node.getSuperClass(); + if (cn.isInterface() && !node.isInterface()) { + // GRECLIPSE addError->addTypeError + addTypeError("You are not allowed to extend the " + getDescription(cn) + ", use implements instead.", node); + } + for (ClassNode anInterface : node.getInterfaces()) { + cn = anInterface; + if (!cn.isInterface()) { + // GRECLIPSE addError->addTypeError + addTypeError("You are not allowed to implement the " + getDescription(cn) + ", use extends instead.", node); + } + } + } + + private void checkMethodsForIncorrectName(ClassNode cn) { + if (!strictNames) return; + List methods = cn.getAllDeclaredMethods(); + for (MethodNode mNode : methods) { + String name = mNode.getName(); + if (name.equals("") || name.equals("")) continue; + // Groovy allows more characters than Character.isValidJavaIdentifier() would allow + // if we find a good way to encode special chars we could remove (some of) these checks + for (String ch : INVALID_NAME_CHARS) { + if (name.contains(ch)) { + addError("You are not allowed to have '" + ch + "' in a method name", mNode); + } + } + } + } + + private void checkMethodsForIncorrectModifiers(ClassNode cn) { + if (!cn.isInterface()) return; + for (MethodNode method : cn.getMethods()) { + if (method.isFinal()) { + addError("The " + getDescription(method) + " from " + getDescription(cn) + + " must not be final. It is by definition abstract.", method); + } + if (method.isStatic() && !isConstructor(method)) { + addError("The " + getDescription(method) + " from " + getDescription(cn) + + " must not be static. Only fields may be static in an interface.", method); + } + } + } + + private void checkMethodsForWeakerAccess(ClassNode cn) { + for (MethodNode method : cn.getMethods()) { + checkMethodForWeakerAccessPrivileges(method, cn); + } + } + + private static boolean isConstructor(MethodNode method) { + return method.getName().equals(""); + } + + private void checkMethodsForOverridingFinal(ClassNode cn) { + for (MethodNode method : cn.getMethods()) { + Parameter[] params = method.getParameters(); + for (MethodNode superMethod : cn.getSuperClass().getMethods(method.getName())) { + Parameter[] superParams = superMethod.getParameters(); + if (!hasEqualParameterTypes(params, superParams)) continue; + if (!superMethod.isFinal()) break; + addInvalidUseOfFinalError(method, params, superMethod.getDeclaringClass()); + return; + } + } + } + + private void addInvalidUseOfFinalError(MethodNode method, Parameter[] parameters, ClassNode superCN) { + StringBuilder msg = new StringBuilder(); + msg.append("You are not allowed to override the final method ").append(method.getName()); + appendParamsDescription(parameters, msg); + msg.append(" from ").append(getDescription(superCN)); + msg.append("."); + addError(msg.toString(), method); + } + + private void appendParamsDescription(Parameter[] parameters, StringBuilder msg) { + msg.append("("); + boolean needsComma = false; + for (Parameter parameter : parameters) { + if (needsComma) { + msg.append(","); + } else { + needsComma = true; + } + msg.append(parameter.getType()); + } + msg.append(")"); + } + + private void addWeakerAccessError(ClassNode cn, MethodNode method, Parameter[] parameters, MethodNode superMethod) { + StringBuilder msg = new StringBuilder(); + msg.append(method.getName()); + appendParamsDescription(parameters, msg); + msg.append(" in "); + msg.append(cn.getName()); + msg.append(" cannot override "); + msg.append(superMethod.getName()); + msg.append(" in "); + msg.append(superMethod.getDeclaringClass().getName()); + msg.append("; attempting to assign weaker access privileges; was "); + msg.append(superMethod.isPublic() ? "public" : "protected"); + addError(msg.toString(), method); + } + + private static boolean hasEqualParameterTypes(Parameter[] first, Parameter[] second) { + if (first.length != second.length) return false; + for (int i = 0; i < first.length; i++) { + String ft = first[i].getType().getName(); + String st = second[i].getType().getName(); + if (ft.equals(st)) continue; + return false; + } + return true; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + public void visitMethod(MethodNode node) { + inConstructor = false; + inStaticConstructor = node.isStaticConstructor(); + checkAbstractDeclaration(node); + checkRepetitiveMethod(node); + checkOverloadingPrivateAndPublic(node); + checkMethodModifiers(node); + checkGenericsUsage(node, node.getParameters()); + checkGenericsUsage(node, node.getReturnType()); + for (Parameter param : node.getParameters()) { + if (param.getType().equals(VOID_TYPE)) { + addError("The " + getDescription(param) + " in " + getDescription(node) + " has invalid type void", param); + } + } + super.visitMethod(node); + } + + private void checkMethodModifiers(MethodNode node) { + // don't check volatile here as it overlaps with ACC_BRIDGE + // additional modifiers not allowed for interfaces + if ((this.currentClass.getModifiers() & ACC_INTERFACE) != 0) { + checkMethodForModifier(node, isStrict(node.getModifiers()), "strictfp"); + checkMethodForModifier(node, isSynchronized(node.getModifiers()), "synchronized"); + checkMethodForModifier(node, isNative(node.getModifiers()), "native"); + } + } + + private void checkMethodForWeakerAccessPrivileges(MethodNode mn, ClassNode cn) { + if (mn.isPublic()) return; + Parameter[] params = mn.getParameters(); + for (MethodNode superMethod : cn.getSuperClass().getMethods(mn.getName())) { + Parameter[] superParams = superMethod.getParameters(); + if (!hasEqualParameterTypes(params, superParams)) continue; + if ((mn.isPrivate() && !superMethod.isPrivate()) || + (mn.isProtected() && superMethod.isPublic())) { + addWeakerAccessError(cn, mn, params, superMethod); + return; + } + } + } + + private void checkOverloadingPrivateAndPublic(MethodNode node) { + if (isConstructor(node)) return; + boolean hasPrivate = node.isPrivate(); + boolean hasPublic = node.isPublic(); + for (MethodNode method : currentClass.getMethods(node.getName())) { + if (method == node) continue; + if (!method.getDeclaringClass().equals(node.getDeclaringClass())) continue; + if (method.isPublic() || method.isProtected()) { + hasPublic = true; + } else { + hasPrivate = true; + } + if (hasPrivate && hasPublic) break; + } + if (hasPrivate && hasPublic) { + addError("Mixing private and public/protected methods of the same name causes multimethods to be disabled and is forbidden to avoid surprising behaviour. Renaming the private methods will solve the problem.", node); + } + } + + private void checkRepetitiveMethod(MethodNode node) { + if (isConstructor(node)) return; + for (MethodNode method : currentClass.getMethods(node.getName())) { + if (method == node) continue; + if (!method.getDeclaringClass().equals(node.getDeclaringClass())) continue; + Parameter[] p1 = node.getParameters(); + Parameter[] p2 = method.getParameters(); + if (p1.length != p2.length) continue; + addErrorIfParamsAndReturnTypeEqual(p2, p1, node, method); + } + } + + private void addErrorIfParamsAndReturnTypeEqual(Parameter[] p2, Parameter[] p1, + MethodNode node, MethodNode element) { + boolean isEqual = true; + for (int i = 0; i < p2.length; i++) { + isEqual &= p1[i].getType().equals(p2[i].getType()); + if (!isEqual) break; + } + isEqual &= node.getReturnType().equals(element.getReturnType()); + if (isEqual) { + addError("Repetitive method name/signature for " + getDescription(node) + + " in " + getDescription(currentClass) + ".", node); + } + } + + public void visitField(FieldNode node) { + if (currentClass.getDeclaredField(node.getName()) != node) { + addError("The " + getDescription(node) + " is declared multiple times.", node); + } + checkInterfaceFieldModifiers(node); + checkGenericsUsage(node, node.getType()); + if (node.getType().equals(VOID_TYPE)) { + addError("The " + getDescription(node) + " has invalid type void", node); + } + super.visitField(node); + } + + public void visitProperty(PropertyNode node) { + checkDuplicateProperties(node); + checkGenericsUsage(node, node.getType()); + super.visitProperty(node); + } + + private void checkDuplicateProperties(PropertyNode node) { + ClassNode cn = node.getDeclaringClass(); + String name = node.getName(); + String getterName = "get" + MetaClassHelper.capitalize(name); + if (Character.isUpperCase(name.charAt(0))) { + for (PropertyNode propNode : cn.getProperties()) { + String otherName = propNode.getField().getName(); + String otherGetterName = "get" + MetaClassHelper.capitalize(otherName); + if (node != propNode && getterName.equals(otherGetterName)) { + String msg = "The field " + name + " and " + otherName + " on the class " + + cn.getName() + " will result in duplicate JavaBean properties, which is not allowed"; + addError(msg, node); + } + } + } + } + + private void checkInterfaceFieldModifiers(FieldNode node) { + if (!currentClass.isInterface()) return; + if ((node.getModifiers() & (ACC_PUBLIC | ACC_STATIC | ACC_FINAL)) == 0 || + (node.getModifiers() & (ACC_PRIVATE | ACC_PROTECTED)) != 0) { + addError("The " + getDescription(node) + " is not 'public static final' but is defined in " + + getDescription(currentClass) + ".", node); + } + } + + public void visitBinaryExpression(BinaryExpression expression) { + if (expression.getOperation().getType() == Types.LEFT_SQUARE_BRACKET && + expression.getRightExpression() instanceof MapEntryExpression) { + addError("You tried to use a map entry for an index operation, this is not allowed. " + + "Maybe something should be set in parentheses or a comma is missing?", + expression.getRightExpression()); + } + super.visitBinaryExpression(expression); + + if (Types.isAssignment(expression.getOperation().getType())) { + checkFinalFieldAccess(expression.getLeftExpression()); + checkSuperOrThisOnLHS(expression.getLeftExpression()); + } + } + + private void checkSuperOrThisOnLHS(Expression expression) { + if (!(expression instanceof VariableExpression)) return; + VariableExpression ve = (VariableExpression) expression; + if (ve.isThisExpression()) { + addError("cannot have 'this' as LHS of an assignment", expression); + } else if (ve.isSuperExpression()) { + addError("cannot have 'super' as LHS of an assignment", expression); + } + } + + private void checkFinalFieldAccess(Expression expression) { + if (!(expression instanceof VariableExpression) && !(expression instanceof PropertyExpression)) return; + Variable v = null; + if (expression instanceof VariableExpression) { + VariableExpression ve = (VariableExpression) expression; + v = ve.getAccessedVariable(); + } else { + PropertyExpression propExp = ((PropertyExpression) expression); + Expression objectExpression = propExp.getObjectExpression(); + if (objectExpression instanceof VariableExpression) { + VariableExpression varExp = (VariableExpression) objectExpression; + if (varExp.isThisExpression()) { + v = currentClass.getDeclaredField(propExp.getPropertyAsString()); + } + } + } + if (v instanceof FieldNode) { + FieldNode fn = (FieldNode) v; + + /* + * if it is static final but not accessed inside a static constructor, or, + * if it is an instance final but not accessed inside a instance constructor, it is an error + */ + boolean isFinal = fn.isFinal(); + boolean isStatic = fn.isStatic(); + boolean error = isFinal && ((isStatic && !inStaticConstructor) || (!isStatic && !inConstructor)); + + if (error) addError("cannot modify" + (isStatic ? " static" : "") + " final field '" + fn.getName() + + "' outside of " + (isStatic ? "static initialization block." : "constructor."), expression); + } + } + + public void visitConstructor(ConstructorNode node) { + inConstructor = true; + inStaticConstructor = node.isStaticConstructor(); + checkGenericsUsage(node, node.getParameters()); + super.visitConstructor(node); + } + + public void visitCatchStatement(CatchStatement cs) { + if (!(cs.getExceptionType().isDerivedFrom(ClassHelper.make(Throwable.class)))) { + addError("Catch statement parameter type is not a subclass of Throwable.", cs); + } + super.visitCatchStatement(cs); + } + + public void visitMethodCallExpression(MethodCallExpression mce) { + super.visitMethodCallExpression(mce); + Expression aexp = mce.getArguments(); + if (aexp instanceof TupleExpression) { + TupleExpression arguments = (TupleExpression) aexp; + for (Expression e : arguments.getExpressions()) { + checkForInvalidDeclaration(e); + } + } else { + checkForInvalidDeclaration(aexp); + } + } + + @Override + public void visitDeclarationExpression(DeclarationExpression expression) { + super.visitDeclarationExpression(expression); + if (expression.isMultipleAssignmentDeclaration()) return; + checkInvalidDeclarationModifier(expression, ACC_ABSTRACT, "abstract"); + checkInvalidDeclarationModifier(expression, ACC_NATIVE, "native"); + checkInvalidDeclarationModifier(expression, ACC_PRIVATE, "private"); + checkInvalidDeclarationModifier(expression, ACC_PROTECTED, "protected"); + checkInvalidDeclarationModifier(expression, ACC_PUBLIC, "public"); + checkInvalidDeclarationModifier(expression, ACC_STATIC, "static"); + checkInvalidDeclarationModifier(expression, ACC_STRICT, "strictfp"); + checkInvalidDeclarationModifier(expression, ACC_SYNCHRONIZED, "synchronized"); + checkInvalidDeclarationModifier(expression, ACC_TRANSIENT, "transient"); + checkInvalidDeclarationModifier(expression, ACC_VOLATILE, "volatile"); + if (expression.getVariableExpression().getOriginType().equals(VOID_TYPE)) { + addError("The variable '" + expression.getVariableExpression().getName() + "' has invalid type void", expression); + } + } + + private void checkInvalidDeclarationModifier(DeclarationExpression expression, int modifier, String modName) { + if ((expression.getVariableExpression().getModifiers() & modifier) != 0) { + addError("Modifier '" + modName + "' not allowed here.", expression); + } + } + + private void checkForInvalidDeclaration(Expression exp) { + if (!(exp instanceof DeclarationExpression)) return; + addError("Invalid use of declaration inside method call.", exp); + } + + public void visitConstantExpression(ConstantExpression expression) { + super.visitConstantExpression(expression); + checkStringExceedingMaximumLength(expression); + } + + public void visitGStringExpression(GStringExpression expression) { + super.visitGStringExpression(expression); + for (ConstantExpression ce : expression.getStrings()) { + checkStringExceedingMaximumLength(ce); + } + } + + private void checkStringExceedingMaximumLength(ConstantExpression expression) { + Object value = expression.getValue(); + if (value instanceof String) { + String s = (String) value; + if (s.length() > 65535) { + addError("String too long. The given string is " + s.length() + " Unicode code units long, but only a maximum of 65535 is allowed.", expression); + } + } + } + + private void checkGenericsUsage(ASTNode ref, ClassNode[] nodes) { + for (ClassNode node : nodes) { + checkGenericsUsage(ref, node); + } + } + + private void checkGenericsUsage(ASTNode ref, Parameter[] params) { + for (Parameter p : params) { + checkGenericsUsage(ref, p.getType()); + } + } + + private void checkGenericsUsage(ASTNode ref, ClassNode node) { + if (node.isArray()) { + checkGenericsUsage(ref, node.getComponentType()); + } else if (!node.isRedirectNode() && node.isUsingGenerics()) { + addError( + "A transform used a generics containing ClassNode "+ node + " " + + "for "+getRefDescriptor(ref) + + "directly. You are not supposed to do this. " + + "Please create a new ClassNode referring to the old ClassNode " + + "and use the new ClassNode instead of the old one. Otherwise " + + "the compiler will create wrong descriptors and a potential " + + "NullPointerException in TypeResolver in the OpenJDK. If this is " + + "not your own doing, please report this bug to the writer of the " + + "transform.", + ref); + } + } + + private static String getRefDescriptor(ASTNode ref) { + if (ref instanceof FieldNode) { + FieldNode f = (FieldNode) ref; + return "the field "+f.getName()+" "; + } else if (ref instanceof PropertyNode) { + PropertyNode p = (PropertyNode) ref; + return "the property "+p.getName()+" "; + } else if (ref instanceof ConstructorNode) { + return "the constructor "+ref.getText()+" "; + } else if (ref instanceof MethodNode) { + return "the method "+ref.getText()+" "; + } else if (ref instanceof ClassNode) { + return "the super class "+ref+" "; + } + return " "; + } + +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/ExtendedVerifier.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/ExtendedVerifier.java new file mode 100644 index 0000000000..cfab9d9829 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/ExtendedVerifier.java @@ -0,0 +1,352 @@ +/* + * 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; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.PackageNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.tools.ParameterUtils; +import org.codehaus.groovy.control.AnnotationConstantsVisitor; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.control.ErrorCollector; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.PreciseSyntaxException; +import org.codehaus.groovy.syntax.SyntaxException; +import groovyjarjarasm.asm.Opcodes; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec; +import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse; +import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec; + +/** + * A specialized Groovy AST visitor meant to perform additional verifications upon the + * current AST. Currently it does checks on annotated nodes and annotations itself. + *

+ * Current limitations: + * - annotations on local variables are not supported + */ +public class ExtendedVerifier extends ClassCodeVisitorSupport { + public static final String JVM_ERROR_MESSAGE = "Please make sure you are running on a JVM >= 1.5"; + + private final SourceUnit source; + private ClassNode currentClass; + + public ExtendedVerifier(SourceUnit sourceUnit) { + this.source = sourceUnit; + } + + public void visitClass(ClassNode node) { + AnnotationConstantsVisitor acv = new AnnotationConstantsVisitor(); + acv.visitClass(node, this.source); + this.currentClass = node; + if (node.isAnnotationDefinition()) { + visitAnnotations(node, AnnotationNode.ANNOTATION_TARGET); + } else { + visitAnnotations(node, AnnotationNode.TYPE_TARGET); + } + PackageNode packageNode = node.getPackage(); + if (packageNode != null) { + visitAnnotations(packageNode, AnnotationNode.PACKAGE_TARGET); + } + node.visitContents(this); + } + + public void visitField(FieldNode node) { + visitAnnotations(node, AnnotationNode.FIELD_TARGET); + } + + @Override + public void visitDeclarationExpression(DeclarationExpression expression) { + visitAnnotations(expression, AnnotationNode.LOCAL_VARIABLE_TARGET); + } + + public void visitConstructor(ConstructorNode node) { + visitConstructorOrMethod(node, AnnotationNode.CONSTRUCTOR_TARGET); + } + + public void visitMethod(MethodNode node) { + visitConstructorOrMethod(node, AnnotationNode.METHOD_TARGET); + } + + private void visitConstructorOrMethod(MethodNode node, int methodTarget) { + visitAnnotations(node, methodTarget); + for (int i = 0; i < node.getParameters().length; i++) { + Parameter parameter = node.getParameters()[i]; + visitAnnotations(parameter, AnnotationNode.PARAMETER_TARGET); + } + + if (this.currentClass.isAnnotationDefinition() && !node.isStaticConstructor()) { + ErrorCollector errorCollector = new ErrorCollector(this.source.getConfiguration()); + AnnotationVisitor visitor = new AnnotationVisitor(this.source, errorCollector); + visitor.setReportClass(currentClass); + visitor.checkReturnType(node.getReturnType(), node); + if (node.getParameters().length > 0) { + addError("Annotation members may not have parameters.", node.getParameters()[0]); + } + if (node.getExceptions().length > 0) { + addError("Annotation members may not have a throws clause.", node.getExceptions()[0]); + } + ReturnStatement code = (ReturnStatement) node.getCode(); + if (code != null) { + visitor.visitExpression(node.getName(), code.getExpression(), node.getReturnType()); + visitor.checkCircularReference(currentClass, node.getReturnType(), code.getExpression()); + } + this.source.getErrorCollector().addCollectorContents(errorCollector); + } + Statement code = node.getCode(); + if (code != null) { + code.visit(this); + } + + } + + public void visitProperty(PropertyNode node) { + } + + protected void visitAnnotations(AnnotatedNode node, int target) { + if (node.getAnnotations().isEmpty()) { + return; + } + this.currentClass.setAnnotated(true); + if (!isAnnotationCompatible()) { + addError("Annotations are not supported in the current runtime. " + JVM_ERROR_MESSAGE, node); + return; + } + Map> runtimeAnnotations = new LinkedHashMap>(); + for (AnnotationNode unvisited : node.getAnnotations()) { + AnnotationNode visited = visitAnnotation0(unvisited); + String name = visited.getClassNode().getName(); + if (visited.hasRuntimeRetention()) { + List seen = runtimeAnnotations.get(name); + if (seen == null) { + seen = new ArrayList(); + } + seen.add(visited); + runtimeAnnotations.put(name, seen); + } + boolean isTargetAnnotation = name.equals("java.lang.annotation.Target"); + + // Check if the annotation target is correct, unless it's the target annotating an annotation definition + // defining on which target elements the annotation applies + if (!isTargetAnnotation && !visited.isTargetAllowed(target)) { + addError("Annotation @" + name + " is not allowed on element " + + AnnotationNode.targetToName(target), visited); + } + visitDeprecation(node, visited); + visitOverride(node, visited); + } + checkForDuplicateAnnotations(node, runtimeAnnotations); + } + + private void checkForDuplicateAnnotations(AnnotatedNode node, Map> runtimeAnnotations) { + for (Map.Entry> next : runtimeAnnotations.entrySet()) { + if (next.getValue().size() > 1) { + ClassNode repeatable = null; + AnnotationNode repeatee = next.getValue().get(0); + List repeateeAnnotations = repeatee.getClassNode().getAnnotations(); + for (AnnotationNode anno : repeateeAnnotations) { + ClassNode annoClassNode = anno.getClassNode(); + if (annoClassNode.getName().equals("java.lang.annotation.Repeatable")) { + Expression value = anno.getMember("value"); + if (value instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) value; + if (ce.getType() != null && ce.getType().isAnnotationDefinition()) { + repeatable = ce.getType(); + break; + } + } + } + } + if (repeatable != null) { + AnnotationNode collector = new AnnotationNode(repeatable); + collector.setRuntimeRetention(true); // checked earlier + List annos = new ArrayList(); + for (AnnotationNode an : next.getValue()) { + annos.add(new AnnotationConstantExpression(an)); + } + collector.addMember("value", new ListExpression(annos)); + node.addAnnotation(collector); + node.getAnnotations().removeAll(next.getValue()); + } + } + } + } + + private static void visitDeprecation(AnnotatedNode node, AnnotationNode visited) { + if (visited.getClassNode().isResolved() && visited.getClassNode().getName().equals("java.lang.Deprecated")) { + if (node instanceof MethodNode) { + MethodNode mn = (MethodNode) node; + mn.setModifiers(mn.getModifiers() | Opcodes.ACC_DEPRECATED); + } else if (node instanceof FieldNode) { + FieldNode fn = (FieldNode) node; + fn.setModifiers(fn.getModifiers() | Opcodes.ACC_DEPRECATED); + } else if (node instanceof ClassNode) { + ClassNode cn = (ClassNode) node; + cn.setModifiers(cn.getModifiers() | Opcodes.ACC_DEPRECATED); + } + } + } + + // TODO GROOVY-5011 handle case of @Override on a property + private void visitOverride(AnnotatedNode node, AnnotationNode visited) { + ClassNode annotationClassNode = visited.getClassNode(); + if (annotationClassNode.isResolved() && annotationClassNode.getName().equals("java.lang.Override")) { + if (node instanceof MethodNode && !Boolean.TRUE.equals(node.getNodeMetaData(Verifier.DEFAULT_PARAMETER_GENERATED))) { + boolean override = false; + MethodNode origMethod = (MethodNode) node; + ClassNode cNode = origMethod.getDeclaringClass(); + if (origMethod.hasDefaultValue()) { + List variants = cNode.getDeclaredMethods(origMethod.getName()); + for (MethodNode m : variants) { + if (m.getAnnotations().contains(visited) && isOverrideMethod(m)) { + override = true; + break; + } + } + } else { + override = isOverrideMethod(origMethod); + } + + if (!override) { + addError("Method '" + origMethod.getName() + "' from class '" + cNode.getName() + "' does not override " + + "method from its superclass or interfaces but is annotated with @Override.", visited); + } + } + } + } + + private static boolean isOverrideMethod(MethodNode method) { + ClassNode cNode = method.getDeclaringClass(); + ClassNode next = cNode; + outer: + while (next != null) { + Map genericsSpec = createGenericsSpec(next); + MethodNode mn = correctToGenericsSpec(genericsSpec, method); + if (next != cNode) { + ClassNode correctedNext = correctToGenericsSpecRecurse(genericsSpec, next); + MethodNode found = getDeclaredMethodCorrected(genericsSpec, mn, correctedNext); + if (found != null) break; + } + List ifaces = new ArrayList(); + ifaces.addAll(Arrays.asList(next.getInterfaces())); + Map updatedGenericsSpec = new HashMap(genericsSpec); + while (!ifaces.isEmpty()) { + ClassNode origInterface = ifaces.remove(0); + if (!origInterface.equals(ClassHelper.OBJECT_TYPE)) { + updatedGenericsSpec = createGenericsSpec(origInterface, updatedGenericsSpec); + ClassNode iNode = correctToGenericsSpecRecurse(updatedGenericsSpec, origInterface); + MethodNode found2 = getDeclaredMethodCorrected(updatedGenericsSpec, mn, iNode); + if (found2 != null) break outer; + ifaces.addAll(Arrays.asList(iNode.getInterfaces())); + } + } + ClassNode superClass = next.getUnresolvedSuperClass(); + if (superClass != null) { + next = correctToGenericsSpecRecurse(updatedGenericsSpec, superClass); + } else { + next = null; + } + } + return next != null; + } + + private static MethodNode getDeclaredMethodCorrected(Map genericsSpec, MethodNode mn, ClassNode correctedNext) { + for (MethodNode orig : correctedNext.getDeclaredMethods(mn.getName())) { + MethodNode method = correctToGenericsSpec(genericsSpec, orig); + if (ParameterUtils.parametersEqual(method.getParameters(), mn.getParameters())) { + return method; + } + } + return null; + } + + /** + * Resolve metadata and details of the annotation. + * + * @param unvisited the node to visit + * @return the visited node + */ + private AnnotationNode visitAnnotation0(AnnotationNode unvisited) { + ErrorCollector errorCollector = new ErrorCollector(this.source.getConfiguration()); + AnnotationVisitor visitor = new AnnotationVisitor(this.source, errorCollector); + AnnotationNode visited = visitor.visit(unvisited); + this.source.getErrorCollector().addCollectorContents(errorCollector); + return visited; + } + + /** + * Check if the current runtime allows Annotation usage. + * + * @return true if running on a 1.5+ runtime + */ + protected boolean isAnnotationCompatible() { + return CompilerConfiguration.isPostJDK5(this.source.getConfiguration().getTargetBytecode()); + } + + public void addError(String msg, ASTNode expr) { + // GRECLIPSE: start: use new form of error message that has an end column + if (expr instanceof AnnotationNode) { + AnnotationNode aNode = (AnnotationNode) expr; + this.source.getErrorCollector().addErrorAndContinue( + new SyntaxErrorMessage( + new PreciseSyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), aNode.getStart(), aNode.getEnd()), this.source) + ); + } else + // GRECLIPSE end + this.source.getErrorCollector().addErrorAndContinue( + new SyntaxErrorMessage( + new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), this.source) + ); + } + + @Override + protected SourceUnit getSourceUnit() { + return source; + } + + // TODO use it or lose it + public void visitGenericType(GenericsType genericsType) { + + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java new file mode 100644 index 0000000000..4b0c31ad1c --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java @@ -0,0 +1,626 @@ +/* + * 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; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.DynamicVariable; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.VariableScope; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.syntax.Types; + +import java.util.Iterator; +import java.util.LinkedList; + +import static java.lang.reflect.Modifier.isFinal; + +/** + * goes through an AST and initializes the scopes + */ +public class VariableScopeVisitor extends ClassCodeVisitorSupport { + + private VariableScope currentScope = null; + private final VariableScope headScope = new VariableScope(); + private ClassNode currentClass = null; + private final SourceUnit source; + private boolean isSpecialConstructorCall = false; + private boolean inConstructor = false; + private final boolean recurseInnerClasses; + + private final LinkedList stateStack = new LinkedList(); + + private class StateStackElement { + final VariableScope scope; + final ClassNode clazz; + final boolean inConstructor; + + StateStackElement() { + scope = VariableScopeVisitor.this.currentScope; + clazz = VariableScopeVisitor.this.currentClass; + inConstructor = VariableScopeVisitor.this.inConstructor; + } + } + + public VariableScopeVisitor(SourceUnit source, boolean recurseInnerClasses) { + this.source = source; + currentScope = headScope; + this.recurseInnerClasses = recurseInnerClasses; + } + + + public VariableScopeVisitor(SourceUnit source) { + this(source, false); + } + + // ------------------------------ + // helper methods + //------------------------------ + + private void pushState(boolean isStatic) { + stateStack.add(new StateStackElement()); + currentScope = new VariableScope(currentScope); + currentScope.setInStaticContext(isStatic); + } + + private void pushState() { + pushState(currentScope.isInStaticContext()); + } + + private void popState() { + StateStackElement element = (StateStackElement) stateStack.removeLast(); + currentScope = element.scope; + currentClass = element.clazz; + inConstructor = element.inConstructor; + } + + private void declare(Parameter[] parameters, ASTNode node) { + for (Parameter parameter : parameters) { + if (parameter.hasInitialExpression()) { + parameter.getInitialExpression().visit(this); + } + declare(parameter, node); + } + } + + private void declare(VariableExpression vex) { + vex.setInStaticContext(currentScope.isInStaticContext()); + declare(vex, vex); + vex.setAccessedVariable(vex); + } + + private void declare(Variable var, ASTNode expr) { + String scopeType = "scope"; + String variableType = "variable"; + + if (expr.getClass() == FieldNode.class) { + scopeType = "class"; + variableType = "field"; + } else if (expr.getClass() == PropertyNode.class) { + scopeType = "class"; + variableType = "property"; + } + + StringBuilder msg = new StringBuilder(); + msg.append("The current ").append(scopeType); + msg.append(" already contains a ").append(variableType); + msg.append(" of the name ").append(var.getName()); + + if (currentScope.getDeclaredVariable(var.getName()) != null) { + addError(msg.toString(), expr); + return; + } + + for (VariableScope scope = currentScope.getParent(); scope != null; scope = scope.getParent()) { + // if we are in a class and no variable is declared until + // now, then we can break the loop, because we are allowed + // to declare a variable of the same name as a class member + if (scope.getClassScope() != null) break; + + if (scope.getDeclaredVariable(var.getName()) != null) { + // variable already declared + addError(msg.toString(), expr); + break; + } + } + // declare the variable even if there was an error to allow more checks + currentScope.putDeclaredVariable(var); + } + + protected SourceUnit getSourceUnit() { + return source; + } + + private Variable findClassMember(ClassNode cn, String name) { + if (cn == null) return null; + if (cn.isScript()) { + return new DynamicVariable(name, false); + } + + for (FieldNode fn : cn.getFields()) { + if (fn.getName().equals(name)) return fn; + } + + for (MethodNode mn : cn.getMethods()) { + String pName = getPropertyName(mn); + // GRECLIPSE edit + //if (pName != null && pName.equals(name)) + // return new PropertyNode(pName, mn.getModifiers(), ClassHelper.OBJECT_TYPE, cn, null, null, null); + if (pName != null && pName.equals(name)) { + PropertyNode property = new PropertyNode(pName, mn.getModifiers(), getPropertyType(mn), cn, null, null, null); + property.getField().setDeclaringClass(cn); + property.setDeclaringClass(cn); + return property; + } + // GRECLIPSE end + } + + for (PropertyNode pn : cn.getProperties()) { + if (pn.getName().equals(name)) return pn; + } + + Variable ret = findClassMember(cn.getSuperClass(), name); + if (ret != null) return ret; + return findClassMember(cn.getOuterClass(), name); + } + + // GRECLIPSE add + private ClassNode getPropertyType(MethodNode m) { + if (m.getReturnType() != ClassHelper.VOID_TYPE) { + return m.getReturnType(); + } + return m.getParameters()[0].getType(); + } + // GRECLIPSE end + + private static String getPropertyName(MethodNode m) { + String name = m.getName(); + if (!(name.startsWith("set") || name.startsWith("get"))) return null; + String pname = name.substring(3); + if (pname.length() == 0) return null; + pname = java.beans.Introspector.decapitalize(pname); + + if (name.startsWith("get") && (m.getReturnType() == ClassHelper.VOID_TYPE || m.getParameters().length != 0)) { + return null; + } + if (name.startsWith("set") && m.getParameters().length != 1) { + return null; + } + return pname; + } + + // ------------------------------- + // different Variable based checks + // ------------------------------- + + private Variable checkVariableNameForDeclaration(String name, Expression expression) { + if ("super".equals(name) || "this".equals(name)) return null; + + VariableScope scope = currentScope; + Variable var = new DynamicVariable(name, currentScope.isInStaticContext()); + Variable orig = var; + // try to find a declaration of a variable + boolean crossingStaticContext = false; + while (true) { + crossingStaticContext = crossingStaticContext || scope.isInStaticContext(); + Variable var1; + var1 = scope.getDeclaredVariable(var.getName()); + + if (var1 != null) { + var = var1; + break; + } + + var1 = scope.getReferencedLocalVariable(var.getName()); + if (var1 != null) { + var = var1; + break; + } + + var1 = scope.getReferencedClassVariable(var.getName()); + if (var1 != null) { + var = var1; + break; + } + + ClassNode classScope = scope.getClassScope(); + if (classScope != null) { + Variable member = findClassMember(classScope, var.getName()); + if (member != null) { + boolean staticScope = crossingStaticContext || isSpecialConstructorCall; + boolean staticMember = member.isInStaticContext(); + // We don't allow a static context (e.g. a static method) to access + // a non-static variable (e.g. a non-static field). + if (!(staticScope && !staticMember)) + var = member; + } + break; + } + scope = scope.getParent(); + } + if (var == orig && crossingStaticContext) { + var = new DynamicVariable(var.getName(), true); + } + + VariableScope end = scope; + + scope = currentScope; + while (scope != end) { + if (end.isClassScope() || + (end.isReferencedClassVariable(name) && end.getDeclaredVariable(name) == null)) { + scope.putReferencedClassVariable(var); + } else { + //var.setClosureSharedVariable(var.isClosureSharedVariable() || inClosure); + scope.putReferencedLocalVariable(var); + } + scope = scope.getParent(); + } + + return var; + } + + /** + * a property on "this", like this.x is transformed to a + * direct field access, so we need to check the + * static context here + * + * @param pe the property expression to check + */ + private void checkPropertyOnExplicitThis(PropertyExpression pe) { + if (!currentScope.isInStaticContext()) return; + Expression object = pe.getObjectExpression(); + if (!(object instanceof VariableExpression)) return; + VariableExpression ve = (VariableExpression) object; + if (!ve.getName().equals("this")) return; + String name = pe.getPropertyAsString(); + if (name == null || name.equals("class")) return; + Variable member = findClassMember(currentClass, name); + if (member == null) return; + checkVariableContextAccess(member, pe); + } + + private void checkVariableContextAccess(Variable v, Expression expr) { + if (v.isInStaticContext() || !currentScope.isInStaticContext()) return; + + String msg = v.getName() + + " is declared in a dynamic context, but you tried to" + + " access it from a static context."; + addError(msg, expr); + + // declare a static variable to be able to continue the check + DynamicVariable v2 = new DynamicVariable(v.getName(), currentScope.isInStaticContext()); + currentScope.putDeclaredVariable(v2); + } + + // ------------------------------ + // code visit + // ------------------------------ + + public void visitBlockStatement(BlockStatement block) { + pushState(); + block.setVariableScope(currentScope); + super.visitBlockStatement(block); + popState(); + } + + public void visitForLoop(ForStatement forLoop) { + pushState(); + forLoop.setVariableScope(currentScope); + Parameter p = forLoop.getVariable(); + p.setInStaticContext(currentScope.isInStaticContext()); + if (p != ForStatement.FOR_LOOP_DUMMY) declare(p, forLoop); + super.visitForLoop(forLoop); + popState(); + } + + public void visitIfElse(IfStatement ifElse) { + ifElse.getBooleanExpression().visit(this); + pushState(); + ifElse.getIfBlock().visit(this); + popState(); + pushState(); + ifElse.getElseBlock().visit(this); + popState(); + } + + public void visitDeclarationExpression(DeclarationExpression expression) { + visitAnnotations(expression); + // visit right side first to avoid the usage of a + // variable before its declaration + expression.getRightExpression().visit(this); + + if (expression.isMultipleAssignmentDeclaration()) { + TupleExpression list = expression.getTupleExpression(); + for (Expression e : list.getExpressions()) { + declare((VariableExpression) e); + } + } else { + declare(expression.getVariableExpression()); + } + } + + @Override + public void visitBinaryExpression(BinaryExpression be) { + super.visitBinaryExpression(be); + + if (Types.isAssignment(be.getOperation().getType())) { + checkFinalFieldAccess(be.getLeftExpression()); + } + } + + private void checkFinalFieldAccess(Expression expression) { + // currently not looking for PropertyExpression: dealt with at runtime using ReadOnlyPropertyException + if (!(expression instanceof VariableExpression) && !(expression instanceof TupleExpression)) return; + if (expression instanceof TupleExpression) { + TupleExpression list = (TupleExpression) expression; + for (Expression e : list.getExpressions()) { + checkForFinal(expression, (VariableExpression) e); + } + } else { + checkForFinal(expression, (VariableExpression) expression); + } + } + + // TODO handle local variables + private void checkForFinal(final Expression expression, VariableExpression ve) { + Variable v = ve.getAccessedVariable(); + if (v != null) { + boolean isFinal = isFinal(v.getModifiers()); + boolean isParameter = v instanceof Parameter; + if (isFinal && isParameter) { + addError("Cannot assign a value to final variable '" + v.getName() + "'", expression); + } + } + } + + public void visitVariableExpression(VariableExpression expression) { + String name = expression.getName(); + Variable v = checkVariableNameForDeclaration(name, expression); + if (v == null) return; + expression.setAccessedVariable(v); + checkVariableContextAccess(v, expression); + } + + public void visitPropertyExpression(PropertyExpression expression) { + expression.getObjectExpression().visit(this); + expression.getProperty().visit(this); + checkPropertyOnExplicitThis(expression); + } + + public void visitClosureExpression(ClosureExpression expression) { + pushState(); + + expression.setVariableScope(currentScope); + + if (expression.isParameterSpecified()) { + Parameter[] parameters = expression.getParameters(); + for (Parameter parameter : parameters) { + parameter.setInStaticContext(currentScope.isInStaticContext()); + if (parameter.hasInitialExpression()) { + parameter.getInitialExpression().visit(this); + } + declare(parameter, expression); + } + } else if (expression.getParameters() != null) { + Parameter var = new Parameter(ClassHelper.OBJECT_TYPE, "it"); + var.setInStaticContext(currentScope.isInStaticContext()); + currentScope.putDeclaredVariable(var); + } + + super.visitClosureExpression(expression); + markClosureSharedVariables(); + + popState(); + } + + private void markClosureSharedVariables() { + VariableScope scope = currentScope; + for (Iterator it = scope.getReferencedLocalVariablesIterator(); it.hasNext(); ) { + it.next().setClosureSharedVariable(true); + } + } + + public void visitCatchStatement(CatchStatement statement) { + pushState(); + Parameter p = statement.getVariable(); + p.setInStaticContext(currentScope.isInStaticContext()); + declare(p, statement); + super.visitCatchStatement(statement); + popState(); + } + + public void visitFieldExpression(FieldExpression expression) { + String name = expression.getFieldName(); + //TODO: change that to get the correct scope + Variable v = checkVariableNameForDeclaration(name, expression); + checkVariableContextAccess(v, expression); + } + + // ------------------------------ + // class visit + // ------------------------------ + + public void visitClass(ClassNode node) { + // AIC are already done, doing them here again will lead to wrong scopes + if (node instanceof InnerClassNode) { + InnerClassNode in = (InnerClassNode) node; + if (in.isAnonymous() && !in.isEnum()) return; + } + + pushState(); + + prepareVisit(node); + + super.visitClass(node); + if (recurseInnerClasses) { + Iterator innerClasses = node.getInnerClasses(); + while (innerClasses.hasNext()) { + visitClass(innerClasses.next()); + } + } + popState(); + } + + /** + * Setup the current class node context. + * @param node + */ + public void prepareVisit(ClassNode node) { + currentClass = node; + currentScope.setClassScope(node); + } + + protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { + pushState(node.isStatic()); + inConstructor = isConstructor; + node.setVariableScope(currentScope); + visitAnnotations(node); + + // GROOVY-2156 + Parameter[] parameters = node.getParameters(); + for (Parameter parameter : parameters) { + visitAnnotations(parameter); + } + + declare(node.getParameters(), node); + visitClassCodeContainer(node.getCode()); + + popState(); + } + + public void visitMethodCallExpression(MethodCallExpression call) { + if (call.isImplicitThis() && call.getMethod() instanceof ConstantExpression) { + ConstantExpression methodNameConstant = (ConstantExpression) call.getMethod(); + Object value = methodNameConstant.getText(); + + if (!(value instanceof String)) { + throw new GroovyBugError("tried to make a method call with a non-String constant method name."); + } + + String methodName = (String) value; + Variable v = checkVariableNameForDeclaration(methodName, call); + if (v != null && !(v instanceof DynamicVariable)) { + checkVariableContextAccess(v, call); + } + + if (v instanceof VariableExpression || v instanceof Parameter) { + VariableExpression object = new VariableExpression(v); + object.setSourcePosition(methodNameConstant); + call.setObjectExpression(object); + ConstantExpression method = new ConstantExpression("call"); + method.setSourcePosition(methodNameConstant); // important for GROOVY-4344 + call.setImplicitThis(false); + call.setMethod(method); + } + + } + super.visitMethodCallExpression(call); + } + + public void visitConstructorCallExpression(ConstructorCallExpression call) { + isSpecialConstructorCall = call.isSpecialCall(); + super.visitConstructorCallExpression(call); + isSpecialConstructorCall = false; + if (!call.isUsingAnonymousInnerClass()) return; + + pushState(); + InnerClassNode innerClass = (InnerClassNode) call.getType(); + innerClass.setVariableScope(currentScope); + for (MethodNode method : innerClass.getMethods()) { + Parameter[] parameters = method.getParameters(); + if (parameters.length == 0) parameters = null; // null means no implicit "it" + ClosureExpression cl = new ClosureExpression(parameters, method.getCode()); + visitClosureExpression(cl); + } + + for (FieldNode field : innerClass.getFields()) { + final Expression expression = field.getInitialExpression(); + pushState(field.isStatic()); + if (expression != null) { + if (expression instanceof VariableExpression) { + VariableExpression vexp = (VariableExpression) expression; + if (vexp.getAccessedVariable() instanceof Parameter) { + // workaround for GROOVY-6834: accessing a parameter which is not yet seen in scope + popState(); + continue; + } + } + expression.visit(this); + } + popState(); + } + + for (Statement statement : innerClass.getObjectInitializerStatements()) { + statement.visit(this); + } + markClosureSharedVariables(); + popState(); + } + + public void visitProperty(PropertyNode node) { + pushState(node.isStatic()); + super.visitProperty(node); + popState(); + } + + public void visitField(FieldNode node) { + pushState(node.isStatic()); + super.visitField(node); + popState(); + } + + /* GRECLIPSE edit + public void visitAnnotations(AnnotatedNode node) { + List annotations = node.getAnnotations(); + if (annotations.isEmpty()) return; + for (AnnotationNode an : annotations) { + // skip built-in properties + if (an.isBuiltIn()) continue; + for (Map.Entry member : an.getMembers().entrySet()) { + Expression annMemberValue = member.getValue(); + annMemberValue.visit(this); + } + } + } + */ +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/Verifier.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/Verifier.java new file mode 100644 index 0000000000..f937d17812 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/Verifier.java @@ -0,0 +1,1586 @@ +/* + * 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; + +import groovy.lang.GroovyClassLoader; +import groovy.lang.GroovyObject; +import groovy.lang.MetaClass; +import groovy.transform.Generated; +import org.apache.groovy.ast.tools.ClassNodeUtils; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.CodeVisitorSupport; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.GroovyClassVisitor; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.VariableScope; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.tools.GenericsUtils; +import org.codehaus.groovy.ast.tools.PropertyNodeUtils; +import org.codehaus.groovy.classgen.asm.BytecodeHelper; +import org.codehaus.groovy.classgen.asm.MopWriter; +import org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.ClassNodeSkip; +import org.codehaus.groovy.classgen.asm.WriterController; +import org.codehaus.groovy.reflection.ClassInfo; +import org.codehaus.groovy.runtime.MetaClassHelper; +import org.codehaus.groovy.syntax.RuntimeParserException; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.trait.Traits; +import groovyjarjarasm.asm.Label; +import groovyjarjarasm.asm.MethodVisitor; +import groovyjarjarasm.asm.Opcodes; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.lang.reflect.Modifier.isAbstract; +import static java.lang.reflect.Modifier.isFinal; +import static java.lang.reflect.Modifier.isPrivate; +import static java.lang.reflect.Modifier.isPublic; +import static java.lang.reflect.Modifier.isStatic; +import static org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType; +import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec; +import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec; + +/** + * Verifies the AST node and adds any default AST code before bytecode generation occurs. + * + * Checks include: + *

    + *
  • Methods with duplicate signatures
  • + *
  • Duplicate interfaces
  • + *
  • Reassigned final variables/parameters
  • + *
  • Uninitialized variables
  • + *
  • Bad code in object initializers or constructors
  • + *
  • Mismatches in modifiers or return types between implementations and interfaces/abstract classes
  • + *
+ * + * Added code includes: + *
    + *
  • Methods needed to implement GroovyObject
  • + *
  • Property accessor methods
  • + *
  • Covariant methods
  • + *
  • Additional methods/constructors as needed for default parameters
  • + *
+ */ +public class Verifier implements GroovyClassVisitor, Opcodes { + + public static final String STATIC_METACLASS_BOOL = "__$stMC"; + public static final String SWAP_INIT = "__$swapInit"; + public static final String INITIAL_EXPRESSION = "INITIAL_EXPRESSION"; + public static final String DEFAULT_PARAMETER_GENERATED = "DEFAULT_PARAMETER_GENERATED"; + + // NOTE: timeStamp constants shouldn't belong to Verifier but kept here + // for binary compatibility + public static final String __TIMESTAMP = "__timeStamp"; + public static final String __TIMESTAMP__ = "__timeStamp__239_neverHappen"; + private static final Parameter[] INVOKE_METHOD_PARAMS = new Parameter[]{ + new Parameter(ClassHelper.STRING_TYPE, "method"), + new Parameter(ClassHelper.OBJECT_TYPE, "arguments") + }; + private static final Parameter[] SET_PROPERTY_PARAMS = new Parameter[]{ + new Parameter(ClassHelper.STRING_TYPE, "property"), + new Parameter(ClassHelper.OBJECT_TYPE, "value") + }; + private static final Parameter[] GET_PROPERTY_PARAMS = new Parameter[]{ + new Parameter(ClassHelper.STRING_TYPE, "property") + }; + private static final Parameter[] SET_METACLASS_PARAMS = new Parameter[]{ + new Parameter(ClassHelper.METACLASS_TYPE, "mc") + }; + + private static final Class GENERATED_ANNOTATION = Generated.class; + + private ClassNode classNode; + private MethodNode methodNode; + // GRECLIPSE add + public boolean inlineStaticFieldInitializersIntoClinit = true; + // GRECLIPSE end + + public ClassNode getClassNode() { + return classNode; + } + + protected void setClassNode(ClassNode classNode) { + this.classNode = classNode; + } + + public MethodNode getMethodNode() { + return methodNode; + } + + private static FieldNode setMetaClassFieldIfNotExists(ClassNode node, FieldNode metaClassField) { + if (metaClassField != null) return metaClassField; + final String classInternalName = BytecodeHelper.getClassInternalName(node); + metaClassField = + node.addField("metaClass", ACC_PRIVATE | ACC_TRANSIENT | ACC_SYNTHETIC, ClassHelper.METACLASS_TYPE, + new BytecodeExpression(ClassHelper.METACLASS_TYPE) { + public void visit(MethodVisitor mv) { + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false); + } + }); + metaClassField.setSynthetic(true); + return metaClassField; + } + + private static FieldNode getMetaClassField(ClassNode node) { + FieldNode ret = node.getDeclaredField("metaClass"); + if (ret != null) { + ClassNode mcFieldType = ret.getType(); + if (!mcFieldType.equals(ClassHelper.METACLASS_TYPE)) { + throw new RuntimeParserException("The class " + node.getName() + + " cannot declare field 'metaClass' of type " + mcFieldType.getName() + " as it needs to be of " + + "the type " + ClassHelper.METACLASS_TYPE.getName() + " for internal groovy purposes", ret); + } + return ret; + } + ClassNode current = node; + while (current != ClassHelper.OBJECT_TYPE) { + current = current.getSuperClass(); + if (current == null) break; + ret = current.getDeclaredField("metaClass"); + if (ret == null) continue; + if (isPrivate(ret.getModifiers())) continue; + return ret; + } + return null; + } + + /** + * walk the class + * + * @param node the node to visit + */ + public void visitClass(final ClassNode node) { + this.classNode = node; + + if (Traits.isTrait(node) // maybe possible to have this true in joint compilation mode + || classNode.isInterface()) { + //interfaces have no constructors, but this code expects one, + //so create a dummy and don't add it to the class node + ConstructorNode dummy = new ConstructorNode(0, null); + addInitialization(node, dummy); + node.visitContents(this); + if (classNode.getNodeMetaData(ClassNodeSkip.class) == null) { + classNode.setNodeMetaData(ClassNodeSkip.class, true); + } + return; + } + + ClassNode[] classNodes = classNode.getInterfaces(); + List interfaces = new ArrayList(); + for (ClassNode classNode : classNodes) { + interfaces.add(classNode.getName()); + } + Set interfaceSet = new HashSet(interfaces); + if (interfaceSet.size() != interfaces.size()) { + throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode); + } + + addDefaultParameterMethods(node); + addDefaultParameterConstructors(node); + + final String classInternalName = BytecodeHelper.getClassInternalName(node); + + addStaticMetaClassField(node, classInternalName); + + boolean knownSpecialCase = + node.isDerivedFrom(ClassHelper.GSTRING_TYPE) + || node.isDerivedFrom(ClassHelper.GROOVY_OBJECT_SUPPORT_TYPE); + + addFastPathHelperFieldsAndHelperMethod(node, classInternalName, knownSpecialCase); + if (!knownSpecialCase) addGroovyObjectInterfaceAndMethods(node, classInternalName); + + addDefaultConstructor(node); + + addInitialization(node); + checkReturnInObjectInitializer(node.getObjectInitializerStatements()); + node.getObjectInitializerStatements().clear(); + node.visitContents(this); + checkForDuplicateMethods(node); + addCovariantMethods(node); + + checkFinalVariables(node); + } + + private void checkFinalVariables(ClassNode node) { + FinalVariableAnalyzer analyzer = new FinalVariableAnalyzer(null, getFinalVariablesCallback()); + analyzer.visitClass(node); + + } + + protected FinalVariableAnalyzer.VariableNotFinalCallback getFinalVariablesCallback() { + return new FinalVariableAnalyzer.VariableNotFinalCallback() { + @Override + public void variableNotFinal(Variable var, Expression bexp) { + if (var instanceof VariableExpression) { + var = ((VariableExpression) var).getAccessedVariable(); + } + if (var instanceof VariableExpression && isFinal(var.getModifiers())) { + throw new RuntimeParserException("The variable [" + var.getName() + "] is declared final but is reassigned", bexp); + } + if (var instanceof Parameter && isFinal(var.getModifiers())) { + throw new RuntimeParserException("The parameter [" + var.getName() + "] is declared final but is reassigned", bexp); + } + } + + @Override + public void variableNotAlwaysInitialized(final VariableExpression var) { + if (Modifier.isFinal(var.getAccessedVariable().getModifiers())) + throw new RuntimeParserException("The variable [" + var.getName() + "] may be uninitialized", var); + } + }; + } + + private static void checkForDuplicateMethods(ClassNode cn) { + Set descriptors = new HashSet(); + for (MethodNode mn : cn.getMethods()) { + if (mn.isSynthetic()) continue; + String mySig = methodDescriptorWithoutReturnType(mn); + if (descriptors.contains(mySig)) { + if (mn.isScriptBody() || mySig.equals(scriptBodySignatureWithoutReturnType(cn))) { + throw new RuntimeParserException("The method " + mn.getText() + + " is a duplicate of the one declared for this script's body code", mn); + } else { + throw new RuntimeParserException("The method " + mn.getText() + + " duplicates another method of the same signature", mn); + } + } + descriptors.add(mySig); + } + } + + private static String scriptBodySignatureWithoutReturnType(ClassNode cn) { + for (MethodNode mn : cn.getMethods()) { + if (mn.isScriptBody()) return methodDescriptorWithoutReturnType(mn); + } + return null; + } + + private static FieldNode checkFieldDoesNotExist(ClassNode node, String fieldName) { + FieldNode ret = node.getDeclaredField(fieldName); + if (ret != null) { + if (isPublic(ret.getModifiers()) && + ret.getType().redirect() == ClassHelper.boolean_TYPE) { + return ret; + } + throw new RuntimeParserException("The class " + node.getName() + + " cannot declare field '" + fieldName + "' as this" + + " field is needed for internal groovy purposes", ret); + } + return null; + } + + private static void addFastPathHelperFieldsAndHelperMethod(ClassNode node, final String classInternalName, boolean knownSpecialCase) { + if (node.getNodeMetaData(ClassNodeSkip.class) != null) return; + FieldNode stMCB = checkFieldDoesNotExist(node, STATIC_METACLASS_BOOL); + if (stMCB == null) { + stMCB = node.addField( + STATIC_METACLASS_BOOL, + ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC | ACC_TRANSIENT, + ClassHelper.boolean_TYPE, null); + stMCB.setSynthetic(true); + } + } + + protected void addDefaultConstructor(ClassNode node) { + if (!node.getDeclaredConstructors().isEmpty()) return; + + BlockStatement empty = new BlockStatement(); + // GRECLIPSE edit -- We don't want source locations for synthetic default constructors + //empty.setSourcePosition(node); + ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, empty); + //constructor.setSourcePosition(node); + // GRECLIPSE end + constructor.setHasNoRealSourcePosition(true); + node.addConstructor(constructor); + } + + private void addStaticMetaClassField(final ClassNode node, final String classInternalName) { + String _staticClassInfoFieldName = "$staticClassInfo"; + while (node.getDeclaredField(_staticClassInfoFieldName) != null) + _staticClassInfoFieldName = _staticClassInfoFieldName + "$"; + final String staticMetaClassFieldName = _staticClassInfoFieldName; + + FieldNode staticMetaClassField = node.addField(staticMetaClassFieldName, ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.make(ClassInfo.class, false), null); + staticMetaClassField.setSynthetic(true); + + node.addSyntheticMethod( + "$getStaticMetaClass", + ACC_PROTECTED, + ClassHelper.make(MetaClass.class), + Parameter.EMPTY_ARRAY, + ClassNode.EMPTY_ARRAY, + new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + if (BytecodeHelper.isClassLiteralPossible(node) || BytecodeHelper.isSameCompilationUnit(classNode, node)) { + BytecodeHelper.visitClassLiteral(mv, node); + } else { + mv.visitMethodInsn(INVOKESTATIC, classInternalName, "$get$$class$" + classInternalName.replaceAll("/", "\\$"), "()Ljava/lang/Class;", false); + } + Label l1 = new Label(); + mv.visitJumpInsn(IF_ACMPEQ, l1); + + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/ScriptBytecodeAdapter", "initMetaClass", "(Ljava/lang/Object;)Lgroovy/lang/MetaClass;", false); + mv.visitInsn(ARETURN); + + mv.visitLabel(l1); + + mv.visitFieldInsn(GETSTATIC, classInternalName, staticMetaClassFieldName, "Lorg/codehaus/groovy/reflection/ClassInfo;"); + mv.visitVarInsn(ASTORE, 1); + mv.visitVarInsn(ALOAD, 1); + Label l0 = new Label(); + mv.visitJumpInsn(IFNONNULL, l0); + + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/reflection/ClassInfo", "getClassInfo", "(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;", false); + mv.visitInsn(DUP); + mv.visitVarInsn(ASTORE, 1); + mv.visitFieldInsn(PUTSTATIC, classInternalName, staticMetaClassFieldName, "Lorg/codehaus/groovy/reflection/ClassInfo;"); + + mv.visitLabel(l0); + + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "org/codehaus/groovy/reflection/ClassInfo", "getMetaClass", "()Lgroovy/lang/MetaClass;", false); + mv.visitInsn(ARETURN); + + } + }) + ); + } + + protected void addGroovyObjectInterfaceAndMethods(ClassNode node, final String classInternalName) { + if (!node.isDerivedFromGroovyObject()) node.addInterface(ClassHelper.make(GroovyObject.class)); + FieldNode metaClassField = getMetaClassField(node); + + boolean shouldAnnotate = classNode.getModule().getContext() != null; + AnnotationNode generatedAnnotation = shouldAnnotate ? new AnnotationNode(ClassHelper.make(GENERATED_ANNOTATION)) : null; + + if (!node.hasMethod("getMetaClass", Parameter.EMPTY_ARRAY)) { + metaClassField = setMetaClassFieldIfNotExists(node, metaClassField); + MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()), + "getMetaClass", + ACC_PUBLIC, + ClassHelper.METACLASS_TYPE, + Parameter.EMPTY_ARRAY, + ClassNode.EMPTY_ARRAY, + new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + Label nullLabel = new Label(); + /** + * the code is: + * if (this.metaClass==null) { + * this.metaClass = this.$getStaticMetaClass() + * return this.metaClass + * } else { + * return this.metaClass + * } + * with the optimization that the result of the + * first this.metaClass is duped on the operand + * stack and reused for the return in the else part + */ + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;"); + mv.visitInsn(DUP); + mv.visitJumpInsn(IFNULL, nullLabel); + mv.visitInsn(ARETURN); + + mv.visitLabel(nullLabel); + mv.visitInsn(POP); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false); + mv.visitFieldInsn(PUTFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;"); + mv.visitInsn(ARETURN); + } + }) + ); + if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation); + } + + Parameter[] parameters = new Parameter[]{new Parameter(ClassHelper.METACLASS_TYPE, "mc")}; + if (!node.hasMethod("setMetaClass", parameters)) { + metaClassField = setMetaClassFieldIfNotExists(node, metaClassField); + Statement setMetaClassCode; + if (isFinal(metaClassField.getModifiers())) { + ConstantExpression text = new ConstantExpression("cannot set read-only meta class"); + ConstructorCallExpression cce = new ConstructorCallExpression(ClassHelper.make(IllegalArgumentException.class), text); + setMetaClassCode = new ExpressionStatement(cce); + } else { + List list = new ArrayList(); + list.add(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + /** + * the code is (meta class is stored in 1): + * this.metaClass = <1> + */ + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, classInternalName, + "metaClass", "Lgroovy/lang/MetaClass;"); + mv.visitInsn(RETURN); + } + }); + setMetaClassCode = new BytecodeSequence(list); + } + + MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()), + "setMetaClass", + ACC_PUBLIC, ClassHelper.VOID_TYPE, + SET_METACLASS_PARAMS, ClassNode.EMPTY_ARRAY, + setMetaClassCode + ); + if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation); + } + + if (!node.hasMethod("invokeMethod", INVOKE_METHOD_PARAMS)) { + VariableExpression vMethods = new VariableExpression("method"); + VariableExpression vArguments = new VariableExpression("arguments"); + VariableScope blockScope = new VariableScope(); + blockScope.putReferencedLocalVariable(vMethods); + blockScope.putReferencedLocalVariable(vArguments); + + MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()), + "invokeMethod", + ACC_PUBLIC, + ClassHelper.OBJECT_TYPE, INVOKE_METHOD_PARAMS, + ClassNode.EMPTY_ARRAY, + new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", true); + mv.visitInsn(ARETURN); + } + }) + ); + if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation); + } + + if (!node.hasMethod("getProperty", GET_PROPERTY_PARAMS)) { + MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()), + "getProperty", + ACC_PUBLIC, + ClassHelper.OBJECT_TYPE, + GET_PROPERTY_PARAMS, + ClassNode.EMPTY_ARRAY, + new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "getProperty", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", true); + mv.visitInsn(ARETURN); + } + }) + ); + if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation); + } + + if (!node.hasMethod("setProperty", SET_PROPERTY_PARAMS)) { + MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()), + "setProperty", + ACC_PUBLIC, + ClassHelper.VOID_TYPE, + SET_PROPERTY_PARAMS, + ClassNode.EMPTY_ARRAY, + new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "setProperty", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + } + }) + ); + if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation); + } + } + + /** + * Helper method to add a new method to a ClassNode. Depending on the shouldBeSynthetic flag the + * call will either be made to ClassNode.addSyntheticMethod() or ClassNode.addMethod(). If a non-synthetic method + * is to be added the ACC_SYNTHETIC modifier is removed if it has been accidentally supplied. + */ + protected MethodNode addMethod(ClassNode node, boolean shouldBeSynthetic, String name, int modifiers, ClassNode returnType, Parameter[] parameters, + ClassNode[] exceptions, Statement code) { + if (shouldBeSynthetic) { + return node.addSyntheticMethod(name, modifiers, returnType, parameters, exceptions, code); + } else { + return node.addMethod(name, modifiers & ~ACC_SYNTHETIC, returnType, parameters, exceptions, code); + } + } + + @Deprecated + protected void addTimeStamp(ClassNode node) { + } + + private static void checkReturnInObjectInitializer(List init) { + CodeVisitorSupport cvs = new CodeVisitorSupport() { + @Override + public void visitClosureExpression(ClosureExpression expression) { + // return is OK in closures in object initializers + } + + public void visitReturnStatement(ReturnStatement statement) { + throw new RuntimeParserException("'return' is not allowed in object initializer", statement); + } + }; + for (Statement stm : init) { + stm.visit(cvs); + } + } + + public void visitConstructor(ConstructorNode node) { + CodeVisitorSupport checkSuper = new CodeVisitorSupport() { + boolean firstMethodCall = true; + String type = null; + + public void visitMethodCallExpression(MethodCallExpression call) { + if (!firstMethodCall) return; + firstMethodCall = false; + String name = call.getMethodAsString(); + // the name might be null if the method name is a GString for example + if (name == null) return; + if (!name.equals("super") && !name.equals("this")) return; + type = name; + call.getArguments().visit(this); + type = null; + } + + public void visitConstructorCallExpression(ConstructorCallExpression call) { + if (!call.isSpecialCall()) return; + type = call.getText(); + call.getArguments().visit(this); + type = null; + } + + public void visitVariableExpression(VariableExpression expression) { + if (type == null) return; + String name = expression.getName(); + if (!name.equals("this") && !name.equals("super")) return; + throw new RuntimeParserException("cannot reference " + name + " inside of " + type + "(....) before supertype constructor has been called", expression); + } + }; + Statement s = node.getCode(); + if (s == null) { + return; + } else { + s.visit(new VerifierCodeVisitor(this)); + } + s.visit(checkSuper); + } + + public void visitMethod(MethodNode node) { + //GROOVY-3712 - if it's an MOP method, it's an error as they aren't supposed to exist before ACG is invoked + if (MopWriter.isMopMethod(node.getName())) { + throw new RuntimeParserException("Found unexpected MOP methods in the class node for " + classNode.getName() + + "(" + node.getName() + ")", classNode); + } + this.methodNode = node; + adjustTypesIfStaticMainMethod(node); + addReturnIfNeeded(node); + Statement statement; + statement = node.getCode(); + if (statement != null) statement.visit(new VerifierCodeVisitor(this)); + } + + private static void adjustTypesIfStaticMainMethod(MethodNode node) { + if (node.getName().equals("main") && node.isStatic()) { + Parameter[] params = node.getParameters(); + if (params.length == 1) { + Parameter param = params[0]; + if (param.getType() == null || param.getType() == ClassHelper.OBJECT_TYPE) { + param.setType(ClassHelper.STRING_TYPE.makeArray()); + ClassNode returnType = node.getReturnType(); + if (returnType == ClassHelper.OBJECT_TYPE) { + node.setReturnType(ClassHelper.VOID_TYPE); + } + } + } + } + } + + protected void addReturnIfNeeded(MethodNode node) { + ReturnAdder adder = new ReturnAdder(); + adder.visitMethod(node); + } + + public void visitField(FieldNode node) { + } + + private boolean methodNeedsReplacement(MethodNode m) { + // no method found, we need to replace + if (m == null) return true; + // method is in current class, nothing to be done + if (m.getDeclaringClass() == this.getClassNode()) return false; + // do not overwrite final + if (isFinal(m.getModifiers())) return false; + return true; + } + + public void visitProperty(PropertyNode node) { + String name = node.getName(); + FieldNode field = node.getField(); + + String getterName = "get" + capitalize(name); + String setterName = "set" + capitalize(name); + + int accessorModifiers = PropertyNodeUtils.adjustPropertyModifiersForMethod(node); + + Statement getterBlock = node.getGetterBlock(); + if (getterBlock == null) { + MethodNode getter = classNode.getGetterMethod(getterName, !node.isStatic()); + if (getter == null && ClassHelper.boolean_TYPE == node.getType()) { + String secondGetterName = "is" + capitalize(name); + getter = classNode.getGetterMethod(secondGetterName); + } + if (!node.isPrivate() && methodNeedsReplacement(getter)) { + getterBlock = createGetterBlock(node, field); + } + } + Statement setterBlock = node.getSetterBlock(); + if (setterBlock == null) { + // 2nd arg false below: though not usual, allow setter with non-void return type + MethodNode setter = classNode.getSetterMethod(setterName, false); + if (!node.isPrivate() && !isFinal(accessorModifiers) && methodNeedsReplacement(setter)) { + setterBlock = createSetterBlock(node, field); + } + } + + int getterModifiers = accessorModifiers; + // don't make static accessors final + if (node.isStatic()) { + getterModifiers = ~Modifier.FINAL & getterModifiers; + } + if (getterBlock != null) { + MethodNode getter = + new MethodNode(getterName, getterModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock); + getter.setSynthetic(true); + addPropertyMethod(getter); + visitMethod(getter); + + if (ClassHelper.boolean_TYPE == node.getType() || ClassHelper.Boolean_TYPE == node.getType()) { + String secondGetterName = "is" + capitalize(name); + MethodNode secondGetter = + new MethodNode(secondGetterName, getterModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock); + secondGetter.setSynthetic(true); + addPropertyMethod(secondGetter); + visitMethod(secondGetter); + } + } + if (setterBlock != null) { + Parameter[] setterParameterTypes = {new Parameter(node.getType(), "value")}; + MethodNode setter = + new MethodNode(setterName, accessorModifiers, ClassHelper.VOID_TYPE, setterParameterTypes, ClassNode.EMPTY_ARRAY, setterBlock); + setter.setSynthetic(true); + addPropertyMethod(setter); + visitMethod(setter); + } + } + + protected void addPropertyMethod(MethodNode method) { + classNode.addMethod(method); + // GROOVY-4415 / GROOVY-4645: check that there's no abstract method which corresponds to this one + List abstractMethods = classNode.getAbstractMethods(); + if (abstractMethods == null) return; + String methodName = method.getName(); + Parameter[] parameters = method.getParameters(); + ClassNode methodReturnType = method.getReturnType(); + for (MethodNode node : abstractMethods) { + if (!node.getDeclaringClass().equals(classNode)) continue; + if (node.getName().equals(methodName) && node.getParameters().length == parameters.length) { + if (parameters.length == 1) { + // setter + ClassNode abstractMethodParameterType = node.getParameters()[0].getType(); + ClassNode methodParameterType = parameters[0].getType(); + if (!methodParameterType.isDerivedFrom(abstractMethodParameterType) && !methodParameterType.implementsInterface(abstractMethodParameterType)) { + continue; + } + } + ClassNode nodeReturnType = node.getReturnType(); + if (!methodReturnType.isDerivedFrom(nodeReturnType) && !methodReturnType.implementsInterface(nodeReturnType)) { + continue; + } + // matching method, remove abstract status and use the same body + node.setModifiers(node.getModifiers() ^ ACC_ABSTRACT); + node.setCode(method.getCode()); + } + } + } + + public interface DefaultArgsAction { + void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method); + } + + /** + * Creates a new helper method for each combination of default parameter expressions + */ + protected void addDefaultParameterMethods(final ClassNode node) { + List methods = new ArrayList(node.getMethods()); + addDefaultParameters(methods, new DefaultArgsAction() { + public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) { + final BlockStatement code = new BlockStatement(); + + MethodNode newMethod = new MethodNode(method.getName(), method.getModifiers(), method.getReturnType(), newParams, method.getExceptions(), code); + + // GROOVY-5681 and GROOVY-5632 + for (Expression argument : arguments.getExpressions()) { + if (argument instanceof CastExpression) { + argument = ((CastExpression) argument).getExpression(); + } + if (argument instanceof ConstructorCallExpression) { + ClassNode type = argument.getType(); + if (type instanceof InnerClassNode && ((InnerClassNode) type).isAnonymous()) { + type.setEnclosingMethod(newMethod); + } + } + + // check whether closure shared variables refer to params with default values (GROOVY-5632) + if (argument instanceof ClosureExpression) { + final List newMethodNodeParameters = Arrays.asList(newParams); + + CodeVisitorSupport visitor = new CodeVisitorSupport() { + @Override + public void visitVariableExpression(VariableExpression expression) { + Variable v = expression.getAccessedVariable(); + if (!(v instanceof Parameter)) return; + + Parameter param = (Parameter) v; + if (param.hasInitialExpression() && code.getVariableScope().getDeclaredVariable(param.getName()) == null && !newMethodNodeParameters.contains(param)) { + + VariableExpression localVariable = new VariableExpression(param.getName(), ClassHelper.makeReference()); + DeclarationExpression declarationExpression = new DeclarationExpression(localVariable, Token.newSymbol(Types.EQUAL, -1, -1), new ConstructorCallExpression(ClassHelper.makeReference(), param.getInitialExpression())); + + code.addStatement(new ExpressionStatement(declarationExpression)); + code.getVariableScope().putDeclaredVariable(localVariable); + } + } + }; + + visitor.visitClosureExpression((ClosureExpression) argument); + } + } + + MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments); + expression.setMethodTarget(method); + expression.setImplicitThis(true); + + if (method.isVoidMethod()) { + code.addStatement(new ExpressionStatement(expression)); + } else { + code.addStatement(new ReturnStatement(expression)); + } + + List annotations = method.getAnnotations(); + if (annotations != null) { + newMethod.addAnnotations(annotations); + } + MethodNode oldMethod = node.getDeclaredMethod(method.getName(), newParams); + if (oldMethod != null) { + throw new RuntimeParserException( + "The method with default parameters \"" + method.getTypeDescriptor() + + "\" defines a method \"" + newMethod.getTypeDescriptor() + + "\" that is already defined.", + method); + } + addPropertyMethod(newMethod); + // GRECLIPSE add + // make sure it is known that this is a variant of another method + newMethod.setSourcePosition(method); + newMethod.setNameStart(method.getNameStart()); + newMethod.setNameEnd(method.getNameEnd()); + newMethod.setOriginal(method); + // GRECLIPSE end + newMethod.setGenericsTypes(method.getGenericsTypes()); + newMethod.putNodeMetaData(DEFAULT_PARAMETER_GENERATED, true); + } + }); + } + + protected void addDefaultParameterConstructors(final ClassNode node) { + List methods = new ArrayList(node.getDeclaredConstructors()); + addDefaultParameters(methods, new DefaultArgsAction() { + public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) { + ConstructorNode ctor = (ConstructorNode) method; + ConstructorCallExpression expression = new ConstructorCallExpression(ClassNode.THIS, arguments); + Statement code = new ExpressionStatement(expression); + addConstructor(newParams, ctor, code, node); + } + }); + } + + protected void addConstructor(Parameter[] newParams, ConstructorNode ctor, Statement code, ClassNode node) { + node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code); + } + + /** + * Creates a new helper method for each combination of default parameter expressions + */ + protected void addDefaultParameters(List methods, DefaultArgsAction action) { + for (Object next : methods) { + MethodNode method = (MethodNode) next; + if (method.hasDefaultValue()) { + addDefaultParameters(action, method); + } + } + } + + protected void addDefaultParameters(DefaultArgsAction action, MethodNode method) { + Parameter[] parameters = method.getParameters(); + int counter = 0; + List paramValues = new ArrayList(); + int size = parameters.length; + for (int i = size - 1; i >= 0; i--) { + Parameter parameter = parameters[i]; + if (parameter != null && parameter.hasInitialExpression()) { + paramValues.add(i); + paramValues.add( + new CastExpression( + parameter.getType(), + parameter.getInitialExpression() + ) + ); + counter++; + } + } + + for (int j = 1; j <= counter; j++) { + Parameter[] newParams = new Parameter[parameters.length - j]; + ArgumentListExpression arguments = new ArgumentListExpression(); + int index = 0; + int k = 1; + for (Parameter parameter : parameters) { + if (parameter == null) { + throw new GroovyBugError("Parameter should not be null for method " + methodNode.getName()); + } else { + if (k > counter - j && parameter.hasInitialExpression()) { + arguments.addExpression( + new CastExpression( + parameter.getType(), + parameter.getInitialExpression() + ) + ); + k++; + } else if (parameter.hasInitialExpression()) { + newParams[index++] = parameter; + arguments.addExpression( + new CastExpression( + parameter.getType(), + new VariableExpression(parameter.getName()) + ) + ); + k++; + } else { + newParams[index++] = parameter; + arguments.addExpression( + new CastExpression( + parameter.getType(), + new VariableExpression(parameter.getName()) + ) + ); + } + } + } + action.call(arguments, newParams, method); + } + + for (Parameter parameter : parameters) { + // remove default expression and store it as node metadata + parameter.putNodeMetaData(Verifier.INITIAL_EXPRESSION, parameter.getInitialExpression()); + parameter.setInitialExpression(null); + } + } + + protected void addClosureCode(InnerClassNode node) { + // add a new invoke + } + + protected void addInitialization(final ClassNode node) { + boolean addSwapInit = moveOptimizedConstantsInitialization(node); + + for (ConstructorNode cn : node.getDeclaredConstructors()) { + addInitialization(node, cn); + } + + if (addSwapInit) { + BytecodeSequence seq = new BytecodeSequence( + new BytecodeInstruction() { + @Override + public void visit(MethodVisitor mv) { + mv.visitMethodInsn(INVOKESTATIC, BytecodeHelper.getClassInternalName(node), SWAP_INIT, "()V", false); + } + }); + + List swapCall = new ArrayList(1); + swapCall.add(seq); + node.addStaticInitializerStatements(swapCall, true); + } + } + + protected void addInitialization(ClassNode node, ConstructorNode constructorNode) { + Statement firstStatement = constructorNode.getFirstStatement(); + // if some transformation decided to generate constructor then it probably knows who it does + if (firstStatement instanceof BytecodeSequence) + return; + + ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement); + + // in case of this(...) let the other constructor do the init + if (first != null && (first.isThisCall())) return; + + List statements = new ArrayList(); + List staticStatements = new ArrayList(); + final boolean isEnum = node.isEnum(); + List initStmtsAfterEnumValuesInit = new ArrayList(); + Set explicitStaticPropsInEnum = new HashSet(); + if (isEnum) { + for (PropertyNode propNode : node.getProperties()) { + if (!propNode.isSynthetic() && propNode.getField().isStatic()) { + explicitStaticPropsInEnum.add(propNode.getField().getName()); + } + } + for (FieldNode fieldNode : node.getFields()) { + if (!fieldNode.isSynthetic() && fieldNode.isStatic() && fieldNode.getType() != node) { + explicitStaticPropsInEnum.add(fieldNode.getName()); + } + } + } + + if (!Traits.isTrait(node)) { + for (FieldNode fn : node.getFields()) { + addFieldInitialization(statements, staticStatements, fn, isEnum, + initStmtsAfterEnumValuesInit, explicitStaticPropsInEnum); + } + } + + statements.addAll(node.getObjectInitializerStatements()); + + Statement code = constructorNode.getCode(); + BlockStatement block = new BlockStatement(); + List otherStatements = block.getStatements(); + if (code instanceof BlockStatement) { + block = (BlockStatement) code; + otherStatements = block.getStatements(); + } else if (code != null) { + otherStatements.add(code); + } + if (!otherStatements.isEmpty()) { + if (first != null) { + // it is super(..) since this(..) is already covered + otherStatements.remove(0); + statements.add(0, firstStatement); + } + Statement stmtThis$0 = getImplicitThis$0StmtIfInnerClass(otherStatements); + if (stmtThis$0 != null) { + // since there can be field init statements that depend on method/property dispatching + // that uses this$0, it needs to bubble up before the super call itself (GROOVY-4471) + statements.add(0, stmtThis$0); + } + statements.addAll(otherStatements); + } + BlockStatement newBlock = new BlockStatement(statements, block.getVariableScope()); + newBlock.setSourcePosition(block); + constructorNode.setCode(newBlock); + + + if (!staticStatements.isEmpty()) { + if (isEnum) { + /* + * GROOVY-3161: initialize statements for explicitly declared static fields + * inside an enum should come after enum values are initialized + */ + staticStatements.removeAll(initStmtsAfterEnumValuesInit); + node.addStaticInitializerStatements(staticStatements, true); + if (!initStmtsAfterEnumValuesInit.isEmpty()) { + node.positionStmtsAfterEnumInitStmts(initStmtsAfterEnumValuesInit); + } + } else { + node.addStaticInitializerStatements(staticStatements, true); + } + } + } + + /* + * when InnerClassVisitor adds this.this$0 = $p$n, it adds it as a BlockStatement having that + * ExpressionStatement + */ + private Statement getImplicitThis$0StmtIfInnerClass(List otherStatements) { + if (!(classNode instanceof InnerClassNode)) return null; + for (Statement stmt : otherStatements) { + if (stmt instanceof BlockStatement) { + List stmts = ((BlockStatement) stmt).getStatements(); + for (Statement bstmt : stmts) { + if (bstmt instanceof ExpressionStatement) { + if (extractImplicitThis$0StmtIfInnerClassFromExpression(stmts, bstmt)) return bstmt; + } + } + } else if (stmt instanceof ExpressionStatement) { + if (extractImplicitThis$0StmtIfInnerClassFromExpression(otherStatements, stmt)) return stmt; + } + } + return null; + } + + private static boolean extractImplicitThis$0StmtIfInnerClassFromExpression(final List stmts, final Statement bstmt) { + Expression expr = ((ExpressionStatement) bstmt).getExpression(); + // GRECLIPSE edit -- avoid transforming CompareIdentity and CompareToNull + if (/*expr instanceof BinaryExpression*/expr != null && expr.getClass() == BinaryExpression.class) { + Expression lExpr = ((BinaryExpression) expr).getLeftExpression(); + if (lExpr instanceof FieldExpression) { + if ("this$0".equals(((FieldExpression) lExpr).getFieldName())) { + stmts.remove(bstmt); // remove from here and let the caller reposition it + return true; + } + } + } + return false; + } + + private static ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) { + if (code == null || !(code instanceof ExpressionStatement)) return null; + + Expression expression = ((ExpressionStatement) code).getExpression(); + if (!(expression instanceof ConstructorCallExpression)) return null; + ConstructorCallExpression cce = (ConstructorCallExpression) expression; + if (cce.isSpecialCall()) return cce; + return null; + } + + protected void addFieldInitialization(List list, List staticList, FieldNode fieldNode, + boolean isEnumClassNode, List initStmtsAfterEnumValuesInit, Set explicitStaticPropsInEnum) { + Expression expression = fieldNode.getInitialExpression(); + if (expression != null) { + final FieldExpression fe = new FieldExpression(fieldNode); + if (fieldNode.getType().equals(ClassHelper.REFERENCE_TYPE) && ((fieldNode.getModifiers() & Opcodes.ACC_SYNTHETIC) != 0)) { + fe.setUseReferenceDirectly(true); + } + ExpressionStatement statement = + new ExpressionStatement( + new BinaryExpression( + fe, + Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()), + expression)); + if (fieldNode.isStatic()) { + // GRECLIPSE add + // only conditionally 'move stuff around' + if (inlineStaticFieldInitializersIntoClinit) { + // GRECLIPSE end + // GROOVY-3311: pre-defined constants added by groovy compiler for numbers/characters should be + // initialized first so that code dependent on it does not see their values as empty + Expression initialValueExpression = fieldNode.getInitialValueExpression(); + if (initialValueExpression instanceof ConstantExpression) { + ConstantExpression cexp = (ConstantExpression) initialValueExpression; + cexp = transformToPrimitiveConstantIfPossible(cexp); + if (fieldNode.isFinal() && ClassHelper.isStaticConstantInitializerType(cexp.getType()) && cexp.getType().equals(fieldNode.getType())) { + return; // GROOVY-5150: primitive type constants will be initialized directly + } + staticList.add(0, statement); + } else { + staticList.add(statement); + } + fieldNode.setInitialValueExpression(null); // to avoid double initialization in case of several constructors + // GRECLIPSE add + } + // GRECLIPSE end + /* + * If it is a statement for an explicitly declared static field inside an enum, store its + * reference. For enums, they need to be handled differently as such init statements should + * come after the enum values have been initialized inside block. GROOVY-3161. + */ + if (isEnumClassNode && explicitStaticPropsInEnum.contains(fieldNode.getName())) { + initStmtsAfterEnumValuesInit.add(statement); + } + } else { + list.add(statement); + } + } + } + + /** + * Capitalizes the start of the given bean property name + */ + public static String capitalize(String name) { + return MetaClassHelper.capitalize(name); + } + + protected Statement createGetterBlock(PropertyNode propertyNode, final FieldNode field) { + return new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + if (field.isStatic()) { + mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType())); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType())); + } + BytecodeHelper.doReturn(mv, field.getType()); + } + }); + } + + protected Statement createSetterBlock(PropertyNode propertyNode, final FieldNode field) { + return new BytecodeSequence(new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + if (field.isStatic()) { + BytecodeHelper.load(mv, field.getType(), 0); + mv.visitFieldInsn(PUTSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType())); + } else { + mv.visitVarInsn(ALOAD, 0); + BytecodeHelper.load(mv, field.getType(), 1); + mv.visitFieldInsn(PUTFIELD, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType())); + } + mv.visitInsn(RETURN); + } + }); + } + + public void visitGenericType(GenericsType genericsType) { + + } + + public static Long getTimestampFromFieldName(String fieldName) { + if (fieldName.startsWith(__TIMESTAMP__)) { + try { + return Long.decode(fieldName.substring(__TIMESTAMP__.length())); + } catch (NumberFormatException e) { + return Long.MAX_VALUE; + } + } + return null; + } + + public static long getTimestamp(Class clazz) { + if (clazz.getClassLoader() instanceof GroovyClassLoader.InnerLoader) { + GroovyClassLoader.InnerLoader innerLoader = (GroovyClassLoader.InnerLoader) clazz.getClassLoader(); + return innerLoader.getTimeStamp(); + } + + final Field[] fields = clazz.getFields(); + for (int i = 0; i != fields.length; ++i) { + if (isStatic(fields[i].getModifiers())) { + Long timestamp = getTimestampFromFieldName(fields[i].getName()); + if (timestamp != null) { + return timestamp; + } + } + } + return Long.MAX_VALUE; + } + + protected void addCovariantMethods(ClassNode classNode) { + Map methodsToAdd = new HashMap(); + Map genericsSpec = new HashMap(); + + // unimplemented abstract methods from interfaces + Map abstractMethods = ClassNodeUtils.getDeclaredMethodsFromInterfaces(classNode); + Map allInterfaceMethods = new HashMap(abstractMethods); + ClassNodeUtils.addDeclaredMethodsFromAllInterfaces(classNode, allInterfaceMethods); + + List declaredMethods = new ArrayList(classNode.getMethods()); + // remove all static, private and package private methods + for (Iterator methodsIterator = declaredMethods.iterator(); methodsIterator.hasNext(); ) { + MethodNode m = (MethodNode) methodsIterator.next(); + abstractMethods.remove(m.getTypeDescriptor()); + if (m.isStatic() || !(m.isPublic() || m.isProtected())) { + methodsIterator.remove(); + } + MethodNode intfMethod = allInterfaceMethods.get(m.getTypeDescriptor()); + if (intfMethod != null && ((m.getModifiers() & ACC_SYNTHETIC) == 0) + && !m.isPublic() && !m.isStaticConstructor()) { + throw new RuntimeParserException("The method " + m.getName() + + " should be public as it implements the corresponding method from interface " + + intfMethod.getDeclaringClass(), m); + + } + } + + addCovariantMethods(classNode, declaredMethods, abstractMethods, methodsToAdd, genericsSpec); + + Map declaredMethodsMap = new HashMap(); + if (!methodsToAdd.isEmpty()) { + for (MethodNode mn : declaredMethods) { + declaredMethodsMap.put(mn.getTypeDescriptor(), mn); + } + } + + for (Object o : methodsToAdd.entrySet()) { + Map.Entry entry = (Map.Entry) o; + MethodNode method = (MethodNode) entry.getValue(); + // we skip bridge methods implemented in current class already + MethodNode mn = declaredMethodsMap.get(entry.getKey()); + if (mn != null && mn.getDeclaringClass().equals(classNode)) continue; + addPropertyMethod(method); + } + } + + private void addCovariantMethods(ClassNode classNode, List declaredMethods, Map abstractMethods, Map methodsToAdd, Map oldGenericsSpec) { + ClassNode sn = classNode.getUnresolvedSuperClass(false); + + if (sn != null) { + Map genericsSpec = createGenericsSpec(sn, oldGenericsSpec); + List classMethods = sn.getMethods(); + // original class causing bridge methods for methods in super class + for (Object declaredMethod : declaredMethods) { + MethodNode method = (MethodNode) declaredMethod; + if (method.isStatic()) continue; + storeMissingCovariantMethods(classMethods, method, methodsToAdd, genericsSpec, false); + } + // super class causing bridge methods for abstract methods in original class + if (!abstractMethods.isEmpty()) { + for (Object classMethod : classMethods) { + MethodNode method = (MethodNode) classMethod; + if (method.isStatic()) continue; + storeMissingCovariantMethods(abstractMethods.values(), method, methodsToAdd, Collections.EMPTY_MAP, true); + } + } + + addCovariantMethods(sn.redirect(), declaredMethods, abstractMethods, methodsToAdd, genericsSpec); + } + + ClassNode[] interfaces = classNode.getInterfaces(); + for (ClassNode anInterface : interfaces) { + List interfacesMethods = anInterface.getMethods(); + Map genericsSpec = createGenericsSpec(anInterface, oldGenericsSpec); + for (Object declaredMethod : declaredMethods) { + MethodNode method = (MethodNode) declaredMethod; + if (method.isStatic()) continue; + storeMissingCovariantMethods(interfacesMethods, method, methodsToAdd, genericsSpec, false); + } + addCovariantMethods(anInterface, declaredMethods, abstractMethods, methodsToAdd, genericsSpec); + } + + } + + private MethodNode getCovariantImplementation(final MethodNode oldMethod, final MethodNode overridingMethod, Map genericsSpec, boolean ignoreError) { + // method name + if (!oldMethod.getName().equals(overridingMethod.getName())) return null; + if ((overridingMethod.getModifiers() & ACC_BRIDGE) != 0) return null; + if (oldMethod.isPrivate()) return null; + + // parameters + boolean normalEqualParameters = equalParametersNormal(overridingMethod, oldMethod); + boolean genericEqualParameters = equalParametersWithGenerics(overridingMethod, oldMethod, genericsSpec); + if (!normalEqualParameters && !genericEqualParameters) return null; + + //correct to method level generics for the overriding method + genericsSpec = GenericsUtils.addMethodGenerics(overridingMethod, genericsSpec); + + // return type + ClassNode mr = overridingMethod.getReturnType(); + ClassNode omr = oldMethod.getReturnType(); + boolean equalReturnType = mr.equals(omr); + + ClassNode testmr = correctToGenericsSpec(genericsSpec, omr); + if (!isAssignable(mr, testmr)) { + if (ignoreError) return null; + throw new RuntimeParserException( + "The return type of " + + overridingMethod.getTypeDescriptor() + + " in " + overridingMethod.getDeclaringClass().getName() + + " is incompatible with " + testmr.getName() + + " in " + oldMethod.getDeclaringClass().getName(), + overridingMethod); + } + + if (equalReturnType && normalEqualParameters) return null; + + if ((oldMethod.getModifiers() & ACC_FINAL) != 0) { + throw new RuntimeParserException( + "Cannot override final method " + + oldMethod.getTypeDescriptor() + + " in " + oldMethod.getDeclaringClass().getName(), + overridingMethod); + } + if (oldMethod.isStatic() != overridingMethod.isStatic()) { + throw new RuntimeParserException( + "Cannot override method " + + oldMethod.getTypeDescriptor() + + " in " + oldMethod.getDeclaringClass().getName() + + " with disparate static modifier", + overridingMethod); + } + if (!equalReturnType) { + boolean oldM = ClassHelper.isPrimitiveType(oldMethod.getReturnType()); + boolean newM = ClassHelper.isPrimitiveType(overridingMethod.getReturnType()); + if (oldM || newM) { + String message = ""; + if (oldM && newM) { + message = " with old and new method having different primitive return types"; + } else if (newM) { + message = " with new method having a primitive return type and old method not"; + } else /* oldM */ { + message = " with old method having a primitive return type and new method not"; + } + throw new RuntimeParserException( + "Cannot override method " + + oldMethod.getTypeDescriptor() + + " in " + oldMethod.getDeclaringClass().getName() + + message, + overridingMethod); + } + } + + // if we reach this point we have at least one parameter or return type, that + // is different in its specified form. That means we have to create a bridge method! + MethodNode newMethod = new MethodNode( + oldMethod.getName(), + overridingMethod.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE, + cleanType(oldMethod.getReturnType()), + cleanParameters(oldMethod.getParameters()), + oldMethod.getExceptions(), + null + ); + List instructions = new ArrayList(1); + instructions.add( + new BytecodeInstruction() { + public void visit(MethodVisitor mv) { + mv.visitVarInsn(ALOAD, 0); + Parameter[] para = oldMethod.getParameters(); + Parameter[] goal = overridingMethod.getParameters(); + int doubleSlotOffset = 0; + for (int i = 0; i < para.length; i++) { + ClassNode type = para[i].getType(); + BytecodeHelper.load(mv, type, i + 1 + doubleSlotOffset); + if (type.redirect() == ClassHelper.double_TYPE || + type.redirect() == ClassHelper.long_TYPE) { + doubleSlotOffset++; + } + if (!type.equals(goal[i].getType())) { + BytecodeHelper.doCast(mv, goal[i].getType()); + } + } + mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(classNode), overridingMethod.getName(), BytecodeHelper.getMethodDescriptor(overridingMethod.getReturnType(), overridingMethod.getParameters()), false); + + BytecodeHelper.doReturn(mv, oldMethod.getReturnType()); + } + } + + ); + newMethod.setCode(new BytecodeSequence(instructions)); + return newMethod; + } + + private boolean isAssignable(ClassNode node, ClassNode testNode) { + if (node.isArray() && testNode.isArray()) { + return isArrayAssignable(node.getComponentType(), testNode.getComponentType()); + } + if (testNode.isInterface()) { + if (node.equals(testNode) || node.implementsInterface(testNode)) return true; + } + return node.isDerivedFrom(testNode); + } + + private boolean isArrayAssignable(ClassNode node, ClassNode testNode) { + if (node.isArray() && testNode.isArray()) { + return isArrayAssignable(node.getComponentType(), testNode.getComponentType()); + } + return isAssignable(node, testNode); + } + + private static Parameter[] cleanParameters(Parameter[] parameters) { + Parameter[] params = new Parameter[parameters.length]; + for (int i = 0; i < params.length; i++) { + params[i] = new Parameter(cleanType(parameters[i].getType()), parameters[i].getName()); + } + return params; + } + + private static ClassNode cleanType(ClassNode type) { + // todo: should this be directly handled by getPlainNodeReference? + if (type.isArray()) return cleanType(type.getComponentType()).makeArray(); + return type.getPlainNodeReference(); + } + + private void storeMissingCovariantMethods(Collection methods, MethodNode method, Map methodsToAdd, Map genericsSpec, boolean ignoreError) { + for (Object next : methods) { + MethodNode toOverride = (MethodNode) next; + MethodNode bridgeMethod = getCovariantImplementation(toOverride, method, genericsSpec, ignoreError); + if (bridgeMethod == null) continue; + methodsToAdd.put(bridgeMethod.getTypeDescriptor(), bridgeMethod); + return; + } + } + + private static boolean equalParametersNormal(MethodNode m1, MethodNode m2) { + Parameter[] p1 = m1.getParameters(); + Parameter[] p2 = m2.getParameters(); + if (p1.length != p2.length) return false; + for (int i = 0; i < p2.length; i++) { + ClassNode type = p2[i].getType(); + ClassNode parameterType = p1[i].getType(); + if (!parameterType.equals(type)) return false; + } + return true; + } + + private static boolean equalParametersWithGenerics(MethodNode m1, MethodNode m2, Map genericsSpec) { + Parameter[] p1 = m1.getParameters(); + Parameter[] p2 = m2.getParameters(); + if (p1.length != p2.length) return false; + for (int i = 0; i < p2.length; i++) { + ClassNode type = p2[i].getType(); + ClassNode genericsType = correctToGenericsSpec(genericsSpec, type); + ClassNode parameterType = p1[i].getType(); + if (!parameterType.equals(genericsType)) return false; + } + return true; + } + + private static boolean moveOptimizedConstantsInitialization(final ClassNode node) { + if (node.isInterface() && !Traits.isTrait(node)) return false; + + final int mods = Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC; + String name = SWAP_INIT; + BlockStatement methodCode = new BlockStatement(); + + methodCode.addStatement(new SwapInitStatement()); + boolean swapInitRequired = false; + for (FieldNode fn : node.getFields()) { + if (!fn.isStatic() || !fn.isSynthetic() || !fn.getName().startsWith("$const$")) continue; + if (fn.getInitialExpression() == null) continue; + final FieldExpression fe = new FieldExpression(fn); + if (fn.getType().equals(ClassHelper.REFERENCE_TYPE)) fe.setUseReferenceDirectly(true); + ConstantExpression init = (ConstantExpression) fn.getInitialExpression(); + init = new ConstantExpression(init.getValue(), true); + ExpressionStatement statement = + new ExpressionStatement( + new BinaryExpression( + fe, + Token.newSymbol(Types.EQUAL, fn.getLineNumber(), fn.getColumnNumber()), + init)); + fn.setInitialValueExpression(null); + methodCode.addStatement(statement); + swapInitRequired = true; + } + + if (swapInitRequired) { + node.addSyntheticMethod( + name, mods, ClassHelper.VOID_TYPE, + Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, methodCode); + } + + return swapInitRequired; + } + + /** + * When constant expressions are created, the value is always wrapped to a non primitive type. + * Some constant expressions are optimized to return primitive types, but not all primitives are + * handled. This method guarantees to return a similar constant expression but with a primitive type + * instead of a boxed type. + *

+ * Additionally, single char strings are converted to 'char' types. + * + * @param constantExpression a constant expression + * @return the same instance of constant expression if the type is already primitive, or a primitive + * constant if possible. + */ + public static ConstantExpression transformToPrimitiveConstantIfPossible(ConstantExpression constantExpression) { + Object value = constantExpression.getValue(); + if (value == null) return constantExpression; + ConstantExpression result; + ClassNode type = constantExpression.getType(); + if (ClassHelper.isPrimitiveType(type)) return constantExpression; + if (value instanceof String && ((String) value).length() == 1) { + result = new ConstantExpression(((String) value).charAt(0)); + result.setType(ClassHelper.char_TYPE); + } else { + type = ClassHelper.getUnwrapper(type); + result = new ConstantExpression(value, true); + result.setType(type); + } + return result; + } + + private static class SwapInitStatement extends BytecodeSequence { + + private WriterController controller; + + public SwapInitStatement() { + super(new SwapInitInstruction()); + ((SwapInitInstruction) getInstructions().get(0)).statement = this; + } + + @Override + public void visit(final GroovyCodeVisitor visitor) { + if (visitor instanceof AsmClassGenerator) { + AsmClassGenerator generator = (AsmClassGenerator) visitor; + controller = generator.getController(); + } + super.visit(visitor); + } + + private static class SwapInitInstruction extends BytecodeInstruction { + SwapInitStatement statement; + + @Override + public void visit(final MethodVisitor mv) { + statement.controller.getCallSiteWriter().makeCallSiteArrayInitializer(); + } + } + } + +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/BytecodeHelper.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/BytecodeHelper.java new file mode 100644 index 0000000000..da7329d69f --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/BytecodeHelper.java @@ -0,0 +1,848 @@ +/* + * 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; + +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.CompileUnit; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.decompiled.DecompiledClassNode; +import org.codehaus.groovy.classgen.asm.util.TypeDescriptionUtil; +import org.codehaus.groovy.reflection.ReflectionCache; +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; +import groovyjarjarasm.asm.Label; +import groovyjarjarasm.asm.MethodVisitor; +import groovyjarjarasm.asm.Opcodes; + +import java.lang.reflect.Modifier; + +import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.char_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.double_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.float_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.int_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.long_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.short_TYPE; + +/** + * A helper class for bytecode generation with AsmClassGenerator. + * + * @author James Strachan + * @author Bing Ran + * @author Jochen Theodorou + */ +public class BytecodeHelper implements Opcodes { + + private static String DTT_CLASSNAME = BytecodeHelper.getClassInternalName(DefaultTypeTransformation.class.getName()); + + public static String getClassInternalName(ClassNode t) { + if (t.isPrimaryClassNode() || t instanceof DecompiledClassNode) { + if (t.isArray()) return "[L"+getClassInternalName(t.getComponentType())+";"; + return getClassInternalName(t.getName()); + } + // GRECLIPSE edit + //return getClassInternalName(t.getTypeClass()); + // don't call getTypeClass() unless necessary; decide if this can ever get into trouble? + // the second part of the if was added because of FindInSource.groovy which refered to + // GroovyModel but that could not be found so we were left with an unresolved import and + // node in the code - crashed whilst doing the code gen + String name = t.getClassInternalName(); + if (name == null) { + if (t.hasClass()) { + name = getClassInternalName(t.getTypeClass()); + } else { + name = getClassInternalName(t.getName()); + } + } + return name; + // GRECLIPSE end + } + + public static String getClassInternalName(Class t) { + return groovyjarjarasm.asm.Type.getInternalName(t); + } + + /** + * @return the ASM internal name of the type + */ + public static String getClassInternalName(String name) { + return name.replace('.', '/'); + } + + public static String getMethodDescriptor(ClassNode returnType, Parameter[] parameters) { + StringBuilder buffer = new StringBuilder(100); + buffer.append("("); + for (Parameter parameter : parameters) { + buffer.append(getTypeDescription(parameter.getType())); + } + buffer.append(")"); + buffer.append(getTypeDescription(returnType)); + return buffer.toString(); + } + + /** + * Returns a method descriptor for the given {@link org.codehaus.groovy.ast.MethodNode}. + * + * @param methodNode the method node for which to create the descriptor + * @return a method descriptor as defined in section JVMS section 4.3.3 + */ + public static String getMethodDescriptor(MethodNode methodNode) { + return getMethodDescriptor(methodNode.getReturnType(), methodNode.getParameters()); + } + + /** + * @return the ASM method type descriptor + */ + public static String getMethodDescriptor(Class returnType, Class[] paramTypes) { + // lets avoid class loading + StringBuilder buffer = new StringBuilder(100); + buffer.append("("); + for (Class paramType : paramTypes) { + buffer.append(getTypeDescription(paramType)); + } + buffer.append(")"); + buffer.append(getTypeDescription(returnType)); + return buffer.toString(); + } + + public static String getTypeDescription(Class c) { + return groovyjarjarasm.asm.Type.getDescriptor(c); + } + + /** + * array types are special: + * eg.: String[]: classname: [Ljava.lang.String; + * Object: classname: java.lang.Object + * int[] : classname: [I + * unlike getTypeDescription '.' is not replaced by '/'. + * it seems that makes problems for + * the class loading if '.' is replaced by '/' + * + * @return the ASM type description for class loading + */ + public static String getClassLoadingTypeDescription(ClassNode c) { + String desc = TypeDescriptionUtil.getDescriptionByType(c); + + if (!c.isArray()) { + if (desc.startsWith("L") && desc.endsWith(";")) { + desc = desc.substring(1, desc.length() - 1); // remove "L" and ";" + } + } + + return desc.replace('/', '.'); + } + + /** + * array types are special: + * eg.: String[]: classname: [Ljava/lang/String; + * int[]: [I + * + * @return the ASM type description + */ + public static String getTypeDescription(ClassNode c) { + return getTypeDescription(c, true); + } + + /** + * array types are special: + * eg.: String[]: classname: [Ljava/lang/String; + * int[]: [I + * + * @return the ASM type description + */ + private static String getTypeDescription(ClassNode c, boolean end) { + ClassNode d = c; + if (ClassHelper.isPrimitiveType(d.redirect())) { + d = d.redirect(); + } + + String desc = TypeDescriptionUtil.getDescriptionByType(d); + + if (!end && desc.endsWith(";")) { + desc = desc.substring(0, desc.length() - 1); + } + + return desc; + } + + + /** + * @return an array of ASM internal names of the type + */ + public static String[] getClassInternalNames(ClassNode[] names) { + int size = names.length; + String[] answer = new String[size]; + for (int i = 0; i < size; i++) { + answer[i] = getClassInternalName(names[i]); + } + return answer; + } + + public static void pushConstant(MethodVisitor mv, int value) { + switch (value) { + case 0: + mv.visitInsn(ICONST_0); + break; + case 1: + mv.visitInsn(ICONST_1); + break; + case 2: + mv.visitInsn(ICONST_2); + break; + case 3: + mv.visitInsn(ICONST_3); + break; + case 4: + mv.visitInsn(ICONST_4); + break; + case 5: + mv.visitInsn(ICONST_5); + break; + default: + if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, value); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, value); + } else { + mv.visitLdcInsn(value); + } + } + } + + /** + * negate a boolean on stack. true->false, false->true + */ + public static void negateBoolean(MethodVisitor mv) { + // code to negate the primitive boolean + Label endLabel = new Label(); + Label falseLabel = new Label(); + mv.visitJumpInsn(IFNE, falseLabel); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, endLabel); + mv.visitLabel(falseLabel); + mv.visitInsn(ICONST_0); + mv.visitLabel(endLabel); + } + + /** + * load a message on the stack and remove it right away. Good for put a mark in the generated bytecode for debugging purpose. + * + * @param msg + */ + /*public void mark(String msg) { + mv.visitLdcInsn(msg); + mv.visitInsn(POP); + }*/ + + /** + * returns a name that Class.forName() can take. Notably for arrays: + * [I, [Ljava.lang.String; etc + * Regular object type: java.lang.String + * + * @param name + */ + public static String formatNameForClassLoading(String name) { + if (name == null) { + return "java.lang.Object;"; + } + + if (TypeDescriptionUtil.isPrimitiveType(name)) { + return name; + } + + if (name.startsWith("[")) { + return name.replace('/', '.'); + } + + if (name.startsWith("L")) { + name = name.substring(1); + if (name.endsWith(";")) { + name = name.substring(0, name.length() - 1); + } + return name.replace('/', '.'); + } + + String prefix = ""; + if (name.endsWith("[]")) { // todo need process multi + prefix = "["; + name = name.substring(0, name.length() - 2); + + return prefix + TypeDescriptionUtil.getDescriptionByName(name); + } + + return name.replace('/', '.'); + } + + /*public void dup() { + mv.visitInsn(DUP); + }*/ + + private static boolean hasGenerics(Parameter[] param) { + if (param.length == 0) return false; + for (int i = 0; i < param.length; i++) { + ClassNode type = param[i].getType(); + if (hasGenerics(type)) return true; + } + return false; + } + + private static boolean hasGenerics(ClassNode type) { + return type.isArray() ? hasGenerics(type.getComponentType()) : type.getGenericsTypes() != null; + } + + public static String getGenericsMethodSignature(MethodNode node) { + GenericsType[] generics = node.getGenericsTypes(); + Parameter[] param = node.getParameters(); + ClassNode returnType = node.getReturnType(); + + if (generics == null && !hasGenerics(param) && !hasGenerics(returnType)) return null; + + StringBuilder ret = new StringBuilder(100); + getGenericsTypeSpec(ret, generics); + + GenericsType[] paramTypes = new GenericsType[param.length]; + for (int i = 0; i < param.length; i++) { + ClassNode pType = param[i].getType(); + if (pType.getGenericsTypes() == null || !pType.isGenericsPlaceHolder()) { + paramTypes[i] = new GenericsType(pType); + } else { + paramTypes[i] = pType.getGenericsTypes()[0]; + } + } + addSubTypes(ret, paramTypes, "(", ")"); + addSubTypes(ret, new GenericsType[]{new GenericsType(returnType)}, "", ""); + return ret.toString(); + } + + private static boolean usesGenericsInClassSignature(ClassNode node) { + if (!node.isUsingGenerics()) return false; + if (hasGenerics(node)) return true; + ClassNode sclass = node.getUnresolvedSuperClass(false); + if (sclass.isUsingGenerics()) return true; + ClassNode[] interfaces = node.getInterfaces(); + if (interfaces != null) { + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i].isUsingGenerics()) return true; + } + } + + return false; + } + + public static String getGenericsSignature(ClassNode node) { + if (!usesGenericsInClassSignature(node)) return null; + GenericsType[] genericsTypes = node.getGenericsTypes(); + StringBuilder ret = new StringBuilder(100); + getGenericsTypeSpec(ret, genericsTypes); + GenericsType extendsPart = new GenericsType(node.getUnresolvedSuperClass(false)); + writeGenericsBounds(ret, extendsPart, true); + ClassNode[] interfaces = node.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + GenericsType interfacePart = new GenericsType(interfaces[i]); + writeGenericsBounds(ret, interfacePart, false); + } + return ret.toString(); + } + + private static void getGenericsTypeSpec(StringBuilder ret, GenericsType[] genericsTypes) { + if (genericsTypes == null) return; + ret.append('<'); + for (int i = 0; i < genericsTypes.length; i++) { + String name = genericsTypes[i].getName(); + ret.append(name); + ret.append(':'); + writeGenericsBounds(ret, genericsTypes[i], true); + } + ret.append('>'); + } + + public static String getGenericsBounds(ClassNode type) { + GenericsType[] genericsTypes = type.getGenericsTypes(); + if (genericsTypes == null) return null; + StringBuilder ret = new StringBuilder(100); + if (type.isGenericsPlaceHolder()) { + addSubTypes(ret, type.getGenericsTypes(), "", ""); + } else { + GenericsType gt = new GenericsType(type); + writeGenericsBounds(ret, gt, false); + } + + return ret.toString(); + } + + private static void writeGenericsBoundType(StringBuilder ret, ClassNode printType, boolean writeInterfaceMarker) { + if (writeInterfaceMarker && printType.isInterface()) ret.append(":"); + if (printType.isGenericsPlaceHolder() && printType.getGenericsTypes()!=null) { + ret.append("T"); + ret.append(printType.getGenericsTypes()[0].getName()); + ret.append(";"); + } + else { + ret.append(getTypeDescription(printType, false)); + addSubTypes(ret, printType.getGenericsTypes(), "<", ">"); + if (!ClassHelper.isPrimitiveType(printType)) ret.append(";"); + } + } + + private static void writeGenericsBounds(StringBuilder ret, GenericsType type, boolean writeInterfaceMarker) { + if (type.getUpperBounds() != null) { + ClassNode[] bounds = type.getUpperBounds(); + for (int i = 0; i < bounds.length; i++) { + writeGenericsBoundType(ret, bounds[i], writeInterfaceMarker); + } + } else if (type.getLowerBound() != null) { + writeGenericsBoundType(ret, type.getLowerBound(), writeInterfaceMarker); + } else { + writeGenericsBoundType(ret, type.getType(), writeInterfaceMarker); + } + } + + private static void addSubTypes(StringBuilder ret, GenericsType[] types, String start, String end) { + if (types == null) return; + ret.append(start); + for (int i = 0; i < types.length; i++) { + if (types[i].getType().isArray()) { + ret.append("["); + addSubTypes(ret, new GenericsType[]{new GenericsType(types[i].getType().getComponentType())}, "", ""); + } + else { + if (types[i].isPlaceholder()) { + ret.append('T'); + String name = types[i].getName(); + ret.append(name); + ret.append(';'); + } else if (types[i].isWildcard()) { + if (types[i].getUpperBounds() != null) { + ret.append('+'); + writeGenericsBounds(ret, types[i], false); + } else if (types[i].getLowerBound() != null) { + ret.append('-'); + writeGenericsBounds(ret, types[i], false); + } else { + ret.append('*'); + } + } else { + writeGenericsBounds(ret, types[i], false); + } + } + } + ret.append(end); + } + + public static void doCast(MethodVisitor mv, ClassNode type) { + if (type == ClassHelper.OBJECT_TYPE) return; + if (ClassHelper.isPrimitiveType(type) && type != VOID_TYPE) { + unbox(mv, type); + } else { + mv.visitTypeInsn( + CHECKCAST, + type.isArray() ? + BytecodeHelper.getTypeDescription(type) : + BytecodeHelper.getClassInternalName(type.getName())); + } + } + + /** + * Given a wrapped number type (Byte, Integer, Short, ...), generates bytecode + * to convert it to a primitive number (int, long, double) using calls to + * wrapped.[targetType]Value() + * @param mv method visitor + * @param sourceType the wrapped number type + * @param targetType the primitive target type + */ + public static void doCastToPrimitive(MethodVisitor mv, ClassNode sourceType, ClassNode targetType) { + mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(sourceType), targetType.getName() + "Value", "()" + BytecodeHelper.getTypeDescription(targetType), false); + } + + /** + * Given a primitive number type (byte, integer, short, ...), generates bytecode + * to convert it to a wrapped number (Integer, Long, Double) using calls to + * [WrappedType].valueOf + * @param mv method visitor + * @param sourceType the primitive number type + * @param targetType the wrapped target type + */ + public static void doCastToWrappedType(MethodVisitor mv, ClassNode sourceType, ClassNode targetType) { + mv.visitMethodInsn(INVOKESTATIC, getClassInternalName(targetType), "valueOf", "(" + getTypeDescription(sourceType) + ")" + getTypeDescription(targetType), false); + } + + public static void doCast(MethodVisitor mv, Class type) { + if (type == Object.class) return; + if (type.isPrimitive() && type != Void.TYPE) { + unbox(mv, type); + } else { + mv.visitTypeInsn( + CHECKCAST, + type.isArray() ? + BytecodeHelper.getTypeDescription(type) : + BytecodeHelper.getClassInternalName(type.getName())); + } + } + + /** + * Generates the bytecode to unbox the current value on the stack + */ + public static void unbox(MethodVisitor mv, Class type) { + if (type.isPrimitive() && type != Void.TYPE) { + String returnString = "(Ljava/lang/Object;)" + BytecodeHelper.getTypeDescription(type); + mv.visitMethodInsn(INVOKESTATIC, DTT_CLASSNAME, type.getName() + "Unbox", returnString, false); + } + } + + public static void unbox(MethodVisitor mv, ClassNode type) { + if (type.isPrimaryClassNode()) return; + unbox(mv, type.getTypeClass()); + } + + /** + * box top level operand + */ + @Deprecated + public static boolean box(MethodVisitor mv, ClassNode type) { + if (type.isPrimaryClassNode()) return false; + // GRECLIPSE add + if (!type.isPrimitive()) return false; + // GRECLIPSE end + return box(mv, type.getTypeClass()); + } + + + /** + * Generates the bytecode to autobox the current value on the stack + */ + @Deprecated + public static boolean box(MethodVisitor mv, Class type) { + if (ReflectionCache.getCachedClass(type).isPrimitive && type != void.class) { + String returnString = "(" + BytecodeHelper.getTypeDescription(type) + ")Ljava/lang/Object;"; + mv.visitMethodInsn(INVOKESTATIC, DTT_CLASSNAME, "box", returnString, false); + return true; + } + return false; + } + + /** + * Visits a class literal. If the type of the classnode is a primitive type, + * the generated bytecode will be a GETSTATIC Integer.TYPE. + * If the classnode is not a primitive type, we will generate a LDC instruction. + */ + public static void visitClassLiteral(MethodVisitor mv, ClassNode classNode) { + if (ClassHelper.isPrimitiveType(classNode)) { + mv.visitFieldInsn( + GETSTATIC, + getClassInternalName(ClassHelper.getWrapper(classNode)), + "TYPE", + "Ljava/lang/Class;"); + } else { + mv.visitLdcInsn(groovyjarjarasm.asm.Type.getType(getTypeDescription(classNode))); + } + } + + /** + * Tells if a class node is candidate for class literal bytecode optimization. If so, + * bytecode may use LDC instructions instead of static constant Class fields to retrieve + * class literals. + * @param classNode the classnode for which we want to know if bytecode optimization is possible + * @return true if the bytecode can be optimized + */ + public static boolean isClassLiteralPossible(ClassNode classNode) { + // the current implementation only checks for public modifier, because Groovy used to allow + // handles on classes even if they are package protected and not in the same package. + // There are situations where we could make more fine grained checks, but be careful of + // potential breakage of existing code. + return Modifier.isPublic(classNode.getModifiers()); + } + + /** + * Returns true if the two classes share the same compilation unit. + * @param a class a + * @param b class b + * @return true if both classes share the same compilation unit + */ + public static boolean isSameCompilationUnit(ClassNode a, ClassNode b) { + CompileUnit cu1 = a.getCompileUnit(); + CompileUnit cu2 = b.getCompileUnit(); + return cu1 !=null && cu2 !=null && cu1==cu2; + } + + /** + * Computes a hash code for a string. The purpose of this hashcode is to be constant independently of + * the JDK being used. + * @param str the string for which to compute the hashcode + * @return hashcode of the string + */ + public static int hashCode(String str) { + final char[] chars = str.toCharArray(); + int h = 0; + for (int i = 0; i < chars.length; i++) { + h = 31 * h + chars[i]; + } + return h; + } + + /** + * Converts a primitive type to boolean. + * + * @param mv method visitor + * @param type primitive type to convert + */ + public static void convertPrimitiveToBoolean(MethodVisitor mv, ClassNode type) { + if (type == boolean_TYPE) { + return; + } + // Special handling is done for floating point types in order to + // handle checking for 0 or NaN values. + if (type == double_TYPE) { + convertDoubleToBoolean(mv); + return; + } else if (type == float_TYPE) { + convertFloatToBoolean(mv); + return; + } + Label trueLabel = new Label(); + Label falseLabel = new Label(); + // Convert long to int for IFEQ comparison using LCMP + if (type== long_TYPE) { + mv.visitInsn(LCONST_0); + mv.visitInsn(LCMP); + } + // This handles byte, short, char and int + mv.visitJumpInsn(IFEQ, falseLabel); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, trueLabel); + mv.visitLabel(falseLabel); + mv.visitInsn(ICONST_0); + mv.visitLabel(trueLabel); + } + + private static void convertDoubleToBoolean(MethodVisitor mv) { + Label trueLabel = new Label(); + Label falseLabel = new Label(); + Label falseLabelWithPop = new Label(); + mv.visitInsn(DUP2); // will need the extra for isNaN call if required + mv.visitInsn(DCONST_0); + mv.visitInsn(DCMPL); + mv.visitJumpInsn(IFEQ, falseLabelWithPop); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "isNaN", "(D)Z", false); + mv.visitJumpInsn(IFNE, falseLabel); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, trueLabel); + mv.visitLabel(falseLabelWithPop); + mv.visitInsn(POP2); + mv.visitLabel(falseLabel); + mv.visitInsn(ICONST_0); + mv.visitLabel(trueLabel); + } + + private static void convertFloatToBoolean(MethodVisitor mv) { + Label trueLabel = new Label(); + Label falseLabel = new Label(); + Label falseLabelWithPop = new Label(); + mv.visitInsn(DUP); // will need the extra for isNaN call if required + mv.visitInsn(FCONST_0); + mv.visitInsn(FCMPL); + mv.visitJumpInsn(IFEQ, falseLabelWithPop); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "isNaN", "(F)Z", false); + mv.visitJumpInsn(IFNE, falseLabel); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, trueLabel); + mv.visitLabel(falseLabelWithPop); + mv.visitInsn(POP); + mv.visitLabel(falseLabel); + mv.visitInsn(ICONST_0); + mv.visitLabel(trueLabel); + } + + public static void doReturn(MethodVisitor mv, ClassNode type) { + new ReturnVarHandler(mv, type).handle(); + } + + public static void load(MethodVisitor mv, ClassNode type, int idx) { + new LoadVarHandler(mv, type, idx).handle(); + } + + public static void store(MethodVisitor mv, ClassNode type, int idx) { + new StoreVarHandler(mv, type, idx).handle(); + } + + private static class ReturnVarHandler extends PrimitiveTypeHandler { + private MethodVisitor mv; + + public ReturnVarHandler(MethodVisitor mv, ClassNode type) { + super(type); + this.mv = mv; + } + + @Override + protected void handleDoubleType() { + mv.visitInsn(DRETURN); + } + + @Override + protected void handleFloatType() { + mv.visitInsn(FRETURN); + } + + @Override + protected void handleLongType() { + mv.visitInsn(LRETURN); + } + + @Override + protected void handleIntType() { + mv.visitInsn(IRETURN); + } + + @Override + protected void handleVoidType() { + mv.visitInsn(RETURN); + } + + @Override + protected void handleRefType() { + mv.visitInsn(ARETURN); + } + } + + private static class LoadVarHandler extends PrimitiveTypeHandler { + private MethodVisitor mv; + private int idx; + + public LoadVarHandler(MethodVisitor mv, ClassNode type, int idx) { + super(type); + this.mv = mv; + this.idx = idx; + } + + @Override + protected void handleDoubleType() { + mv.visitVarInsn(DLOAD, idx); + } + + @Override + protected void handleFloatType() { + mv.visitVarInsn(FLOAD, idx); + } + + @Override + protected void handleLongType() { + mv.visitVarInsn(LLOAD, idx); + } + + @Override + protected void handleIntType() { + mv.visitVarInsn(ILOAD, idx); + } + + @Override + protected void handleVoidType() { + // do nothing + } + + @Override + protected void handleRefType() { + mv.visitVarInsn(ALOAD, idx); + } + } + + private static class StoreVarHandler extends PrimitiveTypeHandler { + private MethodVisitor mv; + private int idx; + + public StoreVarHandler(MethodVisitor mv, ClassNode type, int idx) { + super(type); + this.mv = mv; + this.idx = idx; + } + + @Override + protected void handleDoubleType() { + mv.visitVarInsn(DSTORE, idx); + } + + @Override + protected void handleFloatType() { + mv.visitVarInsn(FSTORE, idx); + } + + @Override + protected void handleLongType() { + mv.visitVarInsn(LSTORE, idx); + } + + @Override + protected void handleIntType() { + mv.visitVarInsn(ISTORE, idx); + } + + @Override + protected void handleVoidType() { + // do nothing + } + + @Override + protected void handleRefType() { + mv.visitVarInsn(ASTORE, idx); + } + } + + private static abstract class PrimitiveTypeHandler { + private ClassNode type; + + public PrimitiveTypeHandler(ClassNode type) { + this.type = type; + } + + public void handle() { + if (type == double_TYPE) { + handleDoubleType(); + } else if (type == float_TYPE) { + handleFloatType(); + } else if (type == long_TYPE) { + handleLongType(); + } else if ( + type == boolean_TYPE + || type == char_TYPE + || type == byte_TYPE + || type == int_TYPE + || type == short_TYPE) { + handleIntType(); + } else if (type == VOID_TYPE) { + handleVoidType(); + } else { + handleRefType(); + } + } + + protected abstract void handleDoubleType(); + protected abstract void handleFloatType(); + protected abstract void handleLongType(); + + /** + * boolean, char, byte, int, short types are handle in the same way + */ + protected abstract void handleIntType(); + + protected abstract void handleVoidType(); + protected abstract void handleRefType(); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/CompileStack.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/CompileStack.java new file mode 100644 index 0000000000..f7b161b2e3 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/CompileStack.java @@ -0,0 +1,875 @@ +/* + * 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; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.VariableScope; +import groovyjarjarasm.asm.Label; +import groovyjarjarasm.asm.MethodVisitor; +import groovyjarjarasm.asm.Opcodes; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +/** + * This class is a helper for AsmClassGenerator. It manages + * different aspects of the code of a code block like + * handling labels, defining variables, and scopes. + * After a MethodNode is visited clear should be called, for + * initialization the method init should be used. + *

+ * Some Notes: + *

    + *
  • every push method will require a later pop call + *
  • method parameters may define a category 2 variable, so + * don't ignore the type stored in the variable object + *
  • the index of the variable may not be as assumed when + * the variable is a parameter of a method because the + * parameter may be used in a closure, so don't ignore + * the stored variable index + *
  • the names of temporary variables can be ignored. The names + * are only used for debugging and do not conflict with each + * other or normal variables. For accessing, the index of the + * variable must be used. + *
  • never mix temporary and normal variables by changes to this class. + * While the name is very important for a normal variable, it is only a + * helper construct for temporary variables. That means for example a + * name for a temporary variable can be used multiple times without + * conflict. So mixing them both may lead to the problem that a normal + * or temporary variable is hidden or even removed. That must not happen! + *
+ * + * + * @see org.codehaus.groovy.classgen.AsmClassGenerator + * @author Jochen Theodorou + */ +public class CompileStack implements Opcodes { + /** + * TODO: remove optimization of this.foo -> this.@foo + * + */ + + // state flag + private boolean clear=true; + // current scope + private VariableScope scope; + // current label for continue + private Label continueLabel; + // current label for break + private Label breakLabel; + // available variables on stack + private Map stackVariables = new HashMap(); + // index of the last variable on stack + private int currentVariableIndex = 1; + // index for the next variable on stack + private int nextVariableIndex = 1; + // currently temporary variables in use + private final LinkedList temporaryVariables = new LinkedList(); + // overall used variables for a method/constructor + private final LinkedList usedVariables = new LinkedList(); + // map containing named labels of parenting blocks + private Map superBlockNamedLabels = new HashMap(); + // map containing named labels of current block + private Map currentBlockNamedLabels = new HashMap(); + // list containing finally blocks + // such a block is created by synchronized or finally and + // must be called for break/continue/return + private LinkedList finallyBlocks = new LinkedList(); + private final LinkedList visitedBlocks = new LinkedList(); + + private Label thisStartLabel, thisEndLabel; + +// private MethodVisitor mv; + + // helper to handle different stack based variables + private final LinkedList stateStack = new LinkedList(); + + // handle different states for the implicit "this" + private final LinkedList implicitThisStack = new LinkedList(); + // handle different states for being on the left hand side + private final LinkedList lhsStack = new LinkedList(); + { + implicitThisStack.add(false); + lhsStack.add(false); + } + + // defines the first variable index usable after + // all parameters of a method + private int localVariableOffset; + // this is used to store the goals for a "break foo" call + // in a loop where foo is a label. + private final Map namedLoopBreakLabel = new HashMap(); + // this is used to store the goals for a "continue foo" call + // in a loop where foo is a label. + private final Map namedLoopContinueLabel = new HashMap(); + private String className; + private final LinkedList typedExceptions = new LinkedList(); + private final LinkedList untypedExceptions = new LinkedList(); + // stores if on left-hand-side during compilation + private boolean lhs; + // stores if implicit or explicit this is used. + private boolean implicitThis; + private final WriterController controller; + private boolean inSpecialConstructorCall; + + protected static class LabelRange { + public Label start; + public Label end; + } + + public static class BlockRecorder { + private boolean isEmpty = true; + public Runnable excludedStatement; + public final LinkedList ranges; + public BlockRecorder() { + ranges = new LinkedList(); + } + public BlockRecorder(Runnable excludedStatement) { + this(); + this.excludedStatement = excludedStatement; + } + public void startRange(Label start) { + LabelRange range = new LabelRange(); + range.start = start; + ranges.add(range); + isEmpty = false; + } + public void closeRange(Label end) { + ranges.getLast().end = end; + } + } + + private static class ExceptionTableEntry { + Label start,end,goal; + String sig; + } + + private class StateStackElement { + final VariableScope scope; + final Label continueLabel; + final Label breakLabel; + final Map stackVariables; + final Map currentBlockNamedLabels; + final LinkedList finallyBlocks; + final boolean inSpecialConstructorCall; + + StateStackElement() { + scope = CompileStack.this.scope; + continueLabel = CompileStack.this.continueLabel; + breakLabel = CompileStack.this.breakLabel; + stackVariables = CompileStack.this.stackVariables; + currentBlockNamedLabels = CompileStack.this.currentBlockNamedLabels; + finallyBlocks = CompileStack.this.finallyBlocks; + inSpecialConstructorCall = CompileStack.this.inSpecialConstructorCall; + } + } + + public CompileStack(WriterController wc) { + this.controller = wc; + } + + public void pushState() { + stateStack.add(new StateStackElement()); + stackVariables = new HashMap(stackVariables); + finallyBlocks = new LinkedList(finallyBlocks); + } + + private void popState() { + if (stateStack.isEmpty()) { + throw new GroovyBugError("Tried to do a pop on the compile stack without push."); + } + StateStackElement element = (StateStackElement) stateStack.removeLast(); + scope = element.scope; + continueLabel = element.continueLabel; + breakLabel = element.breakLabel; + stackVariables = element.stackVariables; + finallyBlocks = element.finallyBlocks; + inSpecialConstructorCall = element.inSpecialConstructorCall; + } + + public Label getContinueLabel() { + return continueLabel; + } + + public Label getBreakLabel() { + return breakLabel; + } + + public void removeVar(int tempIndex) { + final BytecodeVariable head = (BytecodeVariable) temporaryVariables.removeFirst(); + if (head.getIndex() != tempIndex) { + temporaryVariables.addFirst(head); + MethodNode methodNode = controller.getMethodNode(); + if (methodNode==null) { + methodNode = controller.getConstructorNode(); + } + throw new GroovyBugError( + "In method "+ (methodNode!=null?methodNode.getText():"") + ", " + + "CompileStack#removeVar: tried to remove a temporary " + + "variable with index "+ tempIndex + " in wrong order. " + + "Current temporary variables=" + temporaryVariables); + } + } + + private void setEndLabels(){ + Label endLabel = new Label(); + controller.getMethodVisitor().visitLabel(endLabel); + for (Iterator iter = stackVariables.values().iterator(); iter.hasNext();) { + BytecodeVariable var = (BytecodeVariable) iter.next(); + var.setEndLabel(endLabel); + } + thisEndLabel = endLabel; + } + + public void pop() { + setEndLabels(); + popState(); + } + + public VariableScope getScope() { + return scope; + } + + /** + * creates a temporary variable. + * + * @param var defines type and name + * @param store defines if the toplevel argument of the stack should be stored + * @return the index used for this temporary variable + */ + public int defineTemporaryVariable(Variable var, boolean store) { + return defineTemporaryVariable(var.getName(), var.getType(),store); + } + + public BytecodeVariable getVariable(String variableName ) { + return getVariable(variableName, true); + } + + /** + * Returns a normal variable. + *

+ * If mustExist is true and the normal variable doesn't exist, + * then this method will throw a GroovyBugError. It is not the intention of + * this method to let this happen! And the exception should not be used for + * flow control - it is just acting as an assertion. If the exception is thrown + * then it indicates a bug in the class using CompileStack. + * This method can also not be used to return a temporary variable. + * Temporary variables are not normal variables. + * + * @param variableName name of the variable + * @param mustExist throw exception if variable does not exist + * @return the normal variable or null if not found (and mustExist not true) + */ + public BytecodeVariable getVariable(String variableName, boolean mustExist) { + if (variableName.equals("this")) return BytecodeVariable.THIS_VARIABLE; + if (variableName.equals("super")) return BytecodeVariable.SUPER_VARIABLE; + BytecodeVariable v = (BytecodeVariable) stackVariables.get(variableName); + if (v == null && mustExist) + throw new GroovyBugError("tried to get a variable with the name " + variableName + " as stack variable, but a variable with this name was not created"); + return v; + } + + /** + * creates a temporary variable. + * + * @param name defines type and name + * @param store defines if the top-level argument of the stack should be stored + * @return the index used for this temporary variable + */ + public int defineTemporaryVariable(String name,boolean store) { + return defineTemporaryVariable(name, ClassHelper.DYNAMIC_TYPE,store); + } + + /** + * creates a temporary variable. + * + * @param name defines the name + * @param node defines the node + * @param store defines if the top-level argument of the stack should be stored + * @return the index used for this temporary variable + */ + public int defineTemporaryVariable(String name, ClassNode node, boolean store) { + BytecodeVariable answer = defineVar(name, node, false, false); + temporaryVariables.addFirst(answer); // TRICK: we add at the beginning so when we find for remove or get we always have the last one + usedVariables.removeLast(); + + if (store) controller.getOperandStack().storeVar(answer); + + return answer.getIndex(); + } + + private void resetVariableIndex(boolean isStatic) { + temporaryVariables.clear(); + if (!isStatic) { + currentVariableIndex=1; + nextVariableIndex=1; + } else { + currentVariableIndex=0; + nextVariableIndex=0; + } + } + + /** + * Clears the state of the class. This method should be called + * after a MethodNode is visited. Note that a call to init will + * fail if clear is not called before + */ + public void clear() { + if (stateStack.size()>1) { + int size = stateStack.size()-1; + throw new GroovyBugError("the compile stack contains "+size+" more push instruction"+(size==1?"":"s")+" than pops."); + } + if (lhsStack.size()>1) { + int size = lhsStack.size()-1; + throw new GroovyBugError("lhs stack is supposed to be empty, but has " + + size + " elements left."); + } + if (implicitThisStack.size()>1) { + int size = implicitThisStack.size()-1; + throw new GroovyBugError("implicit 'this' stack is supposed to be empty, but has " + + size + " elements left."); + } + clear = true; + MethodVisitor mv = controller.getMethodVisitor(); + // br experiment with local var table so debuggers can retrieve variable names + if (true) {//AsmClassGenerator.CREATE_DEBUG_INFO) { + if (thisEndLabel==null) setEndLabels(); + + if (!scope.isInStaticContext()) { + // write "this" + mv.visitLocalVariable("this", className, null, thisStartLabel, thisEndLabel, 0); + } + + for (Iterator iterator = usedVariables.iterator(); iterator.hasNext();) { + BytecodeVariable v = (BytecodeVariable) iterator.next(); + ClassNode t = v.getType(); + if (v.isHolder()) t = ClassHelper.REFERENCE_TYPE; + String type = BytecodeHelper.getTypeDescription(t); + Label start = v.getStartLabel(); + Label end = v.getEndLabel(); + // GRECLIPSE add + if (start != null && end != null) + // GRECLIPSE end + mv.visitLocalVariable(v.getName(), type, null, start, end, v.getIndex()); + } + } + + //exception table writing + for (ExceptionTableEntry ep : typedExceptions) { + mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig); + } + //exception table writing + for (ExceptionTableEntry ep : untypedExceptions) { + mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig); + } + + + pop(); + typedExceptions.clear(); + untypedExceptions.clear(); + stackVariables.clear(); + usedVariables.clear(); + scope = null; + finallyBlocks.clear(); + resetVariableIndex(false); + superBlockNamedLabels.clear(); + currentBlockNamedLabels.clear(); + namedLoopBreakLabel.clear(); + namedLoopContinueLabel.clear(); + continueLabel=null; + breakLabel=null; + thisStartLabel=null; + thisEndLabel=null; + mv = null; + } + + public void addExceptionBlock (Label start, Label end, Label goal, + String sig) + { + // this code is in an extra method to avoid + // lazy initialization issues + ExceptionTableEntry ep = new ExceptionTableEntry(); + ep.start = start; + ep.end = end; + ep.sig = sig; + ep.goal = goal; + if (sig==null) { + untypedExceptions.add(ep); + } else { + typedExceptions.add(ep); + } + } + + /** + * initializes this class for a MethodNode. This method will + * automatically define variables for the method parameters + * and will create references if needed. The created variables + * can be accessed by calling getVariable(). + * + */ + public void init(VariableScope el, Parameter[] parameters) { + if (!clear) throw new GroovyBugError("CompileStack#init called without calling clear before"); + clear=false; + pushVariableScope(el); + defineMethodVariables(parameters,el.isInStaticContext()); + this.className = BytecodeHelper.getTypeDescription(controller.getClassNode()); + } + + /** + * Causes the state-stack to add an element and sets + * the given scope as new current variable scope. Creates + * a element for the state stack so pop has to be called later + */ + public void pushVariableScope(VariableScope el) { + pushState(); + scope = el; + superBlockNamedLabels = new HashMap(superBlockNamedLabels); + superBlockNamedLabels.putAll(currentBlockNamedLabels); + currentBlockNamedLabels = new HashMap(); + } + + /** + * Should be called when descending into a loop that defines + * also a scope. Calls pushVariableScope and prepares labels + * for a loop structure. Creates a element for the state stack + * so pop has to be called later, TODO: @Deprecate + */ + public void pushLoop(VariableScope el, String labelName) { + pushVariableScope(el); + continueLabel = new Label(); + breakLabel = new Label(); + if (labelName != null) { + initLoopLabels(labelName); + } + } + + /** + * Should be called when descending into a loop that defines + * also a scope. Calls pushVariableScope and prepares labels + * for a loop structure. Creates a element for the state stack + * so pop has to be called later + */ + public void pushLoop(VariableScope el, List labelNames) { + pushVariableScope(el); + continueLabel = new Label(); + breakLabel = new Label(); + if (labelNames != null) { + for (String labelName : labelNames) { + initLoopLabels(labelName); + } + } + } + + private void initLoopLabels(String labelName) { + namedLoopBreakLabel.put(labelName,breakLabel); + namedLoopContinueLabel.put(labelName,continueLabel); + } + + /** + * Should be called when descending into a loop that does + * not define a scope. Creates a element for the state stack + * so pop has to be called later, TODO: @Deprecate + */ + public void pushLoop(String labelName) { + pushState(); + continueLabel = new Label(); + breakLabel = new Label(); + initLoopLabels(labelName); + } + + /** + * Should be called when descending into a loop that does + * not define a scope. Creates a element for the state stack + * so pop has to be called later + */ + public void pushLoop(List labelNames) { + pushState(); + continueLabel = new Label(); + breakLabel = new Label(); + if (labelNames != null) { + for (String labelName : labelNames) { + initLoopLabels(labelName); + } + } + } + + /** + * Used for break foo inside a loop to end the + * execution of the marked loop. This method will return the + * break label of the loop if there is one found for the name. + * If not, the current break label is returned. + */ + public Label getNamedBreakLabel(String name) { + Label label = getBreakLabel(); + Label endLabel = null; + if (name!=null) endLabel = (Label) namedLoopBreakLabel.get(name); + if (endLabel!=null) label = endLabel; + return label; + } + + /** + * Used for continue foo inside a loop to continue + * the execution of the marked loop. This method will return + * the break label of the loop if there is one found for the + * name. If not, getLabel is used. + */ + public Label getNamedContinueLabel(String name) { + Label label = getLabel(name); + Label endLabel = null; + if (name!=null) endLabel = (Label) namedLoopContinueLabel.get(name); + if (endLabel!=null) label = endLabel; + return label; + } + + /** + * Creates a new break label and a element for the state stack + * so pop has to be called later + */ + public Label pushSwitch(){ + pushState(); + breakLabel = new Label(); + return breakLabel; + } + + /** + * because a boolean Expression may not be evaluated completely + * it is important to keep the registers clean + */ + public void pushBooleanExpression(){ + pushState(); + } + + private BytecodeVariable defineVar(String name, ClassNode type, boolean holder, boolean useReferenceDirectly) { + int prevCurrent = currentVariableIndex; + makeNextVariableID(type,useReferenceDirectly); + int index = currentVariableIndex; + if (holder && !useReferenceDirectly) index = localVariableOffset++; + BytecodeVariable answer = new BytecodeVariable(index, type, name, prevCurrent); + usedVariables.add(answer); + answer.setHolder(holder); + return answer; + } + + private void makeLocalVariablesOffset(Parameter[] paras, boolean isInStaticContext) { + resetVariableIndex(isInStaticContext); + + for (Parameter para : paras) { + makeNextVariableID(para.getType(), false); + } + localVariableOffset = nextVariableIndex; + + resetVariableIndex(isInStaticContext); + } + + private void defineMethodVariables(Parameter[] paras, boolean isInStaticContext) { + Label startLabel = new Label(); + thisStartLabel = startLabel; + controller.getMethodVisitor().visitLabel(startLabel); + + makeLocalVariablesOffset(paras,isInStaticContext); + + for (Parameter para : paras) { + String name = para.getName(); + BytecodeVariable answer; + ClassNode type = para.getType(); + if (para.isClosureSharedVariable()) { + boolean useExistingReference = para.getNodeMetaData(ClosureWriter.UseExistingReference.class) != null; + answer = defineVar(name, para.getOriginType(), true, useExistingReference); + answer.setStartLabel(startLabel); + if (!useExistingReference) { + controller.getOperandStack().load(type, currentVariableIndex); + controller.getOperandStack().box(); + + // GROOVY-4237, the original variable should always appear + // in the variable index, otherwise some programs get into + // trouble. So we define a dummy variable for the packaging + // phase and let it end right away before the normal + // reference will be used + Label newStart = new Label(); + controller.getMethodVisitor().visitLabel(newStart); + BytecodeVariable var = new BytecodeVariable(currentVariableIndex, para.getOriginType(), name, currentVariableIndex); + var.setStartLabel(startLabel); + var.setEndLabel(newStart); + usedVariables.add(var); + answer.setStartLabel(newStart); + + createReference(answer); + } + } else { + answer = defineVar(name, type, false, false); + answer.setStartLabel(startLabel); + } + stackVariables.put(name, answer); + } + + nextVariableIndex = localVariableOffset; + } + + private void createReference(BytecodeVariable reference) { + MethodVisitor mv = controller.getMethodVisitor(); + mv.visitTypeInsn(NEW, "groovy/lang/Reference"); + mv.visitInsn(DUP_X1); + mv.visitInsn(SWAP); + mv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "", "(Ljava/lang/Object;)V", false); + mv.visitVarInsn(ASTORE, reference.getIndex()); + } + + private static void pushInitValue(ClassNode type, MethodVisitor mv) { + if (ClassHelper.isPrimitiveType(type)) { + if (type== ClassHelper.long_TYPE) { + mv.visitInsn(LCONST_0); + } else if (type== ClassHelper.double_TYPE) { + mv.visitInsn(DCONST_0); + } else if (type== ClassHelper.float_TYPE) { + mv.visitInsn(FCONST_0); + } else { + mv.visitLdcInsn(0); + } + } else { + mv.visitInsn(ACONST_NULL); + } + } + + /** + * Defines a new Variable using an AST variable. + * @param initFromStack if true the last element of the + * stack will be used to initialize + * the new variable. If false null + * will be used. + */ + public BytecodeVariable defineVariable(Variable v, boolean initFromStack) { + return defineVariable(v, v.getOriginType(), initFromStack); + } + + public BytecodeVariable defineVariable(Variable v, ClassNode variableType, boolean initFromStack) { + String name = v.getName(); + BytecodeVariable answer = defineVar(name, variableType, v.isClosureSharedVariable(), v.isClosureSharedVariable()); + stackVariables.put(name, answer); + + MethodVisitor mv = controller.getMethodVisitor(); + Label startLabel = new Label(); + answer.setStartLabel(startLabel); + ClassNode type = answer.getType().redirect(); + OperandStack operandStack = controller.getOperandStack(); + + if (!initFromStack) { + if (ClassHelper.isPrimitiveType(v.getOriginType()) && ClassHelper.getWrapper(v.getOriginType()) == variableType) { + pushInitValue(v.getOriginType(), mv); + operandStack.push(v.getOriginType()); + operandStack.box(); + operandStack.remove(1); + } else { + pushInitValue(type, mv); + } + } + operandStack.push(answer.getType()); + if (answer.isHolder()) { + operandStack.box(); + operandStack.remove(1); + createReference(answer); + } else { + operandStack.storeVar(answer); + } + + mv.visitLabel(startLabel); + return answer; + } + + /** + * @param name the name of the variable of interest + * @return true if a variable is already defined + */ + public boolean containsVariable(String name) { + return stackVariables.containsKey(name); + } + + /** + * Calculates the index of the next free register stores it + * and sets the current variable index to the old value + */ + private void makeNextVariableID(ClassNode type, boolean useReferenceDirectly) { + currentVariableIndex = nextVariableIndex; + if ((type== ClassHelper.long_TYPE || type== ClassHelper.double_TYPE) && !useReferenceDirectly) { + nextVariableIndex++; + } + nextVariableIndex++; + } + + /** + * Returns the label for the given name + */ + public Label getLabel(String name) { + if (name==null) return null; + Label l = (Label) superBlockNamedLabels.get(name); + if (l==null) l = createLocalLabel(name); + return l; + } + + /** + * creates a new named label + */ + public Label createLocalLabel(String name) { + Label l = (Label) currentBlockNamedLabels.get(name); + if (l==null) { + l = new Label(); + currentBlockNamedLabels.put(name,l); + } + return l; + } + + public void applyFinallyBlocks(Label label, boolean isBreakLabel) { + // first find the state defining the label. That is the state + // directly after the state not knowing this label. If no state + // in the list knows that label, then the defining state is the + // current state. + StateStackElement result = null; + for (ListIterator iter = stateStack.listIterator(stateStack.size()); iter.hasPrevious();) { + StateStackElement element = (StateStackElement) iter.previous(); + if (!element.currentBlockNamedLabels.values().contains(label)) { + if (isBreakLabel && element.breakLabel != label) { + result = element; + break; + } + if (!isBreakLabel && element.continueLabel != label) { + result = element; + break; + } + } + } + + List blocksToRemove; + if (result==null) { + // all Blocks do know the label, so use all finally blocks + blocksToRemove = (List) Collections.EMPTY_LIST; + } else { + blocksToRemove = result.finallyBlocks; + } + + List blocks = new LinkedList(finallyBlocks); + blocks.removeAll(blocksToRemove); + applyBlockRecorder(blocks); + } + + + private void applyBlockRecorder(List blocks) { + if (blocks.isEmpty() || blocks.size() == visitedBlocks.size()) return; + + MethodVisitor mv = controller.getMethodVisitor(); + Label newStart = new Label(); + + for (BlockRecorder fb : blocks) { + if (visitedBlocks.contains(fb)) continue; + + Label end = new Label(); + mv.visitInsn(NOP); + mv.visitLabel(end); + + fb.closeRange(end); + + // we exclude the finally block from the exception table + // here to avoid double visiting of finally statements + fb.excludedStatement.run(); + + fb.startRange(newStart); + } + + mv.visitInsn(NOP); + mv.visitLabel(newStart); + } + + public void applyBlockRecorder() { + applyBlockRecorder(finallyBlocks); + } + + public boolean hasBlockRecorder() { + return !finallyBlocks.isEmpty(); + } + + public void pushBlockRecorder(BlockRecorder recorder) { + pushState(); + finallyBlocks.addFirst(recorder); + } + + public void pushBlockRecorderVisit(BlockRecorder finallyBlock) { + visitedBlocks.add(finallyBlock); + } + + public void popBlockRecorderVisit(BlockRecorder finallyBlock) { + visitedBlocks.remove(finallyBlock); + } + + public void writeExceptionTable(BlockRecorder block, Label goal, String sig) { + if (block.isEmpty) return; + MethodVisitor mv = controller.getMethodVisitor(); + for (LabelRange range : block.ranges) { + mv.visitTryCatchBlock(range.start, range.end, goal, sig); + } + } + +// public MethodVisitor getMethodVisitor() { +// return mv; +// } + + public boolean isLHS() { + return lhs; + } + + public void pushLHS(boolean lhs) { + lhsStack.add(lhs); + this.lhs = lhs; + } + + public void popLHS() { + lhsStack.removeLast(); + this.lhs = lhsStack.getLast(); + } + + public void pushImplicitThis(boolean implicitThis) { + implicitThisStack.add(implicitThis); + this.implicitThis = implicitThis; + } + + public boolean isImplicitThis() { + return implicitThis; + } + + public void popImplicitThis() { + implicitThisStack.removeLast(); + this.implicitThis = implicitThisStack.getLast(); + } + + public boolean isInSpecialConstructorCall() { + return inSpecialConstructorCall; + } + + public void pushInSpecialConstructorCall() { + pushState(); + inSpecialConstructorCall = true; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/StatementWriter.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/StatementWriter.java new file mode 100644 index 0000000000..af72a11d8c --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/StatementWriter.java @@ -0,0 +1,641 @@ +/* + * 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; + +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.EmptyExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.stmt.AssertStatement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.DoWhileStatement; +import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.SynchronizedStatement; +import org.codehaus.groovy.ast.stmt.ThrowStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.classgen.asm.CompileStack.BlockRecorder; +import groovyjarjarasm.asm.Label; +import groovyjarjarasm.asm.MethodVisitor; + +import java.util.Iterator; +import java.util.List; + +import static groovyjarjarasm.asm.Opcodes.ALOAD; +import static groovyjarjarasm.asm.Opcodes.ATHROW; +import static groovyjarjarasm.asm.Opcodes.CHECKCAST; +import static groovyjarjarasm.asm.Opcodes.GOTO; +import static groovyjarjarasm.asm.Opcodes.IFEQ; +import static groovyjarjarasm.asm.Opcodes.MONITORENTER; +import static groovyjarjarasm.asm.Opcodes.MONITOREXIT; +import static groovyjarjarasm.asm.Opcodes.NOP; +import static groovyjarjarasm.asm.Opcodes.RETURN; + +public class StatementWriter { + // iterator + private static final MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next"); + private static final MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext"); + + private final WriterController controller; + public StatementWriter(WriterController controller) { + this.controller = controller; + } + + protected void writeStatementLabel(Statement statement) { + String name = statement.getStatementLabel(); + if (name != null) { + Label label = controller.getCompileStack().createLocalLabel(name); + controller.getMethodVisitor().visitLabel(label); + } + } + + public void writeBlockStatement(BlockStatement block) { + CompileStack compileStack = controller.getCompileStack(); + + //GROOVY-4505 use no line number information for the block + writeStatementLabel(block); + + int mark = controller.getOperandStack().getStackLength(); + compileStack.pushVariableScope(block.getVariableScope()); + for (Statement statement : block.getStatements()) { + statement.visit(controller.getAcg()); + } + compileStack.pop(); + + controller.getOperandStack().popDownTo(mark); + } + + public void writeForStatement(ForStatement loop) { + Parameter loopVar = loop.getVariable(); + if (loopVar == ForStatement.FOR_LOOP_DUMMY) { + writeForLoopWithClosureList(loop); + } else { + writeForInLoop(loop); + } + } + + protected void writeIteratorHasNext(MethodVisitor mv) { + iteratorHasNextMethod.call(mv); + } + + protected void writeIteratorNext(MethodVisitor mv) { + iteratorNextMethod.call(mv); + } + + protected void writeForInLoop(ForStatement loop) { + controller.getAcg().onLineNumber(loop,"visitForLoop"); + writeStatementLabel(loop); + + CompileStack compileStack = controller.getCompileStack(); + MethodVisitor mv = controller.getMethodVisitor(); + OperandStack operandStack = controller.getOperandStack(); + + compileStack.pushLoop(loop.getVariableScope(), loop.getStatementLabels()); + + // Declare the loop counter. + BytecodeVariable variable = compileStack.defineVariable(loop.getVariable(), false); + + // Then get the iterator and generate the loop control + MethodCallExpression iterator = new MethodCallExpression(loop.getCollectionExpression(), "iterator", new ArgumentListExpression()); + iterator.visit(controller.getAcg()); + operandStack.doGroovyCast(ClassHelper.Iterator_TYPE); + + final int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.Iterator_TYPE, true); + + Label continueLabel = compileStack.getContinueLabel(); + Label breakLabel = compileStack.getBreakLabel(); + + mv.visitLabel(continueLabel); + mv.visitVarInsn(ALOAD, iteratorIdx); + writeIteratorHasNext(mv); + // note: ifeq tests for ==0, a boolean is 0 if it is false + mv.visitJumpInsn(IFEQ, breakLabel); + + mv.visitVarInsn(ALOAD, iteratorIdx); + writeIteratorNext(mv); + operandStack.push(ClassHelper.OBJECT_TYPE); + operandStack.storeVar(variable); + + // Generate the loop body + loop.getLoopBlock().visit(controller.getAcg()); + + mv.visitJumpInsn(GOTO, continueLabel); + mv.visitLabel(breakLabel); + + compileStack.removeVar(iteratorIdx); + compileStack.pop(); + } + + private void visitExpressionOfLoopStatement(Expression expression) { + if (expression instanceof ClosureListExpression) { + for (Expression e : ((ClosureListExpression) expression).getExpressions()) { + visitExpressionOrStatement(e); + } + } else { + visitExpressionOrStatement(expression); + } + } + + protected void writeForLoopWithClosureList(ForStatement loop) { + controller.getAcg().onLineNumber(loop,"visitForLoop"); + writeStatementLabel(loop); + + MethodVisitor mv = controller.getMethodVisitor(); + controller.getCompileStack().pushLoop(loop.getVariableScope(), loop.getStatementLabels()); + + ClosureListExpression clExpr = (ClosureListExpression) loop.getCollectionExpression(); + controller.getCompileStack().pushVariableScope(clExpr.getVariableScope()); + + List expressions = clExpr.getExpressions(); + int size = expressions.size(); + + // middle element is condition, lower half is init, higher half is increment + int condIndex = (size - 1) / 2; + + // visit init + for (int i = 0; i < condIndex; i++) { + visitExpressionOfLoopStatement(expressions.get(i)); + } + + Label continueLabel = controller.getCompileStack().getContinueLabel(); + Label breakLabel = controller.getCompileStack().getBreakLabel(); + + Label cond = new Label(); + mv.visitLabel(cond); + // visit condition leave boolean on stack + { + Expression condExpr = expressions.get(condIndex); + int mark = controller.getOperandStack().getStackLength(); + condExpr.visit(controller.getAcg()); + controller.getOperandStack().castToBool(mark,true); + } + // jump if we don't want to continue + // note: ifeq tests for ==0, a boolean is 0 if it is false + controller.getOperandStack().jump(IFEQ, breakLabel); + + // Generate the loop body + loop.getLoopBlock().visit(controller.getAcg()); + + // visit increment + mv.visitLabel(continueLabel); + // GRECLIPSE fix for being on the wrong line when debugging for loop + controller.getAcg().onLineNumber(loop, "increment condition"); + // GRECLIPSE end + for (int i = condIndex + 1; i < size; i++) { + visitExpressionOfLoopStatement(expressions.get(i)); + } + + // jump to test the condition again + mv.visitJumpInsn(GOTO, cond); + + // loop end + mv.visitLabel(breakLabel); + + controller.getCompileStack().pop(); + controller.getCompileStack().pop(); + } + + private void visitExpressionOrStatement(Object o) { + if (o == EmptyExpression.INSTANCE) return; + if (o instanceof Expression) { + Expression expr = (Expression) o; + int mark = controller.getOperandStack().getStackLength(); + expr.visit(controller.getAcg()); + controller.getOperandStack().popDownTo(mark); + } else { + ((Statement) o).visit(controller.getAcg()); + } + } + + private void visitConditionOfLoopingStatement(BooleanExpression bool, Label breakLabel, MethodVisitor mv) { + boolean boolHandled = false; + if (bool.getExpression() instanceof ConstantExpression) { + ConstantExpression constant = (ConstantExpression) bool.getExpression(); + if (constant.getValue()==Boolean.TRUE) { + boolHandled = true; + // do nothing + } else if (constant.getValue()==Boolean.FALSE) { + boolHandled = true; + mv.visitJumpInsn(GOTO, breakLabel); + } + } + + if(!boolHandled) { + bool.visit(controller.getAcg()); + controller.getOperandStack().jump(IFEQ, breakLabel); + } + } + + public void writeWhileLoop(WhileStatement loop) { + controller.getAcg().onLineNumber(loop,"visitWhileLoop"); + writeStatementLabel(loop); + + MethodVisitor mv = controller.getMethodVisitor(); + + controller.getCompileStack().pushLoop(loop.getStatementLabels()); + Label continueLabel = controller.getCompileStack().getContinueLabel(); + Label breakLabel = controller.getCompileStack().getBreakLabel(); + + mv.visitLabel(continueLabel); + + this.visitConditionOfLoopingStatement(loop.getBooleanExpression(), breakLabel, mv); + loop.getLoopBlock().visit(controller.getAcg()); + + mv.visitJumpInsn(GOTO, continueLabel); + mv.visitLabel(breakLabel); + + controller.getCompileStack().pop(); + } + + public void writeDoWhileLoop(DoWhileStatement loop) { + controller.getAcg().onLineNumber(loop,"visitDoWhileLoop"); + writeStatementLabel(loop); + + MethodVisitor mv = controller.getMethodVisitor(); + + controller.getCompileStack().pushLoop(loop.getStatementLabels()); + Label continueLabel = controller.getCompileStack().getContinueLabel(); + Label breakLabel = controller.getCompileStack().getBreakLabel(); + + mv.visitLabel(continueLabel); + + loop.getLoopBlock().visit(controller.getAcg()); + this.visitConditionOfLoopingStatement(loop.getBooleanExpression(), breakLabel, mv); + + mv.visitJumpInsn(GOTO, continueLabel); + mv.visitLabel(breakLabel); + + controller.getCompileStack().pop(); + } + + public void writeIfElse(IfStatement ifElse) { + controller.getAcg().onLineNumber(ifElse,"visitIfElse"); + writeStatementLabel(ifElse); + + MethodVisitor mv = controller.getMethodVisitor(); + + ifElse.getBooleanExpression().visit(controller.getAcg()); + Label l0 = controller.getOperandStack().jump(IFEQ); + + // if-else is here handled as a special version + // of a boolean expression + controller.getCompileStack().pushBooleanExpression(); + ifElse.getIfBlock().visit(controller.getAcg()); + controller.getCompileStack().pop(); + + if (ifElse.getElseBlock()==EmptyStatement.INSTANCE) { + mv.visitLabel(l0); + } else { + Label l1 = new Label(); + mv.visitJumpInsn(GOTO, l1); + mv.visitLabel(l0); + + controller.getCompileStack().pushBooleanExpression(); + ifElse.getElseBlock().visit(controller.getAcg()); + controller.getCompileStack().pop(); + + mv.visitLabel(l1); + } + } + + public void writeTryCatchFinally(TryCatchStatement statement) { + controller.getAcg().onLineNumber(statement, "visitTryCatchFinally"); + writeStatementLabel(statement); + + MethodVisitor mv = controller.getMethodVisitor(); + CompileStack compileStack = controller.getCompileStack(); + OperandStack operandStack = controller.getOperandStack(); + + Statement tryStatement = statement.getTryStatement(); + final Statement finallyStatement = statement.getFinallyStatement(); + + // start try block, label needed for exception table + Label tryStart = new Label(); + mv.visitLabel(tryStart); + BlockRecorder tryBlock = makeBlockRecorder(finallyStatement); + tryBlock.startRange(tryStart); + + tryStatement.visit(controller.getAcg()); + + // goto finally part + Label finallyStart = new Label(); + mv.visitJumpInsn(GOTO, finallyStart); + + Label tryEnd = new Label(); + mv.visitLabel(tryEnd); + tryBlock.closeRange(tryEnd); + // pop for "makeBlockRecorder(finallyStatement)" + controller.getCompileStack().pop(); + + BlockRecorder catches = makeBlockRecorder(finallyStatement); + for (CatchStatement catchStatement : statement.getCatchStatements()) { + ClassNode exceptionType = catchStatement.getExceptionType(); + String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType); + + // start catch block, label needed for exception table + Label catchStart = new Label(); + mv.visitLabel(catchStart); + catches.startRange(catchStart); + + // create exception variable and store the exception + Parameter exceptionVariable = catchStatement.getVariable(); + compileStack.pushState(); + compileStack.defineVariable(exceptionVariable, true); + // handle catch body + catchStatement.visit(controller.getAcg()); + // place holder to avoid problems with empty catch blocks + mv.visitInsn(NOP); + // pop for the variable + controller.getCompileStack().pop(); + + // end of catch + Label catchEnd = new Label(); + mv.visitLabel(catchEnd); + catches.closeRange(catchEnd); + + // goto finally start + mv.visitJumpInsn(GOTO, finallyStart); + compileStack.writeExceptionTable(tryBlock, catchStart, exceptionTypeInternalName); + } + + // Label used to handle exceptions in catches and regularly + // visited finals. + Label catchAny = new Label(); + + // add "catch any" block to exception table for try part we do this + // after the exception blocks, because else this one would supersede + // any of those otherwise + compileStack.writeExceptionTable(tryBlock, catchAny, null); + // same for the catch parts + compileStack.writeExceptionTable(catches, catchAny, null); + + // pop for "makeBlockRecorder(catches)" + compileStack.pop(); + + // start finally + mv.visitLabel(finallyStart); + finallyStatement.visit(controller.getAcg()); + mv.visitInsn(NOP); //** + + // goto after all-catching block + Label skipCatchAll = new Label(); + mv.visitJumpInsn(GOTO, skipCatchAll); + + // start a block catching any Exception + mv.visitLabel(catchAny); + //store exception + //TODO: maybe define a Throwable and use it here instead of Object + operandStack.push(ClassHelper.OBJECT_TYPE); + final int anyExceptionIndex = compileStack.defineTemporaryVariable("exception", true); + + finallyStatement.visit(controller.getAcg()); + + // load the exception and rethrow it + mv.visitVarInsn(ALOAD, anyExceptionIndex); + mv.visitInsn(ATHROW); + + mv.visitLabel(skipCatchAll); + compileStack.removeVar(anyExceptionIndex); + } + + private BlockRecorder makeBlockRecorder(final Statement finallyStatement) { + final BlockRecorder block = new BlockRecorder(); + Runnable tryRunner = new Runnable() { + public void run() { + controller.getCompileStack().pushBlockRecorderVisit(block); + finallyStatement.visit(controller.getAcg()); + controller.getCompileStack().popBlockRecorderVisit(block); + } + }; + block.excludedStatement = tryRunner; + controller.getCompileStack().pushBlockRecorder(block); + return block; + } + + public void writeSwitch(SwitchStatement statement) { + controller.getAcg().onLineNumber(statement, "visitSwitch"); + writeStatementLabel(statement); + + statement.getExpression().visit(controller.getAcg()); + + // switch does not have a continue label. use its parent's for continue + Label breakLabel = controller.getCompileStack().pushSwitch(); + + final int switchVariableIndex = controller.getCompileStack().defineTemporaryVariable("switch", true); + + List caseStatements = statement.getCaseStatements(); + int caseCount = caseStatements.size(); + Label[] labels = new Label[caseCount + 1]; + for (int i = 0; i < caseCount; i++) { + labels[i] = new Label(); + } + + int i = 0; + for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) { + CaseStatement caseStatement = (CaseStatement) iter.next(); + writeCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]); + } + + statement.getDefaultStatement().visit(controller.getAcg()); + + controller.getMethodVisitor().visitLabel(breakLabel); + + controller.getCompileStack().removeVar(switchVariableIndex); + controller.getCompileStack().pop(); + } + + protected void writeCaseStatement( + CaseStatement statement, int switchVariableIndex, + Label thisLabel, Label nextLabel) + { + controller.getAcg().onLineNumber(statement, "visitCaseStatement"); + MethodVisitor mv = controller.getMethodVisitor(); + OperandStack operandStack = controller.getOperandStack(); + + mv.visitVarInsn(ALOAD, switchVariableIndex); + + statement.getExpression().visit(controller.getAcg()); + operandStack.box(); + controller.getBinaryExpressionHelper().getIsCaseMethod().call(mv); + operandStack.replace(ClassHelper.boolean_TYPE); + + Label l0 = controller.getOperandStack().jump(IFEQ); + + mv.visitLabel(thisLabel); + + statement.getCode().visit(controller.getAcg()); + + // now if we don't finish with a break we need to jump past + // the next comparison + if (nextLabel != null) { + mv.visitJumpInsn(GOTO, nextLabel); + } + + mv.visitLabel(l0); + } + + public void writeBreak(BreakStatement statement) { + controller.getAcg().onLineNumber(statement, "visitBreakStatement"); + writeStatementLabel(statement); + + String name = statement.getLabel(); + Label breakLabel = controller.getCompileStack().getNamedBreakLabel(name); + controller.getCompileStack().applyFinallyBlocks(breakLabel, true); + + controller.getMethodVisitor().visitJumpInsn(GOTO, breakLabel); + } + + public void writeContinue(ContinueStatement statement) { + controller.getAcg().onLineNumber(statement, "visitContinueStatement"); + writeStatementLabel(statement); + + String name = statement.getLabel(); + Label continueLabel = controller.getCompileStack().getContinueLabel(); + if (name != null) continueLabel = controller.getCompileStack().getNamedContinueLabel(name); + controller.getCompileStack().applyFinallyBlocks(continueLabel, false); + controller.getMethodVisitor().visitJumpInsn(GOTO, continueLabel); + } + + public void writeSynchronized(SynchronizedStatement statement) { + controller.getAcg().onLineNumber(statement, "visitSynchronizedStatement"); + writeStatementLabel(statement); + final MethodVisitor mv = controller.getMethodVisitor(); + CompileStack compileStack = controller.getCompileStack(); + + statement.getExpression().visit(controller.getAcg()); + controller.getOperandStack().box(); + final int index = compileStack.defineTemporaryVariable("synchronized", ClassHelper.OBJECT_TYPE, true); + + final Label synchronizedStart = new Label(); + final Label synchronizedEnd = new Label(); + final Label catchAll = new Label(); + + mv.visitVarInsn(ALOAD, index); + mv.visitInsn(MONITORENTER); + mv.visitLabel(synchronizedStart); + // place holder for "empty" synchronized blocks, for example + // if there is only a break/continue. + mv.visitInsn(NOP); + + Runnable finallyPart = new Runnable() { + public void run() { + mv.visitVarInsn(ALOAD, index); + mv.visitInsn(MONITOREXIT); + } + }; + BlockRecorder fb = new BlockRecorder(finallyPart); + fb.startRange(synchronizedStart); + compileStack.pushBlockRecorder(fb); + statement.getCode().visit(controller.getAcg()); + + fb.closeRange(catchAll); + compileStack.writeExceptionTable(fb, catchAll, null); + compileStack.pop(); //pop fb + + finallyPart.run(); + mv.visitJumpInsn(GOTO, synchronizedEnd); + mv.visitLabel(catchAll); + finallyPart.run(); + mv.visitInsn(ATHROW); + + mv.visitLabel(synchronizedEnd); + compileStack.removeVar(index); + } + + public void writeAssert(AssertStatement statement) { + controller.getAcg().onLineNumber(statement, "visitAssertStatement"); + writeStatementLabel(statement); + controller.getAssertionWriter().writeAssertStatement(statement); + } + + public void writeThrow(ThrowStatement statement) { + controller.getAcg().onLineNumber(statement, "visitThrowStatement"); + writeStatementLabel(statement); + MethodVisitor mv = controller.getMethodVisitor(); + + statement.getExpression().visit(controller.getAcg()); + + // we should infer the type of the exception from the expression + mv.visitTypeInsn(CHECKCAST, "java/lang/Throwable"); + mv.visitInsn(ATHROW); + + controller.getOperandStack().remove(1); + } + + public void writeReturn(ReturnStatement statement) { + controller.getAcg().onLineNumber(statement, "visitReturnStatement"); + writeStatementLabel(statement); + MethodVisitor mv = controller.getMethodVisitor(); + OperandStack operandStack = controller.getOperandStack(); + ClassNode returnType = controller.getReturnType(); + + if (returnType == ClassHelper.VOID_TYPE) { + if (!(statement.isReturningNullOrVoid())) { + //TODO: move to Verifier + controller.getAcg().throwException("Cannot use return statement with an expression on a method that returns void"); + } + controller.getCompileStack().applyBlockRecorder(); + mv.visitInsn(RETURN); + return; + } + + Expression expression = statement.getExpression(); + expression.visit(controller.getAcg()); + + operandStack.doGroovyCast(returnType); + + if (controller.getCompileStack().hasBlockRecorder()) { + ClassNode type = operandStack.getTopOperand(); + int returnValueIdx = controller.getCompileStack().defineTemporaryVariable("returnValue", returnType, true); + controller.getCompileStack().applyBlockRecorder(); + operandStack.load(type, returnValueIdx); + controller.getCompileStack().removeVar(returnValueIdx); + } + + BytecodeHelper.doReturn(mv, returnType); + operandStack.remove(1); + } + + public void writeExpressionStatement(ExpressionStatement statement) { + controller.getAcg().onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName()); + writeStatementLabel(statement); + + Expression expression = statement.getExpression(); + + int mark = controller.getOperandStack().getStackLength(); + expression.visit(controller.getAcg()); + controller.getOperandStack().popDownTo(mark); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/WriterController.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/WriterController.java new file mode 100644 index 0000000000..9bc4de3dc7 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/WriterController.java @@ -0,0 +1,410 @@ +/* + * 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; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.InterfaceHelperClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.classgen.AsmClassGenerator; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.classgen.asm.indy.IndyBinHelper; +import org.codehaus.groovy.classgen.asm.indy.IndyCallSiteWriter; +import org.codehaus.groovy.classgen.asm.indy.InvokeDynamicWriter; +import org.codehaus.groovy.classgen.asm.util.LoggableClassVisitor; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.control.SourceUnit; +import groovyjarjarasm.asm.ClassVisitor; +import groovyjarjarasm.asm.MethodVisitor; +import groovyjarjarasm.asm.Opcodes; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class WriterController { + private static final String GROOVY_LOG_CLASSGEN = "groovy.log.classgen"; + private static final boolean LOG_CLASSGEN; + static { + LOG_CLASSGEN = Boolean.valueOf(System.getProperty(GROOVY_LOG_CLASSGEN)); + } + private AsmClassGenerator acg; + private MethodVisitor methodVisitor; + private CompileStack compileStack; + private OperandStack operandStack; + private ClassNode classNode; + private CallSiteWriter callSiteWriter; + private ClassVisitor cv; + private ClosureWriter closureWriter; + private LambdaWriter lambdaWriter; + private String internalClassName; + private InvocationWriter invocationWriter; + private BinaryExpressionHelper binaryExpHelper, fastPathBinaryExpHelper; + private UnaryExpressionHelper unaryExpressionHelper, fastPathUnaryExpressionHelper; + private AssertionWriter assertionWriter; + private String internalBaseClassName; + private ClassNode outermostClass; + private MethodNode methodNode; + private SourceUnit sourceUnit; + private ConstructorNode constructorNode; + private GeneratorContext context; + private InterfaceHelperClassNode interfaceClassLoadingClass; + public boolean optimizeForInt = true; + private StatementWriter statementWriter; + private boolean fastPath = false; + private TypeChooser typeChooser; + private int bytecodeVersion = Opcodes.V1_5; + private int lineNumber = -1; + private int helperMethodIndex = 0; + private List superMethodNames = new ArrayList(); + + public void init(AsmClassGenerator asmClassGenerator, GeneratorContext gcon, ClassVisitor cv, ClassNode cn) { + CompilerConfiguration config = cn.getCompileUnit().getConfig(); + Map optOptions = config.getOptimizationOptions(); + boolean invokedynamic=false; + if (optOptions.isEmpty()) { + // IGNORE + } else if (Boolean.FALSE.equals(optOptions.get("all"))) { + optimizeForInt=false; + // set other optimizations options to false here + } else { + if (Boolean.TRUE.equals(optOptions.get(CompilerConfiguration.INVOKEDYNAMIC))) invokedynamic=true; + if (Boolean.FALSE.equals(optOptions.get("int"))) optimizeForInt=false; + if (invokedynamic) optimizeForInt=false; + // set other optimizations options to false here + } + this.classNode = cn; + this.outermostClass = null; + this.internalClassName = BytecodeHelper.getClassInternalName(classNode); + + bytecodeVersion = chooseBytecodeVersion(invokedynamic, config.getTargetBytecode()); + + if (invokedynamic) { + this.invocationWriter = new InvokeDynamicWriter(this); + this.callSiteWriter = new IndyCallSiteWriter(this); + this.binaryExpHelper = new IndyBinHelper(this); + } else { + this.callSiteWriter = new CallSiteWriter(this); + this.invocationWriter = new InvocationWriter(this); + this.binaryExpHelper = new BinaryExpressionHelper(this); + } + + this.unaryExpressionHelper = new UnaryExpressionHelper(this); + if (optimizeForInt) { + this.fastPathBinaryExpHelper = new BinaryExpressionMultiTypeDispatcher(this); + // todo: replace with a real fast path unary expression helper when available + this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this); + } else { + this.fastPathBinaryExpHelper = this.binaryExpHelper; + this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this); + } + + this.operandStack = new OperandStack(this); + this.assertionWriter = new AssertionWriter(this); + this.closureWriter = new ClosureWriter(this); + this.lambdaWriter = new LambdaWriter(this); + this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass()); + this.acg = asmClassGenerator; + this.sourceUnit = acg.getSourceUnit(); + this.context = gcon; + this.compileStack = new CompileStack(this); + this.cv = this.createClassVisitor(cv); + // GRECLIPSE add the 2 trailing conditions + if (optimizeForInt && (sourceUnit == null || !sourceUnit.isReconcile)) { + // GRECLIPSE end + this.statementWriter = new OptimizingStatementWriter(this); + } else { + this.statementWriter = new StatementWriter(this); + } + this.typeChooser = new StatementMetaTypeChooser(); + } + + private ClassVisitor createClassVisitor(ClassVisitor cv) { + if (!LOG_CLASSGEN) { + return cv; + } + if (cv instanceof LoggableClassVisitor) { + return cv; + } + return new LoggableClassVisitor(cv); + } + + private static int chooseBytecodeVersion(final boolean invokedynamic, final String targetBytecode) { + if (invokedynamic) { + if (CompilerConfiguration.JDK8.equals(targetBytecode)) { + return Opcodes.V1_8; + } + return Opcodes.V1_7; + } else { + Integer bytecodeVersion = CompilerConfiguration.JDK_TO_BYTECODE_VERSION_MAP.get(targetBytecode); + + if (null != bytecodeVersion) { + return bytecodeVersion; + } + } + + throw new GroovyBugError("Bytecode version ["+targetBytecode+"] is not supported by the compiler"); + } + + public AsmClassGenerator getAcg() { + return acg; + } + + public void setMethodVisitor(MethodVisitor methodVisitor) { + this.methodVisitor = methodVisitor; + } + + public MethodVisitor getMethodVisitor() { + return methodVisitor; + } + + public CompileStack getCompileStack() { + return compileStack; + } + + public OperandStack getOperandStack() { + return operandStack; + } + + public ClassNode getClassNode() { + return classNode; + } + + public CallSiteWriter getCallSiteWriter() { + return callSiteWriter; + } + + public ClassVisitor getClassVisitor() { + return cv; + } + + public ClosureWriter getClosureWriter() { + return closureWriter; + } + + public LambdaWriter getLambdaWriter() { + return lambdaWriter; + } + + public ClassVisitor getCv() { + return cv; + } + + public String getInternalClassName() { + return internalClassName; + } + + public InvocationWriter getInvocationWriter() { + return invocationWriter; + } + + public BinaryExpressionHelper getBinaryExpressionHelper() { + if (fastPath) { + return fastPathBinaryExpHelper; + } else { + return binaryExpHelper; + } + } + + public UnaryExpressionHelper getUnaryExpressionHelper() { + if (fastPath) { + return fastPathUnaryExpressionHelper; + } else { + return unaryExpressionHelper; + } + } + + public AssertionWriter getAssertionWriter() { + return assertionWriter; + } + + public TypeChooser getTypeChooser() { + return typeChooser; + } + + public String getInternalBaseClassName() { + return internalBaseClassName; + } + + public MethodNode getMethodNode() { + return methodNode; + } + + public void setMethodNode(MethodNode mn) { + methodNode = mn; + constructorNode = null; + } + + public ConstructorNode getConstructorNode(){ + return constructorNode; + } + + public void setConstructorNode(ConstructorNode cn) { + constructorNode = cn; + methodNode = null; + } + + public boolean isNotClinit() { + return methodNode == null || !methodNode.getName().equals(""); + } + + public SourceUnit getSourceUnit() { + return sourceUnit; + } + + public boolean isStaticContext() { + if (compileStack!=null && compileStack.getScope()!=null) { + return compileStack.getScope().isInStaticContext(); + } + if (!isInClosure()) return false; + if (constructorNode != null) return false; + return classNode.isStaticClass() || methodNode.isStatic(); + } + + public boolean isInClosure() { + return classNode.getOuterClass() != null + && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE; + } + + public boolean isInClosureConstructor() { + return constructorNode != null + && classNode.getOuterClass() != null + && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE; + } + + public boolean isNotExplicitThisInClosure(boolean implicitThis) { + return implicitThis || !isInClosure(); + } + + + public boolean isStaticMethod() { + return methodNode != null && methodNode.isStatic(); + } + + public ClassNode getReturnType() { + if (methodNode != null) { + return methodNode.getReturnType(); + } else if (constructorNode != null) { + return constructorNode.getReturnType(); + } else { + throw new GroovyBugError("I spotted a return that is neither in a method nor in a constructor... I can not handle that"); + } + } + + public boolean isStaticConstructor() { + return methodNode != null && methodNode.getName().equals(""); + } + + public boolean isConstructor() { + return constructorNode!=null; + } + + /** + * @return true if we are in a script body, where all variables declared are no longer + * local variables but are properties + */ + public boolean isInScriptBody() { + if (classNode.isScriptBody()) { + return true; + } else { + return classNode.isScript() && methodNode != null && methodNode.getName().equals("run"); + } + } + + public String getClassName() { + String className; + if (!classNode.isInterface() || interfaceClassLoadingClass == null) { + className = internalClassName; + } else { + className = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass); + } + return className; + } + + public ClassNode getOutermostClass() { + if (outermostClass == null) { + outermostClass = classNode; + while (outermostClass instanceof InnerClassNode) { + outermostClass = outermostClass.getOuterClass(); + } + } + return outermostClass; + } + + public GeneratorContext getContext() { + return context; + } + + public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) { + interfaceClassLoadingClass = ihc; + } + + public InterfaceHelperClassNode getInterfaceClassLoadingClass() { + return interfaceClassLoadingClass; + } + + public boolean shouldOptimizeForInt() { + return optimizeForInt; + } + + public StatementWriter getStatementWriter() { + return statementWriter; + } + + public void switchToFastPath() { + fastPath = true; + resetLineNumber(); + } + + public void switchToSlowPath() { + fastPath = false; + resetLineNumber(); + } + + public boolean isFastPath() { + return fastPath; + } + + public int getBytecodeVersion() { + return bytecodeVersion; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int n) { + lineNumber = n; + } + + public void resetLineNumber() { + setLineNumber(-1); + } + + public int getNextHelperMethodIndex() { + return helperMethodIndex++; + } + + public List getSuperMethodNames() { + return superMethodNames; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java new file mode 100644 index 0000000000..fa67bfaae2 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java @@ -0,0 +1,136 @@ +/* + * 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 org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ExpressionTransformer; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.classgen.AsmClassGenerator; +import org.codehaus.groovy.transform.sc.ListOfExpressionsExpression; +import org.codehaus.groovy.transform.sc.TemporaryVariableExpression; + +import java.util.Arrays; + +/** + * Contains helper methods aimed at facilitating the generation of statically compiled bytecode for property access. + * + * @author Cédric Champeau + * @since 2.4.0 + */ +public abstract class StaticPropertyAccessHelper { + public static Expression transformToSetterCall( + Expression receiver, + MethodNode setterMethod, + final Expression arguments, + boolean implicitThis, + boolean safe, + boolean spreadSafe, + boolean requiresReturnValue, + Expression location) { + if (requiresReturnValue) { + TemporaryVariableExpression tmp = new TemporaryVariableExpression(arguments); + PoppingMethodCallExpression call = new PoppingMethodCallExpression(receiver, setterMethod, tmp); + call.setImplicitThis(implicitThis); + call.setSafe(safe); + call.setSpreadSafe(spreadSafe); + call.setSourcePosition(location); + PoppingListOfExpressionsExpression result = new PoppingListOfExpressionsExpression(tmp, call); + result.setSourcePosition(location); + return result; + } else { + MethodCallExpression call = new MethodCallExpression( + receiver, + setterMethod.getName(), + arguments + ); + call.setImplicitThis(implicitThis); + call.setSafe(safe); + call.setSpreadSafe(spreadSafe); + call.setMethodTarget(setterMethod); + call.setSourcePosition(location); + return call; + } + } + + private static class PoppingListOfExpressionsExpression extends ListOfExpressionsExpression { + private final TemporaryVariableExpression tmp; + private final PoppingMethodCallExpression call; + + public PoppingListOfExpressionsExpression(final TemporaryVariableExpression tmp, final PoppingMethodCallExpression call) { + super(Arrays.asList( + tmp, + call + )); + this.tmp = tmp; + this.call = call; + } + + @Override + public Expression transformExpression(final ExpressionTransformer transformer) { + PoppingMethodCallExpression tcall = (PoppingMethodCallExpression) call.transformExpression(transformer); + return new PoppingListOfExpressionsExpression(tcall.tmp, tcall); + } + + @Override + public void visit(final GroovyCodeVisitor visitor) { + super.visit(visitor); + if (visitor instanceof AsmClassGenerator) { + tmp.remove(((AsmClassGenerator) visitor).getController()); + } + } + } + + private static class PoppingMethodCallExpression extends MethodCallExpression { + private final Expression receiver; + private final MethodNode setter; + private final TemporaryVariableExpression tmp; + + public PoppingMethodCallExpression(final Expression receiver, final MethodNode setterMethod, final TemporaryVariableExpression tmp) { + super(receiver, setterMethod.getName(), tmp); + this.receiver = receiver; + this.setter = setterMethod; + this.tmp = tmp; + setMethodTarget(setterMethod); + } + + @Override + public Expression transformExpression(final ExpressionTransformer transformer) { + PoppingMethodCallExpression trn = new PoppingMethodCallExpression(receiver.transformExpression(transformer), setter, (TemporaryVariableExpression) tmp.transformExpression(transformer)); + trn.copyNodeMetaData(this); + // GRECLIPSE add -- GROOVY-8002 + trn.setSourcePosition(this); + // GRECLIPSE end + trn.setImplicitThis(isImplicitThis()); + trn.setSafe(isSafe()); + trn.setSpreadSafe(isSpreadSafe()); + return trn; + } + + @Override + public void visit(final GroovyCodeVisitor visitor) { + super.visit(visitor); + if (visitor instanceof AsmClassGenerator) { + // ignore the return of the call + ((AsmClassGenerator) visitor).getController().getOperandStack().pop(); + } + } + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/CompilationUnit.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/CompilationUnit.java new file mode 100644 index 0000000000..a65f23febf --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/CompilationUnit.java @@ -0,0 +1,1298 @@ +/* + * 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.control; + +import groovy.lang.GroovyClassLoader; +import groovy.lang.GroovyRuntimeException; +import groovy.transform.CompilationUnitAware; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.CompileUnit; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.classgen.AsmClassGenerator; +import org.codehaus.groovy.classgen.ClassCompletionVerifier; +import org.codehaus.groovy.classgen.EnumCompletionVisitor; +import org.codehaus.groovy.classgen.EnumVisitor; +import org.codehaus.groovy.classgen.ExtendedVerifier; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.classgen.InnerClassCompletionVisitor; +import org.codehaus.groovy.classgen.InnerClassVisitor; +import org.codehaus.groovy.classgen.VariableScopeVisitor; +import org.codehaus.groovy.classgen.Verifier; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; +import org.codehaus.groovy.control.io.InputStreamReaderSource; +import org.codehaus.groovy.control.io.ReaderSource; +import org.codehaus.groovy.control.messages.ExceptionMessage; +import org.codehaus.groovy.control.messages.Message; +import org.codehaus.groovy.control.messages.SimpleMessage; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.tools.GroovyClass; +import org.codehaus.groovy.transform.ASTTransformationVisitor; +import org.codehaus.groovy.transform.AnnotationCollectorTransform; +import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys; +import org.codehaus.groovy.transform.trait.TraitComposer; +import groovyjarjarasm.asm.ClassVisitor; +import groovyjarjarasm.asm.ClassWriter; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * The CompilationUnit collects all compilation data as it is generated by the compiler system. + * You can use this object to add additional source units to the compilation, or force the + * compilation to be run again (to affect only the deltas). + *

+ * You can also add PhaseOperations to this compilation using the addPhaseOperation method. + * This is commonly used when you want to wire a new AST Transformation into the compilation. + * + * @author Chris Poirier + * @author Jochen Theodorou + * @author Roshan Dawrani + */ + +public class CompilationUnit extends ProcessingUnit { + + //--------------------------------------------------------------------------- + // CONSTRUCTION AND SUCH + + protected ASTTransformationsContext astTransformationsContext; // AST transformations state data + + protected Map sources; // The SourceUnits from which this unit is built + protected Map summariesBySourceName; // Summary of each SourceUnit + protected Map summariesByPublicClassName; // Summary of each SourceUnit + protected Map classSourcesByPublicClassName; // Summary of each Class + protected List names; // Names for each SourceUnit in sources. + protected LinkedList queuedSources; + + protected CompileUnit ast; // The overall AST for this CompilationUnit. + protected List generatedClasses; // The classes generated during classgen. + + protected Verifier verifier; // For use by verify(). + + protected boolean debug; // Controls behavior of classgen() and other routines. + protected boolean configured; // Set true after the first configure() operation + + protected ClassgenCallback classgenCallback; // A callback for use during classgen() + protected ProgressCallback progressCallback; // A callback for use during compile() + protected ResolveVisitor resolveVisitor; + protected StaticImportVisitor staticImportVisitor; + protected OptimizerVisitor optimizer; + protected ClassNodeResolver classNodeResolver; + + LinkedList[] phaseOperations; + LinkedList[] newPhaseOperations; + + /** + * Initializes the CompilationUnit with defaults. + */ + public CompilationUnit() { + this(null, null, null); + } + + + /** + * Initializes the CompilationUnit with defaults except for class loader. + */ + public CompilationUnit(GroovyClassLoader loader) { + this(null, null, loader); + } + + + /** + * Initializes the CompilationUnit with no security considerations. + */ + public CompilationUnit(CompilerConfiguration configuration) { + this(configuration, null, null); + } + + /** + * Initializes the CompilationUnit with a CodeSource for controlling + * security stuff and a class loader for loading classes. + */ + public CompilationUnit(CompilerConfiguration configuration, CodeSource security, GroovyClassLoader loader) { + // GRECLIPSE added final three params + this(configuration, security, loader, null, true, null, null); + } + + /** + * Initializes the CompilationUnit with a CodeSource for controlling + * security stuff, a class loader for loading classes, and a class + * loader for loading AST transformations. + * Note The transform loader must be + * able to load compiler classes. That means CompilationUnit.class.classLoader + * must be at last a parent to transformLoader. The other loader has no such constraint. + * + * @param transformLoader - the loader for transforms + * @param loader - loader used to resolve classes against during compilation + * @param security - security setting for the compilation + * @param configuration - compilation configuration + */ + // GRECLIPSE added final three params + public CompilationUnit(CompilerConfiguration configuration, CodeSource security, + GroovyClassLoader loader, GroovyClassLoader transformLoader, + boolean allowTransforms, String localTransformsToRunOnReconcile, String excludeGlobalASTScan) { + super(configuration, loader, null); + + this.astTransformationsContext = new ASTTransformationsContext(this, transformLoader); + this.names = new ArrayList(); + this.queuedSources = new LinkedList(); + this.sources = new HashMap(); + this.summariesBySourceName = new HashMap(); + this.summariesByPublicClassName = new HashMap(); + this.classSourcesByPublicClassName = new HashMap(); + + this.ast = new CompileUnit(this.classLoader, security, this.configuration); + this.generatedClasses = new ArrayList(); + + this.verifier = new Verifier(); + this.resolveVisitor = new ResolveVisitor(this); + this.staticImportVisitor = new StaticImportVisitor(); + this.optimizer = new OptimizerVisitor(this); + // GRECLIPSE add + this.allowTransforms = allowTransforms; + this.excludeGlobalASTScan = excludeGlobalASTScan; + this.localTransformsToRunOnReconcile = new ArrayList<>(); + if (localTransformsToRunOnReconcile == null) { + this.localTransformsToRunOnReconcile.add("*"); + } else { + String[] tokens = localTransformsToRunOnReconcile.split(","); + Collections.addAll(this.localTransformsToRunOnReconcile, tokens); + } + // GRECLIPSE end + + phaseOperations = new LinkedList[Phases.ALL + 1]; + newPhaseOperations = new LinkedList[Phases.ALL + 1]; + for (int i = 0; i < phaseOperations.length; i++) { + phaseOperations[i] = new LinkedList(); + newPhaseOperations[i] = new LinkedList(); + } + addPhaseOperation(new SourceUnitOperation() { + public void call(SourceUnit source) throws CompilationFailedException { + source.parse(); + } + }, Phases.PARSING); + addPhaseOperation(convert, Phases.CONVERSION); + addPhaseOperation(new PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + EnumVisitor ev = new EnumVisitor(CompilationUnit.this, source); + ev.visitClass(classNode); + } + }, Phases.CONVERSION); + addPhaseOperation(resolve, Phases.SEMANTIC_ANALYSIS); + addPhaseOperation(staticImport, Phases.SEMANTIC_ANALYSIS); + addPhaseOperation(new PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + InnerClassVisitor iv = new InnerClassVisitor(CompilationUnit.this, source); + iv.visitClass(classNode); + } + }, Phases.SEMANTIC_ANALYSIS); + addPhaseOperation(new PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + if (!classNode.isSynthetic()) { + GenericsVisitor genericsVisitor = new GenericsVisitor(source); + genericsVisitor.visitClass(classNode); + } + } + }, Phases.SEMANTIC_ANALYSIS); + addPhaseOperation(new PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + TraitComposer.doExtendTraits(classNode, source, CompilationUnit.this); + } + }, Phases.CANONICALIZATION); + addPhaseOperation(compileCompleteCheck, Phases.CANONICALIZATION); + addPhaseOperation(classgen, Phases.CLASS_GENERATION); + // GRECLIPSE edit -- skip output phase + //addPhaseOperation(output); + + addPhaseOperation(new PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + AnnotationCollectorTransform.ClassChanger actt = new AnnotationCollectorTransform.ClassChanger(); + actt.transformClass(classNode); + } + }, Phases.SEMANTIC_ANALYSIS); + ASTTransformationVisitor.addPhaseOperations(this); + addPhaseOperation(new PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + StaticVerifier sv = new StaticVerifier(); + sv.visitClass(classNode, source); + } + }, Phases.SEMANTIC_ANALYSIS); + addPhaseOperation(new PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + InnerClassCompletionVisitor iv = new InnerClassCompletionVisitor(CompilationUnit.this, source); + iv.visitClass(classNode); + } + }, Phases.CANONICALIZATION); + addPhaseOperation(new PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + EnumCompletionVisitor ecv = new EnumCompletionVisitor(CompilationUnit.this, source); + ecv.visitClass(classNode); + } + }, Phases.CANONICALIZATION); + addPhaseOperation(new PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, + ClassNode classNode) throws CompilationFailedException { + Object callback = classNode.getNodeMetaData(StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK); + if (callback instanceof PrimaryClassNodeOperation) { + ((PrimaryClassNodeOperation) callback).call(source, context, classNode); + classNode.removeNodeMetaData(StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK); + } + } + }, Phases.INSTRUCTION_SELECTION); + + // apply configuration customizers if any + if (configuration != null) { + final List customizers = configuration.getCompilationCustomizers(); + for (CompilationCustomizer customizer : customizers) { + if (customizer instanceof CompilationUnitAware) { + ((CompilationUnitAware) customizer).setCompilationUnit(this); + } + addPhaseOperation(customizer, customizer.getPhase().getPhaseNumber()); + } + } + this.classgenCallback = null; + this.classNodeResolver = new ClassNodeResolver(); + } + + /** + * Returns the class loader for loading AST transformations. + * @return - the transform class loader + */ + public GroovyClassLoader getTransformLoader() { + return astTransformationsContext.getTransformLoader() == null ? getClassLoader() : astTransformationsContext.getTransformLoader(); + } + + + public void addPhaseOperation(SourceUnitOperation op, int phase) { + if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown"); + phaseOperations[phase].add(op); + } + + public void addPhaseOperation(PrimaryClassNodeOperation op, int phase) { + if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown"); + phaseOperations[phase].add(op); + } + + public void addFirstPhaseOperation(PrimaryClassNodeOperation op, int phase) { + if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown"); + phaseOperations[phase].add(0, op); + } + + public void addPhaseOperation(GroovyClassOperation op) { + phaseOperations[Phases.OUTPUT].addFirst(op); + } + + public void addNewPhaseOperation(SourceUnitOperation op, int phase) { + if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown"); + newPhaseOperations[phase].add(op); + } + + /** + * Configures its debugging mode and classloader classpath from a given compiler configuration. + * This cannot be done more than once due to limitations in {@link java.net.URLClassLoader URLClassLoader}. + */ + public void configure(CompilerConfiguration configuration) { + super.configure(configuration); + this.debug = configuration.getDebug(); + + // GRECLIPSE edit + if (!this.configured && this.classLoader != null/*instanceof GroovyClassLoader*/) { + appendCompilerConfigurationClasspathToClassLoader(configuration, /*(GroovyClassLoader)*/ this.classLoader); + } + // GRECLIPSE end + + this.configured = true; + } + + private void appendCompilerConfigurationClasspathToClassLoader(CompilerConfiguration configuration, GroovyClassLoader classLoader) { + /*for (Iterator iterator = configuration.getClasspath().iterator(); iterator.hasNext(); ) { + classLoader.addClasspath((String) iterator.next()); + }*/ + } + + /** + * Returns the CompileUnit that roots our AST. + */ + public CompileUnit getAST() { + return this.ast; + } + + /** + * Get the source summaries + */ + public Map getSummariesBySourceName() { + return summariesBySourceName; + } + + public Map getSummariesByPublicClassName() { + return summariesByPublicClassName; + } + + public Map getClassSourcesByPublicClassName() { + return classSourcesByPublicClassName; + } + + public boolean isPublicClass(String className) { + return summariesByPublicClassName.containsKey(className); + } + + /** + * Get the GroovyClasses generated by compile(). + */ + public List getClasses() { + return generatedClasses; + } + + /** + * Convenience routine to get the first ClassNode, for + * when you are sure there is only one. + */ + public ClassNode getFirstClassNode() { + return this.ast.getModules().get(0).getClasses().get(0); + } + + /** + * Convenience routine to get the named ClassNode. + */ + public ClassNode getClassNode(final String name) { + final ClassNode[] result = new ClassNode[]{null}; + PrimaryClassNodeOperation handler = new PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) { + if (classNode.getName().equals(name)) { + result[0] = classNode; + } + } + }; + + try { + applyToPrimaryClassNodes(handler); + } catch (CompilationFailedException e) { + if (debug) e.printStackTrace(); + } + return result[0]; + } + + /** + * @return the AST transformations current context + */ + public ASTTransformationsContext getASTTransformationsContext() { + return astTransformationsContext; + } + + //--------------------------------------------------------------------------- + // SOURCE CREATION + + + /** + * Adds a set of file paths to the unit. + */ + public void addSources(String[] paths) { + for (String path : paths) { + addSource(new File(path)); + } + } + + + /** + * Adds a set of source files to the unit. + */ + public void addSources(File[] files) { + for (File file : files) { + addSource(file); + } + } + + + /** + * Adds a source file to the unit. + */ + public SourceUnit addSource(File file) { + return addSource(new SourceUnit(file, configuration, classLoader, getErrorCollector())); + } + + /** + * Adds a source file to the unit. + */ + public SourceUnit addSource(URL url) { + return addSource(new SourceUnit(url, configuration, classLoader, getErrorCollector())); + } + + + /** + * Adds a InputStream source to the unit. + */ + public SourceUnit addSource(String name, InputStream stream) { + ReaderSource source = new InputStreamReaderSource(stream, configuration); + return addSource(new SourceUnit(name, source, configuration, classLoader, getErrorCollector())); + } + + public SourceUnit addSource(String name, String scriptText) { + return addSource(new SourceUnit(name, scriptText, configuration, classLoader, getErrorCollector())); + } + + /** + * Adds a SourceUnit to the unit. + */ + public SourceUnit addSource(SourceUnit source) { + String name = source.getName(); + source.setClassLoader(this.classLoader); + for (SourceUnit su : queuedSources) { + if (name.equals(su.getName())) return su; + } + queuedSources.add(source); + return source; + } + + + /** + * Returns an iterator on the unit's SourceUnits. + */ + public Iterator iterator() { + return new Iterator() { + // GRECLIPSE: Should this use an index instead of names.iterator() to prevent/reduce ConMod exceptions? + Iterator nameIterator = names.iterator(); + + public boolean hasNext() { + return nameIterator.hasNext(); + } + + public SourceUnit next() { + String name = nameIterator.next(); + return sources.get(name); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + + /** + * Adds a ClassNode directly to the unit (ie. without source). + * WARNING: the source is needed for error reporting, using + * this method without setting a SourceUnit will cause + * NullPinterExceptions + */ + public void addClassNode(ClassNode node) { + ModuleNode module = new ModuleNode(this.ast); + this.ast.addModule(module); + module.addClass(node); + } + + //--------------------------------------------------------------------------- + // EXTERNAL CALLBACKS + + + /** + * A callback interface you can use to "accompany" the classgen() + * code as it traverses the ClassNode tree. You will be called-back + * for each primary and inner class. Use setClassgenCallback() before + * running compile() to set your callback. + */ + public abstract static class ClassgenCallback { + public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException; + } + + /** + * Sets a ClassgenCallback. You can have only one, and setting + * it to null removes any existing setting. + */ + public void setClassgenCallback(ClassgenCallback visitor) { + this.classgenCallback = visitor; + } + + /** + * A callback interface you can use to get a callback after every + * unit of the compile process. You will be called-back with a + * ProcessingUnit and a phase indicator. Use setProgressCallback() + * before running compile() to set your callback. + */ + public abstract static class ProgressCallback { + + public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException; + } + + /** + * Sets a ProgressCallback. You can have only one, and setting + * it to null removes any existing setting. + */ + public void setProgressCallback(ProgressCallback callback) { + this.progressCallback = callback; + } + + public ClassgenCallback getClassgenCallback() { + return classgenCallback; + } + + public ProgressCallback getProgressCallback() { + return progressCallback; + } + + //--------------------------------------------------------------------------- + // ACTIONS + + + /** + * Synonym for compile(Phases.ALL). + */ + public void compile() throws CompilationFailedException { + compile(Phases.ALL); + } + + /** + * Compiles the compilation unit from sources. + */ + public void compile(int throughPhase) throws CompilationFailedException { + // + // To support delta compilations, we always restart + // the compiler. The individual passes are responsible + // for not reprocessing old code. + gotoPhase(Phases.INITIALIZATION); + throughPhase = Math.min(throughPhase, Phases.ALL); + + while (throughPhase >= phase && phase <= Phases.ALL) { + + if (phase == Phases.SEMANTIC_ANALYSIS) { + doPhaseOperation(resolve); + if (dequeued()) continue; + } + + processPhaseOperations(phase); + // Grab processing may have brought in new AST transforms into various phases, process them as well + processNewPhaseOperations(phase); + + if (progressCallback != null) progressCallback.call(this, phase); + completePhase(); + applyToSourceUnits(mark); + + if (dequeued()) continue; + + gotoPhase(phase + 1); + + if (phase == Phases.CLASS_GENERATION) { + sortClasses(); + } + } + + errorCollector.failIfErrors(); + } + + private void processPhaseOperations(int ph) { + LinkedList ops = phaseOperations[ph]; + for (Object next : ops) { + doPhaseOperation(next); + } + } + + private void processNewPhaseOperations(int currPhase) { + recordPhaseOpsInAllOtherPhases(currPhase); + LinkedList currentPhaseNewOps = newPhaseOperations[currPhase]; + while (!currentPhaseNewOps.isEmpty()) { + Object operation = currentPhaseNewOps.removeFirst(); + // push this operation to master list and then process it. + phaseOperations[currPhase].add(operation); + doPhaseOperation(operation); + // if this operation has brought in more phase ops for ast transforms, keep recording them + // in master list of other phases and keep processing them for this phase. + recordPhaseOpsInAllOtherPhases(currPhase); + currentPhaseNewOps = newPhaseOperations[currPhase]; + } + + } + + private void doPhaseOperation(Object operation) { + if (operation instanceof PrimaryClassNodeOperation) { + applyToPrimaryClassNodes((PrimaryClassNodeOperation) operation); + } else if (operation instanceof SourceUnitOperation) { + applyToSourceUnits((SourceUnitOperation) operation); + } else { + applyToGeneratedGroovyClasses((GroovyClassOperation) operation); + } + } + + private void recordPhaseOpsInAllOtherPhases(int currPhase) { + // apart from current phase, push new operations for every other phase in the master phase ops list + for (int ph = Phases.INITIALIZATION; ph <= Phases.ALL; ph++) { + if (ph != currPhase && !newPhaseOperations[ph].isEmpty()) { + phaseOperations[ph].addAll(newPhaseOperations[ph]); + newPhaseOperations[ph].clear(); + } + } + } + + private void sortClasses() throws CompilationFailedException { + for (ModuleNode module : this.ast.getModules()) { + module.sortClasses(); + } + } + + + /** + * Dequeues any source units add through addSource and resets the compiler phase + * to initialization. + *

+ * Note: this does not mean a file is recompiled. If a SourceUnit has already passed + * a phase it is skipped until a higher phase is reached. + * + * @return true if there was a queued source + * @throws CompilationFailedException + */ + protected boolean dequeued() throws CompilationFailedException { + boolean dequeue = !queuedSources.isEmpty(); + while (!queuedSources.isEmpty()) { + SourceUnit su = queuedSources.removeFirst(); + String name = su.getName(); + names.add(name); + sources.put(name, su); + } + if (dequeue) { + gotoPhase(Phases.INITIALIZATION); + } + return dequeue; + } + + /** + * Resolves all types + */ + private final SourceUnitOperation resolve = new SourceUnitOperation() { + public void call(SourceUnit source) throws CompilationFailedException { + List classes = source.ast.getClasses(); + for (ClassNode node : classes) { + VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source); + scopeVisitor.visitClass(node); + + resolveVisitor.setClassNodeResolver(classNodeResolver); + resolveVisitor.startResolving(node, source); + } + + } + }; + + private final PrimaryClassNodeOperation staticImport = new PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { + staticImportVisitor.visitClass(classNode, source); + } + }; + + /** + * Runs convert() on a single SourceUnit. + */ + private final SourceUnitOperation convert = new SourceUnitOperation() { + public void call(SourceUnit source) throws CompilationFailedException { + source.convert(); + CompilationUnit.this.ast.addModule(source.getAST()); + + + if (CompilationUnit.this.progressCallback != null) { + CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase); + } + } + }; + + private final GroovyClassOperation output = new GroovyClassOperation() { + public void call(GroovyClass gclass) throws CompilationFailedException { + String name = gclass.getName().replace('.', File.separatorChar) + ".class"; + File path = new File(configuration.getTargetDirectory(), name); + + // + // Ensure the path is ready for the file + // + File directory = path.getParentFile(); + if (directory != null && !directory.exists()) { + directory.mkdirs(); + } + + // + // Create the file and write out the data + // + byte[] bytes = gclass.getBytes(); + + FileOutputStream stream = null; + try { + stream = new FileOutputStream(path); + stream.write(bytes, 0, bytes.length); + } catch (IOException e) { + getErrorCollector().addError(Message.create(e.getMessage(), CompilationUnit.this)); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Exception e) { + // Ignore + } + } + } + } + }; + + /* checks if all needed classes are compiled before generating the bytecode */ + private final SourceUnitOperation compileCompleteCheck = new SourceUnitOperation() { + public void call(SourceUnit source) throws CompilationFailedException { + List classes = source.ast.getClasses(); + for (ClassNode node : classes) { + CompileUnit cu = node.getCompileUnit(); + for (Iterator iter = cu.iterateClassNodeToCompile(); iter.hasNext();) { + String name = (String) iter.next(); + SourceUnit su = ast.getScriptSourceLocation(name); + List classesInSourceUnit = su.ast.getClasses(); + StringBuilder message = new StringBuilder(); + message + .append("Compilation incomplete: expected to find the class ") + .append(name) + .append(" in ") + .append(su.getName()); + if (classesInSourceUnit.isEmpty()) { + message.append(", but the file seems not to contain any classes"); + } else { + message.append(", but the file contains the classes: "); + boolean first = true; + for (ClassNode cn : classesInSourceUnit) { + if (!first) { + message.append(", "); + } else { + first = false; + } + message.append(cn.getName()); + } + } + + getErrorCollector().addErrorAndContinue( + new SimpleMessage(message.toString(), CompilationUnit.this) + ); + iter.remove(); + } + } + } + }; + + + /** + * Runs classgen() on a single ClassNode. + */ + private final PrimaryClassNodeOperation classgen = new PrimaryClassNodeOperation() { + public boolean needSortedInput() { + return true; + } + + public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { + + optimizer.visitClass(classNode, source); // GROOVY-4272: repositioned it here from staticImport + + // + // Run the Verifier on the outer class + // + try { + verifier.visitClass(classNode); + } catch (GroovyRuntimeException rpe) { + ASTNode node = rpe.getNode(); + getErrorCollector().addError( + new SyntaxException(rpe.getMessage(), node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()), + source + ); + } + + LabelVerifier lv = new LabelVerifier(source); + lv.visitClass(classNode); + + ClassCompletionVerifier completionVerifier = new ClassCompletionVerifier(source); + completionVerifier.visitClass(classNode); + + ExtendedVerifier xverifier = new ExtendedVerifier(source); + xverifier.visitClass(classNode); + + // because the class may be generated even if a error was found + // and that class may have an invalid format we fail here if needed + getErrorCollector().failIfErrors(); + + // + // Prep the generator machinery + // + ClassVisitor visitor = createClassVisitor(); + + String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName()); + // only show the file name and its extension like javac does in its stacktraces rather than the full path + // also takes care of both \ and / depending on the host compiling environment + if (sourceName != null) + sourceName = sourceName.substring(Math.max(sourceName.lastIndexOf('\\'), sourceName.lastIndexOf('/')) + 1); + AsmClassGenerator generator = new AsmClassGenerator(source, context, visitor, sourceName); + + // GRECLIPSE add -- if there are errors, don't generate code + // code gen can fail unexpectedly if there was an earlier error + if (source != null && source.getErrorCollector().hasErrors()) return; + // GRECLIPSE end + + // + // Run the generation and create the class (if required) + // + generator.visitClass(classNode); + + byte[] bytes = ((ClassWriter) visitor).toByteArray(); + // GRECLIPSE added classNode, source + generatedClasses.add(new GroovyClass(classNode.getName(), bytes, classNode, source)); + + // + // Handle any callback that's been set + // + if (CompilationUnit.this.classgenCallback != null) { + classgenCallback.call(visitor, classNode); + } + + // + // Recurse for inner classes + // + LinkedList innerClasses = generator.getInnerClasses(); + while (!innerClasses.isEmpty()) { + classgen.call(source, context, (ClassNode) innerClasses.removeFirst()); + } + } + }; + + protected ClassVisitor createClassVisitor() { + CompilerConfiguration config = getConfiguration(); + int computeMaxStackAndFrames = ClassWriter.COMPUTE_MAXS; + if (CompilerConfiguration.isPostJDK7(config.getTargetBytecode()) + || Boolean.TRUE.equals(config.getOptimizationOptions().get("indy"))) { + computeMaxStackAndFrames += ClassWriter.COMPUTE_FRAMES; + } + return new ClassWriter(computeMaxStackAndFrames) { + private ClassNode getClassNode(String name) { + // try classes under compilation + CompileUnit cu = getAST(); + ClassNode cn = cu.getClass(name); + if (cn!=null) return cn; + // try inner classes + cn = cu.getGeneratedInnerClass(name); + if (cn!=null) return cn; + // try class loader classes + try { + cn = ClassHelper.make( + cu.getClassLoader().loadClass(name,false,true), + false); + } catch (Exception e) { + throw new GroovyBugError(e); + } + return cn; + } + private ClassNode getCommonSuperClassNode(ClassNode c, ClassNode d) { + // adapted from ClassWriter code + if (c.isDerivedFrom(d)) return d; + if (d.isDerivedFrom(c)) return c; + if (c.isInterface() || d.isInterface()) return ClassHelper.OBJECT_TYPE; + do { + c = c.getSuperClass(); + } while (c!=null && !d.isDerivedFrom(c)); + if (c==null) return ClassHelper.OBJECT_TYPE; + return c; + } + @Override + protected String getCommonSuperClass(String arg1, String arg2) { + ClassNode a = getClassNode(arg1.replace('/', '.')); + ClassNode b = getClassNode(arg2.replace('/', '.')); + return getCommonSuperClassNode(a,b).getName().replace('.','/'); + } + + }; + } + + //--------------------------------------------------------------------------- + // PHASE HANDLING + + /** + * Updates the phase marker on all sources. + */ + protected void mark() throws CompilationFailedException { + applyToSourceUnits(mark); + } + + + /** + * Marks a single SourceUnit with the current phase, + * if it isn't already there yet. + */ + private final SourceUnitOperation mark = new SourceUnitOperation() { + public void call(SourceUnit source) throws CompilationFailedException { + if (source.phase < phase) { + source.gotoPhase(phase); + } + + if (source.phase == phase && phaseComplete && !source.phaseComplete) { + source.completePhase(); + } + } + }; + + //--------------------------------------------------------------------------- + // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS + + + /** + * An callback interface for use in the applyToSourceUnits loop driver. + */ + public abstract static class SourceUnitOperation { + public abstract void call(SourceUnit source) throws CompilationFailedException; + } + + /** + * A loop driver for applying operations to all SourceUnits. + * Automatically skips units that have already been processed + * through the current phase. + */ + public void applyToSourceUnits(SourceUnitOperation body) throws CompilationFailedException { + // GRECLIPSE edit -- prevent concurrent modification exceptions + for (String name : names.toArray(new String[names.size()])) { + SourceUnit source = sources.get(name); + if ((source.phase < phase) || (source.phase == phase && !source.phaseComplete)) { + try { + body.call(source); + // GRECLIPSE add + if (phase == Phases.CONVERSION && getProgressListener() != null && body == phaseOperations[phase].getLast()) { + getProgressListener().parseComplete(phase, name); + } + // GRECLIPSE end + } catch (CompilationFailedException e) { + throw e; + } catch (Exception e) { + GroovyBugError gbe = new GroovyBugError(e); + changeBugText(gbe, source); + throw gbe; + } catch (GroovyBugError e) { + changeBugText(e, source); + throw e; + } + } + } + + getErrorCollector().failIfErrors(); + } + + //--------------------------------------------------------------------------- + // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS + + + /** + * An callback interface for use in the applyToPrimaryClassNodes loop driver. + */ + public abstract static class PrimaryClassNodeOperation { + public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException; + + public boolean needSortedInput() { + return false; + } + } + + public abstract static class GroovyClassOperation { + public abstract void call(GroovyClass gclass) throws CompilationFailedException; + } + + private static int getSuperClassCount(ClassNode element) { + int count = 0; + while (element != null) { + count++; + element = element.getSuperClass(); + } + return count; + } + + private int getSuperInterfaceCount(ClassNode element) { + int count = 1; + ClassNode[] interfaces = element.getInterfaces(); + for (ClassNode anInterface : interfaces) { + count = Math.max(count, getSuperInterfaceCount(anInterface) + 1); + } + return count; + } + + private List getPrimaryClassNodes(boolean sort) { + // GRECLIPSE add + if (sort) { + List sorted = this.ast.getSortedClasses(); + if (sorted != null) return sorted; + } + // GRECLIPSE end + List unsorted = new ArrayList(); + for (ModuleNode module : this.ast.getModules()) { + unsorted.addAll(module.getClasses()); + } + + if (!sort) return unsorted; + + int unsortedSize = unsorted.size(); + // GRECLIPSE edit + /*int[] indexClass = new int[unsortedSize]; + int[] indexInterface = new int[unsortedSize]; + { + int i = 0; + for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) { + ClassNode element = iter.next(); + if (element.isInterface()) { + indexInterface[i] = getSuperInterfaceCount(element); + indexClass[i] = -1; + } else { + indexClass[i] = getSuperClassCount(element); + indexInterface[i] = -1; + } + } + } + + List sorted = getSorted(indexInterface, unsorted); + sorted.addAll(getSorted(indexClass, unsorted)); + */ + // Sort them by how many types are in their hierarchy, but all interfaces first. + // Algorithm: + // Create a list of integers. Each integer captures the index into the unsorted + // list (bottom 16 bits) and the count of how many types are in that types + // hierarchy (top 16 bits). For classes the count is augmented so that when + // sorting the classes will come out after the interfaces. This list of integers + // is sorted. We then just go through it and for the lower 16 bits of each entry + // that is the index of the next value to pull from the unsorted list and put into + // the sorted list. + int[] countIndexPairs = new int[unsortedSize]; + int count, index = 0; + for (ClassNode node : unsorted) { + if (node.isInterface()) { + count = getSuperInterfaceCount(node); + } else { + count = getSuperClassCount(node) + 5000; + } + countIndexPairs[index] = ((count << 16) + index); + index += 1; + } + Arrays.sort(countIndexPairs); + + List sorted = new ArrayList<>(index); + for (int i : countIndexPairs) { + sorted.add(unsorted.get(i & 0xFFFF)); + } + this.ast.setSortedClasses(sorted); + // GRECLIPSE end + return sorted; + } + + /*private static List getSorted(int[] index, List unsorted) { + int unsortedSize = unsorted.size(); + List sorted = new ArrayList(unsortedSize); + for (int i = 0; i < unsortedSize; i++) { + int min = -1; + for (int j = 0; j < unsortedSize; j++) { + if (index[j] == -1) continue; + if (min == -1 || index[j] < index[min]) { + min = j; + } + } + if (min == -1) break; + sorted.add(unsorted.get(min)); + index[min] = -1; + } + return sorted; + }*/ + + /** + * A loop driver for applying operations to all primary ClassNodes in + * our AST. Automatically skips units that have already been processed + * through the current phase. + */ + public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body) throws CompilationFailedException { + for (ClassNode classNode : getPrimaryClassNodes(body.needSortedInput())) { + SourceUnit context = null; + try { + context = classNode.getModule().getContext(); + if (context == null || context.phase < phase || (context.phase == phase && !context.phaseComplete)) { + int offset = 1; + for (Iterator iterator = classNode.getInnerClasses(); iterator.hasNext(); ) { + iterator.next(); + offset++; + } + body.call(context, new GeneratorContext(this.ast, offset), classNode); + } + } catch (CompilationFailedException e) { + // fall through, getErrorReporter().failIfErrors() will trigger + } catch (NullPointerException npe) { + GroovyBugError gbe = new GroovyBugError("unexpected NullpointerException", npe); + changeBugText(gbe, context); + throw gbe; + } catch (GroovyBugError e) { + changeBugText(e, context); + throw e; + } catch (NoClassDefFoundError e) { + // effort to get more logging in case a dependency of a class is loaded + // although it shouldn't have + convertUncaughtExceptionToCompilationError(e); + } catch (Exception e) { + convertUncaughtExceptionToCompilationError(e); + } + } + + getErrorCollector().failIfErrors(); + } + + private void convertUncaughtExceptionToCompilationError(final Throwable e) { + // check the exception for a nested compilation exception + ErrorCollector nestedCollector = null; + for (Throwable next = e.getCause(); next != e && next != null; next = next.getCause()) { + if (!(next instanceof MultipleCompilationErrorsException)) continue; + MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next; + nestedCollector = mcee.collector; + break; + } + + if (nestedCollector != null) { + getErrorCollector().addCollectorContents(nestedCollector); + } else { + Exception err = e instanceof Exception?((Exception)e):new RuntimeException(e); + getErrorCollector().addError(new ExceptionMessage(err, configuration.getDebug(), this)); + } + } + + public void applyToGeneratedGroovyClasses(GroovyClassOperation body) throws CompilationFailedException { + if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) { + throw new GroovyBugError("CompilationUnit not ready for output(). Current phase=" + getPhaseDescription()); + } + + for (GroovyClass gclass : this.generatedClasses) { + // + // Get the class and calculate its filesystem name + // + try { + body.call(gclass); + } catch (CompilationFailedException e) { + // fall through, getErrorReporter().failIfErrors() will trigger + } catch (NullPointerException npe) { + throw npe; + } catch (GroovyBugError e) { + changeBugText(e, null); + throw e; + } catch (Exception e) { + throw new GroovyBugError(e); + } + } + + getErrorCollector().failIfErrors(); + } + + private void changeBugText(GroovyBugError e, SourceUnit context) { + e.setBugText("exception in phase '" + getPhaseDescription() + "' in source unit '" + ((context != null) ? context.getName() : "?") + "' " + e.getBugText()); + } + + public ClassNodeResolver getClassNodeResolver() { + return classNodeResolver; + } + + + public void setClassNodeResolver(ClassNodeResolver classNodeResolver) { + this.classNodeResolver = classNodeResolver; + } + + // GRECLIPSE add + public interface ProgressListener { + void parseComplete(int phase, String sourceUnitName); + void generateComplete(int phase, ClassNode classNode); + } + + public ProgressListener getProgressListener() { + return this.listener; + } + + public void setProgressListener(ProgressListener listener) { + this.listener = listener; + } + + public ResolveVisitor getResolveVisitor() { + return this.resolveVisitor; + } + + public void setResolveVisitor(ResolveVisitor resolveVisitor) { + this.resolveVisitor = resolveVisitor; + } + + public void ensureASTTransformVisitorAdded() { + ASTTransformationVisitor.addPhaseOperations(this); + } + + // can be called to prevent classfile output (so only use if something else is taking charge of output) + public boolean removeOutputPhaseOperation() { + return phaseOperations[Phases.OUTPUT].remove(output); + } + + public String toString() { + if (sources == null || sources.isEmpty()) + return super.toString(); + // TODO: Why is this loop here? + for (String s : sources.keySet()) { + return "CompilationUnit: source is " + s; + } + return "CompilationUnit: null"; + } + + /** + * Slightly modifies the behaviour of the phases based on what the caller really needs. Some invocations of the compilation + * infrastructure don't need the bytecode, so we can skip creating it, they would rather have a more 'source like' AST. + * + * @param isReconcile is this a reconciling compile? + */ + public void tweak(boolean isReconcile) { + if (isReconcile) { + verifier.inlineStaticFieldInitializersIntoClinit = false; + staticImportVisitor.isReconcile = true; + } else { + verifier.inlineStaticFieldInitializersIntoClinit = true; + } + this.isReconcile = isReconcile; + } + + /** + * Path to a directory that should be ignored when searching for manifest files that define global AST transforms. + * See bug https://jira.codehaus.org/browse/GRECLIPSE-1762 + */ + public String excludeGlobalASTScan; + public boolean allowTransforms = true; + public boolean isReconcile = false; + private ProgressListener listener; + public List localTransformsToRunOnReconcile; + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ErrorCollector.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ErrorCollector.java new file mode 100644 index 0000000000..c3fbc2c1ba --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ErrorCollector.java @@ -0,0 +1,351 @@ +/* + * 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.control; + +import org.codehaus.groovy.control.messages.ExceptionMessage; +import org.codehaus.groovy.control.messages.LocatedMessage; +import org.codehaus.groovy.control.messages.Message; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.control.messages.WarningMessage; +import org.codehaus.groovy.syntax.CSTNode; +import org.codehaus.groovy.syntax.SyntaxException; + +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * A base class for collecting messages and errors during processing. + * Each CompilationUnit should have an ErrorCollector, and the SourceUnits + * should share their ErrorCollector with the CompilationUnit. + * + * @author Chris Poirier + * @author Jochen Theodorou + */ +public class ErrorCollector { + // GRECLIPSE add + public boolean transformActive; + // GRECLIPSE end + + /** + * WarningMessages collected during processing + */ + protected LinkedList warnings; + /** + * ErrorMessages collected during processing + */ + protected LinkedList errors; + /** + * Configuration and other settings that control processing + */ + protected CompilerConfiguration configuration; + + /** + * Initialize the ErrorReporter. + */ + public ErrorCollector(CompilerConfiguration configuration) { + this.warnings = null; + this.errors = null; + + this.configuration = configuration; + } + + public void addCollectorContents(ErrorCollector er) { + if (er.errors!=null) { + if (errors==null) { + errors = er.errors; + } else { + errors.addAll(er.errors); + } + } + if (er.warnings!=null) { + if (warnings==null) { + warnings = er.warnings; + } else { + warnings.addAll(er.warnings); + } + } + } + + public void addErrorAndContinue(SyntaxException error, SourceUnit source) throws CompilationFailedException { + addErrorAndContinue(Message.create(error, source)); + } + + /** + * Adds an error to the message set, but does not cause a failure. The message is not required to have a source + * line and column specified, but it is best practice to try and include that information. + */ + public void addErrorAndContinue(Message message) { + if (this.errors == null) { + this.errors = new LinkedList(); + } + + this.errors.add(message); + } + + /** + * Adds a non-fatal error to the message set, which may cause a failure if the error threshold is exceeded. + * The message is not required to have a source line and column specified, but it is best practice to try + * and include that information. + */ + public void addError(Message message) throws CompilationFailedException { + addErrorAndContinue(message); + + if (errors!=null && this.errors.size() >= configuration.getTolerance()) { + failIfErrors(); + } + } + + /** + * Adds an optionally-fatal error to the message set. + * The message is not required to have a source line and column specified, but it is best practice to try + * and include that information. + * @param fatal + * if true then then processing will stop + */ + public void addError(Message message, boolean fatal) throws CompilationFailedException { + if (fatal) { + addFatalError(message); + } + else { + addError(message); + } + } + + + /** + * Convenience wrapper for addError(). + */ + public void addError(SyntaxException error, SourceUnit source) throws CompilationFailedException { + addError(Message.create(error, source), error.isFatal()); + } + + + /** + * Convenience wrapper for addError(). + */ + public void addError(String text, CSTNode context, SourceUnit source) throws CompilationFailedException { + addError(new LocatedMessage(text, context, source)); + } + + + /** + * Adds a fatal exception to the message set and throws + * the unit as a PhaseFailedException. + */ + public void addFatalError(Message message) throws CompilationFailedException { + addError(message); + failIfErrors(); + } + + + public void addException(Exception cause, SourceUnit source) throws CompilationFailedException { + addError(new ExceptionMessage(cause,configuration.getDebug(),source)); + failIfErrors(); + } + + /** + * Returns true if there are any errors pending. + */ + public boolean hasErrors() { + return this.errors != null; + } + + /** + * @return the compiler configuration used to create this error collector + */ + public CompilerConfiguration getConfiguration() { + return configuration; + } + + /** + * Returns true if there are any warnings pending. + */ + public boolean hasWarnings() { + return this.warnings != null; + } + + /** + * Returns the list of warnings, or null if there are none. + */ + public List getWarnings() { + return this.warnings; + } + + /** + * Returns the list of errors, or null if there are none. + */ + public List getErrors() { + return this.errors; + } + + /** + * Returns the number of warnings. + */ + public int getWarningCount() { + return ((this.warnings == null) ? 0 : this.warnings.size()); + } + + /** + * Returns the number of errors. + */ + public int getErrorCount() { + return ((this.errors == null) ? 0 : this.errors.size()); + } + + /** + * Returns the specified warning message, or null. + */ + public WarningMessage getWarning(int index) { + if (index < getWarningCount()) { + return (WarningMessage) this.warnings.get(index); + } + return null; + } + + /** + * Returns the specified error message, or null. + */ + public Message getError(int index) { + if (index < getErrorCount()) { + return (Message) this.errors.get(index); + } + return null; + } + + /** + * Returns the last error reported + */ + public Message getLastError() { + return (Message) this.errors.getLast(); + } + + /** + * Convenience routine to return the specified error's + * underlying SyntaxException, or null if it isn't one. + */ + public SyntaxException getSyntaxError(int index) { + SyntaxException exception = null; + + Message message = getError(index); + if (message != null && message instanceof SyntaxErrorMessage) { + exception = ((SyntaxErrorMessage) message).getCause(); + } + return exception; + } + + /** + * Convenience routine to return the specified error's + * underlying Exception, or null if it isn't one. + */ + public Exception getException(int index) { + Exception exception = null; + + Message message = getError(index); + if (message != null) { + if (message instanceof ExceptionMessage) { + exception = ((ExceptionMessage) message).getCause(); + } + else if (message instanceof SyntaxErrorMessage) { + exception = ((SyntaxErrorMessage) message).getCause(); + } + } + return exception; + } + + /** + * Adds a WarningMessage to the message set. + */ + public void addWarning(WarningMessage message) { + if (message.isRelevant(configuration.getWarningLevel())) { + if (this.warnings == null) { + this.warnings = new LinkedList(); + } + + this.warnings.add(message); + } + } + + + /** + * Convenience wrapper for addWarning() that won't create an object + * unless it is relevant. + */ + public void addWarning(int importance, String text, CSTNode context, SourceUnit source) { + if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) { + addWarning(new WarningMessage(importance, text, context, source)); + } + } + + + /** + * Convenience wrapper for addWarning() that won't create an object + * unless it is relevant. + */ + public void addWarning(int importance, String text, Object data, CSTNode context, SourceUnit source) { + if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) { + addWarning(new WarningMessage(importance, text, data, context, source)); + } + } + + + /** + * Causes the current phase to fail by throwing a + * CompilationFailedException. + */ + protected void failIfErrors() throws CompilationFailedException { + if (hasErrors()) { + throw new MultipleCompilationErrorsException(this); + } + } + + //--------------------------------------------------------------------------- + // OUTPUT + + + private void write(PrintWriter writer, Janitor janitor, List messages, String txt) { + if (messages==null || messages.isEmpty()) return; + Iterator iterator = messages.iterator(); + while (iterator.hasNext()) { + Message message = (Message) iterator.next(); + message.write(writer, janitor); + + if (configuration.getDebug() && (message instanceof SyntaxErrorMessage)){ + SyntaxErrorMessage sem = (SyntaxErrorMessage) message; + sem.getCause().printStackTrace(writer); + } + writer.println(); + } + + writer.print(messages.size()); + writer.print(" "+txt); + if (messages.size()>1) writer.print("s"); + writer.println(); + } + + /** + * Writes error messages to the specified PrintWriter. + */ + public void write(PrintWriter writer, Janitor janitor) { + write(writer,janitor,warnings,"warning"); + write(writer,janitor,errors,"error"); + } + +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/GenericsVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/GenericsVisitor.java new file mode 100644 index 0000000000..6a28179ffc --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/GenericsVisitor.java @@ -0,0 +1,192 @@ +/* + * 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.control; + +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; + +/** + * class used to verify correct usage of generics in + * class header (class and superclass declaration) + * + * @author Jochen Theodorou + */ +public class GenericsVisitor extends ClassCodeVisitorSupport { + private final SourceUnit source; + + public GenericsVisitor(SourceUnit source) { + this.source = source; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + @Override + public void visitClass(ClassNode node) { + boolean error = checkWildcard(node); + if (error) return; + boolean isAnon = node instanceof InnerClassNode && ((InnerClassNode)node).isAnonymous(); + checkGenericsUsage(node.getUnresolvedSuperClass(false), node.getSuperClass(), isAnon ? true : null); + ClassNode[] interfaces = node.getInterfaces(); + for (ClassNode anInterface : interfaces) { + checkGenericsUsage(anInterface, anInterface.redirect()); + } + node.visitContents(this); + } + + @Override + public void visitField(FieldNode node) { + ClassNode type = node.getType(); + checkGenericsUsage(type, type.redirect()); + super.visitField(node); + } + + @Override + public void visitConstructorCallExpression(ConstructorCallExpression call) { + ClassNode type = call.getType(); + boolean isAnon = type instanceof InnerClassNode && ((InnerClassNode)type).isAnonymous(); + checkGenericsUsage(type, type.redirect(), isAnon); + } + + @Override + public void visitMethod(MethodNode node) { + Parameter[] parameters = node.getParameters(); + for (Parameter param : parameters) { + ClassNode paramType = param.getType(); + checkGenericsUsage(paramType, paramType.redirect()); + } + ClassNode returnType = node.getReturnType(); + checkGenericsUsage(returnType, returnType.redirect()); + super.visitMethod(node); + } + + private boolean checkWildcard(ClassNode cn) { + ClassNode sn = cn.getUnresolvedSuperClass(false); + if (sn == null) return false; + GenericsType[] generics = sn.getGenericsTypes(); + if (generics == null) return false; + boolean error = false; + for (GenericsType generic : generics) { + if (generic.isWildcard()) { + addError("A supertype may not specify a wildcard type", sn); + error = true; + } + } + return error; + } + + private void checkGenericsUsage(ClassNode n, ClassNode cn) { + checkGenericsUsage(n, cn, null); + } + + private void checkGenericsUsage(ClassNode n, ClassNode cn, Boolean isAnonInnerClass) { + if (n.isGenericsPlaceHolder()) return; + GenericsType[] nTypes = n.getGenericsTypes(); + GenericsType[] cnTypes = cn.getGenericsTypes(); + // raw type usage is always allowed + if (nTypes == null) return; + // you can't parameterize a non-generified type + if (cnTypes == null) { + String message = "The class " + getPrintName(n) + " (supplied with " + plural("type parameter", nTypes.length) + + ") refers to the class " + getPrintName(cn) + " which takes no parameters"; + if (nTypes.length == 0) { + message += " (invalid Diamond <> usage?)"; + } + addError(message, n); + return; + } + // parameterize a type by using all of the parameters only + if (nTypes.length != cnTypes.length) { + if (Boolean.FALSE.equals(isAnonInnerClass) && nTypes.length == 0) { + return; // allow Diamond for non-AIC cases from CCE + } + String message; + if (Boolean.TRUE.equals(isAnonInnerClass) && nTypes.length == 0) { + message = "Cannot use diamond <> with anonymous inner classes"; + } else { + message = "The class " + getPrintName(n) + " (supplied with " + plural("type parameter", nTypes.length) + + ") refers to the class " + getPrintName(cn) + + " which takes " + plural("parameter", cnTypes.length); + if (nTypes.length == 0) { + message += " (invalid Diamond <> usage?)"; + } + } + addError(message, n); + return; + } + // check bounds + for (int i = 0; i < nTypes.length; i++) { + ClassNode nType = nTypes[i].getType(); + ClassNode cnType = cnTypes[i].getType(); + // GRECLIPSE edit + //if (!nType.isDerivedFrom(cnType)) { + // if (cnType.isInterface() && nType.implementsInterface(cnType)) continue; + if (!nTypes[i].isWildcard() && !nType.isDerivedFrom(cnType) && + !(cnType.isInterface() && nType.implementsInterface(cnType))) { + // GRECLIPSE end + addError("The type " + nTypes[i].getName() + + " is not a valid substitute for the bounded parameter <" + + getPrintName(cnTypes[i]) + ">", n); + } + } + } + + private String plural(String orig, int count) { + return "" + count + " " + (count == 1 ? orig : orig + "s"); + } + + private static String getPrintName(GenericsType gt) { + StringBuilder ret = new StringBuilder(gt.getName()); + ClassNode[] upperBounds = gt.getUpperBounds(); + ClassNode lowerBound = gt.getLowerBound(); + if (upperBounds != null) { + if (upperBounds.length != 1 || !"java.lang.Object".equals(getPrintName(upperBounds[0]))) { + ret.append(" extends "); + for (int i = 0; i < upperBounds.length; i++) { + ret.append(getPrintName(upperBounds[i])); + if (i + 1 < upperBounds.length) ret.append(" & "); + } + } + } else if (lowerBound != null) { + ret.append(" super ").append(getPrintName(lowerBound)); + } + return ret.toString(); + } + + private static String getPrintName(ClassNode cn) { + StringBuilder ret = new StringBuilder(cn.getName()); + GenericsType[] gts = cn.getGenericsTypes(); + if (gts != null) { + ret.append("<"); + for (int i = 0; i < gts.length; i++) { + if (i != 0) ret.append(","); + ret.append(getPrintName(gts[i])); + } + ret.append(">"); + } + return ret.toString(); + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ParserPluginFactory.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ParserPluginFactory.java new file mode 100644 index 0000000000..fb17b0573e --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ParserPluginFactory.java @@ -0,0 +1,95 @@ +/* + * 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.control; + +import groovy.lang.GroovyRuntimeException; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * A factory of parser plugin instances + * + */ +public abstract class ParserPluginFactory { + private static Class ANTLR4_CLASS=null; + + /** + * creates the ANTLR 4 parser + * @return the factory for the parser + */ + public static ParserPluginFactory antlr4() { + if (ANTLR4_CLASS==null) { + String name = "org.apache.groovy.parser.antlr4.Antlr4PluginFactory"; + try { + ANTLR4_CLASS = + Class.forName(name, false, ParserPluginFactory.class.getClassLoader()); + } catch (Exception e) { + throw new GroovyRuntimeException("Could not find or load parser plugin factory for antlr4", e); + } + } + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + public ParserPluginFactory run() throws Exception { + return (ParserPluginFactory) ANTLR4_CLASS.newInstance(); + } + }); + } catch (PrivilegedActionException e) { + throw new GroovyRuntimeException("Could not create instance of parser plugin factory for antlr4", e.getCause()); + } + } + + /** + * creates the ANTLR 2.7 parser + * @return the factory for the parser + */ + public static ParserPluginFactory antlr2() { + // GRECLIPSE edit + //return new AntlrParserPluginFactory(); + return new org.codehaus.groovy.antlr.ErrorRecoveredCSTParserPluginFactory(); + // GRECLIPSE end + } + + /** + * creates the ANTLR 2.7 parser. This method was used to switch between the pre JSR + * parser and the new ANTLR 2.7 based parser, but even before Groovy 1.0 this + * method was changed to always return the ANTLR 2.7 parser. + * @param useNewParser - ignored + * @return the ANTLR 2.7 based parser + */ + @Deprecated + public static ParserPluginFactory newInstance(boolean useNewParser) { + return newInstance(); + } + + /** + * creates the ANTLR 2.7 parser. This method was used to switch between the pre JSR + * parser and the new ANTLR 2.7 based parser, but even before Groovy 1.0 this + * method was changed to always return the ANTLR 2.7 parser. + * + * @return the new parser factory. + */ + @Deprecated + public static ParserPluginFactory newInstance() { + return antlr2(); + } + + public abstract ParserPlugin createParserPlugin(); +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ProcessingUnit.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ProcessingUnit.java new file mode 100644 index 0000000000..6c445dce9b --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ProcessingUnit.java @@ -0,0 +1,189 @@ +/* + * 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.control; + +import groovy.lang.GroovyClassLoader; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * A base class for data structures that can collect messages and errors + * during processing. + * + * @author Chris Poirier + */ + +public abstract class ProcessingUnit { + + /** + * The current phase + */ + protected int phase; + /** + * Set true if phase is finished + */ + protected boolean phaseComplete; + + // GRECLIPSE add + protected int erroredAtPhase = -1; + // GRECLIPSE end + + /** + * Configuration and other settings that control processing + */ + protected CompilerConfiguration configuration; + + /** + * The ClassLoader to use during processing + */ + protected GroovyClassLoader classLoader; + + /** + * a helper to share errors and report them + */ + protected ErrorCollector errorCollector; + + + /** + * Initialize the ProcessingUnit to the empty state. + */ + + public ProcessingUnit(CompilerConfiguration configuration, GroovyClassLoader classLoader, ErrorCollector er) { + + this.phase = Phases.INITIALIZATION; + this.configuration = configuration; + this.setClassLoader(classLoader); + configure((configuration == null ? new CompilerConfiguration() : configuration)); + if (er==null) er = new ErrorCollector(getConfiguration()); + this.errorCollector = er; + } + + + /** + * Reconfigures the ProcessingUnit. + */ + public void configure(CompilerConfiguration configuration) { + this.configuration = configuration; + } + + + public CompilerConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(CompilerConfiguration configuration) { + this.configuration = configuration; + } + + /** + * Returns the class loader in use by this ProcessingUnit. + */ + + public GroovyClassLoader getClassLoader() { + return classLoader; + } + + + /** + * Sets the class loader for use by this ProcessingUnit. + */ + + public void setClassLoader(final GroovyClassLoader loader) { + // Classloaders should only be created inside doPrivileged block + // This code creates a classloader, which needs permission if a security manage is installed. + // If this code might be invoked by code that does not have security permissions, then the classloader creation needs to occur inside a doPrivileged block. + this.classLoader = AccessController.doPrivileged(new PrivilegedAction() { + public GroovyClassLoader run() { + ClassLoader parent = Thread.currentThread().getContextClassLoader(); + if (parent == null) parent = ProcessingUnit.class.getClassLoader(); + return loader == null ? new GroovyClassLoader(parent, configuration) : loader; + } + }); + } + + + /** + * Returns the current phase. + */ + + public int getPhase() { + return this.phase; + } + + + /** + * Returns the description for the current phase. + */ + + public String getPhaseDescription() { + return Phases.getDescription(this.phase); + } + + /** + * Errors found during the compilation should be reported through the ErrorCollector. + * @return + * the ErrorCollector for this ProcessingUnit + */ + public ErrorCollector getErrorCollector() { + return errorCollector; + } + + //--------------------------------------------------------------------------- + // PROCESSING + + + /** + * Marks the current phase complete and processes any + * errors. + */ + + public void completePhase() throws CompilationFailedException { + // GRECLIPSE edit + //errorCollector.failIfErrors(); + if (errorCollector.hasErrors()) { + erroredAtPhase = phase; + } + // GRECLIPSE end + phaseComplete = true; + } + + + /** + * A synonym for gotoPhase( phase + 1 ). + */ + public void nextPhase() throws CompilationFailedException { + gotoPhase(this.phase + 1); + } + + + /** + * Wraps up any pending operations for the current phase + * and switches to the next phase. + */ + public void gotoPhase(int phase) throws CompilationFailedException { + if (!this.phaseComplete) { + completePhase(); + } + + this.phase = phase; + this.phaseComplete = false; + } + +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ResolveVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ResolveVisitor.java new file mode 100644 index 0000000000..2e041591d2 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/ResolveVisitor.java @@ -0,0 +1,1657 @@ +/* + * 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.control; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.CompileUnit; +import org.codehaus.groovy.ast.DynamicVariable; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.VariableScope; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.SpreadMapExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.ClassNodeResolver.LookupResult; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.trait.Traits; +import groovyjarjarasm.asm.Opcodes; + +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage; +import static org.codehaus.groovy.ast.tools.GeneralUtils.isDefaultVisibility; + +/** + * Visitor to resolve Types and convert VariableExpression to + * ClassExpressions if needed. The ResolveVisitor will try to + * find the Class for a ClassExpression and prints an error if + * it fails to do so. Constructions like C[], foo as C, (C) foo + * will force creation of a ClassExpression for C + *

+ * Note: the method to start the resolving is startResolving(ClassNode, SourceUnit). + */ +public class ResolveVisitor extends ClassCodeExpressionTransformer { + // GRECLIPSE private->public + public ClassNode currentClass; + // note: BigInteger and BigDecimal are also imported by default + public static final String[] DEFAULT_IMPORTS = {"java.lang.", "java.io.", "java.net.", "java.util.", "groovy.lang.", "groovy.util."}; + // GRECLIPSE private->public + public final CompilationUnit compilationUnit; + private SourceUnit source; + private VariableScope currentScope; + + private boolean isTopLevelProperty = true; + private boolean inPropertyExpression = false; + private boolean inClosure = false; + + private Map genericParameterNames = new HashMap(); + private final Set fieldTypesChecked = new HashSet(); + // GRECLIPSE add + private final Set resolutionFailedCache = new HashSet<>(32); + // GRECLIPSE end + private boolean checkingVariableTypeInDeclaration = false; + private ImportNode currImportNode = null; + private MethodNode currentMethod; + private ClassNodeResolver classNodeResolver; + + /** + * A ConstructedNestedClass consists of an outer class and a name part, denoting a + * nested class with an unknown number of levels down. This allows resolve tests to + * skip this node for further inner class searches and combinations with imports, since + * the outer class we know is already resolved. + */ + private static class ConstructedNestedClass extends ClassNode { + final ClassNode knownEnclosingType; + public ConstructedNestedClass(ClassNode outer, String inner) { + super(outer.getName()+"$"+(inner=replacePoints(inner)), Opcodes.ACC_PUBLIC,ClassHelper.OBJECT_TYPE); + this.knownEnclosingType = outer; + this.isPrimaryNode = false; + } + // GRECLIPSE add + public String getUnresolvedName() { + // outer class (aka knownEnclosingType) may have aliased name that should be reflected here too + return super.getUnresolvedName().replace(knownEnclosingType.getName(), knownEnclosingType.getUnresolvedName()); + } + // GRECLIPSE end + public boolean hasPackageName() { + if (redirect()!=this) return super.hasPackageName(); + return knownEnclosingType.hasPackageName(); + } + public String setName(String name) { + if (redirect()!=this) { + return super.setName(name); + } else { + throw new GroovyBugError("ConstructedNestedClass#setName should not be called"); + } + } + } + + private static String replacePoints(String name) { + return name.replace('.','$'); + } + + /** + * we use ConstructedClassWithPackage to limit the resolving the compiler + * does when combining package names and class names. The idea + * that if we use a package, then we do not want to replace the + * '.' with a '$' for the package part, only for the class name + * part. There is also the case of a imported class, so this logic + * can't be done in these cases... + */ + // GRECLIPSE private->public + public static class ConstructedClassWithPackage extends ClassNode { + final String prefix; + String className; + public ConstructedClassWithPackage(String pkg, String name) { + super(pkg+name, Opcodes.ACC_PUBLIC,ClassHelper.OBJECT_TYPE); + isPrimaryNode = false; + this.prefix = pkg; + this.className = name; + } + public String getName() { + if (redirect()!=this) return super.getName(); + return prefix+className; + } + public boolean hasPackageName() { + if (redirect()!=this) return super.hasPackageName(); + return className.indexOf('.')!=-1; + } + public String setName(String name) { + if (redirect()!=this) { + return super.setName(name); + } else { + throw new GroovyBugError("ConstructedClassWithPackage#setName should not be called"); + } + } + } + + /** + * we use LowerCaseClass to limit the resolving the compiler + * does for vanilla names starting with a lower case letter. The idea + * that if we use a vanilla name with a lower case letter, that this + * is in most cases no class. If it is a class the class needs to be + * imported explicitly. The effect is that in an expression like + * "def foo = bar" we do not have to use a loadClass call to check the + * name foo and bar for being classes. Instead we will ask the module + * for an alias for this name which is much faster. + */ + // GRECLIPSE private->public + public static class LowerCaseClass extends ClassNode { + final String className; + public LowerCaseClass(String name) { + super(name, Opcodes.ACC_PUBLIC,ClassHelper.OBJECT_TYPE); + isPrimaryNode = false; + this.className = name; + } + public String getName() { + if (redirect()!=this) return super.getName(); + return className; + } + public boolean hasPackageName() { + if (redirect()!=this) return super.hasPackageName(); + return false; + } + public String setName(String name) { + if (redirect()!=this) { + return super.setName(name); + } else { + throw new GroovyBugError("LowerCaseClass#setName should not be called"); + } + } + } + + public ResolveVisitor(CompilationUnit cu) { + compilationUnit = cu; + this.classNodeResolver = new ClassNodeResolver(); + } + + public void startResolving(ClassNode node, SourceUnit source) { + this.source = source; + visitClass(node); + } + + protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { + VariableScope oldScope = currentScope; + currentScope = node.getVariableScope(); + Map oldPNames = genericParameterNames; + genericParameterNames = node.isStatic() + ? new HashMap() + : new HashMap(genericParameterNames); + + resolveGenericsHeader(node.getGenericsTypes()); + + Parameter[] paras = node.getParameters(); + for (Parameter p : paras) { + p.setInitialExpression(transform(p.getInitialExpression())); + resolveOrFail(p.getType(), p.getType()); + visitAnnotations(p); + } + ClassNode[] exceptions = node.getExceptions(); + for (ClassNode t : exceptions) { + resolveOrFail(t, node); + } + resolveOrFail(node.getReturnType(), node); + + MethodNode oldCurrentMethod = currentMethod; + currentMethod = node; + super.visitConstructorOrMethod(node, isConstructor); + + currentMethod = oldCurrentMethod; + genericParameterNames = oldPNames; + currentScope = oldScope; + } + + public void visitField(FieldNode node) { + ClassNode t = node.getType(); + if(!fieldTypesChecked.contains(node)) { + resolveOrFail(t, node); + } + super.visitField(node); + } + + public void visitProperty(PropertyNode node) { + Map oldPNames = genericParameterNames; + if (node.isStatic()) { + genericParameterNames = new HashMap(); + } + + ClassNode t = node.getType(); + resolveOrFail(t, node); + super.visitProperty(node); + fieldTypesChecked.add(node.getField()); + + genericParameterNames = oldPNames; + } + + // GRECLIPSE private->protected + protected boolean resolveToInner(ClassNode type) { + // we do not do our name mangling to find an inner class + // if the type is a ConstructedClassWithPackage, because in this case we + // are resolving the name at a different place already + if (type instanceof ConstructedClassWithPackage) return false; + if (type instanceof ConstructedNestedClass) return false; + String name = type.getName(); + String saved = name; + while (true) { + int len = name.lastIndexOf('.'); + if (len == -1) break; + name = name.substring(0,len) + "$" + name.substring(len+1); + type.setName(name); + if (resolve(type)) return true; + } + if(resolveToNestedOfCurrent(type)) return true; + + type.setName(saved); + return false; + } + + // GRECLIPSE add -- support backwards compatibility with org.eclipse.jdt.groovy.core + protected boolean resolveToInnerEnum(ClassNode type) { + return resolveToNestedOfCurrent(type); + } + // GRECLIPSE end + + private boolean resolveToNestedOfCurrent(ClassNode type) { + if (type instanceof ConstructedNestedClass) return false; + // GROOVY-3110: It may be an inner enum defined by this class itself, in which case it does not need to be + // explicitly qualified by the currentClass name + String name = type.getName(); + if (currentClass != type && !name.contains(".") && type.getClass().equals(ClassNode.class)) { + ClassNode tmp = new ConstructedNestedClass(currentClass,name); + if (resolve(tmp)) { + type.setRedirect(tmp); + return true; + } + } + return false; + } + + private void resolveOrFail(ClassNode type, String msg, ASTNode node) { + if (resolve(type)) return; + if (resolveToInner(type)) return; + // GRECLIPSE edit + //addError("unable to resolve class " + type.getName() + " " + msg, node); + String fullMsg = "unable to resolve class " + type.toString(false) + " " + msg; + if (type.getEnd() > 0) { + if (type.getNameEnd() > 0) { + addTypeError(fullMsg, type); + } else { + addError(fullMsg, type); + } + } else { + addError(fullMsg, node); + } + // GRECLIPSE end + } + + private void resolveOrFail(ClassNode type, ASTNode node, boolean prefereImports) { + resolveGenericsTypes(type.getGenericsTypes()); + if (prefereImports && resolveAliasFromModule(type)) return; + resolveOrFail(type, node); + } + + private void resolveOrFail(ClassNode type, ASTNode node) { + resolveOrFail(type, "", node); + } + + // GRECLIPSE private->protected + protected boolean resolve(ClassNode type) { + return resolve(type, true, true, true); + } + + // GRECLIPSE private->protected + protected boolean resolve(ClassNode type, boolean testModuleImports, boolean testDefaultImports, boolean testStaticInnerClasses) { + resolveGenericsTypes(type.getGenericsTypes()); + if (type.isResolved() || type.isPrimaryClassNode()) return true; + if (type.isArray()) { + ClassNode element = type.getComponentType(); + boolean resolved = resolve(element, testModuleImports, testDefaultImports, testStaticInnerClasses); + if (resolved) { + ClassNode cn = element.makeArray(); + type.setRedirect(cn); + } + return resolved; + } + + // test if vanilla name is current class name + if (currentClass == type) return true; + + if (genericParameterNames.get(type.getName()) != null) { + GenericsType gt = genericParameterNames.get(type.getName()); + type.setRedirect(gt.getType()); + type.setGenericsTypes(new GenericsType[]{gt}); + type.setGenericsPlaceHolder(true); + return true; + } + + if (currentClass.getNameWithoutPackage().equals(type.getName())) { + type.setRedirect(currentClass); + return true; + } + + return resolveNestedClass(type) || + resolveFromModule(type, testModuleImports) || + resolveFromCompileUnit(type) || + resolveFromDefaultImports(type, testDefaultImports) || + resolveFromStaticInnerClasses(type, testStaticInnerClasses) || + resolveToOuter(type); + } + + // GRECLIPSE add + private boolean existsAsInnerClass(ClassNode maybeEnclosing, String name) { + for (Iterator innerClasses = maybeEnclosing.getInnerClasses(); innerClasses.hasNext();) { + InnerClassNode innerClass = innerClasses.next(); + if (name.equals(innerClass.getName())) { + return true; + } + } + return false; + } + // GRECLIPSE end + + private boolean resolveNestedClass(ClassNode type) { + if (type instanceof ConstructedNestedClass || type instanceof ConstructedClassWithPackage) return false; + // GRECLIPSE add + String qualName = type.getName(); + int dotIndex = qualName.indexOf('.'); + int dollarIndex = qualName.indexOf('$'); + String firstComponent = dotIndex == -1 && dollarIndex == -1 ? qualName + : (dotIndex == -1 ? qualName.substring(0, dollarIndex) : qualName.substring(0, dotIndex)); + // GRECLIPSE end + + // we have for example a class name A, are in class X + // and there is a nested class A$X. we want to be able + // to access that class directly, so A becomes a valid + // name in X. + // GROOVY-4043: Do this check up the hierarchy, if needed + Map hierClasses = new LinkedHashMap(); + ClassNode val; + for(ClassNode classToCheck = currentClass; classToCheck != ClassHelper.OBJECT_TYPE; + classToCheck = classToCheck.getSuperClass()) { + if(classToCheck == null || hierClasses.containsKey(classToCheck.getName())) break; + hierClasses.put(classToCheck.getName(), classToCheck); + } + + for (ClassNode classToCheck : hierClasses.values()) { + // GRECLIPSE add + if (classToCheck.mightHaveInners() && existsAsInnerClass(classToCheck, classToCheck.getName() + '$' + firstComponent)) { + // GRECLIPSE end + val = new ConstructedNestedClass(classToCheck,type.getName()); + if (resolveFromCompileUnit(val)) { + type.setRedirect(val); + return true; + } + // GRECLIPSE add + } + // GRECLIPSE end + // also check interfaces in case we have interfaces with nested classes + for (ClassNode next : classToCheck.getAllInterfaces()) { + if (type.getName().contains(next.getName())) continue; + val = new ConstructedNestedClass(next,type.getName()); + if (resolve(val, false, false, false)) { + type.setRedirect(val); + return true; + } + } + } + + // another case we want to check here is if we are in a + // nested class A$B$C and want to access B without + // qualifying it by A.B. A alone will work, since that + // is the qualified (minus package) name of that class + // anyway. + + // That means if the current class is not an InnerClassNode + // there is nothing to be done. + if (!(currentClass instanceof InnerClassNode)) return false; + + // since we have B and want to get A we start with the most + // outer class, put them together and then see if that does + // already exist. In case of B from within A$B we are done + // after the first step already. In case of for example + // A.B.C.D.E.F and accessing E from F we test A$E=failed, + // A$B$E=failed, A$B$C$E=fail, A$B$C$D$E=success + + LinkedList outerClasses = new LinkedList(); + ClassNode outer = currentClass.getOuterClass(); + while (outer!=null) { + outerClasses.addFirst(outer); + outer = outer.getOuterClass(); + } + // most outer class is now element 0 + // GRECLIPSE refactor into new method + return resolveFromInnerClass(type, outerClasses); + } + + // GRECLIPSE add + private boolean resolveFromInnerClass(ClassNode type, Collection outerClasses) { + String name; + ClassNode val; + if (type.getNameWithoutPackage().equals(type.getName())) { + // GRECLIPSE end + for (ClassNode testNode : outerClasses) { + // GRECLIPSE add + name = testNode.getName() + '$' + type.getName(); + if (!resolutionFailedCache.contains(name)) { + // GRECLIPSE end + val = new ConstructedNestedClass(testNode,type.getName()); + if (resolveFromCompileUnit(val)) { + type.setRedirect(val); + return true; + } + // GRECLIPSE add + } + resolutionFailedCache.add(name); + // GRECLIPSE end + // also check interfaces in case we have interfaces with nested classes + for (ClassNode next : testNode.getAllInterfaces()) { + if (type.getName().contains(next.getName())) continue; + // GRECLIPSE add + name = next.getName() + '$' + type.getName(); + if (resolutionFailedCache.contains(name)) continue; + // GRECLIPSE end + val = new ConstructedNestedClass(next,type.getName()); + if (resolve(val, false, false, false)) { + type.setRedirect(val); + return true; + } + // GRECLIPSE add + resolutionFailedCache.add(name); + // GRECLIPSE end + } + } + // GRECLIPSE add + } + // GRECLIPSE end + + return false; + } + + private static String replaceLastPoint(String name) { + int lastPoint = name.lastIndexOf('.'); + name = new StringBuffer() + .append(name.substring(0, lastPoint)) + .append("$") + .append(name.substring(lastPoint + 1)) + .toString(); + return name; + } + + // GRECLIPSE private->protected + protected boolean resolveFromStaticInnerClasses(ClassNode type, boolean testStaticInnerClasses) { + if (type instanceof ConstructedNestedClass) return false; + + // a class consisting of a vanilla name can never be + // a static inner class, because at least one dot is + // required for this. Example: foo.bar -> foo$bar + if (type instanceof LowerCaseClass) return false; + + // try to resolve a public static inner class' name + testStaticInnerClasses &= type.hasPackageName(); + if (testStaticInnerClasses) { + if (type instanceof ConstructedClassWithPackage) { + // we replace '.' only in the className part + // with '$' to find an inner class. The case that + // the package is really a class is handled elsewhere + ConstructedClassWithPackage tmp = (ConstructedClassWithPackage) type; + String savedName = tmp.className; + tmp.className = replaceLastPoint(savedName); + if (resolve(tmp, false, true, true)) { + type.setRedirect(tmp.redirect()); + return true; + } + tmp.className = savedName; + } else { + String savedName = type.getName(); + String replacedPointType = replaceLastPoint(savedName); + type.setName(replacedPointType); + if (resolve(type, false, true, true)) return true; + type.setName(savedName); + } + } + return false; + } + + // GRECLIPSE private->protected + protected boolean resolveFromDefaultImports(ClassNode type, boolean testDefaultImports) { + // test default imports + testDefaultImports &= !type.hasPackageName(); + // we do not resolve a vanilla name starting with a lower case letter + // try to resolve against a default import, because we know that the + // default packages do not contain classes like these + testDefaultImports &= !(type instanceof LowerCaseClass); + if (testDefaultImports) { + for (int i = 0, size = DEFAULT_IMPORTS.length; i < size; i++) { + String packagePrefix = DEFAULT_IMPORTS[i]; + String name = type.getName(); + // We limit the inner class lookups here by using ConstructedClassWithPackage. + // This way only the name will change, the packagePrefix will + // not be included in the lookup. The case where the + // packagePrefix is really a class is handled elsewhere. + // WARNING: This code does not expect a class that has a static + // inner class in DEFAULT_IMPORTS + // GRECLIPSE add + String className = packagePrefix + name; + if (resolutionFailedCache.contains(className)) continue; + // GRECLIPSE end + ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(packagePrefix,name); + if (resolve(tmp, false, false, false)) { + type.setRedirect(tmp.redirect()); + return true; + } + // GRECLIPSE add + resolutionFailedCache.add(className); + // GRECLIPSE end + } + String name = type.getName(); + if (name.equals("BigInteger")) { + type.setRedirect(ClassHelper.BigInteger_TYPE); + return true; + } else if (name.equals("BigDecimal")) { + type.setRedirect(ClassHelper.BigDecimal_TYPE); + return true; + } + } + return false; + } + + // GRECLIPSE private->protected + protected boolean resolveFromCompileUnit(ClassNode type) { + // look into the compile unit if there is a class with that name + CompileUnit compileUnit = currentClass.getCompileUnit(); + if (compileUnit == null) return false; + ClassNode cuClass = compileUnit.getClass(type.getName()); + if (cuClass != null) { + if (type != cuClass) type.setRedirect(cuClass); + return true; + } + return false; + } + + private void ambiguousClass(ClassNode type, ClassNode iType, String name) { + if (type.getName().equals(iType.getName())) { + addError("reference to " + name + " is ambiguous, both class " + type.getName() + " and " + iType.getName() + " match", type); + } else { + type.setRedirect(iType); + } + } + + private boolean resolveAliasFromModule(ClassNode type) { + // In case of getting a ConstructedClassWithPackage here we do not do checks for partial + // matches with imported classes. The ConstructedClassWithPackage is already a constructed + // node and any subclass resolving will then take place elsewhere + if (type instanceof ConstructedClassWithPackage) return false; + + ModuleNode module = currentClass.getModule(); + if (module == null) return false; + String name = type.getName(); + + // check module node imports aliases + // the while loop enables a check for inner classes which are not fully imported, + // but visible as the surrounding class is imported and the inner class is public/protected static + String pname = name; + int index = name.length(); + /* + * we have a name foo.bar and an import foo.foo. This means foo.bar is possibly + * foo.foo.bar rather than foo.bar. This means to cut at the dot in foo.bar and + * foo for import + */ + while (true) { + pname = name.substring(0, index); + ClassNode aliasedNode = null; + ImportNode importNode = module.getImport(pname); + if (importNode != null && importNode != currImportNode) { + aliasedNode = importNode.getType(); + } + if (aliasedNode == null) { + importNode = module.getStaticImports().get(pname); + if (importNode != null && importNode != currImportNode) { + // static alias only for inner classes and must be at end of chain + ClassNode tmp = new ConstructedNestedClass(importNode.getType(), importNode.getFieldName()); + if (resolve(tmp, false, false, true)) { + if ((tmp.getModifiers() & Opcodes.ACC_STATIC) != 0) { + type.setRedirect(tmp.redirect()); + return true; + } + } + } + } + + if (aliasedNode != null) { + if (pname.length() == name.length()) { + // full match + + // We can compare here by length, because pname is always + // a substring of name, so same length means they are equal. + type.setRedirect(aliasedNode); + return true; + } else { + //partial match + + // At this point we know that we have a match for pname. This may + // mean, that name[pname.length()..<-1] is a static inner class. + // For this the rest of the name does not need any dots in its name. + // It is either completely a inner static class or it is not. + // Since we do not want to have useless lookups we create the name + // completely and use a ConstructedClassWithPackage to prevent lookups against the package. + String className = aliasedNode.getNameWithoutPackage() + '$' + + name.substring(pname.length() + 1).replace('.', '$'); + ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(aliasedNode.getPackageName()+".", className); + if (resolve(tmp, true, true, false)) { + type.setRedirect(tmp.redirect()); + return true; + } + } + } + index = pname.lastIndexOf('.'); + if (index == -1) break; + } + return false; + } + + // GRECLIPSE private->protected + protected boolean resolveFromModule(ClassNode type, boolean testModuleImports) { + if (type instanceof ConstructedNestedClass) return false; + + // we decided if we have a vanilla name starting with a lower case + // letter that we will not try to resolve this name against .* + // imports. Instead a full import is needed for these. + // resolveAliasFromModule will do this check for us. This method + // does also check the module contains a class in the same package + // of this name. This check is not done for vanilla names starting + // with a lower case letter anymore + if (type instanceof LowerCaseClass) { + return resolveAliasFromModule(type); + } + + String name = type.getName(); + ModuleNode module = currentClass.getModule(); + if (module == null) return false; + + boolean newNameUsed = false; + // we add a package if there is none yet and the module has one. But we + // do not add that if the type is a ConstructedClassWithPackage. The code in ConstructedClassWithPackage + // hasPackageName() will return true if ConstructedClassWithPackage#className has no dots. + // but since the prefix may have them and the code there does ignore that + // fact. We check here for ConstructedClassWithPackage. + if (!type.hasPackageName() && module.hasPackageName() && !(type instanceof ConstructedClassWithPackage)) { + type.setName(module.getPackageName() + name); + newNameUsed = true; + } + // look into the module node if there is a class with that name + List moduleClasses = module.getClasses(); + for (ClassNode mClass : moduleClasses) { + if (mClass.getName().equals(type.getName())) { + if (mClass != type) type.setRedirect(mClass); + return true; + } + } + if (newNameUsed) type.setName(name); + + if (testModuleImports) { + if (resolveAliasFromModule(type)) return true; + + if (module.hasPackageName()) { + // check package this class is defined in. The usage of ConstructedClassWithPackage here + // means, that the module package will not be involved when the + // compiler tries to find an inner class. + // GRECLIPSE add + if (!resolutionFailedCache.contains(module.getPackageName() + name)) { + // GRECLIPSE end + ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(module.getPackageName(), name); + if (resolve(tmp, false, false, false)) { + ambiguousClass(type, tmp, name); + type.setRedirect(tmp.redirect()); + return true; + } + // GRECLIPSE add + resolutionFailedCache.add(tmp.getName()); + } + // GRECLIPSE end + } + + // check module static imports (for static inner classes) + for (ImportNode importNode : module.getStaticImports().values()) { + if (importNode.getFieldName().equals(name)) { + // GRECLIPSE add + String cName = importNode.getType().getName() + '$' + name; + if (resolutionFailedCache.contains(cName)) continue; + // GRECLIPSE end + ClassNode tmp = new ConstructedNestedClass(importNode.getType(), name); + if (resolve(tmp, false, false, true)) { + if ((tmp.getModifiers() & Opcodes.ACC_STATIC) != 0) { + type.setRedirect(tmp.redirect()); + return true; + } + } + // GRECLIPSE add + resolutionFailedCache.add(cName); + // GRECLIPSE end + } + } + + // check module node import packages + for (ImportNode importNode : module.getStarImports()) { + String packagePrefix = importNode.getPackageName(); + // We limit the inner class lookups here by using ConstructedClassWithPackage. + // This way only the name will change, the packagePrefix will + // not be included in the lookup. The case where the + // packagePrefix is really a class is handled elsewhere. + // GRECLIPSE add + if (resolutionFailedCache.contains(packagePrefix + name)) continue; + // GRECLIPSE end + ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(packagePrefix, name); + if (resolve(tmp, false, false, true)) { + ambiguousClass(type, tmp, name); + type.setRedirect(tmp.redirect()); + return true; + } + // GRECLIPSE add + resolutionFailedCache.add(tmp.getName()); + // GRECLIPSE end + } + + // check for star imports (import static pkg.Outer.*) matching static inner classes + for (ImportNode importNode : module.getStaticStarImports().values()) { + // GRECLIPSE add + if (!importNode.isUnresolvable()) { + String cName = importNode.getClassName() + '$' + name; + if (resolutionFailedCache.contains(cName)) continue; + // GRECLIPSE end + ClassNode tmp = new ConstructedNestedClass(importNode.getType(), name); + if (resolve(tmp, false, false, true)) { + if ((tmp.getModifiers() & Opcodes.ACC_STATIC) != 0) { + ambiguousClass(type, tmp, name); + type.setRedirect(tmp.redirect()); + return true; + } + } + // GRECLIPSE add + resolutionFailedCache.add(tmp.getName()); + } + // GRECLIPSE end + } + } + return false; + } + + // GRECLIPSE private->protected + protected boolean resolveToOuter(ClassNode type) { + String name = type.getName(); + + // We do not need to check instances of LowerCaseClass + // to be a Class, because unless there was an import for + // for this we do not lookup these cases. This was a decision + // made on the mailing list. To ensure we will not visit this + // method again we set a NO_CLASS for this name + if (type instanceof LowerCaseClass) { + classNodeResolver.cacheClass(name, ClassNodeResolver.NO_CLASS); + return false; + } + + if (currentClass.getModule().hasPackageName() && name.indexOf('.') == -1) return false; + LookupResult lr = null; + lr = classNodeResolver.resolveName(name, compilationUnit); + if (lr!=null) { + if (lr.isSourceUnit()) { + SourceUnit su = lr.getSourceUnit(); + currentClass.getCompileUnit().addClassNodeToCompile(type, su); + } else { + type.setRedirect(lr.getClassNode()); + } + return true; + } + return false; + } + + public Expression transform(Expression exp) { + if (exp == null) return null; + Expression ret = null; + if (exp instanceof VariableExpression) { + ret = transformVariableExpression((VariableExpression) exp); + } else if (exp.getClass() == PropertyExpression.class) { + ret = transformPropertyExpression((PropertyExpression) exp); + } else if (exp instanceof DeclarationExpression) { + ret = transformDeclarationExpression((DeclarationExpression) exp); + } else if (/* GRECLIPSE avoid transforming CompareIdentity and CompareToNull: exp instanceof BinaryExpression*/exp.getClass() == BinaryExpression.class) { + ret = transformBinaryExpression((BinaryExpression) exp); + } else if (exp instanceof MethodCallExpression) { + ret = transformMethodCallExpression((MethodCallExpression) exp); + } else if (exp instanceof ClosureExpression) { + ret = transformClosureExpression((ClosureExpression) exp); + } else if (exp instanceof ConstructorCallExpression) { + ret = transformConstructorCallExpression((ConstructorCallExpression) exp); + } else if (exp instanceof AnnotationConstantExpression) { + ret = transformAnnotationConstantExpression((AnnotationConstantExpression) exp); + } else { + resolveOrFail(exp.getType(), exp); + ret = exp.transformExpression(this); + } + if (ret!=null && ret!=exp) ret.setSourcePosition(exp); + return ret; + } + + private static String lookupClassName(PropertyExpression pe) { + boolean doInitialClassTest=true; + StringBuilder name = new StringBuilder(32); + // this loop builds a name from right to left each name part + // separated by "." + for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) { + if (it instanceof VariableExpression) { + VariableExpression ve = (VariableExpression) it; + // stop at super and this + if (ve.isSuperExpression() || ve.isThisExpression()) { + return null; + } + String varName = ve.getName(); + if (doInitialClassTest) { + // we are at the first name part. This is the right most part. + // If this part is in lower case, then we do not need a class + // check. other parts of the property expression will be tested + // by a different method call to this method, so foo.Bar.bar + // can still be resolved to the class foo.Bar and the static + // field bar. + if (!testVanillaNameForClass(varName)) return null; + doInitialClassTest = false; + name = new StringBuilder(varName); + } else { + name.insert(0, varName + "."); + } + break; + } + // anything other than PropertyExpressions or + // VariableExpressions will stop resolving + else if (it.getClass() != PropertyExpression.class) { + return null; + } else { + PropertyExpression current = (PropertyExpression) it; + String propertyPart = current.getPropertyAsString(); + // the class property stops resolving, dynamic property names too + if (propertyPart == null || propertyPart.equals("class")) { + return null; + } + if (doInitialClassTest) { + // we are at the first name part. This is the right most part. + // If this part is in lower case, then we do not need a class + // check. other parts of the property expression will be tested + // by a different method call to this method, so foo.Bar.bar + // can still be resolved to the class foo.Bar and the static + // field bar. + if (!testVanillaNameForClass(propertyPart)) return null; + doInitialClassTest= false; + name = new StringBuilder(propertyPart); + } else { + name.insert(0, propertyPart + "."); + } + } + } + if (name.length() == 0) return null; + return name.toString(); + } + + // iterate from the inner most to the outer and check for classes + // this check will ignore a .class property, for Example Integer.class will be + // a PropertyExpression with the ClassExpression of Integer as objectExpression + // and class as property + private static Expression correctClassClassChain(PropertyExpression pe) { + LinkedList stack = new LinkedList(); + ClassExpression found = null; + for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) { + if (it instanceof ClassExpression) { + found = (ClassExpression) it; + break; + } else if (!(it.getClass() == PropertyExpression.class)) { + return pe; + } + stack.addFirst(it); + } + if (found == null) return pe; + + if (stack.isEmpty()) return pe; + Object stackElement = stack.removeFirst(); + if (!(stackElement.getClass() == PropertyExpression.class)) return pe; + PropertyExpression classPropertyExpression = (PropertyExpression) stackElement; + String propertyNamePart = classPropertyExpression.getPropertyAsString(); + if (propertyNamePart == null || !propertyNamePart.equals("class")) return pe; + + found.setSourcePosition(classPropertyExpression); + if (stack.isEmpty()) return found; + stackElement = stack.removeFirst(); + if (!(stackElement.getClass() == PropertyExpression.class)) return pe; + PropertyExpression classPropertyExpressionContainer = (PropertyExpression) stackElement; + + classPropertyExpressionContainer.setObjectExpression(found); + return pe; + } + + protected Expression transformPropertyExpression(PropertyExpression pe) { + boolean itlp = isTopLevelProperty; + boolean ipe = inPropertyExpression; + + Expression objectExpression = pe.getObjectExpression(); + inPropertyExpression = true; + isTopLevelProperty = (objectExpression.getClass() != PropertyExpression.class); + objectExpression = transform(objectExpression); + // we handle the property part as if it were not part of the property + inPropertyExpression = false; + Expression property = transform(pe.getProperty()); + isTopLevelProperty = itlp; + inPropertyExpression = ipe; + + boolean spreadSafe = pe.isSpreadSafe(); + PropertyExpression old = pe; + pe = new PropertyExpression(objectExpression, property, pe.isSafe()); + pe.setSpreadSafe(spreadSafe); + pe.setSourcePosition(old); + + String className = lookupClassName(pe); + if (className != null) { + ClassNode type = ClassHelper.make(className); + if (resolve(type)) { + Expression ret = new ClassExpression(type); + ret.setSourcePosition(pe); + return ret; + } + } + if (objectExpression instanceof ClassExpression && pe.getPropertyAsString() != null) { + // possibly an inner class (or inherited inner class) + ClassExpression ce = (ClassExpression) objectExpression; + ClassNode classNode = ce.getType(); + while (classNode != null) { + ClassNode type = new ConstructedNestedClass(classNode, pe.getPropertyAsString()); + if (resolve(type, false, false, false)) { + if (classNode == ce.getType() || isVisibleNestedClass(type, ce.getType())) { + Expression ret = new ClassExpression(type); + ret.setSourcePosition(pe); // GRECLIPSE ce->pe + return ret; + } + } + classNode = classNode.getSuperClass(); + } + } + Expression ret = pe; + checkThisAndSuperAsPropertyAccess(pe); + if (isTopLevelProperty) ret = correctClassClassChain(pe); + return ret; + } + + private boolean isVisibleNestedClass(ClassNode type, ClassNode ceType) { + if (!type.isRedirectNode()) return false; + ClassNode redirect = type.redirect(); + if (Modifier.isPublic(redirect.getModifiers()) || Modifier.isProtected(redirect.getModifiers())) return true; + // package local + return isDefaultVisibility(redirect.getModifiers()) && inSamePackage(ceType, redirect); + } + + private boolean directlyImplementsTrait(ClassNode trait) { + ClassNode[] interfaces = currentClass.getInterfaces(); + if (interfaces==null) { + return currentClass.getSuperClass().equals(trait); + } + for (ClassNode node : interfaces) { + if (node.equals(trait)) { + return true; + } + } + return currentClass.getSuperClass().equals(trait); + } + + private void checkThisAndSuperAsPropertyAccess(PropertyExpression expression) { + if (expression.isImplicitThis()) return; + String prop = expression.getPropertyAsString(); + if (prop == null) return; + if (!prop.equals("this") && !prop.equals("super")) return; + + ClassNode type = expression.getObjectExpression().getType(); + if (expression.getObjectExpression() instanceof ClassExpression) { + if (!(currentClass instanceof InnerClassNode) && !Traits.isTrait(type)) { + addError("The usage of 'Class.this' and 'Class.super' is only allowed in nested/inner classes.", expression); + return; + } + if (currentScope!=null && !currentScope.isInStaticContext() && Traits.isTrait(type) && "super".equals(prop) && directlyImplementsTrait(type)) { + return; + } + ClassNode iterType = currentClass; + while (iterType != null) { + if (iterType.equals(type)) break; + iterType = iterType.getOuterClass(); + } + if (iterType == null) { + addError("The class '" + type.getName() + "' needs to be an outer class of '" + + currentClass.getName() + "' when using '.this' or '.super'.", expression); + } + if ((currentClass.getModifiers() & Opcodes.ACC_STATIC) == 0) return; + if (currentScope != null && !currentScope.isInStaticContext()) return; + addError("The usage of 'Class.this' and 'Class.super' within static nested class '" + + currentClass.getName() + "' is not allowed in a static context.", expression); + } + } + + protected Expression transformVariableExpression(VariableExpression ve) { + visitAnnotations(ve); + Variable v = ve.getAccessedVariable(); + + if(!(v instanceof DynamicVariable) && !checkingVariableTypeInDeclaration) { + /* + * GROOVY-4009: when a normal variable is simply being used, there is no need to try to + * resolve its type. Variable type resolve should proceed only if the variable is being declared. + */ + return ve; + } + if (v instanceof DynamicVariable){ + String name = ve.getName(); + ClassNode t = ClassHelper.make(name); + // asking isResolved here allows to check if a primitive + // type name like "int" was used to make t. In such a case + // we have nothing left to do. + boolean isClass = t.isResolved(); + if (!isClass) { + // It was no primitive type, so next we see if the name, + // which is a vanilla name, starts with a lower case letter. + // In that case we change it to a LowerCaseClass to let the + // compiler skip the resolving at several places in this class. + if (Character.isLowerCase(name.charAt(0))) { + t = new LowerCaseClass(name); + } + isClass = resolve(t); + if(!isClass) isClass = resolveToNestedOfCurrent(t); + } + if (isClass) { + // the name is a type so remove it from the scoping + // as it is only a classvariable, it is only in + // referencedClassVariables, but must be removed + // for each parentscope too + for (VariableScope scope = currentScope; scope != null && !scope.isRoot(); scope = scope.getParent()) { + if (scope.isRoot()) break; + if (scope.removeReferencedClassVariable(ve.getName()) == null) break; + } + ClassExpression ce = new ClassExpression(t); + ce.setSourcePosition(ve); + return ce; + } + } + resolveOrFail(ve.getType(), ve); + ClassNode origin = ve.getOriginType(); + if (origin!=ve.getType()) resolveOrFail(origin, ve); + return ve; + } + + private static boolean testVanillaNameForClass(String name) { + if (name==null || name.length()==0) return false; + return !Character.isLowerCase(name.charAt(0)); + } + + private static boolean isLeftSquareBracket(int op) { + return op == Types.ARRAY_EXPRESSION + || op == Types.LEFT_SQUARE_BRACKET + || op == Types.SYNTH_LIST + || op == Types.SYNTH_MAP; + } + + protected Expression transformBinaryExpression(BinaryExpression be) { + Expression left = transform(be.getLeftExpression()); + int type = be.getOperation().getType(); + if ((type == Types.ASSIGNMENT_OPERATOR || type == Types.EQUAL) && + left instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) left; + String error = "you tried to assign a value to the class '" + ce.getType().getName() + "'"; + if (ce.getType().isScript()) { + error += ". Do you have a script with this name?"; + } + addError(error, be.getLeftExpression()); + return be; + } + if (left instanceof ClassExpression && isLeftSquareBracket(type)) { + if (be.getRightExpression() instanceof ListExpression) { + ListExpression list = (ListExpression) be.getRightExpression(); + if (list.getExpressions().isEmpty()) { + // we have C[] if the list is empty -> should be an array then! + final ClassExpression ce = new ClassExpression(left.getType().makeArray()); + ce.setSourcePosition(be); + return ce; + } + else { + // may be we have C[k1:v1, k2:v2] -> should become (C)([k1:v1, k2:v2]) + boolean map = true; + for (Expression expression : list.getExpressions()) { + if(!(expression instanceof MapEntryExpression)) { + map = false; + break; + } + } + + if (map) { + final MapExpression me = new MapExpression(); + for (Expression expression : list.getExpressions()) { + me.addMapEntryExpression((MapEntryExpression) transform(expression)); + } + me.setSourcePosition(list); + final CastExpression ce = new CastExpression(left.getType(), me); + ce.setSourcePosition(be); + return ce; + } + } + } else if (be.getRightExpression() instanceof SpreadMapExpression) { + // we have C[*:map] -> should become (C) map + SpreadMapExpression mapExpression = (SpreadMapExpression) be.getRightExpression(); + Expression right = transform(mapExpression.getExpression()); + Expression ce = new CastExpression(left.getType(), right); + ce.setSourcePosition(be); + return ce; + } + + if (be.getRightExpression() instanceof MapEntryExpression) { + // may be we have C[k1:v1] -> should become (C)([k1:v1]) + final MapExpression me = new MapExpression(); + me.addMapEntryExpression((MapEntryExpression) transform(be.getRightExpression())); + me.setSourcePosition(be.getRightExpression()); + final CastExpression ce = new CastExpression(left.getType(), me); + ce.setSourcePosition(be); + return ce; + } + } + Expression right = transform(be.getRightExpression()); + be.setLeftExpression(left); + be.setRightExpression(right); + return be; + } + + protected Expression transformClosureExpression(ClosureExpression ce) { + boolean oldInClosure = inClosure; + inClosure = true; + Parameter[] paras = ce.getParameters(); + if (paras != null) { + for (Parameter para : paras) { + ClassNode t = para.getType(); + resolveOrFail(t, ce); + visitAnnotations(para); + if (para.hasInitialExpression()) { + Object initialVal = para.getInitialExpression(); + if (initialVal instanceof Expression) { + para.setInitialExpression(transform((Expression) initialVal)); + } + } + visitAnnotations(para); + } + } + Statement code = ce.getCode(); + if (code != null) code.visit(this); + inClosure = oldInClosure; + return ce; + } + + protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) { + ClassNode type = cce.getType(); + resolveOrFail(type, cce); + if (Modifier.isAbstract(type.getModifiers())) { + addError("You cannot create an instance from the abstract " + getDescription(type) + ".", cce); + } + + Expression ret = cce.transformExpression(this); + return ret; + } + + private static String getDescription(ClassNode node) { + return (node.isInterface() ? "interface" : "class") + " '" + node.getName() + "'"; + } + + protected Expression transformMethodCallExpression(MethodCallExpression mce) { + Expression args = transform(mce.getArguments()); + Expression method = transform(mce.getMethod()); + Expression object = transform(mce.getObjectExpression()); + + resolveGenericsTypes(mce.getGenericsTypes()); + + MethodCallExpression result = new MethodCallExpression(object, method, args); + result.setSafe(mce.isSafe()); + result.setImplicitThis(mce.isImplicitThis()); + result.setSpreadSafe(mce.isSpreadSafe()); + result.setSourcePosition(mce); + result.setGenericsTypes(mce.getGenericsTypes()); + result.setMethodTarget(mce.getMethodTarget()); + return result; + } + + protected Expression transformDeclarationExpression(DeclarationExpression de) { + visitAnnotations(de); + Expression oldLeft = de.getLeftExpression(); + checkingVariableTypeInDeclaration = true; + Expression left = transform(oldLeft); + checkingVariableTypeInDeclaration = false; + if (left instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) left; + addError("you tried to assign a value to the class " + ce.getType().getName(), oldLeft); + return de; + } + Expression right = transform(de.getRightExpression()); + if (right == de.getRightExpression()) { + fixDeclaringClass(de); + return de; + } + DeclarationExpression newDeclExpr = new DeclarationExpression(left, de.getOperation(), right); + newDeclExpr.setDeclaringClass(de.getDeclaringClass()); + fixDeclaringClass(newDeclExpr); + newDeclExpr.setSourcePosition(de); + newDeclExpr.addAnnotations(de.getAnnotations()); + return newDeclExpr; + } + + // TODO get normal resolving to set declaring class + private void fixDeclaringClass(DeclarationExpression newDeclExpr) { + if (newDeclExpr.getDeclaringClass() == null && currentMethod != null) { + newDeclExpr.setDeclaringClass(currentMethod.getDeclaringClass()); + } + } + + protected Expression transformAnnotationConstantExpression(AnnotationConstantExpression ace) { + AnnotationNode an = (AnnotationNode) ace.getValue(); + ClassNode type = an.getClassNode(); + resolveOrFail(type, ", unable to find class for annotation", an); + for (Map.Entry member : an.getMembers().entrySet()) { + member.setValue(transform(member.getValue())); + } + return ace; + } + + public void visitAnnotations(AnnotatedNode node) { + List annotations = node.getAnnotations(); + if (annotations.isEmpty()) return; + //Map tmpAnnotations = new HashMap(); + ClassNode annType; + for (AnnotationNode an : annotations) { + // skip built-in properties + if (an.isBuiltIn()) continue; + annType = an.getClassNode(); + resolveOrFail(annType, ", unable to find class for annotation", an); + for (Map.Entry member : an.getMembers().entrySet()) { + Expression newValue = transform(member.getValue()); + newValue = transformInlineConstants(newValue); + member.setValue(newValue); + checkAnnotationMemberValue(newValue); + } + /* GRECLIPSE edit -- can't do this + if(annType.isResolved()) { + Class annTypeClass = annType.getTypeClass(); + Retention retAnn = (Retention) annTypeClass.getAnnotation(Retention.class); + if (retAnn != null && retAnn.value().equals(RetentionPolicy.RUNTIME)) { + AnnotationNode anyPrevAnnNode = tmpAnnotations.put(annTypeClass.getName(), an); + if(anyPrevAnnNode != null) { + addError("Cannot specify duplicate annotation on the same member : " + annType.getName(), an); + } + } + } + */ + } + } + + // resolve constant-looking expressions statically (do here as gets transformed away later) + private Expression transformInlineConstants(Expression exp) { + if (exp instanceof PropertyExpression) { + PropertyExpression pe = (PropertyExpression) exp; + if (pe.getObjectExpression() instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) pe.getObjectExpression(); + ClassNode type = ce.getType(); + if (type.isEnum()) + return exp; + + FieldNode fn = type.getField(pe.getPropertyAsString()); + if (fn != null && !fn.isEnum() && fn.isStatic() && fn.isFinal()) { + if (fn.getInitialValueExpression() instanceof ConstantExpression) { + // GRECLIPSE edit + return cloneConstantExpression(fn.getInitialValueExpression(), exp); + // GRECLIPS end + } + } + } + // GRECLIPSE add + } else if (exp instanceof VariableExpression) { + VariableExpression ve = (VariableExpression) exp; + if (ve.getAccessedVariable() instanceof FieldNode) { + FieldNode fn = (FieldNode) ve.getAccessedVariable(); + if (!fn.isEnum() && fn.isStatic() && fn.isFinal() && + fn.getInitialValueExpression() instanceof ConstantExpression) { + return cloneConstantExpression(fn.getInitialValueExpression(), exp); + } + } + // GRECLIPSE end + } else if (exp instanceof ListExpression) { + ListExpression le = (ListExpression) exp; + ListExpression result = new ListExpression(); + for (Expression e : le.getExpressions()) { + result.addExpression(transformInlineConstants(e)); + } + // GRECLIPSE add + result.setSourcePosition(exp); + // GRECLIPSE end + return result; + } else if (exp instanceof AnnotationConstantExpression) { + ConstantExpression ce = (ConstantExpression) exp; + if (ce.getValue() instanceof AnnotationNode) { + // replicate a little bit of AnnotationVisitor here + // because we can't wait until later to do this + AnnotationNode an = (AnnotationNode) ce.getValue(); + for (Map.Entry member : an.getMembers().entrySet()) { + member.setValue(transformInlineConstants(member.getValue())); + } + } + } + return exp; + } + + // GRECLIPSE add + protected static ConstantExpression cloneConstantExpression(Expression val, Expression src) { + ConstantExpression ret = new ConstantExpression(((ConstantExpression) val).getValue()); + ret.setNodeMetaData(ClassCodeVisitorSupport.ORIGINAL_EXPRESSION, src); + // TODO: Copy any other fields or metadata? + // Source position is dropped by design. + return ret; + } + // GRECLIPSE end + + private void checkAnnotationMemberValue(Expression newValue) { + if (newValue instanceof PropertyExpression) { + PropertyExpression pe = (PropertyExpression) newValue; + if (!(pe.getObjectExpression() instanceof ClassExpression)) { + addError("unable to find class '" + pe.getText() + "' for annotation attribute constant", pe.getObjectExpression()); + } + } else if (newValue instanceof ListExpression) { + ListExpression le = (ListExpression) newValue; + for (Expression e : le.getExpressions()) { + checkAnnotationMemberValue(e); + } + } + } + + public void visitClass(ClassNode node) { + ClassNode oldNode = currentClass; + + if (node instanceof InnerClassNode) { + if (Modifier.isStatic(node.getModifiers())) { + genericParameterNames = new HashMap(); + } + } else { + genericParameterNames = new HashMap(); + } + currentClass = node; + // GRECLIPSE add + if (!commencingResolution()) return; + // GRECLIPSE end + resolveGenericsHeader(node.getGenericsTypes()); + + ModuleNode module = node.getModule(); + if (!module.hasImportsResolved()) { + for (ImportNode importNode : module.getImports()) { + currImportNode = importNode; + ClassNode type = importNode.getType(); + if (resolve(type, false, false, true)) { + currImportNode = null; + continue; + } + currImportNode = null; + addError("unable to resolve class " + type.getName(), type); + } + for (ImportNode importNode : module.getStaticStarImports().values()) { + ClassNode type = importNode.getType(); + if (resolve(type, false, false, true)) continue; + // Maybe this type belongs in the same package as the node that is doing the + // static import. In that case, the package may not have been explicitly specified. + // Try with the node's package too. If still not found, revert to original type name. + if (type.getPackageName() == null && node.getPackageName() != null) { + String oldTypeName = type.getName(); + type.setName(node.getPackageName() + "." + oldTypeName); + if (resolve(type, false, false, true)) continue; + type.setName(oldTypeName); + } + addError("unable to resolve class " + type.getName(), type); + // GRECLIPSE add + importNode.markAsUnresolvable(); + // GRECLIPSE end + } + for (ImportNode importNode : module.getStaticImports().values()) { + ClassNode type = importNode.getType(); + if (resolve(type, true, true, true)) continue; + addError("unable to resolve class " + type.getName(), type); + } + for (ImportNode importNode : module.getStaticStarImports().values()) { + ClassNode type = importNode.getType(); + if (resolve(type, true, true, true)) continue; + // GRECLIPSE add + if (!importNode.isUnresolvable()) + // GRECLIPSE end + addError("unable to resolve class " + type.getName(), type); + } + module.setImportsResolved(true); + } + + ClassNode sn = node.getUnresolvedSuperClass(); + if (sn != null) resolveOrFail(sn, node, true); + + for (ClassNode anInterface : node.getInterfaces()) { + resolveOrFail(anInterface, node, true); + } + + checkCyclicInheritance(node, node.getUnresolvedSuperClass(), node.getInterfaces()); + + super.visitClass(node); + + // GRECLIPSE add + finishedResolution(); + // GRECLIPSE end + currentClass = oldNode; + } + + private void checkCyclicInheritance(ClassNode originalNode, ClassNode parentToCompare, ClassNode[] interfacesToCompare) { + if(!originalNode.isInterface()) { + if(parentToCompare == null) return; + if(originalNode == parentToCompare.redirect()) { + addError("Cyclic inheritance involving " + parentToCompare.getName() + " in class " + originalNode.getName(), originalNode); + // GRECLIPSE add + originalNode.redirect().setHasInconsistentHierarchy(true); + // GRECLIPSE end + return; + } + if(interfacesToCompare != null && interfacesToCompare.length > 0) { + for(ClassNode intfToCompare : interfacesToCompare) { + if(originalNode == intfToCompare.redirect()) { + addError("Cycle detected: the type " + originalNode.getName() + " cannot implement itself" , originalNode); + // GRECLIPSE add + originalNode.redirect().setHasInconsistentHierarchy(true); + // GRECLIPSE end + return; + } + } + } + if(parentToCompare == ClassHelper.OBJECT_TYPE) return; + checkCyclicInheritance(originalNode, parentToCompare.getUnresolvedSuperClass(), null); + } else { + if(interfacesToCompare != null && interfacesToCompare.length > 0) { + // check interfaces at this level first + for(ClassNode intfToCompare : interfacesToCompare) { + if(originalNode == intfToCompare.redirect()) { + addError("Cyclic inheritance involving " + intfToCompare.getName() + " in interface " + originalNode.getName(), originalNode); + // GRECLIPSE add + originalNode.redirect().setHasInconsistentHierarchy(true); + // GRECLIPSE end + return; + } + } + // check next level of interfaces + for(ClassNode intf : interfacesToCompare) { + checkCyclicInheritance(originalNode, null, intf.getInterfaces()); + } + } else { + } + } + } + + public void visitCatchStatement(CatchStatement cs) { + resolveOrFail(cs.getExceptionType(), cs); + if (cs.getExceptionType() == ClassHelper.DYNAMIC_TYPE) { + cs.getVariable().setType(ClassHelper.make(Exception.class)); + } + super.visitCatchStatement(cs); + } + + public void visitForLoop(ForStatement forLoop) { + resolveOrFail(forLoop.getVariableType(), forLoop); + super.visitForLoop(forLoop); + } + + public void visitBlockStatement(BlockStatement block) { + VariableScope oldScope = currentScope; + currentScope = block.getVariableScope(); + super.visitBlockStatement(block); + currentScope = oldScope; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + private boolean resolveGenericsTypes(GenericsType[] types) { + if (types == null) return true; + currentClass.setUsingGenerics(true); + boolean resolved = true; + for (GenericsType type : types) { + // attempt resolution on all types, so don't short-circuit and stop if we've previously failed + resolved = resolveGenericsType(type) && resolved; + } + return resolved; + } + + private void resolveGenericsHeader(GenericsType[] types) { + if (types == null) return; + currentClass.setUsingGenerics(true); + for (GenericsType type : types) { + ClassNode classNode = type.getType(); + String name = type.getName(); + ClassNode[] bounds = type.getUpperBounds(); + if (bounds != null) { + boolean nameAdded = false; + for (ClassNode upperBound : bounds) { + if (!nameAdded && upperBound != null || !resolve(classNode)) { + genericParameterNames.put(name, type); + type.setPlaceholder(true); + classNode.setRedirect(upperBound); + nameAdded = true; + } + resolveOrFail(upperBound, classNode); + } + } else { + genericParameterNames.put(name, type); + classNode.setRedirect(ClassHelper.OBJECT_TYPE); + type.setPlaceholder(true); + } + } + } + + private boolean resolveGenericsType(GenericsType genericsType) { + if (genericsType.isResolved()) return true; + currentClass.setUsingGenerics(true); + ClassNode type = genericsType.getType(); + // save name before redirect + String name = type.getName(); + ClassNode[] bounds = genericsType.getUpperBounds(); + if (!genericParameterNames.containsKey(name)) { + if (bounds != null) { + for (ClassNode upperBound : bounds) { + resolveOrFail(upperBound, genericsType); + type.setRedirect(upperBound); + resolveGenericsTypes(upperBound.getGenericsTypes()); + } + } else if (genericsType.isWildcard()) { + type.setRedirect(ClassHelper.OBJECT_TYPE); + } else { + resolveOrFail(type, genericsType); + } + } else { + GenericsType gt = genericParameterNames.get(name); + type.setRedirect(gt.getType()); + genericsType.setPlaceholder(true); + } + + if (genericsType.getLowerBound() != null) { + resolveOrFail(genericsType.getLowerBound(), genericsType); + } + + if (resolveGenericsTypes(type.getGenericsTypes())) { + genericsType.setResolved(genericsType.getType().isResolved()); + } + return genericsType.isResolved(); + + } + + public void setClassNodeResolver(ClassNodeResolver classNodeResolver) { + this.classNodeResolver = classNodeResolver; + } + + // GRECLIPSE add + /** + * @return {@code true} if resolution should continue, {@code false} otherwise (because, for example, it previously succeeded for this unit) + */ + protected boolean commencingResolution() { + // template method + return true; + } + + protected void finishedResolution() { + // template method + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java new file mode 100644 index 0000000000..9d4ce67718 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/SourceUnit.java @@ -0,0 +1,372 @@ +/* + * 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.control; + +import groovyjarjarantlr.CharScanner; +import groovyjarjarantlr.MismatchedCharException; +import groovyjarjarantlr.MismatchedTokenException; +import groovyjarjarantlr.NoViableAltException; +import groovyjarjarantlr.NoViableAltForCharException; +import groovy.lang.GroovyClassLoader; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.Comment; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.control.io.FileReaderSource; +import org.codehaus.groovy.control.io.ReaderSource; +import org.codehaus.groovy.control.io.StringReaderSource; +import org.codehaus.groovy.control.io.URLReaderSource; +import org.codehaus.groovy.control.messages.Message; +import org.codehaus.groovy.control.messages.SimpleMessage; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.Reduction; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.tools.Utilities; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.List; + +/** + * Provides an anchor for a single source unit (usually a script file) + * as it passes through the compiler system. + * + * @author Chris Poirier + * @author Bing Ran + */ + +public class SourceUnit extends ProcessingUnit { + + /** + * The pluggable parser used to generate the AST - we allow + * pluggability currently as we need to have Classic and JSR support + */ + private ParserPlugin parserPlugin; + + /** + * Where we can get Readers for our source unit + */ + protected ReaderSource source; + + /** + * A descriptive name of the source unit. This name shouldn't + * be used for controlling the SourceUnit, it is only for error + * messages and to determine the name of the class for + * a script. + */ + protected String name; + + /** + * A Concrete Syntax Tree of the source + */ + protected Reduction cst; + + /** + * The root of the Abstract Syntax Tree for the source + */ + protected ModuleNode ast; + + // GRECLIPSE add + public boolean isReconcile; + + private List comments; + public List getComments() { + return comments; + } + public void setComments(List comments) { + this.comments = comments; + } + // GRECLIPSE end + + /** + * Initializes the SourceUnit from existing machinery. + */ + public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, + GroovyClassLoader loader, ErrorCollector er) { + super(flags, loader, er); + + this.name = name; + this.source = source; + } + + /** + * Initializes the SourceUnit from the specified file. + */ + public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) { + this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er); + } + + /** + * Initializes the SourceUnit from the specified URL. + */ + public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) { + this(source.toExternalForm(), new URLReaderSource(source, configuration), configuration, loader, er); + } + + /** + * Initializes the SourceUnit for a string of source. + */ + public SourceUnit(String name, String source, CompilerConfiguration configuration, + GroovyClassLoader loader, ErrorCollector er) { + this(name, new StringReaderSource(source, configuration), configuration, loader, er); + } + + /** + * Returns the name for the SourceUnit. This name shouldn't + * be used for controlling the SourceUnit, it is only for error + * messages + */ + public String getName() { + return name; + } + + + /** + * Returns the Concrete Syntax Tree produced during parse()ing. + */ + public Reduction getCST() { + return this.cst; + } + + /** + * Returns the Abstract Syntax Tree produced during convert()ing + * and expanded during later phases. + */ + public ModuleNode getAST() { + return this.ast; + } + + + /** + * Convenience routine, primarily for use by the InteractiveShell, + * that returns true if parse() failed with an unexpected EOF. + */ + public boolean failedWithUnexpectedEOF() { + // Implementation note - there are several ways for the Groovy compiler + // to report an unexpected EOF. Perhaps this implementation misses some. + // If you find another way, please add it. + if (getErrorCollector().hasErrors()) { + Message last = (Message) getErrorCollector().getLastError(); + Throwable cause = null; + if (last instanceof SyntaxErrorMessage) { + cause = ((SyntaxErrorMessage) last).getCause().getCause(); + } + if (cause != null) { + if (cause instanceof NoViableAltException) { + return isEofToken(((NoViableAltException) cause).token); + } else if (cause instanceof NoViableAltForCharException) { + char badChar = ((NoViableAltForCharException) cause).foundChar; + return badChar == CharScanner.EOF_CHAR; + } else if (cause instanceof MismatchedCharException) { + char badChar = (char) ((MismatchedCharException) cause).foundChar; + return badChar == CharScanner.EOF_CHAR; + } else if (cause instanceof MismatchedTokenException) { + return isEofToken(((MismatchedTokenException) cause).token); + } + } + } + return false; + } + + protected boolean isEofToken(groovyjarjarantlr.Token token) { + return token.getType() == groovyjarjarantlr.Token.EOF_TYPE; + } + + + //--------------------------------------------------------------------------- + // FACTORIES + + + /** + * A convenience routine to create a standalone SourceUnit on a String + * with defaults for almost everything that is configurable. + */ + public static SourceUnit create(String name, String source) { + CompilerConfiguration configuration = new CompilerConfiguration(); + configuration.setTolerance(1); + + return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration)); + } + + + /** + * A convenience routine to create a standalone SourceUnit on a String + * with defaults for almost everything that is configurable. + */ + public static SourceUnit create(String name, String source, int tolerance) { + CompilerConfiguration configuration = new CompilerConfiguration(); + configuration.setTolerance(tolerance); + + return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration)); + } + + //--------------------------------------------------------------------------- + // PROCESSING + + /** + * Parses the source to a CST. You can retrieve it with getCST(). + */ + public void parse() throws CompilationFailedException { + if (this.phase > Phases.PARSING) { + throw new GroovyBugError("parsing is already complete"); + } + + if (this.phase == Phases.INITIALIZATION) { + nextPhase(); + } + + // + // Create a reader on the source and run the parser. + + try (Reader reader = source.getReader()) { + // let's recreate the parser each time as it tends to keep around state + parserPlugin = getConfiguration().getPluginFactory().createParserPlugin(); + + cst = parserPlugin.parseCST(this, reader); + } catch (IOException e) { + getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(), this)); + } + } + + /** + * Generates an AST from the CST. You can retrieve it with getAST(). + */ + public void convert() throws CompilationFailedException { + if (this.phase == Phases.PARSING && this.phaseComplete) { + gotoPhase(Phases.CONVERSION); + } + + if (this.phase != Phases.CONVERSION) { + throw new GroovyBugError("SourceUnit not ready for convert()"); + } + + // + // Build the AST + + try { + this.ast = parserPlugin.buildAST(this, this.classLoader, this.cst); + this.ast.setDescription(this.name); + } + catch (SyntaxException e) { + if (this.ast == null) { + // Create a dummy ModuleNode to represent a failed parse - in case a later phase attempts to use the ast + this.ast = new ModuleNode(this); + } + getErrorCollector().addError(new SyntaxErrorMessage(e, this)); + } + // GRECLIPSE add + catch (CompilationFailedException cfe) { + if (this.ast == null) { + // Create a dummy ModuleNode to represent a failed parse - in case a later phase attempts to use the ast + this.ast = new ModuleNode(this); + } + throw cfe; + } + // GRECLIPSE end + + String property = (String) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return System.getProperty("groovy.ast"); + } + }); + + if ("xml".equals(property)) { + saveAsXML(name, ast); + } + } + + private static void saveAsXML(String name, ModuleNode ast) { + // GRECLIPSE edit + //XStreamUtils.serialize(name, ast); + // GRECLIPSE end + } + + //--------------------------------------------------------------------------- + // SOURCE SAMPLING + + /** + * Returns a sampling of the source at the specified line and column, + * or null if it is unavailable. + */ + public String getSample(int line, int column, Janitor janitor) { + String sample = null; + String text = source.getLine(line, janitor); + + if (text != null) { + if (column > 0) { + String marker = Utilities.repeatString(" ", column - 1) + "^"; + + if (column > 40) { + int start = column - 30 - 1; + int end = (column + 10 > text.length() ? text.length() : column + 10 - 1); + sample = " " + text.substring(start, end) + Utilities.eol() + " " + + marker.substring(start, marker.length()); + } else { + sample = " " + text + Utilities.eol() + " " + marker; + } + } else { + sample = text; + } + } + + return sample; + } + + /** + * This method adds an exception to the error collector. The Exception most likely has no line number attached to it. + * For this reason you should use this method sparingly. Prefer using addError for syntax errors or add an error + * to the {@link ErrorCollector} directly by retrieving it with getErrorCollector(). + * @param e + * the exception that occurred + * @throws CompilationFailedException + * on error + */ + public void addException(Exception e) throws CompilationFailedException { + getErrorCollector().addException(e, this); + } + + /** + * This method adds a SyntaxException to the error collector. The exception should specify the line and column + * number of the error. This method should be reserved for real errors in the syntax of the SourceUnit. If + * your error is not in syntax, and is a semantic error, or more general error, then use addException or use + * the error collector directly by retrieving it with getErrorCollector(). + * @param se + * the exception, which should have line and column information + * @throws CompilationFailedException + * on error + */ + public void addError(SyntaxException se) throws CompilationFailedException { + getErrorCollector().addError(se, this); + } + + public void addErrorAndContinue(SyntaxException se) throws CompilationFailedException { + getErrorCollector().addErrorAndContinue(se, this); + } + + public ReaderSource getSource() { + return source; + } + + public void setSource(ReaderSource source) { + this.source = source; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/StaticImportVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/StaticImportVisitor.java new file mode 100644 index 0000000000..9ba18f4f02 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/StaticImportVisitor.java @@ -0,0 +1,639 @@ +/* + * 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.control; + +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.DynamicVariable; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.EmptyExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MethodCall; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.NamedArgumentListExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.syntax.Types; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.apache.groovy.ast.tools.ClassNodeUtils.getPropNameForAccessor; +import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticMethod; +import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticProperty; +import static org.apache.groovy.ast.tools.ClassNodeUtils.hasStaticProperty; +import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass; +import static org.apache.groovy.ast.tools.ClassNodeUtils.isValidAccessorName; +import static org.codehaus.groovy.runtime.MetaClassHelper.capitalize; + +/** + * Visitor to resolve constants and method calls from static Imports + */ +public class StaticImportVisitor extends ClassCodeExpressionTransformer { + private ClassNode currentClass; + private MethodNode currentMethod; + private SourceUnit source; + private boolean inSpecialConstructorCall; + private boolean inClosure; + private boolean inPropertyExpression; + private Expression foundConstant; + private Expression foundArgs; + private boolean inAnnotation; + private boolean inLeftExpression; + + // GRECLIPE-1371 and GRECLIPSE-1363 ability to toggle behavior based on reconcile or not + boolean isReconcile; + // GRECLIPSE end + + public void visitClass(ClassNode node, SourceUnit source) { + this.currentClass = node; + this.source = source; + super.visitClass(node); + } + + @Override + protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { + this.currentMethod = node; + super.visitConstructorOrMethod(node, isConstructor); + this.currentMethod = null; + } + + @Override + public void visitAnnotations(AnnotatedNode node) { + boolean oldInAnnotation = inAnnotation; + inAnnotation = true; + super.visitAnnotations(node); + inAnnotation = oldInAnnotation; + } + + public Expression transform(Expression exp) { + if (exp == null) return null; + if (exp.getClass() == VariableExpression.class) { + return transformVariableExpression((VariableExpression) exp); + } + if (exp.getClass() == BinaryExpression.class) { + return transformBinaryExpression((BinaryExpression) exp); + } + if (exp.getClass() == PropertyExpression.class) { + return transformPropertyExpression((PropertyExpression) exp); + } + if (exp.getClass() == MethodCallExpression.class) { + return transformMethodCallExpression((MethodCallExpression) exp); + } + if (exp.getClass() == ClosureExpression.class) { + return transformClosureExpression((ClosureExpression) exp); + } + if (exp.getClass() == ConstructorCallExpression.class) { + return transformConstructorCallExpression((ConstructorCallExpression) exp); + } + if (exp.getClass() == ArgumentListExpression.class) { + Expression result = exp.transformExpression(this); + if (inPropertyExpression) { + foundArgs = result; + } + return result; + } + if (exp instanceof ConstantExpression) { + Expression result = exp.transformExpression(this); + if (inPropertyExpression) { + foundConstant = result; + } + if (inAnnotation && exp instanceof AnnotationConstantExpression) { + ConstantExpression ce = (ConstantExpression) result; + if (ce.getValue() instanceof AnnotationNode) { + // replicate a little bit of AnnotationVisitor here + // because we can't wait until later to do this + AnnotationNode an = (AnnotationNode) ce.getValue(); + Map attributes = an.getMembers(); + for (Map.Entry entry : attributes.entrySet()) { + Expression attrExpr = transform(entry.getValue()); + entry.setValue(attrExpr); + } + + } + } + return result; + } + return exp.transformExpression(this); + } + + // if you have a Bar class with a static foo property, and this: + // import static Bar.foo as baz + // then this constructor (not normal usage of statics): + // new Bar(baz:1) + // will become: + // new Bar(foo:1) + + private Expression transformMapEntryExpression(MapEntryExpression me, ClassNode constructorCallType) { + Expression key = me.getKeyExpression(); + Expression value = me.getValueExpression(); + ModuleNode module = currentClass.getModule(); + if (module != null && key instanceof ConstantExpression) { + Map importNodes = module.getStaticImports(); + if (importNodes.containsKey(key.getText())) { + ImportNode importNode = importNodes.get(key.getText()); + if (importNode.getType().equals(constructorCallType)) { + String newKey = importNode.getFieldName(); + return new MapEntryExpression(new ConstantExpression(newKey), value.transformExpression(this)); + } + } + } + return me; + } + + protected Expression transformBinaryExpression(BinaryExpression be) { + int type = be.getOperation().getType(); + boolean oldInLeftExpression; + Expression right = transform(be.getRightExpression()); + be.setRightExpression(right); + Expression left; + if (type == Types.EQUAL && be.getLeftExpression() instanceof VariableExpression) { + oldInLeftExpression = inLeftExpression; + inLeftExpression = true; + left = transform(be.getLeftExpression()); + inLeftExpression = oldInLeftExpression; + if (left instanceof StaticMethodCallExpression) { + StaticMethodCallExpression smce = (StaticMethodCallExpression) left; + StaticMethodCallExpression result = new StaticMethodCallExpression(smce.getOwnerType(), smce.getMethod(), right); + setSourcePosition(result, be); + return result; + } + } else { + left = transform(be.getLeftExpression()); + } + be.setLeftExpression(left); + return be; + } + + protected Expression transformVariableExpression(VariableExpression ve) { + Variable v = ve.getAccessedVariable(); + if (v != null && v instanceof DynamicVariable) { + Expression result = findStaticFieldOrPropAccessorImportFromModule(v.getName()); + if (result != null) { + setSourcePosition(result, ve); + if (inAnnotation) { + result = transformInlineConstants(result); + } + return result; + } + } + return ve; + } + + /** + * Set the source position of toSet including its property expression if it has one. + * + * @param toSet resulting node + * @param origNode original node + */ + private static void setSourcePosition(Expression toSet, Expression origNode) { + toSet.setSourcePosition(origNode); + if (toSet instanceof PropertyExpression) { + ((PropertyExpression) toSet).getProperty().setSourcePosition(origNode); + } + } + + // resolve constant-looking expressions statically (do here as gets transformed away later) + + private Expression transformInlineConstants(Expression exp) { + if (exp instanceof PropertyExpression) { + PropertyExpression pe = (PropertyExpression) exp; + if (pe.getObjectExpression() instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) pe.getObjectExpression(); + ClassNode type = ce.getType(); + if (type.isEnum()) return exp; + Expression constant = findConstant(getField(type, pe.getPropertyAsString())); + // GRECLIPSE edit + //if (constant != null) return constant; + if (constant != null) { + return ResolveVisitor.cloneConstantExpression(constant, exp); + } + // GRECLIPSE end + } + } else if (exp instanceof ListExpression) { + ListExpression le = (ListExpression) exp; + ListExpression result = new ListExpression(); + for (Expression e : le.getExpressions()) { + result.addExpression(transformInlineConstants(e)); + } + return result; + } + + return exp; + } + + private static Expression findConstant(FieldNode fn) { + if (fn != null && !fn.isEnum() && fn.isStatic() && fn.isFinal()) { + if (fn.getInitialValueExpression() instanceof ConstantExpression) { + return fn.getInitialValueExpression(); + } + } + return null; + } + + protected Expression transformMethodCallExpression(MethodCallExpression mce) { + Expression args = transform(mce.getArguments()); + Expression method = transform(mce.getMethod()); + Expression object = transform(mce.getObjectExpression()); + boolean isExplicitThisOrSuper = false; + boolean isExplicitSuper = false; + if (object instanceof VariableExpression) { + VariableExpression ve = (VariableExpression) object; + isExplicitThisOrSuper = !mce.isImplicitThis() && (ve.isThisExpression() || ve.isSuperExpression()); + isExplicitSuper = ve.isSuperExpression(); + } + + if (mce.isImplicitThis() || isExplicitThisOrSuper) { + if (mce.isImplicitThis()) { + Expression ret = findStaticMethodImportFromModule(method, args); + if (ret != null) { + // GRECLIPSE add + if (!((MethodCall) ret).getMethodAsString().equals(method.getText())) { + // store the identifier to facilitate organizing static imports + ret.putNodeMetaData("static.import.alias", method.getText()); + } + // GRECLIPSE end + setSourcePosition(ret, mce); + return ret; + } + if (method instanceof ConstantExpression && !inLeftExpression) { + // could be a closure field + String methodName = (String) ((ConstantExpression) method).getValue(); + ret = findStaticFieldOrPropAccessorImportFromModule(methodName); + if (ret != null) { + ret = new MethodCallExpression(ret, "call", args); + setSourcePosition(ret, mce); + return ret; + } + } + } else if (currentMethod!=null && currentMethod.isStatic() && isExplicitSuper) { + MethodCallExpression ret = new MethodCallExpression(new ClassExpression(currentClass.getSuperClass()), method, args); + setSourcePosition(ret, mce); + return ret; + } + + if (method instanceof ConstantExpression) { + ConstantExpression ce = (ConstantExpression) method; + Object value = ce.getValue(); + if (value instanceof String) { + boolean foundInstanceMethod = false; + String methodName = (String) value; + boolean inInnerClass = isInnerClass(currentClass); + if (currentMethod != null && !currentMethod.isStatic()) { + if (currentClass.hasPossibleMethod(methodName, args)) { + foundInstanceMethod = true; + } + } + boolean lookForPossibleStaticMethod = !methodName.equals("call"); + lookForPossibleStaticMethod &= !foundInstanceMethod; + lookForPossibleStaticMethod |= inSpecialConstructorCall; + lookForPossibleStaticMethod &= !inInnerClass; + if (!inClosure && lookForPossibleStaticMethod && + (hasPossibleStaticMethod(currentClass, methodName, args, true)) + || hasPossibleStaticProperty(currentClass, methodName)) { + StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass, methodName, args); + setSourcePosition(smce, mce); + return smce; + } + if (!inClosure && inInnerClass && inSpecialConstructorCall && mce.isImplicitThis() && !foundInstanceMethod) { + if (currentClass.getOuterClass().hasPossibleMethod(methodName, args)) { + object = new PropertyExpression(new ClassExpression(currentClass.getOuterClass()), new ConstantExpression("this")); + } else if (hasPossibleStaticMethod(currentClass.getOuterClass(), methodName, args, true) + || hasPossibleStaticProperty(currentClass.getOuterClass(), methodName)) { + StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass.getOuterClass(), methodName, args); + setSourcePosition(smce, mce); + return smce; + } + } + } + } + } + + MethodCallExpression result = new MethodCallExpression(object, method, args); + result.setSafe(mce.isSafe()); + result.setImplicitThis(mce.isImplicitThis()); + result.setSpreadSafe(mce.isSpreadSafe()); + result.setMethodTarget(mce.getMethodTarget()); + // GROOVY-6757 + result.setGenericsTypes(mce.getGenericsTypes()); + setSourcePosition(result, mce); + return result; + } + + protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) { + inSpecialConstructorCall = cce.isSpecialCall(); + Expression expression = cce.getArguments(); + if (expression instanceof TupleExpression) { + TupleExpression tuple = (TupleExpression) expression; + if (tuple.getExpressions().size() == 1) { + expression = tuple.getExpression(0); + if (expression instanceof NamedArgumentListExpression) { + NamedArgumentListExpression namedArgs = (NamedArgumentListExpression) expression; + List entryExpressions = namedArgs.getMapEntryExpressions(); + for (int i = 0; i < entryExpressions.size(); i++) { + entryExpressions.set(i, (MapEntryExpression) transformMapEntryExpression(entryExpressions.get(i), cce.getType())); + } + } + } + } + Expression ret = cce.transformExpression(this); + inSpecialConstructorCall = false; + return ret; + } + + protected Expression transformClosureExpression(ClosureExpression ce) { + boolean oldInClosure = inClosure; + inClosure = true; + if (ce.getParameters() != null) { + for (Parameter p : ce.getParameters()) { + if (p.hasInitialExpression()) { + p.setInitialExpression(transform(p.getInitialExpression())); + } + } + } + Statement code = ce.getCode(); + if (code != null) code.visit(this); + inClosure = oldInClosure; + return ce; + } + + protected Expression transformPropertyExpression(PropertyExpression pe) { + if (currentMethod!=null && currentMethod.isStatic() + && pe.getObjectExpression() instanceof VariableExpression + && ((VariableExpression) pe.getObjectExpression()).isSuperExpression()) { + PropertyExpression pexp = new PropertyExpression( + new ClassExpression(currentClass.getSuperClass()), + transform(pe.getProperty()) + ); + pexp.setSourcePosition(pe); + return pexp; + } + boolean oldInPropertyExpression = inPropertyExpression; + Expression oldFoundArgs = foundArgs; + Expression oldFoundConstant = foundConstant; + inPropertyExpression = true; + foundArgs = null; + foundConstant = null; + Expression objectExpression = transform(pe.getObjectExpression()); + boolean candidate = false; + if (objectExpression instanceof MethodCallExpression) { + candidate = ((MethodCallExpression)objectExpression).isImplicitThis(); + } + + if (foundArgs != null && foundConstant != null && candidate) { + Expression result = findStaticMethodImportFromModule(foundConstant, foundArgs); + if (result != null) { + objectExpression = result; + objectExpression.setSourcePosition(pe); + } + } + inPropertyExpression = oldInPropertyExpression; + foundArgs = oldFoundArgs; + foundConstant = oldFoundConstant; + pe.setObjectExpression(objectExpression); + return pe; + } + + private Expression findStaticFieldOrPropAccessorImportFromModule(String name) { + ModuleNode module = currentClass.getModule(); + if (module == null) return null; + Map importNodes = module.getStaticImports(); + Expression expression = null; + String accessorName = getAccessorName(name); + // look for one of these: + // import static MyClass.setProp [as setOtherProp] + // import static MyClass.getProp [as getOtherProp] + // when resolving prop reference + if (importNodes.containsKey(accessorName)) { + expression = findStaticProperty(importNodes, accessorName); + if (expression != null) return expression; + } + if (accessorName.startsWith("get")) { + accessorName = "is" + accessorName.substring(3); + if (importNodes.containsKey(accessorName)) { + expression = findStaticProperty(importNodes, accessorName); + if (expression != null) return expression; + } + } + + // look for one of these: + // import static MyClass.prop [as otherProp] + // when resolving prop or field reference + if (importNodes.containsKey(name)) { + ImportNode importNode = importNodes.get(name); + // GRECLIPSE add + try { + if (!isReconcile) { + // GRECLIPSE end + expression = findStaticPropertyAccessor(importNode.getType(), importNode.getFieldName()); + if (expression != null) return expression; + // GRECLIPSE add + } + // GRECLIPSE end + expression = findStaticField(importNode.getType(), importNode.getFieldName()); + if (expression != null) return expression; + // GRECLIPSE add + } finally { + // store the identifier to facilitate organizing static imports + if (expression != null) expression.putNodeMetaData("static.import.alias", name); + } + // GRECLIPSE end + } + // look for one of these: + // import static MyClass.* + // when resolving prop or field reference + for (ImportNode importNode : module.getStaticStarImports().values()) { + ClassNode node = importNode.getType(); + expression = findStaticPropertyAccessor(node, name); + if (expression != null) return expression; + expression = findStaticField(node, name); + if (expression != null) return expression; + } + return null; + } + + private Expression findStaticProperty(Map importNodes, String accessorName) { + Expression result = null; + ImportNode importNode = importNodes.get(accessorName); + ClassNode importClass = importNode.getType(); + String importMember = importNode.getFieldName(); + result = findStaticPropertyAccessorByFullName(importClass, importMember); + if (result == null) { + result = findStaticPropertyAccessor(importClass, getPropNameForAccessor(importMember)); + } + return result; + } + + private Expression findStaticMethodImportFromModule(Expression method, Expression args) { + ModuleNode module = currentClass.getModule(); + if (module == null || !(method instanceof ConstantExpression)) return null; + Map importNodes = module.getStaticImports(); + ConstantExpression ce = (ConstantExpression) method; + Expression expression; + Object value = ce.getValue(); + // skip non-Strings, e.g. Integer + if (!(value instanceof String)) return null; + final String name = (String) value; + // look for one of these: + // import static SomeClass.method [as otherName] + // when resolving methodCall() or getProp() or setProp() + if (importNodes.containsKey(name)) { + ImportNode importNode = importNodes.get(name); + expression = findStaticMethod(importNode.getType(), importNode.getFieldName(), args); + if (expression != null) return expression; + expression = findStaticPropertyAccessorGivenArgs(importNode.getType(), getPropNameForAccessor(importNode.getFieldName()), args); + if (expression != null) { + return new StaticMethodCallExpression(importNode.getType(), importNode.getFieldName(), args); + } + } + // look for one of these: + // import static SomeClass.someProp [as otherName] + // when resolving getProp() or setProp() + if (isValidAccessorName(name)) { + String propName = getPropNameForAccessor(name); + if (importNodes.containsKey(propName)) { + ImportNode importNode = importNodes.get(propName); + ClassNode importClass = importNode.getType(); + String importMember = importNode.getFieldName(); + expression = findStaticMethod(importClass, prefix(name) + capitalize(importMember), args); + if (expression != null) return expression; + expression = findStaticPropertyAccessorGivenArgs(importClass, importMember, args); + if (expression != null) { + return new StaticMethodCallExpression(importClass, prefix(name) + capitalize(importMember), args); + } + } + } + Map starImports = module.getStaticStarImports(); + ClassNode starImportType; + if (currentClass.isEnum() && starImports.containsKey(currentClass.getName())) { + ImportNode importNode = starImports.get(currentClass.getName()); + starImportType = importNode == null ? null : importNode.getType(); + expression = findStaticMethod(starImportType, name, args); + if (expression != null) return expression; + } else { + for (ImportNode importNode : starImports.values()) { + starImportType = importNode == null ? null : importNode.getType(); + expression = findStaticMethod(starImportType, name, args); + if (expression != null) return expression; + expression = findStaticPropertyAccessorGivenArgs(starImportType, getPropNameForAccessor(name), args); + if (expression != null) { + return new StaticMethodCallExpression(starImportType, name, args); + } + } + } + return null; + } + + private static String prefix(String name) { + return name.startsWith("is") ? "is" : name.substring(0, 3); + } + + private String getAccessorName(String name) { + return (inLeftExpression ? "set" : "get") + capitalize(name); + } + + private Expression findStaticPropertyAccessorGivenArgs(ClassNode staticImportType, String propName, Expression args) { + // TODO validate args? + return findStaticPropertyAccessor(staticImportType, propName); + } + + private Expression findStaticPropertyAccessor(ClassNode staticImportType, String propName) { + String accessorName = getAccessorName(propName); + Expression accessor = findStaticPropertyAccessorByFullName(staticImportType, accessorName); + if (accessor == null && accessorName.startsWith("get")) { + accessor = findStaticPropertyAccessorByFullName(staticImportType, "is" + accessorName.substring(3)); + } + if (accessor == null && hasStaticProperty(staticImportType, propName)) { + // args will be replaced + if (inLeftExpression) + accessor = new StaticMethodCallExpression(staticImportType, accessorName, ArgumentListExpression.EMPTY_ARGUMENTS); + else + accessor = new PropertyExpression(new ClassExpression(staticImportType), propName); + } + return accessor; + } + + private Expression findStaticPropertyAccessorByFullName(ClassNode staticImportType, String accessorMethodName) { + // anything will do as we only check size == 1 + ArgumentListExpression dummyArgs = new ArgumentListExpression(); + dummyArgs.addExpression(EmptyExpression.INSTANCE); + return findStaticMethod(staticImportType, accessorMethodName, (inLeftExpression ? dummyArgs : ArgumentListExpression.EMPTY_ARGUMENTS)); + } + + private static Expression findStaticField(ClassNode staticImportType, String fieldName) { + if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) { + FieldNode field = getField(staticImportType, fieldName); + if (field != null && field.isStatic()) + return new PropertyExpression(new ClassExpression(staticImportType), fieldName); + } + return null; + } + + private static FieldNode getField(ClassNode classNode, String fieldName) { + ClassNode node = classNode; + Set visited = new HashSet(); + while (node != null) { + FieldNode fn = node.getDeclaredField(fieldName); + if (fn != null) return fn; + ClassNode[] interfaces = node.getInterfaces(); + for (ClassNode iNode : interfaces) { + if (visited.contains(iNode.getName())) continue; + FieldNode ifn = getField(iNode, fieldName); + visited.add(iNode.getName()); + if (ifn != null) return ifn; + } + node = node.getSuperClass(); + } + return null; + } + + private static Expression findStaticMethod(ClassNode staticImportType, String methodName, Expression args) { + if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) { + if (staticImportType.hasPossibleStaticMethod(methodName, args)) { + return new StaticMethodCallExpression(staticImportType, methodName, args); + } + } + return null; + } + + protected SourceUnit getSourceUnit() { + return source; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/messages/LocatedMessage.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/messages/LocatedMessage.java new file mode 100644 index 0000000000..1f20dbcbc0 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/control/messages/LocatedMessage.java @@ -0,0 +1,82 @@ +/* + * 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.control.messages; + +import org.codehaus.groovy.control.Janitor; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.syntax.CSTNode; + +import java.io.PrintWriter; + +/** + * A base class for compilation messages. + * + * @author Chris Poirier + */ + +public class LocatedMessage extends SimpleMessage +{ + protected CSTNode context; // The CSTNode that indicates the location to which the message applies + + public LocatedMessage( String message, CSTNode context, SourceUnit source ) + { + super( message, source ); + this.context = context; + } + + public LocatedMessage( String message, Object data, CSTNode context, SourceUnit source ) + { + super( message, data, source ); + this.context = context; + } + + // GRECLIPSE add + public CSTNode getContext() { + return context; + } + // GRECLIPSE end + + public void write( PrintWriter writer, Janitor janitor ) + { + if (owner instanceof SourceUnit) { + SourceUnit source = (SourceUnit) owner; + + String name = source.getName(); + int line = context.getStartLine(); + int column = context.getStartColumn(); + String sample = source.getSample( line, column, janitor ); + + if( sample != null ) + { + writer.println( source.getSample(line, column, janitor) ); + } + + writer.println( name + ": " + line + ": " + this.message ); + writer.println(""); + } else { + writer.println( ": " + this.message ); + writer.println(""); + } + } + +} + + + + diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/syntax/PreciseSyntaxException.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/syntax/PreciseSyntaxException.java new file mode 100644 index 0000000000..c6b719ad7c --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/syntax/PreciseSyntaxException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2009-2017 the original author or authors. + * + * Licensed 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.syntax; + +/** + * A more precise form of SyntaxException that is aware of the precise start/end offsets. + * + * @author Andy Clement + */ +@SuppressWarnings("serial") +public class PreciseSyntaxException extends SyntaxException { + + private int startOffset; + private int endOffset; + + public PreciseSyntaxException(String message, int line, int col, int startOffset, int endOffset) { + super(message, line, col); + this.startOffset = startOffset; + this.endOffset = endOffset; + } + + public int getStartOffset() { + return startOffset; + } + + public int getEndOffset() { + return endOffset; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/tools/GroovyClass.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/tools/GroovyClass.java new file mode 100644 index 0000000000..dd6c435f21 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/tools/GroovyClass.java @@ -0,0 +1,61 @@ +/* + * 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.tools; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.control.SourceUnit; + +public class GroovyClass +{ + public static final GroovyClass[] EMPTY_ARRAY = new GroovyClass[ 0 ]; + + private final String name; + private final byte[] bytes; + // GRECLIPSE add + private final ClassNode classNode; + private final SourceUnit source; + + public GroovyClass(String name, byte[] bytes, ClassNode classNode, SourceUnit source) + { + this.name = name; + this.bytes = bytes; + this.classNode = classNode; + this.source = source; + } + + public ClassNode getClassNode() { + return classNode; + } + + public SourceUnit getSourceUnit() { + return source; + } + // GRECLIPSE end + + public String getName() + { + return this.name; + } + + public byte[] getBytes() + { + return this.bytes; + } +} + diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/ASTTransformationCollectorCodeVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/ASTTransformationCollectorCodeVisitor.java new file mode 100644 index 0000000000..d32a81104c --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/ASTTransformationCollectorCodeVisitor.java @@ -0,0 +1,595 @@ +/* + * 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.transform; + +import groovy.lang.GroovyClassLoader; +import groovy.transform.AnnotationCollector; +import groovy.transform.AnnotationCollectorMode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.ExceptionMessage; +import org.codehaus.groovy.control.messages.SimpleMessage; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.transform.trait.TraitASTTransformation; +import org.codehaus.groovy.transform.trait.Traits; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * This visitor walks the AST tree and collects references to Annotations that + * are annotated themselves by {@link GroovyASTTransformation}. Each such + * annotation is added. + *

+ * This visitor is only intended to be executed once, during the + * SEMANTIC_ANALYSIS phase of compilation. + * + * @author Danno Ferrin (shemnon) + * @author Roshan Dawrani (roshandawrani) + * @author Jochen Theodorou (blackdrag) + */ +public class ASTTransformationCollectorCodeVisitor extends ClassCodeVisitorSupport { + private final SourceUnit source; + private ClassNode classNode; + private final GroovyClassLoader transformLoader; + + // GRECLIPSE add + private boolean allowTransforms; + private List localTransformsAllowed; + + public ASTTransformationCollectorCodeVisitor(SourceUnit source, GroovyClassLoader transformLoader, boolean allowTransforms, List localTransformsAllowed) { + this(source, transformLoader); + this.allowTransforms = allowTransforms; + this.localTransformsAllowed = localTransformsAllowed; + } + // GRECLIPSE end + + public ASTTransformationCollectorCodeVisitor(SourceUnit source, GroovyClassLoader transformLoader) { + this.source = source; + this.transformLoader = transformLoader; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + public void visitClass(ClassNode klassNode) { + ClassNode oldClass = classNode; + classNode = klassNode; + super.visitClass(classNode); + classNode = oldClass; + } + + /** + * If the annotation is annotated with {@link GroovyASTTransformation} + * the annotation is added to stageVisitors at the appropriate processor visitor. + * + * @param node the node to process + */ + public void visitAnnotations(AnnotatedNode node) { + super.visitAnnotations(node); + + Map> existing = new TreeMap>(); + Map> replacements = new LinkedHashMap>(); + Map modes = new LinkedHashMap(); + int index = 0; + for (AnnotationNode annotation : node.getAnnotations()) { + findCollectedAnnotations(annotation, node, index, modes, existing, replacements); + index++; + } + for (Map.Entry> entry : replacements.entrySet()) { + Integer replacementIndex = entry.getKey(); + List annotationNodeList = entry.getValue(); + mergeCollectedAnnotations(modes.get(replacementIndex), existing, annotationNodeList); + existing.put(replacementIndex, annotationNodeList); + } + List mergedList = new ArrayList(); + for (List next : existing.values()) { + mergedList.addAll(next); + } + + node.getAnnotations().clear(); + node.getAnnotations().addAll(mergedList); + + for (AnnotationNode annotation : node.getAnnotations()) { + /* GRECLIPSE edit + Annotation transformClassAnnotation = getTransformClassAnnotation(annotation.getClassNode()); + if (transformClassAnnotation == null) { + // skip if there is no such annotation + continue; + } + addTransformsToClassNode(annotation, transformClassAnnotation); + */ + if (!this.allowTransforms && !isAllowed(annotation.getClassNode().getName())) { + continue; + } + String[] transformClassNames = getTransformClassNames(annotation.getClassNode()); + Class[] transformClasses = getTransformClasses(annotation.getClassNode()); + if (transformClassNames == null && transformClasses == null) { + continue; + } + if (transformClassNames == null) { + transformClassNames = NONE; + } + if (transformClasses == null) { + transformClasses = NO_CLASSES; + } + addTransformsToClassNode(annotation, transformClassNames, transformClasses); + // GRECLIPSE end + } + } + + private static void mergeCollectedAnnotations(AnnotationCollectorMode mode, Map> existing, List replacements) { + switch(mode) { + case PREFER_COLLECTOR: + deleteExisting(false, existing, replacements); + break; + case PREFER_COLLECTOR_MERGED: + deleteExisting(true, existing, replacements); + break; + case PREFER_EXPLICIT: + deleteReplacement(false, existing, replacements); + break; + case PREFER_EXPLICIT_MERGED: + deleteReplacement(true, existing, replacements); + break; + default: + // nothing to do + } + } + + private static void deleteExisting(boolean mergeParams, Map> existingMap, List replacements) { + for (AnnotationNode replacement : replacements) { + for (Map.Entry> entry : existingMap.entrySet()) { + Integer key = entry.getKey(); + List annotationNodes = new ArrayList(entry.getValue()); + Iterator iterator = annotationNodes.iterator(); + while (iterator.hasNext()) { + AnnotationNode existing = iterator.next(); + if (replacement.getClassNode().getName().equals(existing.getClassNode().getName())) { + if (mergeParams) { + mergeParameters(replacement, existing); + } + iterator.remove(); + } + } + existingMap.put(key, annotationNodes); + } + } + } + + private static void deleteReplacement(boolean mergeParams, Map> existingMap, List replacements) { + Iterator nodeIterator = replacements.iterator(); + while (nodeIterator.hasNext()) { + boolean remove = false; + AnnotationNode replacement = nodeIterator.next(); + for (Map.Entry> entry : existingMap.entrySet()) { + for (AnnotationNode existing : entry.getValue()) { + if (replacement.getClassNode().getName().equals(existing.getClassNode().getName())) { + if (mergeParams) { + mergeParameters(existing, replacement); + } + remove = true; + } + } + } + if (remove) { + nodeIterator.remove(); + } + } + } + + private static void mergeParameters(AnnotationNode to, AnnotationNode from) { + for (String name : from.getMembers().keySet()) { + if (to.getMember(name) == null) { + to.setMember(name, from.getMember(name)); + } + } + } + + private void assertStringConstant(Expression exp) { + if (exp == null) return; + if (!(exp instanceof ConstantExpression)) { + source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(new SyntaxException( + "Expected a String constant.", exp.getLineNumber(), exp.getColumnNumber()), + source)); + } + ConstantExpression ce = (ConstantExpression) exp; + if (!(ce.getValue() instanceof String)) { + source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(new SyntaxException( + "Expected a String constant.", exp.getLineNumber(), exp.getColumnNumber()), + source)); + } + } + + private void findCollectedAnnotations(AnnotationNode aliasNode, AnnotatedNode origin, Integer index, Map modes, Map> existing, Map> replacements) { + ClassNode classNode = aliasNode.getClassNode(); + for (AnnotationNode annotation : classNode.getAnnotations()) { + if (annotation.getClassNode().getName().equals(AnnotationCollector.class.getName())) { + AnnotationCollectorMode mode = getMode(annotation); + if (mode == null) { + mode = AnnotationCollectorMode.DUPLICATE; + } + modes.put(index, mode); + Expression processorExp = annotation.getMember("processor"); + AnnotationCollectorTransform act = null; + assertStringConstant(processorExp); + if (processorExp != null) { + String className = (String) ((ConstantExpression) processorExp).getValue(); + Class klass = loadTransformClass(className, aliasNode); + if (klass != null) { + try { + act = (AnnotationCollectorTransform) klass.newInstance(); + } catch (InstantiationException e) { + source.getErrorCollector().addErrorAndContinue(new ExceptionMessage(e, true, source)); + } catch (IllegalAccessException e) { + source.getErrorCollector().addErrorAndContinue(new ExceptionMessage(e, true, source)); + } + } + } else { + act = new AnnotationCollectorTransform(); + } + if (act != null) { + // GRECLIPSE edit + //replacements.put(index, act.visit(annotation, aliasNode, origin, source)); + // original annotation added to metadata to prevent import organizer from deleting its import + List visitResult = act.visit(annotation, aliasNode, origin, source); + for (AnnotationNode annotationNode : visitResult) { + Set aliases = annotationNode.getNodeMetaData("AnnotationCollector"); + if (aliases == null) annotationNode.setNodeMetaData("AnnotationCollector", (aliases = new HashSet<>(1))); + + aliases.add(aliasNode); + } + replacements.put(index, visitResult); + // GRECLIPSE end + return; + } + } + } + if (!replacements.containsKey(index)) { + existing.put(index, Collections.singletonList(aliasNode)); + } + } + + private static AnnotationCollectorMode getMode(AnnotationNode node) { + final Expression member = node.getMember("mode"); + if (member != null && member instanceof PropertyExpression) { + PropertyExpression prop = (PropertyExpression) member; + Expression oe = prop.getObjectExpression(); + if (oe instanceof ClassExpression) { + ClassExpression ce = (ClassExpression) oe; + if (ce.getType().getName().equals("groovy.transform.AnnotationCollectorMode")) { + return AnnotationCollectorMode.valueOf(prop.getPropertyAsString()); + } + } + } + return null; + } + + /* GRECLIPSE edit + private void addTransformsToClassNode(AnnotationNode annotation, Annotation transformClassAnnotation) { + List transformClassNames = getTransformClassNames(annotation, transformClassAnnotation); + + if (transformClassNames.isEmpty()) { + source.getErrorCollector().addError(new SimpleMessage("@GroovyASTTransformationClass in " + + annotation.getClassNode().getName() + " does not specify any transform class names/classes", source)); + } + + for (String transformClass : transformClassNames) { + Class klass = loadTransformClass(transformClass, annotation); + if (klass != null) { + verifyAndAddTransform(annotation, klass); + } + } + } + */ + + // GRECLIPSE add + private void addTransformsToClassNode(AnnotationNode annotation, String[] transformClassNames, Class[] transformClasses) { + if (transformClassNames.length == 0 && transformClasses.length == 0) { + source.getErrorCollector().addError(new SimpleMessage( + "@GroovyASTTransformationClass in " + annotation.getClassNode().getName() + + " does not specify any transform class names/classes", source)); + } else if (transformClassNames.length > 0 && transformClasses.length > 0) { + source.getErrorCollector().addError(new SimpleMessage( + "@GroovyASTTransformationClass in " + annotation.getClassNode().getName() + + " should specify transforms by class names or by classes, not by both", source)); + } + + for (String transformClass : transformClassNames) { + try { + Class klass = transformLoader.loadClass(transformClass, false, true, false); + verifyAndAddTransform(annotation, klass); + } catch (ClassNotFoundException e) { + source.getErrorCollector().addErrorAndContinue(new SimpleMessage( + "Could not find class for Transformation Processor "+ transformClass + + " declared by " + annotation.getClassNode().getName(), source)); + } + } + for (Class klass : transformClasses) { + verifyAndAddTransform(annotation, klass); + } + } + + private String[] getTransformClassNames(Annotation transformClassAnnotation) { + try { + Method valueMethod = transformClassAnnotation.getClass().getMethod("value"); + return (String[]) valueMethod.invoke(transformClassAnnotation); + } catch (Exception e) { + source.addException(e); + return NONE; + } + } + + private Class[] getTransformClasses(Annotation transformClassAnnotation) { + try { + Method classesMethod = transformClassAnnotation.getClass().getMethod("classes"); + return (Class[]) classesMethod.invoke(transformClassAnnotation); + } catch (Exception e) { + source.addException(e); + return NO_CLASSES; + } + } + // GRECLIPSE end + + private Class loadTransformClass(String transformClass, AnnotationNode annotation) { + try { + return transformLoader.loadClass(transformClass, false, true, false); + } catch (ClassNotFoundException e) { + source.getErrorCollector().addErrorAndContinue( + new SimpleMessage( + "Could not find class for Transformation Processor " + transformClass + + " declared by " + annotation.getClassNode().getName(), + source)); + } + return null; + } + + private void verifyAndAddTransform(AnnotationNode annotation, Class klass) { + verifyClass(annotation, klass); + verifyCompilePhase(annotation, klass); + addTransform(annotation, klass); + } + + private void verifyCompilePhase(AnnotationNode annotation, Class klass) { + GroovyASTTransformation transformationClass = klass.getAnnotation(GroovyASTTransformation.class); + if (transformationClass != null) { + CompilePhase specifiedCompilePhase = transformationClass.phase(); + if (specifiedCompilePhase.getPhaseNumber() < CompilePhase.SEMANTIC_ANALYSIS.getPhaseNumber()) { + source.getErrorCollector().addError( + new SimpleMessage( + annotation.getClassNode().getName() + " is defined to be run in compile phase " + specifiedCompilePhase + ". Local AST transformations must run in " + CompilePhase.SEMANTIC_ANALYSIS + " or later!", + source)); + } + + } else { + source.getErrorCollector().addError( + new SimpleMessage("AST transformation implementation classes must be annotated with " + GroovyASTTransformation.class.getName() + ". " + klass.getName() + " lacks this annotation.", source)); + } + } + + private void verifyClass(AnnotationNode annotation, Class klass) { + if (!ASTTransformation.class.isAssignableFrom(klass)) { + source.getErrorCollector().addError(new SimpleMessage("Not an ASTTransformation: " + + klass.getName() + " declared by " + annotation.getClassNode().getName(), source)); + } + } + + private void addTransform(AnnotationNode annotation, Class klass) { + boolean apply = !Traits.isTrait(classNode) || klass == TraitASTTransformation.class; + if (apply) { + classNode.addTransform(klass, annotation); + } + } + + private static Annotation getTransformClassAnnotation(ClassNode annotatedType) { + if (!annotatedType.isResolved()) return null; + + for (Annotation ann : annotatedType.getTypeClass().getAnnotations()) { + // because compiler clients are free to choose any GroovyClassLoader for + // resolving ClassNodeS such as annotatedType, we have to compare by name, + // and cannot cast the return value to GroovyASTTransformationClass + if (ann.annotationType().getName().equals(GroovyASTTransformationClass.class.getName())) { + return ann; + } + } + + return null; + } + + /* GRECLIPSE edit + private List getTransformClassNames(AnnotationNode annotation, Annotation transformClassAnnotation) { + List result = new ArrayList(); + + try { + Method valueMethod = transformClassAnnotation.getClass().getMethod("value"); + String[] names = (String[]) valueMethod.invoke(transformClassAnnotation); + result.addAll(Arrays.asList(names)); + + Method classesMethod = transformClassAnnotation.getClass().getMethod("classes"); + Class[] classes = (Class[]) classesMethod.invoke(transformClassAnnotation); + for (Class klass : classes) { + result.add(klass.getName()); + } + + if (names.length > 0 && classes.length > 0) { + source.getErrorCollector().addError(new SimpleMessage("@GroovyASTTransformationClass in " + + annotation.getClassNode().getName() + + " should specify transforms only by class names or by classes and not by both", source)); + } + } catch (Exception e) { + source.addException(e); + } + + return result; + } + */ + + // GRECLIPSE add + private static final String[] NONE = new String[0]; + private static final Class[] NO_CLASSES = new Class[0]; + + /** + * For the supplied classnode, this method will check if there is an annotation on it of kind 'GroovyASTTransformationClass'. If there is then + * the 'value' member of that annotation will be retrieved and the value considered to be the class name of a transformation. + * + * @return null if no annotation found, otherwise a String[] of classnames - this will be size 0 if no value was specified + */ + private String[] getTransformClassNames(ClassNode cn) { + if (!cn.hasClass()) { + List annotations = cn.getAnnotations(); + AnnotationNode transformAnnotation = null; + for (AnnotationNode anno: annotations) { + if (anno.getClassNode().getName().equals(GroovyASTTransformationClass.class.getName())) { + transformAnnotation = anno; + break; + } + } + if (transformAnnotation != null) { + // will work so long as signature for the member 'value' is String[] + Expression expr2 = transformAnnotation.getMember("value"); + String[] values = null; + if (expr2 == null) { + return NONE; + } + if (expr2 instanceof ListExpression) { + ListExpression expression = (ListExpression) expr2; + List expressions = expression.getExpressions(); + values = new String[expressions.size()]; + int e = 0; + for (Expression expr: expressions) { + values[e++] = ((ConstantExpression) expr).getText(); + } + } else if (expr2 instanceof ConstantExpression) { + values = new String[1]; + values[0] = ((ConstantExpression) expr2).getText(); + } else { + throw new IllegalStateException("NYI: eclipse doesn't understand this kind of expression in an Ast transform definition: " + expr2 + " (class=" + expr2.getClass().getName() + ")"); + } + return values; + } + return null; + } else { + // FIXASC check haven't broken transforms for 'vanilla' (outside of eclipse) execution of groovyc + Annotation transformClassAnnotation = getTransformClassAnnotation(cn); + if (transformClassAnnotation == null) { + return null; + } + return getTransformClassNames(transformClassAnnotation); + } + } + + private Class[] getTransformClasses(ClassNode classNode) { + if (!classNode.hasClass()) { + List annotations = classNode.getAnnotations(); + AnnotationNode transformAnnotation = null; + for (AnnotationNode anno: annotations) { + if (anno.getClassNode().getName().equals(GroovyASTTransformationClass.class.getName())) { + transformAnnotation = anno; + break; + } + } + if (transformAnnotation != null) { + Expression expr = transformAnnotation.getMember("classes"); + if (expr == null) { + return NO_CLASSES; + } + Class[] values = NO_CLASSES; + // Will need to extract the classnames + if (expr instanceof ListExpression) { + List> loadedClasses = new ArrayList<>(); + ListExpression expression = (ListExpression) expr; + List expressions = expression.getExpressions(); + for (Expression oneExpr: expressions) { + String classname = ((ClassExpression) oneExpr).getType().getName(); + try { + Class clazz = Class.forName(classname, false, transformLoader); + loadedClasses.add(clazz); + } catch (ClassNotFoundException cnfe) { + source.getErrorCollector().addError(new SimpleMessage("Ast transform processing, cannot find " + classname, source)); + } + } + if (!loadedClasses.isEmpty()) { + values = loadedClasses.toArray(new Class[loadedClasses.size()]); + } + return values; + } + throw new RuntimeException("nyi implemented in eclipse: need to support: " + expr + " (class=" + expr.getClass() + ")"); + } + return null; + } else { + Annotation transformClassAnnotation = getTransformClassAnnotation(classNode); + if (transformClassAnnotation == null) { + return null; + } + return getTransformClasses(transformClassAnnotation); + } + } + + /** + * Check the transform name against the allowed transforms. + * + * @param transformName the name of the transform + * @return {@code true} if it is allowed + */ + private boolean isAllowed(String transformName) { + if (localTransformsAllowed == null || + "groovy.transform.TypeChecked".equals(transformName) || + "groovy.transform.CompileStatic".equals(transformName) || + "grails.transaction.Transactional".equals(transformName)) { + return true; + } + for (String localTransformAllowed : localTransformsAllowed) { + if (localTransformAllowed.equals("*")) { + return true; + } else if (localTransformAllowed.endsWith("$")) { + // must be the last part of the name + if (transformName.endsWith(localTransformAllowed.substring(0, localTransformAllowed.length() - 1))) { + return true; + } + } else { + // indexof is good enough + if (transformName.indexOf(localTransformAllowed) != -1) { + return true; + } + } + } + return false; + } + // GRECLIPSE end +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/ASTTransformationVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/ASTTransformationVisitor.java new file mode 100644 index 0000000000..c067e36fd3 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/ASTTransformationVisitor.java @@ -0,0 +1,481 @@ +/* + * 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.transform; + +import groovy.lang.GroovyClassLoader; +import groovy.transform.CompilationUnitAware; +import org.codehaus.groovy.GroovyException; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.control.ASTTransformationsContext; +import org.codehaus.groovy.control.CompilationFailedException; +import org.codehaus.groovy.control.CompilationUnit; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.Phases; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SimpleMessage; +import org.codehaus.groovy.control.messages.WarningMessage; +import org.codehaus.groovy.eclipse.GroovyLogManager; +import org.codehaus.groovy.eclipse.TraceCategory; +import org.codehaus.groovy.syntax.SyntaxException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * This class handles the invocation of the ASTAnnotationTransformation + * when it is encountered by a tree walk. One instance of each exists + * for each phase of the compilation it applies to. Before invocation the + *

+ * {@link org.codehaus.groovy.transform.ASTTransformationCollectorCodeVisitor} will add a list + * of annotations that this visitor should be concerned about. All other + * annotations are ignored, whether or not they are GroovyASTTransformation + * annotated or not. + *

+ * A Two-pass method is used. First all candidate annotations are added to a + * list then the transformations are called on those collected annotations. + * This is done to avoid concurrent modification exceptions during the AST tree + * walk and allows the transformations to alter any portion of the AST tree. + * Hence annotations that are added in this phase will not be processed as + * transformations. They will only be handled in later phases (and then only + * if the type was in the AST prior to any AST transformations being run + * against it). + * + * @author Danno Ferrin (shemnon) + */ +public final class ASTTransformationVisitor extends ClassCodeVisitorSupport { + + private final ASTTransformationsContext context; + private final CompilePhase phase; + private SourceUnit source; + private List targetNodes; + private Map> transforms; + + private ASTTransformationVisitor(final CompilePhase phase, final ASTTransformationsContext context) { + this.phase = phase; + this.context = context; + } + + protected SourceUnit getSourceUnit() { + return source; + } + + /** + * Main loop entry. + *

+ * First, it delegates to the super visitClass so we can collect the + * relevant annotations in an AST tree walk. + *

+ * Second, it calls the visit method on the transformation for each relevant + * annotation found. + * + * @param classNode the class to visit + */ + public void visitClass(ClassNode classNode) { + // only descend if we have annotations to look for + Map, Set> baseTransforms = classNode.getTransforms(phase); + if (!baseTransforms.isEmpty()) { + final Map, ASTTransformation> transformInstances = new HashMap, ASTTransformation>(); + for (Class transformClass : baseTransforms.keySet()) { + try { + transformInstances.put(transformClass, transformClass.newInstance()); + } catch (InstantiationException e) { + source.getErrorCollector().addError( + new SimpleMessage( + "Could not instantiate Transformation Processor " + transformClass + , //+ " declared by " + annotation.getClassNode().getName(), + source)); + } catch (IllegalAccessException e) { + source.getErrorCollector().addError( + new SimpleMessage( + "Could not instantiate Transformation Processor " + transformClass + , //+ " declared by " + annotation.getClassNode().getName(), + source)); + } + } + + // invert the map, is now one to many + transforms = new HashMap>(); + for (Map.Entry, Set> entry : baseTransforms.entrySet()) { + for (ASTNode node : entry.getValue()) { + List list = transforms.get(node); + if (list == null) { + list = new ArrayList(); + transforms.put(node, list); + } + list.add(transformInstances.get(entry.getKey())); + } + } + + targetNodes = new LinkedList(); + + // first pass, collect nodes + super.visitClass(classNode); + + // second pass, call visit on all of the collected nodes + for (ASTNode[] node : targetNodes) { + for (ASTTransformation snt : transforms.get(node[0])) { + // GRECLIPSE add + try { + long stime = System.nanoTime(); + boolean okToSet = (source != null && source.getErrorCollector() != null); + try { + if (okToSet) { + source.getErrorCollector().transformActive = true; + } + // GRECLIPSE end + if (snt instanceof CompilationUnitAware) { + ((CompilationUnitAware)snt).setCompilationUnit(context.getCompilationUnit()); + } + snt.visit(node, source); + // GRECLIPSE add + } finally { + if (okToSet) { + source.getErrorCollector().transformActive = false; + } + } + long etime = System.nanoTime(); + if (GroovyLogManager.manager.hasLoggers()) { + try { + GroovyLogManager.manager.log(TraceCategory.AST_TRANSFORM, "Local transform " + snt.getClass().getName() + " on " + classNode.getName() + ":" + node[1] + " = " + ((etime - stime) / 1000000) + "ms"); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } catch (NoClassDefFoundError ncdfe) { + String transformName = snt.getClass().getName(); + StringBuilder sb = new StringBuilder(); + sb.append("Unable to run AST transform ").append(transformName).append(": missing class ").append(ncdfe.getMessage()); + sb.append(": are you attempting to use groovy classes in an AST transform in the same project in which it is defined?"); + source.addError(new SyntaxException(sb.toString(), ncdfe, 0, 0)); + } + // GRECLIPSE end + } + } + } + } + + /** + * Adds the annotation to the internal target list if a match is found. + * + * @param node the node to be processed + */ + public void visitAnnotations(AnnotatedNode node) { + super.visitAnnotations(node); + for (AnnotationNode annotation : node.getAnnotations()) { + if (transforms.containsKey(annotation)) { + targetNodes.add(new ASTNode[]{annotation, node}); + } + } + } + + public static void addPhaseOperations(final CompilationUnit compilationUnit) { + final ASTTransformationsContext context = compilationUnit.getASTTransformationsContext(); + addGlobalTransforms(context); + + compilationUnit.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { + ASTTransformationCollectorCodeVisitor collector = + // GRECLIPSE added params + new ASTTransformationCollectorCodeVisitor(source, compilationUnit.getTransformLoader(), compilationUnit.allowTransforms, compilationUnit.localTransformsToRunOnReconcile); + collector.visitClass(classNode); + } + }, Phases.SEMANTIC_ANALYSIS); + for (CompilePhase phase : CompilePhase.values()) { + final ASTTransformationVisitor visitor = new ASTTransformationVisitor(phase, context); + switch (phase) { + case INITIALIZATION: + case PARSING: + case CONVERSION: + // with transform detection alone these phases are inaccessible, so don't add it + break; + + default: + compilationUnit.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() { + public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { + visitor.source = source; + visitor.visitClass(classNode); + } + }, phase.getPhaseNumber()); + break; + + } + } + } + + public static void addGlobalTransformsAfterGrab(ASTTransformationsContext context) { + doAddGlobalTransforms(context, false); + } + + public static void addGlobalTransforms(ASTTransformationsContext context) { + doAddGlobalTransforms(context, true); + } + + private static void doAddGlobalTransforms(ASTTransformationsContext context, boolean isFirstScan) { + final CompilationUnit compilationUnit = context.getCompilationUnit(); + // GRECLIPSE add + ensureGlobalTransformsAllowedInReconcileInitialized(); + // GRECLIPSE end + GroovyClassLoader transformLoader = compilationUnit.getTransformLoader(); + Map transformNames = new LinkedHashMap(); + try { + Enumeration globalServices = transformLoader.getResources("META-INF/services/org.codehaus.groovy.transform.ASTTransformation"); + while (globalServices.hasMoreElements()) { + URL service = globalServices.nextElement(); + String className; + // GRECLIPSE add -- don't consume our own META-INF entries + if (skipManifest(compilationUnit, service)) continue; + // GRECLIPSE end + BufferedReader svcIn = null; + try { + svcIn = new BufferedReader(new InputStreamReader(service.openStream(), "UTF-8")); + try { + className = svcIn.readLine(); + } catch (IOException ioe) { + compilationUnit.getErrorCollector().addError(new SimpleMessage( + "IOException reading the service definition at " + + service.toExternalForm() + " because of exception " + ioe.toString(), null)); + continue; + } + Set disabledGlobalTransforms = compilationUnit.getConfiguration().getDisabledGlobalASTTransformations(); + if (disabledGlobalTransforms == null) disabledGlobalTransforms = Collections.emptySet(); + while (className != null) { + if (!className.startsWith("#") && className.length() > 0) { + if (!disabledGlobalTransforms.contains(className)) { + if (transformNames.containsKey(className)) { + try { + if (!service.toURI().equals(transformNames.get(className).toURI())) { + compilationUnit.getErrorCollector().addWarning( + WarningMessage.POSSIBLE_ERRORS, + "The global transform for class " + className + " is defined in both " + + transformNames.get(className).toExternalForm() + + " and " + + service.toExternalForm() + + " - the former definition will be used and the latter ignored.", + null, + null); + } + } catch (URISyntaxException e) { + compilationUnit.getErrorCollector().addWarning( + WarningMessage.POSSIBLE_ERRORS, + "Failed to parse URL as URI because of exception " + e.toString(), + null, + null); + } + // GRECLIPSE added condition + } else if (compilationUnit.allowTransforms || globalTransformsAllowedInReconcile.contains(className)) { + transformNames.put(className, service); + } + } + } + try { + className = svcIn.readLine(); + } catch (IOException ioe) { + compilationUnit.getErrorCollector().addError(new SimpleMessage( + "IOException reading the service definition at " + + service.toExternalForm() + " because of exception " + ioe.toString(), null)); + //noinspection UnnecessaryContinue + continue; + } + } + } finally { + if (svcIn != null) + svcIn.close(); + } + } + } catch (IOException e) { + //FIXME the warning message will NPE with what I have :( + compilationUnit.getErrorCollector().addError(new SimpleMessage( + "IO Exception attempting to load global transforms:" + e.getMessage(), + null)); + } + + // record the transforms found in the first scan, so that in the 2nd scan, phase operations + // can be added for only for new transforms that have come in + if(isFirstScan) { + for (Map.Entry entry : transformNames.entrySet()) { + context.getGlobalTransformNames().add(entry.getKey()); + } + addPhaseOperationsForGlobalTransforms(context.getCompilationUnit(), transformNames, isFirstScan); + } else { + Iterator> it = transformNames.entrySet().iterator(); + while(it.hasNext()) { + Map.Entry entry = it.next(); + if(!context.getGlobalTransformNames().add(entry.getKey())) { + // phase operations for this transform class have already been added before, so remove from current scan cycle + it.remove(); + } + } + addPhaseOperationsForGlobalTransforms(context.getCompilationUnit(), transformNames, isFirstScan); + } + } + + // GRECLIPSE add + /** + * Determines whether a given services manifest file belongs to the current project. If so + * it must be skipped because we can not apply a GlobalASTTransform to the project that + * defines it. + */ + private static boolean skipManifest(CompilationUnit compilationUnit, URL service) { + if (service == null) { + //This shouldn't happen, but anyhow... + return true; + } + String exclude = compilationUnit.excludeGlobalASTScan; + if (exclude == null) { + return false; + } + String proto = service.getProtocol(); + if ("file".equals(proto)) { + return service.getPath().startsWith(exclude); + } + return false; + } + + private static Set globalTransformsAllowedInReconcile = null; + + private static void ensureGlobalTransformsAllowedInReconcileInitialized() { + if (globalTransformsAllowedInReconcile == null) { + globalTransformsAllowedInReconcile = new TreeSet<>(); + globalTransformsAllowedInReconcile.add("groovy.grape.GrabAnnotationTransformation"); + String transformNames = System.getProperty("greclipse.globalTransformsInReconcile", ""); + for (String transformName : transformNames.split(",")) { + globalTransformsAllowedInReconcile.add(transformName.trim()); + } + globalTransformsAllowedInReconcile.remove(""); + } + } + // GRECLIPSE end + + private static void addPhaseOperationsForGlobalTransforms(CompilationUnit compilationUnit, + Map transformNames, boolean isFirstScan) { + GroovyClassLoader transformLoader = compilationUnit.getTransformLoader(); + for (Map.Entry entry : transformNames.entrySet()) { + try { + Class gTransClass = transformLoader.loadClass(entry.getKey(), false, true, false); + //no inspection unchecked + GroovyASTTransformation transformAnnotation = (GroovyASTTransformation) gTransClass.getAnnotation(GroovyASTTransformation.class); + if (transformAnnotation == null) { + compilationUnit.getErrorCollector().addWarning(new WarningMessage( + WarningMessage.POSSIBLE_ERRORS, + "Transform Class " + entry.getKey() + " is specified as a global transform in " + entry.getValue().toExternalForm() + + " but it is not annotated by " + GroovyASTTransformation.class.getName() + + " the global transform associated with it may fail and cause the compilation to fail.", + null, + null)); + continue; + } + if (ASTTransformation.class.isAssignableFrom(gTransClass)) { + // GRECLIPSE add + try { + // GRECLIPSE end + final ASTTransformation instance = (ASTTransformation)gTransClass.newInstance(); + if (instance instanceof CompilationUnitAware) { + ((CompilationUnitAware)instance).setCompilationUnit(compilationUnit); + } + CompilationUnit.SourceUnitOperation suOp = new CompilationUnit.SourceUnitOperation() { + // GRECLIPSE add + private boolean isBuggered; + // GRECLIPSE end + public void call(SourceUnit source) throws CompilationFailedException { + // GRECLIPSE add + if (isBuggered) return; + try { + long stime = System.nanoTime(); + boolean okToSet = (source != null && source.getErrorCollector() != null); + try { + if (okToSet) { + source.getErrorCollector().transformActive = true; + } + // GRECLIPSE end + instance.visit(new ASTNode[] {source.getAST()}, source); + // GRECLIPSE add + } finally { + if (okToSet) { + source.getErrorCollector().transformActive = false; + } + } + long etime = System.nanoTime(); + if (GroovyLogManager.manager.hasLoggers()) { + long timetaken = (etime - stime) / 1000000; + if (timetaken > 0) { + try { + GroovyLogManager.manager.log(TraceCategory.AST_TRANSFORM, "Global transform " + instance.getClass().getName() + " on " + source.getName() + " = " + timetaken + "ms"); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + } catch (NoClassDefFoundError ncdfe) { + // Suggests that the transform is written in Java but has dependencies on Groovy source + // within the same project - as this has yet to be compiled, we can't find the class. + Exception e = new GroovyException("Transform " + instance.toString() + " cannot be run", ncdfe); + e.printStackTrace(); + source.addException(e); + // disable this visitor because it is BUGGERED + isBuggered = true; + } + // GRECLIPSE end + } + }; + if(isFirstScan) { + compilationUnit.addPhaseOperation(suOp, transformAnnotation.phase().getPhaseNumber()); + } else { + compilationUnit.addNewPhaseOperation(suOp, transformAnnotation.phase().getPhaseNumber()); + } + // GRECLIPSE add + } catch (Throwable t) { + // unexpected problem with the transformation. Could be: + // - problem instantiating the transformation class + compilationUnit.getErrorCollector().addError(new SimpleMessage( + "Unexpected problem with AST transform: " + t.getMessage(), null)); + } + // GRECLIPSE end + } else { + compilationUnit.getErrorCollector().addError(new SimpleMessage( + "Transform Class " + entry.getKey() + " specified at " + + entry.getValue().toExternalForm() + " is not an ASTTransformation.", null)); + } + } catch (Exception e) { + compilationUnit.getErrorCollector().addError(new SimpleMessage( + "Could not instantiate global transform class " + entry.getKey() + " specified at " + + entry.getValue().toExternalForm() + " because of exception " + e.toString(), null)); + } + } + } +} + diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/AnnotationCollectorTransform.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/AnnotationCollectorTransform.java new file mode 100644 index 0000000000..6c2c97f15a --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/AnnotationCollectorTransform.java @@ -0,0 +1,371 @@ +/* + * 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.transform; + +import groovy.transform.AnnotationCollector; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ArrayExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.syntax.SyntaxException; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import static groovyjarjarasm.asm.Opcodes.ACC_ABSTRACT; +import static groovyjarjarasm.asm.Opcodes.ACC_ANNOTATION; +import static groovyjarjarasm.asm.Opcodes.ACC_ENUM; +import static groovyjarjarasm.asm.Opcodes.ACC_FINAL; +import static groovyjarjarasm.asm.Opcodes.ACC_INTERFACE; +import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC; +import static groovyjarjarasm.asm.Opcodes.ACC_STATIC; + +/** + * This class is the base for any annotation alias processor. + * @see AnnotationCollector + * @see AnnotationCollectorTransform#visit(AnnotationNode, AnnotationNode, AnnotatedNode, SourceUnit) + * @author Jochen "blackdrag" Theodorou + */ +public class AnnotationCollectorTransform { + + private static List getMeta(ClassNode cn) { + List meta = cn.getNodeMetaData(AnnotationCollector.class); + if (meta == null) { + if (cn.isPrimaryClassNode()) { + meta = getTargetListFromAnnotations(cn); + } else { + meta = getTargetListFromClass(cn); + } + cn.setNodeMetaData(AnnotationCollector.class, meta); + } + return meta; + } + + /** + * Class used by {@link org.codehaus.groovy.control.CompilationUnit} to transform the alias class + * into what is needed by the compiler. This means removing invalid + * modifiers, interfaces and superclasses, as well as adding a static + * value method returning our serialized version of the data for processing + * from a pre-compiled state. By doing this the old annotations will be + * removed as well + * @author Jochen "blackdrag" Theodorou + */ + public static class ClassChanger { + + /** + * Method to transform the given ClassNode, if it is annotated with + * {@link AnnotationCollector}. See class description for what the + * transformation includes. + */ + public void transformClass(ClassNode cn) { + AnnotationNode collector = null; + for (ListIterator it = cn.getAnnotations().listIterator(); it.hasNext();) { + AnnotationNode an = it.next(); + if (an.getClassNode().getName().equals(AnnotationCollector.class.getName())) { + collector = an; + break; + } + } + if (collector == null) { + return; + } + // force final class, remove interface, annotation, enum and abstract modifiers + cn.setModifiers((ACC_FINAL+cn.getModifiers()) & ~(ACC_ENUM|ACC_INTERFACE|ACC_ANNOTATION|ACC_ABSTRACT)); + // force Object super class + cn.setSuperClass(ClassHelper.OBJECT_TYPE); + // force no interfaces implemented + cn.setInterfaces(ClassNode.EMPTY_ARRAY); + + // add static value():Object[][] method + List meta = getMeta(cn); + List outer = new ArrayList(meta.size()); + for (AnnotationNode an : meta) { + Expression serialized = serialize(an); + outer.add(serialized); + } + + ArrayExpression ae = new ArrayExpression(ClassHelper.OBJECT_TYPE.makeArray(), outer); + Statement code = new ReturnStatement(ae); + cn.addMethod( "value", ACC_PUBLIC+ACC_STATIC, + ClassHelper.OBJECT_TYPE.makeArray().makeArray(), + Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, + code); + + // remove annotations + for (ListIterator it = cn.getAnnotations().listIterator(); it.hasNext();) { + AnnotationNode an = it.next(); + if (an == collector) { + continue; + } + it.remove(); + } + } + + private Expression serialize(Expression e) { + if (e instanceof AnnotationConstantExpression) { + AnnotationConstantExpression ace = (AnnotationConstantExpression) e; + return serialize((AnnotationNode) ace.getValue()); + } else if (e instanceof ListExpression) { + boolean annotationConstant = false; + ListExpression le = (ListExpression) e; + List list = le.getExpressions(); + List newList = new ArrayList(list.size()); + for (Expression exp: list) { + annotationConstant = annotationConstant || exp instanceof AnnotationConstantExpression; + newList.add(serialize(exp)); + } + ClassNode type = ClassHelper.OBJECT_TYPE; + if (annotationConstant) type = type.makeArray(); + return new ArrayExpression(type, newList); + } + return e; + } + + private Expression serialize(AnnotationNode an) { + MapExpression map = new MapExpression(); + for (String key : an.getMembers().keySet()) { + map.addMapEntryExpression(new ConstantExpression(key), serialize(an.getMember(key))); + } + List l = new ArrayList(2); + l.add(new ClassExpression(an.getClassNode())); + // GRECLIPSE add + l.get(0).setSourcePosition(an.getClassNode()); + // GRECLIPSE end + l.add(map); + return new ArrayExpression(ClassHelper.OBJECT_TYPE, l); + } + } + + /** + * Adds a new syntax error to the source unit and then continues. + * + * @param message the message + * @param node the node for the error report + * @param source the source unit for the error report + */ + protected void addError(String message, ASTNode node, SourceUnit source) { + source.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage(new SyntaxException( + message, node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber() + ), source)); + } + + private List getTargetListFromValue(AnnotationNode collector, AnnotationNode aliasAnnotationUsage, SourceUnit source) { + Expression memberValue = collector.getMember("value"); + if (memberValue == null) { + return Collections.emptyList(); + } + if (!(memberValue instanceof ListExpression)) { + addError("Annotation collector expected a list of classes, but got a "+memberValue.getClass(), collector, source); + return Collections.emptyList(); + } + ListExpression memberListExp = (ListExpression) memberValue; + List memberList = memberListExp.getExpressions(); + if (memberList.isEmpty()) { + return Collections.emptyList(); + } + List ret = new ArrayList(); + for (Expression e : memberList) { + AnnotationNode toAdd = new AnnotationNode(e.getType()); + toAdd.setSourcePosition(aliasAnnotationUsage); + ret.add(toAdd); + } + return ret; + } + + private static List getStoredTargetList(AnnotationNode aliasAnnotationUsage, SourceUnit source) { + ClassNode alias = aliasAnnotationUsage.getClassNode().redirect(); + List ret = getMeta(alias); + return copy(ret, aliasAnnotationUsage); + } + + private static List copy(List orig, AnnotationNode aliasAnnotationUsage) { + if (orig.isEmpty()) return orig; + List ret = new ArrayList(orig.size()); + for (AnnotationNode an : orig) { + AnnotationNode newAn = new AnnotationNode(an.getClassNode()); + copyMembers(an, newAn); + newAn.setSourcePosition(aliasAnnotationUsage); + ret.add(newAn); + } + return ret; + } + + private static List getTargetListFromAnnotations(ClassNode alias) { + List annotations = alias.getAnnotations(); + if (annotations.size() < 2) { + return Collections.emptyList(); + } + List ret = new ArrayList(annotations.size()); + for (AnnotationNode an : annotations) { + ClassNode type = an.getClassNode(); + if (type.getName().equals(AnnotationCollector.class.getName())) continue; + AnnotationNode toAdd = new AnnotationNode(type); + copyMembers(an, toAdd); + ret.add(toAdd); + } + return ret; + } + + private static void copyMembers(final AnnotationNode from, final AnnotationNode to) { + Map members = from.getMembers(); + copyMembers(members, to); + } + + private static void copyMembers(final Map members, final AnnotationNode to) { + for (Map.Entry entry : members.entrySet()) { + to.addMember(entry.getKey(), entry.getValue()); + } + } + + private static List getTargetListFromClass(ClassNode alias) { + Class c = alias.getTypeClass(); + Object[][] data; + try { + Method m = c.getMethod("value"); + data = (Object[][]) m.invoke(null); + } catch (Exception e) { + throw new GroovyBugError(e); + } + return makeListOfAnnotations(data); + } + + private static List makeListOfAnnotations(Object[][] data) { + if (data.length == 0) { + return Collections.emptyList(); + } + List ret = new ArrayList(data.length); + for (Object[] inner : data) { + Class anno = (Class) inner[0]; + AnnotationNode toAdd = new AnnotationNode(ClassHelper.make(anno)); + ret.add(toAdd); + + Map member = (Map) inner[1]; + if (member.isEmpty()) { + continue; + } + Map generated = new HashMap(member.size()); + for (Map.Entry entry : member.entrySet()) { + generated.put(entry.getKey(), makeExpression(entry.getValue())); + } + copyMembers(generated, toAdd); + } + return ret; + } + + private static Expression makeExpression(Object o) { + if (o instanceof Class) { + return new ClassExpression(ClassHelper.make((Class) o)); + } + //TODO: value as Annotation here! + if (o instanceof Object[][]) { + List annotations = makeListOfAnnotations((Object[][])o); + ListExpression le = new ListExpression(); + for (AnnotationNode an : annotations) { + le.addExpression(new AnnotationConstantExpression(an)); + } + return le; + } else if (o instanceof Object[]) { + ListExpression le = new ListExpression(); + Object[] values = (Object[]) o; + for (Object val : values) { + le.addExpression(makeExpression(val)); + } + return le; + } + return new ConstantExpression(o,true); + } + + /** + * Returns a list of AnnotationNodes for the value attribute of the given + * AnnotationNode. + * + * @param collector the node containing the value member with the list + * @param source the source unit for error reporting + * @return a list of string constants + */ + protected List getTargetAnnotationList(AnnotationNode collector, AnnotationNode aliasAnnotationUsage, SourceUnit source) { + List stored = getStoredTargetList(aliasAnnotationUsage, source); + List targetList = getTargetListFromValue(collector, aliasAnnotationUsage, source); + int size = targetList.size()+stored.size(); + if (size == 0) { + return Collections.emptyList(); + } + List ret = new ArrayList(size); + ret.addAll(stored); + ret.addAll(targetList); + + return ret; + } + + /** + * Implementation method of the alias annotation processor. This method will + * get the list of annotations we aliased from the collector and adds it to + * aliasAnnotationUsage. The method will also map all members from + * aliasAnnotationUsage to the aliased nodes. Should a member stay unmapped, + * we will ad an error. Further processing of those members is done by the + * annotations. + * + * @param collector reference to the annotation with {@link AnnotationCollector} + * @param aliasAnnotationUsage reference to the place of usage of the alias + * @param aliasAnnotated reference to the node that has been annotated by the alias + * @param source source unit for error reporting + * @return list of the new AnnotationNodes + */ + public List visit(AnnotationNode collector, AnnotationNode aliasAnnotationUsage, AnnotatedNode aliasAnnotated, SourceUnit source) { + List ret = getTargetAnnotationList(collector, aliasAnnotationUsage, source); + Set unusedNames = new HashSet(aliasAnnotationUsage.getMembers().keySet()); + + for (AnnotationNode an: ret) { + for (String name : aliasAnnotationUsage.getMembers().keySet()) { + if (an.getClassNode().hasMethod(name, Parameter.EMPTY_ARRAY)) { + unusedNames.remove(name); + an.setMember(name, aliasAnnotationUsage.getMember(name)); + } + } + } + + if (!unusedNames.isEmpty()) { + String message = "Annotation collector got unmapped names "+unusedNames.toString()+"."; + addError(message, aliasAnnotationUsage, source); + } + + return ret; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/FieldASTTransformation.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/FieldASTTransformation.java new file mode 100644 index 0000000000..9e28258a6b --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/FieldASTTransformation.java @@ -0,0 +1,295 @@ +/* + * 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.transform; + +import groovy.cli.Option; +import groovy.lang.Lazy; +import groovy.transform.Field; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.VariableScope; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.classgen.VariableScopeVisitor; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.MetaClassHelper; +import groovyjarjarasm.asm.Opcodes; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import static org.codehaus.groovy.ast.ClassHelper.make; +import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.block; +import static org.codehaus.groovy.ast.tools.GeneralUtils.param; +import static org.codehaus.groovy.ast.tools.GeneralUtils.params; +import static org.codehaus.groovy.ast.tools.GeneralUtils.propX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; +import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; + +/** + * Handles transformation for the @Field annotation. + */ +@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) +public class FieldASTTransformation extends ClassCodeExpressionTransformer implements ASTTransformation, Opcodes { + + private static final Class MY_CLASS = Field.class; + private static final ClassNode MY_TYPE = make(MY_CLASS); + private static final ClassNode LAZY_TYPE = make(Lazy.class); + private static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage(); + private static final ClassNode ASTTRANSFORMCLASS_TYPE = make(GroovyASTTransformationClass.class); + private static final ClassNode OPTION_TYPE = make(Option.class); + private SourceUnit sourceUnit; + private DeclarationExpression candidate; + private boolean insideScriptBody; + private String variableName; + private FieldNode fieldNode; + private ClosureExpression currentClosure; + private ConstructorCallExpression currentAIC; + + public void visit(ASTNode[] nodes, SourceUnit source) { + sourceUnit = source; + if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) { + throw new GroovyBugError("Internal error: expecting [AnnotationNode, AnnotatedNode] but got: " + Arrays.asList(nodes)); + } + + AnnotatedNode parent = (AnnotatedNode) nodes[1]; + AnnotationNode node = (AnnotationNode) nodes[0]; + if (!MY_TYPE.equals(node.getClassNode())) return; + + if (parent instanceof DeclarationExpression) { + DeclarationExpression de = (DeclarationExpression) parent; + ClassNode cNode = de.getDeclaringClass(); + if (!cNode.isScript()) { + addError("Annotation " + MY_TYPE_NAME + " can only be used within a Script.", parent); + return; + } + candidate = de; + // GROOVY-4548: temp fix to stop CCE until proper support is added + if (de.isMultipleAssignmentDeclaration()) { + addError("Annotation " + MY_TYPE_NAME + " not supported with multiple assignment notation.", parent); + return; + } + VariableExpression ve = de.getVariableExpression(); + variableName = ve.getName(); + // set owner null here, it will be updated by addField + fieldNode = new FieldNode(variableName, ve.getModifiers(), ve.getType(), null, de.getRightExpression()); + fieldNode.setSourcePosition(de); + // GRECLIPSE add + fieldNode.setNameStart(ve.getStart()); + fieldNode.setNameEnd(ve.getEnd() - 1); + // GRECLIPSE end + cNode.addField(fieldNode); + // provide setter for CLI Builder purposes unless final + if (fieldNode.isFinal()) { + if (!de.getAnnotations(OPTION_TYPE).isEmpty()) { + addError("Can't have a final field also annotated with @" + OPTION_TYPE.getNameWithoutPackage(), de); + } + } else { + String setterName = "set" + MetaClassHelper.capitalize(variableName); + cNode.addMethod(setterName, ACC_PUBLIC | ACC_SYNTHETIC, ClassHelper.VOID_TYPE, params(param(ve.getType(), variableName)), ClassNode.EMPTY_ARRAY, block( + stmt(assignX(propX(varX("this"), variableName), varX(variableName))) + )); + } + + // GROOVY-4833 : annotations that are not Groovy transforms should be transferred to the generated field + // GROOVY-6112 : also copy acceptable Groovy transforms + final List annotations = de.getAnnotations(); + for (AnnotationNode annotation : annotations) { + // GROOVY-6337 HACK: in case newly created field is @Lazy + if (annotation.getClassNode().equals(LAZY_TYPE)) { + LazyASTTransformation.visitField(this, annotation, fieldNode); + } + final ClassNode annotationClassNode = annotation.getClassNode(); + if (notTransform(annotationClassNode) || acceptableTransform(annotation)) { + fieldNode.addAnnotation(annotation); + } + } + + super.visitClass(cNode); + // GROOVY-5207 So that Closures can see newly added fields + // (not super efficient for a very large class with many @Fields but we chose simplicity + // and understandability of this solution over more complex but efficient alternatives) + VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source); + scopeVisitor.visitClass(cNode); + } + } + + private static boolean acceptableTransform(AnnotationNode annotation) { + // TODO also check for phase after sourceUnit.getPhase()? but will be ignored anyway? + // TODO we should only copy those annotations with FIELD_TARGET but haven't visited annotations + // and gathered target info at this phase, so we can't do this: + // return annotation.isTargetAllowed(AnnotationNode.FIELD_TARGET); + // instead just don't copy ourselves for now + return !annotation.getClassNode().equals(MY_TYPE); + } + + private static boolean notTransform(ClassNode annotationClassNode) { + return annotationClassNode.getAnnotations(ASTTRANSFORMCLASS_TYPE).isEmpty(); + } + + @Override + public Expression transform(Expression expr) { + if (expr == null) return null; + if (expr instanceof DeclarationExpression) { + DeclarationExpression de = (DeclarationExpression) expr; + if (de.getLeftExpression() == candidate.getLeftExpression()) { + if (insideScriptBody) { + // TODO make EmptyExpression work + // partially works but not if only thing in script + // return EmptyExpression.INSTANCE; + return new ConstantExpression(null); + } + addError("Annotation " + MY_TYPE_NAME + " can only be used within a Script body.", expr); + return expr; + } + } else if (insideScriptBody && expr instanceof VariableExpression && currentClosure != null) { + VariableExpression ve = (VariableExpression) expr; + if (ve.getName().equals(variableName)) { + adjustToClassVar(ve); + return ve; + } + } else if (currentAIC != null && expr instanceof ArgumentListExpression) { + // if a match is found, the compiler will have already set up aic constructor to hav + // an argument which isn't needed since we'll be accessing the field; we must undo it + Expression skip = null; + List origArgList = ((ArgumentListExpression) expr).getExpressions(); + for (int i = 0; i < origArgList.size(); i++) { + Expression arg = origArgList.get(i); + if (matchesCandidate(arg)) { + skip = arg; + adjustConstructorAndFields(i, currentAIC.getType()); + break; + } + } + if (skip != null) { + return adjustedArgList(skip, origArgList); + } + } + return expr.transformExpression(this); + } + + private boolean matchesCandidate(Expression arg) { + return arg instanceof VariableExpression && ((VariableExpression) arg).getAccessedVariable() == candidate.getVariableExpression().getAccessedVariable(); + } + + private Expression adjustedArgList(Expression skip, List origArgs) { + List newArgs = new ArrayList(origArgs.size() - 1); + for (Expression origArg : origArgs) { + if (skip != origArg) { + newArgs.add(origArg); + } + } + return new ArgumentListExpression(newArgs); + } + + private void adjustConstructorAndFields(int skipIndex, ClassNode type) { + List constructors = type.getDeclaredConstructors(); + if (constructors.size() == 1) { + ConstructorNode constructor = constructors.get(0); + Parameter[] params = constructor.getParameters(); + Parameter[] newParams = new Parameter[params.length - 1]; + int to = 0; + for (int from = 0; from < params.length; from++) { + if (from != skipIndex) { + newParams[to++] = params[from]; + } + } + type.removeConstructor(constructor); + // code doesn't mention the removed param at this point, okay to leave as is + type.addConstructor(constructor.getModifiers(), newParams, constructor.getExceptions(), constructor.getCode()); + type.removeField(variableName); + } + } + + private void adjustToClassVar(VariableExpression expr) { + // we only need to check the variable name because the Groovy compiler + // already fails if a variable with the same name already exists in the scope. + // this means that a closure cannot shadow a class variable + expr.setAccessedVariable(fieldNode); + final VariableScope variableScope = currentClosure.getVariableScope(); + final Iterator iterator = variableScope.getReferencedLocalVariablesIterator(); + while (iterator.hasNext()) { + Variable next = iterator.next(); + if (next.getName().equals(variableName)) iterator.remove(); + } + variableScope.putReferencedClassVariable(fieldNode); + } + + @Override + public void visitClosureExpression(final ClosureExpression expression) { + ClosureExpression old = currentClosure; + currentClosure = expression; + super.visitClosureExpression(expression); + currentClosure = old; + } + + @Override + public void visitConstructorCallExpression(final ConstructorCallExpression cce) { + if (!insideScriptBody || !cce.isUsingAnonymousInnerClass()) return; + ConstructorCallExpression old = currentAIC; + currentAIC = cce; + Expression newArgs = transform(cce.getArguments()); + if (cce.getArguments() instanceof TupleExpression && newArgs instanceof TupleExpression) { + List argList = ((TupleExpression) cce.getArguments()).getExpressions(); + argList.clear(); + argList.addAll(((TupleExpression) newArgs).getExpressions()); + } + currentAIC = old; + } + + @Override + public void visitMethod(MethodNode node) { + Boolean oldInsideScriptBody = insideScriptBody; + if (node.isScriptBody()) insideScriptBody = true; + super.visitMethod(node); + insideScriptBody = oldInsideScriptBody; + } + + @Override + public void visitExpressionStatement(ExpressionStatement es) { + Expression exp = es.getExpression(); + exp.visit(this); + super.visitExpressionStatement(es); + } + + protected SourceUnit getSourceUnit() { + return sourceUnit; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/LogASTTransformation.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/LogASTTransformation.java new file mode 100644 index 0000000000..d381bf5a90 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/LogASTTransformation.java @@ -0,0 +1,302 @@ +/* + * 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.transform; + +import groovy.lang.GroovyClassLoader; +import groovy.transform.CompilationUnitAware; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.DynamicVariable; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.classgen.VariableScopeVisitor; +import org.codehaus.groovy.control.CompilationUnit; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * This class provides an AST Transformation to add a log field to a class. + * + * @author Guillaume Laforge + * @author Jochen Theodorou + * @author Dinko Srkoc + * @author Hamlet D'Arcy + * @author Raffaele Cigni + * @author Alberto Vilches Raton + * @author Tomasz Bujok + * @author Martin Ghados + * @author Matthias Cullmann + */ +@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) +public class LogASTTransformation extends AbstractASTTransformation implements CompilationUnitAware { + + /** + * This is just a dummy value used because String annotations values can not be null. + * It will be replaced by the fully qualified class name of the annotated class. + */ + public static final String DEFAULT_CATEGORY_NAME = "##default-category-name##"; + + private CompilationUnit compilationUnit; + + public void visit(ASTNode[] nodes, final SourceUnit source) { + init(nodes, source); + AnnotatedNode targetClass = (AnnotatedNode) nodes[1]; + AnnotationNode logAnnotation = (AnnotationNode) nodes[0]; + + final GroovyClassLoader classLoader = compilationUnit != null ? compilationUnit.getTransformLoader() : source.getClassLoader(); + final LoggingStrategy loggingStrategy = createLoggingStrategy(logAnnotation, classLoader); + if (loggingStrategy == null) return; + + final String logFieldName = lookupLogFieldName(logAnnotation); + + final String categoryName = lookupCategoryName(logAnnotation); + + if (!(targetClass instanceof ClassNode)) + throw new GroovyBugError("Class annotation " + logAnnotation.getClassNode().getName() + " annotated no Class, this must not happen."); + + final ClassNode classNode = (ClassNode) targetClass; + + ClassCodeExpressionTransformer transformer = new ClassCodeExpressionTransformer() { + private FieldNode logNode; + + @Override + protected SourceUnit getSourceUnit() { + return source; + } + + public Expression transform(Expression exp) { + if (exp == null) return null; + if (exp instanceof MethodCallExpression) { + return transformMethodCallExpression(exp); + } + if (exp instanceof ClosureExpression) { + return transformClosureExpression((ClosureExpression) exp); + } + return super.transform(exp); + } + + @Override + public void visitClass(ClassNode node) { + FieldNode logField = node.getField(logFieldName); + if (logField != null && logField.getOwner().equals(node)) { + addError("Class annotated with Log annotation cannot have log field declared", logField); + } else if (logField != null && !Modifier.isPrivate(logField.getModifiers())) { + addError("Class annotated with Log annotation cannot have log field declared because the field exists in the parent class: " + logField.getOwner().getName(), logField); + } else { + logNode = loggingStrategy.addLoggerFieldToClass(node, logFieldName, categoryName); + } + super.visitClass(node); + } + + private Expression transformClosureExpression(ClosureExpression exp) { + if (exp.getCode() instanceof BlockStatement) { + BlockStatement code = (BlockStatement) exp.getCode(); + super.visitBlockStatement(code); + } + return exp; + } + + private Expression transformMethodCallExpression(Expression exp) { + Expression modifiedCall = addGuard((MethodCallExpression) exp); + return modifiedCall == null ? super.transform(exp) : modifiedCall; + } + + private Expression addGuard(MethodCallExpression mce) { + // only add guard to methods of the form: logVar.logMethod(params) + if (!(mce.getObjectExpression() instanceof VariableExpression)) { + return null; + } + VariableExpression variableExpression = (VariableExpression) mce.getObjectExpression(); + if (!variableExpression.getName().equals(logFieldName) + || !(variableExpression.getAccessedVariable() instanceof DynamicVariable)) { + return null; + } + + String methodName = mce.getMethodAsString(); + if (methodName == null) return null; + if (!loggingStrategy.isLoggingMethod(methodName)) return null; + // also don't bother with guard if we have "simple" method args + // since there is no saving + if (usesSimpleMethodArgumentsOnly(mce)) return null; + + variableExpression.setAccessedVariable(logNode); + return loggingStrategy.wrapLoggingMethodCall(variableExpression, methodName, mce); + } + + private boolean usesSimpleMethodArgumentsOnly(MethodCallExpression mce) { + Expression arguments = mce.getArguments(); + if (arguments instanceof TupleExpression) { + TupleExpression tuple = (TupleExpression) arguments; + for (Expression exp : tuple.getExpressions()) { + if (!isSimpleExpression(exp)) return false; + } + return true; + } + return !isSimpleExpression(arguments); + } + + private boolean isSimpleExpression(Expression exp) { + if (exp instanceof ConstantExpression) return true; + if (exp instanceof VariableExpression) return true; + return false; + } + + }; + transformer.visitClass(classNode); + + // GROOVY-6373: references to 'log' field are normally already FieldNodes by now, so revisit scoping + new VariableScopeVisitor(sourceUnit, true).visitClass(classNode); + } + + private static String lookupLogFieldName(AnnotationNode logAnnotation) { + Expression member = logAnnotation.getMember("value"); + if (member != null && member.getText() != null) { + return member.getText(); + } else { + return "log"; + } + } + + private static String lookupCategoryName(AnnotationNode logAnnotation) { + Expression member = logAnnotation.getMember("category"); + if (member != null && member.getText() != null) { + return member.getText(); + } + return DEFAULT_CATEGORY_NAME; + } + + private static LoggingStrategy createLoggingStrategy(AnnotationNode logAnnotation, GroovyClassLoader loader) { + + String annotationName = logAnnotation.getClassNode().getName(); + + Class annotationClass; + try { + annotationClass = Class.forName(annotationName, false, loader); + } catch (Throwable e) { + throw new RuntimeException("Could not resolve class named " + annotationName); + } + + Method annotationMethod; + try { + annotationMethod = annotationClass.getDeclaredMethod("loggingStrategy", (Class[]) null); + } catch (Throwable e) { + throw new RuntimeException("Could not find method named loggingStrategy on class named " + annotationName); + } + + Object defaultValue; + try { + defaultValue = annotationMethod.getDefaultValue(); + } catch (Throwable e) { + throw new RuntimeException("Could not find default value of method named loggingStrategy on class named " + annotationName); + } + + if (!LoggingStrategy.class.isAssignableFrom((Class) defaultValue)) { + throw new RuntimeException("Default loggingStrategy value on class named " + annotationName + " is not a LoggingStrategy"); + } + + try { + Class strategyClass = (Class) defaultValue; + if (AbstractLoggingStrategy.class.isAssignableFrom(strategyClass)) { + return DefaultGroovyMethods.newInstance(strategyClass, new Object[]{loader}); + } else { + return strategyClass.newInstance(); + } + } catch (Exception e) { + return null; + } + } + + + /** + * A LoggingStrategy defines how to wire a new logger instance into an existing class. + * It is meant to be used with the @Log family of annotations to allow you to + * write your own Log annotation provider. + */ + public interface LoggingStrategy { + /** + * In this method, you are given a ClassNode, a field name and a category name, and you must add a new Field + * onto the class. Return the result of the ClassNode.addField operations. + * + * @param classNode the class that was originally annotated with the Log transformation. + * @param fieldName the name of the logger field + * @param categoryName the name of the logging category + * @return the FieldNode instance that was created and added to the class + */ + FieldNode addLoggerFieldToClass(ClassNode classNode, String fieldName, String categoryName); + + boolean isLoggingMethod(String methodName); + + String getCategoryName(ClassNode classNode, String categoryName); + + Expression wrapLoggingMethodCall(Expression logVariable, String methodName, Expression originalExpression); + } + + public abstract static class AbstractLoggingStrategy implements LoggingStrategy { + protected final GroovyClassLoader loader; + + protected AbstractLoggingStrategy(final GroovyClassLoader loader) { + this.loader = loader; + } + + protected AbstractLoggingStrategy() { + this(null); + } + + public String getCategoryName(ClassNode classNode, String categoryName) { + if (categoryName.equals(DEFAULT_CATEGORY_NAME)) { + return classNode.getName(); + } + return categoryName; + } + + protected ClassNode classNode(String name) { + ClassLoader cl = loader == null ? this.getClass().getClassLoader() : loader; + try { + return ClassHelper.make(Class.forName(name, false, cl)); + } catch (ClassNotFoundException e) { + // GRECLIPSE edit + //throw new GroovyRuntimeException("Unable to load logging class", e); + // don't throw an exception, rather just make the class from text + // See https://jira.codehaus.org/browse/GROOVY-5736 + // might be fixed in Groovy 2.0.5 and later + return ClassHelper.make(name); + // GRECLIPSE end + } + } + } + + public void setCompilationUnit(final CompilationUnit unit) { + this.compilationUnit = unit; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java new file mode 100644 index 0000000000..801ae48dd9 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java @@ -0,0 +1,542 @@ +/* + * 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.transform.sc; + +import groovy.lang.Reference; +import groovy.transform.CompileStatic; +import groovy.transform.TypeChecked; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.SpreadExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.tools.GeneralUtils; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.classgen.asm.InvocationWriter; +import org.codehaus.groovy.classgen.asm.MopWriter; +import org.codehaus.groovy.classgen.asm.TypeChooser; +import org.codehaus.groovy.classgen.asm.WriterControllerFactory; +import org.codehaus.groovy.classgen.asm.sc.StaticCompilationMopWriter; +import org.codehaus.groovy.classgen.asm.sc.StaticTypesTypeChooser; +import org.codehaus.groovy.control.CompilationFailedException; +import org.codehaus.groovy.control.CompilationUnit; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor; +import org.codehaus.groovy.transform.stc.StaticTypesMarker; +import groovyjarjarasm.asm.Opcodes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics; +import static org.codehaus.groovy.ast.tools.GenericsUtils.applyGenericsContextToPlaceHolders; +import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse; +import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec; +import static org.codehaus.groovy.ast.tools.GenericsUtils.extractSuperClassGenerics; +import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.BINARY_EXP_TARGET; +import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.COMPONENT_TYPE; +import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS; +import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS; +import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_FIELDS_MUTATORS; +import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.STATIC_COMPILE_NODE; +import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET; +import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC; +import static groovyjarjarasm.asm.Opcodes.ACC_STATIC; +import static groovyjarjarasm.asm.Opcodes.ACC_SYNTHETIC; + +/** + * This visitor is responsible for amending the AST with static compilation metadata or transform the AST so that + * a class or a method can be statically compiled. It may also throw errors specific to static compilation which + * are not considered as an error at the type check pass. For example, usage of spread operator is not allowed + * in statically compiled portions of code, while it may be statically checked. + * + * Static compilation relies on static type checking, which explains why this visitor extends the type checker + * visitor. + * + * @author Cedric Champeau + */ +public class StaticCompilationVisitor extends StaticTypeCheckingVisitor { + private static final ClassNode TYPECHECKED_CLASSNODE = ClassHelper.make(TypeChecked.class); + private static final ClassNode COMPILESTATIC_CLASSNODE = ClassHelper.make(CompileStatic.class); + private static final ClassNode[] TYPECHECKED_ANNOTATIONS = {TYPECHECKED_CLASSNODE, COMPILESTATIC_CLASSNODE}; + + public static final ClassNode ARRAYLIST_CLASSNODE = ClassHelper.make(ArrayList.class); + public static final MethodNode ARRAYLIST_CONSTRUCTOR; + public static final MethodNode ARRAYLIST_ADD_METHOD = ARRAYLIST_CLASSNODE.getMethod("add", new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE, "o")}); + + static { + ARRAYLIST_CONSTRUCTOR = new ConstructorNode(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE); + ARRAYLIST_CONSTRUCTOR.setDeclaringClass(StaticCompilationVisitor.ARRAYLIST_CLASSNODE); + } + + private final TypeChooser typeChooser = new StaticTypesTypeChooser(); + + private ClassNode classNode; + + public StaticCompilationVisitor(final SourceUnit unit, final ClassNode node) { + super(unit, node); + } + + @Override + protected ClassNode[] getTypeCheckingAnnotations() { + return TYPECHECKED_ANNOTATIONS; + } + + public static boolean isStaticallyCompiled(AnnotatedNode node) { + if (node.getNodeMetaData(STATIC_COMPILE_NODE)!=null) return (Boolean)node.getNodeMetaData(STATIC_COMPILE_NODE); + if (node instanceof MethodNode) { + return isStaticallyCompiled(node.getDeclaringClass()); + } + if (node instanceof InnerClassNode) { + return isStaticallyCompiled(((InnerClassNode)node).getOuterClass()); + } + return false; + } + + private void addPrivateFieldAndMethodAccessors(ClassNode node) { + addPrivateBridgeMethods(node); + addPrivateFieldsAccessors(node); + Iterator it = node.getInnerClasses(); + while (it.hasNext()) { + addPrivateFieldAndMethodAccessors(it.next()); + } + } + + private void addDynamicOuterClassAccessorsCallback(final ClassNode outer) { + if (outer != null && !isStaticallyCompiled(outer) + && outer.getNodeMetaData(StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK) == null) { + outer.putNodeMetaData(StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK, new CompilationUnit.PrimaryClassNodeOperation() { + @Override + public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { + if (classNode == outer) { + addPrivateBridgeMethods(classNode); + addPrivateFieldsAccessors(classNode); + } + } + }); + } + } + + @Override + public void visitClass(final ClassNode node) { + boolean skip = shouldSkipClassNode(node); + if (!skip && !anyMethodSkip(node)) { + node.putNodeMetaData(MopWriter.Factory.class, StaticCompilationMopWriter.FACTORY); + } + ClassNode oldCN = classNode; + classNode = node; + Iterator innerClasses = classNode.getInnerClasses(); + while (innerClasses.hasNext()) { + InnerClassNode innerClassNode = innerClasses.next(); + boolean innerStaticCompile = !(skip || isSkippedInnerClass(innerClassNode)); + innerClassNode.putNodeMetaData(STATIC_COMPILE_NODE, innerStaticCompile); + innerClassNode.putNodeMetaData(WriterControllerFactory.class, node.getNodeMetaData(WriterControllerFactory.class)); + if (innerStaticCompile && !anyMethodSkip(innerClassNode)) { + innerClassNode.putNodeMetaData(MopWriter.Factory.class, StaticCompilationMopWriter.FACTORY); + } + } + super.visitClass(node); + addPrivateFieldAndMethodAccessors(node); + if (isStaticallyCompiled(node)) addDynamicOuterClassAccessorsCallback(node.getOuterClass()); + classNode = oldCN; + } + + private boolean anyMethodSkip(final ClassNode node) { + for (MethodNode methodNode : node.getMethods()) { + if (isSkipMode(methodNode)) return true; + } + return false; + } + + /** + * If we are in a constructor, that is static compiled, but in a class, that + * is not, it may happen that init code from object initializers, fields + * or properties is added into the constructor code. The backend assumes + * a purely static constructor, so it may fail if it encounters dynamic + * code here. Thus we make this kind of code fail + */ + private void checkForConstructorWithCSButClassWithout(MethodNode node) { + if (!(node instanceof ConstructorNode)) return; + Object meta = node.getNodeMetaData(STATIC_COMPILE_NODE); + if (!Boolean.TRUE.equals(meta)) return; + ClassNode clz = typeCheckingContext.getEnclosingClassNode(); + meta = clz.getNodeMetaData(STATIC_COMPILE_NODE); + if (Boolean.TRUE.equals(meta)) return; + if ( clz.getObjectInitializerStatements().isEmpty() && + clz.getFields().isEmpty() && + clz.getProperties().isEmpty()) + { + return; + } + + addStaticTypeError("Cannot statically compile constructor implicitly including non static elements from object initializers, properties or fields.",node); + } + + @Override + public void visitMethod(final MethodNode node) { + if (isSkipMode(node)) { + node.putNodeMetaData(STATIC_COMPILE_NODE, false); + } + super.visitMethod(node); + checkForConstructorWithCSButClassWithout(node); + if (isStaticallyCompiled(node)) addDynamicOuterClassAccessorsCallback(node.getDeclaringClass()); + } + + /** + * Adds special accessors and mutators for private fields so that inner classes can get/set them + */ + private static void addPrivateFieldsAccessors(ClassNode node) { + Set accessedFields = (Set) node.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS); + Set mutatedFields = (Set) node.getNodeMetaData(StaticTypesMarker.PV_FIELDS_MUTATION); + if (accessedFields == null && mutatedFields == null) return; + Map privateFieldAccessors = (Map) node.getNodeMetaData(PRIVATE_FIELDS_ACCESSORS); + Map privateFieldMutators = (Map) node.getNodeMetaData(PRIVATE_FIELDS_MUTATORS); + if (privateFieldAccessors != null || privateFieldMutators != null) { + // already added + return; + } + int acc = -1; + privateFieldAccessors = accessedFields != null ? new HashMap() : null; + privateFieldMutators = mutatedFields != null ? new HashMap() : null; + final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC; + for (FieldNode fieldNode : node.getFields()) { + boolean generateAccessor = accessedFields != null && accessedFields.contains(fieldNode); + boolean generateMutator = mutatedFields != null && mutatedFields.contains(fieldNode); + if (generateAccessor) { + acc++; + Parameter param = new Parameter(node.getPlainNodeReference(), "$that"); + Expression receiver = fieldNode.isStatic() ? new ClassExpression(node) : new VariableExpression(param); + Statement stmt = new ExpressionStatement(new PropertyExpression( + receiver, + fieldNode.getName() + )); + MethodNode accessor = node.addMethod("pfaccess$" + acc, access, fieldNode.getOriginType(), new Parameter[]{param}, ClassNode.EMPTY_ARRAY, stmt); + privateFieldAccessors.put(fieldNode.getName(), accessor); + } + + if (generateMutator) { + //increment acc if it hasn't been incremented in the current iteration + if (!generateAccessor) acc++; + Parameter param = new Parameter(node.getPlainNodeReference(), "$that"); + Expression receiver = fieldNode.isStatic() ? new ClassExpression(node) : new VariableExpression(param); + Parameter value = new Parameter(fieldNode.getOriginType(), "$value"); + Statement stmt = GeneralUtils.assignS( + new PropertyExpression(receiver, fieldNode.getName()), + new VariableExpression(value) + ); + MethodNode mutator = node.addMethod("pfaccess$0" + acc, access, fieldNode.getOriginType(), new Parameter[]{param, value}, ClassNode.EMPTY_ARRAY, stmt); + privateFieldMutators.put(fieldNode.getName(), mutator); + } + } + if (privateFieldAccessors != null) node.setNodeMetaData(PRIVATE_FIELDS_ACCESSORS, privateFieldAccessors); + if (privateFieldMutators != null) node.setNodeMetaData(PRIVATE_FIELDS_MUTATORS, privateFieldMutators); + } + + /** + * This method is used to add "bridge" methods for private methods of an inner/outer + * class, so that the outer class is capable of calling them. It does basically + * the same job as access$000 like methods in Java. + * + * @param node an inner/outer class node for which to generate bridge methods + */ + private static void addPrivateBridgeMethods(final ClassNode node) { + Set accessedMethods = (Set) node.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS); + if (accessedMethods==null) return; + List methods = new ArrayList(node.getAllDeclaredMethods()); + methods.addAll(node.getDeclaredConstructors()); + Map privateBridgeMethods = (Map) node.getNodeMetaData(PRIVATE_BRIDGE_METHODS); + if (privateBridgeMethods!=null) { + // private bridge methods already added + return; + } + privateBridgeMethods = new HashMap(); + int i=-1; + final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC; + for (MethodNode method : methods) { + if (accessedMethods.contains(method)) { + List methodSpecificGenerics = methodSpecificGenerics(method); + i++; + ClassNode declaringClass = method.getDeclaringClass(); + Map genericsSpec = createGenericsSpec(node); + genericsSpec = addMethodGenerics(method, genericsSpec); + extractSuperClassGenerics(node, declaringClass, genericsSpec); + Parameter[] methodParameters = method.getParameters(); + Parameter[] newParams = new Parameter[methodParameters.length+1]; + for (int j = 1; j < newParams.length; j++) { + Parameter orig = methodParameters[j-1]; + newParams[j] = new Parameter( + correctToGenericsSpecRecurse(genericsSpec, orig.getOriginType(), methodSpecificGenerics), + orig.getName() + ); + } + Expression arguments; + if (method.getParameters()==null || method.getParameters().length==0) { + arguments = ArgumentListExpression.EMPTY_ARGUMENTS; + } else { + List args = new LinkedList(); + for (Parameter parameter : methodParameters) { + args.add(new VariableExpression(parameter)); + } + arguments = new ArgumentListExpression(args); + } + + MethodNode bridge; + if (method instanceof ConstructorNode) { + // create constructor with a nested class as the first parameter, creating one if necessary + ClassNode thatType = null; + Iterator innerClasses = node.getInnerClasses(); + if (innerClasses.hasNext()) { + thatType = innerClasses.next(); + } else { + thatType = new InnerClassNode(node.redirect(), node.getName() + "$1", ACC_STATIC | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE); + node.getModule().addClass(thatType); + } + newParams[0] = new Parameter(thatType.getPlainNodeReference(), "$that"); + Expression cce = new ConstructorCallExpression(ClassNode.THIS, arguments); + Statement body = new ExpressionStatement(cce); + bridge = node.addConstructor(ACC_SYNTHETIC, newParams, ClassNode.EMPTY_ARRAY, body); + } else { + newParams[0] = new Parameter(node.getPlainNodeReference(), "$that"); + Expression receiver = method.isStatic()?new ClassExpression(node):new VariableExpression(newParams[0]); + MethodCallExpression mce = new MethodCallExpression(receiver, method.getName(), arguments); + mce.setMethodTarget(method); + + ExpressionStatement returnStatement = new ExpressionStatement(mce); + bridge = node.addMethod( + "access$"+i, access, + correctToGenericsSpecRecurse(genericsSpec, method.getReturnType(), methodSpecificGenerics), + newParams, + method.getExceptions(), + returnStatement); + } + GenericsType[] origGenericsTypes = method.getGenericsTypes(); + if (origGenericsTypes !=null) { + bridge.setGenericsTypes(applyGenericsContextToPlaceHolders(genericsSpec,origGenericsTypes)); + } + privateBridgeMethods.put(method, bridge); + bridge.addAnnotation(new AnnotationNode(COMPILESTATIC_CLASSNODE)); + } + } + if (!privateBridgeMethods.isEmpty()) { + node.setNodeMetaData(PRIVATE_BRIDGE_METHODS, privateBridgeMethods); + } + } + + private static List methodSpecificGenerics(final MethodNode method) { + List genericTypeTokens = new ArrayList(); + GenericsType[] candidateGenericsTypes = method.getGenericsTypes(); + if (candidateGenericsTypes != null) { + for (GenericsType gt : candidateGenericsTypes) { + genericTypeTokens.add(gt.getName()); + } + } + return genericTypeTokens; + } + + private static void memorizeInitialExpressions(final MethodNode node) { + // add node metadata for default parameters because they are erased by the Verifier + if (node.getParameters()!=null) { + for (Parameter parameter : node.getParameters()) { + parameter.putNodeMetaData(StaticTypesMarker.INITIAL_EXPRESSION, parameter.getInitialExpression()); + } + } + } + + @Override + public void visitSpreadExpression(final SpreadExpression expression) { + } + + @Override + public void visitMethodCallExpression(final MethodCallExpression call) { + super.visitMethodCallExpression(call); + + MethodNode target = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET); + if (target!=null) { + call.setMethodTarget(target); + memorizeInitialExpressions(target); + } + + if (call.getMethodTarget()==null && call.getLineNumber()>0) { + addError("Target method for method call expression hasn't been set", call); + } + + } + + @Override + public void visitConstructorCallExpression(final ConstructorCallExpression call) { + super.visitConstructorCallExpression(call); + + MethodNode target = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET); + if (target==null && call.getLineNumber()>0) { + addError("Target constructor for constructor call expression hasn't been set", call); + } else { + if (target==null) { + // try to find a target + ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(call.getArguments()); + List expressions = argumentListExpression.getExpressions(); + ClassNode[] args = new ClassNode[expressions.size()]; + for (int i = 0; i < args.length; i++) { + args[i] = typeChooser.resolveType(expressions.get(i), classNode); + } + MethodNode constructor = findMethodOrFail(call, call.isSuperCall() ? classNode.getSuperClass() : classNode, "", args); + call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, constructor); + target = constructor; + } + } + if (target!=null) { + memorizeInitialExpressions(target); + } + } + + @Override + public void visitForLoop(final ForStatement forLoop) { + super.visitForLoop(forLoop); + Expression collectionExpression = forLoop.getCollectionExpression(); + if (!(collectionExpression instanceof ClosureListExpression)) { + final ClassNode collectionType = getType(forLoop.getCollectionExpression()); + ClassNode componentType = inferLoopElementType(collectionType); + forLoop.getVariable().setType(componentType); + // GRECLIPSE edit -- preserve origin type for code select + //forLoop.getVariable().setOriginType(componentType); + // GRECLIPSE end + } + } + + @Override + protected MethodNode findMethodOrFail(final Expression expr, final ClassNode receiver, final String name, final ClassNode... args) { + MethodNode methodNode = super.findMethodOrFail(expr, receiver, name, args); + if (expr instanceof BinaryExpression && methodNode!=null) { + expr.putNodeMetaData(BINARY_EXP_TARGET, new Object[] {methodNode, name}); + } + return methodNode; + } + + @Override + protected boolean existsProperty(final PropertyExpression pexp, final boolean checkForReadOnly, final ClassCodeVisitorSupport visitor) { + Expression objectExpression = pexp.getObjectExpression(); + ClassNode objectExpressionType = getType(objectExpression); + final Reference rType = new Reference(objectExpressionType); + ClassCodeVisitorSupport receiverMemoizer = new ClassCodeVisitorSupport() { + @Override + protected SourceUnit getSourceUnit() { + return null; + } + + public void visitField(final FieldNode node) { + if (visitor!=null) visitor.visitField(node); + ClassNode declaringClass = node.getDeclaringClass(); + if (declaringClass!=null) { + if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, ClassHelper.LIST_TYPE)) { + boolean spread = declaringClass.getDeclaredField(node.getName()) != node; + pexp.setSpreadSafe(spread); + } + rType.set(declaringClass); + } + } + + public void visitMethod(final MethodNode node) { + if (visitor!=null) visitor.visitMethod(node); + ClassNode declaringClass = node.getDeclaringClass(); + if (declaringClass!=null){ + if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, ClassHelper.LIST_TYPE)) { + List properties = declaringClass.getDeclaredMethods(node.getName()); + boolean spread = true; + for (MethodNode mn : properties) { + if (node==mn) { + spread = false; + break; + } + } + // it's no real property but a property of the component + pexp.setSpreadSafe(spread); + } + rType.set(declaringClass); + } + } + + @Override + public void visitProperty(final PropertyNode node) { + if (visitor!=null) visitor.visitProperty(node); + ClassNode declaringClass = node.getDeclaringClass(); + if (declaringClass!=null) { + if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, ClassHelper.LIST_TYPE)) { + List properties = declaringClass.getProperties(); + boolean spread = true; + for (PropertyNode propertyNode : properties) { + if (propertyNode==node) { + spread = false; + break; + } + } + // it's no real property but a property of the component + pexp.setSpreadSafe(spread); + } + rType.set(declaringClass); + } + } + }; + boolean exists = super.existsProperty(pexp, checkForReadOnly, receiverMemoizer); + if (exists) { + if (objectExpression.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER)==null) { + objectExpression.putNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER, rType.get()); + } + if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(objectExpressionType, ClassHelper.LIST_TYPE)) { + objectExpression.putNodeMetaData(COMPONENT_TYPE, inferComponentType(objectExpressionType, ClassHelper.int_TYPE)); + } + } + return exists; + } + + @Override + public void visitPropertyExpression(final PropertyExpression pexp) { + super.visitPropertyExpression(pexp); + Object dynamic = pexp.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION); + if (dynamic !=null) { + pexp.getObjectExpression().putNodeMetaData(StaticCompilationMetadataKeys.RECEIVER_OF_DYNAMIC_PROPERTY, dynamic); + } + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java new file mode 100644 index 0000000000..a5b5f5d6b3 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/sc/transformers/MethodCallExpressionTransformer.java @@ -0,0 +1,201 @@ +/* + * 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.transform.sc.transformers; + +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.classgen.asm.MopWriter; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.transform.stc.ExtensionMethodNode; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor; +import org.codehaus.groovy.transform.stc.StaticTypesMarker; + +import java.util.List; + +import static groovyjarjarasm.asm.Opcodes.ACC_PUBLIC; +import static groovyjarjarasm.asm.Opcodes.ACC_SYNTHETIC; + +public class MethodCallExpressionTransformer { + private static final ClassNode DGM_CLASSNODE = ClassHelper.make(DefaultGroovyMethods.class); + + private final StaticCompilationTransformer staticCompilationTransformer; + + public MethodCallExpressionTransformer(StaticCompilationTransformer staticCompilationTransformer) { + this.staticCompilationTransformer = staticCompilationTransformer; + } + + Expression transformMethodCallExpression(final MethodCallExpression expr) { + Expression trn = tryTransformIsToCompareIdentity(expr); + if (trn!=null) { + return trn; + } + ClassNode superCallReceiver = expr.getNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED); + if (superCallReceiver!=null) { + return transformMethodCallExpression(transformToMopSuperCall(superCallReceiver, expr)); + } + Expression objectExpression = expr.getObjectExpression(); + ClassNode type = staticCompilationTransformer.getTypeChooser().resolveType(objectExpression, staticCompilationTransformer.getClassNode()); + if (isCallOnClosure(expr)) { + FieldNode field = staticCompilationTransformer.getClassNode().getField(expr.getMethodAsString()); + if (field != null) { + VariableExpression vexp = new VariableExpression(field); + MethodCallExpression result = new MethodCallExpression( + vexp, + "call", + staticCompilationTransformer.transform(expr.getArguments()) + ); + result.setImplicitThis(false); + result.setSourcePosition(expr); + result.setSafe(expr.isSafe()); + result.setSpreadSafe(expr.isSpreadSafe()); + result.setMethodTarget(StaticTypeCheckingVisitor.CLOSURE_CALL_VARGS); + result.copyNodeMetaData(expr); + return result; + } + } + if (type != null && type.isArray()) { + String method = expr.getMethodAsString(); + ClassNode componentType = type.getComponentType(); + if ("getAt".equals(method)) { + Expression arguments = expr.getArguments(); + if (arguments instanceof TupleExpression) { + List argList = ((TupleExpression) arguments).getExpressions(); + if (argList.size() == 1) { + Expression indexExpr = argList.get(0); + ClassNode argType = staticCompilationTransformer.getTypeChooser().resolveType(indexExpr, staticCompilationTransformer.getClassNode()); + ClassNode indexType = ClassHelper.getWrapper(argType); + if (componentType.isEnum() && ClassHelper.Number_TYPE == indexType) { + // workaround for generated code in enums which use .next() returning a Number + indexType = ClassHelper.Integer_TYPE; + } + if (argType != null && ClassHelper.Integer_TYPE == indexType) { + BinaryExpression binaryExpression = new BinaryExpression( + objectExpression, + Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()), + indexExpr + ); + binaryExpression.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType); + return staticCompilationTransformer.transform(binaryExpression); + } + } + } + } else if ("putAt".equals(method)) { + Expression arguments = expr.getArguments(); + if (arguments instanceof TupleExpression) { + List argList = ((TupleExpression) arguments).getExpressions(); + if (argList.size() == 2) { + Expression indexExpr = argList.get(0); + Expression objExpr = argList.get(1); + ClassNode argType = staticCompilationTransformer.getTypeChooser().resolveType(indexExpr, staticCompilationTransformer.getClassNode()); + if (argType != null && ClassHelper.Integer_TYPE == ClassHelper.getWrapper(argType)) { + BinaryExpression arrayGet = new BinaryExpression( + objectExpression, + Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()), + indexExpr + ); + arrayGet.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType); + BinaryExpression assignment = new BinaryExpression( + arrayGet, + Token.newSymbol("=", objExpr.getLineNumber(), objExpr.getColumnNumber()), + objExpr + ); + return staticCompilationTransformer.transform(assignment); + } + } + } + } + } + return staticCompilationTransformer.superTransform(expr); + } + + private static MethodCallExpression transformToMopSuperCall(final ClassNode superCallReceiver, final MethodCallExpression expr) { + MethodNode mn = expr.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + String mopName = MopWriter.getMopMethodName(mn, false); + MethodNode direct = new MethodNode( + mopName, + ACC_PUBLIC | ACC_SYNTHETIC, + mn.getReturnType(), + mn.getParameters(), + mn.getExceptions(), + EmptyStatement.INSTANCE + ); + direct.setDeclaringClass(superCallReceiver); + MethodCallExpression result = new MethodCallExpression( + new VariableExpression("this"), + mopName, + expr.getArguments() + ); + // GRECLIPSE add + result.getObjectExpression().putNodeMetaData(ClassCodeVisitorSupport.ORIGINAL_EXPRESSION, expr.getObjectExpression()); + // GRECLIPSE end + result.setImplicitThis(true); + result.setSpreadSafe(false); + result.setSafe(false); + result.setSourcePosition(expr); + result.setMethodTarget(direct); + return result; + } + + private static boolean isCallOnClosure(final MethodCallExpression expr) { + MethodNode target = expr.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + return expr.isImplicitThis() + && !"call".equals(expr.getMethodAsString()) + && (target == StaticTypeCheckingVisitor.CLOSURE_CALL_VARGS + || target == StaticTypeCheckingVisitor.CLOSURE_CALL_NO_ARG + || target == StaticTypeCheckingVisitor.CLOSURE_CALL_ONE_ARG); + } + + /** + * Identifies a method call expression on {@link DefaultGroovyMethods#is(Object, Object)} and if recognized, transforms it into a {@link CompareIdentityExpression}. + * @param call a method call to be transformed + * @return null if the method call is not DGM#is, or {@link CompareIdentityExpression} + */ + private static Expression tryTransformIsToCompareIdentity(MethodCallExpression call) { + if (call.isSafe()) return null; + MethodNode methodTarget = call.getMethodTarget(); + if (methodTarget instanceof ExtensionMethodNode && "is".equals(methodTarget.getName()) && methodTarget.getParameters().length==1) { + methodTarget = ((ExtensionMethodNode) methodTarget).getExtensionMethodNode(); + ClassNode owner = methodTarget.getDeclaringClass(); + if (DGM_CLASSNODE.equals(owner)) { + Expression args = call.getArguments(); + if (args instanceof ArgumentListExpression) { + ArgumentListExpression arguments = (ArgumentListExpression) args; + List exprs = arguments.getExpressions(); + if (exprs.size() == 1) { + CompareIdentityExpression cid = new CompareIdentityExpression(call.getObjectExpression(), exprs.get(0)); + cid.setSourcePosition(call); + return cid; + } + } + } + } + return null; + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java new file mode 100644 index 0000000000..1d89534313 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -0,0 +1,5204 @@ +/* + * 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.transform.stc; + +import groovy.lang.Closure; +import groovy.lang.DelegatesTo; +import groovy.lang.IntRange; +import groovy.lang.Range; +import groovy.transform.TypeChecked; +import groovy.transform.TypeCheckingMode; +import groovy.transform.stc.ClosureParams; +import groovy.transform.stc.ClosureSignatureConflictResolver; +import groovy.transform.stc.ClosureSignatureHint; +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.DynamicVariable; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.AttributeExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.EmptyExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.MethodCall; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PostfixExpression; +import org.codehaus.groovy.ast.expr.PrefixExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.SpreadExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.TernaryExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.UnaryMinusExpression; +import org.codehaus.groovy.ast.expr.UnaryPlusExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.EmptyStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.ast.tools.GenericsUtils; +import org.codehaus.groovy.ast.tools.WideningCategories; +import org.codehaus.groovy.classgen.ReturnAdder; +import org.codehaus.groovy.classgen.asm.InvocationWriter; +import org.codehaus.groovy.control.CompilationUnit; +import org.codehaus.groovy.control.ErrorCollector; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.messages.SyntaxErrorMessage; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.codehaus.groovy.runtime.MetaClassHelper; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.TokenUtil; +import org.codehaus.groovy.transform.StaticTypesTransformation; +import org.codehaus.groovy.transform.trait.Traits; +import org.codehaus.groovy.util.ListHashMap; +import groovyjarjarasm.asm.Opcodes; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import static org.codehaus.groovy.ast.ClassHelper.BigDecimal_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.BigInteger_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Boolean_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Byte_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.CLASS_Type; +import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Character_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.DYNAMIC_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Double_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Float_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.GROOVY_OBJECT_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.GSTRING_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Iterator_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Number_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.PATTERN_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.RANGE_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.Short_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.char_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.double_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.findSAM; +import static org.codehaus.groovy.ast.ClassHelper.float_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.getNextSuperClass; +import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper; +import static org.codehaus.groovy.ast.ClassHelper.getWrapper; +import static org.codehaus.groovy.ast.ClassHelper.int_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.isNumberType; +import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType; +import static org.codehaus.groovy.ast.ClassHelper.isSAMType; +import static org.codehaus.groovy.ast.ClassHelper.long_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.make; +import static org.codehaus.groovy.ast.ClassHelper.short_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE; +import static org.codehaus.groovy.ast.tools.GeneralUtils.args; +import static org.codehaus.groovy.ast.tools.GeneralUtils.binX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.castX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; +import static org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode; +import static org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.isBigIntCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.isDouble; +import static org.codehaus.groovy.ast.tools.WideningCategories.isDoubleCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.isFloat; +import static org.codehaus.groovy.ast.tools.WideningCategories.isFloatingCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.isIntCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory; +import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound; +import static org.codehaus.groovy.syntax.Types.ASSIGN; +import static org.codehaus.groovy.syntax.Types.ASSIGNMENT_OPERATOR; +import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL; +import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL; +import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_IN; +import static org.codehaus.groovy.syntax.Types.COMPARE_TO; +import static org.codehaus.groovy.syntax.Types.DIVIDE; +import static org.codehaus.groovy.syntax.Types.DIVIDE_EQUAL; +import static org.codehaus.groovy.syntax.Types.ELVIS_EQUAL; +import static org.codehaus.groovy.syntax.Types.EQUAL; +import static org.codehaus.groovy.syntax.Types.FIND_REGEX; +import static org.codehaus.groovy.syntax.Types.INTDIV; +import static org.codehaus.groovy.syntax.Types.INTDIV_EQUAL; +import static org.codehaus.groovy.syntax.Types.KEYWORD_IN; +import static org.codehaus.groovy.syntax.Types.KEYWORD_INSTANCEOF; +import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET; +import static org.codehaus.groovy.syntax.Types.MINUS_MINUS; +import static org.codehaus.groovy.syntax.Types.MOD; +import static org.codehaus.groovy.syntax.Types.MOD_EQUAL; +import static org.codehaus.groovy.syntax.Types.PLUS_PLUS; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.ArrayList_TYPE; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Collection_TYPE; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Matcher_TYPE; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.NUMBER_OPS; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.UNKNOWN_PARAMETER_TYPE; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.addMethodLevelDeclaredGenerics; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.allParametersAndArgumentsMatch; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsConnections; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsContext; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsContextToParameterClass; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.boundUnboundedWildcards; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.checkCompatibleAssignmentTypes; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.checkPossibleLossOfPrecision; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.chooseBestMethod; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.evaluateExpression; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extractGenericsConnections; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.extractGenericsParameterMapOfThis; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findSetters; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findTargetVariable; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.fullyResolveType; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getGenericsWithoutArray; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getOperationName; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isArrayOp; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isAssignableTo; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isAssignment; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBeingCompiled; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBitOperator; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isBoolIntrinsicOp; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isClassClassNodeWrappingConcreteType; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isCompareToBoolean; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isOperationInGroup; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isParameterizedWithGStringOrGStringString; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isParameterizedWithString; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isPowerOperator; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isShiftOperation; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isTraitSelf; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUsingGenericsOrIsArrayUsingGenerics; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isVargs; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isWildcardLeftHandSide; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.lastArgMatchesVarg; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.missesGenericsTypes; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.prettyPrintType; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.resolveClassNodeGenerics; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.toMethodParametersString; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.typeCheckMethodArgumentWithGenerics; +import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.typeCheckMethodsWithGenerics; + +/** + * The main class code visitor responsible for static type checking. It will perform various inspections like checking + * assignment types, type inference, ... Eventually, class nodes may be annotated with inferred type information. + */ +public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { + + private static final boolean DEBUG_GENERATED_CODE = Boolean.valueOf(System.getProperty("groovy.stc.debug", "false")); + private static final AtomicLong UNIQUE_LONG = new AtomicLong(); + + protected static final Object ERROR_COLLECTOR = ErrorCollector.class; + protected static final ClassNode ITERABLE_TYPE = ClassHelper.make(Iterable.class); + protected static final List EMPTY_METHODNODE_LIST = Collections.emptyList(); + protected static final ClassNode TYPECHECKED_CLASSNODE = ClassHelper.make(TypeChecked.class); + protected static final ClassNode[] TYPECHECKING_ANNOTATIONS = new ClassNode[]{TYPECHECKED_CLASSNODE}; + protected static final ClassNode TYPECHECKING_INFO_NODE = ClassHelper.make(TypeChecked.TypeCheckingInfo.class); + protected static final ClassNode DGM_CLASSNODE = ClassHelper.make(DefaultGroovyMethods.class); + protected static final int CURRENT_SIGNATURE_PROTOCOL_VERSION = 1; + protected static final Expression CURRENT_SIGNATURE_PROTOCOL = new ConstantExpression(CURRENT_SIGNATURE_PROTOCOL_VERSION, true); + protected static final MethodNode GET_DELEGATE = CLOSURE_TYPE.getGetterMethod("getDelegate"); + protected static final MethodNode GET_OWNER = CLOSURE_TYPE.getGetterMethod("getOwner"); + protected static final MethodNode GET_THISOBJECT = CLOSURE_TYPE.getGetterMethod("getThisObject"); + protected static final ClassNode DELEGATES_TO = ClassHelper.make(DelegatesTo.class); + protected static final ClassNode DELEGATES_TO_TARGET = ClassHelper.make(DelegatesTo.Target.class); + protected static final ClassNode LINKEDHASHMAP_CLASSNODE = make(LinkedHashMap.class); + protected static final ClassNode CLOSUREPARAMS_CLASSNODE = make(ClosureParams.class); + protected static final ClassNode MAP_ENTRY_TYPE = make(Map.Entry.class); + protected static final ClassNode ENUMERATION_TYPE = make(Enumeration.class); + + public static final Statement GENERATED_EMPTY_STATEMENT = EmptyStatement.INSTANCE; + + public static final MethodNode CLOSURE_CALL_NO_ARG; + public static final MethodNode CLOSURE_CALL_ONE_ARG; + public static final MethodNode CLOSURE_CALL_VARGS; + + public static final String CALL = "call"; + + static { + // Cache closure call methods + CLOSURE_CALL_NO_ARG = CLOSURE_TYPE.getDeclaredMethod(CALL, Parameter.EMPTY_ARRAY); + CLOSURE_CALL_ONE_ARG = CLOSURE_TYPE.getDeclaredMethod(CALL, new Parameter[]{ + new Parameter(OBJECT_TYPE, "arg") + }); + CLOSURE_CALL_VARGS = CLOSURE_TYPE.getDeclaredMethod(CALL, new Parameter[]{ + new Parameter(OBJECT_TYPE.makeArray(), "args") + }); + } + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + protected final ReturnAdder.ReturnStatementListener returnListener = new ReturnAdder.ReturnStatementListener() { + public void returnStatementAdded(final ReturnStatement returnStatement) { + if (returnStatement.getExpression() == ConstantExpression.NULL) return; + if (isNullConstant(returnStatement.getExpression())) return; + checkReturnType(returnStatement); + if (typeCheckingContext.getEnclosingClosure()!=null) { + addClosureReturnType(getType(returnStatement.getExpression())); + } else if (typeCheckingContext.getEnclosingMethod() != null) { + } else { + throw new GroovyBugError("Unexpected return statement at " + + returnStatement.getLineNumber()+":"+returnStatement.getColumnNumber() + + " "+returnStatement.getText()); + } + } + }; + + protected final ReturnAdder returnAdder = new ReturnAdder(returnListener); + + protected TypeCheckingContext typeCheckingContext; + protected DefaultTypeCheckingExtension extension; + protected FieldNode currentField; + protected PropertyNode currentProperty; + + public StaticTypeCheckingVisitor(SourceUnit source, ClassNode cn) { + this.typeCheckingContext = new TypeCheckingContext(this); + this.extension = createDefaultTypeCheckingExtension(); + this.typeCheckingContext.source = source; + this.typeCheckingContext.pushEnclosingClassNode(cn); + this.typeCheckingContext.pushErrorCollector(source.getErrorCollector()); + this.typeCheckingContext.pushTemporaryTypeInfo(); + } + + private DefaultTypeCheckingExtension createDefaultTypeCheckingExtension() { + DefaultTypeCheckingExtension ext = new DefaultTypeCheckingExtension(this); + ext.addHandler(new TraitTypeCheckingExtension(this)); + return ext; + } + + protected SourceUnit getSourceUnit() { + return typeCheckingContext.source; + } + + public void initialize() { + extension.setup(); + } + + /** + * Returns the current type checking context. The context is used internally by the type + * checker during type checking to store various state data. + * + * @return the type checking context + */ + public TypeCheckingContext getTypeCheckingContext() { + return typeCheckingContext; + } + + public void addTypeCheckingExtension(TypeCheckingExtension extension) { + this.extension.addHandler(extension); + } + + public void setCompilationUnit(CompilationUnit cu) { + typeCheckingContext.setCompilationUnit(cu); + } + + @Override + public void visitClass(final ClassNode node) { + if (shouldSkipClassNode(node)) return; + if (extension.beforeVisitClass(node)) { + extension.afterVisitClass(node); + return; + } + Object type = node.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + if (type != null) { + // transformation has already been run on this class node + // so we'll use a silent collector in order not to duplicate errors + typeCheckingContext.pushErrorCollector(); + } + typeCheckingContext.pushEnclosingClassNode(node); + Set oldVisitedMethod = typeCheckingContext.alreadyVisitedMethods; + typeCheckingContext.alreadyVisitedMethods = new LinkedHashSet(); + super.visitClass(node); + Iterator innerClasses = node.getInnerClasses(); + while (innerClasses.hasNext()) { + InnerClassNode innerClassNode = innerClasses.next(); + visitClass(innerClassNode); + } + typeCheckingContext.alreadyVisitedMethods = oldVisitedMethod; + node.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, node); + // mark all methods as visited. We can't do this in visitMethod because the type checker + // works in a two pass sequence and we don't want to skip the second pass + for (MethodNode methodNode : node.getMethods()) { + methodNode.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE); + } + for (ConstructorNode constructorNode : node.getDeclaredConstructors()) { + constructorNode.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE); + } + extension.afterVisitClass(node); + } + + protected boolean shouldSkipClassNode(final ClassNode node) { + if (isSkipMode(node)) return true; + return false; + } + + /** + * Returns the list of type checking annotations class nodes. Subclasses may override this method + * in order to provide additional classes which must be looked up when checking if a method or + * a class node should be skipped. + *

+ * The default implementation returns {@link TypeChecked}. + * + * @return array of class nodes + */ + protected ClassNode[] getTypeCheckingAnnotations() { + return TYPECHECKING_ANNOTATIONS; + } + + public boolean isSkipMode(final AnnotatedNode node) { + if (node == null) return false; + for (ClassNode tca : getTypeCheckingAnnotations()) { + List annotations = node.getAnnotations(tca); + if (annotations != null) { + for (AnnotationNode annotation : annotations) { + Expression value = annotation.getMember("value"); + if (value != null) { + if (value instanceof ConstantExpression) { + ConstantExpression ce = (ConstantExpression) value; + if (TypeCheckingMode.SKIP.toString().equals(ce.getValue().toString())) return true; + } else if (value instanceof PropertyExpression) { + PropertyExpression pe = (PropertyExpression) value; + if (TypeCheckingMode.SKIP.toString().equals(pe.getPropertyAsString())) return true; + } + } + } + } + } + if (node instanceof MethodNode) { + return isSkipMode(node.getDeclaringClass()); + } + if (isSkippedInnerClass(node)) return true; + return false; + } + + /** + * Test if a node is an inner class node, and if it is, then checks if the enclosing method is skipped. + * @param node + * @return true if the inner class node should be skipped + */ + protected boolean isSkippedInnerClass(AnnotatedNode node) { + if (!(node instanceof InnerClassNode)) return false; + MethodNode enclosingMethod = ((InnerClassNode) node).getEnclosingMethod(); + return enclosingMethod != null && isSkipMode(enclosingMethod); + } + + @Override + public void visitClassExpression(final ClassExpression expression) { + super.visitClassExpression(expression); + ClassNode cn = (ClassNode) expression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + if (cn == null) { + storeType(expression, getType(expression)); + } + } + + private static void addPrivateFieldOrMethodAccess(Expression source, ClassNode cn, StaticTypesMarker type, ASTNode accessedMember) { + Set set = (Set) cn.getNodeMetaData(type); + if (set==null) { + set = new LinkedHashSet(); + cn.putNodeMetaData(type, set); + } + set.add(accessedMember); + source.putNodeMetaData(type, accessedMember); + } + + /** + * Given a field node, checks if we are accessing or setting a private field from an inner class. + */ + private void checkOrMarkPrivateAccess(Expression source, FieldNode fn, boolean lhsOfAssignment) { + if (fn!=null && Modifier.isPrivate(fn.getModifiers()) && + (fn.getDeclaringClass() != typeCheckingContext.getEnclosingClassNode() || typeCheckingContext.getEnclosingClosure()!=null) && + fn.getDeclaringClass().getModule() == typeCheckingContext.getEnclosingClassNode().getModule()) { + StaticTypesMarker marker = lhsOfAssignment ? StaticTypesMarker.PV_FIELDS_MUTATION : StaticTypesMarker.PV_FIELDS_ACCESS; + addPrivateFieldOrMethodAccess(source, fn.getDeclaringClass(), marker, fn); + } + } + + /** + * Given a method node, checks if we are calling a private method from an inner class. + */ + private void checkOrMarkPrivateAccess(Expression source, MethodNode mn) { + if (mn==null) { + return; + } + ClassNode declaringClass = mn.getDeclaringClass(); + ClassNode enclosingClassNode = typeCheckingContext.getEnclosingClassNode(); + if (declaringClass != enclosingClassNode || typeCheckingContext.getEnclosingClosure() != null) { + int mods = mn.getModifiers(); + boolean sameModule = declaringClass.getModule() == enclosingClassNode.getModule(); + String packageName = declaringClass.getPackageName(); + if (packageName==null) { + packageName = ""; + } + if ((Modifier.isPrivate(mods) && sameModule)) { + addPrivateFieldOrMethodAccess(source, declaringClass, StaticTypesMarker.PV_METHODS_ACCESS, mn); + } else if (Modifier.isProtected(mods) && !packageName.equals(enclosingClassNode.getPackageName()) + && !implementsInterfaceOrIsSubclassOf(enclosingClassNode, declaringClass)) { + ClassNode cn = enclosingClassNode; + while ((cn = cn.getOuterClass()) != null) { + if (implementsInterfaceOrIsSubclassOf(cn, declaringClass)) { + addPrivateFieldOrMethodAccess(source, cn, StaticTypesMarker.PV_METHODS_ACCESS, mn); + break; + } + } + } + } + } + + private void checkSuperCallFromClosure(Expression call, MethodNode directCallTarget) { + if (call instanceof MethodCallExpression && typeCheckingContext.getEnclosingClosure() != null) { + Expression objectExpression = ((MethodCallExpression)call).getObjectExpression(); + if (objectExpression instanceof VariableExpression) { + VariableExpression var = (VariableExpression) objectExpression; + if (var.isSuperExpression()) { + ClassNode current = typeCheckingContext.getEnclosingClassNode(); + LinkedList list = current.getNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED); + if (list == null) { + list = new LinkedList(); + current.putNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED, list); + } + list.add(directCallTarget); + call.putNodeMetaData(StaticTypesMarker.SUPER_MOP_METHOD_REQUIRED, current); + } + } + } + } + + /** + * wrap type in Class<> if usingClass==true + */ + private static ClassNode makeType(ClassNode cn, boolean usingClass) { + if (usingClass) { + ClassNode clazzType = CLASS_Type.getPlainNodeReference(); + clazzType.setGenericsTypes(new GenericsType[] {new GenericsType(cn)}); + return clazzType; + } else { + return cn; + } + } + + private boolean storeTypeForThis(VariableExpression vexp) { + if (vexp == VariableExpression.THIS_EXPRESSION) return true; + if (!vexp.isThisExpression()) return false; + ClassNode enclosingClassNode = typeCheckingContext.getEnclosingClassNode(); + storeType(vexp, makeType(enclosingClassNode, typeCheckingContext.isInStaticContext)); + return true; + } + + private boolean storeTypeForSuper(VariableExpression vexp) { + if (vexp == VariableExpression.SUPER_EXPRESSION) return true; + if (!vexp.isSuperExpression()) return false; + ClassNode superClassNode = typeCheckingContext.getEnclosingClassNode().getSuperClass(); + storeType(vexp, makeType(superClassNode, typeCheckingContext.isInStaticContext)); + return true; + } + + @Override + public void visitVariableExpression(VariableExpression vexp) { + super.visitVariableExpression(vexp); + + if (storeTypeForThis(vexp)) return; + if (storeTypeForSuper(vexp)) return; + if (vexp.getAccessedVariable() instanceof PropertyNode) { + // we must be careful, because the property node may be of a wrong type: + // if a class contains a getter and a setter of different types or + // overloaded setters, the type of the property node is arbitrary! + if (tryVariableExpressionAsProperty(vexp, vexp.getName())) { + BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression(); + if (enclosingBinaryExpression != null) { + Expression leftExpression = enclosingBinaryExpression.getLeftExpression(); + Expression rightExpression = enclosingBinaryExpression.getRightExpression(); + SetterInfo setterInfo = removeSetterInfo(leftExpression); + if (setterInfo != null) { + if (!ensureValidSetter(vexp, leftExpression, rightExpression, setterInfo)) { + return; + } + + } + } + } + } + + TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure(); + if (enclosingClosure != null) { + String name = vexp.getName(); + if (name.equals("owner") || name.equals("thisObject")) { + storeType(vexp, typeCheckingContext.getEnclosingClassNode()); + return; + } else if ("delegate".equals(name)) { + DelegationMetadata md = getDelegationMetadata(enclosingClosure.getClosureExpression()); + ClassNode type = typeCheckingContext.getEnclosingClassNode(); + if (md!=null) type = md.getType(); + storeType(vexp, type); + return; + } + } + + if (!(vexp.getAccessedVariable() instanceof DynamicVariable)) { + if (typeCheckingContext.getEnclosingClosure() == null) { + VariableExpression variable = null; + if (vexp.getAccessedVariable() instanceof Parameter) { + variable = new ParameterVariableExpression((Parameter) vexp.getAccessedVariable()); + } else if (vexp.getAccessedVariable() instanceof VariableExpression) { + variable = (VariableExpression) vexp.getAccessedVariable(); + } + if (variable != null) { + ClassNode inferredType = getInferredTypeFromTempInfo(variable, (ClassNode) variable.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE)); + // instanceof applies, stash away the type, reusing key used elsewhere + if (inferredType != null && !inferredType.getName().equals("java.lang.Object")) { + vexp.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, inferredType); + } + } + } + return; + } + + // a dynamic variable is either an undeclared variable + // or a member of a class used in a 'with' + DynamicVariable dyn = (DynamicVariable) vexp.getAccessedVariable(); + // first, we must check the 'with' context + String dynName = dyn.getName(); + if (tryVariableExpressionAsProperty(vexp, dynName)) return; + + if (!extension.handleUnresolvedVariableExpression(vexp)) { + addStaticTypeError("The variable [" + vexp.getName() + "] is undeclared.", vexp); + } + } + + private boolean tryVariableExpressionAsProperty(final VariableExpression vexp, final String dynName) { + VariableExpression implicitThis = varX("this"); + PropertyExpression pe = new PropertyExpression(implicitThis, dynName); + pe.setImplicitThis(true); + if (visitPropertyExpressionSilent(pe, vexp)) { + ClassNode previousIt = vexp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + vexp.copyNodeMetaData(implicitThis); + vexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, previousIt); + storeType(vexp, getType(pe)); + Object val = pe.getNodeMetaData(StaticTypesMarker.READONLY_PROPERTY); + if (val!=null) vexp.putNodeMetaData(StaticTypesMarker.READONLY_PROPERTY,val); + val = pe.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER); + if (val!=null) vexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER,val); + return true; + } + return false; + } + + private boolean visitPropertyExpressionSilent(PropertyExpression pe, Expression lhsPart) { + return (existsProperty(pe, !isLHSOfEnclosingAssignment(lhsPart))); + } + + @Override + public void visitPropertyExpression(final PropertyExpression pexp) { + if (visitPropertyExpressionSilent(pexp,pexp)) return; + + if (!extension.handleUnresolvedProperty(pexp)) { + Expression objectExpression = pexp.getObjectExpression(); + addStaticTypeError("No such property: " + pexp.getPropertyAsString() + + " for class: " + findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), pexp); + } + } + + private boolean isLHSOfEnclosingAssignment(final Expression expression) { + final BinaryExpression ec = typeCheckingContext.getEnclosingBinaryExpression(); + return ec != null && ec.getLeftExpression() == expression && isAssignment(ec.getOperation().getType()); + } + + @Override + public void visitAttributeExpression(final AttributeExpression expression) { + super.visitAttributeExpression(expression); + if (!existsProperty(expression, true) && !extension.handleUnresolvedAttribute(expression)) { + Expression objectExpression = expression.getObjectExpression(); + addStaticTypeError("No such property: " + expression.getPropertyAsString() + + " for class: " + findCurrentInstanceOfClass(objectExpression, objectExpression.getType()), expression); + } + } + + @Override + public void visitRangeExpression(final RangeExpression expression) { + super.visitRangeExpression(expression); + ClassNode fromType = getWrapper(getType(expression.getFrom())); + ClassNode toType = getWrapper(getType(expression.getTo())); + if (Integer_TYPE.equals(fromType) && Integer_TYPE.equals(toType)) { + storeType(expression, ClassHelper.make(IntRange.class)); + } else { + ClassNode rangeType = ClassHelper.make(Range.class).getPlainNodeReference(); + rangeType.setGenericsTypes(new GenericsType[] { new GenericsType(WideningCategories.lowestUpperBound(fromType, toType))}); + storeType(expression, rangeType); + } + } + + @Override + public void visitBinaryExpression(BinaryExpression expression) { + BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression(); + typeCheckingContext.pushEnclosingBinaryExpression(expression); + try { + final Expression leftExpression = expression.getLeftExpression(); + final Expression rightExpression = expression.getRightExpression(); + int op = expression.getOperation().getType(); + leftExpression.visit(this); + SetterInfo setterInfo = removeSetterInfo(leftExpression); + if (setterInfo != null) { + if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) { + return; + } + + } else { + rightExpression.visit(this); + } + ClassNode lType = getType(leftExpression); + ClassNode rType = getType(rightExpression); + if (isNullConstant(rightExpression)) { + if (!isPrimitiveType(lType)) + rType = UNKNOWN_PARAMETER_TYPE; // primitive types should be ignored as they will result in another failure + } + BinaryExpression reversedBinaryExpression = binX(rightExpression, expression.getOperation(), leftExpression); + ClassNode resultType = (op==KEYWORD_IN || op==COMPARE_NOT_IN) + ?getResultType(rType,op,lType,reversedBinaryExpression) + :getResultType(lType, op, rType, expression); + if (op==KEYWORD_IN || op==COMPARE_NOT_IN) { + // in case of the "in" operator, the receiver and the arguments are reversed + // so we use the reversedExpression and get the target method from it + storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET)); + } else if (op == LEFT_SQUARE_BRACKET + && leftExpression instanceof VariableExpression + && leftExpression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE)==null) { + storeType(leftExpression, lType); + } + if (resultType == null) { + resultType = lType; + } + + // if left expression is a closure shared variable, a second pass should be done + if (leftExpression instanceof VariableExpression) { + VariableExpression leftVar = (VariableExpression) leftExpression; + if (leftVar.isClosureSharedVariable()) { + // if left expression is a closure shared variable, we should check it twice + // see GROOVY-5874 + typeCheckingContext.secondPassExpressions.add(new SecondPassExpression(expression)); + } + } + + if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) { + // unchecked assignment + // examples: + // List list = new LinkedList() + // List list = [] + // Iterable list = new LinkedList() + + // in that case, the inferred type of the binary expression is the type of the RHS + // "completed" with generics type information available in the LHS + ClassNode completedType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference()); + + resultType = completedType; + + } + if (isArrayOp(op) && + enclosingBinaryExpression != null + && enclosingBinaryExpression.getLeftExpression() == expression + && isAssignment(enclosingBinaryExpression.getOperation().getType()) + && !lType.isArray()) { + // left hand side of an assignment : map['foo'] = ... + Expression enclosingBE_rightExpr = enclosingBinaryExpression.getRightExpression(); + if (!(enclosingBE_rightExpr instanceof ClosureExpression)) { + enclosingBE_rightExpr.visit(this); + } + ClassNode[] arguments = {rType, getType(enclosingBE_rightExpr)}; + List nodes = findMethod(lType.redirect(), "putAt", arguments); + if (nodes.size() == 1) { + typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingBE_rightExpr); + } else if (nodes.isEmpty()) { + addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression); + } + } + boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression; + if (!isEmptyDeclaration && isAssignment(op)) { + if (rightExpression instanceof ConstructorCallExpression) { + inferDiamondType((ConstructorCallExpression) rightExpression, lType); + } + + ClassNode originType = getOriginalDeclarationType(leftExpression); + typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType); + // if assignment succeeds but result type is not a subtype of original type, then we are in a special cast handling + // and we must update the result type + if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) { + resultType = originType; + } else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) { + // for example, LHS is List and RHS is List where T is a placeholder + resultType = lType; + } + + // make sure we keep primitive types + if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) { + resultType = originType; + } + + // if we are in an if/else branch, keep track of assignment + if (typeCheckingContext.ifElseForWhileAssignmentTracker != null && leftExpression instanceof VariableExpression + && !isNullConstant(rightExpression)) { + Variable accessedVariable = ((VariableExpression) leftExpression).getAccessedVariable(); + if (accessedVariable instanceof Parameter) { + accessedVariable = new ParameterVariableExpression((Parameter) accessedVariable); + } + if (accessedVariable instanceof VariableExpression) { + VariableExpression var = (VariableExpression) accessedVariable; + List types = typeCheckingContext.ifElseForWhileAssignmentTracker.get(var); + if (types == null) { + types = new LinkedList(); + ClassNode type = var.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + types.add(type); + typeCheckingContext.ifElseForWhileAssignmentTracker.put(var, types); + } + types.add(resultType); + } + } + storeType(leftExpression, resultType); + + // if right expression is a ClosureExpression, store parameter type information + if (leftExpression instanceof VariableExpression) { + if (rightExpression instanceof ClosureExpression) { + Parameter[] parameters = ((ClosureExpression) rightExpression).getParameters(); + leftExpression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, parameters); + } else if (rightExpression instanceof VariableExpression && + ((VariableExpression)rightExpression).getAccessedVariable() instanceof Expression && + ((Expression)((VariableExpression)rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS)!=null) { + Variable targetVariable = findTargetVariable((VariableExpression)leftExpression); + if (targetVariable instanceof ASTNode) { + ((ASTNode)targetVariable).putNodeMetaData( + StaticTypesMarker.CLOSURE_ARGUMENTS, + ((Expression)((VariableExpression)rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS)); + } + } + } + + + } else if (op == KEYWORD_INSTANCEOF) { + pushInstanceOfTypeInfo(leftExpression, rightExpression); + } + if (!isEmptyDeclaration) { + storeType(expression, resultType); + } + } finally { + typeCheckingContext.popEnclosingBinaryExpression(); + } + } + + /** + * Given a binary expression corresponding to an assignment, will check that the type of the RHS matches one + * of the possible setters and if not, throw a type checking error. + * @param expression the assignment expression + * @param leftExpression left expression of the assignment + * @param rightExpression right expression of the assignment + * @param setterInfo possible setters + * @return true if type checking passed + */ + private boolean ensureValidSetter(final Expression expression, final Expression leftExpression, final Expression rightExpression, final SetterInfo setterInfo) { + // for expressions like foo = { ... } + // we know that the RHS type is a closure + // but we must check if the binary expression is an assignment + // because we need to check if a setter uses @DelegatesTo + VariableExpression ve = varX("%", setterInfo.receiverType); + // for compound assignment "x op= y" find type as if it was "x = (x op y)" + final Expression newRightExpression = isCompoundAssignment(expression) + ? binX(leftExpression, getOpWithoutEqual(expression), rightExpression) + : rightExpression; + MethodCallExpression call = callX(ve, setterInfo.name, newRightExpression); + call.setImplicitThis(false); + visitMethodCallExpression(call); + MethodNode directSetterCandidate = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + if (directSetterCandidate==null) { + // this may happen if there's a setter of type boolean/String/Class, and that we are using the property + // notation AND that the RHS is not a boolean/String/Class + for (MethodNode setter : setterInfo.setters) { + ClassNode type = getWrapper(setter.getParameters()[0].getOriginType()); + if (Boolean_TYPE.equals(type) || STRING_TYPE.equals(type) || CLASS_Type.equals(type)) { + call = callX(ve, setterInfo.name, castX(type, newRightExpression)); + call.setImplicitThis(false); + visitMethodCallExpression(call); + directSetterCandidate = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + if (directSetterCandidate!=null) { + break; + } + } + } + } + if (directSetterCandidate != null) { + for (MethodNode setter : setterInfo.setters) { + if (setter == directSetterCandidate) { + leftExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, directSetterCandidate); + storeType(leftExpression, getType(newRightExpression)); + break; + } + } + } else { + ClassNode firstSetterType = setterInfo.setters.iterator().next().getParameters()[0].getOriginType(); + addAssignmentError(firstSetterType, getType(newRightExpression), expression); + return true; + } + return false; + } + + private boolean isCompoundAssignment(Expression exp) { + if (!(exp instanceof BinaryExpression)) return false; + int type = ((BinaryExpression) exp).getOperation().getType(); + return isAssignment(type) && type != ASSIGN; + } + + private Token getOpWithoutEqual(Expression exp) { + if (!(exp instanceof BinaryExpression)) return null; // should never happen + Token op = ((BinaryExpression) exp).getOperation(); + int typeWithoutEqual = TokenUtil.removeAssignment(op.getType()); + return new Token(typeWithoutEqual, op.getText() /* will do */, op.getStartLine(), op.getStartColumn()); + } + + protected ClassNode getOriginalDeclarationType(Expression lhs) { + if (lhs instanceof VariableExpression) { + Variable var = findTargetVariable((VariableExpression) lhs); + if (var instanceof PropertyNode) { + // Do NOT trust the type of the property node! + return getType(lhs); + } + if (var instanceof DynamicVariable) return getType(lhs); + return var.getOriginType(); + } + if (lhs instanceof FieldExpression) { + return ((FieldExpression) lhs).getField().getOriginType(); + } + return getType(lhs); + } + + protected void inferDiamondType(final ConstructorCallExpression cce, final ClassNode lType) { + // check if constructor call expression makes use of the diamond operator + ClassNode node = cce.getType(); + if (node.isUsingGenerics() && node.getGenericsTypes() != null && node.getGenericsTypes().length == 0) { + ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(cce.getArguments()); + if (argumentListExpression.getExpressions().isEmpty()) { + adjustGenerics(lType, node); + } else { + ClassNode type = getType(argumentListExpression.getExpression(0)); + if (type.isUsingGenerics()) { + adjustGenerics(type, node); + } + } + // store inferred type on CCE + storeType(cce, node); + } + } + + private void adjustGenerics(ClassNode from, ClassNode to) { + GenericsType[] genericsTypes = from.getGenericsTypes(); + if (genericsTypes == null) { + // case of: def foo = new HashMap<>() + genericsTypes = to.redirect().getGenericsTypes(); + } + GenericsType[] copy = new GenericsType[genericsTypes.length]; + for (int i = 0; i < genericsTypes.length; i++) { + GenericsType genericsType = genericsTypes[i]; + copy[i] = new GenericsType( + wrapTypeIfNecessary(genericsType.getType()), + genericsType.getUpperBounds(), + genericsType.getLowerBound() + ); + } + to.setGenericsTypes(copy); + } + + /** + * Stores information about types when [objectOfInstanceof instanceof typeExpression] is visited + * + * @param objectOfInstanceOf the expression which must be checked against instanceof + * @param typeExpression the expression which represents the target type + */ + protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final Expression typeExpression) { + final Map> tempo = typeCheckingContext.temporaryIfBranchTypeInformation.peek(); + Object key = extractTemporaryTypeInfoKey(objectOfInstanceOf); + List potentialTypes = tempo.get(key); + if (potentialTypes == null) { + potentialTypes = new LinkedList(); + tempo.put(key, potentialTypes); + } + potentialTypes.add(typeExpression.getType()); + } + + private boolean typeCheckMultipleAssignmentAndContinue(Expression leftExpression, Expression rightExpression) { + // multiple assignment check + if (!(leftExpression instanceof TupleExpression)) return true; + + if (!(rightExpression instanceof ListExpression)) { + addStaticTypeError("Multiple assignments without list expressions on the right hand side are unsupported in static type checking mode", rightExpression); + return false; + } + + TupleExpression tuple = (TupleExpression) leftExpression; + ListExpression list = (ListExpression) rightExpression; + List listExpressions = list.getExpressions(); + List tupleExpressions = tuple.getExpressions(); + if (listExpressions.size() < tupleExpressions.size()) { + addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + listExpressions.size(), list); + return false; + } + for (int i = 0, tupleExpressionsSize = tupleExpressions.size(); i < tupleExpressionsSize; i++) { + Expression tupleExpression = tupleExpressions.get(i); + Expression listExpression = listExpressions.get(i); + ClassNode elemType = getType(listExpression); + ClassNode tupleType = getType(tupleExpression); + if (!isAssignableTo(elemType, tupleType)) { + addStaticTypeError("Cannot assign value of type " + elemType.toString(false) + " to variable of type " + tupleType.toString(false), rightExpression); + return false; // avoids too many errors + } else { + storeType(tupleExpression, elemType); + } + } + + return true; + } + + private static ClassNode adjustTypeForSpreading(ClassNode inferredRightExpressionType, Expression leftExpression) { + // imagine we have: list*.foo = 100 + // then the assignment must be checked against [100], not 100 + ClassNode wrappedRHS = inferredRightExpressionType; + if (leftExpression instanceof PropertyExpression && ((PropertyExpression) leftExpression).isSpreadSafe()) { + wrappedRHS = LIST_TYPE.getPlainNodeReference(); + wrappedRHS.setGenericsTypes(new GenericsType[]{ + new GenericsType(getWrapper(inferredRightExpressionType)) + }); + } + return wrappedRHS; + } + + private boolean addedReadOnlyPropertyError(Expression expr) { + // if expr is of READONLY_PROPERTY_RETURN type, then it means we are on a missing property + if (expr.getNodeMetaData(StaticTypesMarker.READONLY_PROPERTY) == null) return false; + String name; + if (expr instanceof VariableExpression) { + name = ((VariableExpression) expr).getName(); + } else { + name = ((PropertyExpression) expr).getPropertyAsString(); + } + addStaticTypeError("Cannot set read-only property: " + name, expr); + return true; + } + + private void addPrecisionErrors(ClassNode leftRedirect, ClassNode lhsType, ClassNode inferredrhsType, Expression rightExpression) { + if (isNumberType(leftRedirect) && isNumberType(inferredrhsType)) { + if (checkPossibleLossOfPrecision(leftRedirect, inferredrhsType, rightExpression)) { + addStaticTypeError("Possible loss of precision from " + inferredrhsType + " to " + leftRedirect, rightExpression); + return; + } + } + // if left type is array, we should check the right component types + if (!lhsType.isArray()) return; + ClassNode leftComponentType = lhsType.getComponentType(); + ClassNode rightRedirect = rightExpression.getType().redirect(); + if (rightRedirect.isArray()) { + ClassNode rightComponentType = rightRedirect.getComponentType(); + if (!checkCompatibleAssignmentTypes(leftComponentType, rightComponentType)) { + addStaticTypeError("Cannot assign value of type " + rightComponentType.toString(false) + " into array of type " + lhsType.toString(false), rightExpression); + } + } else if (rightExpression instanceof ListExpression) { + for (Expression element : ((ListExpression) rightExpression).getExpressions()) { + ClassNode rightComponentType = this.getType(element); + if (!checkCompatibleAssignmentTypes(leftComponentType, rightComponentType) + && !(isNullConstant(element) && !isPrimitiveType(leftComponentType))) { + addStaticTypeError("Cannot assign value of type " + rightComponentType.toString(false) + " into array of type " + lhsType.toString(false), rightExpression); + } + } + } + } + + private void addListAssignmentConstructorErrors( + ClassNode leftRedirect, ClassNode leftExpressionType, + ClassNode inferredRightExpressionType, Expression rightExpression, + Expression assignmentExpression) + { + // if left type is not a list but right type is a list, then we're in the case of a groovy + // constructor type : Dimension d = [100,200] + // In that case, more checks can be performed + if (rightExpression instanceof ListExpression && !implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)) { + ArgumentListExpression argList = args(((ListExpression) rightExpression).getExpressions()); + ClassNode[] args = getArgumentTypes(argList); + MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, args, assignmentExpression); + if (methodNode!=null) { + rightExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, methodNode); + } + } else if (!implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, leftRedirect) + && implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, LIST_TYPE) + && !isWildcardLeftHandSide(leftExpressionType)) { + if (!extension.handleIncompatibleAssignment(leftExpressionType, inferredRightExpressionType, assignmentExpression)) { + addAssignmentError(leftExpressionType, inferredRightExpressionType, assignmentExpression); + } + } + } + + private void addMapAssignmentConstructorErrors(ClassNode leftRedirect, Expression leftExpression, Expression rightExpression) { + // if left type is not a list but right type is a map, then we're in the case of a groovy + // constructor type : A a = [x:2, y:3] + // In this case, more checks can be performed + if (!implementsInterfaceOrIsSubclassOf(leftRedirect, MAP_TYPE) && rightExpression instanceof MapExpression) { + if (!(leftExpression instanceof VariableExpression) || !((VariableExpression) leftExpression).isDynamicTyped()) { + ArgumentListExpression argList = args(rightExpression); + ClassNode[] argTypes = getArgumentTypes(argList); + checkGroovyStyleConstructor(leftRedirect, argTypes, rightExpression); + // perform additional type checking on arguments + MapExpression mapExpression = (MapExpression) rightExpression; + checkGroovyConstructorMap(leftExpression, leftRedirect, mapExpression); + } + } + } + + private void checkTypeGenerics(ClassNode leftExpressionType, ClassNode wrappedRHS, Expression rightExpression) { + // last, check generic type information to ensure that inferred types are compatible + if (!leftExpressionType.isUsingGenerics()) return; + // List l = new List() is an example for incomplete generics type info + // we assume arity related errors are already handled here. + if (hasRHSIncompleteGenericTypeInfo(wrappedRHS)) return; + + GenericsType gt = GenericsUtils.buildWildcardType(leftExpressionType); + if ( UNKNOWN_PARAMETER_TYPE.equals(wrappedRHS) || + gt.isCompatibleWith(wrappedRHS) || + isNullConstant(rightExpression)) return; + + addStaticTypeError("Incompatible generic argument types. Cannot assign " + + wrappedRHS.toString(false) + + " to: " + leftExpressionType.toString(false), rightExpression); + } + + private boolean hasGStringStringError(ClassNode leftExpressionType, ClassNode wrappedRHS, Expression rightExpression) { + if (isParameterizedWithString(leftExpressionType) && isParameterizedWithGStringOrGStringString(wrappedRHS)) { + addStaticTypeError("You are trying to use a GString in place of a String in a type which explicitly declares accepting String. " + + "Make sure to call toString() on all GString values.", rightExpression); + return true; + } + return false; + } + + protected void typeCheckAssignment( + final BinaryExpression assignmentExpression, + final Expression leftExpression, + final ClassNode leftExpressionType, + final Expression rightExpression, + final ClassNode inferredRightExpressionTypeOrig) + { + ClassNode inferredRightExpressionType = inferredRightExpressionTypeOrig; + if (!typeCheckMultipleAssignmentAndContinue(leftExpression, rightExpression)) return; + + if (leftExpression instanceof VariableExpression + && ((VariableExpression) leftExpression).getAccessedVariable() instanceof FieldNode) { + checkOrMarkPrivateAccess(leftExpression, (FieldNode) ((VariableExpression) leftExpression).getAccessedVariable(), true); + } + + //TODO: need errors for write-only too! + if (addedReadOnlyPropertyError(leftExpression)) return; + + ClassNode leftRedirect = leftExpressionType.redirect(); + // see if instanceof applies + if (rightExpression instanceof VariableExpression && hasInferredReturnType(rightExpression) && assignmentExpression.getOperation().getType() == EQUAL) { + inferredRightExpressionType = rightExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } + ClassNode wrappedRHS = adjustTypeForSpreading(inferredRightExpressionType, leftExpression); + + // check types are compatible for assignment + boolean compatible = checkCompatibleAssignmentTypes(leftRedirect, wrappedRHS, rightExpression); + + if (!compatible) { + if (!extension.handleIncompatibleAssignment(leftExpressionType, inferredRightExpressionType, assignmentExpression)) { + addAssignmentError(leftExpressionType, inferredRightExpressionType, assignmentExpression.getRightExpression()); + } + } else { + addPrecisionErrors(leftRedirect, leftExpressionType, inferredRightExpressionType, rightExpression); + addListAssignmentConstructorErrors(leftRedirect, leftExpressionType, inferredRightExpressionType, rightExpression, assignmentExpression); + addMapAssignmentConstructorErrors(leftRedirect, leftExpression, rightExpression); + if (hasGStringStringError(leftExpressionType, wrappedRHS, rightExpression)) return; + checkTypeGenerics(leftExpressionType, wrappedRHS, rightExpression); + } + } + + protected void checkGroovyConstructorMap(final Expression receiver, final ClassNode receiverType, final MapExpression mapExpression) { + // workaround for map-style checks putting setter info on wrong AST nodes + typeCheckingContext.pushEnclosingBinaryExpression(null); + for (MapEntryExpression entryExpression : mapExpression.getMapEntryExpressions()) { + Expression keyExpr = entryExpression.getKeyExpression(); + if (!(keyExpr instanceof ConstantExpression)) { + addStaticTypeError("Dynamic keys in map-style constructors are unsupported in static type checking", keyExpr); + } else { + AtomicReference lookup = new AtomicReference(); + PropertyExpression pexp = new PropertyExpression(varX("_", receiverType), keyExpr.getText()); + boolean hasProperty = existsProperty(pexp, false, new PropertyLookupVisitor(lookup)); + if (!hasProperty) { + addStaticTypeError("No such property: " + keyExpr.getText() + + " for class: " + receiverType.getName(), receiver); + } else { + ClassNode valueType = getType(entryExpression.getValueExpression()); + MethodNode setter = receiverType.getSetterMethod("set" + MetaClassHelper.capitalize(pexp.getPropertyAsString()), false); + ClassNode toBeAssignedTo = setter == null ? lookup.get() : setter.getParameters()[0].getType(); + if (!isAssignableTo(valueType, toBeAssignedTo) + && !extension.handleIncompatibleAssignment(toBeAssignedTo, valueType, entryExpression)) { + addAssignmentError(toBeAssignedTo, valueType, entryExpression); + } + } + } + } + typeCheckingContext.popEnclosingBinaryExpression(); + } + + protected static boolean hasRHSIncompleteGenericTypeInfo(final ClassNode inferredRightExpressionType) { + boolean replaceType = false; + GenericsType[] genericsTypes = inferredRightExpressionType.getGenericsTypes(); + if (genericsTypes != null) { + for (GenericsType genericsType : genericsTypes) { + if (genericsType.isPlaceholder()) { + replaceType = true; + break; + } + } + } + return replaceType; + } + + /** + * Checks that a constructor style expression is valid regarding the number of arguments and the argument types. + * + * @param node the class node for which we will try to find a matching constructor + * @param arguments the constructor arguments + * @deprecated use {@link #checkGroovyStyleConstructor(org.codehaus.groovy.ast.ClassNode, org.codehaus.groovy.ast.ClassNode[], org.codehaus.groovy.ast.ASTNode)} )} + */ + @Deprecated + protected void checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments) { + checkGroovyStyleConstructor(node, arguments, typeCheckingContext.getEnclosingClassNode()); + } + + /** + * Checks that a constructor style expression is valid regarding the number of arguments and the argument types. + * + * @param node the class node for which we will try to find a matching constructor + * @param arguments the constructor arguments + */ + protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode source) { + if (node.equals(ClassHelper.OBJECT_TYPE) || node.equals(ClassHelper.DYNAMIC_TYPE)) { + // in that case, we are facing a list constructor assigned to a def or object + return null; + } + List constructors = node.getDeclaredConstructors(); + if (constructors.isEmpty() && arguments.length == 0) { + return null; + } + List constructorList = findMethod(node, "", arguments); + if (constructorList.isEmpty()) { + if (isBeingCompiled(node) && arguments.length==1 && LINKEDHASHMAP_CLASSNODE.equals(arguments[0])) { + // there will be a default hash map constructor added later + ConstructorNode cn = new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{ + new Parameter(LINKEDHASHMAP_CLASSNODE, "args") + }, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE); + return cn; + } else { + addStaticTypeError("No matching constructor found: " + node + toMethodParametersString("", arguments), source); + return null; + } + } else if (constructorList.size()>1) { + addStaticTypeError("Ambiguous constructor call " + node + toMethodParametersString("", arguments), source); + return null; + } + return constructorList.get(0); + } + + /** + * When instanceof checks are found in the code, we store temporary type information data in the {@link + * TypeCheckingContext#temporaryIfBranchTypeInformation} table. This method computes the key which must be used to store this type + * info. + * + * @param expression the expression for which to compute the key + * @return a key to be used for {@link TypeCheckingContext#temporaryIfBranchTypeInformation} + */ + protected Object extractTemporaryTypeInfoKey(final Expression expression) { + return expression instanceof VariableExpression ? findTargetVariable((VariableExpression) expression) : expression.getText(); + } + + /** + * A helper method which determines which receiver class should be used in error messages when a field or attribute + * is not found. The returned type class depends on whether we have temporary type information available (due to + * instanceof checks) and whether there is a single candidate in that case. + * + * @param expr the expression for which an unknown field has been found + * @param type the type of the expression (used as fallback type) + * @return if temporary information is available and there's only one type, returns the temporary type class + * otherwise falls back to the provided type class. + */ + protected ClassNode findCurrentInstanceOfClass(final Expression expr, final ClassNode type) { + if (!typeCheckingContext.temporaryIfBranchTypeInformation.empty()) { + List nodes = getTemporaryTypesForExpression(expr); + if (nodes != null && nodes.size() == 1) return nodes.get(0); + } + return type; + } + + protected boolean existsProperty(final PropertyExpression pexp, final boolean checkForReadOnly) { + return existsProperty(pexp, checkForReadOnly, null); + } + + /** + * Checks whether a property exists on the receiver, or on any of the possible receiver classes (found in the + * temporary type information table) + * + * @param pexp a property expression + * @param readMode if true, look for property read, else for property set + * @param visitor if not null, when the property node is found, visit it with the provided visitor + * @return true if the property is defined in any of the possible receiver classes + */ + protected boolean existsProperty(final PropertyExpression pexp, final boolean readMode, final ClassCodeVisitorSupport visitor) { + super.visitPropertyExpression(pexp); + + String propertyName = pexp.getPropertyAsString(); + if (propertyName == null) return false; + + Expression objectExpression = pexp.getObjectExpression(); + final ClassNode objectExpressionType = getType(objectExpression); + + boolean staticOnlyAccess = isClassClassNodeWrappingConcreteType(objectExpressionType); + if ("this".equals(propertyName) && staticOnlyAccess) { + // Outer.this for any level of nesting + ClassNode outerNode = objectExpressionType.getGenericsTypes()[0].getType(); + List candidates = typeCheckingContext.getEnclosingClassNodes(); + ClassNode found = null; + for (ClassNode current : candidates) { + if (!current.isStaticClass() && current instanceof InnerClassNode && outerNode.equals(current.getOuterClass())) { + found = current; + break; + } + } + if (found != null) { + storeType(pexp, outerNode); + return true; + } + } + + if (objectExpressionType.isArray() && "length".equals(pexp.getPropertyAsString())) { + storeType(pexp, int_TYPE); + if (visitor != null) { + PropertyNode node = new PropertyNode("length", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, int_TYPE, objectExpressionType, null, null, null); + visitor.visitProperty(node); + } + return true; + } + + boolean foundGetterOrSetter = false; + List> receivers = new LinkedList>(); + List> owners = makeOwnerList(objectExpression); + addReceivers(receivers, owners, pexp.isImplicitThis()); + + String capName = MetaClassHelper.capitalize(propertyName); + boolean isAttributeExpression = pexp instanceof AttributeExpression; + HashSet handledNodes = new HashSet(); + for (Receiver receiver : receivers) { + ClassNode testClass = receiver.getType(); + LinkedList queue = new LinkedList(); + queue.add(testClass); + if (isPrimitiveType(testClass)) { + queue.add(getWrapper(testClass)); + } + while (!queue.isEmpty()) { + ClassNode current = queue.removeFirst(); + if (handledNodes.contains(current)) continue; + handledNodes.add(current); + Set allInterfaces = current.getAllInterfaces(); + for (ClassNode intf : allInterfaces) { + //TODO: apply right generics here! + queue.add(GenericsUtils.parameterizeType(current, intf)); + } + + // in case of a lookup on Class we look for instance methods on Class + // as well, since in case of a static property access we have the class + // itself in the list of receivers already; + boolean staticOnly; + if (isClassClassNodeWrappingConcreteType(current)) { + staticOnly = false; + } else { + staticOnly = staticOnlyAccess; + } + + FieldNode field = current.getDeclaredField(propertyName); + field = allowStaticAccessToMember(field, staticOnly); + if (storeField(field, isAttributeExpression, pexp, current, visitor, receiver.getData(), !readMode)) return true; + + boolean isThisExpression = objectExpression instanceof VariableExpression + && ((VariableExpression) objectExpression).isThisExpression() + && objectExpressionType.equals(current); + + if (storeField(field, isThisExpression, pexp, receiver.getType(), visitor, receiver.getData(), !readMode)) + return true; + + MethodNode getter = findGetter(current, "get" + capName, pexp.isImplicitThis()); + getter = allowStaticAccessToMember(getter, staticOnly); + if (getter == null) getter = findGetter(current, "is" + capName, pexp.isImplicitThis()); + getter = allowStaticAccessToMember(getter, staticOnly); + final String setterName = "set" + capName; + List setters = findSetters(current, setterName, false); + setters = allowStaticAccessToMember(setters, staticOnly); + + // TODO: remove this visit + // need to visit even if we only look for a setters for compatibility + if (visitor != null && getter != null) visitor.visitMethod(getter); + + PropertyNode propertyNode = current.getProperty(propertyName); + propertyNode = allowStaticAccessToMember(propertyNode, staticOnly); + //prefer explicit getter or setter over property if receiver is not 'this' + boolean checkGetterOrSetter = !isThisExpression || propertyNode == null; + + if (readMode && checkGetterOrSetter) { + if (getter != null) { + ClassNode cn = inferReturnTypeGenerics(current, getter, ArgumentListExpression.EMPTY_ARGUMENTS); + storeInferredTypeForPropertyExpression(pexp, cn); + pexp.removeNodeMetaData(StaticTypesMarker.READONLY_PROPERTY); + String delegationData = receiver.getData(); + if (delegationData != null) + pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData); + return true; + } + } else if (!readMode && checkGetterOrSetter) { + if (!setters.isEmpty()) { + if (visitor != null) { + if (field != null) { + visitor.visitField(field); + } else { + for (MethodNode setter : setters) { + ClassNode setterType = setter.getParameters()[0].getOriginType(); + FieldNode virtual = new FieldNode(propertyName, 0, setterType, current, EmptyExpression.INSTANCE); + visitor.visitField(virtual); + } + } + } + //TODO: apply generics on parameter[0]? +// storeType(pexp, setter.getParameters()[0].getType()); + SetterInfo info = new SetterInfo(current, setterName, setters); + BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression(); + if (enclosingBinaryExpression != null) { + putSetterInfo(enclosingBinaryExpression.getLeftExpression(), info); + } + String delegationData = receiver.getData(); + if (delegationData != null) { + pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData); + } + return true; + } else if (getter != null && propertyNode == null) { + pexp.putNodeMetaData(StaticTypesMarker.READONLY_PROPERTY, true); + } + } + foundGetterOrSetter = foundGetterOrSetter || !setters.isEmpty() || getter != null; + + if (storeProperty(propertyNode, pexp, current, visitor, receiver.getData())) return true; + + if (storeField(field, true, pexp, current, visitor, receiver.getData(), !readMode)) return true; + // if the property expression is an attribute expression (o.@attr), then + // we stop now, otherwise we must check the parent class + if (/*!isAttributeExpression && */current.getSuperClass() != null) { + queue.add(current.getUnresolvedSuperClass()); + } + } + // GROOVY-5568, the property may be defined by DGM + List dgmReceivers = new ArrayList(2); + dgmReceivers.add(testClass); + if (isPrimitiveType(testClass)) dgmReceivers.add(getWrapper(testClass)); + for (ClassNode dgmReceiver: dgmReceivers) { + List methods = findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY); + for (MethodNode m : findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) { + if (Boolean_TYPE.equals(getWrapper(m.getReturnType()))) methods.add(m); + } + if (!methods.isEmpty()) { + List methodNodes = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY); + if (methodNodes.size() == 1) { + MethodNode getter = methodNodes.get(0); + if (visitor != null) { + visitor.visitMethod(getter); + } + ClassNode cn = inferReturnTypeGenerics(dgmReceiver, getter, ArgumentListExpression.EMPTY_ARGUMENTS); + storeInferredTypeForPropertyExpression(pexp, cn); + + return true; + } + } + } + } + + for (Receiver receiver : receivers) { + ClassNode testClass = receiver.getType(); + ClassNode propertyType = getTypeForMapPropertyExpression(testClass, objectExpressionType, pexp); + if (propertyType==null) propertyType = getTypeForListPropertyExpression(testClass, objectExpressionType, pexp); + if (propertyType==null) propertyType = getTypeForSpreadExpression(testClass, objectExpressionType, pexp); + if (propertyType==null) continue; + if (visitor!=null) { + // todo : type inference on maps and lists, if possible + PropertyNode node = new PropertyNode(propertyName, Opcodes.ACC_PUBLIC, propertyType, receiver.getType(), null, null, null); + node.setDeclaringClass(receiver.getType()); + visitor.visitProperty(node); + } + storeType(pexp, propertyType); + String delegationData = receiver.getData(); + if (delegationData!=null) pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData); + return true; + } + return foundGetterOrSetter; + } + + private MethodNode findGetter(ClassNode current, String name, boolean searchOuterClasses) { + MethodNode getterMethod = current.getGetterMethod(name); + if (getterMethod == null && searchOuterClasses && current instanceof InnerClassNode) { + return findGetter(current.getOuterClass(), name, true); + } + return getterMethod; + } + + private ClassNode getTypeForSpreadExpression(ClassNode testClass, ClassNode objectExpressionType, PropertyExpression pexp) { + if (!pexp.isSpreadSafe()) return null; + MethodCallExpression mce = callX(varX("_", testClass), "iterator", ArgumentListExpression.EMPTY_ARGUMENTS); + mce.setImplicitThis(false); + mce.visit(this); + ClassNode callType = getType(mce); + if (!implementsInterfaceOrIsSubclassOf(callType, Iterator_TYPE)) return null; + GenericsType[] types = callType.getGenericsTypes(); + ClassNode contentType = OBJECT_TYPE; + if (types!=null && types.length==1) contentType = types[0].getType(); + PropertyExpression subExp = new PropertyExpression(varX("{}", contentType), pexp.getPropertyAsString()); + AtomicReference result = new AtomicReference(); + if (existsProperty(subExp, true, new PropertyLookupVisitor(result))) { + ClassNode intf = LIST_TYPE.getPlainNodeReference(); + intf.setGenericsTypes(new GenericsType[] { new GenericsType(getWrapper(result.get()))}); + return intf; + } + return null; + } + + private ClassNode getTypeForListPropertyExpression(ClassNode testClass, ClassNode objectExpressionType, PropertyExpression pexp) { + if (!implementsInterfaceOrIsSubclassOf(testClass, LIST_TYPE)) return null; + ClassNode intf = GenericsUtils.parameterizeType(objectExpressionType, LIST_TYPE.getPlainNodeReference()); + GenericsType[] types = intf.getGenericsTypes(); + if (types==null || types.length!=1) return OBJECT_TYPE; + + PropertyExpression subExp = new PropertyExpression(varX("{}", types[0].getType()), pexp.getPropertyAsString()); + AtomicReference result = new AtomicReference(); + if (existsProperty(subExp, true, new PropertyLookupVisitor(result))) { + intf = LIST_TYPE.getPlainNodeReference(); + ClassNode itemType = result.get(); + intf.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(itemType))}); + return intf; + } + return null; + } + + private ClassNode getTypeForMapPropertyExpression(ClassNode testClass, ClassNode objectExpressionType, PropertyExpression pexp) { + if (!implementsInterfaceOrIsSubclassOf(testClass, MAP_TYPE)) return null; + ClassNode intf; + if (objectExpressionType.getGenericsTypes()!=null) { + intf = GenericsUtils.parameterizeType(objectExpressionType, MAP_TYPE.getPlainNodeReference()); + } else { + intf = MAP_TYPE.getPlainNodeReference(); + } + // 0 is the key, 1 is the value + GenericsType[] types = intf.getGenericsTypes(); + if (types == null || types.length != 2) return OBJECT_TYPE; + + if (pexp.isSpreadSafe()) { + // map*.property syntax + // only "key" and "value" are allowed + if ("key".equals(pexp.getPropertyAsString())) { + ClassNode listKey = LIST_TYPE.getPlainNodeReference(); + listKey.setGenericsTypes(new GenericsType[]{types[0]}); + return listKey; + } else if ("value".equals(pexp.getPropertyAsString())) { + ClassNode listValue = LIST_TYPE.getPlainNodeReference(); + listValue.setGenericsTypes(new GenericsType[]{types[1]}); + return listValue; + } else { + addStaticTypeError("Spread operator on map only allows one of [key,value]", pexp); + } + } else { + return types[1].getType(); + } + return null; + } + + /** + * This method is used to filter search results in which null means "no match", + * to filter out illegal access to instance members from a static context. + * + * Return null if the given member is not static, but we want to access in + * a static way (staticOnly=true). If we want to access in a non-static way + * we always return the member, since then access to static members and + * non-static members is allowed. + */ + private T allowStaticAccessToMember(T member, boolean staticOnly) { + if (member == null) return null; + if (!staticOnly) return member; + boolean isStatic; + if (member instanceof Variable) { + Variable v = (Variable) member; + isStatic = Modifier.isStatic(v.getModifiers()); + } else if (member instanceof List) { + List list = (List) member; + if (list.size()==1) { + return (T) Collections.singletonList(allowStaticAccessToMember(list.get(0), staticOnly)); + } + return (T) Collections.emptyList(); + } else { + MethodNode mn = (MethodNode) member; + isStatic = mn.isStatic(); + } + if (staticOnly && !isStatic) return null; + return member; + } + + private void storeWithResolve(ClassNode typeToResolve, ClassNode receiver, ClassNode declaringClass, boolean isStatic, PropertyExpression expressionToStoreOn) { + ClassNode type = typeToResolve; + if (getGenericsWithoutArray(type)!=null) { + Map resolvedPlaceholders = resolvePlaceHoldersFromDeclaration(receiver, declaringClass, null, isStatic); + type = resolveGenericsWithContext(resolvedPlaceholders, type); + } + storeInferredTypeForPropertyExpression(expressionToStoreOn, type); + storeType(expressionToStoreOn, type); + } + + private boolean storeField(FieldNode field, boolean returnTrueIfFieldExists, PropertyExpression expressionToStoreOn, ClassNode receiver, ClassCodeVisitorSupport visitor, String delegationData, boolean lhsOfAssignment) { + if (field==null || !returnTrueIfFieldExists) return false; + if (visitor != null) visitor.visitField(field); + storeWithResolve(field.getOriginType(), receiver, field.getDeclaringClass(), field.isStatic(), expressionToStoreOn); + checkOrMarkPrivateAccess(expressionToStoreOn, field, lhsOfAssignment); + if (delegationData!=null) { + expressionToStoreOn.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData); + } + return true; + } + + private boolean storeProperty(PropertyNode propertyNode, PropertyExpression expressionToStoreOn, ClassNode receiver, ClassCodeVisitorSupport visitor, String delegationData) { + if (propertyNode == null) return false; + if (visitor != null) visitor.visitProperty(propertyNode); + storeWithResolve(propertyNode.getOriginType(), receiver, propertyNode.getDeclaringClass(), propertyNode.isStatic(), expressionToStoreOn); + if (delegationData!=null) { + expressionToStoreOn.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, delegationData); + } + return true; + } + + protected void storeInferredTypeForPropertyExpression(final PropertyExpression pexp, final ClassNode flatInferredType) { + if (pexp.isSpreadSafe()) { + ClassNode list = LIST_TYPE.getPlainNodeReference(); + list.setGenericsTypes(new GenericsType[] { + new GenericsType(flatInferredType) + }); + storeType(pexp, list); + } else { + storeType(pexp, flatInferredType); + } + } + + @Deprecated + protected SetterInfo hasSetter(final PropertyExpression pexp) { + String propertyName = pexp.getPropertyAsString(); + if (propertyName == null) return null; + + Expression objectExpression = pexp.getObjectExpression(); + List> receivers = new LinkedList>(); + List> owners = makeOwnerList(objectExpression); + addReceivers(receivers, owners, pexp.isImplicitThis()); + + String capName = MetaClassHelper.capitalize(propertyName); + boolean isAttributeExpression = pexp instanceof AttributeExpression; + + for (Receiver receiver: receivers) { + ClassNode testClass = receiver.getType(); + LinkedList queue = new LinkedList(); + queue.add(testClass); + if (testClass.isInterface()) { + queue.addAll(testClass.getAllInterfaces()); + } + while (!queue.isEmpty()) { + ClassNode current = queue.removeFirst(); + current = current.redirect(); + // check that a setter also exists + String setterName = "set" + capName; + List setterMethods = findSetters(current, setterName, false); + if (!setterMethods.isEmpty()) { +// storeType(pexp, setterMethod.getParameters()[0].getType()); + return new SetterInfo(current, setterName, setterMethods); + } + if (!isAttributeExpression && current.getSuperClass() != null) { + queue.add(current.getSuperClass()); + } + } + } + return null; + } + + @Override + public void visitProperty(PropertyNode node) { + final boolean osc = typeCheckingContext.isInStaticContext; + try { + typeCheckingContext.isInStaticContext = node.isInStaticContext(); + currentProperty = node; + super.visitProperty(node); + } finally { + currentProperty = null; + typeCheckingContext.isInStaticContext = osc; + } + } + + @Override + public void visitField(final FieldNode node) { + final boolean osc = typeCheckingContext.isInStaticContext; + try { + typeCheckingContext.isInStaticContext = node.isInStaticContext(); + currentField = node; + super.visitField(node); + Expression init = node.getInitialExpression(); + if (init != null) { + FieldExpression left = new FieldExpression(node); + BinaryExpression bexp = binX( + left, + Token.newSymbol("=", node.getLineNumber(), node.getColumnNumber()), + init + ); + bexp.setSourcePosition(init); + typeCheckAssignment(bexp, left, node.getOriginType(), init, getType(init)); + if (init instanceof ConstructorCallExpression) { + inferDiamondType((ConstructorCallExpression) init, node.getOriginType()); + } + } + } finally { + currentField = null; + typeCheckingContext.isInStaticContext = osc; + } + } + + @Override + public void visitForLoop(final ForStatement forLoop) { + // collect every variable expression used in the loop body + final Map varOrigType = new HashMap(); + forLoop.getLoopBlock().visit(new VariableExpressionTypeMemoizer(varOrigType)); + + // visit body + Map> oldTracker = pushAssignmentTracking(); + Expression collectionExpression = forLoop.getCollectionExpression(); + if (collectionExpression instanceof ClosureListExpression) { + // for (int i=0; i<...; i++) style loop + super.visitForLoop(forLoop); + } else { + collectionExpression.visit(this); + final ClassNode collectionType = getType(collectionExpression); + ClassNode componentType = inferLoopElementType(collectionType); + ClassNode forLoopVariableType = forLoop.getVariableType(); + if (ClassHelper.getUnwrapper(componentType) == forLoopVariableType) { + // prefer primitive type over boxed type + componentType = forLoopVariableType; + } + if (!checkCompatibleAssignmentTypes(forLoopVariableType, componentType)) { + addStaticTypeError("Cannot loop with element of type " + forLoopVariableType.toString(false) + " with collection of type " + collectionType.toString(false), forLoop); + } + if (forLoopVariableType != DYNAMIC_TYPE) { + // user has specified a type, prefer it over the inferred type + componentType = forLoopVariableType; + } + typeCheckingContext.controlStructureVariables.put(forLoop.getVariable(), componentType); + try { + super.visitForLoop(forLoop); + } finally { + typeCheckingContext.controlStructureVariables.remove(forLoop.getVariable()); + } + } + boolean typeChanged = isSecondPassNeededForControlStructure(varOrigType, oldTracker); + if (typeChanged) visitForLoop(forLoop); + } + + /** + * Given a loop collection type, returns the inferred type of the loop element. Used, for + * example, to infer the element type of a (for e in list) loop. + * + * @param collectionType the type of the collection + * @return the inferred component type + */ + public static ClassNode inferLoopElementType(final ClassNode collectionType) { + ClassNode componentType = collectionType.getComponentType(); + if (componentType == null) { + if (implementsInterfaceOrIsSubclassOf(collectionType, ITERABLE_TYPE)) { + ClassNode intf = GenericsUtils.parameterizeType(collectionType, ITERABLE_TYPE); + GenericsType[] genericsTypes = intf.getGenericsTypes(); + componentType = genericsTypes[0].getType(); + } else if (implementsInterfaceOrIsSubclassOf(collectionType, MAP_TYPE)) { + // GROOVY-6240 + ClassNode intf = GenericsUtils.parameterizeType(collectionType, MAP_TYPE); + GenericsType[] genericsTypes = intf.getGenericsTypes(); + componentType = MAP_ENTRY_TYPE.getPlainNodeReference(); + componentType.setGenericsTypes(genericsTypes); + } else if (STRING_TYPE.equals(collectionType)) { + componentType = ClassHelper.Character_TYPE; + } else if (ENUMERATION_TYPE.equals(collectionType)) { + // GROOVY-6123 + ClassNode intf = GenericsUtils.parameterizeType(collectionType, ENUMERATION_TYPE); + GenericsType[] genericsTypes = intf.getGenericsTypes(); + componentType = genericsTypes[0].getType(); + } else { + componentType = ClassHelper.OBJECT_TYPE; + } + } + return componentType; + } + + protected boolean isSecondPassNeededForControlStructure(final Map varOrigType, final Map> oldTracker) { + Map assignedVars = popAssignmentTracking(oldTracker); + for (Map.Entry entry : assignedVars.entrySet()) { + Variable key = findTargetVariable(entry.getKey()); + if (key instanceof VariableExpression) { + ClassNode origType = varOrigType.get(key); + ClassNode newType = entry.getValue(); + if (varOrigType.containsKey(key) && (origType == null || !newType.equals(origType))) { + return true; + } + } + } + return false; + } + + @Override + public void visitWhileLoop(final WhileStatement loop) { + Map> oldTracker = pushAssignmentTracking(); + super.visitWhileLoop(loop); + popAssignmentTracking(oldTracker); + } + + @Override + public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) { + super.visitBitwiseNegationExpression(expression); + ClassNode type = getType(expression); + ClassNode typeRe = type.redirect(); + ClassNode resultType; + if (isBigIntCategory(typeRe)) { + // allow any internal number that is not a floating point one + resultType = type; + } else if (typeRe == STRING_TYPE || typeRe == GSTRING_TYPE) { + resultType = PATTERN_TYPE; + } else if (typeRe == ArrayList_TYPE) { + resultType = ArrayList_TYPE; + } else if (typeRe.equals(PATTERN_TYPE)) { + resultType = PATTERN_TYPE; + } else { + MethodNode mn = findMethodOrFail(expression, type, "bitwiseNegate"); + if (mn!=null) { + resultType = mn.getReturnType(); + } else { + resultType = OBJECT_TYPE; + } + } + storeType(expression, resultType); + } + + @Override + public void visitUnaryPlusExpression(UnaryPlusExpression expression) { + super.visitUnaryPlusExpression(expression); + negativeOrPositiveUnary(expression, "positive"); + } + + @Override + public void visitUnaryMinusExpression(UnaryMinusExpression expression) { + super.visitUnaryMinusExpression(expression); + negativeOrPositiveUnary(expression, "negative"); + } + + @Override + public void visitPostfixExpression(final PostfixExpression expression) { + super.visitPostfixExpression(expression); + Expression inner = expression.getExpression(); + int op = expression.getOperation().getType(); + visitPrefixOrPostifExpression(expression, inner, op); + } + + @Override + public void visitPrefixExpression(final PrefixExpression expression) { + super.visitPrefixExpression(expression); + Expression inner = expression.getExpression(); + int type = expression.getOperation().getType(); + visitPrefixOrPostifExpression(expression, inner, type); + } + + private static ClassNode getMathWideningClassNode(ClassNode type) { + if (byte_TYPE.equals(type) || short_TYPE.equals(type) || int_TYPE.equals(type)) { + return int_TYPE; + } + if (Byte_TYPE.equals(type) || Short_TYPE.equals(type) || Integer_TYPE.equals(type)) { + return Integer_TYPE; + } + if (float_TYPE.equals(type)) return double_TYPE; + if (Float_TYPE.equals(type)) return Double_TYPE; + return type; + } + + private void visitPrefixOrPostifExpression(final Expression origin, final Expression innerExpression, final int operationType) { + boolean isPostfix = origin instanceof PostfixExpression; + ClassNode exprType = getType(innerExpression); + String name = operationType == PLUS_PLUS ? "next" : operationType == MINUS_MINUS ? "previous" : null; + if (isPrimitiveType(exprType) || isPrimitiveType(getUnwrapper(exprType))) { + if (operationType == PLUS_PLUS || operationType == MINUS_MINUS) { + if (!isPrimitiveType(exprType)) { + MethodNode node = findMethodOrFail(varX("_dummy_", exprType), exprType, name); + if (node != null) { + storeTargetMethod(origin, node); + storeType(origin, + isPostfix?exprType:getMathWideningClassNode(exprType)); + return; + } + } + storeType(origin, exprType); + return; + } + addUnsupportedPreOrPostfixExpressionError(origin); + return; + } else if (implementsInterfaceOrIsSubclassOf(exprType, Number_TYPE) && (operationType == PLUS_PLUS || operationType == MINUS_MINUS)) { + // special case for numbers, improve type checking as we can expect ++ and -- to return the same type + MethodNode node = findMethodOrFail(innerExpression, exprType, name); + if (node != null) { + storeTargetMethod(origin, node); + storeType(origin, getMathWideningClassNode(exprType)); + return; + } + } + // not a primitive type. We must find a method which is called next + if (name == null) { + addUnsupportedPreOrPostfixExpressionError(origin); + return; + } + MethodNode node = findMethodOrFail(innerExpression, exprType, name); + if (node != null) { + storeTargetMethod(origin, node); + storeType(origin, isPostfix?exprType:inferReturnTypeGenerics(exprType, node, ArgumentListExpression.EMPTY_ARGUMENTS)); + } + } + + private void negativeOrPositiveUnary(Expression expression, String name) { + ClassNode type = getType(expression); + ClassNode typeRe = type.redirect(); + ClassNode resultType; + if (isDoubleCategory(ClassHelper.getUnwrapper(typeRe))) { + resultType = type; + } else if (typeRe == ArrayList_TYPE) { + resultType = ArrayList_TYPE; + } else { + MethodNode mn = findMethodOrFail(expression, type, name); + if (mn != null) { + resultType = mn.getReturnType(); + } else { + resultType = type; + } + } + storeType(expression, resultType); + } + + @Override + protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { + typeCheckingContext.pushEnclosingMethod(node); + if (!isSkipMode(node) && !shouldSkipMethodNode(node)) { + super.visitConstructorOrMethod(node, isConstructor); + } + if (!isConstructor) { + returnAdder.visitMethod(node); + } + typeCheckingContext.popEnclosingMethod(); + } + + @Override + public void visitReturnStatement(ReturnStatement statement) { + super.visitReturnStatement(statement); + returnListener.returnStatementAdded(statement); + } + + protected ClassNode checkReturnType(final ReturnStatement statement) { + Expression expression = statement.getExpression(); + ClassNode type = getType(expression); + if (typeCheckingContext.getEnclosingClosure()!=null) { + return type; + } + // handle instanceof cases + if ((expression instanceof VariableExpression) && hasInferredReturnType(expression)) { + type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } + MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod(); + if (enclosingMethod != null && typeCheckingContext.getEnclosingClosure()==null) { + if (!enclosingMethod.isVoidMethod() + && !type.equals(void_WRAPPER_TYPE) + && !type.equals(VOID_TYPE) + && !checkCompatibleAssignmentTypes(enclosingMethod.getReturnType(), type, null, false) + && !(isNullConstant(expression))) { + if (!extension.handleIncompatibleReturnType(statement, type)) { + addStaticTypeError("Cannot return value of type " + type.toString(false) + " on method returning type " + enclosingMethod.getReturnType().toString(false), expression); + } + } else if (!enclosingMethod.isVoidMethod()) { + ClassNode previousType = getInferredReturnType(enclosingMethod); + ClassNode inferred = previousType == null ? type : lowestUpperBound(type, previousType); + if (implementsInterfaceOrIsSubclassOf(inferred, enclosingMethod.getReturnType())) { + if (missesGenericsTypes(inferred)) { + DeclarationExpression virtualDecl = new DeclarationExpression( + varX("{target}", enclosingMethod.getReturnType()), + Token.newSymbol(EQUAL, -1, -1), + varX("{source}", type) + ); + virtualDecl.setSourcePosition(statement); + virtualDecl.visit(this); + ClassNode newlyInferred = (ClassNode) virtualDecl.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + if (!missesGenericsTypes(newlyInferred)) type = newlyInferred; + } else { + checkTypeGenerics(enclosingMethod.getReturnType(), inferred, expression); + } + return type; + } else { + return enclosingMethod.getReturnType(); + } + } + } + return type; + } + + protected void addClosureReturnType(ClassNode returnType) { + typeCheckingContext.getEnclosingClosure().addReturnType(returnType); + } + + @Override + public void visitConstructorCallExpression(ConstructorCallExpression call) { + super.visitConstructorCallExpression(call); + if (extension.beforeMethodCall(call)) { + extension.afterMethodCall(call); + return; + } + ClassNode receiver = call.isThisCall() ? typeCheckingContext.getEnclosingClassNode() : + call.isSuperCall() ? typeCheckingContext.getEnclosingClassNode().getSuperClass() : call.getType(); + Expression arguments = call.getArguments(); + + ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(arguments); + + checkForbiddenSpreadArgument(argumentList); + + ClassNode[] args = getArgumentTypes(argumentList); + if ( args.length>0 && + typeCheckingContext.getEnclosingClosure()!=null && + argumentList.getExpression(0) instanceof VariableExpression && + ((VariableExpression) argumentList.getExpression(0)).isThisExpression() && + call.getType() instanceof InnerClassNode && + call.getType().getOuterClass().equals(args[0]) && + !call.getType().isStaticClass()) + { + args[0] = CLOSURE_TYPE; + } + + + MethodNode node; + if (looksLikeNamedArgConstructor(receiver, args) + && findMethod(receiver, "", DefaultGroovyMethods.init(args)).size() == 1 + && findMethod(receiver, "", args).isEmpty()) { + // bean-style constructor + node = typeCheckMapConstructor(call, receiver, arguments); + if (node != null) { + storeTargetMethod(call, node); + extension.afterMethodCall(call); + return; + } + } + node = findMethodOrFail(call, receiver, "", args); + if (node != null) { + if (looksLikeNamedArgConstructor(receiver, args) && node.getParameters().length + 1 == args.length) { + node = typeCheckMapConstructor(call, receiver, arguments); + } else { + typeCheckMethodsWithGenericsOrFail(receiver, args, node, call); + } + if (node != null) storeTargetMethod(call, node); + } + extension.afterMethodCall(call); + } + + private boolean looksLikeNamedArgConstructor(ClassNode receiver, ClassNode[] args) { + return (args.length == 1 || args.length == 2 && isInnerConstructor(receiver, args[0])) + && implementsInterfaceOrIsSubclassOf(args[args.length - 1], MAP_TYPE); + } + + private boolean isInnerConstructor(ClassNode receiver, ClassNode parent) { + return receiver.isRedirectNode() && receiver.redirect() instanceof InnerClassNode && + receiver.redirect().getOuterClass().equals(parent); + } + + protected MethodNode typeCheckMapConstructor(final ConstructorCallExpression call, final ClassNode receiver, final Expression arguments) { + MethodNode node = null; + if (arguments instanceof TupleExpression) { + TupleExpression texp = (TupleExpression) arguments; + List expressions = texp.getExpressions(); + // should only get here with size = 2 when inner class constructor + if (expressions.size() == 1 || expressions.size() == 2) { + Expression expression = expressions.get(expressions.size() - 1); + if (expression instanceof MapExpression) { + MapExpression argList = (MapExpression) expression; + checkGroovyConstructorMap(call, receiver, argList); + Parameter[] params = expressions.size() == 1 + ? new Parameter[]{new Parameter(MAP_TYPE, "map")} + : new Parameter[]{new Parameter(receiver.redirect().getOuterClass(), "$p$"), new Parameter(MAP_TYPE, "map")}; + node = new ConstructorNode(Opcodes.ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT); + node.setDeclaringClass(receiver); + } + } + } + return node; + } + + protected ClassNode[] getArgumentTypes(ArgumentListExpression args) { + List arglist = args.getExpressions(); + ClassNode[] ret = new ClassNode[arglist.size()]; + for (int i = 0; i < arglist.size(); i++) { + Expression exp = arglist.get(i); + if (isNullConstant(exp)) { + ret[i] = UNKNOWN_PARAMETER_TYPE; + } else { + ret[i] = getInferredTypeFromTempInfo(exp, getType(exp)); + } + } + return ret; + } + + private ClassNode getInferredTypeFromTempInfo(Expression exp, ClassNode result) { + Map> info = typeCheckingContext.temporaryIfBranchTypeInformation.empty() ? null : typeCheckingContext.temporaryIfBranchTypeInformation.peek(); + if (exp instanceof VariableExpression && info != null) { + List classNodes = getTemporaryTypesForExpression(exp); + if (classNodes != null && !classNodes.isEmpty()) { + ArrayList arr = new ArrayList(classNodes.size() + 1); + if (result != null && !classNodes.contains(result)) arr.add(result); + arr.addAll(classNodes); + // GROOVY-7333: filter out Object + Iterator iterator = arr.iterator(); + while (iterator.hasNext()) { + ClassNode next = iterator.next(); + if (ClassHelper.OBJECT_TYPE.equals(next)) { + iterator.remove(); + } + } + if (arr.isEmpty()) { + result = ClassHelper.OBJECT_TYPE.getPlainNodeReference(); + } else if (arr.size()==1) { + result = arr.get(0); + } else { + result = new UnionTypeClassNode(arr.toArray(new ClassNode[arr.size()])); + } + } + } + return result; + } + + @Override + public void visitClosureExpression(final ClosureExpression expression) { + boolean oldStaticContext = typeCheckingContext.isInStaticContext; + typeCheckingContext.isInStaticContext = false; + + // collect every variable expression used in the loop body + final Map varOrigType = new HashMap(); + Statement code = expression.getCode(); + code.visit(new VariableExpressionTypeMemoizer(varOrigType)); + Map> oldTracker = pushAssignmentTracking(); + + // first, collect closure shared variables and reinitialize types + SharedVariableCollector collector = new SharedVariableCollector(getSourceUnit()); + collector.visitClosureExpression(expression); + Set closureSharedExpressions = collector.getClosureSharedExpressions(); + Map typesBeforeVisit = null; + if (!closureSharedExpressions.isEmpty()) { + typesBeforeVisit = new HashMap(); + saveVariableExpressionMetadata(closureSharedExpressions, typesBeforeVisit); + } + + // perform visit + typeCheckingContext.pushEnclosingClosureExpression(expression); + DelegationMetadata dmd = getDelegationMetadata(expression); + if (dmd ==null) { + typeCheckingContext.delegationMetadata = new DelegationMetadata( + typeCheckingContext.getEnclosingClassNode(), Closure.OWNER_FIRST, typeCheckingContext.delegationMetadata + ); + } else { + typeCheckingContext.delegationMetadata = new DelegationMetadata( + dmd.getType(), + dmd.getStrategy(), + typeCheckingContext.delegationMetadata + ); + } + super.visitClosureExpression(expression); + typeCheckingContext.delegationMetadata = typeCheckingContext.delegationMetadata.getParent(); + MethodNode node = new MethodNode("dummy", 0, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, code); + returnAdder.visitMethod(node); + + TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure(); + if (!enclosingClosure.getReturnTypes().isEmpty()) { + ClassNode returnType = lowestUpperBound(enclosingClosure.getReturnTypes()); + storeInferredReturnType(expression, returnType); + ClassNode inferredType = wrapClosureType(returnType); + storeType(enclosingClosure.getClosureExpression(), inferredType); + } + + typeCheckingContext.popEnclosingClosure(); + + boolean typeChanged = isSecondPassNeededForControlStructure(varOrigType, oldTracker); + if (typeChanged) visitClosureExpression(expression); + + // restore original metadata + restoreVariableExpressionMetadata(typesBeforeVisit); + typeCheckingContext.isInStaticContext = oldStaticContext; + Parameter[] parameters = expression.getParameters(); + if (parameters!=null) { + for (Parameter parameter : parameters) { + typeCheckingContext.controlStructureVariables.remove(parameter); + } + } + } + + private static ClassNode wrapClosureType(final ClassNode returnType) { + ClassNode inferredType = CLOSURE_TYPE.getPlainNodeReference(); + inferredType.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(returnType))}); + return inferredType; + } + + protected DelegationMetadata getDelegationMetadata(final ClosureExpression expression) { + return (DelegationMetadata) expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA); + } + + protected void restoreVariableExpressionMetadata(final Map typesBeforeVisit) { + if (typesBeforeVisit != null) { + for (Map.Entry entry : typesBeforeVisit.entrySet()) { + VariableExpression ve = entry.getKey(); + ListHashMap metadata = entry.getValue(); + for (StaticTypesMarker marker : StaticTypesMarker.values()) { + ve.removeNodeMetaData(marker); + Object value = metadata.get(marker); + if (value != null) ve.setNodeMetaData(marker, value); + } + } + } + } + + protected void saveVariableExpressionMetadata(final Set closureSharedExpressions, final Map typesBeforeVisit) { + for (VariableExpression ve : closureSharedExpressions) { + // GROOVY-6921: We must force a call to getType in order to update closure shared variable whose + // types are inferred thanks to closure parameter type inference + getType(ve); + ListHashMap metadata = new ListHashMap(); + for (StaticTypesMarker marker : StaticTypesMarker.values()) { + Object value = ve.getNodeMetaData(marker); + if (value != null) { + metadata.put(marker, value); + } + } + typesBeforeVisit.put(ve, metadata); + Variable accessedVariable = ve.getAccessedVariable(); + if (accessedVariable != ve && accessedVariable instanceof VariableExpression) { + saveVariableExpressionMetadata(Collections.singleton((VariableExpression) accessedVariable), typesBeforeVisit); + } + } + } + + protected boolean shouldSkipMethodNode(final MethodNode node) { + Object type = node.getNodeMetaData(StaticTypeCheckingVisitor.class); + return Boolean.TRUE.equals(type); + } + + @Override + public void visitMethod(final MethodNode node) { + if (shouldSkipMethodNode(node)) { + // method has already been visited by a static type checking visitor + return; + } + if (!extension.beforeVisitMethod(node)) { + ErrorCollector collector = (ErrorCollector) node.getNodeMetaData(ERROR_COLLECTOR); + if (collector != null) { + typeCheckingContext.getErrorCollector().addCollectorContents(collector); + } else { + startMethodInference(node, typeCheckingContext.getErrorCollector()); + } + node.removeNodeMetaData(ERROR_COLLECTOR); + } + extension.afterVisitMethod(node); + } + + @Override + public void visitConstructor(final ConstructorNode node) { + if (shouldSkipMethodNode(node)) { + // method has already been visited by a static type checking visitor + return; + } + for (Parameter parameter : node.getParameters()) { + if (parameter.getInitialExpression()!=null) { + parameter.getInitialExpression().visit(this); + } + } + super.visitConstructor(node); + } + + protected void startMethodInference(final MethodNode node, ErrorCollector collector) { + if (isSkipMode(node)) return; + + // second, we must ensure that this method MUST be statically checked + // for example, in a mixed mode where only some methods are statically checked + // we must not visit a method which used dynamic dispatch. + // We do not check for an annotation because some other AST transformations + // may use this visitor without the annotation being explicitly set + if (!typeCheckingContext.methodsToBeVisited.isEmpty() && !typeCheckingContext.methodsToBeVisited.contains(node)) return; + + // alreadyVisitedMethods prevents from visiting the same method multiple times + // and prevents from infinite loops + if (typeCheckingContext.alreadyVisitedMethods.contains(node)) return; + typeCheckingContext.alreadyVisitedMethods.add(node); + + typeCheckingContext.pushErrorCollector(collector); + + final boolean osc = typeCheckingContext.isInStaticContext; + try { + typeCheckingContext.isInStaticContext = node.isStatic(); + super.visitMethod(node); + for (Parameter parameter : node.getParameters()) { + if (parameter.getInitialExpression()!=null) { + parameter.getInitialExpression().visit(this); + } + } +/* + ClassNode rtype = getInferredReturnType(node); + if (rtype == null) { + storeInferredReturnType(node, node.getReturnType()); + } + addTypeCheckingInfoAnnotation(node); +*/ + } finally { + typeCheckingContext.isInStaticContext = osc; + } + + typeCheckingContext.popErrorCollector(); + node.putNodeMetaData(ERROR_COLLECTOR, collector); + } + + protected void addTypeCheckingInfoAnnotation(final MethodNode node) { + // TypeChecked$TypeCheckingInfo can not be applied on constructors + if (node instanceof ConstructorNode) return; + + // if a returned inferred type is available and no @TypeCheckingInfo is on node, then add an + // annotation to the method node + ClassNode rtype = getInferredReturnType(node); + if (rtype != null && node.getAnnotations(TYPECHECKING_INFO_NODE).isEmpty()) { + AnnotationNode anno = new AnnotationNode(TYPECHECKING_INFO_NODE); + anno.setMember("version", CURRENT_SIGNATURE_PROTOCOL); + SignatureCodec codec = SignatureCodecFactory.getCodec(CURRENT_SIGNATURE_PROTOCOL_VERSION, getTransformLoader()); + String genericsSignature = codec.encode(rtype); + if (genericsSignature != null) { + ConstantExpression signature = new ConstantExpression(genericsSignature); + signature.setType(STRING_TYPE); + anno.setMember("inferredType", signature); + node.addAnnotation(anno); + } + } + } + + @Override + public void visitStaticMethodCallExpression(final StaticMethodCallExpression call) { + final String name = call.getMethod(); + if (name == null) { + addStaticTypeError("cannot resolve dynamic method name at compile time.", call); + return; + } + + if (extension.beforeMethodCall(call)) { + extension.afterMethodCall(call); + return; + } + + Expression callArguments = call.getArguments(); + + ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments); + + checkForbiddenSpreadArgument(argumentList); + + final ClassNode receiver = call.getOwnerType(); + visitMethodCallArguments(receiver, argumentList, false, null); + + ClassNode[] args = getArgumentTypes(argumentList); + + try { + + // method call receivers are : + // - possible "with" receivers + // - the actual receiver as found in the method call expression + // - any of the potential receivers found in the instanceof temporary table + // in that order + List> receivers = new LinkedList>(); + addReceivers(receivers, makeOwnerList(new ClassExpression(receiver)), false); + List mn = null; + Receiver chosenReceiver = null; + for (Receiver currentReceiver : receivers) { + mn = findMethod(currentReceiver.getType(), name, args); + if (!mn.isEmpty()) { + if (mn.size() == 1) typeCheckMethodsWithGenericsOrFail(currentReceiver.getType(), args, mn.get(0), call); + chosenReceiver = currentReceiver; + break; + } + } + if (mn.isEmpty()) { + mn = extension.handleMissingMethod(receiver, name, argumentList, args, call); + } + boolean callArgsVisited = false; + if (mn.isEmpty()) { + addNoMatchingMethodError(receiver, name, args, call); + } else { + mn = disambiguateMethods(mn, receiver, args, call); + if (mn.size() == 1) { + MethodNode directMethodCallCandidate = mn.get(0); + ClassNode returnType = getType(directMethodCallCandidate); + if (returnType.isUsingGenerics() && !returnType.isEnum()) { + visitMethodCallArguments(receiver, argumentList, true, directMethodCallCandidate); + ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), directMethodCallCandidate, callArguments); + returnType = irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType) ? irtg : returnType; + callArgsVisited = true; + } + storeType(call, returnType); + storeTargetMethod(call, directMethodCallCandidate); + + } else { + addAmbiguousErrorMessage(mn, name, args, call); + } + if (!callArgsVisited) { + visitMethodCallArguments(receiver, argumentList, true, (MethodNode)call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET)); + } + } + } finally { + extension.afterMethodCall(call); + } + } + + /** + * @deprecated this method is unused, replaced with {@link DelegatesTo} inference. + * @param callArguments + * @param receiver + */ + @Deprecated + protected void checkClosureParameters(final Expression callArguments, final ClassNode receiver) { + if (callArguments instanceof ArgumentListExpression) { + ArgumentListExpression argList = (ArgumentListExpression) callArguments; + ClosureExpression closure = (ClosureExpression) argList.getExpression(0); + Parameter[] parameters = closure.getParameters(); + if (parameters.length > 1) { + addStaticTypeError("Unexpected number of parameters for a with call", argList); + } else if (parameters.length == 1) { + Parameter param = parameters[0]; + if (!param.isDynamicTyped() && !isAssignableTo(receiver, param.getType().redirect())) { + addStaticTypeError("Expected parameter type: " + receiver.toString(false) + " but was: " + param.getType().redirect().toString(false), param); + } + } + closure.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata( + receiver, + Closure.DELEGATE_FIRST, + typeCheckingContext.delegationMetadata + )); + } + } + + /** + * visit a method call target, to infer the type. Don't report errors right + * away, that will be done by a later visitMethod call + */ + protected void silentlyVisitMethodNode(final MethodNode directMethodCallCandidate) { + // visit is authorized because the classnode belongs to the same source unit + ErrorCollector collector = new ErrorCollector(typeCheckingContext.getErrorCollector().getConfiguration()); + startMethodInference(directMethodCallCandidate, collector); + } + + protected void visitMethodCallArguments(final ClassNode receiver, ArgumentListExpression arguments, boolean visitClosures, final MethodNode selectedMethod) { + Parameter[] params = selectedMethod != null ? selectedMethod.getParameters() : Parameter.EMPTY_ARRAY; + List expressions = new LinkedList(arguments.getExpressions()); + if (selectedMethod instanceof ExtensionMethodNode) { + params = ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode().getParameters(); + expressions.add(0, varX("$self", receiver)); + } + ArgumentListExpression newArgs = args(expressions); + + for (int i = 0, expressionsSize = expressions.size(); i < expressionsSize; i++) { + final Expression expression = expressions.get(i); + if (visitClosures && expression instanceof ClosureExpression + || !visitClosures && !(expression instanceof ClosureExpression)) { + if (i0) { + inferClosureParameterTypes(receiver, arguments, (ClosureExpression)expression, param, selectedMethod); + } + } else { + inferClosureParameterTypes(receiver, newArgs, (ClosureExpression) expression, param, selectedMethod); + } + } + expression.visit(this); + if (expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA)!=null) { + expression.removeNodeMetaData(StaticTypesMarker.DELEGATION_METADATA); + } + } + } + } + + /** + * This method is responsible for performing type inference on closure argument types whenever code like this is + * found: foo.collect { it.toUpperCase() }. + * In this case, the type checker tries to find if the collect method has its {@link Closure} argument + * annotated with {@link groovy.transform.stc.ClosureParams}. If yes, then additional type inference can be performed + * and the type of it may be inferred. + * + * @param receiver + * @param arguments + * @param expression a closure expression for which the argument types should be inferred + * @param param the parameter where to look for a {@link groovy.transform.stc.ClosureParams} annotation. + * @param selectedMethod the method accepting a closure + */ + protected void inferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final Parameter param, final MethodNode selectedMethod) { + List annotations = param.getAnnotations(CLOSUREPARAMS_CLASSNODE); + if (annotations!=null && !annotations.isEmpty()) { + for (AnnotationNode annotation : annotations) { + Expression hintClass = annotation.getMember("value"); + Expression options = annotation.getMember("options"); + Expression resolverClass = annotation.getMember("conflictResolutionStrategy"); + if (hintClass instanceof ClassExpression) { + doInferClosureParameterTypes(receiver, arguments, expression, selectedMethod, hintClass, resolverClass, options); + } + } + } else if (isSAMType(param.getOriginType())) { + // SAM coercion + inferSAMType(param, receiver, selectedMethod, InvocationWriter.makeArgumentList(arguments), expression); + } + } + + private void inferSAMType(Parameter param, ClassNode receiver, MethodNode methodWithSAMParameter, ArgumentListExpression originalMethodCallArguments, ClosureExpression openBlock) { + // In a method call with SAM coercion the inference is to be + // understood as a two phase process. We have the normal method call + // to the target method with the closure argument and we have the + // SAM method that will be called inside the normal target method. + // To infer correctly we have to "simulate" this process. We know the + // call to the closure will be done through the SAM type, so the SAM + // type generics deliver information about the Closure. At the same + // time the SAM class is used in the target method parameter, + // providing a connection from the SAM type and the target method + // declaration class. + + // First we try to get as much information about the declaration + // class through the receiver + Map targetMethodDeclarationClassConnections = new HashMap(); + extractGenericsConnections(targetMethodDeclarationClassConnections, receiver, receiver.redirect()); + // then we use the method with the SAM parameter to get more information about the declaration + Parameter[] parametersOfMethodContainingSAM = methodWithSAMParameter.getParameters(); + for (int i = 0; i < parametersOfMethodContainingSAM.length; i++) { + // potentially skip empty varargs + if (i == parametersOfMethodContainingSAM.length - 1 + && i == originalMethodCallArguments.getExpressions().size() + && parametersOfMethodContainingSAM[i].getType().isArray()) + continue; + Expression callArg = originalMethodCallArguments.getExpression(i); + // we look at the closure later in detail, so skip it here + if (callArg == openBlock) continue; + ClassNode parameterType = parametersOfMethodContainingSAM[i].getType(); + extractGenericsConnections(targetMethodDeclarationClassConnections, getType(callArg), parameterType); + } + + // To make a connection to the SAM class we use that new information + // to replace the generics in the SAM type parameter of the target + // method and than that to make the connections to the SAM type generics + ClassNode paramTypeWithReceiverInformation = applyGenericsContext(targetMethodDeclarationClassConnections, param.getOriginType()); + Map SAMTypeConnections = new HashMap(); + ClassNode classForSAM = paramTypeWithReceiverInformation.redirect(); + extractGenericsConnections(SAMTypeConnections, paramTypeWithReceiverInformation, classForSAM); + + // should the open block provide final information we apply that + // to the corresponding parameters of the SAM type method + MethodNode methodForSAM = findSAM(classForSAM); + ClassNode[] parameterTypesForSAM = extractTypesFromParameters(methodForSAM.getParameters()); + ClassNode[] blockParameterTypes = (ClassNode[]) openBlock.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS); + if (blockParameterTypes==null) { + Parameter[] p = openBlock.getParameters(); + if (p == null) { + // zero parameter closure e.g. { -> println 'no args' } + blockParameterTypes = ClassNode.EMPTY_ARRAY; + } else if (p.length==0 && parameterTypesForSAM.length!=0) { + // implicit it + blockParameterTypes = parameterTypesForSAM; + } else { + blockParameterTypes = new ClassNode[p.length]; + for (int i = 0; i < p.length; i++) { + if (p[i] != null && !p[i].isDynamicTyped()) { + blockParameterTypes[i] = p[i].getType(); + } else { + blockParameterTypes[i] = typeOrNull(parameterTypesForSAM, i); + } + } + } + } + for (int i=0; i getSignaturesFromHint(final ClosureExpression expression, final MethodNode selectedMethod, final Expression hintClass, final Expression options) { + // initialize hints + List closureSignatures; + try { + ClassLoader transformLoader = getTransformLoader(); + Class hint = (Class) transformLoader.loadClass(hintClass.getText()); + ClosureSignatureHint hintInstance = hint.newInstance(); + closureSignatures = hintInstance.getClosureSignatures( + selectedMethod instanceof ExtensionMethodNode ?((ExtensionMethodNode) selectedMethod).getExtensionMethodNode():selectedMethod, + typeCheckingContext.source, + typeCheckingContext.compilationUnit, + convertToStringArray(options), expression); + } catch (ClassNotFoundException e) { + throw new GroovyBugError(e); + } catch (InstantiationException e) { + throw new GroovyBugError(e); + } catch (IllegalAccessException e) { + throw new GroovyBugError(e); + } + return closureSignatures; + } + + private List resolveWithResolver(List candidates, ClassNode receiver, Expression arguments, final ClosureExpression expression, final MethodNode selectedMethod, final Expression resolverClass, final Expression options) { + // initialize resolver + try { + ClassLoader transformLoader = getTransformLoader(); + Class resolver = (Class) transformLoader.loadClass(resolverClass.getText()); + ClosureSignatureConflictResolver resolverInstance = resolver.newInstance(); + return resolverInstance.resolve( + candidates, + receiver, + arguments, + expression, + selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod, + typeCheckingContext.source, + typeCheckingContext.compilationUnit, + convertToStringArray(options)); + } catch (ClassNotFoundException e) { + throw new GroovyBugError(e); + } catch (InstantiationException e) { + throw new GroovyBugError(e); + } catch (IllegalAccessException e) { + throw new GroovyBugError(e); + } + } + + private ClassLoader getTransformLoader() { + CompilationUnit compilationUnit = typeCheckingContext.getCompilationUnit(); + return compilationUnit!=null?compilationUnit.getTransformLoader():getSourceUnit().getClassLoader(); + } + + private void doInferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final MethodNode selectedMethod, final Expression hintClass, Expression resolverClass, final Expression options) { + List closureSignatures = getSignaturesFromHint(expression, selectedMethod, hintClass, options); + List candidates = new LinkedList(); + Parameter[] closureParams = expression.getParameters(); + for (ClassNode[] signature : closureSignatures) { + // in order to compute the inferred types of the closure parameters, we're using the following trick: + // 1. create a dummy MethodNode for which the return type is a class node for which the generic types are the types returned by the hint + // 2. call inferReturnTypeGenerics + // 3. fetch inferred types from the result of inferReturnTypeGenerics + // In practice, it could be done differently but it has the main advantage of reusing + // existing code, hence reducing the amount of code to debug in case of failure. + ClassNode[] inferred = resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature); + if (signature.length == closureParams.length // same number of arguments + || (signature.length == 1 && closureParams.length == 0) // implicit it + || (closureParams.length > signature.length && inferred[inferred.length - 1].isArray())) { // vargs + candidates.add(inferred); + } + } + if (candidates.size()>1) { + Iterator candIt = candidates.iterator(); + while (candIt.hasNext()) { + ClassNode[] inferred = candIt.next(); + final int length = closureParams.length; + for (int i = 0; i < length; i++) { + Parameter closureParam = closureParams[i]; + final ClassNode originType = closureParam.getOriginType(); + ClassNode inferredType; + if (i 1 && resolverClass instanceof ClassExpression) { + candidates = resolveWithResolver(candidates, receiver, arguments, expression, selectedMethod, resolverClass, options); + } + if (candidates.size()>1) { + addError("Ambiguous prototypes for closure. More than one target method matches. Please use explicit argument types.", expression); + } + } + if (candidates.size()==1) { + ClassNode[] inferred = candidates.get(0); + if (closureParams.length==0 && inferred.length==1) { + expression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, inferred); + } else { + final int length = closureParams.length; + for (int i = 0; i < length; i++) { + Parameter closureParam = closureParams[i]; + final ClassNode originType = closureParam.getOriginType(); + ClassNode inferredType = OBJECT_TYPE; + if (i list = ((ListExpression) options).getExpressions(); + List result = new ArrayList(list.size()); + for (Expression expression : list) { + result.add(expression.getText()); + } + return result.toArray(new String[result.size()]); + } + throw new IllegalArgumentException("Unexpected options for @ClosureParams:"+options); + } + + private void checkClosureWithDelegatesTo(final ClassNode receiver, + final MethodNode mn, + final ArgumentListExpression arguments, + final Parameter[] params, + final Expression expression, + final Parameter param) { + List annotations = param.getAnnotations(DELEGATES_TO); + if (annotations!=null && !annotations.isEmpty()) { + for (AnnotationNode annotation : annotations) { + // in theory, there can only be one annotation of that type + Expression value = annotation.getMember("value"); + Expression strategy = annotation.getMember("strategy"); + Expression genericTypeIndex = annotation.getMember("genericTypeIndex"); + Expression type = annotation.getMember("type"); + Integer stInt = Closure.OWNER_FIRST; + if (strategy!=null) { + stInt = (Integer) evaluateExpression(castX(ClassHelper.Integer_TYPE,strategy), typeCheckingContext.source.getConfiguration()); + } + if (value instanceof ClassExpression && !value.getType().equals(DELEGATES_TO_TARGET)) { + if (genericTypeIndex!=null) { + addStaticTypeError("Cannot use @DelegatesTo(genericTypeIndex="+genericTypeIndex.getText() + +") without @DelegatesTo.Target because generic argument types are not available at runtime", value); + } + // temporarily store the delegation strategy and the delegate type + expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(value.getType(), stInt, typeCheckingContext.delegationMetadata)); + } else if (type!=null && !"".equals(type.getText()) && type instanceof ConstantExpression) { + String typeString = type.getText(); + ClassNode[] resolved = GenericsUtils.parseClassNodesFromString( + typeString, + getSourceUnit(), + typeCheckingContext.compilationUnit, + mn, + type + ); + if (resolved!=null) { + if (resolved.length==1) { + resolved = resolveGenericsFromTypeHint(receiver, arguments, mn, resolved ); + expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(resolved[0], stInt, typeCheckingContext.delegationMetadata)); + } else { + addStaticTypeError("Incorrect type hint found in method " + (mn), type); + } + } + } else { + final List expressions = arguments.getExpressions(); + final int expressionsSize = expressions.size(); + Expression parameter = annotation.getMember("target"); + String parameterName = parameter!=null && parameter instanceof ConstantExpression ?parameter.getText():""; + // todo: handle vargs! + for (int j = 0, paramsLength = params.length; j < paramsLength; j++) { + final Parameter methodParam = params[j]; + List targets = methodParam.getAnnotations(DELEGATES_TO_TARGET); + if (targets != null && targets.size() == 1) { + AnnotationNode targetAnnotation = targets.get(0); // @DelegatesTo.Target Obj foo + Expression idMember = targetAnnotation.getMember("value"); + String id = idMember != null && idMember instanceof ConstantExpression ? idMember.getText() : ""; + if (id.equals(parameterName)) { + if (j < expressionsSize) { + Expression actualArgument = expressions.get(j); + ClassNode actualType = getType(actualArgument); + if (genericTypeIndex!=null && genericTypeIndex instanceof ConstantExpression) { + int gti = Integer.parseInt(genericTypeIndex.getText()); + ClassNode paramType = methodParam.getType(); // type annotated with @DelegatesTo.Target + GenericsType[] genericsTypes = paramType.getGenericsTypes(); + if (genericsTypes==null) { + addStaticTypeError("Cannot use @DelegatesTo(genericTypeIndex="+genericTypeIndex.getText() + + ") with a type that doesn't use generics", methodParam); + } else if (gti<0 || gti>=genericsTypes.length) { + addStaticTypeError("Index of generic type @DelegatesTo(genericTypeIndex="+genericTypeIndex.getText() + + ") "+(gti<0?"lower":"greater")+" than those of the selected type", methodParam); + } else { + ClassNode pType = GenericsUtils.parameterizeType(actualType, paramType); + GenericsType[] pTypeGenerics = pType.getGenericsTypes(); + if (pTypeGenerics!=null && pTypeGenerics.length>gti) { + actualType = pTypeGenerics[gti].getType(); + } else { + addStaticTypeError("Unable to map actual type ["+actualType.toString(false)+"] onto "+paramType.toString(false), methodParam); + } + } + } + expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(actualType, stInt, typeCheckingContext.delegationMetadata)); + break; + } + } + } + } + if (expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA)==null) { + addError("Not enough arguments found for a @DelegatesTo method call. Please check that you either use an explicit class or @DelegatesTo.Target with a correct id", arguments); + } + } + } + } + } + + private static boolean isTraitHelper(ClassNode node) { + return node instanceof InnerClassNode && Traits.isTrait(node.getOuterClass()); + } + + protected void addReceivers(final List> receivers, + final Collection> owners, + final boolean implicitThis) { + if (typeCheckingContext.delegationMetadata ==null || !implicitThis) { + receivers.addAll(owners); + return; + } + + DelegationMetadata dmd = typeCheckingContext.delegationMetadata; + StringBuilder path = new StringBuilder(); + while (dmd != null) { + int strategy = dmd.getStrategy(); + ClassNode delegate = dmd.getType(); + dmd = dmd.getParent(); + switch (strategy) { + case Closure.OWNER_FIRST: + receivers.addAll(owners); + path.append("delegate"); + doAddDelegateReceiver(receivers, path, delegate); + break; + case Closure.DELEGATE_FIRST: + path.append("delegate"); + doAddDelegateReceiver(receivers, path, delegate); + receivers.addAll(owners); + break; + case Closure.OWNER_ONLY: + receivers.addAll(owners); + dmd = null; + break; + case Closure.DELEGATE_ONLY: + path.append("delegate"); + doAddDelegateReceiver(receivers, path, delegate); + dmd = null; + break; + } + path.append('.'); + } + } + + private static void doAddDelegateReceiver(final List> receivers, final StringBuilder path, final ClassNode delegate) { + receivers.add(new Receiver(delegate, path.toString())); + if (isTraitHelper(delegate)) { + receivers.add(new Receiver(delegate.getOuterClass(), path.toString())); + } + } + + @Override + public void visitMethodCallExpression(MethodCallExpression call) { + final String name = call.getMethodAsString(); + if (name == null) { + addStaticTypeError("cannot resolve dynamic method name at compile time.", call.getMethod()); + return; + } + if (extension.beforeMethodCall(call)) { + extension.afterMethodCall(call); + return; + } + typeCheckingContext.pushEnclosingMethodCall(call); + final Expression objectExpression = call.getObjectExpression(); + + objectExpression.visit(this); + call.getMethod().visit(this); + + // if the call expression is a spread operator call, then we must make sure that + // the call is made on a collection type + if (call.isSpreadSafe()) { + //TODO check if this should not be change to iterator based call logic + ClassNode expressionType = getType(objectExpression); + if (!implementsInterfaceOrIsSubclassOf(expressionType, Collection_TYPE) && !expressionType.isArray()) { + addStaticTypeError("Spread operator can only be used on collection types", objectExpression); + return; + } else { + // type check call as if it was made on component type + ClassNode componentType = inferComponentType(expressionType, int_TYPE); + MethodCallExpression subcall = callX(castX(componentType, EmptyExpression.INSTANCE), name, call.getArguments()); + subcall.setLineNumber(call.getLineNumber()); + subcall.setColumnNumber(call.getColumnNumber()); + subcall.setImplicitThis(call.isImplicitThis()); + visitMethodCallExpression(subcall); + // the inferred type here should be a list of what the subcall returns + ClassNode subcallReturnType = getType(subcall); + ClassNode listNode = LIST_TYPE.getPlainNodeReference(); + listNode.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(subcallReturnType))}); + storeType(call, listNode); + // store target method + storeTargetMethod(call, (MethodNode) subcall.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET)); + typeCheckingContext.popEnclosingMethodCall(); + return; + } + } + + Expression callArguments = call.getArguments(); + ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments); + + checkForbiddenSpreadArgument(argumentList); + + // for arguments, we need to visit closures *after* the method has been chosen + + + final ClassNode receiver = getType(objectExpression); + visitMethodCallArguments(receiver, argumentList, false, null); + + ClassNode[] args = getArgumentTypes(argumentList); + final boolean isCallOnClosure = isClosureCall(name, objectExpression, callArguments); + + try { + boolean callArgsVisited = false; + if (isCallOnClosure) { + // this is a closure.call() call + if (objectExpression == VariableExpression.THIS_EXPRESSION) { + // isClosureCall() check verified earlier that a field exists + FieldNode field = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name); + GenericsType[] genericsTypes = field.getType().getGenericsTypes(); + if (genericsTypes != null) { + ClassNode closureReturnType = genericsTypes[0].getType(); + Object data = field.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS); + if (data != null) { + Parameter[] parameters = (Parameter[]) data; + typeCheckClosureCall(callArguments, args, parameters); + } + storeType(call, closureReturnType); + } + } else if (objectExpression instanceof VariableExpression) { + Variable variable = findTargetVariable((VariableExpression) objectExpression); + if (variable instanceof ASTNode) { + Object data = ((ASTNode) variable).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS); + if (data != null) { + Parameter[] parameters = (Parameter[]) data; + typeCheckClosureCall(callArguments, args, parameters); + } + ClassNode type = getType(((ASTNode) variable)); + if (type!=null && type.equals(CLOSURE_TYPE)) { + GenericsType[] genericsTypes = type.getGenericsTypes(); + type = OBJECT_TYPE; + if (genericsTypes != null) { + if (!genericsTypes[0].isPlaceholder()) { + type = genericsTypes[0].getType(); + } + } + } + if (type != null) { + storeType(call, type); + } + } + } else if (objectExpression instanceof ClosureExpression) { + // we can get actual parameters directly + Parameter[] parameters = ((ClosureExpression) objectExpression).getParameters(); + typeCheckClosureCall(callArguments, args, parameters); + ClassNode data = getInferredReturnType(objectExpression); + if (data != null) { + storeType(call, data); + } + } + int nbOfArgs; + if (callArguments instanceof ArgumentListExpression) { + ArgumentListExpression list = (ArgumentListExpression) callArguments; + nbOfArgs = list.getExpressions().size(); + } else { + // todo : other cases + nbOfArgs = 0; + } + storeTargetMethod(call, + nbOfArgs == 0 ? CLOSURE_CALL_NO_ARG : + nbOfArgs == 1 ? CLOSURE_CALL_ONE_ARG : + CLOSURE_CALL_VARGS); + } else { + // method call receivers are : + // - possible "with" receivers + // - the actual receiver as found in the method call expression + // - any of the potential receivers found in the instanceof temporary table + // in that order + List> receivers = new LinkedList>(); + List> owners = makeOwnerList(objectExpression); + addReceivers(receivers, owners, call.isImplicitThis()); + List mn = null; + Receiver chosenReceiver = null; + for (Receiver currentReceiver : receivers) { + ClassNode receiverType = currentReceiver.getType(); + mn = findMethod(receiverType, name, args); + + // if the receiver is "this" or "implicit this", then we must make sure that the compatible + // methods are only static if we are in a static context + // if we are not in a static context but the the current receiver is a static class, we must + // ensure that all methods are either static or declared by the current receiver or a superclass + if (!mn.isEmpty() + && (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0) + && (call.isImplicitThis() || (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()))) { + // we create separate method lists just to be able to print out + // a nice error message to the user + // a method is accessible if it is static, or if we are not in a static context and it is + // declared by the current receiver or a superclass + List accessibleMethods = new LinkedList(); + List inaccessibleMethods = new LinkedList(); + for (final MethodNode node : mn) { + if (node.isStatic() + || (!typeCheckingContext.isInStaticContext && implementsInterfaceOrIsSubclassOf(receiverType, node.getDeclaringClass()))) { + accessibleMethods.add(node); + } else { + inaccessibleMethods.add(node); + } + } + mn = accessibleMethods; + if (accessibleMethods.isEmpty()) { + // choose an arbitrary method to display an error message + MethodNode node = inaccessibleMethods.get(0); + ClassNode owner = node.getDeclaringClass(); + addStaticTypeError("Non static method " + owner.getName() + "#" + node.getName() + " cannot be called from static context", call); + } + } + + if (!mn.isEmpty()) { + chosenReceiver = currentReceiver; + break; + } + } + if (mn.isEmpty() && typeCheckingContext.getEnclosingClosure() != null && args.length == 0) { + // add special handling of getDelegate() and getOwner() + if ("getDelegate".equals(name)) { + mn = Collections.singletonList(GET_DELEGATE); + } else if ("getOwner".equals(name)) { + mn = Collections.singletonList(GET_OWNER); + } else if ("getThisObject".equals(name)) { + mn = Collections.singletonList(GET_THISOBJECT); + } + } + if (mn.isEmpty()) { + mn = extension.handleMissingMethod(receiver, name, argumentList, args, call); + } + if (mn.isEmpty()) { + addNoMatchingMethodError(receiver, name, args, call); + } else { + if (areCategoryMethodCalls(mn, name, args)) { + addCategoryMethodCallError(call); + } + mn = disambiguateMethods(mn, chosenReceiver!=null?chosenReceiver.getType():null, args, call); + if (mn.size() == 1) { + MethodNode directMethodCallCandidate = mn.get(0); + if (call.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) == null && + !directMethodCallCandidate.isStatic() && objectExpression instanceof ClassExpression && + !"java.lang.Class".equals(directMethodCallCandidate.getDeclaringClass().getName())) { + ClassNode owner = directMethodCallCandidate.getDeclaringClass(); + addStaticTypeError("Non static method " + owner.getName() + "#" + directMethodCallCandidate.getName() + " cannot be called from static context", call); + } + if (chosenReceiver==null) { + chosenReceiver = Receiver.make(directMethodCallCandidate.getDeclaringClass()); + } + + ClassNode returnType = getType(directMethodCallCandidate); + + if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) { + visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, directMethodCallCandidate); + ClassNode irtg = inferReturnTypeGenerics( + chosenReceiver.getType(), + directMethodCallCandidate, + callArguments, + call.getGenericsTypes()); + returnType = irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType) ? irtg : returnType; + callArgsVisited = true; + } + if (directMethodCallCandidate==GET_DELEGATE && typeCheckingContext.getEnclosingClosure()!=null) { + DelegationMetadata md = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression()); + returnType = typeCheckingContext.getEnclosingClassNode(); + if (md!=null) { + returnType = md.getType(); + } + } + if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, mn.get(0), call)) { + returnType = adjustWithTraits(directMethodCallCandidate,chosenReceiver.getType(), args, returnType); + storeType(call, returnType); + storeTargetMethod(call, directMethodCallCandidate); + String data = chosenReceiver.getData(); + if (data != null) { + // the method which has been chosen is supposed to be a call on delegate or owner + // so we store the information so that the static compiler may reuse it + call.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, data); + } + // if the object expression is a closure shared variable, we will have to perform a second pass + if (objectExpression instanceof VariableExpression) { + VariableExpression var = (VariableExpression) objectExpression; + if (var.isClosureSharedVariable()) { + SecondPassExpression wrapper = new SecondPassExpression( + call, + args + ); + typeCheckingContext.secondPassExpressions.add(wrapper); + } + } + } + } else { + addAmbiguousErrorMessage(mn, name, args, call); + } + } + } + // adjust typing for explicit math methods which have special handling - operator variants handled elsewhere + if (NUMBER_OPS.containsKey(name) && isNumberType(receiver) && argumentList.getExpressions().size() == 1 + && isNumberType(getType(argumentList.getExpression(0)))) { + ClassNode right = getType(argumentList.getExpression(0)); + ClassNode resultType = getMathResultType(NUMBER_OPS.get(name), receiver, right, name); + if (resultType != null) { + storeType(call, resultType); + } + } + + // now that a method has been chosen, we are allowed to visit the closures + if (!callArgsVisited) { + MethodNode mn = (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + visitMethodCallArguments(receiver, argumentList, true, mn); + // GROOVY-6219 + if (mn!=null) { + List argExpressions = argumentList.getExpressions(); + Parameter[] parameters = mn.getParameters(); + for (int i = 0; i < argExpressions.size() && i< parameters.length; i++) { + Expression arg = argExpressions.get(i); + ClassNode pType = parameters[i].getType(); + ClassNode aType = getType(arg); + if (CLOSURE_TYPE.equals(pType) && CLOSURE_TYPE.equals(aType)) { + if (!isAssignableTo(aType, pType)) { + addNoMatchingMethodError(receiver, name, getArgumentTypes(argumentList), call); + call.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + } + } + } + } + } + } finally { + typeCheckingContext.popEnclosingMethodCall(); + extension.afterMethodCall(call); + } + } + + /** + * A special method handling the "withTrait" call for which the type checker knows more than + * what the type signature is able to tell. If "withTrait" is detected, then a new class node + * is created representing the list of trait interfaces. + * + * @param directMethodCallCandidate a method selected by the type checker + * @param receiver the receiver of the method call + * @param args the arguments of the method call + * @param returnType the original return type, as inferred by the type checker + * @return fixed return type if the selected method is {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#withTraits(Object, Class[]) withTraits} + */ + private static ClassNode adjustWithTraits(final MethodNode directMethodCallCandidate, final ClassNode receiver, final ClassNode[] args, final ClassNode returnType) { + if (directMethodCallCandidate instanceof ExtensionMethodNode) { + ExtensionMethodNode emn = (ExtensionMethodNode) directMethodCallCandidate; + if ("withTraits".equals(emn.getName()) && "DefaultGroovyMethods".equals(emn.getExtensionMethodNode().getDeclaringClass().getNameWithoutPackage())) { + List nodes = new LinkedList(); + Collections.addAll(nodes, receiver.getInterfaces()); + for (ClassNode arg : args) { + if (isClassClassNodeWrappingConcreteType(arg)) { + nodes.add(arg.getGenericsTypes()[0].getType()); + } else { + nodes.add(arg); + } + } + return new LowestUpperBoundClassNode(returnType.getName()+"Composed", OBJECT_TYPE, nodes.toArray(new ClassNode[nodes.size()])); + } + } + return returnType; + } + + /** + * add various getAt and setAt methods for primitive arrays + * @param receiver the receiver class + * @param name the name of the method + * @param args the argument classes + */ + private static void addArrayMethods(List methods, ClassNode receiver, String name, ClassNode[] args) { + if (args.length!=1) return; + if (!receiver.isArray()) return; + if (!isIntCategory(getUnwrapper(args[0]))) return; + if ("getAt".equals(name)) { + MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, receiver.getComponentType(), new Parameter[]{new Parameter(args[0],"arg")}, null, null); + node.setDeclaringClass(receiver.redirect()); + methods.add(node); + } else if ("setAt".equals(name)) { + MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, VOID_TYPE, new Parameter[]{new Parameter(args[0],"arg")}, null, null); + node.setDeclaringClass(receiver.redirect()); + methods.add(node); + } + } + + /** + * In the case of a Object.with { ... } call, this method is supposed to retrieve + * the inferred closure return type. + * + * @param callArguments the argument list from the Object#with(Closure) call, ie. a single closure expression + * @return the inferred closure return type or null + */ + protected ClassNode getInferredReturnTypeFromWithClosureArgument(Expression callArguments) { + if (!(callArguments instanceof ArgumentListExpression)) return null; + + ArgumentListExpression argList = (ArgumentListExpression) callArguments; + ClosureExpression closure = (ClosureExpression) argList.getExpression(0); + + visitClosureExpression(closure); + + if (getInferredReturnType(closure) != null) { + return getInferredReturnType(closure); + } + + return null; + } + + /** + * Given an object expression (a receiver expression), generate the list of potential receiver types. + * @param objectExpression the receiver expression + * @return the list of types the receiver may be + */ + protected List> makeOwnerList(final Expression objectExpression) { + final ClassNode receiver = getType(objectExpression); + List> owners = new LinkedList>(); + owners.add(Receiver.make(receiver)); + if (isClassClassNodeWrappingConcreteType(receiver)) { + GenericsType clazzGT = receiver.getGenericsTypes()[0]; + owners.add(0, Receiver.make(clazzGT.getType())); + } + if (receiver.isInterface()) { + // GROOVY-xxxx + owners.add(Receiver.make(OBJECT_TYPE)); + } + addSelfTypes(receiver, owners); + if (!typeCheckingContext.temporaryIfBranchTypeInformation.empty()) { + List potentialReceiverType = getTemporaryTypesForExpression(objectExpression); + if (potentialReceiverType != null) { + for (ClassNode node : potentialReceiverType) { + owners.add(Receiver.make(node)); + } + } + } + if (typeCheckingContext.lastImplicitItType != null + && objectExpression instanceof VariableExpression + && ((VariableExpression) objectExpression).getName().equals("it")) { + owners.add(Receiver.make(typeCheckingContext.lastImplicitItType)); + } + return owners; + } + + private static void addSelfTypes(final ClassNode receiver, final List> owners) { + LinkedHashSet selfTypes = new LinkedHashSet(); + for (ClassNode selfType : Traits.collectSelfTypes(receiver, selfTypes)) { + owners.add(Receiver.make(selfType)); + } + } + + protected void checkForbiddenSpreadArgument(ArgumentListExpression argumentList) { + for (Expression arg : argumentList.getExpressions()) { + if (arg instanceof SpreadExpression) { + addStaticTypeError("The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time", arg); + } + } + } + + protected List getTemporaryTypesForExpression(final Expression objectExpression) { + List classNodes = null; + int depth = typeCheckingContext.temporaryIfBranchTypeInformation.size(); + while (classNodes == null && depth > 0) { + final Map> tempo = typeCheckingContext.temporaryIfBranchTypeInformation.get(--depth); + Object key = objectExpression instanceof ParameterVariableExpression + ? ((ParameterVariableExpression) objectExpression).parameter + : extractTemporaryTypeInfoKey(objectExpression); + classNodes = tempo.get(key); + } + return classNodes; + } + + protected void storeTargetMethod(final Expression call, final MethodNode directMethodCallCandidate) { + call.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, directMethodCallCandidate); + checkOrMarkPrivateAccess(call, directMethodCallCandidate); + checkSuperCallFromClosure(call, directMethodCallCandidate); + extension.onMethodSelection(call, directMethodCallCandidate); + } + + protected boolean isClosureCall(final String name, final Expression objectExpression, final Expression arguments) { + if (objectExpression instanceof ClosureExpression && (CALL.equals(name)||"doCall".equals(name))) return true; + if (objectExpression == VariableExpression.THIS_EXPRESSION) { + FieldNode fieldNode = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name); + if (fieldNode != null) { + ClassNode type = fieldNode.getType(); + if (CLOSURE_TYPE.equals(type) && !typeCheckingContext.getEnclosingClassNode().hasPossibleMethod(name, arguments)) { + return true; + } + } + } else { + if (!CALL.equals(name) && !"doCall".equals(name)) return false; + } + return (getType(objectExpression).equals(CLOSURE_TYPE)); + } + + protected void typeCheckClosureCall(final Expression callArguments, final ClassNode[] args, final Parameter[] parameters) { + if (allParametersAndArgumentsMatch(parameters, args) < 0 && + lastArgMatchesVarg(parameters, args) < 0) { + StringBuilder sb = new StringBuilder("["); + for (int i = 0, parametersLength = parameters.length; i < parametersLength; i++) { + final Parameter parameter = parameters[i]; + sb.append(parameter.getType().getName()); + if (i < parametersLength - 1) sb.append(", "); + } + sb.append("]"); + addStaticTypeError("Closure argument types: " + sb + " do not match with parameter types: " + formatArgumentList(args), callArguments); + } + } + + @Override + public void visitIfElse(final IfStatement ifElse) { + Map> oldTracker = pushAssignmentTracking(); + + try { + // create a new temporary element in the if-then-else type info + typeCheckingContext.pushTemporaryTypeInfo(); + visitStatement(ifElse); + ifElse.getBooleanExpression().visit(this); + ifElse.getIfBlock().visit(this); + + // pop if-then-else temporary type info + typeCheckingContext.popTemporaryTypeInfo(); + + // GROOVY-6099: restore assignment info as before the if branch + restoreTypeBeforeConditional(); + + Statement elseBlock = ifElse.getElseBlock(); + if (elseBlock instanceof EmptyStatement) { + // dispatching to EmptyStatement will not call back visitor, + // must call our visitEmptyStatement explicitly + visitEmptyStatement((EmptyStatement) elseBlock); + } else { + elseBlock.visit(this); + } + } finally { + popAssignmentTracking(oldTracker); + } + } + + @Override + public void visitSwitch(final SwitchStatement statement) { + Map> oldTracker = pushAssignmentTracking(); + try { + super.visitSwitch(statement); + } finally { + popAssignmentTracking(oldTracker); + } + } + + @Override + public void visitCaseStatement(final CaseStatement statement) { + super.visitCaseStatement(statement); + restoreTypeBeforeConditional(); + } + + private void restoreTypeBeforeConditional() { + Set>> entries = typeCheckingContext.ifElseForWhileAssignmentTracker.entrySet(); + for (Map.Entry> entry : entries) { + VariableExpression var = entry.getKey(); + List items = entry.getValue(); + ClassNode originValue = items.get(0); + storeType(var, originValue); + } + } + + protected Map popAssignmentTracking(final Map> oldTracker) { + Map assignments = new HashMap(); + if (!typeCheckingContext.ifElseForWhileAssignmentTracker.isEmpty()) { + for (Map.Entry> entry : typeCheckingContext.ifElseForWhileAssignmentTracker.entrySet()) { + VariableExpression key = entry.getKey(); + List allValues = entry.getValue(); + // GROOVY-6099: First element of the list may be null, if no assignment was made before the branch + List nonNullValues = new ArrayList(allValues.size()); + for (ClassNode value : allValues) { + if (value!=null) nonNullValues.add(value); + } + ClassNode cn = lowestUpperBound(nonNullValues); + storeType(key, cn); + assignments.put(key, cn); + } + } + typeCheckingContext.ifElseForWhileAssignmentTracker = oldTracker; + return assignments; + } + + protected Map> pushAssignmentTracking() { + // memorize current assignment context + Map> oldTracker = typeCheckingContext.ifElseForWhileAssignmentTracker; + typeCheckingContext.ifElseForWhileAssignmentTracker = new HashMap>(); + return oldTracker; + } + + @Override + public void visitCastExpression(final CastExpression expression) { + super.visitCastExpression(expression); + if (!expression.isCoerce()) { + ClassNode targetType = expression.getType(); + Expression source = expression.getExpression(); + ClassNode expressionType = getType(source); + if (!checkCast(targetType, source) && !isDelegateOrOwnerInClosure(source)) { + addStaticTypeError("Inconvertible types: cannot cast " + expressionType.toString(false) + " to " + targetType.toString(false), expression); + } + } + storeType(expression, expression.getType()); + } + + private boolean isDelegateOrOwnerInClosure(Expression exp) { + return typeCheckingContext.getEnclosingClosure()!=null && + exp instanceof VariableExpression && + (("delegate".equals(((VariableExpression) exp).getName())) || ("owner".equals(((VariableExpression) exp).getName()))); + } + + protected boolean checkCast(final ClassNode targetType, final Expression source) { + boolean sourceIsNull = isNullConstant(source); + ClassNode expressionType = getType(source); + if (targetType.isArray() && expressionType.isArray()) { + return checkCast(targetType.getComponentType(), varX("foo", expressionType.getComponentType())); + } else if (targetType.equals(char_TYPE) && expressionType == STRING_TYPE + && source instanceof ConstantExpression && source.getText().length() == 1) { + // ex: (char) 'c' + } else if (targetType.equals(Character_TYPE) && (expressionType == STRING_TYPE || sourceIsNull) + && (sourceIsNull || source instanceof ConstantExpression && source.getText().length() == 1)) { + // ex : (Character) 'c' + } else if (isNumberCategory(getWrapper(targetType)) && (isNumberCategory(getWrapper(expressionType)) || char_TYPE == expressionType)) { + // ex: short s = (short) 0 + } else if (sourceIsNull && !isPrimitiveType(targetType)) { + // ex: (Date)null + } else if (char_TYPE == targetType && isPrimitiveType(expressionType) && isNumberType(expressionType)) { + // char c = (char) ... + } else if (sourceIsNull && isPrimitiveType(targetType) && !boolean_TYPE.equals(targetType)) { + return false; + } else if ((expressionType.getModifiers()& Opcodes.ACC_FINAL)==0 && targetType.isInterface()) { + return true; + } else if ((targetType.getModifiers()& Opcodes.ACC_FINAL)==0 && expressionType.isInterface()) { + return true; + } else if (!isAssignableTo(targetType, expressionType) && !implementsInterfaceOrIsSubclassOf(expressionType, targetType)) { + return false; + } + return true; + } + + @Override + public void visitTernaryExpression(final TernaryExpression expression) { + Map> oldTracker = pushAssignmentTracking(); + // create a new temporary element in the if-then-else type info + typeCheckingContext.pushTemporaryTypeInfo(); + expression.getBooleanExpression().visit(this); + Expression trueExpression = expression.getTrueExpression(); + Expression falseExpression = expression.getFalseExpression(); + trueExpression.visit(this); + // GRECLIPSE add + ClassNode typeOfTrue = findCurrentInstanceOfClass(trueExpression, getType(trueExpression)); + // GRECLIPSE end + // pop if-then-else temporary type info + typeCheckingContext.popTemporaryTypeInfo(); + falseExpression.visit(this); + ClassNode resultType; + ClassNode typeOfFalse = getType(falseExpression); + // GRECLIPSE edit + //ClassNode typeOfTrue = getType(trueExpression); + // GRECLIPSE end + // handle instanceof cases + if (hasInferredReturnType(falseExpression)) { + typeOfFalse = falseExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } + if (hasInferredReturnType(trueExpression)) { + typeOfTrue = trueExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } + // TODO consider moving next two statements "up a level", i.e. have just one more widely invoked + // check but determine no -ve consequences first + typeOfFalse = checkForTargetType(falseExpression, typeOfFalse); + typeOfTrue = checkForTargetType(trueExpression, typeOfTrue); + if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) { + BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression(); + if (enclosingBinaryExpression != null && enclosingBinaryExpression.getRightExpression()==expression) { + resultType = getType(enclosingBinaryExpression.getLeftExpression()); + } else if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { + resultType = OBJECT_TYPE; + } else if (isNullConstant(trueExpression)) { + resultType = wrapTypeIfNecessary(typeOfFalse); + } else { + resultType = wrapTypeIfNecessary(typeOfTrue); + } + } else { + // store type information + resultType = lowestUpperBound(typeOfTrue, typeOfFalse); + } + storeType(expression, resultType); + popAssignmentTracking(oldTracker); + } + + // currently just for empty literals, not for e.g. Collections.emptyList() at present + /// it seems attractive to want to do this for more cases but perhaps not all cases + private ClassNode checkForTargetType(final Expression expr, final ClassNode type) { + BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression(); + if (enclosingBinaryExpression != null && enclosingBinaryExpression instanceof DeclarationExpression + && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) { + VariableExpression target = (VariableExpression) enclosingBinaryExpression.getLeftExpression(); + return adjustForTargetType(target.getType(), type); + } + if (currentField != null) { + return adjustForTargetType(currentField.getType(), type); + } + if (currentProperty != null) { + return adjustForTargetType(currentProperty.getType(), type); + } + return type; + } + + private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) { + if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) { + // unchecked assignment within ternary/elvis + // examples: + // List list = existingAs ?: [] + // in that case, the inferred type of the RHS is the type of the RHS + // "completed" with generics type information available in the LHS + return GenericsUtils.parameterizeType(targetType, resultType.getPlainNodeReference()); + } + return resultType; + } + + private static boolean isEmptyCollection(Expression expr) { + return (expr instanceof ListExpression && ((ListExpression) expr).getExpressions().size() == 0) || + (expr instanceof MapExpression && ((MapExpression) expr).getMapEntryExpressions().size() == 0); + } + + private static boolean hasInferredReturnType(Expression expression) { + ClassNode type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + return type != null && !type.getName().equals("java.lang.Object"); + } + + @Override + public void visitTryCatchFinally(final TryCatchStatement statement) { + final List catchStatements = statement.getCatchStatements(); + for (CatchStatement catchStatement : catchStatements) { + ClassNode exceptionType = catchStatement.getExceptionType(); + typeCheckingContext.controlStructureVariables.put(catchStatement.getVariable(), exceptionType); + } + try { + super.visitTryCatchFinally(statement); + } finally { + for (CatchStatement catchStatement : catchStatements) { + typeCheckingContext.controlStructureVariables.remove(catchStatement.getVariable()); + } + } + + } + + protected void storeType(Expression exp, ClassNode cn) { + if (exp instanceof VariableExpression && ((VariableExpression) exp).isClosureSharedVariable() && isPrimitiveType(cn)) { + cn = getWrapper(cn); + } else if (exp instanceof MethodCallExpression && ((MethodCallExpression) exp).isSafe() && isPrimitiveType(cn)) { + cn = getWrapper(cn); + } else if (exp instanceof PropertyExpression && ((PropertyExpression) exp).isSafe() && isPrimitiveType(cn)) { + cn = getWrapper(cn); + } + if (cn == UNKNOWN_PARAMETER_TYPE) { + // this can happen for example when "null" is used in an assignment or a method parameter. + // In that case, instead of storing the virtual type, we must "reset" type information + // by determining the declaration type of the expression + storeType(exp, getOriginalDeclarationType(exp)); + return; + } + ClassNode oldValue = (ClassNode) exp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, cn); + if (oldValue != null) { + // this may happen when a variable declaration type is wider than the subsequent assignment values + // for example : + // def o = 1 // first, an int + // o = 'String' // then a string + // o = new Object() // and eventually an object ! + // in that case, the INFERRED_TYPE corresponds to the current inferred type, while + // DECLARATION_INFERRED_TYPE is the type which should be used for the initial type declaration + ClassNode oldDIT = (ClassNode) exp.getNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE); + if (oldDIT != null) { + exp.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, cn==null?oldDIT : lowestUpperBound(oldDIT, cn)); + } else { + exp.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, cn==null?null : lowestUpperBound(oldValue, cn)); + } + } + if (exp instanceof VariableExpression) { + VariableExpression var = (VariableExpression) exp; + final Variable accessedVariable = var.getAccessedVariable(); + if (accessedVariable != null && accessedVariable != exp && accessedVariable instanceof VariableExpression) { + storeType((Expression) accessedVariable, cn); + } + if (accessedVariable instanceof Parameter) { + ((Parameter) accessedVariable).putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, cn); + } + if (var.isClosureSharedVariable() && cn!=null) { + List assignedTypes = typeCheckingContext.closureSharedVariablesAssignmentTypes.get(var); + if (assignedTypes == null) { + assignedTypes = new LinkedList(); + typeCheckingContext.closureSharedVariablesAssignmentTypes.put(var, assignedTypes); + } + assignedTypes.add(cn); + } + if (!typeCheckingContext.temporaryIfBranchTypeInformation.empty()) { + List temporaryTypesForExpression = getTemporaryTypesForExpression(exp); + if (temporaryTypesForExpression != null && !temporaryTypesForExpression.isEmpty()) { + // a type inference has been made on a variable whose type was defined in an instanceof block + // we erase available information with the new type + temporaryTypesForExpression.clear(); + } + } + } + } + + protected ClassNode getResultType(ClassNode left, int op, ClassNode right, BinaryExpression expr) { + ClassNode leftRedirect = left.redirect(); + ClassNode rightRedirect = right.redirect(); + + Expression leftExpression = expr.getLeftExpression(); + Expression rightExpression = expr.getRightExpression(); + if (op == ASSIGN || op == ASSIGNMENT_OPERATOR || op == ELVIS_EQUAL) { + if (leftRedirect.isArray() && implementsInterfaceOrIsSubclassOf(rightRedirect, Collection_TYPE)) return leftRedirect; + if (leftRedirect.implementsInterface(Collection_TYPE) && rightRedirect.implementsInterface(Collection_TYPE)) { + // because of type inference, we must perform an additional check if the right expression + // is an empty list expression ([]). In that case and only in that case, the inferred type + // will be wrong, so we will prefer the left type + if (rightExpression instanceof ListExpression) { + List list = ((ListExpression) rightExpression).getExpressions(); + if (list.isEmpty()) return left; + } + return right; + } + if (rightRedirect.implementsInterface(Collection_TYPE) && rightRedirect.isDerivedFrom(leftRedirect)) { + // ex : def foos = ['a','b','c'] + return right; + } + if (rightRedirect.isDerivedFrom(CLOSURE_TYPE) && isSAMType(leftRedirect) && rightExpression instanceof ClosureExpression) { + return inferSAMTypeGenericsInAssignment(left, findSAM(left),right,(ClosureExpression) rightExpression); + } + + if (leftExpression instanceof VariableExpression) { + ClassNode initialType = getOriginalDeclarationType(leftExpression).redirect(); + if (isPrimitiveType(right) && initialType.isDerivedFrom(Number_TYPE)) { + return getWrapper(right); + } + + if (isPrimitiveType(initialType) && rightRedirect.isDerivedFrom(Number_TYPE)) { + return getUnwrapper(right); + } + + // as anything can be assigned to a String, Class or boolean, return the left type instead + if (STRING_TYPE.equals(initialType) + || CLASS_Type.equals(initialType) + || Boolean_TYPE.equals(initialType) + || boolean_TYPE.equals(initialType)) { + return initialType; + } + } + return right; + } + if (isBoolIntrinsicOp(op)) { + return boolean_TYPE; + } + if (isArrayOp(op)) { + // using getPNR() to ignore generics at this point + // and a different binary expression not to pollute the AST + BinaryExpression newExpr = binX(expr.getLeftExpression(), expr.getOperation(), rightExpression); + newExpr.setSourcePosition(expr); + MethodNode method = findMethodOrFail(newExpr, left.getPlainNodeReference(), "getAt", right.getPlainNodeReference()); + if (method!=null && implementsInterfaceOrIsSubclassOf(right, RANGE_TYPE)) { + return inferReturnTypeGenerics(left, method, rightExpression); + } + return method!=null?inferComponentType(left, right):null; + } + if (op == FIND_REGEX) { + // this case always succeeds the result is a Matcher + return Matcher_TYPE; + } + // the left operand is determining the result of the operation + // for primitives and their wrapper we use a fixed table here + String operationName = getOperationName(op); + ClassNode mathResultType = getMathResultType(op, leftRedirect, rightRedirect, operationName); + if (mathResultType != null) { + return mathResultType; + } + + // GROOVY-5890 + // do not mix Class with Foo + if (leftExpression instanceof ClassExpression) { + left = CLASS_Type.getPlainNodeReference(); + } + + MethodNode method = findMethodOrFail(expr, left, operationName, right); + if (method != null) { + storeTargetMethod(expr, method); + typeCheckMethodsWithGenericsOrFail(left, new ClassNode[]{right}, method, expr); + if (isAssignment(op)) return left; + if (isCompareToBoolean(op)) return boolean_TYPE; + if (op == COMPARE_TO) return int_TYPE; + return inferReturnTypeGenerics(left, method, args(rightExpression)); + } + //TODO: other cases + return null; + } + + private ClassNode getMathResultType(int op, ClassNode leftRedirect, ClassNode rightRedirect, String operationName) { + if (isNumberType(leftRedirect) && isNumberType(rightRedirect)) { + if (isOperationInGroup(op)) { + if (isIntCategory(leftRedirect) && isIntCategory(rightRedirect)) return int_TYPE; + if (isLongCategory(leftRedirect) && isLongCategory(rightRedirect)) return long_TYPE; + if (isFloat(leftRedirect) && isFloat(rightRedirect)) return float_TYPE; + if (isDouble(leftRedirect) && isDouble(rightRedirect)) return double_TYPE; + } else if (isPowerOperator(op)) { + return Number_TYPE; + } else if (isBitOperator(op) || op == INTDIV || op == INTDIV_EQUAL) { + if (isIntCategory(getUnwrapper(leftRedirect)) && isIntCategory(getUnwrapper(rightRedirect))) return int_TYPE; + if (isLongCategory(getUnwrapper(leftRedirect)) && isLongCategory(getUnwrapper(rightRedirect))) return long_TYPE; + if (isBigIntCategory(getUnwrapper(leftRedirect)) && isBigIntCategory(getUnwrapper(rightRedirect))) return BigInteger_TYPE; + } else if (isCompareToBoolean(op) || op == COMPARE_EQUAL || op == COMPARE_NOT_EQUAL) { + return boolean_TYPE; + } + } else if (char_TYPE.equals(leftRedirect) && char_TYPE.equals(rightRedirect)) { + if (isCompareToBoolean(op) || op == COMPARE_EQUAL || op == COMPARE_NOT_EQUAL) { + return boolean_TYPE; + } + } + + // try to find a method for the operation + if (isShiftOperation(operationName) && isNumberCategory(leftRedirect) && (isIntCategory(rightRedirect) || isLongCategory(rightRedirect))) { + return leftRedirect; + } + + // Divisions may produce different results depending on operand types + if (isNumberCategory(getWrapper(rightRedirect)) && (isNumberCategory(getWrapper(leftRedirect)) && (DIVIDE == op || DIVIDE_EQUAL == op))) { + if (isFloatingCategory(leftRedirect) || isFloatingCategory(rightRedirect)) { + if (!isPrimitiveType(leftRedirect) || !isPrimitiveType(rightRedirect)) { + return Double_TYPE; + } + return double_TYPE; + } + if (DIVIDE == op) { + return BigDecimal_TYPE; + } + return leftRedirect; + } else if (isOperationInGroup(op)) { + if (isNumberCategory(getWrapper(leftRedirect)) && isNumberCategory(getWrapper(rightRedirect))) { + return getGroupOperationResultType(leftRedirect, rightRedirect); + } + } + if (isNumberCategory(getWrapper(rightRedirect)) && isNumberCategory(getWrapper(leftRedirect)) && (MOD == op || MOD_EQUAL == op)) { + return leftRedirect; + } + return null; + } + + private ClassNode inferSAMTypeGenericsInAssignment(ClassNode samUsage, MethodNode sam, ClassNode closureType, ClosureExpression closureExpression) { + // if the sam type or closure type do not provide generics information, + // we cannot infer anything, thus we simply return the provided samUsage + GenericsType[] samGt = samUsage.getGenericsTypes(); + GenericsType[] closureGt = closureType.getGenericsTypes(); + if (samGt==null || closureGt==null) return samUsage; + + // extract the generics from the return type + Map connections = new HashMap(); + extractGenericsConnections(connections, getInferredReturnType(closureExpression),sam.getReturnType()); + + // next we get the block parameter types and set the generics + // information just like before + // TODO: add vargs handling + Parameter[] closureParams = closureExpression.getParameters(); + Parameter[] methodParams = sam.getParameters(); + for (int i=0; i methods = findMethod(receiver, name, args); + if (methods.isEmpty() && (expr instanceof BinaryExpression)) { + BinaryExpression be = (BinaryExpression) expr; + MethodCallExpression call = callX(be.getLeftExpression(), name, be.getRightExpression()); + methods = extension.handleMissingMethod(receiver, name, args(be.getLeftExpression()), args, call); + } + if (methods.isEmpty()) { + addNoMatchingMethodError(receiver, name, args, expr); + } else { + if (areCategoryMethodCalls(methods, name, args)) { + addCategoryMethodCallError(expr); + } + methods = disambiguateMethods(methods, receiver, args, expr); + if (methods.size() == 1) { + return methods.get(0); + } else { + addAmbiguousErrorMessage(methods, name, args, expr); + } + } + return null; + } + + private List disambiguateMethods(List methods, ClassNode receiver, ClassNode[] argTypes, final Expression call) { + if (methods.size()>1 && receiver!=null && argTypes!=null) { + List filteredWithGenerics = new LinkedList(); + for (MethodNode methodNode : methods) { + // GRECLIPSE edit + //if (typeCheckMethodsWithGenerics(receiver, argTypes, methodNode)) { + if (typeCheckMethodsWithGenerics(receiver, argTypes, methodNode) && (methodNode.getModifiers() & /*bridge:*/0x40) == 0) { + // GRECLIPSE end + filteredWithGenerics.add(methodNode); + } + } + if (filteredWithGenerics.size()==1) { + return filteredWithGenerics; + } + methods = extension.handleAmbiguousMethods(methods, call); + } + + if (methods.size() > 1) { + if (call instanceof MethodCall) { + List methodNodeList = new LinkedList<>(); + + String methodName = ((MethodCall) call).getMethodAsString(); + + for (MethodNode methodNode : methods) { + if (!methodNode.getName().equals(methodName)) { + continue; + } + methodNodeList.add(methodNode); + } + + methods = methodNodeList; + } + } + + return methods; + } + + protected static String prettyPrintMethodList(List nodes) { + StringBuilder sb = new StringBuilder("["); + for (int i = 0, nodesSize = nodes.size(); i < nodesSize; i++) { + final MethodNode node = nodes.get(i); + sb.append(node.getReturnType().toString(false)); + sb.append(" "); + sb.append(node.getDeclaringClass().toString(false)); + sb.append("#"); + sb.append(toMethodParametersString(node.getName(), extractTypesFromParameters(node.getParameters()))); + if (i foundMethods, final String name, final ClassNode[] args) { + boolean category = false; + if ("use".equals(name) && args != null && args.length == 2 && args[1].equals(ClassHelper.CLOSURE_TYPE)) { + category = true; + for (MethodNode method : foundMethods) { + if (!(method instanceof ExtensionMethodNode) || !((ExtensionMethodNode) method).getExtensionMethodNode().getDeclaringClass().equals(DGM_CLASSNODE)) { + category = false; + break; + } + } + } + return category; + } + + /** + * This method returns the list of methods named against the supplied parameter that + * are defined on the specified receiver, but it will also add "non existing" methods + * that will be generated afterwards by the compiler, for example if a method is using + * default values and that the specified class node isn't compiled yet. + * @param receiver the receiver where to find methods + * @param name the name of the methods to return + * @return the methods that are defined on the receiver completed with stubs for future methods + */ + protected List findMethodsWithGenerated(ClassNode receiver, String name) { + List methods = receiver.getMethods(name); + if (methods.isEmpty() || receiver.isResolved()) return methods; + List result = addGeneratedMethods(receiver, methods); + + return result; + } + + private static List addGeneratedMethods(final ClassNode receiver, final List methods) { + // using a comparator of parameters + List result = new LinkedList(); + for (MethodNode method : methods) { + result.add(method); + Parameter[] parameters = method.getParameters(); + int counter = 0; + int size = parameters.length; + for (int i = size - 1; i >= 0; i--) { + Parameter parameter = parameters[i]; + if (parameter != null && parameter.hasInitialExpression()) { + counter++; + } + } + + for (int j = 1; j <= counter; j++) { + Parameter[] newParams = new Parameter[parameters.length - j]; + int index = 0; + int k = 1; + for (Parameter parameter : parameters) { + if (k > counter - j && parameter != null && parameter.hasInitialExpression()) { + k++; + } else if (parameter != null && parameter.hasInitialExpression()) { + newParams[index++] = parameter; + k++; + } else { + newParams[index++] = parameter; + } + } + MethodNode stubbed; + if ("".equals(method.getName())) { + stubbed= new ConstructorNode( + method.getModifiers(), + newParams, + method.getExceptions(), + GENERATED_EMPTY_STATEMENT + ); + + } else { + stubbed= new MethodNode( + method.getName(), + method.getModifiers(), + method.getReturnType(), + newParams, + method.getExceptions(), + GENERATED_EMPTY_STATEMENT + ); + stubbed.setGenericsTypes(method.getGenericsTypes()); + } + stubbed.setDeclaringClass(method.getDeclaringClass()); + result.add(stubbed); + } + } + return result; + } + + protected List findMethod( + ClassNode receiver, String name, ClassNode... args) { + if (isPrimitiveType(receiver)) receiver = getWrapper(receiver); + List methods; + if (!receiver.isInterface() && "".equals(name)) { + methods = addGeneratedMethods(receiver,new ArrayList(receiver.getDeclaredConstructors())); + if (methods.isEmpty()) { + MethodNode node = new ConstructorNode(Opcodes.ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT); + node.setDeclaringClass(receiver); + methods = Collections.singletonList(node); + if (receiver.isArray()) { + // No need to check the arguments against an array constructor: it just needs to exist. The array is + // created through coercion or by specifying its dimension(s), anyway, and would not match an + // arbitrary number of parameters. + return methods; + } + } + } else { + methods = findMethodsWithGenerated(receiver,name); + if (receiver.isInterface()) { + collectAllInterfaceMethodsByName(receiver, name, methods); + methods.addAll(OBJECT_TYPE.getMethods(name)); + + if (CALL.equals(name) && ClassHelper.isFunctionalInterface(receiver)) { + MethodNode sam = ClassHelper.findSAM(receiver); + MethodNode callMethodNode = new MethodNode(CALL, sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode()); + callMethodNode.setDeclaringClass(sam.getDeclaringClass()); + callMethodNode.setSourcePosition(sam); + + methods.addAll(Collections.singletonList(callMethodNode)); + } + } + // TODO: investigate the trait exclusion a bit further, needed otherwise + // CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying + // not static method can't be called from a static context + if (typeCheckingContext.getEnclosingClosure() == null || (receiver instanceof InnerClassNode && !receiver.getName().endsWith("$Trait$Helper"))) { + // not in a closure or within an inner class + ClassNode parent = receiver; + while (parent instanceof InnerClassNode && !parent.isStaticClass()) { + parent = parent.getOuterClass(); + methods.addAll(findMethodsWithGenerated(parent,name)); + } + } + if (methods.isEmpty()) { + addArrayMethods(methods, receiver, name, args); + } + if (methods.isEmpty() && (args == null || args.length == 0)) { + // check if it's a property + String pname = extractPropertyNameFromMethodName("get", name); + if (pname==null) { + pname = extractPropertyNameFromMethodName("is", name); + } + if (pname != null) { + // we don't use property exists there because findMethod is called on super clases recursively + PropertyNode property = null; + ClassNode curNode = receiver; + while (property == null && curNode != null) { + property = curNode.getProperty(pname); + ClassNode svCur = curNode; + while (property==null && svCur instanceof InnerClassNode && !svCur.isStaticClass()) { + svCur = svCur.getOuterClass(); + property = svCur.getProperty(pname); + if (property!=null) { + receiver = svCur; + break; + } + } + curNode = curNode.getSuperClass(); + } + if (property != null) { + MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, property.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT); + if (property.isStatic()) { + node.setModifiers(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC); + } + node.setDeclaringClass(receiver); + return Collections.singletonList( + node); + + } + } + } else if (methods.isEmpty() && args != null && args.length == 1) { + // maybe we are looking for a setter ? + String pname = extractPropertyNameFromMethodName("set", name); + if (pname!=null) { + ClassNode curNode = receiver; + PropertyNode property = null; + while (property == null && curNode != null) { + property = curNode.getProperty(pname); + curNode = curNode.getSuperClass(); + } + if (property != null) { + ClassNode type = property.getOriginType(); + if (implementsInterfaceOrIsSubclassOf(wrapTypeIfNecessary(args[0]), wrapTypeIfNecessary(type))) { + MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, VOID_TYPE, new Parameter[]{ + new Parameter(type, "arg") + }, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT); + if (property.isStatic()) { + node.setModifiers(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC); + } + node.setDeclaringClass(receiver); + return Collections.singletonList(node); + } + } + } + } + } + + if (methods.isEmpty()) { + // look at the interfaces, there's a chance that a method is not implemented and we should not hide the + // error from the compiler + collectAllInterfaceMethodsByName(receiver, name, methods); + } + + // lookup in DGM methods too + findDGMMethodsByNameAndArguments(getTransformLoader(), receiver, name, args, methods); + List chosen = chooseBestMethod(receiver, methods, args); + if (!chosen.isEmpty()) return chosen; + + // GROOVY-5566 + if (receiver instanceof InnerClassNode && ((InnerClassNode) receiver).isAnonymous() && methods.size() == 1 && args != null && "".equals(name)) { + MethodNode constructor = methods.get(0); + if (constructor.getParameters().length == args.length) { + return methods; + } + } + + if (receiver.equals(CLASS_Type) && receiver.getGenericsTypes() != null) { + List result = findMethod(receiver.getGenericsTypes()[0].getType(), name, args); + if (!result.isEmpty()) return result; + } + + if (ClassHelper.GSTRING_TYPE.equals(receiver)) return findMethod(ClassHelper.STRING_TYPE, name, args); + + if (isBeingCompiled(receiver)) { + chosen = findMethod(GROOVY_OBJECT_TYPE, name, args); + if (!chosen.isEmpty()) return chosen; + } + + return EMPTY_METHODNODE_LIST; + } + + /** + * Given a method name and a prefix, returns the name of the property that should be looked up, + * following the java beans rules. For example, "getName" would return "name", while + * "getFullName" would return "fullName". + * If the prefix is not found, returns null. + * @param prefix the method name prefix ("get", "is", "set", ...) + * @param methodName the method name + * @return a property name if the prefix is found and the method matches the java beans rules, null otherwise + */ + public static String extractPropertyNameFromMethodName(String prefix, String methodName) { + if (prefix==null || methodName==null) return null; + if (methodName.startsWith(prefix) && prefix.length() methods) { + ClassNode cNode = receiver; + while (cNode != null) { + ClassNode[] interfaces = cNode.getInterfaces(); + if (interfaces != null && interfaces.length > 0) { + for (ClassNode node : interfaces) { + List intfMethods = node.getMethods(name); + methods.addAll(intfMethods); + collectAllInterfaceMethodsByName(node, name, methods); + } + } + cNode = cNode.getSuperClass(); + } + } + + protected ClassNode getType(final ASTNode exp) { + ClassNode cn = exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + if (cn != null) { + return cn; + } + if (exp instanceof ClassExpression) { + ClassNode node = CLASS_Type.getPlainNodeReference(); + node.setGenericsTypes(new GenericsType[]{ + new GenericsType(((ClassExpression) exp).getType()) + }); + return node; + } + if (exp instanceof VariableExpression) { + final VariableExpression vexp = (VariableExpression) exp; + ClassNode selfTrait = isTraitSelf(vexp); + if (selfTrait != null) return makeSelf(selfTrait); + if (vexp == VariableExpression.THIS_EXPRESSION) return makeThis(); + if (vexp == VariableExpression.SUPER_EXPRESSION) return makeSuper(); + final Variable variable = vexp.getAccessedVariable(); + if (variable instanceof FieldNode) { + checkOrMarkPrivateAccess(vexp, (FieldNode) variable, isLHSOfEnclosingAssignment(vexp)); + return getType((FieldNode) variable); + } + if (variable != null && variable != vexp && variable instanceof VariableExpression) { + return getType((Expression) variable); + } + if (variable instanceof Parameter) { + Parameter parameter = (Parameter) variable; + ClassNode type = null; + // check if param part of control structure - but not if inside instanceof + List temporaryTypesForExpression = getTemporaryTypesForExpression(vexp); + if (temporaryTypesForExpression == null || temporaryTypesForExpression.isEmpty()) { + type = typeCheckingContext.controlStructureVariables.get(parameter); + } + // now check for closure override + TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure(); + if (type == null && enclosingClosure != null && temporaryTypesForExpression == null) { + type = getTypeFromClosureArguments(parameter, enclosingClosure); + } + if (type != null) { + storeType(vexp, type); + return type; + } + return getType((Parameter) variable); + } + return vexp.getOriginType(); + } + + if (exp instanceof ListExpression) { + return inferListExpressionType((ListExpression) exp); + } + if (exp instanceof MapExpression) { + return inferMapExpressionType((MapExpression) exp); + } + if (exp instanceof ConstructorCallExpression) { + return ((ConstructorCallExpression) exp).getType(); + } + if (exp instanceof MethodNode) { + if ((exp == GET_DELEGATE || exp == GET_OWNER || exp == GET_THISOBJECT) && typeCheckingContext.getEnclosingClosure() != null) { + return typeCheckingContext.getEnclosingClassNode(); + } + ClassNode ret = getInferredReturnType(exp); + return ret != null ? ret : ((MethodNode) exp).getReturnType(); + } + if (exp instanceof RangeExpression) { + ClassNode plain = ClassHelper.RANGE_TYPE.getPlainNodeReference(); + RangeExpression re = (RangeExpression) exp; + ClassNode fromType = getType(re.getFrom()); + ClassNode toType = getType(re.getTo()); + if (fromType.equals(toType)) { + plain.setGenericsTypes(new GenericsType[]{ + new GenericsType(wrapTypeIfNecessary(fromType)) + }); + } else { + plain.setGenericsTypes(new GenericsType[]{ + new GenericsType(wrapTypeIfNecessary(lowestUpperBound(fromType, toType))) + }); + } + return plain; + } + if (exp instanceof UnaryPlusExpression) { + return getType(((UnaryPlusExpression) exp).getExpression()); + } + if (exp instanceof UnaryMinusExpression) { + return getType(((UnaryMinusExpression) exp).getExpression()); + } + if (exp instanceof BitwiseNegationExpression) { + return getType(((BitwiseNegationExpression) exp).getExpression()); + } + if (exp instanceof Parameter) { + return ((Parameter) exp).getOriginType(); + } + if (exp instanceof FieldNode) { + FieldNode fn = (FieldNode) exp; + return getGenericsResolvedTypeOfFieldOrProperty(fn, fn.getOriginType()); + } + if (exp instanceof PropertyNode) { + PropertyNode pn = (PropertyNode) exp; + return getGenericsResolvedTypeOfFieldOrProperty(pn, pn.getOriginType()); + } + if (exp instanceof ClosureExpression) { + ClassNode irt = getInferredReturnType(exp); + if (irt != null) { + irt = wrapTypeIfNecessary(irt); + ClassNode result = CLOSURE_TYPE.getPlainNodeReference(); + result.setGenericsTypes(new GenericsType[]{new GenericsType(irt)}); + return result; + } + } else if (exp instanceof MethodCall) { + MethodNode target = (MethodNode) exp.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + if (target != null) { + return getType(target); + } + } + return ((Expression) exp).getType(); + } + + private ClassNode getTypeFromClosureArguments(Parameter parameter, TypeCheckingContext.EnclosingClosure enclosingClosure) { + ClosureExpression closureExpression = enclosingClosure.getClosureExpression(); + ClassNode[] closureParamTypes = (ClassNode[]) closureExpression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS); + if (closureParamTypes == null) return null; + final Parameter[] parameters = closureExpression.getParameters(); + String name = parameter.getName(); + + if (parameters.length == 0) { + return "it".equals(name) && closureParamTypes.length != 0 ? closureParamTypes[0] : null; + } + + for (int index = 0; index < parameters.length; index++) { + if (name.equals(parameters[index].getName())) { + return closureParamTypes.length > index ? closureParamTypes[index] : null; + } + } + + return null; + } + + /** + * resolves a Field or Property node generics by using the current class and + * the declaring class to extract the right meaning of the generics symbols + * @param an a FieldNode or PropertyNode + * @param type the origin type + * @return the new ClassNode with corrected generics + */ + private ClassNode getGenericsResolvedTypeOfFieldOrProperty(AnnotatedNode an, ClassNode type) { + if (!type.isUsingGenerics()) return type; + Map connections = new HashMap(); + //TODO: inner classes mean a different this-type. This is ignored here! + extractGenericsConnections(connections, typeCheckingContext.getEnclosingClassNode(), an.getDeclaringClass()); + type= applyGenericsContext(connections, type); + return type; + } + + private ClassNode makeSuper() { + ClassNode ret = typeCheckingContext.getEnclosingClassNode().getSuperClass(); + if (typeCheckingContext.isInStaticContext) { + ClassNode staticRet = CLASS_Type.getPlainNodeReference(); + GenericsType gt = new GenericsType(ret); + staticRet.setGenericsTypes(new GenericsType[]{gt}); + ret = staticRet; + } + return ret; + } + + private ClassNode makeThis() { + ClassNode ret = typeCheckingContext.getEnclosingClassNode(); + if (typeCheckingContext.isInStaticContext) { + ClassNode staticRet = CLASS_Type.getPlainNodeReference(); + GenericsType gt = new GenericsType(ret); + staticRet.setGenericsTypes(new GenericsType[]{gt}); + ret = staticRet; + } + return ret; + } + + private static ClassNode makeSelf(ClassNode trait) { + ClassNode ret = trait; + LinkedHashSet selfTypes = new LinkedHashSet(); + Traits.collectSelfTypes(ret, selfTypes); + if (!selfTypes.isEmpty()) { + selfTypes.add(ret); + ret = new UnionTypeClassNode(selfTypes.toArray(new ClassNode[selfTypes.size()])); + } + return ret; + } + + /** + * Stores the inferred return type of a closure or a method. We are using a separate key to store + * inferred return type because the inferred type of a closure is {@link Closure}, which is different + * from the inferred type of the code of the closure. + * + * @param node a {@link ClosureExpression} or a {@link MethodNode} + * @param type the inferred return type of the code + * @return the old value of the inferred type + */ + protected ClassNode storeInferredReturnType(final ASTNode node, final ClassNode type) { + if (!(node instanceof ClosureExpression)) { + throw new IllegalArgumentException("Storing inferred return type is only allowed on closures but found "+node.getClass()); + } + return (ClassNode) node.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, type); + } + + /** + * Returns the inferred return type of a closure or a method, if stored on the AST node. This method + * doesn't perform any type inference by itself. + * @param exp a {@link ClosureExpression} or {@link MethodNode} + * @return the inferred type, as stored on node metadata. + */ + protected ClassNode getInferredReturnType(final ASTNode exp) { + return (ClassNode) exp.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } + + protected ClassNode inferListExpressionType(final ListExpression list) { + List expressions = list.getExpressions(); + if (expressions.isEmpty()) { + // cannot infer, return list type + return list.getType(); + } + ClassNode listType = list.getType(); + GenericsType[] genericsTypes = listType.getGenericsTypes(); + if ((genericsTypes == null + || genericsTypes.length == 0 + || (genericsTypes.length == 1 && OBJECT_TYPE.equals(genericsTypes[0].getType()))) + && (!expressions.isEmpty())) { + // maybe we can infer the component type + List nodes = new LinkedList(); + for (Expression expression : expressions) { + if (isNullConstant(expression)) { + // a null element is found in the list, skip it because we'll use the other elements from the list + } else { + nodes.add(getType(expression)); + } + } + if (nodes.isEmpty()) { + // every element was the null constant + return listType; + } + ClassNode superType = getWrapper(lowestUpperBound(nodes)); // to be used in generics, type must be boxed + ClassNode inferred = listType.getPlainNodeReference(); + inferred.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(superType))}); + return inferred; + } + return listType; + } + + protected static boolean isNullConstant(final Expression expression) { + return expression instanceof ConstantExpression && ((ConstantExpression) expression).isNullExpression(); + } + + protected ClassNode inferMapExpressionType(final MapExpression map) { + ClassNode mapType = LINKEDHASHMAP_CLASSNODE.getPlainNodeReference(); + List entryExpressions = map.getMapEntryExpressions(); + if (entryExpressions.isEmpty()) return mapType; + GenericsType[] genericsTypes = mapType.getGenericsTypes(); + if (genericsTypes == null + || genericsTypes.length < 2 + || (genericsTypes.length == 2 && OBJECT_TYPE.equals(genericsTypes[0].getType()) && OBJECT_TYPE.equals(genericsTypes[1].getType()))) { + List keyTypes = new LinkedList(); + List valueTypes = new LinkedList(); + for (MapEntryExpression entryExpression : entryExpressions) { + keyTypes.add(getType(entryExpression.getKeyExpression())); + valueTypes.add(getType(entryExpression.getValueExpression())); + } + ClassNode keyType = getWrapper(lowestUpperBound(keyTypes)); // to be used in generics, type must be boxed + ClassNode valueType = getWrapper(lowestUpperBound(valueTypes)); // to be used in generics, type must be boxed + if (!OBJECT_TYPE.equals(keyType) || !OBJECT_TYPE.equals(valueType)) { + ClassNode inferred = mapType.getPlainNodeReference(); + inferred.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(keyType)), new GenericsType(wrapTypeIfNecessary(valueType))}); + return inferred; + } + } + return mapType; + } + + private static class ExtensionMethodDeclaringClass{} + + /** + * If a method call returns a parameterized type, then we can perform additional inference on the + * return type, so that the type gets actual type parameters. For example, the method + * Arrays.asList(T...) is generified with type T which can be deduced from actual type + * arguments. + * + * @param method the method node + * @param arguments the method call arguments + * @return parameterized, infered, class node + */ + protected ClassNode inferReturnTypeGenerics(ClassNode receiver, MethodNode method, Expression arguments) { + return inferReturnTypeGenerics(receiver, method, arguments, null); + } + + /** + * If a method call returns a parameterized type, then we can perform additional inference on the + * return type, so that the type gets actual type parameters. For example, the method + * Arrays.asList(T...) is generified with type T which can be deduced from actual type + * arguments. + * + * @param method the method node + * @param arguments the method call arguments + * @param explicitTypeHints explicit type hints as found for example in Collections.<String>emptyList() + * @return parameterized, infered, class node + */ + protected ClassNode inferReturnTypeGenerics( + ClassNode receiver, + MethodNode method, + Expression arguments, + GenericsType[] explicitTypeHints) { + ClassNode returnType = method.getReturnType(); + if (method instanceof ExtensionMethodNode + && (isUsingGenericsOrIsArrayUsingGenerics(returnType))) { + // check if the placeholder corresponds to the placeholder of the first parameter + ExtensionMethodNode emn = (ExtensionMethodNode) method; + MethodNode dgmMethod = emn.getExtensionMethodNode(); + ClassNode dc = emn.getDeclaringClass(); + ArgumentListExpression argList = new ArgumentListExpression(); + VariableExpression vexp = varX("$foo", receiver); + vexp.setNodeMetaData(ExtensionMethodDeclaringClass.class, dc); + argList.addExpression(vexp); + if (arguments instanceof ArgumentListExpression) { + List expressions = ((ArgumentListExpression) arguments).getExpressions(); + for (Expression arg : expressions) { + argList.addExpression(arg); + } + } else { + argList.addExpression(arguments); + } + return inferReturnTypeGenerics(receiver, dgmMethod, argList); + } + if (!isUsingGenericsOrIsArrayUsingGenerics(returnType)) return returnType; + if (getGenericsWithoutArray(returnType)==null) return returnType; + Map resolvedPlaceholders = resolvePlaceHoldersFromDeclaration(receiver, getDeclaringClass(method, arguments), method, method.isStatic()); + if (!receiver.isGenericsPlaceHolder()) { + GenericsUtils.extractPlaceholders(receiver, resolvedPlaceholders); + } + resolvePlaceholdersFromExplicitTypeHints(method, explicitTypeHints, resolvedPlaceholders); + if (resolvedPlaceholders.isEmpty()) { + return boundUnboundedWildcards(returnType); + } + Map placeholdersFromContext = extractGenericsParameterMapOfThis(typeCheckingContext.getEnclosingMethod()); + applyGenericsConnections(placeholdersFromContext,resolvedPlaceholders); + + // then resolve receivers from method arguments + Parameter[] parameters = method.getParameters(); + boolean isVargs = isVargs(parameters); + ArgumentListExpression argList = InvocationWriter.makeArgumentList(arguments); + List expressions = argList.getExpressions(); + int paramLength = parameters.length; + if (expressions.size() >= paramLength) { + for (int i = 0; i < paramLength; i++) { + boolean lastArg = i == paramLength - 1; + ClassNode type = parameters[i].getType(); + ClassNode actualType = getType(expressions.get(i)); + while (!type.isUsingGenerics() && type.isArray() && actualType.isArray()) { + type = type.getComponentType(); + actualType = actualType.getComponentType(); + } + if (isUsingGenericsOrIsArrayUsingGenerics(type)) { + if (implementsInterfaceOrIsSubclassOf(actualType, CLOSURE_TYPE) && + isSAMType(type)) { + // implicit closure coercion in action! + Map pholders = applyGenericsContextToParameterClass(resolvedPlaceholders, type); + actualType = convertClosureTypeToSAMType(expressions.get(i), actualType, type, pholders); + } + if (isVargs && lastArg && actualType.isArray()) { + actualType = actualType.getComponentType(); + } + if (isVargs && lastArg && type.isArray()) { + type = type.getComponentType(); + } + actualType = wrapTypeIfNecessary(actualType); + + Map connections = new HashMap(); + extractGenericsConnections(connections, actualType,type); + extractGenericsConnectionsForSuperClassAndInterfaces(resolvedPlaceholders, connections); + applyGenericsConnections(connections, resolvedPlaceholders); + } + } + } + + return applyGenericsContext(resolvedPlaceholders, returnType); + } + + private static void resolvePlaceholdersFromExplicitTypeHints(final MethodNode method, final GenericsType[] explicitTypeHints, final Map resolvedPlaceholders) { + if (explicitTypeHints!=null) { + GenericsType[] methodGenericTypes = method.getGenericsTypes(); + if (methodGenericTypes!=null && methodGenericTypes.length==explicitTypeHints.length) { + for (int i = 0; i < explicitTypeHints.length; i++) { + GenericsType methodGenericType = methodGenericTypes[i]; + GenericsType explicitTypeHint = explicitTypeHints[i]; + resolvedPlaceholders.put(methodGenericType.getName(), explicitTypeHint); + } + } + } + } + + private static void extractGenericsConnectionsForSuperClassAndInterfaces(final Map resolvedPlaceholders, final Map connections) { + for (GenericsType value : new HashSet(connections.values())) { + if (!value.isPlaceholder() && !value.isWildcard()) { + ClassNode valueType = value.getType(); + List deepNodes = new LinkedList(); + ClassNode unresolvedSuperClass = valueType.getUnresolvedSuperClass(); + if (unresolvedSuperClass!=null && unresolvedSuperClass.isUsingGenerics()) { + deepNodes.add(unresolvedSuperClass); + } + for (ClassNode node : valueType.getUnresolvedInterfaces()) { + if (node.isUsingGenerics()) { + deepNodes.add(node); + } + } + if (!deepNodes.isEmpty()) { + for (GenericsType genericsType : resolvedPlaceholders.values()) { + ClassNode lowerBound = genericsType.getLowerBound(); + if (lowerBound != null) { + for (ClassNode deepNode : deepNodes) { + if (lowerBound.equals(deepNode)) { + extractGenericsConnections(connections, deepNode, lowerBound); + } + } + } + ClassNode[] upperBounds = genericsType.getUpperBounds(); + if (upperBounds != null) { + for (ClassNode upperBound : upperBounds) { + for (ClassNode deepNode : deepNodes) { + if (upperBound.equals(deepNode)) { + extractGenericsConnections(connections, deepNode, upperBound); + } + } + } + } + } + } + } + } + } + + /** + * This method will convert a closure type to the appropriate SAM type, which will be used + * to infer return type generics. + * + * @param closureType the inferred type of a closure (Closure<ClosureReturnType>) + * @param samType the type into which the closure is coerced into + * @return same SAM type, but completed with information from the closure node + */ + private static ClassNode convertClosureTypeToSAMType(final Expression expression, final ClassNode closureType, final ClassNode samType, final Map placeholders) { + if (!samType.isUsingGenerics()) return samType; + + // use the generics information from the Closure to further specify the type + MethodNode sam = findSAM(samType); + if (closureType.isUsingGenerics() && sam!=null) { + //correct SAM type for generics + //sam = applyGenericsContext(placeholders, sam); + + // the return type of the SAM method exactly corresponds to the inferred return type + ClassNode samReturnType = sam.getReturnType(); + ClassNode closureReturnType = expression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + if (closureReturnType!=null && closureReturnType.isUsingGenerics()) { + ClassNode unwrapped = closureReturnType.getGenericsTypes()[0].getType(); + extractGenericsConnections(placeholders, unwrapped, samReturnType); + } else if (samReturnType.isGenericsPlaceHolder()) { + placeholders.put(samReturnType.getGenericsTypes()[0].getName(), closureType.getGenericsTypes()[0]); + } + + // now repeat the same for each parameter given in the ClosureExpression + if (expression instanceof ClosureExpression && sam.getParameters().length > 0) { + List genericsToConnect = new LinkedList(); + Parameter[] closureParams = ((ClosureExpression) expression).getParameters(); + ClassNode[] closureParamTypes = extractTypesFromParameters(closureParams); + if (expression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS)!=null) { + closureParamTypes = expression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS); + } + final Parameter[] parameters = sam.getParameters(); + for (int i = 0; i < parameters.length; i++) { + final Parameter parameter = parameters[i]; + if (parameter.getOriginType().isUsingGenerics() && closureParamTypes.length>i) { + genericsToConnect.add(new ClassNode[]{closureParamTypes[i], parameter.getOriginType()}); + } + } + for (ClassNode[] classNodes : genericsToConnect) { + ClassNode found = classNodes[0]; + ClassNode expected = classNodes[1]; + if (!isAssignableTo(found, expected)) { + // probably facing a type mismatch + continue; + } + ClassNode generifiedType = GenericsUtils.parameterizeType(found, expected); + while (expected.isArray()) { + expected = expected.getComponentType(); + generifiedType = generifiedType.getComponentType(); + } + if (expected.isGenericsPlaceHolder()) { + placeholders.put(expected.getGenericsTypes()[0].getName(), new GenericsType(generifiedType)); + } else { + GenericsType[] expectedGenericsTypes = expected.getGenericsTypes(); + GenericsType[] foundGenericsTypes = generifiedType.getGenericsTypes(); + + for (int i = 0; i < expectedGenericsTypes.length; i++) { + final GenericsType type = expectedGenericsTypes[i]; + if (type.isPlaceholder()) { + String name = type.getName(); + placeholders.put(name, foundGenericsTypes[i]); + } + } + } + } + } + } + ClassNode result = applyGenericsContext(placeholders,samType.redirect()); + return result; + } + + private ClassNode resolveGenericsWithContext(Map resolvedPlaceholders, ClassNode currentType) { + Map placeholdersFromContext = extractGenericsParameterMapOfThis(typeCheckingContext.getEnclosingMethod()); + return resolveClassNodeGenerics(resolvedPlaceholders, placeholdersFromContext, currentType); + } + + private static ClassNode getDeclaringClass(MethodNode method, Expression arguments) { + ClassNode declaringClass = method.getDeclaringClass(); + + // correcting declaring class for extension methods: + if (arguments instanceof ArgumentListExpression) { + ArgumentListExpression al = (ArgumentListExpression) arguments; + List list = al.getExpressions(); + if (list.isEmpty()) return declaringClass; + Expression exp = list.get(0); + ClassNode cn = exp.getNodeMetaData(ExtensionMethodDeclaringClass.class); + if (cn!=null) return cn; + } + return declaringClass; + } + + private Map resolvePlaceHoldersFromDeclaration(ClassNode receiver, ClassNode declaration, MethodNode method, boolean isStaticTarget) { + Map resolvedPlaceholders; + if ( isStaticTarget && CLASS_Type.equals(receiver) && + receiver.isUsingGenerics() && + receiver.getGenericsTypes().length>0 && + !OBJECT_TYPE.equals(receiver.getGenericsTypes()[0].getType())) + { + return resolvePlaceHoldersFromDeclaration(receiver.getGenericsTypes()[0].getType(), declaration, method, isStaticTarget); + } else { + resolvedPlaceholders = extractPlaceHolders(method, receiver, declaration); + } + return resolvedPlaceholders; + } + + private static boolean isGenericsPlaceHolderOrArrayOf(ClassNode cn) { + if (cn.isArray()) return isGenericsPlaceHolderOrArrayOf(cn.getComponentType()); + return cn.isGenericsPlaceHolder(); + } + + private static Map extractPlaceHolders(MethodNode method, ClassNode receiver, ClassNode declaringClass) { + if (declaringClass.equals(OBJECT_TYPE)) { + Map resolvedPlaceholders = new HashMap(); + if (method!=null) addMethodLevelDeclaredGenerics(method, resolvedPlaceholders); + return resolvedPlaceholders; + } + + Map resolvedPlaceholders = null; + if (isPrimitiveType(receiver) && !isPrimitiveType(declaringClass)) { + receiver = getWrapper(receiver); + } + final List queue; + if (receiver instanceof UnionTypeClassNode) { + queue = Arrays.asList(((UnionTypeClassNode) receiver).getDelegates()); + } else { + queue = Collections.singletonList(receiver); + } + for (ClassNode item : queue) { + ClassNode current = item; + while (current!=null) { + boolean continueLoop = true; + //extract the place holders + Map currentPlaceHolders = new HashMap(); + if (isGenericsPlaceHolderOrArrayOf(declaringClass) || declaringClass.equals(current)) { + extractGenericsConnections(currentPlaceHolders, current, declaringClass); + if (method!=null) addMethodLevelDeclaredGenerics(method, currentPlaceHolders); + continueLoop = false; + } else { + GenericsUtils.extractPlaceholders(current, currentPlaceHolders); + } + + if (resolvedPlaceholders!=null) { + // merge maps + Set> entries = currentPlaceHolders.entrySet(); + for (Map.Entry entry : entries) { + GenericsType gt = entry.getValue(); + if (!gt.isPlaceholder()) continue; + GenericsType referenced = resolvedPlaceholders.get(gt.getName()); + if (referenced==null) continue; + entry.setValue(referenced); + } + } + resolvedPlaceholders = currentPlaceHolders; + + // we are done if we are now in the declaring class + if (!continueLoop) break; + + current = getNextSuperClass(current, declaringClass); + if (current==null && CLASS_Type.equals(declaringClass)) { + // this can happen if the receiver is Class, then + // the actual receiver is Foo and declaringClass is Class + current = declaringClass; + } + } + } + if (resolvedPlaceholders==null) { + String descriptor = "<>"; + if (method!=null) descriptor = method.getTypeDescriptor(); + throw new GroovyBugError( + "Declaring class for method call to '" + + descriptor + "' declared in " + declaringClass.getName() + + " was not matched with found receiver "+ receiver.getName() + "." + + " This should not have happened!"); + } + return resolvedPlaceholders; + } + + protected boolean typeCheckMethodsWithGenericsOrFail(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod, Expression location) { + if (!typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod)) { + Map classGTs = GenericsUtils.extractPlaceholders(receiver); + ClassNode[] ptypes = new ClassNode[candidateMethod.getParameters().length]; + final Parameter[] parameters = candidateMethod.getParameters(); + for (int i = 0; i < parameters.length; i++) { + final Parameter parameter = parameters[i]; + ClassNode type = parameter.getType(); + ptypes[i] = fullyResolveType(type, classGTs); + } + addStaticTypeError("Cannot call " + toMethodGenericTypesString(candidateMethod) + receiver.toString(false) + "#" + + toMethodParametersString(candidateMethod.getName(), ptypes) + + " with arguments " + formatArgumentList(arguments), location); + return false; + } + return true; + } + + private static String toMethodGenericTypesString(MethodNode node) { + GenericsType[] genericsTypes = node.getGenericsTypes(); + if (genericsTypes ==null) return ""; + StringBuilder sb = new StringBuilder("<"); + for (int i = 0; i < genericsTypes.length; i++) { + final GenericsType genericsType = genericsTypes[i]; + sb.append(genericsType.toString()); + if (i "); + return sb.toString(); + } + + protected static String formatArgumentList(ClassNode[] nodes) { + if (nodes == null || nodes.length==0) return "[]"; + StringBuilder sb = new StringBuilder(24 * nodes.length); + sb.append("["); + for (ClassNode node : nodes) { + sb.append(prettyPrintType(node)); + sb.append(", "); + } + if (sb.length() > 1) { + sb.setCharAt(sb.length() - 2, ']'); + } + return sb.toString(); + } + + private static void putSetterInfo(Expression exp, SetterInfo info) { + exp.putNodeMetaData(SetterInfo.class, info); + } + + private static SetterInfo removeSetterInfo(Expression exp) { + Object nodeMetaData = exp.getNodeMetaData(SetterInfo.class); + if (nodeMetaData!=null) { + exp.removeNodeMetaData(SetterInfo.class); + return (SetterInfo) nodeMetaData; + } + return null; + } + + @Override + public void addError(final String msg, final ASTNode expr) { + Long err = ((long) expr.getLineNumber()) << 16 + expr.getColumnNumber(); + if ((DEBUG_GENERATED_CODE && expr.getLineNumber()<0) || !typeCheckingContext.reportedErrors.contains(err)) { + typeCheckingContext.getErrorCollector().addErrorAndContinue(new SyntaxErrorMessage( + new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), + typeCheckingContext.source) + ); + typeCheckingContext.reportedErrors.add(err); + } + } + + protected void addStaticTypeError(final String msg, final ASTNode expr) { + if (expr.getColumnNumber() > 0 && expr.getLineNumber() > 0) { + addError(StaticTypesTransformation.STATIC_ERROR_PREFIX + msg, expr); + } else { + if (DEBUG_GENERATED_CODE) { + addError(StaticTypesTransformation.STATIC_ERROR_PREFIX + "Error in generated code ["+expr.getText()+"] - "+ msg, expr); + } + // ignore errors which are related to unknown source locations + // because they are likely related to generated code + } + } + + protected void addNoMatchingMethodError(ClassNode receiver, final String name, final ClassNode[] args, final Expression call) { + if (isClassClassNodeWrappingConcreteType(receiver)) { + receiver = receiver.getGenericsTypes()[0].getType(); + } + addStaticTypeError("Cannot find matching method " + receiver.getText() + "#" + toMethodParametersString(name, args) + ". Please check if the declared type is correct and if the method exists.", call); + } + + protected void addAmbiguousErrorMessage(final List foundMethods, final String name, final ClassNode[] args, final Expression expr) { + addStaticTypeError("Reference to method is ambiguous. Cannot choose between " + prettyPrintMethodList(foundMethods), expr); + } + + protected void addCategoryMethodCallError(final Expression call) { + addStaticTypeError("Due to their dynamic nature, usage of categories is not possible with static type checking active", call); + } + + protected void addAssignmentError(final ClassNode leftType, final ClassNode rightType, final Expression assignmentExpression) { + addStaticTypeError("Cannot assign value of type " + rightType.toString(false) + " to variable of type " + leftType.toString(false), assignmentExpression); + } + + protected void addUnsupportedPreOrPostfixExpressionError(final Expression expression) { + if (expression instanceof PostfixExpression) { + addStaticTypeError("Unsupported postfix operation type [" + ((PostfixExpression)expression).getOperation() + "]", expression); + } else if (expression instanceof PrefixExpression) { + addStaticTypeError("Unsupported prefix operation type [" + ((PrefixExpression)expression).getOperation() + "]", expression); + } else { + throw new IllegalArgumentException("Method should be called with a PostfixExpression or a PrefixExpression"); + } + } + + public void setMethodsToBeVisited(final Set methodsToBeVisited) { + this.typeCheckingContext.methodsToBeVisited = methodsToBeVisited; + } + + public void performSecondPass() { + for (SecondPassExpression wrapper : typeCheckingContext.secondPassExpressions) { + Expression expression = wrapper.getExpression(); + if (expression instanceof BinaryExpression) { + Expression left = ((BinaryExpression) expression).getLeftExpression(); + if (left instanceof VariableExpression) { + // should always be the case + // this should always be the case, but adding a test is safer + Variable target = findTargetVariable((VariableExpression) left); + if (target instanceof VariableExpression) { + VariableExpression var = (VariableExpression) target; + List classNodes = typeCheckingContext.closureSharedVariablesAssignmentTypes.get(var); + if (classNodes != null && classNodes.size() > 1) { + ClassNode lub = lowestUpperBound(classNodes); + String message = getOperationName(((BinaryExpression) expression).getOperation().getType()); + if (message!=null) { + List method = findMethod(lub, message, getType(((BinaryExpression) expression).getRightExpression())); + if (method.isEmpty()) { + addStaticTypeError("A closure shared variable [" + target.getName() + "] has been assigned with various types and the method" + + " [" + toMethodParametersString(message, getType(((BinaryExpression) expression).getRightExpression())) + "]" + + " does not exist in the lowest upper bound of those types: [" + + lub.toString(false) + "]. In general, this is a bad practice (variable reuse) because the compiler cannot" + + " determine safely what is the type of the variable at the moment of the call in a multithreaded context.", expression); + } + } + } + } + } + } else if (expression instanceof MethodCallExpression) { + MethodCallExpression call = (MethodCallExpression) expression; + Expression objectExpression = call.getObjectExpression(); + if (objectExpression instanceof VariableExpression) { + // this should always be the case, but adding a test is safer + Variable target = findTargetVariable((VariableExpression) objectExpression); + if (target instanceof VariableExpression) { + VariableExpression var = (VariableExpression) target; + List classNodes = typeCheckingContext.closureSharedVariablesAssignmentTypes.get(var); + if (classNodes != null && classNodes.size() > 1) { + ClassNode lub = lowestUpperBound(classNodes); + MethodNode methodNode = (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + // we must check that such a method exists on the LUB + Parameter[] parameters = methodNode.getParameters(); + ClassNode[] params = extractTypesFromParameters(parameters); + ClassNode[] argTypes = (ClassNode[]) wrapper.getData(); + List method = findMethod(lub, methodNode.getName(), argTypes); + if (method.size() != 1) { + addStaticTypeError("A closure shared variable [" + target.getName() + "] has been assigned with various types and the method" + + " [" + toMethodParametersString(methodNode.getName(), params) + "]" + + " does not exist in the lowest upper bound of those types: [" + + lub.toString(false) + "]. In general, this is a bad practice (variable reuse) because the compiler cannot" + + " determine safely what is the type of the variable at the moment of the call in a multithreaded context.", call); + } + } + } + } + } + } + // give a chance to type checker extensions to throw errors based on information gathered afterwards + extension.finish(); + } + + protected static ClassNode[] extractTypesFromParameters(final Parameter[] parameters) { + ClassNode[] params = new ClassNode[parameters.length]; + for (int i = 0; i < params.length; i++) { + params[i] = parameters[i].getType(); + } + return params; + } + + /** + * Returns a wrapped type if, and only if, the provided class node is a primitive type. + * This method differs from {@link ClassHelper#getWrapper(org.codehaus.groovy.ast.ClassNode)} as it will + * return the same instance if the provided type is not a generic type. + * + * @param type + * @return the wrapped type + */ + protected static ClassNode wrapTypeIfNecessary(ClassNode type) { + if (isPrimitiveType(type)) return getWrapper(type); + return type; + } + + protected static boolean isClassInnerClassOrEqualTo(ClassNode toBeChecked, ClassNode start) { + if (start == toBeChecked) return true; + if (start instanceof InnerClassNode) { + return isClassInnerClassOrEqualTo(toBeChecked, start.getOuterClass()); + } + return false; + } + + protected class VariableExpressionTypeMemoizer extends ClassCodeVisitorSupport { + private final Map varOrigType; + + public VariableExpressionTypeMemoizer(final Map varOrigType) { + this.varOrigType = varOrigType; + } + + @Override + protected SourceUnit getSourceUnit() { + return typeCheckingContext.source; + } + + @Override + public void visitVariableExpression(final VariableExpression expression) { + super.visitVariableExpression(expression); + Variable var = findTargetVariable(expression); + if (var instanceof VariableExpression) { + VariableExpression ve = (VariableExpression) var; + varOrigType.put(ve, (ClassNode) ve.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE)); + } + } + } + + // ------------------- codecs for method return type signatures ------------------------------ + + public static class SignatureCodecFactory { + public static SignatureCodec getCodec(int version, final ClassLoader classLoader) { + switch (version) { + case 1: return new SignatureCodecVersion1(classLoader); + default: return null; + } + } + } + + // class only used to store setter information when an expression of type + // a.x = foo or x=foo is found and that it corresponds to a setter call + private static class SetterInfo { + final ClassNode receiverType; + final String name; + final List setters; + + private SetterInfo(final ClassNode receiverType, final String name, final List setters) { + this.receiverType = receiverType; + this.setters = setters; + this.name = name; + } + } + + /** + * Wrapper for a Parameter so it can be treated like a VariableExpression + * and tracked in the ifElseForWhileAssignmentTracker. + * + * This class purposely does not adhere to the normal equals and hashCode + * contract on the Object class and delegates those calls to the wrapped + * variable. + */ + private static class ParameterVariableExpression extends VariableExpression { + + private final Parameter parameter; + + ParameterVariableExpression(Parameter parameter) { + super(parameter); + this.parameter = parameter; + ClassNode inferred = parameter.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + if (inferred == null) { + parameter.setNodeMetaData(StaticTypesMarker.INFERRED_TYPE, parameter.getOriginType()); + } + } + + @Override + public void copyNodeMetaData(ASTNode other) { + parameter.copyNodeMetaData(other); + } + + @Override + public Object putNodeMetaData(Object key, Object value) { + return parameter.putNodeMetaData(key, value); + } + + @Override + public void removeNodeMetaData(Object key) { + parameter.removeNodeMetaData(key); + } + + @Override + public Map getNodeMetaData() { + return parameter.getNodeMetaData(); + } + + @Override + public T getNodeMetaData(Object key) { + return parameter.getNodeMetaData(key); + } + + @Override + public void setNodeMetaData(Object key, Object value) { + parameter.setNodeMetaData(key, value); + } + + @Override + public int hashCode() { + return parameter.hashCode(); + } + + @Override + public boolean equals(Object other) { + return parameter.equals(other); + } + } +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/trait/TraitASTTransformation.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/trait/TraitASTTransformation.java new file mode 100644 index 0000000000..4319c7e4c4 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/transform/trait/TraitASTTransformation.java @@ -0,0 +1,632 @@ +/* + * 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.transform.trait; + +import groovy.transform.CompilationUnitAware; +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.ast.tools.GeneralUtils; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.classgen.VariableScopeVisitor; +import org.codehaus.groovy.classgen.Verifier; +import org.codehaus.groovy.control.CompilationFailedException; +import org.codehaus.groovy.control.CompilationUnit; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.codehaus.groovy.syntax.SyntaxException; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.ASTTransformationCollectorCodeVisitor; +import org.codehaus.groovy.transform.AbstractASTTransformation; +import org.codehaus.groovy.transform.GroovyASTTransformation; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS; +import static org.codehaus.groovy.transform.trait.SuperCallTraitTransformer.UNRESOLVED_HELPER_CLASS; + +/** + * Handles generation of code for the traits (trait keyword is equivalent to using the @Trait annotation). + * A class annotated with @Trait will generate, instead: + *

+ * + * @author Cedric Champeau + */ +@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) +public class TraitASTTransformation extends AbstractASTTransformation implements CompilationUnitAware { + + public static final String DO_DYNAMIC = TraitReceiverTransformer.class+".doDynamic"; + public static final String POST_TYPECHECKING_REPLACEMENT = TraitReceiverTransformer.class+".replacement"; + + private static final ClassNode INVOKERHELPER_CLASSNODE = ClassHelper.make(InvokerHelper.class); + + private static final ClassNode OVERRIDE_CLASSNODE = ClassHelper.make(Override.class); + + private SourceUnit unit; + private CompilationUnit compilationUnit; + + public void visit(ASTNode[] nodes, SourceUnit source) { + AnnotatedNode parent = (AnnotatedNode) nodes[1]; + AnnotationNode anno = (AnnotationNode) nodes[0]; + if (!Traits.TRAIT_CLASSNODE.equals(anno.getClassNode())) return; + unit = source; + init(nodes, source); + if (parent instanceof ClassNode) { + ClassNode cNode = (ClassNode) parent; + if (!checkNotInterface(cNode, Traits.TRAIT_TYPE_NAME)) return; + checkNoConstructor(cNode); + checkExtendsClause(cNode); + generateMethodsWithDefaultArgs(cNode); + replaceExtendsByImplements(cNode); + ClassNode helperClassNode = createHelperClass(cNode); + resolveHelperClassIfNecessary(helperClassNode); + } + } + + private void resolveHelperClassIfNecessary(final ClassNode helperClassNode) { + if (helperClassNode == null) { + return; + } + for (ClassNode cNode : unit.getAST().getClasses()) { + ClassNode unresolvedHelperNode = cNode.getNodeMetaData(UNRESOLVED_HELPER_CLASS); + if (unresolvedHelperNode != null + && unresolvedHelperNode.getName().equals(helperClassNode.getName())) { + unresolvedHelperNode.setRedirect(helperClassNode); + } + } + } + + private static void generateMethodsWithDefaultArgs(final ClassNode cNode) { + DefaultArgsMethodsAdder adder = new DefaultArgsMethodsAdder(); + adder.addDefaultParameterMethods(cNode); + } + + private void checkExtendsClause(final ClassNode cNode) { + ClassNode superClass = cNode.getSuperClass(); + if (superClass.isInterface() && !Traits.isTrait(superClass)) { + addError("Trait cannot extend an interface. Use 'implements' instead", cNode); + } + } + + private void replaceExtendsByImplements(final ClassNode cNode) { + ClassNode superClass = cNode.getUnresolvedSuperClass(); + if (Traits.isTrait(superClass)) { + // move from super class to interface; + cNode.setSuperClass(ClassHelper.OBJECT_TYPE); + cNode.setUnresolvedSuperClass(ClassHelper.OBJECT_TYPE); + cNode.addInterface(superClass); + resolveScope(cNode); + } + } + + private void resolveScope(final ClassNode cNode) { + // we need to resolve again! + VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(unit); + scopeVisitor.visitClass(cNode); + } + + private void checkNoConstructor(final ClassNode cNode) { + if (!cNode.getDeclaredConstructors().isEmpty()) { + addError("Error processing trait '" + cNode.getName() + "'. " + + " Constructors are not allowed.", cNode); + } + } + + private ClassNode createHelperClass(final ClassNode cNode) { + ClassNode helper = new InnerClassNode( + cNode, + Traits.helperClassName(cNode), + ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_SYNTHETIC, + ClassHelper.OBJECT_TYPE, + ClassNode.EMPTY_ARRAY, + null + ); + cNode.setModifiers(ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT); + + checkInnerClasses(cNode); + + MethodNode initializer = createInitMethod(false, cNode, helper); + MethodNode staticInitializer = createInitMethod(true, cNode, helper); + + // apply the verifier to have the property nodes generated + generatePropertyMethods(cNode); + + // prepare fields + List fields = new ArrayList(); + Set fieldNames = new HashSet(); + for (FieldNode field : cNode.getFields()) { + if (!"metaClass".equals(field.getName()) && (!field.isSynthetic() || field.getName().indexOf('$') < 0)) { + fields.add(field); + fieldNames.add(field.getName()); + } + } + ClassNode fieldHelper = null; + if (!fields.isEmpty()) { + fieldHelper = new InnerClassNode( + cNode, + Traits.fieldHelperClassName(cNode), + ACC_STATIC | ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT, + ClassHelper.OBJECT_TYPE + ); + } + + // add methods + List methods = new ArrayList(cNode.getMethods()); + List nonPublicAPIMethods = new LinkedList(); + for (final MethodNode methodNode : methods) { + boolean declared = methodNode.getDeclaringClass() == cNode; + if (declared) { + if (!methodNode.isSynthetic() && (methodNode.isProtected() || methodNode.getModifiers()==0)) { + unit.addError(new SyntaxException("Cannot have protected/package private method in a trait (" + cNode.getName() + "#" + methodNode.getTypeDescriptor() + ")", + methodNode.getLineNumber(), methodNode.getColumnNumber())); + return null; + } + helper.addMethod(processMethod(cNode, helper, methodNode, fieldHelper, fieldNames)); + if (methodNode.isPrivate() || methodNode.isStatic()) { + nonPublicAPIMethods.add(methodNode); + } + } + } + + // remove methods which should not appear in the trait interface + for (MethodNode privateMethod : nonPublicAPIMethods) { + cNode.removeMethod(privateMethod); + } + // GRECLIPSE add + cNode.putNodeMetaData("trait.methods", nonPublicAPIMethods); + // GRECLIPSE end + + // add fields + for (FieldNode field : fields) { + processField(field, initializer, staticInitializer, fieldHelper, helper, cNode, fieldNames); + } + + // clear properties to avoid generation of methods + cNode.getProperties().clear(); + + // copy annotations + copyClassAnnotations(cNode, helper); + + fields = new ArrayList(cNode.getFields()); // reuse the full list of fields + for (FieldNode field : fields) { + cNode.removeField(field.getName()); + } + // GRECLIPSE add + cNode.putNodeMetaData("trait.fields", fields); + // GRECLIPSE end + + // visit AST xforms + registerASTTransformations(helper); + + unit.getAST().addClass(helper); + if (fieldHelper != null) { + unit.getAST().addClass(fieldHelper); + } + + // resolve scope (for closures) + resolveScope(helper); + if (fieldHelper!=null) { + resolveScope(fieldHelper); + } + return helper; + } + + private static MethodNode createInitMethod(final boolean isStatic, final ClassNode cNode, final ClassNode helper) { + MethodNode initializer = new MethodNode( + isStatic?Traits.STATIC_INIT_METHOD:Traits.INIT_METHOD, + ACC_STATIC | ACC_PUBLIC | ACC_SYNTHETIC, + ClassHelper.VOID_TYPE, + new Parameter[]{createSelfParameter(cNode, isStatic)}, + ClassNode.EMPTY_ARRAY, + new BlockStatement() + ); + helper.addMethod(initializer); + + // Cannot add static compilation of init method because of GROOVY-7217, see example 2 of test case + //AnnotationNode an = new AnnotationNode(TraitComposer.COMPILESTATIC_CLASSNODE); + //initializer.addAnnotation(an); + //cNode.addTransform(StaticCompileTransformation.class, an); + + return initializer; + } + + private void registerASTTransformations(final ClassNode helper) { + ASTTransformationCollectorCodeVisitor collector = new ASTTransformationCollectorCodeVisitor( + unit, compilationUnit.getTransformLoader() + ); + collector.visitClass(helper); + // Perform an additional phase which has to be done *after* type checking + compilationUnit.addPhaseOperation(new CompilationUnit.PrimaryClassNodeOperation() { + @Override + public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) throws CompilationFailedException { + if (classNode==helper) { + PostTypeCheckingExpressionReplacer replacer = new PostTypeCheckingExpressionReplacer(source); + replacer.visitClass(helper); + } + } + }, CompilePhase.INSTRUCTION_SELECTION.getPhaseNumber()); + } + + /** + * Copies annotation from the trait to the helper, excluding the trait annotation itself + * @param cNode the trait class node + * @param helper the helper class node + */ + private static void copyClassAnnotations(final ClassNode cNode, final ClassNode helper) { + List annotations = cNode.getAnnotations(); + for (AnnotationNode annotation : annotations) { + if (!annotation.getClassNode().equals(Traits.TRAIT_CLASSNODE)) { + helper.addAnnotation(annotation); + } + } + } + + private void checkInnerClasses(final ClassNode cNode) { + Iterator it = cNode.getInnerClasses(); + while (it.hasNext()) { + InnerClassNode origin = it.next(); + if ((origin.getModifiers() & ACC_STATIC) == 0) { + unit.addError(new SyntaxException("Cannot have non-static inner class inside a trait ("+origin.getName()+")", origin.getLineNumber(), origin.getColumnNumber())); + } + } + } + + private static void generatePropertyMethods(final ClassNode cNode) { + for (PropertyNode node : cNode.getProperties()) { + processProperty(cNode, node); + } + } + + /** + * Mostly copied from the {@link Verifier} class but does *not* generate bytecode + * + * @param cNode + * @param node + */ + private static void processProperty(final ClassNode cNode, PropertyNode node) { + String name = node.getName(); + FieldNode field = node.getField(); + int propNodeModifiers = node.getModifiers(); + + String getterName = "get" + Verifier.capitalize(name); + String setterName = "set" + Verifier.capitalize(name); + + // GROOVY-3726: clear volatile, transient modifiers so that they don't get applied to methods + if ((propNodeModifiers & Modifier.VOLATILE) != 0) { + propNodeModifiers = propNodeModifiers - Modifier.VOLATILE; + } + if ((propNodeModifiers & Modifier.TRANSIENT) != 0) { + propNodeModifiers = propNodeModifiers - Modifier.TRANSIENT; + } + + Statement getterBlock = node.getGetterBlock(); + if (getterBlock == null) { + MethodNode getter = cNode.getGetterMethod(getterName); + if (getter == null && ClassHelper.boolean_TYPE == node.getType()) { + String secondGetterName = "is" + Verifier.capitalize(name); + getter = cNode.getGetterMethod(secondGetterName); + } + if (!node.isPrivate() && methodNeedsReplacement(cNode, getter)) { + getterBlock = new ExpressionStatement(new FieldExpression(field)); + } + } + Statement setterBlock = node.getSetterBlock(); + if (setterBlock == null) { + // 2nd arg false below: though not usual, allow setter with non-void return type + MethodNode setter = cNode.getSetterMethod(setterName, false); + if (!node.isPrivate() && + (propNodeModifiers & ACC_FINAL) == 0 && + methodNeedsReplacement(cNode, setter)) { + setterBlock = new ExpressionStatement( + new BinaryExpression( + new FieldExpression(field), + Token.newSymbol(Types.EQUAL, 0, 0), + new VariableExpression("value") + ) + ); + } + } + + if (getterBlock != null) { + MethodNode getter = + new MethodNode(getterName, propNodeModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock); + getter.setSynthetic(true); + cNode.addMethod(getter); + + if (ClassHelper.boolean_TYPE == node.getType() || ClassHelper.Boolean_TYPE == node.getType()) { + String secondGetterName = "is" + Verifier.capitalize(name); + MethodNode secondGetter = + new MethodNode(secondGetterName, propNodeModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock); + secondGetter.setSynthetic(true); + cNode.addMethod(secondGetter); + } + } + if (setterBlock != null) { + Parameter[] setterParameterTypes = {new Parameter(node.getType(), "value")}; + VariableExpression var = (VariableExpression) ((BinaryExpression) ((ExpressionStatement) setterBlock).getExpression()).getRightExpression(); + var.setAccessedVariable(setterParameterTypes[0]); + MethodNode setter = + new MethodNode(setterName, propNodeModifiers, ClassHelper.VOID_TYPE, setterParameterTypes, ClassNode.EMPTY_ARRAY, setterBlock); + setter.setSynthetic(true); + cNode.addMethod(setter); + } + } + + private static boolean methodNeedsReplacement(ClassNode classNode, MethodNode m) { + // no method found, we need to replace + if (m == null) return true; + // method is in current class, nothing to be done + if (m.getDeclaringClass() == classNode) return false; + // do not overwrite final + if ((m.getModifiers() & ACC_FINAL) != 0) return false; + return true; + } + + private void processField(final FieldNode field, final MethodNode initializer, final MethodNode staticInitializer, + final ClassNode fieldHelper, final ClassNode helper, final ClassNode trait, + final Set knownFields) { + if (field.isProtected()) { + unit.addError(new SyntaxException("Cannot have protected field in a trait (" + trait.getName() + "#" + field.getName() + ")", + field.getLineNumber(), field.getColumnNumber())); + return; + } + + Expression initialExpression = field.getInitialExpression(); + MethodNode selectedMethod = field.isStatic()?staticInitializer:initializer; + if (initialExpression != null) { + VariableExpression thisObject = new VariableExpression(selectedMethod.getParameters()[0]); + ExpressionStatement initCode = new ExpressionStatement(initialExpression); + processBody(thisObject, initCode, trait, helper, fieldHelper, knownFields); + if (field.isFinal()) { + String baseName = field.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD; + MethodNode fieldInitializer = new MethodNode( + baseName + Traits.remappedFieldName(trait, field.getName()), + ACC_STATIC | ACC_PUBLIC | ACC_SYNTHETIC, + field.getOriginType(), + new Parameter[]{createSelfParameter(trait, field.isStatic())}, + ClassNode.EMPTY_ARRAY, + returnS(initCode.getExpression()) + ); + helper.addMethod(fieldInitializer); + } + BlockStatement code = (BlockStatement) selectedMethod.getCode(); + MethodCallExpression mce; + if (field.isStatic()) { + mce = new MethodCallExpression( + new ClassExpression(INVOKERHELPER_CLASSNODE), + "invokeStaticMethod", + new ArgumentListExpression( + thisObject, + new ConstantExpression(Traits.helperSetterName(field)), + initCode.getExpression() + ) + ); + } else { + mce = new MethodCallExpression( + new CastExpression(createReceiverType(field.isStatic(), fieldHelper), thisObject), + Traits.helperSetterName(field), + new CastExpression(field.getOriginType(),initCode.getExpression()) + ); + } + mce.setImplicitThis(false); + mce.setSourcePosition(initialExpression); + code.addStatement(new ExpressionStatement(mce)); + } + // define setter/getter helper methods (setter added even for final fields for legacy compatibility) + fieldHelper.addMethod( + Traits.helperSetterName(field), + ACC_PUBLIC | ACC_ABSTRACT, + field.getOriginType(), + new Parameter[]{new Parameter(field.getOriginType(), "val")}, + ClassNode.EMPTY_ARRAY, + null + ); + fieldHelper.addMethod( + Traits.helperGetterName(field), + ACC_PUBLIC | ACC_ABSTRACT, + field.getOriginType(), + Parameter.EMPTY_ARRAY, + ClassNode.EMPTY_ARRAY, + null + ); + + // dummy fields are only used to carry annotations if instance field + // and to differentiate from static fields otherwise + int mods = field.getModifiers() & Traits.FIELD_PREFIX_MASK; + String dummyFieldName = String.format("$0x%04x", mods) + Traits.remappedFieldName(field.getOwner(), field.getName()); + FieldNode dummyField = new FieldNode( + dummyFieldName, + ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC, + field.getOriginType(), + fieldHelper, + null + ); + // copy annotations from field to dummy field + List copied = new LinkedList(); + List notCopied = new LinkedList(); + GeneralUtils.copyAnnotatedNodeAnnotations(field, copied, notCopied); + dummyField.addAnnotations(copied); + fieldHelper.addField(dummyField); + + // retain legacy field (will be given lower precedence than above) + dummyFieldName = (field.isStatic() ? Traits.STATIC_FIELD_PREFIX : Traits.FIELD_PREFIX) + + (field.isPublic()? Traits.PUBLIC_FIELD_PREFIX : Traits.PRIVATE_FIELD_PREFIX)+ + Traits.remappedFieldName(field.getOwner(), field.getName()); + dummyField = new FieldNode( + dummyFieldName, + ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC, + field.getOriginType(), + fieldHelper, + null + ); + // copy annotations from field to legacy dummy field + copied = new LinkedList(); + notCopied = new LinkedList(); + GeneralUtils.copyAnnotatedNodeAnnotations(field, copied, notCopied); + dummyField.addAnnotations(copied); + fieldHelper.addField(dummyField); + } + + private MethodNode processMethod(ClassNode traitClass, ClassNode traitHelperClass, MethodNode methodNode, ClassNode fieldHelper, Collection knownFields) { + Parameter[] initialParams = methodNode.getParameters(); + Parameter[] newParams = new Parameter[initialParams.length + 1]; + newParams[0] = createSelfParameter(traitClass, methodNode.isStatic()); + System.arraycopy(initialParams, 0, newParams, 1, initialParams.length); + final int mod = methodNode.isPrivate()?ACC_PRIVATE:ACC_PUBLIC; + MethodNode mNode = new MethodNode( + methodNode.getName(), + mod | ACC_STATIC, + methodNode.getReturnType(), + newParams, + methodNode.getExceptions(), + processBody(new VariableExpression(newParams[0]), methodNode.getCode(), traitClass, traitHelperClass, fieldHelper, knownFields) + ); + mNode.setSourcePosition(methodNode); + mNode.addAnnotations(filterAnnotations(methodNode.getAnnotations())); + mNode.setGenericsTypes(methodNode.getGenericsTypes()); + if (methodNode.isAbstract()) { + mNode.setModifiers(ACC_PUBLIC | ACC_ABSTRACT); + } else { + methodNode.addAnnotation(new AnnotationNode(Traits.IMPLEMENTED_CLASSNODE)); + } + // GRECLIPSE edit + //methodNode.setCode(null); + // GRECLIPSE end + + if (!methodNode.isPrivate() && !methodNode.isStatic()) { + methodNode.setModifiers(ACC_PUBLIC | ACC_ABSTRACT); + } + return mNode; + } + + private static List filterAnnotations(List annotations) { + List result = new ArrayList(annotations.size()); + for (AnnotationNode annotation : annotations) { + if (!OVERRIDE_CLASSNODE.equals(annotation.getClassNode())) { + result.add(annotation); + } + } + + return result; + } + + private static Parameter createSelfParameter(final ClassNode traitClass, boolean isStatic) { + final ClassNode rawType = traitClass.getPlainNodeReference(); + ClassNode type = createReceiverType(isStatic, rawType); + return new Parameter(type, isStatic?Traits.STATIC_THIS_OBJECT:Traits.THIS_OBJECT); + } + + private static ClassNode createReceiverType(final boolean isStatic, final ClassNode rawType) { + ClassNode type; + if (isStatic) { + // Class + type = ClassHelper.CLASS_Type.getPlainNodeReference(); + type.setGenericsTypes(new GenericsType[]{ + new GenericsType(rawType) + }); + } else { + // TraitClass + type = rawType; + } + return type; + } + + private Statement processBody(VariableExpression thisObject, Statement code, ClassNode trait, ClassNode traitHelper, ClassNode fieldHelper, Collection knownFields) { + if (code == null) return null; + NAryOperationRewriter operationRewriter = new NAryOperationRewriter(unit, knownFields); + code.visit(operationRewriter); + SuperCallTraitTransformer superTrn = new SuperCallTraitTransformer(unit); + code.visit(superTrn); + TraitReceiverTransformer trn = new TraitReceiverTransformer(thisObject, unit, trait, traitHelper, fieldHelper, knownFields); + code.visit(trn); + return code; + } + + public void setCompilationUnit(final CompilationUnit unit) { + this.compilationUnit = unit; + } + + private static class DefaultArgsMethodsAdder extends Verifier { + + @Override + public void addDefaultParameterMethods(final ClassNode node) { + setClassNode(node); + super.addDefaultParameterMethods(node); + } + } + + private static class PostTypeCheckingExpressionReplacer extends ClassCodeExpressionTransformer { + private final SourceUnit sourceUnit; + + private PostTypeCheckingExpressionReplacer(final SourceUnit sourceUnit) { + this.sourceUnit = sourceUnit; + } + + @Override + protected SourceUnit getSourceUnit() { + return sourceUnit; + } + + @Override + public Expression transform(final Expression exp) { + if (exp != null) { + Expression replacement = exp.getNodeMetaData(TraitASTTransformation.POST_TYPECHECKING_REPLACEMENT); + if (replacement!=null) { + return replacement; + } + } + return super.transform(exp); + } + } + +} diff --git a/base/org.codehaus.groovy26/src/org/codehaus/groovy/vmplugin/v5/Java5.java b/base/org.codehaus.groovy26/src/org/codehaus/groovy/vmplugin/v5/Java5.java new file mode 100644 index 0000000000..f42b700864 --- /dev/null +++ b/base/org.codehaus.groovy26/src/org/codehaus/groovy/vmplugin/v5/Java5.java @@ -0,0 +1,487 @@ +/* + * 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.vmplugin.v5; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.CompileUnit; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.PackageNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.vmplugin.VMPlugin; +import org.codehaus.groovy.vmplugin.VMPluginFactory; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.MalformedParameterizedTypeException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.List; + +/** + * java 5 based functions + * + * @author Jochen Theodorou + */ +public class Java5 implements VMPlugin { + private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + private static final Class[] PLUGIN_DGM = {PluginDefaultGroovyMethods.class}; + + public void setAdditionalClassInformation(ClassNode cn) { + setGenericsTypes(cn); + } + + private void setGenericsTypes(ClassNode cn) { + TypeVariable[] tvs = cn.getTypeClass().getTypeParameters(); + GenericsType[] gts = configureTypeVariable(tvs); + cn.setGenericsTypes(gts); + } + + private GenericsType[] configureTypeVariable(TypeVariable[] tvs) { + if (tvs.length == 0) return null; + GenericsType[] gts = new GenericsType[tvs.length]; + for (int i = 0; i < tvs.length; i++) { + gts[i] = configureTypeVariableDefinition(tvs[i]); + } + return gts; + } + + private GenericsType configureTypeVariableDefinition(TypeVariable tv) { + return configureTypeVariableDefinition(configureTypeVariableReference(tv.getName()), configureTypes(tv.getBounds())); + } + + public static GenericsType configureTypeVariableDefinition(ClassNode base, ClassNode[] cBounds) { + ClassNode redirect = base.redirect(); + base.setRedirect(null); + GenericsType gt; + if (cBounds == null || cBounds.length == 0) { + gt = new GenericsType(base); + } else { + gt = new GenericsType(base, cBounds, null); + gt.setName(base.getName()); + gt.setPlaceholder(true); + } + base.setRedirect(redirect); + return gt; + } + + private ClassNode[] configureTypes(Type[] types) { + if (types.length == 0) return null; + ClassNode[] nodes = new ClassNode[types.length]; + for (int i = 0; i < types.length; i++) { + nodes[i] = configureType(types[i]); + } + return nodes; + } + + private ClassNode configureType(Type type) { + if (type instanceof WildcardType) { + return configureWildcardType((WildcardType) type); + } else if (type instanceof ParameterizedType) { + return configureParameterizedType((ParameterizedType) type); + } else if (type instanceof GenericArrayType) { + return configureGenericArray((GenericArrayType) type); + } else if (type instanceof TypeVariable) { + return configureTypeVariableReference(((TypeVariable) type).getName()); + } else if (type instanceof Class) { + return configureClass((Class) type); + } else if (type==null) { + throw new GroovyBugError("Type is null. Most probably you let a transform reuse existing ClassNodes with generics information, that is now used in a wrong context."); + } else { + throw new GroovyBugError("unknown type: " + type + " := " + type.getClass()); + } + } + + private static ClassNode configureClass(Class c) { + if (c.isPrimitive()) { + return ClassHelper.make(c); + } else { + return ClassHelper.makeWithoutCaching(c, false); + } + } + + private ClassNode configureGenericArray(GenericArrayType genericArrayType) { + Type component = genericArrayType.getGenericComponentType(); + ClassNode node = configureType(component); + return node.makeArray(); + } + + private ClassNode configureWildcardType(WildcardType wildcardType) { + ClassNode base = ClassHelper.makeWithoutCaching("?"); + base.setRedirect(ClassHelper.OBJECT_TYPE); + //TODO: more than one lower bound for wildcards? + ClassNode[] lowers = configureTypes(wildcardType.getLowerBounds()); + ClassNode lower = null; + // TODO: is it safe to remove this? What was the original intention? + if (lowers != null) lower = lowers[0]; + + // GRECLIPSE edit -- want like JDT does, not + //ClassNode[] upper = configureTypes(wildcardType.getUpperBounds()); + ClassNode[] upper = wildcardType.toString().equals("?") ? null : configureTypes(wildcardType.getUpperBounds()); + // GRECLIPSE end + GenericsType t = new GenericsType(base, upper, lower); + t.setWildcard(true); + + ClassNode ref = ClassHelper.makeWithoutCaching(Object.class, false); + ref.setGenericsTypes(new GenericsType[]{t}); + + return ref; + } + + private ClassNode configureParameterizedType(ParameterizedType parameterizedType) { + ClassNode base = configureType(parameterizedType.getRawType()); + GenericsType[] gts = configureTypeArguments(parameterizedType.getActualTypeArguments()); + base.setGenericsTypes(gts); + return base; + } + + public static ClassNode configureTypeVariableReference(String name) { + ClassNode cn = ClassHelper.makeWithoutCaching(name); + cn.setGenericsPlaceHolder(true); + ClassNode cn2 = ClassHelper.makeWithoutCaching(name); + cn2.setGenericsPlaceHolder(true); + GenericsType[] gts = new GenericsType[]{new GenericsType(cn2)}; + cn.setGenericsTypes(gts); + cn.setRedirect(ClassHelper.OBJECT_TYPE); + return cn; + } + + private GenericsType[] configureTypeArguments(Type[] ta) { + if (ta.length == 0) return null; + GenericsType[] gts = new GenericsType[ta.length]; + for (int i = 0; i < ta.length; i++) { + ClassNode t = configureType(ta[i]); + if (ta[i] instanceof WildcardType) { + GenericsType[] gen = t.getGenericsTypes(); + gts[i] = gen[0]; + } else { + gts[i] = new GenericsType(t); + } + } + return gts; + } + + public Class[] getPluginDefaultGroovyMethods() { + return PLUGIN_DGM; + } + + public Class[] getPluginStaticGroovyMethods() { + return EMPTY_CLASS_ARRAY; + } + + private void setAnnotationMetaData(Annotation[] annotations, AnnotatedNode an) { + for (Annotation annotation : annotations) { + AnnotationNode node = new AnnotationNode(ClassHelper.make(annotation.annotationType())); + configureAnnotation(node, annotation); + an.addAnnotation(node); + } + } + + @Deprecated + public static void configureAnnotationFromDefinition(AnnotationNode definition, AnnotationNode root) { + VMPluginFactory.getPlugin().configureAnnotationNodeFromDefinition(definition, root); + } + + public void configureAnnotationNodeFromDefinition(AnnotationNode definition, AnnotationNode root) { + ClassNode type = definition.getClassNode(); + if ("java.lang.annotation.Retention".equals(type.getName())) { + Expression exp = definition.getMember("value"); + if (!(exp instanceof PropertyExpression)) return; + PropertyExpression pe = (PropertyExpression) exp; + String name = pe.getPropertyAsString(); + RetentionPolicy policy = RetentionPolicy.valueOf(name); + setRetentionPolicy(policy, root); + } else if ("java.lang.annotation.Target".equals(type.getName())) { + Expression exp = definition.getMember("value"); + if (!(exp instanceof ListExpression)) return; + ListExpression le = (ListExpression) exp; + int bitmap = 0; + for (Expression e : le.getExpressions()) { + if (!(e instanceof PropertyExpression)) return; + PropertyExpression element = (PropertyExpression) e; + String name = element.getPropertyAsString(); + ElementType value = ElementType.valueOf(name); + bitmap |= getElementCode(value); + } + root.setAllowedTargets(bitmap); + } + } + + public void configureAnnotation(AnnotationNode node) { + ClassNode type = node.getClassNode(); + List annotations = type.getAnnotations(); + for (AnnotationNode an : annotations) { + configureAnnotationFromDefinition(an, node); + } + configureAnnotationFromDefinition(node, node); + } + + private void configureAnnotation(AnnotationNode node, Annotation annotation) { + Class type = annotation.annotationType(); + if (type == Retention.class) { + Retention r = (Retention) annotation; + RetentionPolicy value = r.value(); + setRetentionPolicy(value, node); + node.setMember("value", new PropertyExpression( + new ClassExpression(ClassHelper.makeWithoutCaching(RetentionPolicy.class, false)), + value.toString())); + } else if (type == Target.class) { + Target t = (Target) annotation; + ElementType[] elements = t.value(); + ListExpression elementExprs = new ListExpression(); + for (ElementType element : elements) { + elementExprs.addExpression(new PropertyExpression( + new ClassExpression(ClassHelper.ELEMENT_TYPE_TYPE), element.name())); + } + node.setMember("value", elementExprs); + } else { + Method[] declaredMethods; + try { + declaredMethods = type.getDeclaredMethods(); + } catch (SecurityException se) { + declaredMethods = new Method[0]; + } + for (Method declaredMethod : declaredMethods) { + try { + Object value = declaredMethod.invoke(annotation); + Expression valueExpression = annotationValueToExpression(value); + if (valueExpression == null) + continue; + node.setMember(declaredMethod.getName(), valueExpression); + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + } + } + } + + private Expression annotationValueToExpression (Object value) { + if (value == null || value instanceof String || value instanceof Number || value instanceof Character || value instanceof Boolean) + return new ConstantExpression(value); + + if (value instanceof Class) + return new ClassExpression(ClassHelper.makeWithoutCaching((Class)value)); + + if (value.getClass().isArray()) { + ListExpression elementExprs = new ListExpression(); + int len = Array.getLength(value); + for (int i = 0; i != len; ++i) + elementExprs.addExpression(annotationValueToExpression(Array.get(value, i))); + return elementExprs; + } + + return null; + } + + private static void setRetentionPolicy(RetentionPolicy value, AnnotationNode node) { + switch (value) { + case RUNTIME: + node.setRuntimeRetention(true); + break; + case SOURCE: + node.setSourceRetention(true); + break; + case CLASS: + node.setClassRetention(true); + break; + default: + throw new GroovyBugError("unsupported Retention " + value); + } + } + + protected int getElementCode(ElementType value) { + switch (value) { + case TYPE: + return AnnotationNode.TYPE_TARGET; + case CONSTRUCTOR: + return AnnotationNode.CONSTRUCTOR_TARGET; + case METHOD: + return AnnotationNode.METHOD_TARGET; + case FIELD: + return AnnotationNode.FIELD_TARGET; + case PARAMETER: + return AnnotationNode.PARAMETER_TARGET; + case LOCAL_VARIABLE: + return AnnotationNode.LOCAL_VARIABLE_TARGET; + case ANNOTATION_TYPE: + return AnnotationNode.ANNOTATION_TARGET; + case PACKAGE: + return AnnotationNode.PACKAGE_TARGET; + } + if ("MODULE".equals(value.name())) { + return AnnotationNode.TYPE_TARGET; + } else { + throw new GroovyBugError("unsupported Target " + value); + } + } + + private static void setMethodDefaultValue(MethodNode mn, Method m) { + Object defaultValue = m.getDefaultValue(); + ConstantExpression cExp = ConstantExpression.NULL; + if (defaultValue!=null) cExp = new ConstantExpression(defaultValue); + mn.setCode(new ReturnStatement(cExp)); + mn.setAnnotationDefault(true); + } + + public void configureClassNode(CompileUnit compileUnit, ClassNode classNode) { + try { + Class clazz = classNode.getTypeClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field f : fields) { + ClassNode ret = makeClassNode(compileUnit, f.getGenericType(), f.getType()); + FieldNode fn = new FieldNode(f.getName(), f.getModifiers(), ret, classNode, null); + setAnnotationMetaData(f.getAnnotations(), fn); + classNode.addField(fn); + } + Method[] methods = clazz.getDeclaredMethods(); + for (Method m : methods) { + ClassNode ret = makeClassNode(compileUnit, m.getGenericReturnType(), m.getReturnType()); + Parameter[] params = makeParameters(compileUnit, m.getGenericParameterTypes(), m.getParameterTypes(), m.getParameterAnnotations()); + ClassNode[] exceptions = makeClassNodes(compileUnit, m.getGenericExceptionTypes(), m.getExceptionTypes()); + MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ret, params, exceptions, null); + mn.setSynthetic(m.isSynthetic()); + setMethodDefaultValue(mn, m); + setAnnotationMetaData(m.getAnnotations(), mn); + mn.setGenericsTypes(configureTypeVariable(m.getTypeParameters())); + classNode.addMethod(mn); + } + Constructor[] constructors = clazz.getDeclaredConstructors(); + for (Constructor ctor : constructors) { + Parameter[] params = makeParameters(compileUnit, ctor.getGenericParameterTypes(), ctor.getParameterTypes(), ctor.getParameterAnnotations()); + ClassNode[] exceptions = makeClassNodes(compileUnit, ctor.getGenericExceptionTypes(), ctor.getExceptionTypes()); + classNode.addConstructor(ctor.getModifiers(), params, exceptions, null); + } + + Class sc = clazz.getSuperclass(); + if (sc != null) classNode.setUnresolvedSuperClass(makeClassNode(compileUnit, clazz.getGenericSuperclass(), sc)); + makeInterfaceTypes(compileUnit, classNode, clazz); + setAnnotationMetaData(classNode.getTypeClass().getAnnotations(), classNode); + + PackageNode packageNode = classNode.getPackage(); + if (packageNode != null) { + setAnnotationMetaData(classNode.getTypeClass().getPackage().getAnnotations(), packageNode); + } + } catch (NoClassDefFoundError e) { + throw new NoClassDefFoundError("Unable to load class "+classNode.toString(false)+" due to missing dependency "+e.getMessage()); + } catch (MalformedParameterizedTypeException e) { + throw new RuntimeException("Unable to configure class node for class "+classNode.toString(false)+" due to malformed parameterized types", e); + } + } + + private void makeInterfaceTypes(CompileUnit cu, ClassNode classNode, Class clazz) { + Type[] interfaceTypes = clazz.getGenericInterfaces(); + if (interfaceTypes.length == 0) { + classNode.setInterfaces(ClassNode.EMPTY_ARRAY); + } else { + ClassNode[] ret = new ClassNode[interfaceTypes.length]; + for (int i = 0; i < interfaceTypes.length; i++) { + Type type = interfaceTypes[i]; + while (!(type instanceof Class)) { + ParameterizedType pt = (ParameterizedType) type; + Type t2 = pt.getRawType(); + if (t2==type) { + throw new GroovyBugError("Cannot transform generic signature of "+clazz+ + " with generic interface "+interfaceTypes[i]+" to a class."); + } + type = t2; + } + ret[i] = makeClassNode(cu, interfaceTypes[i], (Class) type); + } + classNode.setInterfaces(ret); + } + } + + private ClassNode[] makeClassNodes(CompileUnit cu, Type[] types, Class[] cls) { + ClassNode[] nodes = new ClassNode[types.length]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = makeClassNode(cu, types[i], cls[i]); + } + return nodes; + } + + private ClassNode makeClassNode(CompileUnit cu, Type t, Class c) { + ClassNode back = null; + if (cu != null) back = cu.getClass(c.getName()); + if (back == null) back = ClassHelper.make(c); + if (!(t instanceof Class)) { + ClassNode front = configureType(t); + front.setRedirect(back); + return front; + } + return back.getPlainNodeReference(); + } + + private Parameter[] makeParameters(CompileUnit cu, Type[] types, Class[] cls, Annotation[][] parameterAnnotations) { + Parameter[] params = Parameter.EMPTY_ARRAY; + if (types.length > 0) { + params = new Parameter[types.length]; + for (int i = 0; i < params.length; i++) { + params[i] = makeParameter(cu, types[i], cls[i], parameterAnnotations[i], i); + } + } + return params; + } + + private Parameter makeParameter(CompileUnit cu, Type type, Class cl, Annotation[] annotations, int idx) { + ClassNode cn = makeClassNode(cu, type, cl); + Parameter parameter = new Parameter(cn, "param" + idx); + setAnnotationMetaData(annotations, parameter); + return parameter; + } + + public void invalidateCallSites() {} + + @Override + public Object getInvokeSpecialHandle(Method m, Object receiver){ + throw new GroovyBugError("getInvokeSpecialHandle requires at least JDK 7 wot private access to Lookup"); + } + + @Override + public int getVersion() { + return 5; + } + + @Override + public Object invokeHandle(Object handle, Object[] args) throws Throwable { + throw new GroovyBugError("invokeHandle requires at least JDK 7"); + } +} + diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java index 351c243754..4d6b67d222 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,9 @@ */ package org.codehaus.jdt.groovy.internal.compiler.ast; +import static java.beans.Introspector.decapitalize; + +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -35,6 +38,7 @@ import org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration.FieldDeclarationWithInitializer; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.groovy.search.AccessorSupport; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.impl.BooleanConstant; @@ -87,8 +91,7 @@ public class JDTClassNode extends ClassNode implements JDTNode { //-------------------------------------------------------------------------- /** Configuration flags */ - private int bits; - + private volatile int bits; private boolean beingInitialized; private boolean anyGenericsInitialized; @@ -169,46 +172,11 @@ public void lazyClassInit() { } } - public void setUpGenerics() { - if (anyGenericsInitialized) { - return; - } - try { - if (jdtBinding instanceof RawTypeBinding) { - // nothing to do - } else if (jdtBinding instanceof ParameterizedTypeBinding) { - GenericsType[] gts = new JDTClassNodeBuilder(this.resolver).configureTypeArguments(((ParameterizedTypeBinding) jdtBinding).arguments); - setGenericsTypes(gts); - } else { - // SourceTB, BinaryTB, TypeVariableB, WildcardB - TypeVariableBinding[] typeVariables = jdtBinding.typeVariables(); - GenericsType[] generics = new JDTClassNodeBuilder(this.resolver).configureTypeVariables(typeVariables); - if (generics != null) { - this.setGenericsTypes(generics); - } - } - } finally { - anyGenericsInitialized = true; - } - } - - // JDTClassNodes are created because of a JDT Reference Binding file so are always 'resolved' (although not initialized on creation) - @Override - public boolean isResolved() { - return true; - } - - @Override - public void setGenericsTypes(GenericsType[] genericsTypes) { - this.anyGenericsInitialized = true; - super.setGenericsTypes(genericsTypes); - } - /** * Basic initialization of the node - try and do most resolution lazily but some elements are worth getting correct up front: superclass, superinterfaces */ // FIXASC confusing (and problematic?) that the superclass is setup after the generics information - void initialize() { + private void initialize() { if (beingInitialized) { return; } @@ -341,11 +309,6 @@ private void initializeMembers() { } } - @Override - public boolean mightHaveInners() { - return (jdtBinding.memberTypes().length != 0); - } - /** * Convert a JDT MethodBinding to a Groovy MethodNode */ @@ -434,24 +397,6 @@ private ClassNode makeClassNode(TypeBinding t, TypeBinding c) { return back; } - @Override - public GenericsType[] getGenericsTypes() { - ensureGenericsInitialized(); - return genericsTypes; - } - - @Override - public boolean isUsingGenerics() { - ensureGenericsInitialized(); - return super.isUsingGenerics(); - } - - private void ensureGenericsInitialized() { - if (!anyGenericsInitialized) { - setUpGenerics(); - } - } - private ConstructorNode constructorBindingToConstructorNode(MethodBinding methodBinding) { int modifiers = methodBinding.modifiers; Parameter[] parameters = makeParameters(methodBinding.parameters); @@ -511,182 +456,130 @@ private FieldNode fieldBindingToFieldNode(FieldBinding fieldBinding, TypeDeclara return fNode; } - @Override - public boolean isReallyResolved() { - return true; - } + //-------------------------------------------------------------------------- @Override public String getClassInternalName() { return getName().replace('.', '/'); } - @Override - public boolean isPrimitive() { - // FIXASC (M3) verify always true. Think it is a jdtReferenceBinding is a - // reference binding and not a typebinding - return false; - } - - /** - * Annotations on a JDTClassNode are initialized lazily when requested. - */ @Override public List getAnnotations() { - ensureAnnotationsInitialized(); - return super.getAnnotations(); - } - - @Override - public List getAnnotations(ClassNode type) { - ensureAnnotationsInitialized(); - return super.getAnnotations(type); - } - - private synchronized void ensureAnnotationsInitialized() { if ((bits & ANNOTATIONS_INITIALIZED) == 0) { - if ((jdtBinding instanceof SourceTypeBinding)) { - // ensure resolved - ((SourceTypeBinding) jdtBinding).getAnnotationTagBits(); - } - AnnotationBinding[] annotationBindings = jdtBinding.getAnnotations(); - for (AnnotationBinding annotationBinding : annotationBindings) { - addAnnotation(new JDTAnnotationNode(annotationBinding, this.resolver)); - } - bits |= ANNOTATIONS_INITIALIZED; - } - } - - @Override - protected void ensurePropertiesInitialized() { - if ((bits & PROPERTIES_INITIALIZED) == 0) { - initializeProperties(); - } - } - - protected synchronized void initializeProperties() { - if ((bits & PROPERTIES_INITIALIZED) == 0) { - lazyClassInit(); - // getX methods - // make it behave like groovy - no property nodes unless it is groovy source - if (groovyTypeDecl != null) { - Set existing = new HashSet<>(); - for (MethodNode methodNode : getMethods()) { - if (isGetter(methodNode)) { - // STS-2628 be careful not to double-add properties if there is a getter and an isser variant - String propertyName = convertToPropertyName(methodNode.getName()); - if (!existing.contains(propertyName)) { - existing.add(propertyName); - // Adding a real field for these accessors can trip up CompileStatic which - // will attempt to access it as a real field - super.addPropertyWithoutField(createPropertyNodeForMethodNode(methodNode, propertyName)); - // super.addProperty(createPropertyNodeForMethodNode(methodNode, propertyName)); - } + synchronized (this) { + if ((bits & ANNOTATIONS_INITIALIZED) == 0) { + if (jdtBinding instanceof SourceTypeBinding) { + @SuppressWarnings("unused") // ensure resolved + long tagBits = ((SourceTypeBinding) jdtBinding).getAnnotationTagBits(); } + for (AnnotationBinding annotationBinding : jdtBinding.getAnnotations()) { + addAnnotation(new JDTAnnotationNode(annotationBinding, resolver)); + } + bits |= ANNOTATIONS_INITIALIZED; } - // fields - FIXASC nyi for fields - // for (FieldNode fieldNode : getFields()) { - // super.addProperty(createPropertyNodeFromFieldNode(fieldNode)); - // } } - bits |= PROPERTIES_INITIALIZED; } + return Collections.unmodifiableList(super.getAnnotations()); } - private PropertyNode createPropertyNodeForMethodNode(MethodNode methodNode, String propertyName) { - ClassNode propertyType = methodNode.getReturnType(); - - int mods = methodNode.getModifiers(); - FieldNode field = this.getField(propertyName); - if (field == null) { - field = new FieldNode(propertyName, mods, propertyType, this, null); - field.setDeclaringClass(this); - } else { - // field already exists - // must remove this field since when "addProperty" is called - // later on, it will add it again. We do not want dups. - this.removeField(propertyName); + @Override + public List getAnnotations(ClassNode type) { + if ((bits & ANNOTATIONS_INITIALIZED) == 0) { + @SuppressWarnings("unused") // ensure initialized + List annotations = getAnnotations(); } - PropertyNode property = new PropertyNode(field, mods, null, null); - property.setDeclaringClass(this); - return property; + return Collections.unmodifiableList(super.getAnnotations(type)); } - /** - * Converts from a method get/set/is name to a property name. - * Assumes that methodName is more than 4/3 characters long and starts with a proper prefix. - */ - private String convertToPropertyName(String methodName) { - StringBuilder propertyName = new StringBuilder(); - int prefixLen; - if (methodName.startsWith("is")) { - prefixLen = 2; - } else { - prefixLen = 3; - } - propertyName.append(Character.toLowerCase(methodName.charAt(prefixLen))); - if (methodName.length() > prefixLen + 1) { - propertyName.append(methodName.substring(prefixLen + 1)); + @Override + public GenericsType[] getGenericsTypes() { + if (!anyGenericsInitialized) { + setUpGenerics(); } - String name = propertyName.toString(); - return name; - } - - /** - * @return {@code true} if the methodNode looks like a setter method for a property: - * method starting set with a void return type and taking one parameter - */ - @SuppressWarnings("unused") - private boolean isSetter(MethodNode methodNode) { - return methodNode.getReturnType() == ClassHelper.VOID_TYPE && - methodNode.getParameters().length == 1 && - methodNode.getName().startsWith("set") && - methodNode.getName().length() > 3; - } - - /** - * @return {@code true} if the methodNode looks like a getter method for a property: - * method starting get with a non void return type and taking no parameters - */ - private boolean isGetter(MethodNode methodNode) { - return methodNode.getReturnType() != ClassHelper.VOID_TYPE && - methodNode.getParameters().length == 0 && - ((methodNode.getName().startsWith("get") && methodNode.getName().length() > 3) || - (methodNode.getName().startsWith("is") && methodNode.getName().length() > 2)); + return super.getGenericsTypes(); } @Override - public List getProperties() { - ensurePropertiesInitialized(); - return super.getProperties(); + public boolean isUsingGenerics() { + if (!anyGenericsInitialized) { + setUpGenerics(); + } + return super.isUsingGenerics(); } @Override - public PropertyNode getProperty(String name) { - ensurePropertiesInitialized(); - return super.getProperty(name); + public void setGenericsTypes(GenericsType[] genericsTypes) { + anyGenericsInitialized = true; + super.setGenericsTypes(genericsTypes); } - @Override - public boolean hasProperty(String name) { - ensurePropertiesInitialized(); - return super.hasProperty(name); + void setUpGenerics() { + if (!anyGenericsInitialized) + try { + if (jdtBinding instanceof RawTypeBinding) { + // nothing to do + } else if (jdtBinding instanceof ParameterizedTypeBinding) { + GenericsType[] gts = new JDTClassNodeBuilder(resolver).configureTypeArguments(((ParameterizedTypeBinding) jdtBinding).arguments); + setGenericsTypes(gts); + } else { + // SourceTB, BinaryTB, TypeVariableB, WildcardB + TypeVariableBinding[] typeVariables = jdtBinding.typeVariables(); + GenericsType[] generics = new JDTClassNodeBuilder(resolver).configureTypeVariables(typeVariables); + if (generics != null) { + setGenericsTypes(generics); + } + } + } finally { + anyGenericsInitialized = true; + } } @Override public void addProperty(PropertyNode node) { - new RuntimeException("JDTClassNode is immutable, should not be called to add property: " + node.getName()).printStackTrace(); + throw new UnsupportedOperationException("JDTClassNode is immutable, should not be called to add property: " + node.getName()); } @Override public PropertyNode addProperty(String name, int modifiers, ClassNode type, Expression initialValueExpression, Statement getterBlock, Statement setterBlock) { - new RuntimeException("JDTClassNode is immutable, should not be called to add property: " + name).printStackTrace(); - return null; + throw new UnsupportedOperationException("JDTClassNode is immutable, should not be called to add property: " + name); } @Override - public boolean isDeprecated() { - return jdtBinding.isDeprecated(); + public List getProperties() { + if ((bits & PROPERTIES_INITIALIZED) == 0) { + synchronized (this) { + if ((bits & PROPERTIES_INITIALIZED) == 0) { + lazyClassInit(); + if (groovyTypeDecl != null) { + Set names = new HashSet<>(); + List nodes = super.getProperties(); + getMethods().stream().filter(AccessorSupport::isGetter).forEach(methodNode -> { + String methodName = methodNode.getName(); + String propertyName = decapitalize(methodName.substring(methodName.startsWith("is") ? 2 : 3)); + + // STS-2628: don't double-add properties if there is a getter and an isser variant + if (names.add(propertyName)) { + FieldNode field = getField(propertyName); + boolean synth = (field == null); + if (synth) { + field = new FieldNode(propertyName, methodNode.getModifiers(), methodNode.getReturnType(), this, null); + field.setDeclaringClass(this); + field.setSynthetic(true); + } + PropertyNode property = new PropertyNode(field, methodNode.getModifiers(), null, null); + property.setDeclaringClass(this); + property.setSynthetic(synth); + + nodes.add(property); + } + }); + } + bits |= PROPERTIES_INITIALIZED; + } + } + } + + return Collections.unmodifiableList(super.getProperties()); } /** @@ -702,9 +595,9 @@ public Class getTypeClass() { } ClassLoader transformLoader = resolver.compilationUnit.getTransformLoader(); if (transformLoader != null) { - // What about array types + // TODO: What about array types? try { - clazz = Class.forName(this.getName(), false, transformLoader); + clazz = Class.forName(getName(), false, transformLoader); return clazz; } catch (ClassNotFoundException e) { unfindable = true; @@ -712,4 +605,29 @@ public Class getTypeClass() { } throw new GroovyBugError("JDTClassNode.getTypeClass() cannot locate class for " + getName() + " using transform loader " + transformLoader); } + + @Override + public boolean isDeprecated() { + return jdtBinding.isDeprecated(); + } + + @Override + public boolean isPrimitive() { + return false; // FIXASC (M3) verify always true. Think it is a jdtReferenceBinding is a reference binding and not a typebinding + } + + @Override + public boolean isResolved() { + return true; // JDTClassNodes are created because of a JDT Reference Binding file so are always 'resolved' (although not initialized upon creation) + } + + @Override + public boolean isReallyResolved() { + return true; + } + + @Override + public boolean mightHaveInners() { + return (jdtBinding.memberTypes().length != 0); + } } diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/DepthFirstVisitor.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/DepthFirstVisitor.java index da24cba285..7861c2d553 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/DepthFirstVisitor.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/DepthFirstVisitor.java @@ -59,6 +59,7 @@ import org.codehaus.groovy.ast.expr.ExpressionTransformer; import org.codehaus.groovy.ast.expr.FieldExpression; import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.LambdaExpression; import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapEntryExpression; import org.codehaus.groovy.ast.expr.MapExpression; @@ -480,6 +481,10 @@ public void visitGStringExpression(GStringExpression expression) { visitExpression(expression); } + public void visitLambdaExpression(LambdaExpression expression) { + visitClosureExpression(expression); + } + @Override public void visitListExpression(ListExpression expression) { visitAnnotations(expression.getAnnotations()); diff --git a/extras/groovy-eclipse-batch-builder/build.properties b/extras/groovy-eclipse-batch-builder/build.properties index 90c29704ef..59ab6fd49b 100644 --- a/extras/groovy-eclipse-batch-builder/build.properties +++ b/extras/groovy-eclipse-batch-builder/build.properties @@ -1,6 +1,7 @@ # version numbers version2.4=2.4.13-02 version2.5=2.5.0-01 +version2.6=2.6.0-01 # uncomment to do a particular build -- only one should be uncommented at a time do-24-build=true diff --git a/extras/groovy-eclipse-batch-builder/build.xml b/extras/groovy-eclipse-batch-builder/build.xml index af48a7755b..c0cf5e94a5 100644 --- a/extras/groovy-eclipse-batch-builder/build.xml +++ b/extras/groovy-eclipse-batch-builder/build.xml @@ -155,6 +155,13 @@ + + + + + + + @@ -162,6 +169,7 @@ +
@@ -169,7 +177,7 @@ - + diff --git a/groovy-eclipse.setup b/groovy-eclipse.setup index 1212bc5756..c3f453de2d 100644 --- a/groovy-eclipse.setup +++ b/groovy-eclipse.setup @@ -130,6 +130,8 @@ name="org.codehaus.groovy24.feature.feature.group"/> + + url="http://download.eclipse.org/eclipse/updates/4.8milestones"/> diff --git a/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/SanityTests.groovy b/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/SanityTests.groovy index df0aa6d80d..34816b6e61 100644 --- a/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/SanityTests.groovy +++ b/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/SanityTests.groovy @@ -53,7 +53,7 @@ final class SanityTests { int major = groovyVersion.major, minor = groovyVersion.minor - assert "${major}.${minor}" == '2.5' + assert "${major}.${minor}" == '2.6' } @Test diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy index 34afcde18a..4ac32f8839 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy @@ -15,8 +15,6 @@ */ package org.codehaus.groovy.eclipse.codeassist.tests -import groovy.transform.NotYetImplemented - import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist import org.eclipse.jdt.ui.PreferenceConstants import org.eclipse.jface.preference.IPreferenceStore @@ -339,27 +337,45 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite { checkUniqueProposal((SCRIPTCONTENTS - ~/\s*$/) + '.', 'new Foo().', 'method3', 'method3(arg: arg, c1: { it }) { it }') } - @NotYetImplemented @Test // GRECLIPSE-268 + @Test void testGString1() { ICompletionProposal[] proposals = createProposalsAtOffset('""""""', 3) assert proposals.length == 0 : 'Should not have found any proposals, but found:\n' + printProposals(proposals) } - @Test // GRECLIPSE-268 + @Test void testGString2() { - ICompletionProposal[] proposals = createProposalsAtOffset('"""${this}"""', 3) + ICompletionProposal[] proposals = createProposalsAtOffset('"""${Groo}"""', 3) assert proposals.length == 0 : 'Should not have found any proposals, but found:\n' + printProposals(proposals) } - @Test // GRECLIPSE-268 + @Test void testGString3() { - ICompletionProposal[] proposals = createProposalsAtOffset('"""this"""', '"""this'.length()) - assert proposals.length == 0 : 'Should not have found any proposals, but found:\n' + printProposals(proposals) + ICompletionProposal[] proposals = createProposalsAtOffset('"""${Groo}"""', '"""${Groo'.length()) + assert proposals.length > 0 : 'Should not have found any proposals, but found:\n' + printProposals(proposals) } - @Test // GRECLIPSE-268 + @Test void testGString4() { - String contents = 'def flarb;\n"""${flarb}"""' - checkUniqueProposal(contents, '${flarb', 'flarb') + ICompletionProposal[] proposals = createProposalsAtOffset('"""Groo"""', '"""Groo'.length()) + assert proposals.length == 0 : 'Should not have found any proposals, but found:\n' + printProposals(proposals) + } + + @Test + void testGString5() { + String contents = '''\ + def flarb + """$flar""" + '''.stripIndent() + checkUniqueProposal(contents, '$flar', 'flarb') + } + + @Test + void testGString6() { + String contents = '''\ + def flarb + """${flar}""" + '''.stripIndent() + checkUniqueProposal(contents, '${flar', 'flarb') } } diff --git a/ide/Feature-org.codehaus.groovy26.feature/.project b/ide/Feature-org.codehaus.groovy26.feature/.project new file mode 100644 index 0000000000..7c554f46f7 --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/.project @@ -0,0 +1,17 @@ + + + Feature-org.codehaus.groovy26.feature + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/ide/Feature-org.codehaus.groovy26.feature/build.properties b/ide/Feature-org.codehaus.groovy26.feature/build.properties new file mode 100644 index 0000000000..8003cbc57e --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/build.properties @@ -0,0 +1,3 @@ +bin.includes = feature.properties,\ + feature.xml,\ + *.html diff --git a/ide/Feature-org.codehaus.groovy26.feature/epl-v10.html b/ide/Feature-org.codehaus.groovy26.feature/epl-v10.html new file mode 100644 index 0000000000..483f9a9ebd --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/epl-v10.html @@ -0,0 +1,319 @@ + + + + + + + +Eclipse Public License - Version 1.0 + + + + +
+ +

Eclipse Public License - v 1.0 +

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER +THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, +REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE +OF THIS AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) +in the case of the initial Contributor, the initial code and documentation +distributed under this Agreement, and
+b) in the case of each subsequent Contributor:

+ +

i) +changes to the Program, and

+ +

ii) +additions to the Program;

+ +

where +such changes and/or additions to the Program originate from and are distributed +by that particular Contributor. A Contribution 'originates' from a Contributor +if it was added to the Program by such Contributor itself or anyone acting on +such Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in conjunction +with the Program under their own license agreement, and (ii) are not derivative +works of the Program.

+ +

"Contributor" means any person or +entity that distributes the Program.

+ +

"Licensed Patents " mean patent +claims licensable by a Contributor which are necessarily infringed by the use +or sale of its Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions +distributed in accordance with this Agreement.

+ +

"Recipient" means anyone who +receives the Program under this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) +Subject to the terms of this Agreement, each Contributor hereby grants Recipient +a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly +display, publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and object code +form.

+ +

b) +Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free +patent license under Licensed Patents to make, use, sell, offer to sell, import +and otherwise transfer the Contribution of such Contributor, if any, in source +code and object code form. This patent license shall apply to the combination +of the Contribution and the Program if, at the time the Contribution is added +by the Contributor, such addition of the Contribution causes such combination +to be covered by the Licensed Patents. The patent license shall not apply to +any other combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) +Recipient understands that although each Contributor grants the licenses to its +Contributions set forth herein, no assurances are provided by any Contributor +that the Program does not infringe the patent or other intellectual property +rights of any other entity. Each Contributor disclaims any liability to Recipient +for claims brought by any other entity based on infringement of intellectual +property rights or otherwise. As a condition to exercising the rights and +licenses granted hereunder, each Recipient hereby assumes sole responsibility +to secure any other intellectual property rights needed, if any. For example, +if a third party patent license is required to allow Recipient to distribute +the Program, it is Recipient's responsibility to acquire that license before +distributing the Program.

+ +

d) +Each Contributor represents that to its knowledge it has sufficient copyright +rights in its Contribution, if any, to grant the copyright license set forth in +this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the +Program in object code form under its own license agreement, provided that: +

+ +

a) +it complies with the terms and conditions of this Agreement; and

+ +

b) +its license agreement:

+ +

i) +effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose;

+ +

ii) +effectively excludes on behalf of all Contributors all liability for damages, +including direct, indirect, special, incidental and consequential damages, such +as lost profits;

+ +

iii) +states that any provisions which differ from this Agreement are offered by that +Contributor alone and not by any other party; and

+ +

iv) +states that source code for the Program is available from such Contributor, and +informs licensees how to obtain it in a reasonable manner on or through a +medium customarily used for software exchange.

+ +

When the Program is made available in source +code form:

+ +

a) +it must be made available under this Agreement; and

+ +

b) a +copy of this Agreement must be included with each copy of the Program.

+ +

Contributors may not remove or alter any +copyright notices contained within the Program.

+ +

Each Contributor must identify itself as the +originator of its Contribution, if any, in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may +accept certain responsibilities with respect to end users, business partners +and the like. While this license is intended to facilitate the commercial use +of the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes the +Program in a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified Contributor to +the extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may participate +in any such claim at its own expense.

+ +

For example, a Contributor might include the +Program in a commercial product offering, Product X. That Contributor is then a +Commercial Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance claims and +warranties are such Commercial Contributor's responsibility alone. Under this +section, the Commercial Contributor would have to defend claims against the +other Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, +WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and distributing the +Program and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs or +equipment, and unavailability or interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS +AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF +THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid +or unenforceable under applicable law, it shall not affect the validity or +enforceability of the remainder of the terms of this Agreement, and without +further action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Program itself (excluding combinations of the Program with +other software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the date +such litigation is filed.

+ +

All Recipient's rights under this Agreement +shall terminate if it fails to comply with any of the material terms or +conditions of this Agreement and does not cure such failure in a reasonable +period of time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute +copies of this Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The Agreement +Steward reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the initial +Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved.

+ +

This Agreement is governed by the laws of the +State of New York and the intellectual property laws of the United States of +America. No party to this Agreement will bring a legal action under this +Agreement more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation.

+ +

 

+ +
+ + \ No newline at end of file diff --git a/ide/Feature-org.codehaus.groovy26.feature/feature.properties b/ide/Feature-org.codehaus.groovy26.feature/feature.properties new file mode 100644 index 0000000000..f5b4b3cde7 --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/feature.properties @@ -0,0 +1,123 @@ +# contains externalized strings for feature.xml +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# "%foo" in feature.xml corresponds to the key "foo" in this file + +providerName=Codehaus.org + +featureName=Groovy Compiler 2.6 (early access) + +descriptionURL=https://github.com/groovy/groovy-eclipse/wiki + +description=Provides the 2.6 version of the Groovy compiler + +# "licenseURL" property - URL of the "Feature License" +# do not translate value - just change to point to a locale-specific HTML page +licenseURL=license.html + +# "license" property - text of the "Feature Update License" +# should be plain text version of license agreement pointed to be "licenseURL" +license=\ +ECLIPSE FOUNDATION SOFTWARE USER AGREEMENT\n\ +March 17, 2005\n\ +\n\ +Usage Of Content\n\ +\n\ +THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\ +OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\ +USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\ +AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\ +NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\ +AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\ +AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\ +OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\ +TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\ +OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\ +BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\ +\n\ +Applicable Licenses\n\ +\n\ +Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\ +is provided to you under the terms and conditions of the Eclipse Public\n\ +License Version 1.0 ("EPL"). A copy of the EPL is provided with this\n\ +Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\ +For purposes of the EPL, "Program" will mean the Content.\n\ +\n\ +Content includes, but is not limited to, source code, object code,\n\ +documentation and other files maintained in the Eclipse.org CVS\n\ +repository ("Repository") in CVS modules ("Modules") and made available\n\ +as downloadable archives ("Downloads").\n\ +\n\ + - Content may be structured and packaged into modules to facilitate delivering,\n\ + extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\ + plug-in fragments ("Fragments"), and features ("Features").\n\ + - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java? ARchive)\n\ + in a directory named "plugins".\n\ + - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\ + Each Feature may be packaged as a sub-directory in a directory named "features".\n\ + Within a Feature, files named "feature.xml" may contain a list of the names and version\n\ + numbers of the Plug-ins and/or Fragments associated with that Feature.\n\ + - Features may also include other Features ("Included Features"). Within a Feature, files\n\ + named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\ +\n\ +Features may also include other Features ("Included Features"). Files named\n\ +"feature.xml" may contain a list of the names and version numbers of\n\ +Included Features.\n\ +\n\ +The terms and conditions governing Plug-ins and Fragments should be\n\ +contained in files named "about.html" ("Abouts"). The terms and\n\ +conditions governing Features and Included Features should be contained\n\ +in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\ +Licenses may be located in any directory of a Download or Module\n\ +including, but not limited to the following locations:\n\ +\n\ + - The top-level (root) directory\n\ + - Plug-in and Fragment directories\n\ + - Inside Plug-ins and Fragments packaged as JARs\n\ + - Sub-directories of the directory named "src" of certain Plug-ins\n\ + - Feature directories\n\ +\n\ +Note: if a Feature made available by the Eclipse Foundation is installed using the\n\ +Eclipse Update Manager, you must agree to a license ("Feature Update\n\ +License") during the installation process. If the Feature contains\n\ +Included Features, the Feature Update License should either provide you\n\ +with the terms and conditions governing the Included Features or inform\n\ +you where you can locate them. Feature Update Licenses may be found in\n\ +the "license" property of files named "feature.properties". Such Abouts,\n\ +Feature Licenses and Feature Update Licenses contain the terms and\n\ +conditions (or references to such terms and conditions) that govern your\n\ +use of the associated Content in that directory.\n\ +\n\ +THE ABOUTS, FEATURE LICENSES AND FEATURE UPDATE LICENSES MAY REFER\n\ +TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\ +SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\ +\n\ + - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\ + - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\ + - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\ + - IBM Public License 1.0 (available at http://oss.software.ibm.com/developerworks/opensource/license10.html)\n\ + - Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)\n\ + - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\ +\n\ +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\ +TO USE OF THE CONTENT. If no About, Feature License or Feature Update License\n\ +is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\ +govern that particular Content.\n\ +\n\ +Cryptography\n\ +\n\ +Content may contain encryption software. The country in which you are\n\ +currently may have restrictions on the import, possession, and use,\n\ +and/or re-export to another country, of encryption software. BEFORE\n\ +using any encryption software, please check the country's laws,\n\ +regulations and policies concerning the import, possession, or use,\n\ +and re-export of encryption software, to see if this is permitted.\n\ +\n\ +Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.\n +########### end of license property ########################################## + +# "copyright" property - text of the "Feature Update Copyright" +copyright=\Copyright (c) 2009-2018 Pivotal Software, Inc. and others.\n\ +All rights reserved. This program and the accompanying materials\n\ +are made available under the terms of the Eclipse Public License v1.0\n\ +which accompanies this distribution, and is available at\n\ +http://www.eclipse.org/legal/epl-v10.html\n\ diff --git a/ide/Feature-org.codehaus.groovy26.feature/feature.xml b/ide/Feature-org.codehaus.groovy26.feature/feature.xml new file mode 100644 index 0000000000..c924956eb4 --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/feature.xml @@ -0,0 +1,41 @@ + + + + + + %description + + + + %copyright + + + + %license + + + + + + + + + + + + diff --git a/ide/Feature-org.codehaus.groovy26.feature/license.html b/ide/Feature-org.codehaus.groovy26.feature/license.html new file mode 100644 index 0000000000..71b4d05e59 --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/license.html @@ -0,0 +1,109 @@ + + + + +Eclipse.org Software User Agreement +

Eclipse Foundation Software User Agreement

+

March 17, 2005

+ +

Usage Of Content

+ +

THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS + (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND + CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE + OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR + NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND + CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.

+ +

Applicable Licenses

+ +

Unless otherwise indicated, all Content made available by the +Eclipse Foundation is provided to you under the terms and conditions of +the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is +provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html. + For purposes of the EPL, "Program" will mean the Content.

+ +

Content includes, but is not limited to, source code, object code, +documentation and other files maintained in the Eclipse.org CVS +repository ("Repository") in CVS modules ("Modules") and made available +as downloadable archives ("Downloads").

+ +
    +
  • Content may be structured and packaged into modules to +facilitate delivering, extending, and upgrading the Content. Typical +modules may include plug-ins ("Plug-ins"), plug-in fragments +("Fragments"), and features ("Features").
  • +
  • Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".
  • +
  • A +Feature is a bundle of one or more Plug-ins and/or Fragments and +associated material. Each Feature may be packaged as a sub-directory in +a directory named "features". Within a Feature, files named +"feature.xml" may contain a list of the names and version numbers of +the Plug-ins and/or Fragments associated with that Feature.
  • +
  • Features +may also include other Features ("Included Features"). Within a +Feature, files named "feature.xml" may contain a list of the names and +version numbers of Included Features.
  • +
+ +

The terms and conditions governing Plug-ins and Fragments should be +contained in files named "about.html" ("Abouts"). The terms and +conditions governing Features and +Included Features should be contained in files named "license.html" +("Feature Licenses"). Abouts and Feature Licenses may be located in any +directory of a Download or Module +including, but not limited to the following locations:

+ +
    +
  • The top-level (root) directory
  • +
  • Plug-in and Fragment directories
  • +
  • Inside Plug-ins and Fragments packaged as JARs
  • +
  • Sub-directories of the directory named "src" of certain Plug-ins
  • +
  • Feature directories
  • +
+ +

Note: if a Feature made available by the Eclipse Foundation is +installed using the Eclipse Update Manager, you must agree to a license +("Feature Update License") during the +installation process. If the Feature contains Included Features, the +Feature Update License should either provide you with the terms and +conditions governing the Included Features or +inform you where you can locate them. Feature Update Licenses may be +found in the "license" property of files named "feature.properties" +found within a Feature. +Such Abouts, Feature Licenses, and Feature Update Licenses contain the +terms and conditions (or references to such terms and conditions) that +govern your use of the associated Content in +that directory.

+ +

THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER +TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND +CONDITIONS. SOME OF THESE +OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):

+ + + +

IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND +CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, +or Feature Update License is provided, please +contact the Eclipse Foundation to determine what terms and conditions +govern that particular Content.

+ +

Cryptography

+ +

Content may contain encryption software. The country in which you +are currently may have restrictions on the import, possession, and use, +and/or re-export to another country, of encryption software. BEFORE +using any encryption software, please check the country's laws, +regulations and policies concerning the import, possession, or use, and +re-export of encryption software, to see if this is permitted.

+ +Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. + \ No newline at end of file diff --git a/ide/Feature-org.codehaus.groovy26.feature/pom.xml b/ide/Feature-org.codehaus.groovy26.feature/pom.xml new file mode 100644 index 0000000000..a56caf36ba --- /dev/null +++ b/ide/Feature-org.codehaus.groovy26.feature/pom.xml @@ -0,0 +1,13 @@ + + 4.0.0 + + ../../pom.xml + org.codehaus.groovy.eclipse + org.codehaus.groovy.eclipse.parent + 3.0.0-SNAPSHOT + + org.codehaus.groovy.eclipse + org.codehaus.groovy26.feature + 3.0.0-SNAPSHOT + eclipse-feature + \ No newline at end of file diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/requestor/CompletionNodeFinder.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/requestor/CompletionNodeFinder.java index 5f74bc360d..96394fe00e 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/requestor/CompletionNodeFinder.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/requestor/CompletionNodeFinder.java @@ -21,6 +21,7 @@ import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.DynamicVariable; import org.codehaus.groovy.ast.FieldNode; @@ -492,7 +493,11 @@ public void visitConstantExpression(ConstantExpression expression) { createContext(expression, blockStack.getLast(), ContentAssistLocation.ANNOTATION); } if (check(expression)) { - createContext(expression, blockStack.getLast(), expressionOrStatement()); + if (isStringLiteral(expression)) { + throw new VisitCompleteException(); + } else { + createContext(expression, blockStack.getLast(), expressionOrStatement()); + } } super.visitConstantExpression(expression); } @@ -870,4 +875,11 @@ private static boolean isMethodCallWithOneArgument(Expression expr) { return false; } } + + private static boolean isStringLiteral(ConstantExpression expr) { + if (ClassHelper.STRING_TYPE.equals(expr.getType())) { + return (expr.getLength() > expr.getText().length()); + } + return false; + } } diff --git a/ide/org.codehaus.groovy.eclipse.codebrowsing/src/org/codehaus/groovy/eclipse/codebrowsing/fragments/EmptyASTFragment.java b/ide/org.codehaus.groovy.eclipse.codebrowsing/src/org/codehaus/groovy/eclipse/codebrowsing/fragments/EmptyASTFragment.java index 784080bbb8..b665000e3e 100644 --- a/ide/org.codehaus.groovy.eclipse.codebrowsing/src/org/codehaus/groovy/eclipse/codebrowsing/fragments/EmptyASTFragment.java +++ b/ide/org.codehaus.groovy.eclipse.codebrowsing/src/org/codehaus/groovy/eclipse/codebrowsing/fragments/EmptyASTFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ public class EmptyASTFragment implements IASTFragment { // all empty expressions are really the same - private static final EmptyExpression emptyExpression = new EmptyExpression(); + private static final EmptyExpression emptyExpression = EmptyExpression.INSTANCE; @Override public void accept(FragmentVisitor visitor) { diff --git a/pom.xml b/pom.xml index 51e4080e46..1e6d59f4c6 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ base/org.codehaus.groovy24 base/org.codehaus.groovy25 + base/org.codehaus.groovy26 base/org.codehaus.groovy.eclipse.compilerResolver base/org.eclipse.jdt.groovy.core @@ -78,6 +79,7 @@ ide/Feature-org.codehaus.groovy24.feature ide/Feature-org.codehaus.groovy25.feature + ide/Feature-org.codehaus.groovy26.feature ide/Feature-org.codehaus.groovy.compilerless.feature ide/Feature-org.codehaus.groovy.eclipse.feature ide/Feature-org.codehaus.groovy.headless.feature