You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2016/02/26 20:36:23 UTC

[1/4] incubator-tinkerpop git commit: Added some more asserts around variable scoping with interpreter mode.

Repository: incubator-tinkerpop
Updated Branches:
  refs/heads/tp31 d83dba4be -> 959378ca8


Added some more asserts around variable scoping with interpreter mode.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/73489b3a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/73489b3a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/73489b3a

Branch: refs/heads/tp31
Commit: 73489b3a89bab18d59fc5e1dfc9b23a10c5222ab
Parents: 566c488
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Feb 19 10:05:41 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Feb 25 07:40:47 2016 -0500

----------------------------------------------------------------------
 .../jsr223/GremlinGroovyScriptEngineTest.java   | 25 ++++++++++++++++++++
 1 file changed, 25 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/73489b3a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
index 17cd809..19ced88 100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
@@ -19,6 +19,8 @@
 package org.apache.tinkerpop.gremlin.groovy.jsr223;
 
 import groovy.lang.Closure;
+import groovy.lang.MissingPropertyException;
+import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.NoImportCustomizerProvider;
@@ -55,6 +57,7 @@ import java.util.stream.IntStream;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -99,6 +102,17 @@ public class GremlinGroovyScriptEngineTest {
         assertEquals(4, engine.eval("yyy = xxx + 1"));
         assertEquals(7, engine.eval("def zzz = yyy + xxx"));
         assertEquals(4, engine.eval("zzz - xxx"));
+        assertEquals("accessible-globally", engine.eval("if (yyy > 0) { def inner = 'should-stay-local'; outer = 'accessible-globally' }\n outer"));
+        assertEquals("accessible-globally", engine.eval("outer"));
+
+        try {
+            engine.eval("inner");
+            fail("Should not have been able to access 'inner'");
+        } catch (Exception ex) {
+            final Throwable root = ExceptionUtils.getRootCause(ex);
+            assertThat(root, instanceOf(MissingPropertyException.class));
+        }
+
         assertEquals(10, engine.eval("addItUp(zzz,xxx)"));
     }
 
@@ -112,6 +126,17 @@ public class GremlinGroovyScriptEngineTest {
         assertEquals(4, engine.eval("yyy = xxx + 1", b));
         assertEquals(7, engine.eval("def zzz = yyy + xxx", b));
         assertEquals(4, engine.eval("zzz - xxx", b));
+        assertEquals("accessible-globally", engine.eval("if (yyy > 0) { def inner = 'should-stay-local'; outer = 'accessible-globally' }\n outer", b));
+        assertEquals("accessible-globally", engine.eval("outer", b));
+
+        try {
+            engine.eval("inner", b);
+            fail("Should not have been able to access 'inner'");
+        } catch (Exception ex) {
+            final Throwable root = ExceptionUtils.getRootCause(ex);
+            assertThat(root, instanceOf(MissingPropertyException.class));
+        }
+
         assertEquals(10, engine.eval("addItUp(zzz,xxx)", b));
     }
 


[2/4] incubator-tinkerpop git commit: Allow ScriptEngine to accept scripts in interpreter mode.

Posted by sp...@apache.org.
Allow ScriptEngine to accept scripts in interpreter mode.

Scripts sent with variables normally considered "local" by use of the def keyword or the inclusion of a type will be treated as global variables when the InterpreterModeCustomizerProvider configuration is included in the configuration of the ScriptEngine.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/566c4889
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/566c4889
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/566c4889

