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 2019/12/20 17:36:11 UTC

[groovy] branch GROOVY_3_0_X updated (1edcb66 -> ca0fc53)

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

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


    from 1edcb66  add serialVersionUID
     new 8c07c38  GROOVY-9340: return type, not type redirect
     new d5745ad  GROOVY-9347: STC: support "I<? super T> i = lambda/closure"
     new 174925e  Improve back-compatibility
     new ca0fc53  Move tests to fix weird exception occurred only on zulu JDK 11.0.5(ubuntu)

The 4 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:
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 68 +++++++++++-----------
 .../codehaus/groovy/control/CompilationUnit.java   |  4 ++
 .../bugs/{Groovy8468.groovy => Groovy9324.groovy}  | 30 +++++-----
 src/test/groovy/transform/stc/LambdaTest.groovy    | 19 +++++-
 .../ast/builder/AstStringCompilerTest.groovy       | 39 -------------
 5 files changed, 72 insertions(+), 88 deletions(-)
 copy src/test/groovy/bugs/{Groovy8468.groovy => Groovy9324.groovy} (57%)
 delete mode 100644 src/test/org/codehaus/groovy/ast/builder/AstStringCompilerTest.groovy


[groovy] 02/04: GROOVY-9347: STC: support "I i = lambda/closure"

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

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

commit d5745ad97d80aabe52b412e460e3c398992f6dd4
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Dec 19 13:14:37 2019 -0600

    GROOVY-9347: STC: support "I<? super T> i = lambda/closure"
    
    (cherry picked from commit 4b50ac20ac37960d25896d861a55d1693d3cf891)
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 55 ++++++++++++----------
 src/test/groovy/transform/stc/LambdaTest.groovy    | 17 ++++++-
 2 files changed, 46 insertions(+), 26 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 0cc25c9..07d0262 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -49,6 +49,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 import static groovy.lang.Tuple.tuple;
@@ -926,7 +927,7 @@ public class GenericsUtils {
     }
 
     /**
-     * Get the parameter and return types of the abstract method of SAM
+     * Gets the parameter and return types of the abstract method of SAM.
      *
      * If the abstract method is not parameterized, we will get generics placeholders, e.g. T, U
      * For example, the abstract method of {@link java.util.function.Function} is
@@ -943,31 +944,23 @@ public class GenericsUtils {
      * we can get parameter types and return type of the above abstract method,
      * i.e. ClassNode {@code ClassHelper.STRING_TYPE} and {@code ClassHelper.Integer_TYPE}
      *
-     * @param sam the class node which contains only one abstract method
-     * @return the parameter and return types
-     * @since 3.0.0
+     * @param samType the class node which contains only one abstract method
      *
+     * @since 3.0.0
      */
-    public static Tuple2<ClassNode[], ClassNode> parameterizeSAM(ClassNode sam) {
-        MethodNode methodNode = ClassHelper.findSAM(sam);
-        final Map<GenericsType, GenericsType> map = makeDeclaringAndActualGenericsTypeMapOfExactType(methodNode.getDeclaringClass(), sam);
-
-        ClassNode[] parameterTypes =
-                Arrays.stream(methodNode.getParameters())
-                    .map(e -> {
-                        ClassNode originalParameterType = e.getType();
-                        return originalParameterType.isGenericsPlaceHolder()
-                                ? findActualTypeByGenericsPlaceholderName(originalParameterType.getUnresolvedName(), map)
-                                : originalParameterType;
-                    })
-                    .toArray(ClassNode[]::new);
-
-        ClassNode originalReturnType = methodNode.getReturnType();
-        ClassNode returnType =
-                originalReturnType.isGenericsPlaceHolder()
-                        ? findActualTypeByGenericsPlaceholderName(originalReturnType.getUnresolvedName(), map)
-                        : originalReturnType;
+    public static Tuple2<ClassNode[], ClassNode> parameterizeSAM(final ClassNode samType) {
+        MethodNode abstractMethod = ClassHelper.findSAM(samType);
+
+        Map<GenericsType, GenericsType> generics = makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(), samType);
+        Function<ClassNode, ClassNode> resolver = t -> {
+            if (t.isGenericsPlaceHolder()) {
+                return findActualTypeByGenericsPlaceholderName(t.getUnresolvedName(), generics);
+            }
+            return t;
+        };
 
+        ClassNode[] parameterTypes = Arrays.stream(abstractMethod.getParameters()).map(Parameter::getType).map(resolver).toArray(ClassNode[]::new);
+        ClassNode returnType = resolver.apply(abstractMethod.getReturnType());
         return tuple(parameterTypes, returnType);
     }
 
