All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.groovy.tools.shell.util.ScriptVariableAnalyzer.groovy Maven / Gradle / Ivy

There is a newer version: 3.0.23
Show newest version
/*
 * Copyright 2003-2014 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.tools.shell.util

import groovy.transform.TypeChecked
import org.codehaus.groovy.ast.ClassCodeVisitorSupport
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.DynamicVariable
import org.codehaus.groovy.ast.GroovyClassVisitor
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.classgen.GeneratorContext
import org.codehaus.groovy.control.*

import java.security.CodeSource

/**
 * Class to Class parsing a script to detect all bound and unbound variables.
 * Based on http://glaforge.appspot.com/article/knowing-which-variables-are-bound-or-not-in-a-groovy-script
 */
@TypeChecked
class ScriptVariableAnalyzer {

    /**
     * define a visitor that visits all variable expressions
     */
    static class VariableVisitor extends ClassCodeVisitorSupport implements GroovyClassVisitor {
        Set bound = new HashSet()
        Set unbound = new HashSet()

        @Override
        void visitVariableExpression(VariableExpression expression) {
            // we're not interested in some special implicit variables
            if (!(expression.variable in ['args', 'context', 'this', 'super'])) {
                // thanks to this instanceof
                // we know if the variable is bound or not
                if (expression.accessedVariable instanceof DynamicVariable) {
                    unbound << expression.variable
                } else {
                    bound << expression.variable
                }
            }
            super.visitVariableExpression(expression)
        }

        @Override
        protected SourceUnit getSourceUnit() {
            return null
        }
    }

    /**
     * custom PrimaryClassNodeOperation
     * to be able to hook our code visitor
     */
    static class VisitorSourceOperation extends CompilationUnit.PrimaryClassNodeOperation {

        final GroovyClassVisitor visitor

        VisitorSourceOperation(final GroovyClassVisitor visitor) {
            this.visitor = visitor
        }

        @Override
        void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode)
                throws CompilationFailedException {
            classNode.visitContents(visitor)
        }
    }

    /**
     * class loader to add our phase operation
     */
    static class VisitorClassLoader extends GroovyClassLoader {
        final GroovyClassVisitor visitor

        VisitorClassLoader(final GroovyClassVisitor visitor) {
            this.visitor = visitor
        }

        @Override
        protected CompilationUnit createCompilationUnit(final CompilerConfiguration config, final CodeSource source) {
            CompilationUnit cu = super.createCompilationUnit(config, source)
            cu.addPhaseOperation(new VisitorSourceOperation(visitor), Phases.CLASS_GENERATION)
            return cu
        }
    }

    static Set getBoundVars(final String scriptText) {
        assert scriptText != null
        GroovyClassVisitor visitor = new VariableVisitor()
        VisitorClassLoader myCL = new VisitorClassLoader(visitor)
        // simply by parsing the script with our classloader
        // our visitor will be called and will visit all the variables
        myCL.parseClass(scriptText)
        return visitor.bound
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy