1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.jexl3.internal; 18 19 import java.util.Arrays; 20 21 /** 22 * A call frame, created from a scope, stores the arguments and local variables in a "stack frame" (sic). 23 * @since 3.0 24 */ 25 public final class Frame { 26 /** The scope. */ 27 private final Scope scope; 28 /** The actual stack frame. */ 29 private final Object[] stack; 30 /** Number of curried parameters. */ 31 private final int curried; 32 33 /** 34 * Creates a new frame. 35 * @param s the scope 36 * @param r the stack frame 37 * @param c the number of curried parameters 38 */ 39 Frame(final Scope s, final Object[] r, final int c) { 40 scope = s; 41 stack = r; 42 curried = c; 43 } 44 45 /** 46 * Replace any instance of a closure in this stack by its (fuzzy encoded) offset in it. 47 * <p>This is to avoid the cyclic dependency between the closure and its frame stack that 48 * may point back to it that occur with recursive function definitions.</p> 49 * @param closure the owning closure 50 * @return the cleaned-up stack or the stack itself (most of the time) 51 */ 52 Object[] nocycleStack(Closure closure) { 53 Object[] ns = stack; 54 for(int i = 0; i < stack.length; ++i) { 55 if (stack[i] == closure) { 56 if (ns == stack) { 57 ns = stack.clone(); 58 } 59 // fuzz it a little 60 ns[i] = Closure.class.hashCode() + i; 61 } 62 } 63 return ns; 64 } 65 66 /** 67 * Gets this script unbound parameters, i.e. parameters not bound through curry(). 68 * @return the parameter names 69 */ 70 public String[] getUnboundParameters() { 71 return scope.getParameters(curried); 72 } 73 74 /** 75 * Gets the scope. 76 * @return this frame scope 77 */ 78 public Scope getScope() { 79 return scope; 80 } 81 82 /** 83 * Gets a value. 84 * @param s the offset in this frame 85 * @return the stacked value 86 */ 87 Object get(final int s) { 88 return stack[s]; 89 } 90 91 /** 92 * Whether this frame defines a symbol, ie declared it and assigned it a value. 93 * @param s the offset in this frame 94 * @return true if this symbol has been assigned a value, false otherwise 95 */ 96 boolean has(final int s) { 97 return s >= 0 && s < stack.length && stack[s] != Scope.UNDECLARED; 98 } 99 100 /** 101 * Sets a value. 102 * @param r the offset in this frame 103 * @param value the value to set in this frame 104 */ 105 void set(final int r, final Object value) { 106 stack[r] = value; 107 } 108 109 /** 110 * Assign values to this frame. 111 * @param values the values 112 * @return this frame 113 */ 114 Frame assign(final Object... values) { 115 if (stack != null) { 116 final int nparm = scope.getArgCount(); 117 final Object[] copy = stack.clone(); 118 int ncopy = 0; 119 if (values != null && values.length > 0) { 120 ncopy = Math.min(nparm - curried, Math.min(nparm, values.length)); 121 System.arraycopy(values, 0, copy, curried, ncopy); 122 } 123 // unbound parameters are defined as null 124 Arrays.fill(copy, curried + ncopy, nparm, null); 125 return new Frame(scope, copy, curried + ncopy); 126 } 127 return this; 128 } 129 130 }