You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/01/10 23:14:40 UTC

[groovy] branch master updated (5cbdf15 -> 801aca8)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 5cbdf15  GROOVY-10447: Bump checkstyle to 9.2.1 (build dependency)
     new d1a4d94  GROOVY-5169: JSON output: exclude non-public properties
     new 08982d3  GROOVY-7682, GROOVY-8371: JSON output: exclude static properties
     new 801aca8  GROOVY-5169 (pt.2): JSON output: include field-based properties

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/main/java/groovy/lang/MetaClassImpl.java       |  45 +++----
 src/test/groovy/ClosureTest.groovy                 |   9 +-
 src/test/groovy/Property2Test.groovy               |  14 +-
 .../antlr4/util/ASTComparatorCategory.groovy       |  26 ++--
 .../console/ui/ScriptToTreeNodeAdapter.groovy      |  42 +++---
 .../groovy/jmx/builder/JmxMetaMapBuilder.groovy    |  38 +++---
 subprojects/groovy-json/build.gradle               |  14 +-
 .../java/groovy/json/DefaultJsonGenerator.java     |  35 +++--
 .../groovy/json/DefaultJsonGeneratorTest.groovy    |   5 +
 .../test/groovy/groovy/json/JsonOutputTest.groovy  | 150 ++++++++++++++++++---
 10 files changed, 250 insertions(+), 128 deletions(-)

[groovy] 03/03: GROOVY-5169 (pt.2): JSON output: include field-based properties

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 801aca8abdce440f7c94092ed6e0144f6e0c1826
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Jan 5 09:56:03 2022 -0600

    GROOVY-5169 (pt.2): JSON output: include field-based properties
