You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/02/01 04:46:25 UTC

[groovy] branch master updated: GROOVY-8959: Avoid NPE in StaticVerifier

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7529d3c  GROOVY-8959: Avoid NPE in StaticVerifier
7529d3c is described below

commit 7529d3c47e4fecc52b60d4cbd1cb9123c72eaa1c
Author: Paul King <pa...@asert.com.au>
AuthorDate: Fri Feb 1 14:43:30 2019 +1000

    GROOVY-8959: Avoid NPE in StaticVerifier
---
 .../codehaus/groovy/control/StaticVerifier.java    | 407 ++++++++++-----------
 1 file changed, 203 insertions(+), 204 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
index 6bf3908..a0442a4 100644
--- a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
+++ b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
@@ -1,204 +1,203 @@
-/*
- *  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.CodeVisitorSupport;
-import org.codehaus.groovy.ast.DynamicVariable;
-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.expr.ClosureExpression;
-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.VariableExpression;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
-
-/**
- * Verifier to check non-static access in static contexts
- */
-public class StaticVerifier extends ClassCodeVisitorSupport {
-    private boolean inSpecialConstructorCall;
-    private boolean inPropertyExpression; // TODO use it or lose it
-    private boolean inClosure;
-    private MethodNode currentMethod;
-    private SourceUnit source;
-
-    public void visitClass(ClassNode node, SourceUnit source) {
-        this.source = source;
-        super.visitClass(node);
-    }
-
-    @Override
-    public void visitVariableExpression(VariableExpression ve) {
-        Variable v = ve.getAccessedVariable();
-        if (v instanceof DynamicVariable) {
-            if (!inPropertyExpression || inSpecialConstructorCall) addStaticVariableError(ve);
-        }
-    }
-
-    @Override
-    public void visitClosureExpression(ClosureExpression ce) {
-        boolean oldInClosure = inClosure;
-        inClosure = true;
-        super.visitClosureExpression(ce);
-        inClosure = oldInClosure;
-    }
-
-    @Override
-    public void visitConstructorCallExpression(ConstructorCallExpression cce) {
-        boolean oldIsSpecialConstructorCall = inSpecialConstructorCall;
-        inSpecialConstructorCall = cce.isSpecialCall();
-        super.visitConstructorCallExpression(cce);
-        inSpecialConstructorCall = oldIsSpecialConstructorCall;
-    }
-
-    @Override
-    public void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
-        MethodNode oldCurrentMethod = currentMethod;
-        currentMethod = node;
-        super.visitConstructorOrMethod(node, isConstructor);
-        if (isConstructor) {
-            final Set<String> exceptions = new HashSet<String>();
-            for (final Parameter param : node.getParameters()) {
-                exceptions.add(param.getName());
-                if (param.hasInitialExpression()) {
-                    param.getInitialExpression().visit(new CodeVisitorSupport() {
-                        @Override
-                        public void visitVariableExpression(VariableExpression ve) {
-                            if (exceptions.contains(ve.getName())) return;
-                            Variable av = ve.getAccessedVariable();
-                            if (av instanceof DynamicVariable || !av.isInStaticContext()) {
-                                addVariableError(ve);
-                            }
-                        }
-
-                        @Override
-                        public void visitMethodCallExpression(MethodCallExpression call) {
-                            Expression objectExpression = call.getObjectExpression();
-                            if (objectExpression instanceof VariableExpression) {
-                                VariableExpression ve = (VariableExpression) objectExpression;
-                                if (ve.isThisExpression()) {
-                                    addError("Can't access instance method '" + call.getMethodAsString() + "' for a constructor parameter default value", param);
-                                    return;
-                                }
-                            }
-                            super.visitMethodCallExpression(call);
-                        }
-
-                        @Override
-                        public void visitClosureExpression(ClosureExpression expression) {
-                            //skip contents, because of dynamic scope
-                        }
-                    });
-                }
-            }
-        }
-        currentMethod = oldCurrentMethod;
-    }
-
-    @Override
-    public void visitMethodCallExpression(MethodCallExpression mce) {
-        if (inSpecialConstructorCall && !isInnerClass(currentMethod.getDeclaringClass())) {
-            Expression objectExpression = mce.getObjectExpression();
-            if (objectExpression instanceof VariableExpression) {
-                VariableExpression ve = (VariableExpression) objectExpression;
-                if (ve.isThisExpression()) {
-                    addError("Can't access instance method '" + mce.getMethodAsString() + "' before the class is constructed", mce);
-                    return;
-                }
-            }
-        }
-        super.visitMethodCallExpression(mce);
-    }
-
-    @Override
-    public void visitPropertyExpression(PropertyExpression pe) {
-        if (!inSpecialConstructorCall) checkStaticScope(pe);
-    }
-
-    @Override
-    protected SourceUnit getSourceUnit() {
-        return source;
-    }
-
-
-    private void checkStaticScope(PropertyExpression pe) {
-        if (inClosure) return;
-        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
-            if (it instanceof PropertyExpression) continue;
-            if (it instanceof VariableExpression) {
-                addStaticVariableError((VariableExpression) it);
-            }
-            return;
-        }
-    }
-
-    private void addStaticVariableError(VariableExpression ve) {
-        // closures are always dynamic
-        // propertyExpressions will handle the error a bit differently
-        if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;
-        if (ve.isThisExpression() || ve.isSuperExpression()) return;
-        Variable v = ve.getAccessedVariable();
-        if (currentMethod != null && currentMethod.isStatic()) {
-            FieldNode fieldNode = getDeclaredOrInheritedField(currentMethod.getDeclaringClass(), ve.getName());
-            if (fieldNode != null && fieldNode.isStatic()) return;
-        }
-        if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;
-        addVariableError(ve);
-    }
-
-    private void addVariableError(VariableExpression ve) {
-        addError("Apparent variable '" + ve.getName() + "' was found in a static scope but doesn't refer" +
-                " to a local variable, static field or class. Possible causes:\n" +
-                "You attempted to reference a variable in the binding or an instance variable from a static context.\n" +
-                "You misspelled a classname or statically imported field. Please check the spelling.\n" +
-                "You attempted to use a method '" + ve.getName() +
-                "' but left out brackets in a place not allowed by the grammar.", ve);
-    }
-
-    private static FieldNode getDeclaredOrInheritedField(ClassNode cn, String fieldName) {
-        ClassNode node = cn;
-        while (node != null) {
-            FieldNode fn = node.getDeclaredField(fieldName);
-            if (fn != null) return fn;
-            List<ClassNode> interfacesToCheck = new ArrayList<ClassNode>(Arrays.asList(node.getInterfaces()));
-            while (!interfacesToCheck.isEmpty()) {
-                ClassNode nextInterface = interfacesToCheck.remove(0);
-                fn = nextInterface.getDeclaredField(fieldName);
-                if (fn != null) return fn;
-                interfacesToCheck.addAll(Arrays.asList(nextInterface.getInterfaces()));
-            }
-            node = node.getSuperClass();
-        }
-        return null;
-    }
-
-}
+/*
+ *  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.CodeVisitorSupport;
+import org.codehaus.groovy.ast.DynamicVariable;
+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.expr.ClosureExpression;
+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.VariableExpression;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
+
+/**
+ * Verifier to check non-static access in static contexts
+ */
+public class StaticVerifier extends ClassCodeVisitorSupport {
+    private boolean inSpecialConstructorCall;
+    private boolean inPropertyExpression; // TODO use it or lose it
+    private boolean inClosure;
+    private MethodNode currentMethod;
+    private SourceUnit source;
+
+    public void visitClass(ClassNode node, SourceUnit source) {
+        this.source = source;
+        super.visitClass(node);
+    }
+
+    @Override
+    public void visitVariableExpression(VariableExpression ve) {
+        Variable v = ve.getAccessedVariable();
+        if (v instanceof DynamicVariable) {
+            if (!inPropertyExpression || inSpecialConstructorCall) addStaticVariableError(ve);
+        }
+    }
+
+    @Override
+    public void visitClosureExpression(ClosureExpression ce) {
+        boolean oldInClosure = inClosure;
+        inClosure = true;
+        super.visitClosureExpression(ce);
+        inClosure = oldInClosure;
+    }
+
+    @Override
+    public void visitConstructorCallExpression(ConstructorCallExpression cce) {
+        boolean oldIsSpecialConstructorCall = inSpecialConstructorCall;
+        inSpecialConstructorCall = cce.isSpecialCall();
+        super.visitConstructorCallExpression(cce);
+        inSpecialConstructorCall = oldIsSpecialConstructorCall;
+    }
+
+    @Override
+    public void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        MethodNode oldCurrentMethod = currentMethod;
+        currentMethod = node;
+        super.visitConstructorOrMethod(node, isConstructor);
+        if (isConstructor) {
+            final Set<String> exceptions = new HashSet<String>();
+            for (final Parameter param : node.getParameters()) {
+                exceptions.add(param.getName());
+                if (param.hasInitialExpression()) {
+                    param.getInitialExpression().visit(new CodeVisitorSupport() {
+                        @Override
+                        public void visitVariableExpression(VariableExpression ve) {
+                            if (exceptions.contains(ve.getName())) return;
+                            if (ve.getAccessedVariable() instanceof DynamicVariable || !ve.isInStaticContext()) {
+                                addVariableError(ve);
+                            }
+                        }
+
+                        @Override
+                        public void visitMethodCallExpression(MethodCallExpression call) {
+                            Expression objectExpression = call.getObjectExpression();
+                            if (objectExpression instanceof VariableExpression) {
+                                VariableExpression ve = (VariableExpression) objectExpression;
+                                if (ve.isThisExpression()) {
+                                    addError("Can't access instance method '" + call.getMethodAsString() + "' for a constructor parameter default value", param);
+                                    return;
+                                }
+                            }
+                            super.visitMethodCallExpression(call);
+                        }
+
+                        @Override
+                        public void visitClosureExpression(ClosureExpression expression) {
+                            //skip contents, because of dynamic scope
+                        }
+                    });
+                }
+            }
+        }
+        currentMethod = oldCurrentMethod;
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression mce) {
+        if (inSpecialConstructorCall && !isInnerClass(currentMethod.getDeclaringClass())) {
+            Expression objectExpression = mce.getObjectExpression();
+            if (objectExpression instanceof VariableExpression) {
+                VariableExpression ve = (VariableExpression) objectExpression;
+                if (ve.isThisExpression()) {
+                    addError("Can't access instance method '" + mce.getMethodAsString() + "' before the class is constructed", mce);
+                    return;
+                }
+            }
+        }
+        super.visitMethodCallExpression(mce);
+    }
+
+    @Override
+    public void visitPropertyExpression(PropertyExpression pe) {
+        if (!inSpecialConstructorCall) checkStaticScope(pe);
+    }
+
+    @Override
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+
+
+    private void checkStaticScope(PropertyExpression pe) {
+        if (inClosure) return;
+        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
+            if (it instanceof PropertyExpression) continue;
+            if (it instanceof VariableExpression) {
+                addStaticVariableError((VariableExpression) it);
+            }
+            return;
+        }
+    }
+
+    private void addStaticVariableError(VariableExpression ve) {
+        // closures are always dynamic
+        // propertyExpressions will handle the error a bit differently
+        if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;
+        if (ve.isThisExpression() || ve.isSuperExpression()) return;
+        Variable v = ve.getAccessedVariable();
+        if (currentMethod != null && currentMethod.isStatic()) {
+            FieldNode fieldNode = getDeclaredOrInheritedField(currentMethod.getDeclaringClass(), ve.getName());
+            if (fieldNode != null && fieldNode.isStatic()) return;
+        }
+        if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;
+        addVariableError(ve);
+    }
+
+    private void addVariableError(VariableExpression ve) {
+        addError("Apparent variable '" + ve.getName() + "' was found in a static scope but doesn't refer" +
+                " to a local variable, static field or class. Possible causes:\n" +
+                "You attempted to reference a variable in the binding or an instance variable from a static context.\n" +
+                "You misspelled a classname or statically imported field. Please check the spelling.\n" +
+                "You attempted to use a method '" + ve.getName() +
+                "' but left out brackets in a place not allowed by the grammar.", ve);
+    }
+
+    private static FieldNode getDeclaredOrInheritedField(ClassNode cn, String fieldName) {
+        ClassNode node = cn;
+        while (node != null) {
+            FieldNode fn = node.getDeclaredField(fieldName);
+            if (fn != null) return fn;
+            List<ClassNode> interfacesToCheck = new ArrayList<ClassNode>(Arrays.asList(node.getInterfaces()));
+            while (!interfacesToCheck.isEmpty()) {
+                ClassNode nextInterface = interfacesToCheck.remove(0);
+                fn = nextInterface.getDeclaredField(fieldName);
+                if (fn != null) return fn;
+                interfacesToCheck.addAll(Arrays.asList(nextInterface.getInterfaces()));
+            }
+            node = node.getSuperClass();
+        }
+        return null;
+    }
+
+}