Branch: refs/heads/tp31
Commit: 566c4889d05175057ecfa51ebf34cc9aa651b123
Parents: d0005b7
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Feb 18 10:47:28 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Feb 25 07:40:47 2016 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 .../jsr223/GremlinVariableAnalyzer.groovy       | 104 +++++++++++++++++
 .../groovy/jsr223/ast/InterpreterMode.groovy    |  30 +++++
 .../ast/InterpreterModeASTTransformation.groovy | 115 +++++++++++++++++++
 .../jsr223/GremlinGroovyScriptEngine.java       |  41 ++++++-
 .../InterpreterModeCustomizerProvider.java      |  37 ++++++
 .../jsr223/GremlinGroovyScriptEngineTest.java   |  27 +++++
 .../server/GremlinServerIntegrateTest.java      |  57 +++++++++
 8 files changed, 408 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 6a57a60..b3a05fe 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/incubator-tinkerpop/master/docs/
 TinkerPop 3.1.2 (NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* Added "interpreter mode" for the `ScriptEngine` and Gremlin Server which allows variables defined with `def` or a type to be recognized as "global".
 * Bumped to Apache Groovy 2.4.6.
 * Added the `gremlin-archetype-server` archetype that demonstrates
 * Added the `gremlin-archetype-tinkergraph` archetype that demonstrates a basic project that uses TinkerGraph.

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy
new file mode 100644
index 0000000..1d1bcbe
--- /dev/null
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy
@@ -0,0 +1,104 @@
+/*
+ * 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.apache.tinkerpop.gremlin.groovy.jsr223
+
+import org.codehaus.groovy.ast.DynamicVariable
+import org.codehaus.groovy.ast.GroovyClassVisitor
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.Phases
+import org.codehaus.groovy.tools.shell.util.ScriptVariableAnalyzer
+
+import java.security.CodeSource
+
+/**
+ * An extension of Groovy's {@code VariableVisitor} that exposes the bound and unbound variables publicly. This
+ * class can likely be removed with the next update of Groovy (after 2.4.5) as the {code ScriptVariableAnalyzer} ends
+ * up exposing {@code getBoundVars() in such a way as to allow for the {@code ClassLoader} to be supplied.
+ */
+class GremlinVariableAnalyzer {
+    public static class GremlinVariableVisitor extends ScriptVariableAnalyzer.VariableVisitor {
+        String lastBound
+
+        @Override
+        void visitVariableExpression(VariableExpression expression) {
+            if (!(expression.variable in ['args', 'context', 'this', 'super'])) {
+                if (expression.accessedVariable instanceof DynamicVariable) {
+                    unbound << expression.variable
+                } else {
+                    bound << expression.variable
+                    lastBound = bound
+                }
+            }
+            super.visitVariableExpression(expression)
+        }
+
+        @Override
+        public Set<String> getBound() {
+            return super.getBound()
+        }
+
+        @Override
+        public Set<String> getUnbound() {
+            return super.getUnbound()
+        }
+    }
+
+    public static class GremlinVisitorClassLoader extends GroovyClassLoader {
+        private final GroovyClassVisitor visitor
+
+        public GremlinVisitorClassLoader(final GroovyClassVisitor visitor, ClassLoader parent) {
+            super(parent == null ?  Thread.currentThread().getContextClassLoader() : parent)
+            this.visitor = visitor
+        }
+
+        @Override
+        protected CompilationUnit createCompilationUnit(final CompilerConfiguration config, final CodeSource source) {
+            CompilationUnit cu = super.createCompilationUnit(config, source)
+            cu.addPhaseOperation(new ScriptVariableAnalyzer.VisitorSourceOperation(visitor), Phases.CLASS_GENERATION)
+            return cu
+        }
+    }
+
+    public static BoundVars getBoundVars(final String scriptText, ClassLoader parent) {
+        assert scriptText != null
+        final GroovyClassVisitor visitor = new GremlinVariableVisitor()
+        new GremlinVisitorClassLoader(visitor, parent).parseClass(scriptText)
+        return new BoundVars(visitor.getLastBound(), visitor.getBound())
+    }
+
+    public static class BoundVars {
+        private String lastBound;
+        private Set<String> bound;
+
+        BoundVars(String lastBound, Set<String> bound) {
+            this.lastBound = lastBound
+            this.bound = bound
+        }
+
+        String getLastBound() {
+            return lastBound
+        }
+
+        Set<String> getBound() {
+            return bound
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterMode.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterMode.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterMode.groovy
new file mode 100644
index 0000000..a8da72f
--- /dev/null
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterMode.groovy
@@ -0,0 +1,30 @@
+/*
+ * 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.apache.tinkerpop.gremlin.groovy.jsr223.ast
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+
+@Retention(RetentionPolicy.SOURCE)
+@GroovyASTTransformationClass(["org.apache.tinkerpop.gremlin.groovy.jsr223.ast.InterpreterModeASTTransformation"])
+public @interface InterpreterMode {
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterModeASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterModeASTTransformation.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterModeASTTransformation.groovy
new file mode 100644
index 0000000..66638b8
--- /dev/null
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/ast/InterpreterModeASTTransformation.groovy
@@ -0,0 +1,115 @@
+/*
+ * 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.apache.tinkerpop.gremlin.groovy.jsr223.ast
+
+import groovy.transform.CompileStatic
+import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.Parameter
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.expr.BinaryExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.DeclarationExpression
+import org.codehaus.groovy.ast.expr.MapExpression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.ast.stmt.CatchStatement
+import org.codehaus.groovy.ast.stmt.EmptyStatement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.ast.stmt.TryCatchStatement
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.syntax.Token
+import org.codehaus.groovy.syntax.Types
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.transform.GroovyASTTransformation
+
+/**
+ * An {@code ASTTransformation} that promotes "local" variables to global ones.  In this case, "local" refers to those
+ * variables that are defined in a script with "def" at the root of the script.  These would typically be interpreted
+ * as local to the script, but this transform changes that, by wrapping the entire script in a try/catch where such
+ * variables are written to a "hidden" {@link Map} so that the {@code ScriptEngine} can later access them to place
+ * them into the global context.
+ */
+@CompileStatic
+@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)
+class InterpreterModeASTTransformation implements ASTTransformation {
+
+    @Override
+    void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
+        ClassNode scriptNode = (ClassNode) astNodes[1]
+        def runMethodOfScript = scriptNode.declaredMethodsMap["java.lang.Object run()"]
+        runMethodOfScript.code = wrap(runMethodOfScript)
+    }
+
+    private static BlockStatement wrap(MethodNode method) {
+        BlockStatement wrappedBlock = new BlockStatement()
+        BlockStatement existingBlock = ((BlockStatement) method.code)
+
+        // the variable names that will be written back to the global context
+        def variableNames = [] as Set<String>
+        variableNames.addAll(findTopLevelVariableDeclarations(existingBlock.statements))
+        method.variableScope.referencedClassVariablesIterator.each{variableNames << it.name}
+
+        // the map to hold the variables and values
+        wrappedBlock.addStatement(createGlobalMapAST())
+
+        // the finally block will capture all the vars in the "globals" map
+        BlockStatement finallyBlock = new BlockStatement()
+        variableNames.each {
+            finallyBlock.addStatement(createAssignToGlobalMapAST(it))
+        }
+
+        wrappedBlock.addStatement(new TryCatchStatement(existingBlock, finallyBlock))
+
+        return wrappedBlock
+    }
+
+    private static List<String> findTopLevelVariableDeclarations(def existingStatements) {
+        existingStatements.findAll{ it instanceof ExpressionStatement }
+                .collect{ ((ExpressionStatement) it).expression }
+                .findAll{ it instanceof DeclarationExpression}
+                .collect{ ((DeclarationExpression) it).leftExpression }
+                .collect{ ((VariableExpression) it).name }
+    }
+
+    private static Statement createAssignToGlobalMapAST(String varName) {
+        def tryCatch = new TryCatchStatement(new ExpressionStatement(
+                new MethodCallExpression(
+                        new VariableExpression(GremlinGroovyScriptEngine.COLLECTED_BOUND_VARS_MAP_VARNAME),
+                        "put",
+                        new ArgumentListExpression(new ConstantExpression(varName), new VariableExpression(varName)))), EmptyStatement.INSTANCE)
+
+        tryCatch.addCatch(new CatchStatement(new Parameter(ClassHelper.make(MissingPropertyException), "ex"), EmptyStatement.INSTANCE))
+        return tryCatch
+    }
+
+    private static Statement createGlobalMapAST() {
+        new ExpressionStatement(
+                new BinaryExpression(
+                        new VariableExpression(GremlinGroovyScriptEngine.COLLECTED_BOUND_VARS_MAP_VARNAME),
+                        Token.newSymbol(Types.EQUAL, 0, 0),
+                        new MapExpression()))
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
index 72e38c3..23240cb 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.groovy.DefaultImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.EmptyImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.ImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.NoImportCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.InterpreterModeCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.loaders.GremlinLoader;
 import org.apache.tinkerpop.gremlin.groovy.plugin.Artifact;
 import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin;
@@ -125,6 +126,12 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
      */
     public static final String REFERENCE_TYPE_HARD = "hard";
 
+    /**
+     * Name of variable that holds local variables to be globally bound if "interpreter mode" is enabled with
+     * {@link InterpreterModeCustomizerProvider}.
+     */
+    public static final String COLLECTED_BOUND_VARS_MAP_VARNAME = "gremlin_script_engine_collected_boundvars";
+
     private static final Pattern patternImportStatic = Pattern.compile("\\Aimport\\sstatic.*");
 
     public static final ThreadLocal<Map<String, Object>> COMPILE_OPTIONS = new ThreadLocal<Map<String, Object>>(){
@@ -146,7 +153,7 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
 
     private GremlinGroovyClassLoader loader;
 
-    private AtomicLong counter = new AtomicLong(0l);
+    private AtomicLong counter = new AtomicLong(0L);
 
     /**
      * The list of loaded plugins for the console.
@@ -164,6 +171,7 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
     private final List<CompilerCustomizerProvider> customizerProviders;
 
     private final Set<Artifact> artifactsToUse = new HashSet<>();
+    private final boolean interpreterModeEnabled;
 
     /**
      * Creates a new instance using the {@link DefaultImportCustomizerProvider}.
@@ -193,9 +201,14 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
                 .map(p -> (ImportCustomizerProvider) p)
                 .findFirst().orElse(NoImportCustomizerProvider.INSTANCE);
 
+        // determine if interpreter mode should be enabled
+        interpreterModeEnabled = providers.stream()
+                .anyMatch(p -> p.getClass().equals(InterpreterModeCustomizerProvider.class));
+
         // remove used providers as the rest will be applied directly
         customizerProviders = providers.stream()
-                .filter(p -> p != null && !(p instanceof ImportCustomizerProvider))
+                .filter(p -> p != null &&
+                             !((p instanceof ImportCustomizerProvider)))
                 .collect(Collectors.toList());
 
         createClassLoader();
@@ -531,7 +544,28 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
                     }
                 }
             });
-            return scriptObject.run();
+
+            final Object o = scriptObject.run();
+
+            // if interpreter mode is enable then local vars of the script are promoted to engine scope bindings.
+            if (interpreterModeEnabled) {
+                final Map<String, Object> localVars = (Map<String, Object>) context.getAttribute(COLLECTED_BOUND_VARS_MAP_VARNAME);
+                if (localVars != null) {
+                    localVars.entrySet().forEach(e -> {
+                        // closures need to be cached for later use
+                        if (e.getValue() instanceof Closure)
+                            globalClosures.put(e.getKey(), (Closure) e.getValue());
+
+                        context.setAttribute(e.getKey(), e.getValue(), ScriptContext.ENGINE_SCOPE);
+                    });
+
+                    // get rid of the temporary collected vars
+                    context.removeAttribute(COLLECTED_BOUND_VARS_MAP_VARNAME, ScriptContext.ENGINE_SCOPE);
+                    localVars.clear();
+                }
+            }
+
+            return o;
         } catch (Exception e) {
             throw new ScriptException(e);
         }
@@ -642,5 +676,4 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
         }
         return buf.toString();
     }
-
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java
new file mode 100644
index 0000000..e3f95f4
--- /dev/null
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.apache.tinkerpop.gremlin.groovy.jsr223.customizer;
+
+import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.ast.InterpreterMode;
+import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
+import org.codehaus.groovy.control.customizers.CompilationCustomizer;
+
+/**
+ * Places the {@code ScriptEngine} in "interpreter mode" where local variables of a script are treated as global
+ * bindings. This implementation is technically not a true {@link CompilerCustomizerProvider} instance as the
+ * "interpreter mode" feature does not require a {@code CompilerCustomizer}. This class merely acts as a flag that
+ * tells the {@link org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine} to turn this feature on.
+ */
+public class InterpreterModeCustomizerProvider implements CompilerCustomizerProvider {
+    @Override
+    public CompilationCustomizer create() {
+        return new ASTTransformationCustomizer(InterpreterMode.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
index 4740cd2..17cd809 100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
@@ -22,6 +22,7 @@ import groovy.lang.Closure;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.NoImportCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.InterpreterModeCustomizerProvider;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.javatuples.Pair;
@@ -85,7 +86,33 @@ public class GremlinGroovyScriptEngineTest {
     @Test
     public void shouldEvalWithNoBindings() throws Exception {
         final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine();
+        engine.eval("def addItUp(x,y){x+y}");
         assertEquals(3, engine.eval("1+2"));
+        assertEquals(3, engine.eval("addItUp(1,2)"));
+    }
+
+    @Test
+    public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindings() throws Exception {
+        final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeCustomizerProvider());
+        engine.eval("def addItUp = { x, y -> x + y }");
+        assertEquals(3, engine.eval("int xxx = 1 + 2"));
+        assertEquals(4, engine.eval("yyy = xxx + 1"));
+        assertEquals(7, engine.eval("def zzz = yyy + xxx"));
+        assertEquals(4, engine.eval("zzz - xxx"));
+        assertEquals(10, engine.eval("addItUp(zzz,xxx)"));
+    }
+
+    @Test
+    public void shouldPromoteDefinedVarsInInterpreterModeWithBindings() throws Exception {
+        final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeCustomizerProvider());
+        final Bindings b = new SimpleBindings();
+        b.put("x", 2);
+        engine.eval("def addItUp = { x, y -> x + y }", b);
+        assertEquals(3, engine.eval("int xxx = 1 + x", b));
+        assertEquals(4, engine.eval("yyy = xxx + 1", b));
+        assertEquals(7, engine.eval("def zzz = yyy + xxx", b));
+        assertEquals(4, engine.eval("zzz - xxx", b));
+        assertEquals(10, engine.eval("addItUp(zzz,xxx)", b));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/566c4889/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index 7714a93..61c1a98 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@ -32,6 +32,7 @@ import org.apache.tinkerpop.gremlin.driver.simple.SimpleClient;
 import org.apache.tinkerpop.gremlin.driver.simple.WebSocketClient;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.CompileStaticCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.InterpreterModeCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.SimpleSandboxExtension;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TimedInterruptCustomizerProvider;
 import org.apache.tinkerpop.gremlin.structure.T;
@@ -45,6 +46,7 @@ import org.junit.Test;
 
 import java.nio.channels.ClosedChannelException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -136,6 +138,9 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
             case "shouldUseSimpleSandbox":
                 settings.scriptEngines.get("gremlin-groovy").config = getScriptEngineConfForSimpleSandbox();
                 break;
+            case "shouldUseInterpreterMode":
+                settings.scriptEngines.get("gremlin-groovy").config = getScriptEngineConfForInterpreterMode();
+                break;
             case "shouldReceiveFailureTimeOutOnScriptEvalOfOutOfControlLoop":
                 settings.scriptEngines.get("gremlin-groovy").config = getScriptEngineConfForTimedInterrupt();
                 break;
@@ -164,6 +169,58 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
         return scriptEngineConf;
     }
 
+    private static Map<String, Object> getScriptEngineConfForInterpreterMode() {
+        final Map<String,Object> scriptEngineConf = new HashMap<>();
+        final Map<String,Object> interpreterProviderConf = new HashMap<>();
+        interpreterProviderConf.put(InterpreterModeCustomizerProvider.class.getName(), Collections.EMPTY_LIST);
+        scriptEngineConf.put("compilerCustomizerProviders", interpreterProviderConf);
+        return scriptEngineConf;
+    }
+
+    @Test
+    public void shouldUseInterpreterMode() throws Exception {
+        final Cluster cluster = Cluster.open();
+        final Client client = cluster.connect(name.getMethodName());
+
+        client.submit("def subtractAway(x,y){x-y};[]").all().get();
+        client.submit("multiplyIt = { x,y -> x * y};[]").all().get();
+
+        assertEquals(2, client.submit("x = 1 + 1").all().get().get(0).getInt());
+        assertEquals(3, client.submit("int y = x + 1").all().get().get(0).getInt());
+        assertEquals(5, client.submit("def z = x + y").all().get().get(0).getInt());
+
+        final Map<String,Object> m = new HashMap<>();
+        m.put("x", 10);
+        assertEquals(-5, client.submit("z - x", m).all().get().get(0).getInt());
+        assertEquals(15, client.submit("addItUp(x,z)", m).all().get().get(0).getInt());
+        assertEquals(5, client.submit("subtractAway(x,z)", m).all().get().get(0).getInt());
+        assertEquals(50, client.submit("multiplyIt(x,z)", m).all().get().get(0).getInt());
+
+        cluster.close();
+    }
+
+    @Test
+    public void shouldNotUseInterpreterMode() throws Exception {
+        final Cluster cluster = Cluster.open();
+        final Client client = cluster.connect(name.getMethodName());
+
+        client.submit("def subtractAway(x,y){x-y};[]").all().get();
+        client.submit("multiplyIt = { x,y -> x * y};[]").all().get();
+
+        assertEquals(2, client.submit("x = 1 + 1").all().get().get(0).getInt());
+        assertEquals(3, client.submit("y = x + 1").all().get().get(0).getInt());
+        assertEquals(5, client.submit("z = x + y").all().get().get(0).getInt());
+
+        final Map<String,Object> m = new HashMap<>();
+        m.put("x", 10);
+        assertEquals(-5, client.submit("z - x", m).all().get().get(0).getInt());
+        assertEquals(15, client.submit("addItUp(x,z)", m).all().get().get(0).getInt());
+        assertEquals(5, client.submit("subtractAway(x,z)", m).all().get().get(0).getInt());
+        assertEquals(50, client.submit("multiplyIt(x,z)", m).all().get().get(0).getInt());
+
+        cluster.close();
+    }
+
     @Test
     public void shouldUseSimpleSandbox() throws Exception {
         final Cluster cluster = Cluster.open();


[3/4] incubator-tinkerpop git commit: With the bump to groovy 2.4.6 this class is no longer needed.

Posted by sp...@apache.org.
With the bump to groovy 2.4.6 this class is no longer needed.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/3447b02d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/3447b02d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/3447b02d

Branch: refs/heads/tp31
Commit: 3447b02dc2220161bd191b482e95ad709d6571e5
Parents: 73489b3
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Feb 25 09:05:36 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Feb 25 09:05:36 2016 -0500

----------------------------------------------------------------------
 .../jsr223/GremlinVariableAnalyzer.groovy       | 104 -------------------
 1 file changed, 104 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/3447b02d/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy
deleted file mode 100644
index 1d1bcbe..0000000
--- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinVariableAnalyzer.groovy
+++ /dev/null
@@ -1,104 +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.apache.tinkerpop.gremlin.groovy.jsr223
-
-import org.codehaus.groovy.ast.DynamicVariable
-import org.codehaus.groovy.ast.GroovyClassVisitor
-import org.codehaus.groovy.ast.expr.VariableExpression
-import org.codehaus.groovy.control.CompilationUnit
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.codehaus.groovy.control.Phases
-import org.codehaus.groovy.tools.shell.util.ScriptVariableAnalyzer
-
-import java.security.CodeSource
-
-/**
- * An extension of Groovy's {@code VariableVisitor} that exposes the bound and unbound variables publicly. This
- * class can likely be removed with the next update of Groovy (after 2.4.5) as the {code ScriptVariableAnalyzer} ends
- * up exposing {@code getBoundVars() in such a way as to allow for the {@code ClassLoader} to be supplied.
- */
-class GremlinVariableAnalyzer {
-    public static class GremlinVariableVisitor extends ScriptVariableAnalyzer.VariableVisitor {
-        String lastBound
-
-        @Override
-        void visitVariableExpression(VariableExpression expression) {
-            if (!(expression.variable in ['args', 'context', 'this', 'super'])) {
-                if (expression.accessedVariable instanceof DynamicVariable) {
-                    unbound << expression.variable
-                } else {
-                    bound << expression.variable
-                    lastBound = bound
-                }
-            }
-            super.visitVariableExpression(expression)
-        }
-
-        @Override
-        public Set<String> getBound() {
-            return super.getBound()
-        }
-
-        @Override
-        public Set<String> getUnbound() {
-            return super.getUnbound()
-        }
-    }
-
-    public static class GremlinVisitorClassLoader extends GroovyClassLoader {
-        private final GroovyClassVisitor visitor
-
-        public GremlinVisitorClassLoader(final GroovyClassVisitor visitor, ClassLoader parent) {
-            super(parent == null ?  Thread.currentThread().getContextClassLoader() : parent)
-            this.visitor = visitor
-        }
-
-        @Override
-        protected CompilationUnit createCompilationUnit(final CompilerConfiguration config, final CodeSource source) {
-            CompilationUnit cu = super.createCompilationUnit(config, source)
-            cu.addPhaseOperation(new ScriptVariableAnalyzer.VisitorSourceOperation(visitor), Phases.CLASS_GENERATION)
-            return cu
-        }
-    }
-
-    public static BoundVars getBoundVars(final String scriptText, ClassLoader parent) {
-        assert scriptText != null
-        final GroovyClassVisitor visitor = new GremlinVariableVisitor()
-        new GremlinVisitorClassLoader(visitor, parent).parseClass(scriptText)
-        return new BoundVars(visitor.getLastBound(), visitor.getBound())
-    }
-
-    public static class BoundVars {
-        private String lastBound;
-        private Set<String> bound;
-
-        BoundVars(String lastBound, Set<String> bound) {
-            this.lastBound = lastBound
-            this.bound = bound
-        }
-
-        String getLastBound() {
-            return lastBound
-        }
-
-        Set<String> getBound() {
-            return bound
-        }
-    }
-}


[4/4] incubator-tinkerpop git commit: Merge remote-tracking branch 'origin/TINKERPOP-1107' into tp31

Posted by sp...@apache.org.
Merge remote-tracking branch 'origin/TINKERPOP-1107' into tp31


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/959378ca
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/959378ca
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/959378ca

Branch: refs/heads/tp31
Commit: 959378ca8820ebb148e314a1fc99dabdae34cb5b
Parents: d83dba4 3447b02
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Feb 26 14:22:21 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Feb 26 14:22:21 2016 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 .../groovy/jsr223/ast/InterpreterMode.groovy    |  30 +++++
 .../ast/InterpreterModeASTTransformation.groovy | 115 +++++++++++++++++++
 .../jsr223/GremlinGroovyScriptEngine.java       |  41 ++++++-
 .../InterpreterModeCustomizerProvider.java      |  37 ++++++
 .../jsr223/GremlinGroovyScriptEngineTest.java   |  52 +++++++++
 .../server/GremlinServerIntegrateTest.java      |  57 +++++++++
 7 files changed, 329 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/959378ca/CHANGELOG.asciidoc
----------------------------------------------------------------------