@Override public Object call(Object[] arguments, FuncallExpression ast, Environment env) throws EvalException, InterruptedException { if (!env.mutability().isMutable()) { throw new EvalException(getLocation(), "Trying to call in frozen environment"); } if (env.getStackTrace().contains(this)) { throw new EvalException( getLocation(), String.format( "Recursion was detected when calling '%s' from '%s'", getName(), Iterables.getLast(env.getStackTrace()).getName())); } if (enableCompiler && method.isPresent()) { Object returnValue = callCompiledFunction(arguments, ast, env); if (returnValue != null) { return returnValue; } } Profiler.instance().startTask(ProfilerTask.SKYLARK_USER_FN, getName()); try { env.enterScope(this, ast, definitionGlobals); ImmutableList<String> names = signature.getSignature().getNames(); // Registering the functions's arguments as variables in the local Environment int i = 0; for (String name : names) { env.update(name, arguments[i++]); } try { for (Statement stmt : statements) { if (stmt instanceof ReturnStatement) { // Performance optimization. // Executing the statement would throw an exception, which is slow. return ((ReturnStatement) stmt).getReturnExpression().eval(env); } else { stmt.exec(env); } } } catch (ReturnStatement.ReturnException e) { return e.getValue(); } return Runtime.NONE; } finally { Profiler.instance().completeTask(ProfilerTask.SKYLARK_USER_FN); env.exitScope(); } }
/** * Checks that this Freezable object can be mutated from the given {@link Environment}. * * @param object a Freezable object that we check is still mutable. * @param env the {@link Environment} attempting the mutation. * @throws MutabilityException when the object was frozen already, or is from another context. */ public static void checkMutable(Freezable object, Environment env) throws MutabilityException { if (!object.mutability().isMutable()) { throw new MutabilityException("trying to mutate a frozen object"); } // Consider an {@link Environment} e1, in which is created {@link UserDefinedFunction} f1, // that closes over some variable v1 bound to list l1. If somehow, via the magic of callbacks, // f1 or l1 is passed as argument to some function f2 evaluated in {@link environment} e2 // while e1 is be mutable, e2, being a different {@link Environment}, should not be // allowed to mutate objects from e1. It's a bug, that shouldn't happen in our current code // base, so we throw an AssertionError. If in the future such situations are allowed to happen, // then we should throw a MutabilityException instead. if (!object.mutability().equals(env.mutability())) { throw new AssertionError("trying to mutate an object from a different context"); } }