public Object build(Script script) {
   // this used to be synchronized, but we also used to remove the
   // metaclass.  Since adding the metaclass is now a side effect, we
   // don't need to ensure the meta-class won't be observed and don't
   // need to hide the side effect.
   MetaClass scriptMetaClass = script.getMetaClass();
   script.setMetaClass(new FactoryInterceptorMetaClass(scriptMetaClass, this));
   script.setBinding(this);
   Object oldScriptName = getProxyBuilder().getVariables().get(SCRIPT_CLASS_NAME);
   try {
     getProxyBuilder().setVariable(SCRIPT_CLASS_NAME, script.getClass().getName());
     return script.run();
   } finally {
     if (oldScriptName != null) {
       getProxyBuilder().setVariable(SCRIPT_CLASS_NAME, oldScriptName);
     } else {
       getProxyBuilder().getVariables().remove(SCRIPT_CLASS_NAME);
     }
   }
 }
  public Object eval(final Context context, final Bindings request, final Bindings... scopes)
      throws ScriptException {

    final Map<String, Object> bindings = mergeBindings(context, request, scopes);

    // Bindings so script has access to this environment.
    // Only initialize once.
    if (null == bindings.get("context")) {
      // add context to bindings
      // ctx.setAttribute("context", ctx);

      // direct output to ctx.getWriter
      // If we're wrapping with a PrintWriter here,
      // enable autoFlush because otherwise it might not get done!
      final Writer writer = engine.getWriter();
      bindings.put("out", (writer instanceof PrintWriter) ? writer : new PrintWriter(writer, true));

      // Not going to do this after all (at least for now).
      // Scripts can use context.{reader, writer, errorWriter}.
      // That is a modern version of System.{in, out, err} or
      // Console.{reader, writer}().
      //
      // // New I/O names consistent with ScriptContext and
      // java.io.Console.
      //
      // ctx.setAttribute("writer", writer, ScriptContext.ENGINE_SCOPE);
      //
      // // Direct errors to ctx.getErrorWriter
      // final Writer errorWriter = ctx.getErrorWriter();
      // ctx.setAttribute("errorWriter", (errorWriter instanceof
      // PrintWriter) ?
      // errorWriter :
      // new PrintWriter(errorWriter),
      // ScriptContext.ENGINE_SCOPE);
      //
      // // Get input from ctx.getReader
      // // We don't wrap with BufferedReader here because we expect that
      // if
      // // the host wants that they do it. Either way Groovy scripts will
      // // always have readLine because the GDK supplies it for Reader.
      // ctx.setAttribute("reader", ctx.getReader(),
      // ScriptContext.ENGINE_SCOPE);
    }

    // Fix for GROOVY-3669: Can't use several times the same JSR-223
    // ScriptContext for differents groovy script
    // if (ctx.getWriter() != null) {
    // ctx.setAttribute("out", new PrintWriter(ctx.getWriter(), true),
    // DefaultScriptContext.REQUEST_SCOPE);
    // }

    try {
      Script scriptObject = engine.createScript(scriptName, new Binding(bindings));

      // create a Map of MethodClosures from this new script object
      Method[] methods = scriptObject.getClass().getMethods();
      final Map<String, Closure> closures = new HashMap<String, Closure>();
      for (Method m : methods) {
        String name = m.getName();
        closures.put(name, new MethodClosure(scriptObject, name));
      }

      MetaClass oldMetaClass = scriptObject.getMetaClass();

      /*
       * We override the MetaClass of this script object so that we can
       * forward calls to global closures (of previous or future "eval"
       * calls) This gives the illusion of working on the same "global"
       * scope.
       */
      scriptObject.setMetaClass(
          new DelegatingMetaClass(oldMetaClass) {
            @Override
            public Object invokeMethod(Object object, String name, Object args) {
              if (args == null) {
                return invokeMethod(object, name, MetaClassHelper.EMPTY_ARRAY);
              }
              if (args instanceof Tuple) {
                return invokeMethod(object, name, ((Tuple) args).toArray());
              }
              if (args instanceof Object[]) {
                return invokeMethod(object, name, (Object[]) args);
              } else {
                return invokeMethod(object, name, new Object[] {args});
              }
            }

            @Override
            public Object invokeMethod(Object object, String name, Object[] args) {
              try {
                return super.invokeMethod(object, name, args);
              } catch (MissingMethodException mme) {
                return callGlobal(name, args, bindings);
              }
            }

            @Override
            public Object invokeStaticMethod(Object object, String name, Object[] args) {
              try {
                return super.invokeStaticMethod(object, name, args);
              } catch (MissingMethodException mme) {
                return callGlobal(name, args, bindings);
              }
            }

            private Object callGlobal(String name, Object[] args, Map<String, Object> ctx) {
              Closure closure = closures.get(name);
              if (closure != null) {
                return closure.call(args);
              } else {
                // Look for closure valued variable in the
                // given ScriptContext. If available, call it.
                Object value = ctx.get(name);
                if (value instanceof Closure) {
                  return ((Closure) value).call(args);
                } // else fall thru..
              }
              throw new MissingMethodException(name, getClass(), args);
            }
          });

      try {
        return scriptObject.run();
      } catch (Exception e) {
        throw new ScriptThrownException(e.getMessage(), e);
      }
    } catch (ScriptException e) {
      throw e;
    } catch (CompilationFailedException e) {
      throw new ScriptCompilationException(e);
    } catch (Exception e) {
      throw new ScriptException(e);
    } finally {
      // Fix for GROOVY-3669: Can't use several times the same JSR-223
      // ScriptContext for different groovy script
      // Groovy's scripting engine implementation adds those two variables
      // in the binding
      // but should clean up afterwards
      // ctx.removeAttribute("context",
      // DefaultScriptContext.REQUEST_SCOPE);
      // ctx.removeAttribute("out", DefaultScriptContext.REQUEST_SCOPE);
    }
  }