---
 src/main/java/groovy/lang/MetaClassImpl.java       |  4 +--
 src/test/groovy/ClosureTest.groovy                 |  9 +++--
 src/test/groovy/Property2Test.groovy               | 14 +++-----
 .../antlr4/util/ASTComparatorCategory.groovy       | 26 +++++---------
 .../console/ui/ScriptToTreeNodeAdapter.groovy      | 42 ++++++++++------------
 .../groovy/jmx/builder/JmxMetaMapBuilder.groovy    | 38 ++++++++++----------
 .../test/groovy/groovy/json/JsonOutputTest.groovy  |  4 +--
 7 files changed, 62 insertions(+), 75 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 45ca41c..3710648 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -2313,11 +2313,9 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         // simply return the values of the metaproperty map as a List
         List<MetaProperty> ret = new ArrayList<>(propertyMap.size());
         for (MetaProperty mp : propertyMap.values()) {
-            if (mp instanceof CachedField) continue;
-
             if (mp instanceof MetaBeanProperty) {
                 MetaBeanProperty mbp = (MetaBeanProperty) mp;
-                // filter out DGM beans
+                // filter out extrinsic properties (DGM, ...)
                 boolean getter = true, setter = true;
                 MetaMethod getterMetaMethod = mbp.getGetter();
                 if (getterMetaMethod == null || getterMetaMethod instanceof GeneratedMetaMethod || getterMetaMethod instanceof NewInstanceMetaMethod) {
diff --git a/src/test/groovy/ClosureTest.groovy b/src/test/groovy/ClosureTest.groovy
index 19e0a15..8ca0195 100644
--- a/src/test/groovy/ClosureTest.groovy
+++ b/src/test/groovy/ClosureTest.groovy
@@ -22,6 +22,8 @@ import groovy.test.GroovyTestCase
 import org.codehaus.groovy.control.MultipleCompilationErrorsException
 
 import static groovy.lang.Closure.IDENTITY
+import static java.lang.reflect.Modifier.isPublic
+import static java.lang.reflect.Modifier.isStatic
 
 final class ClosureTest extends GroovyTestCase {
 
@@ -204,8 +206,11 @@ final class ClosureTest extends GroovyTestCase {
         Closure.metaClass = null
 
         Closure.metaClass.properties.each { property ->
-            def closure = { println it }
-            closure."$property.name" == closure."${MetaProperty.getGetterName(property.name, property.type)}"()
+            int modifiers = property.modifiers
+            if (isPublic(modifiers) && !isStatic(modifiers)) {
+                Closure closure = { -> }
+                closure."$property.name" == closure."${MetaProperty.getGetterName(property.name, property.type)}"()
+            }
         }
     }
 
diff --git a/src/test/groovy/Property2Test.groovy b/src/test/groovy/Property2Test.groovy
index 152657e..5ebe2aa 100644
--- a/src/test/groovy/Property2Test.groovy
+++ b/src/test/groovy/Property2Test.groovy
@@ -23,7 +23,7 @@ import groovy.test.GroovyTestCase
 /**
  * Tests the use of getMetaPropertyValues() and getProperties() for Beans and Expandos.
  */
-class Property2Test extends GroovyTestCase {
+final class Property2Test extends GroovyTestCase {
 
     void testGetPropertiesBeanCheckingValues() {
         def foo = new Foo()
@@ -31,19 +31,16 @@ class Property2Test extends GroovyTestCase {
         // these are the properties and their values that should be there
         def props = ['name': 'James', 'count': 1, 'location': 'London', 'blah': 9]
         foo.properties.each { name, value ->
-            // GROOVY-996 - We should see protected properties, but not  private ones.
-            assert name != "invisible"
+            // GROOVY-996, GROOVY-10438: should see protected properties, but not private ones
+            if (name == 'invisible') return
 
-            def pvalue = props[name]
+            def pvalue = props.remove(name)
             if (pvalue != null)
                 assert pvalue == value
-
-            // remove this one from the map
-            props.remove(name)
         }
 
         // make sure there are none left over
-        assert props.size() == 0
+        assert props.isEmpty()
     }
 
     void testMetaPropertyValuesFromObject() {
@@ -71,4 +68,3 @@ class Property2Test extends GroovyTestCase {
         assert props.size() == 0
     }
 }
-
diff --git a/src/test/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy b/src/test/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy
index 25671ea..129da6a 100644
--- a/src/test/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy
+++ b/src/test/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy
@@ -219,14 +219,14 @@ class ASTComparatorCategory {
     static reflexiveEquals(a, b, ignore = []) {
         if (a.getClass() != b.getClass()) {
             log.warning(" !!!! DIFFERENCE WAS FOUND! ${a.getClass()} != ${b.getClass()}")
-            return false;
+            return false
         }
 
         def objects = [a, b]
         Boolean res = this.objects[objects]
         if (res != null) {
             log.info("Skipping [$a, $b] comparison as they are ${ res ? "" : "un" }equal.")
-            return res;
+            return res
         }
         else if (this.objects.containsKey(objects)) {
             log.info("Skipping as they are processed at higher levels.")
@@ -239,29 +239,21 @@ class ASTComparatorCategory {
             return true
 
         def difference = a.metaClass.properties.find { MetaProperty mp  ->
-            MetaBeanProperty p = (MetaBeanProperty) mp
-            if (!p.getter)
+            if (mp !instanceof MetaBeanProperty || !mp.getter)
                 return false
 
-            def name = p.name
+            def name = mp.name
             lastName = "$name :::: ${ a.getClass() } ${ a.hashCode() }"
 
-
             for (Map.Entry<Class, List<String>> me : COLLECTION_PROPERTY_CONFIGURATION) {
-                if (!(me.key.isCase(a) && me.key.isCase(b))) {
-                    continue;
-                }
-
-                String propName = me.value[0];
-
-                if (name != propName) {
-                    continue;
+                if (name != me.value[0] || !(me.key.isCase(a) && me.key.isCase(b))) {
+                    continue
                 }
 
-                def aValue = a."${propName}"; // FIXME when the propName is "classes", a classNode will be added to moduleNode.classes
-                def bValue = b."${propName}";
+                def aValue = a."${name}" // FIXME when the name is "classes", a classNode will be added to moduleNode.classes
+                def bValue = b."${name}"
 
-                String orderName = me.value[1];
+                String orderName = me.value[1]
 
                 return new LinkedList(aValue?.getClass()?.isArray() ? Arrays.asList(aValue) : (aValue ?: [])).sort {c1, c2 -> c1."${orderName}" <=> c2."${orderName}"} !=
                         new LinkedList(bValue?.getClass()?.isArray() ? Arrays.asList(bValue) : (bValue ?: [])).sort {c1, c2 -> c1."${orderName}" <=> c2."${orderName}"}
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ScriptToTreeNodeAdapter.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ScriptToTreeNodeAdapter.groovy
index f825de2..426590f 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ScriptToTreeNodeAdapter.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ScriptToTreeNodeAdapter.groovy
@@ -215,30 +215,26 @@ class ScriptToTreeNodeAdapter {
      * Creates the property table for the node so that the properties view can display nicely.
      */
     private List<List<String>> getPropertyTable(node) {
-        node.metaClass.properties?.
-                findAll { it.getter }?.
-                collect {
-                    def name = it.name.toString()
-                    def value
-                    try {
-                        // multiple assignment statements cannot be cast to VariableExpression so
-                        // instead reference the value through the leftExpression property, which is the same
-                        if (node instanceof DeclarationExpression &&
-                                (name == 'variableExpression' || name == 'tupleExpression')) {
-                            value = toString(node.leftExpression)
-                        } else {
-                            value = toString(it.getProperty(node))
-                        }
-                    } catch (GroovyBugError reflectionArtefact) {
-                        // compiler throws error if it thinks a field is being accessed
-                        // before it is set under certain conditions. It wasn't designed
-                        // to be walked reflectively like this.
-                        value = null
+        node.metaClass.properties.findResults { mp ->
+            if (mp instanceof MetaBeanProperty && mp.getter) {
+                String name = mp.name, type = mp.type.simpleName
+                Object value = null
+                try {
+                    // multiple assignment statements cannot be cast to VariableExpression so
+                    // instead reference the value through the leftExpression property, which is the same
+                    if (node instanceof DeclarationExpression && (name == 'variableExpression' || name == 'tupleExpression')) {
+                        value = toString(node.leftExpression)
+                    } else {
+                        value = toString(mp.getProperty(node))
                     }
-                    def type = it.type.simpleName.toString()
-                    [name, value, type]
-                }?.
-                sort { it[0] }
+                } catch (GroovyBugError ignore) {
+                    // compiler throws error if it thinks a field is being accessed
+                    // before it is set under certain conditions. It wasn't designed
+                    // to be walked reflectively like this.
+                }
+                [name, value, type]
+            }
+        }.sort { list -> list[0] } // sort by name
     }
 
     // GROOVY-8339: to avoid illegal access to a non-visible implementation class - can be removed if a more general solution is found
diff --git a/subprojects/groovy-jmx/src/main/groovy/groovy/jmx/builder/JmxMetaMapBuilder.groovy b/subprojects/groovy-jmx/src/main/groovy/groovy/jmx/builder/JmxMetaMapBuilder.groovy
index 7a77889..4c8d8c8 100644
--- a/subprojects/groovy-jmx/src/main/groovy/groovy/jmx/builder/JmxMetaMapBuilder.groovy
+++ b/subprojects/groovy-jmx/src/main/groovy/groovy/jmx/builder/JmxMetaMapBuilder.groovy
@@ -19,7 +19,9 @@
 package groovy.jmx.builder
 
 import javax.management.ObjectName
+
 import java.lang.reflect.Constructor
+import java.lang.reflect.Modifier
 
 /**
  * The JmxMetaMapBuilder class is used to collect meta data passed in JmxBuilder nodes.  Once collected,
@@ -27,8 +29,8 @@ import java.lang.reflect.Constructor
  */
 class JmxMetaMapBuilder {
 
-    private static final ATTRIB_EXCEPTION_LIST = ["class", "descriptor", "jmx", "metaClass"]
-    private static final OPS_EXCEPTION_LIST = [
+    private static final Set<String> ATTRIB_EXCEPTION_LIST = ["class", "descriptor", "jmx", "metaClass"]
+    private static final Set<String> OPS_EXCEPTION_LIST = [
             "clone",
             "equals",
             "finalize",
@@ -186,22 +188,20 @@ class JmxMetaMapBuilder {
      * @return the meta map for the attribute
      */
     static Map buildAttributeMapFrom(def object) {
-        def properties = object.metaClass.getProperties()
-
         def attribs = [:]
-        properties.each { MetaProperty prop ->
-            if (!ATTRIB_EXCEPTION_LIST.contains(prop.name)) {
-                def attrib = [:]
-                def getterPrefix = (prop.type.name == "java.lang.Boolean" || prop.type.name == "boolean") ? "is" : "get"
-                def name = JmxBuilderTools.capitalize(prop.name)
-                attrib.name = name
-                attrib.displayName = "Property ${prop.name}".toString()
-                attrib.readable = true
-                attrib.getMethod = getterPrefix + name
-                attrib.writable = false
-                attrib.type = prop.type.name
-                attrib.property = prop
-                attribs.put(name, attrib)
+        object.metaClass.properties.each { MetaProperty prop ->
+            if (!ATTRIB_EXCEPTION_LIST.contains(prop.name)
+                    && !Modifier.isPrivate(prop.modifiers)) { // GROOVY-10438
+                String name = JmxBuilderTools.capitalize(prop.name)
+                attribs.put(name, [
+                   name: name,
+                   type: prop.type.name,
+                   displayName: "Property ${prop.name}".toString(),
+                   getMethod: (prop.type.name == 'boolean' ? 'is' : 'get') + name,
+                   property: prop,
+                   readable: true,
+                   writable: false
+                ])
             }
         }
         return attribs
@@ -393,10 +393,10 @@ class JmxMetaMapBuilder {
      * @return The meta map generated.
      */
     static Map buildOperationMapFrom(def object) {
-        def methods = object.metaClass.getMethods()
+        def methods = object.metaClass.methods
         def ops = [:]
 
-        def declaredMethods = object.getClass().getDeclaredMethods()*.name
+        def declaredMethods = object.class.declaredMethods*.name
 
         methods.each { method ->
             // avoid picking up extra methods from parents
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
index 41c3ceb..b7cabee 100644
--- a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
@@ -463,14 +463,14 @@ final class JsonOutputTest {
     void testPropertyModifiers1() {
         assertScript '''
             class Pogo {
-                public    String foo = 'foo' //TODO
+                public    String foo = 'foo'
                 public    String getBar() { 'bar' }
                 protected String getBaz() { 'baz' }
                 private   String getBoo() { 'boo' }
             }
 
             String json = groovy.json.JsonOutput.toJson(new Pogo())
-            assert json == '{"bar":"bar"}'
+            assert json == '{"foo":"foo","bar":"bar"}'
         '''
     }
 

[groovy] 02/03: GROOVY-7682, GROOVY-8371: JSON output: exclude static properties

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 08982d3c9bfcd3fcac52414f41fc4cc5257f44cd
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Jan 4 14:58:29 2022 -0600

    GROOVY-7682, GROOVY-8371: JSON output: exclude static properties
---
 subprojects/groovy-json/build.gradle               | 14 +++--
 .../java/groovy/json/DefaultJsonGenerator.java     |  2 +
 .../test/groovy/groovy/json/JsonOutputTest.groovy  | 72 +++++++++++++++++++++-
 3 files changed, 82 insertions(+), 6 deletions(-)

diff --git a/subprojects/groovy-json/build.gradle b/subprojects/groovy-json/build.gradle
index d9fcb6b..7190c9c 100644
--- a/subprojects/groovy-json/build.gradle
+++ b/subprojects/groovy-json/build.gradle
@@ -21,13 +21,19 @@ plugins {
 }
 
 dependencies {
-    api rootProject  // JsonBuilder extends GroovyObjectSupport...
+    api rootProject // JsonBuilder extends GroovyObjectSupport...
+
     testImplementation project(':groovy-test')
     testImplementation project(':groovy-dateutil')
     testImplementation 'net.javacrumbs.json-unit:json-unit:2.28.0'
-    testRuntimeOnly "org.slf4j:slf4j-api:${versions.slf4j}"
-    testRuntimeOnly project(':groovy-ant') // for JavadocAssertionTests
-    testRuntimeOnly 'com.google.code.gson:gson:2.8.9' // json-unit requires gson, jackson1, or jackson2
+
+    testRuntimeOnly project(':'), {
+        capabilities {
+            requireCapability 'org.apache.groovy:groovy-grapes'
+        }
+    }
+    testRuntimeOnly project(':groovy-ant')
+    testRuntimeOnly 'com.google.code.gson:gson:2.8.9' // json-unit requires gson, jackson1 or jackson2
 }
 
 plugins.withId('eclipse') {
diff --git a/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java b/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java
index 360ff72..cb9a8b8 100644
--- a/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java
+++ b/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java
@@ -55,6 +55,7 @@ import static groovy.json.JsonOutput.EMPTY_STRING_CHARS;
 import static groovy.json.JsonOutput.OPEN_BRACE;
 import static groovy.json.JsonOutput.OPEN_BRACKET;
 import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
 
 /**
  * A JsonGenerator that can be configured with various {@link JsonGenerator.Options}.
@@ -242,6 +243,7 @@ public class DefaultJsonGenerator implements JsonGenerator {
 
         for (MetaProperty mp : metaProperties) {
             if (!isPublic(mp.getModifiers())) continue; // GROOVY-5169
+            if ( isStatic(mp.getModifiers())) continue; // GROOVY-7682
 
             // skip write-only property: see File
             if (mp instanceof MetaBeanProperty) {
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
index 2f053a5..41c3ceb 100644
--- a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
@@ -325,7 +325,7 @@ final class JsonOutputTest {
                 ] as JsonStreet[])
         ])
 
-        assert JsonOutput.prettyPrint(JsonOutput.toJson(city)) == '''\
+        assert JsonOutput.prettyPrint(toJson(city)) == '''\
             {
                 "name": "Paris",
                 "districts": [
@@ -448,7 +448,7 @@ final class JsonOutputTest {
         assert toJson({'\u0002' 0}) == '{"\\u0002":0}'
     }
 
-    @Test
+    @Test // GROOVY-7519
     void testFile() {
         def file  = File.createTempFile('test', 'file-json')
         file.deleteOnExit()
@@ -473,6 +473,74 @@ final class JsonOutputTest {
             assert json == '{"bar":"bar"}'
         '''
     }
+
+    @Test // GROOVY-7682
+    void testStackOverflowError1() {
+        assertScript '''
+            @Grab('joda-time:joda-time:2.10.10')
+            import org.joda.time.DateTime
+            import org.joda.time.format.DateTimeFormat
+            import org.joda.time.format.DateTimeFormatter
+
+            DateTimeFormatter formatter = DateTimeFormat.forPattern('yyyy-MM-dd HH:mm:ss.SSS z')
+            DateTime dt = formatter.parseDateTime('2015-11-20 13:37:21.123 EST')
+            String json = groovy.json.JsonOutput.toJson(dt)
+            net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals("""{
+                "afterNow":false,
+                "beforeNow":true,
+                "centuryOfEra":20,
+                "dayOfMonth":20,
+                "dayOfWeek":5,
+                "dayOfYear":324,
+                "equalNow":false,
+                "era":1,
+                "hourOfDay":13,
+                "millisOfDay":49041123,
+                "millisOfSecond":123,
+                "minuteOfDay":817,
+                "minuteOfHour":37,
+                "monthOfYear":11,
+                "secondOfDay":49041,
+                "secondOfMinute":21,
+                "weekOfWeekyear":47,
+                "weekyear":2015,
+                "year":2015,
+                "yearOfCentury":15,
+                "yearOfEra":2015,
+                "zone":{
+                    "ID":"America/New_York",
+                    "fixed":false,
+                    "uncachedZone":{
+                        "ID":"America/New_York",
+                        "cachable":true,
+                        "fixed":false
+                    }
+                }
+            }""", json)
+        '''
+    }
+
+    @Test // GROOVY-8371
+    void testStackOverflowError2() {
+        assertScript '''
+            final  date = java.time.LocalDate.of(1970, 1, 1)
+            String json = groovy.json.JsonOutput.toJson(date)
+            net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals("""{
+                "chronology":{
+                    "calendarType":"iso8601",
+                    "id":"ISO"
+                },
+                "dayOfMonth":1,
+                "dayOfWeek":"THURSDAY",
+                "dayOfYear":1,
+                "era":"CE",
+                "leapYear":false,
+                "month":"JANUARY",
+                "monthValue":1,
+                "year":1970
+            }""", json)
+        '''
+    }
 }
 
 //------------------------------------------------------------------------------

[groovy] 01/03: GROOVY-5169: JSON output: exclude non-public properties

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit d1a4d948996d409be0c1e3180ef60d713cf2999a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Jan 4 13:29:38 2022 -0600

    GROOVY-5169: JSON output: exclude non-public properties
---
 src/main/java/groovy/lang/MetaClassImpl.java       | 47 ++++++-------
 .../java/groovy/json/DefaultJsonGenerator.java     | 33 ++++++---
 .../groovy/json/DefaultJsonGeneratorTest.groovy    |  5 ++
 .../test/groovy/groovy/json/JsonOutputTest.groovy  | 80 +++++++++++++++++-----
 4 files changed, 112 insertions(+), 53 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 93f78ee..45ca41c 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -2305,51 +2305,46 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     @Override
     public List<MetaProperty> getProperties() {
         checkInitalised();
-        LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.get(theCachedClass);
+        Map<String, MetaProperty> propertyMap = classPropertyIndex.get(theCachedClass);
         if (propertyMap == null) {
-            // GROOVY-6903: May happen in some special environment, like under Android, due
-            // to classloading issues
-            propertyMap = new LinkedHashMap<>();
+            // GROOVY-6903: May happen in some special environment, like Android, due to class-loading issues
+            propertyMap = Collections.emptyMap();
         }
         // simply return the values of the metaproperty map as a List
         List<MetaProperty> ret = new ArrayList<>(propertyMap.size());
-        for (Map.Entry<String, MetaProperty> stringMetaPropertyEntry : propertyMap.entrySet()) {
-            MetaProperty element = stringMetaPropertyEntry.getValue();
-            if (element instanceof CachedField) continue;
-            // filter out DGM beans
-            if (element instanceof MetaBeanProperty) {
-                MetaBeanProperty mp = (MetaBeanProperty) element;
-                boolean setter = true;
-                boolean getter = true;
-                MetaMethod getterMetaMethod = mp.getGetter();
+        for (MetaProperty mp : propertyMap.values()) {
+            if (mp instanceof CachedField) continue;
+
+            if (mp instanceof MetaBeanProperty) {
+                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+                // filter out DGM beans
+                boolean getter = true, setter = true;
+                MetaMethod getterMetaMethod = mbp.getGetter();
                 if (getterMetaMethod == null || getterMetaMethod instanceof GeneratedMetaMethod || getterMetaMethod instanceof NewInstanceMetaMethod) {
                     getter = false;
                 }
-                MetaMethod setterMetaMethod = mp.getSetter();
+                MetaMethod setterMetaMethod = mbp.getSetter();
                 if (setterMetaMethod == null || setterMetaMethod instanceof GeneratedMetaMethod || setterMetaMethod instanceof NewInstanceMetaMethod) {
                     setter = false;
                 }
                 if (!setter && !getter) continue;
+
 //  TODO: I (ait) don't know why these strange tricks needed and comment following as it effects some Grails tests
-//                if (!setter && mp.getSetter() != null) {
-//                    element = new MetaBeanProperty(mp.getName(), mp.getType(), mp.getGetter(), null);
+//                if (!setter && mbp.getSetter() != null) {
+//                    mp = new MetaBeanProperty(mbp.getName(), mbp.getType(), mbp.getGetter(), null);
 //                }
-//                if (!getter && mp.getGetter() != null) {
-//                    element = new MetaBeanProperty(mp.getName(), mp.getType(), null, mp.getSetter());
+//                if (!getter && mbp.getGetter() != null) {
+//                    mp = new MetaBeanProperty(mbp.getName(), mbp.getType(), null, mbp.getSetter());
 //                }
-            }
-
-            if (!permissivePropertyAccess) {
-                if (element instanceof MetaBeanProperty) {
-                    MetaBeanProperty mbp = (MetaBeanProperty) element;
-                    boolean getterAccessible = canAccessLegally(mbp.getGetter());
-                    boolean setterAccessible = canAccessLegally(mbp.getSetter());
 
+                if (!permissivePropertyAccess) {
+                    boolean getterAccessible = canAccessLegally(getterMetaMethod);
+                    boolean setterAccessible = canAccessLegally(setterMetaMethod);
                     if (!(getterAccessible && setterAccessible)) continue;
                 }
             }
 
-            ret.add(element);
+            ret.add(mp);
         }
         return ret;
     }
diff --git a/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java b/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java
index 5766843..360ff72 100644
--- a/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java
+++ b/subprojects/groovy-json/src/main/java/groovy/json/DefaultJsonGenerator.java
@@ -21,8 +21,9 @@ package groovy.json;
 import org.apache.groovy.json.internal.CharBuf;
 import org.apache.groovy.json.internal.Chr;
 import groovy.lang.Closure;
+import groovy.lang.MetaBeanProperty;
+import groovy.lang.MetaProperty;
 import groovy.util.Expando;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 
 import java.io.File;
 import java.math.BigDecimal;
@@ -36,6 +37,7 @@ import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
@@ -52,6 +54,7 @@ import static groovy.json.JsonOutput.EMPTY_MAP_CHARS;
 import static groovy.json.JsonOutput.EMPTY_STRING_CHARS;
 import static groovy.json.JsonOutput.OPEN_BRACE;
 import static groovy.json.JsonOutput.OPEN_BRACKET;
+import static java.lang.reflect.Modifier.isPublic;
 
 /**
  * A JsonGenerator that can be configured with various {@link JsonGenerator.Options}.
@@ -232,12 +235,27 @@ public class DefaultJsonGenerator implements JsonGenerator {
         }
     }
 
-    protected Map<?, ?> getObjectProperties(Object object) {
-        Map<?, ?> properties = DefaultGroovyMethods.getProperties(object);
-        properties.remove("class");
-        properties.remove("declaringClass");
-        properties.remove("metaClass");
-        return properties;
+    protected Map<?, ?> getObjectProperties(final Object object) {
+        List<MetaProperty> metaProperties = org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(object).getProperties();
+
+        Map<String, Object> namesAndValues = new LinkedHashMap<>(metaProperties.size());
+
+        for (MetaProperty mp : metaProperties) {
+            if (!isPublic(mp.getModifiers())) continue; // GROOVY-5169
+
+            // skip write-only property: see File
+            if (mp instanceof MetaBeanProperty) {
+                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+                if (mbp.getField() == null && mbp.getGetter() == null) continue;
+            }
+
+            String name = mp.getName();
+            if (name.equals("class") || name.equals("metaClass") || name.equals("declaringClass")) continue;
+
+            namesAndValues.put(name, mp.getProperty(object));
+        }
+
+        return namesAndValues;
     }
 
     /**
@@ -530,5 +548,4 @@ public class DefaultJsonGenerator implements JsonGenerator {
             return super.toString() + "<" + this.type.toString() + ">";
         }
     }
-
 }
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/DefaultJsonGeneratorTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/DefaultJsonGeneratorTest.groovy
index 48d7c60..a78290f 100644
--- a/subprojects/groovy-json/src/test/groovy/groovy/json/DefaultJsonGeneratorTest.groovy
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/DefaultJsonGeneratorTest.groovy
@@ -291,6 +291,11 @@ class DefaultJsonGeneratorTest extends GroovyTestCase {
 
 }
 
+class JsonObject {
+    String name
+    Map properties
+}
+
 class JsonBar {
     String favoriteDrink
     Date lastVisit
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
index 20c061d..2f053a5 100644
--- a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
@@ -18,19 +18,22 @@
  */
 package groovy.json
 
-import groovy.test.GroovyTestCase
 import groovy.transform.Canonical
+import org.junit.Test
 
 import static groovy.json.JsonOutput.toJson
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
 
-class JsonOutputTest extends GroovyTestCase {
+final class JsonOutputTest {
 
-    // Check for GROOVY-5918
+    @Test // GROOVY-5918
     void testExpando() {
         assert toJson(new Expando(a: 42)) == '{"a":42}'
         assert new JsonBuilder(new Expando(a: 42)).toString() == '{"a":42}'
     }
 
+    @Test
     void testBooleanValues() {
         assert toJson(Boolean.TRUE) == "true"
         assert toJson(Boolean.FALSE) == "false"
@@ -38,6 +41,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson(false) == "false"
     }
 
+    @Test
     void testNullValue() {
         assert toJson(null) == "null"
         // test every overloaded version
@@ -55,6 +59,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson((Map) null) == 'null'
     }
 
+    @Test
     void testNumbers() {
         assert toJson(-1) == "-1"
         assert toJson(1) == "1"
@@ -91,16 +96,19 @@ class JsonOutputTest extends GroovyTestCase {
         shouldFail { toJson(Float.NEGATIVE_INFINITY) }
     }
 
+    @Test
     void testEmptyListOrArray() {
         assert toJson([]) == "[]"
         assert toJson([] as Object[]) == "[]"
     }
 
+    @Test
     void testListOfPrimitives() {
         assert toJson([true, false, null, true, 4, 1.1234]) == "[true,false,null,true,4,1.1234]"
         assert toJson([true, [false, null], true, [4, [1.1234]]]) == "[true,[false,null],true,[4,[1.1234]]]"
     }
 
+    @Test
     void testPrimitiveArray() {
         assert toJson([1, 2, 3, 4] as byte[]) == "[1,2,3,4]"
         assert toJson([1, 2, 3, 4] as short[]) == "[1,2,3,4]"
@@ -108,16 +116,19 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson([1, 2, 3, 4] as long[]) == "[1,2,3,4]"
     }
 
+    @Test
     void testEmptyMap() {
         assert toJson([:]) == "{}"
     }
 
+    @Test
     void testMap() {
         assert toJson([a: 1]) == '{"a":1}'
         assert toJson([a: 1, b: 2]) == '{"a":1,"b":2}'
         assert toJson([a: 1, b: true, c: null, d: [], e: 'hello']) == '{"a":1,"b":true,"c":null,"d":[],"e":"hello"}'
     }
 
+    @Test
     void testString() {
         assert toJson("") == '""'
 
@@ -153,32 +164,38 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson("\u0019") == '"\\u0019"'
     }
 
+    @Test
     void testGString() {
         assert toJson("1 + 2 = ${1 + 2}") == '"1 + 2 = 3"'
     }
 
+    @Test
     void testStringBuilderBuffer() {
         assert toJson(new StringBuilder().append(14).append(' March ').append(2014)) == '"14 March 2014"'
         assert toJson(new StringBuffer().append(14).append(' March ').append(2014)) == '"14 March 2014"'
     }
 
+    @Test
     void testCharArray() {
         char[] charArray = ['a', 'b', 'c']
 
         assert toJson(charArray) == '["a","b","c"]'
     }
 
+    @Test
     void testDate() {
         def d = Date.parse("yyyy/MM/dd HH:mm:ss Z", "2008/03/04 13:50:00 +0100")
 
         assert toJson(d) == '"2008-03-04T12:50:00+0000"'
     }
 
+    @Test
     void testURL() {
         assert toJson(new URL("http://glaforge.appspot.com")) == '"http://glaforge.appspot.com"'
         assert toJson(new URL('file', '', 'C:\\this\\is\\windows\\path')) == '"file:C:\\\\this\\\\is\\\\windows\\\\path"' // GROOVY-6560
     }
 
+    @Test
     void testCalendar() {
         def c = GregorianCalendar.getInstance(TimeZone.getTimeZone('GMT+1'))
         c.clearTime()
@@ -187,6 +204,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson(c) == '"2008-03-04T12:50:00+0000"'
     }
 
+    @Test
     void testComplexObject() {
         assert toJson([name: 'Guillaume', age: 33, address: [line1: "1 main street", line2: "", zip: 1234], pets: ['dog', 'cat']]) ==
                 '{"name":"Guillaume","age":33,"address":{"line1":"1 main street","line2":"","zip":1234},"pets":["dog","cat"]}'
@@ -194,6 +212,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson([[:], [:]]) == '[{},{}]'
     }
 
+    @Test
     void testClosure() {
         assert toJson({
             a 1
@@ -208,11 +227,13 @@ class JsonOutputTest extends GroovyTestCase {
         }) == '{"a":1,"b":{"c":2,"d":{"e":[3,{"f":4}]}}}'
     }
 
+    @Test
     void testIteratorEnumeration() {
         assert toJson([1, 2, 3].iterator()) == '[1,2,3]'
         assert toJson(Collections.enumeration([1, 2, 3])) == '[1,2,3]'
     }
 
+    @Test
     void testPrettyPrint() {
         def json = new JsonBuilder()
 
@@ -271,6 +292,7 @@ class JsonOutputTest extends GroovyTestCase {
         return str.replaceAll(~/\s/, '')
     }
 
+    @Test
     void testPrettyPrintStringZeroLen() {
         def tree = [myStrings: [str3: 'abc', str0: '']]
         def result = stripWhiteSpace(new JsonBuilder(tree).toPrettyString())
@@ -278,6 +300,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert result == expected
     }
 
+    @Test
     void testPrettyPrintDoubleQuoteEscape() {
         def json = new JsonBuilder()
         json.text { content 'abc"def' }
@@ -289,6 +312,7 @@ class JsonOutputTest extends GroovyTestCase {
             }""".stripIndent()
     }
 
+    @Test
     void testSerializePogos() {
         def city = new JsonCity("Paris", [
                 new JsonDistrict(1, [
@@ -335,29 +359,38 @@ class JsonOutputTest extends GroovyTestCase {
             }'''.stripIndent()
     }
 
+    @Test
     void testMapWithNullKey() {
         shouldFail IllegalArgumentException, {
             toJson([(null): 1])
         }
     }
 
-    void testGROOVY5247() {
+    @Test // GROOVY-5247
+    void testTreeMap() {
         def m = new TreeMap()
         m.a = 1
         assert toJson(m) == '{"a":1}'
     }
 
-    void testObjectWithDeclaredPropertiesField() {
-        def person = new JsonObject(name: "pillow", properties: [state: "fluffy", color: "white"])
-        def json = toJson(person)
+    @Test
+    void testDeclaredPropertiesField() {
+        String json = toJson(new JsonObject(name: "pillow", properties: [state: "fluffy", color: "white"]))
         assert json == '{"name":"pillow","properties":{"state":"fluffy","color":"white"}}'
     }
 
-    void testGROOVY5494() {
-        def json = toJson(new JsonFoo(name: "foo"))
+    @Test // GROOVY-5494
+    void testDeclaredPropertiesMethod() {
+        String json = toJson(new Pogo5494(name: "foo"))
         assert json == '{"name":"foo","properties":0}'
     }
 
+    static class Pogo5494 {
+        String name
+        int getProperties() { return 0 }
+    }
+
+    @Test
     void testCharacter() {
         assert toJson('a' as char) == '"a"'
         assert toJson('"' as char) == '"\\""'
@@ -371,6 +404,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson('\u0002' as char) == '"\\u0002"'
     }
 
+    @Test
     void testEmptyValue() {
         assert toJson('') == '""'
         assert toJson(['']) == '[""]'
@@ -378,6 +412,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson(new Expando('': '')) == '{"":""}'
     }
 
+    @Test
     void testSpecialCharEscape() {
         // Map
         assert toJson(['"': 0]) == '{"\\"":0}'
@@ -413,6 +448,7 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson({'\u0002' 0}) == '{"\\u0002":0}'
     }
 
+    @Test
     void testFile() {
         def file  = File.createTempFile('test', 'file-json')
         file.deleteOnExit()
@@ -423,8 +459,24 @@ class JsonOutputTest extends GroovyTestCase {
         assert toJson(dir)
     }
 
+    @Test // GROOVY-5169
+    void testPropertyModifiers1() {
+        assertScript '''
+            class Pogo {
+                public    String foo = 'foo' //TODO
+                public    String getBar() { 'bar' }
+                protected String getBaz() { 'baz' }
+                private   String getBoo() { 'boo' }
+            }
+
+            String json = groovy.json.JsonOutput.toJson(new Pogo())
+            assert json == '{"bar":"bar"}'
+        '''
+    }
 }
 
+//------------------------------------------------------------------------------
+
 @Canonical
 class JsonCity {
     String name
@@ -443,16 +495,6 @@ class JsonStreet {
     JsonStreetKind kind
 }
 
-class JsonObject {
-    String name
-    Map properties
-}
-
-class JsonFoo {
-    String name
-    int getProperties() { return 0 }
-}
-
 enum JsonStreetKind {
     street, boulevard, avenue
 }