You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2022/08/28 12:36:17 UTC

[groovy] 02/03: Fix accessing `clone` illegally

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

sunlan pushed a commit to branch danielsun/fix-clone-array-based-on-groovy10730pr
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 45146486e11af896103c7a8ac7c040527f7ffac7
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Aug 28 20:05:23 2022 +0800

    Fix accessing `clone` illegally
---
 src/main/java/groovy/lang/MetaClassImpl.java       | 22 +---------------
 .../org/codehaus/groovy/runtime/ArrayUtil.java     | 30 ++++++++++++++++++++++
 .../org/codehaus/groovy/vmplugin/v8/Selector.java  | 11 ++++++++
 src/test/groovy/bugs/Groovy9103.groovy             |  9 +++++++
 4 files changed, 51 insertions(+), 21 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index c32102989d..14ca8081fb 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1131,7 +1131,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             if (receiverClass.isArray()) {
                 if (methodName.equals("clone") && arguments.length == 0) {
                     try {
-                        return (Object[]) MethodHandleHolder.CLONE_ARRAY_METHOD_HANDLE.invokeExact((Object[]) object);
+                        return (Object[]) ArrayUtil.getCloneArrayMethodHandle().invokeExact((Object[]) object);
                     } catch (Throwable t) {
                         throw new GroovyRuntimeException(t);
                     }
@@ -1164,26 +1164,6 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
     }
 
-    private static class MethodHandleHolder {
-        private static final MethodHandle CLONE_ARRAY_METHOD_HANDLE;
-        static {
-            final Class<ArrayUtil> arrayUtilClass = ArrayUtil.class;
-            Method cloneArrayMethod;
-            try {
-                cloneArrayMethod = arrayUtilClass.getDeclaredMethod("cloneArray", Object[].class);
-            } catch (NoSuchMethodException e) {
-                throw new GroovyBugError("Failed to find `cloneArray` method in class `" + arrayUtilClass.getName() + "`", e);
-            }
-
-            try {
-                CLONE_ARRAY_METHOD_HANDLE = MethodHandles.lookup().in(arrayUtilClass).unreflect(cloneArrayMethod);
-            } catch (IllegalAccessException e) {
-                throw new GroovyBugError("Failed to create method handle for " + cloneArrayMethod);
-            }
-        }
-        private MethodHandleHolder() {}
-    }
-
     private static final ClassValue<Map<String, Set<Method>>> SPECIAL_METHODS_MAP = new ClassValue<Map<String, Set<Method>>>() {
         @Override
         protected Map<String, Set<Method>> computeValue(final Class<?> type) {
diff --git a/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java b/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java
index 72bcd83c51..d35a4d80ea 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ArrayUtil.java
@@ -18,6 +18,12 @@
  */
 package org.codehaus.groovy.runtime;
 
+import org.codehaus.groovy.GroovyBugError;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
 /**
 * This is a generated class used internally during the writing of bytecode within the CallSiteWriter logic.
 * This is not a class exposed to users, as is the case with almost all classes in the org.codehaus.groovy packages.
@@ -55,6 +61,30 @@ package org.codehaus.groovy.runtime;
 public class ArrayUtil {
     private static final Object[] EMPTY = new Object[0];
 
+    public static MethodHandle getCloneArrayMethodHandle() {
+        return MethodHandleHolder.CLONE_ARRAY_METHOD_HANDLE;
+    }
+
+    private static class MethodHandleHolder {
+        private static final MethodHandle CLONE_ARRAY_METHOD_HANDLE;
+        static {
+            final Class<ArrayUtil> arrayUtilClass = ArrayUtil.class;
+            Method cloneArrayMethod;
+            try {
+                cloneArrayMethod = arrayUtilClass.getDeclaredMethod("cloneArray", Object[].class);
+            } catch (NoSuchMethodException e) {
+                throw new GroovyBugError("Failed to find `cloneArray` method in class `" + arrayUtilClass.getName() + "`", e);
+            }
+
+            try {
+                CLONE_ARRAY_METHOD_HANDLE = MethodHandles.lookup().in(arrayUtilClass).unreflect(cloneArrayMethod);
+            } catch (IllegalAccessException e) {
+                throw new GroovyBugError("Failed to create method handle for " + cloneArrayMethod);
+            }
+        }
+        private MethodHandleHolder() {}
+    }
+
     public static <T> T[] cloneArray(T[] array) {
         return array.clone();
     }
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
index 2352a1c21b..c5d75a4968 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Selector.java
@@ -38,6 +38,7 @@ import org.codehaus.groovy.reflection.CachedMethod;
 import org.codehaus.groovy.reflection.ClassInfo;
 import org.codehaus.groovy.reflection.GeneratedMetaMethod;
 import org.codehaus.groovy.reflection.stdclasses.CachedSAMClass;
+import org.codehaus.groovy.runtime.ArrayUtil;
 import org.codehaus.groovy.runtime.GeneratedClosure;
 import org.codehaus.groovy.runtime.GroovyCategorySupport;
 import org.codehaus.groovy.runtime.GroovyCategorySupport.CategoryMethod;
@@ -673,9 +674,19 @@ public abstract class Selector {
             }
         }
 
+        private static final Method OBJECT_CLONE_METHOD;
+        static {
+            try {
+                OBJECT_CLONE_METHOD = Object.class.getDeclaredMethod("clone");
+            } catch (NoSuchMethodException e) {
+                throw new GroovyBugError(e); // should never happen
+            }
+        }
         private MethodHandle correctClassForNameAndUnReflectOtherwise(Method m) throws IllegalAccessException {
             if (m.getDeclaringClass() == Class.class && m.getName().equals("forName") && m.getParameterTypes().length == 1) {
                 return MethodHandles.insertArguments(CLASS_FOR_NAME, 1, true, sender.getClassLoader());
+            } else if (null != args && args.getClass().isArray() && OBJECT_CLONE_METHOD.equals(m)) {
+                return ArrayUtil.getCloneArrayMethodHandle();
             } else {
                 return LOOKUP.unreflect(m);
             }
diff --git a/src/test/groovy/bugs/Groovy9103.groovy b/src/test/groovy/bugs/Groovy9103.groovy
index ab1be1a0c5..721344f0c6 100644
--- a/src/test/groovy/bugs/Groovy9103.groovy
+++ b/src/test/groovy/bugs/Groovy9103.groovy
@@ -69,4 +69,13 @@ final class Groovy9103 {
         Object obj = new Tuple1('abc')
         assert obj.clone()
     }
+
+    @Test
+    void testClone4() {
+        assertScript '''
+            int[] nums = [1, 2, 3]
+            int[] cloned = nums.clone()
+            assert nums == cloned
+        '''
+    }
 }