@@ -978,9 +971,21 @@ public class GenericsUtils {
      * @param genericsPlaceholderAndTypeMap the result of {@link #makeDeclaringAndActualGenericsTypeMap(ClassNode, ClassNode)}
      */
     public static ClassNode findActualTypeByGenericsPlaceholderName(final String placeholderName, final Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
+        Function<GenericsType, ClassNode> resolver = gt -> {
+            if (gt.isWildcard()) {
+                if (gt.getLowerBound() != null) {
+                    return gt.getLowerBound();
+                }
+                if (gt.getUpperBounds() != null) {
+                    return gt.getUpperBounds()[0];
+                }
+            }
+            return gt.getType();
+        };
+
         return genericsPlaceholderAndTypeMap.entrySet().stream()
-                .filter(entry -> entry.getKey().getName().equals(placeholderName))
-                .map(entry -> entry.getValue().getType()).findFirst().orElse(null);
+                .filter(e -> e.getKey().getName().equals(placeholderName))
+                .map(Map.Entry::getValue).map(resolver).findFirst().orElse(null);
     }
 
     private static class ParameterizedTypeCacheKey {
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 4b2960b..0f33f84 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -741,6 +741,21 @@ final class LambdaTest {
         '''
     }
 
+    @Test // GROOVY-9347
+    void testConsumer7() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            void test() {
+                int sum = 0
+                java.util.function.Consumer<? super Integer> add = i -> sum += i
+
+                [1, 2, 3].forEach(add)
+                assert sum == 6
+            }
+            test()
+        '''
+    }
+
     @Test
     void testFunctionalInterface1() {
         assertScript '''
@@ -1066,7 +1081,7 @@ final class LambdaTest {
         '''
     }
 
-    @Test @NotYetImplemented // GROOVY-9342
+    @Test @NotYetImplemented // GROOVY-9347
     void testStaticInitializeBlocks2() {
         assertScript '''
             @groovy.transform.CompileStatic


[groovy] 01/04: GROOVY-9340: return type, not type redirect

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

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

commit 8c07c38dfe88f1c20c6dede58e6d389ba83719fe
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Dec 19 10:41:46 2019 -0600

    GROOVY-9340: return type, not type redirect
    
    (cherry picked from commit ba93a5585a6396d2d716a388dc9346f03f5974f1)
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java    | 21 ++++++++-------------
 src/test/groovy/transform/stc/LambdaTest.groovy     |  2 +-
 2 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 1fc1b9f..0cc25c9 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -972,22 +972,15 @@ public class GenericsUtils {
     }
 
     /**
-     * Get the actual type according to the placeholder name
+     * Gets the actual type according to the placeholder name.
      *
-     * @param placeholderName the placeholder name, e.g. T, E
+     * @param placeholderName the placeholder name (i.e. "T", "E", etc.)
      * @param genericsPlaceholderAndTypeMap the result of {@link #makeDeclaringAndActualGenericsTypeMap(ClassNode, ClassNode)}
-     * @return the actual type
      */
-    public static ClassNode findActualTypeByGenericsPlaceholderName(String placeholderName, Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
-        for (Map.Entry<GenericsType, GenericsType> entry : genericsPlaceholderAndTypeMap.entrySet()) {
-            GenericsType declaringGenericsType = entry.getKey();
-
-            if (placeholderName.equals(declaringGenericsType.getName())) {
-                return entry.getValue().getType().redirect();
-            }
-        }
-
-        return null;
+    public static ClassNode findActualTypeByGenericsPlaceholderName(final String placeholderName, final Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
+        return genericsPlaceholderAndTypeMap.entrySet().stream()
+                .filter(entry -> entry.getKey().getName().equals(placeholderName))
+                .map(entry -> entry.getValue().getType()).findFirst().orElse(null);
     }
 
     private static class ParameterizedTypeCacheKey {
@@ -1003,6 +996,7 @@ public class GenericsUtils {
             return genericsClass;
         }
 
+        @SuppressWarnings("unused")
         public void setGenericsClass(ClassNode genericsClass) {
             this.genericsClass = genericsClass;
         }
@@ -1011,6 +1005,7 @@ public class GenericsUtils {
             return actualType;
         }
 
+        @SuppressWarnings("unused")
         public void setActualType(ClassNode actualType) {
             this.actualType = actualType;
         }
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index d7b9553..4b2960b 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -178,7 +178,7 @@ final class LambdaTest {
         '''
     }
 
-    @Test @NotYetImplemented // GROOVY-9340
+    @Test // GROOVY-9340
     void testConsumerWithSelfType() {
         assertScript '''
             @groovy.transform.CompileStatic


[groovy] 03/04: Improve back-compatibility

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

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

commit 174925e7c368c705045090b718d080f1584e3ffe
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Dec 21 00:21:23 2019 +0800

    Improve back-compatibility
    
    (cherry picked from commit 3b99603f0c91bd571ff3f10256dfb979d6ce48ce)
---
 src/main/java/org/codehaus/groovy/control/CompilationUnit.java | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
index c33846d..eb75133 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -1058,6 +1058,10 @@ public class CompilationUnit extends ProcessingUnit {
     }
 
     //--------------------------------------------------------------------------
+    @Deprecated // IntelliJ IDEA depends on the API
+    public void applyToPrimaryClassNodes(final PrimaryClassNodeOperation op) throws CompilationFailedException {
+        op.doPhaseOperation(this);
+    }
 
     @Deprecated
     public void addPhaseOperation(final GroovyClassOperation op) {


[groovy] 04/04: Move tests to fix weird exception occurred only on zulu JDK 11.0.5(ubuntu)

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

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

commit ca0fc5392356fc96038f05217ac36bc0f83bd1cd
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Dec 21 01:23:15 2019 +0800

    Move tests to fix weird exception occurred only on zulu JDK 11.0.5(ubuntu)
    
    See:
    https://pipelines.actions.githubusercontent.com/jEQJx6aVIMasUiQpy6TlKzY89nERxcoEfA7W2lq6ikiWDlN5Fx/_apis/pipelines/1/runs/163/signedlogcontent/5?urlExpires=2019-12-20T17%3A23%3A11.4504485Z&urlSigningMethod=HMACV1&urlSignature=a8aONyqHjuG13XhEfAI2Tvjo1eK83su2fJipQwVZ2q0%3D
    
    2019-12-20T16:09:07.9493747Z org.codehaus.groovy.transform.classloading.TransformsAndCustomClassLoadersTest > testThirdPartyLocalTransform FAILED
    2019-12-20T16:09:07.9493968Z     java.lang.TypeNotPresentException: Type javax.tools.JavaFileObject not present
    2019-12-20T16:09:07.9494139Z         at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
    2019-12-20T16:09:07.9494385Z         at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
    2019-12-20T16:09:07.9494566Z         at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
    2019-12-20T16:09:07.9494804Z         at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
    2019-12-20T16:09:07.9494953Z         at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
    2019-12-20T16:09:07.9495083Z         at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
    2019-12-20T16:09:07.9495410Z         at java.base/sun.reflect.generics.repository.ConstructorRepository.computeParameterTypes(ConstructorRepository.java:111)
    2019-12-20T16:09:07.9495575Z         at java.base/sun.reflect.generics.repository.ConstructorRepository.getParameterTypes(ConstructorRepository.java:87)
    2019-12-20T16:09:07.9495726Z         at java.base/java.lang.reflect.Executable.getGenericParameterTypes(Executable.java:279)
    2019-12-20T16:09:07.9495874Z         at java.base/java.lang.reflect.Method.getGenericParameterTypes(Method.java:330)
    2019-12-20T16:09:07.9496031Z         at java.desktop/java.beans.FeatureDescriptor.getParameterTypes(FeatureDescriptor.java:391)
    2019-12-20T16:09:07.9496566Z         at java.desktop/java.beans.MethodDescriptor.setMethod(MethodDescriptor.java:118)
    2019-12-20T16:09:07.9496698Z         at java.desktop/java.beans.MethodDescriptor.<init>(MethodDescriptor.java:74)
    2019-12-20T16:09:07.9496851Z         at java.desktop/java.beans.MethodDescriptor.<init>(MethodDescriptor.java:58)
    2019-12-20T16:09:07.9496998Z         at java.desktop/java.beans.Introspector.getTargetMethodInfo(Introspector.java:1047)
    2019-12-20T16:09:07.9497151Z         at java.desktop/java.beans.Introspector.getBeanInfo(Introspector.java:462)
    2019-12-20T16:09:07.9497276Z         at java.desktop/java.beans.Introspector.getBeanInfo(Introspector.java:205)
    2019-12-20T16:09:07.9497508Z         at groovy.lang.MetaClassImpl.lambda$addProperties$4(MetaClassImpl.java:3398)
    2019-12-20T16:09:07.9497632Z         at java.base/java.security.AccessController.doPrivileged(Native Method)
    2019-12-20T16:09:07.9497901Z         at groovy.lang.MetaClassImpl.addProperties(MetaClassImpl.java:3398)
    2019-12-20T16:09:07.9498160Z         at groovy.lang.MetaClassImpl.initialize(MetaClassImpl.java:3379)
    2019-12-20T16:09:07.9498309Z         at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:287)
    2019-12-20T16:09:07.9498577Z         at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:329)
    2019-12-20T16:09:07.9498704Z         at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:258)
    2019-12-20T16:09:07.9498852Z         at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:1000)
    2019-12-20T16:09:07.9499007Z         at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareEqual(DefaultTypeTransformation.java:654)
    2019-12-20T16:09:07.9499241Z         at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareEqual(ScriptBytecodeAdapter.java:708)
    2019-12-20T16:09:07.9499416Z         at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareNotEqual(ScriptBytecodeAdapter.java:712)
    2019-12-20T16:09:07.9499654Z         at org.codehaus.groovy.transform.classloading.TransformsAndCustomClassLoadersTest.setUp(TransformsAndCustomClassLoadersTest.groovy:62)
    2019-12-20T16:09:07.9499745Z
    2019-12-20T16:09:07.9499863Z         Caused by:
    2019-12-20T16:09:07.9499988Z         java.lang.ClassNotFoundException: javax.tools.JavaFileObject
    2019-12-20T16:09:07.9500121Z             at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)
    2019-12-20T16:09:07.9500238Z             at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
    2019-12-20T16:09:07.9500374Z             at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    2019-12-20T16:09:07.9500512Z             at java.base/java.lang.Class.forName0(Native Method)
    2019-12-20T16:09:07.9500641Z             at java.base/java.lang.Class.forName(Class.java:398)
    2019-12-20T16:09:08.0513460Z             at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)
    2019-12-20T16:09:08.0513630Z             ... 28 more
    (cherry picked from commit 870244cd157ed0bb74430e9c235804823ba2f320)
---
 src/test/groovy/bugs/Groovy9324.groovy             | 50 ++++++++++++++++++++++
 .../ast/builder/AstStringCompilerTest.groovy       | 39 -----------------
 2 files changed, 50 insertions(+), 39 deletions(-)

diff --git a/src/test/groovy/bugs/Groovy9324.groovy b/src/test/groovy/bugs/Groovy9324.groovy
new file mode 100644
index 0000000..cfd1803
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9324.groovy
@@ -0,0 +1,50 @@
+/*
+ *  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.bugs
+
+import groovy.transform.CompileStatic
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+@CompileStatic
+final class Groovy9324 {
+
+    @Test
+    void testAstStringCompilerCompile() {
+        assertScript '''
+            import org.codehaus.groovy.ast.builder.AstStringCompiler
+            import org.codehaus.groovy.ast.builder.AstAssert
+            import static org.codehaus.groovy.ast.tools.GeneralUtils.block
+            import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
+            import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt
+
+            def actual = new AstStringCompiler().compile(""" "Hello World" """)
+            def expected = [
+                    block(
+                            stmt(
+                                    constX("Hello World")
+                            )
+                    )
+            ]
+            AstAssert.assertSyntaxTree(expected, actual)
+        '''
+    }
+
+}
diff --git a/src/test/org/codehaus/groovy/ast/builder/AstStringCompilerTest.groovy b/src/test/org/codehaus/groovy/ast/builder/AstStringCompilerTest.groovy
deleted file mode 100644
index 575768e..0000000
--- a/src/test/org/codehaus/groovy/ast/builder/AstStringCompilerTest.groovy
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  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.builder
-
-import groovy.test.GroovyTestCase
-
-import static org.codehaus.groovy.ast.tools.GeneralUtils.block
-import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt
-
-class AstStringCompilerTest extends GroovyTestCase {
-    void testCompile() {
-        def actual = new AstStringCompiler().compile(""" "Hello World" """)
-        def expected = [
-                block(
-                        stmt(
-                                constX("Hello World")
-                        )
-                )
-        ]
-        AstAssert.assertSyntaxTree(expected, actual)
-    }
-}