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 * Assign values to this frame. 47 * @param values the values 48 * @return this frame 49 */ 50 Frame assign(final Object... values) { 51 if (stack != null) { 52 final int nparm = scope.getArgCount(); 53 final Object[] copy = stack.clone(); 54 int ncopy = 0; 55 if (values != null && values.length > 0) { 56 ncopy = Math.min(nparm - curried, Math.min(nparm, values.length)); 57 System.arraycopy(values, 0, copy, curried, ncopy); 58 } 59 // unbound parameters are defined as null 60 Arrays.fill(copy, curried + ncopy, nparm, null); 61 return new Frame(scope, copy, curried + ncopy); 62 } 63 return this; 64 } 65 66 /** 67 * Gets a value. 68 * @param s the offset in this frame 69 * @return the stacked value 70 */ 71 Object get(final int s) { 72 return stack[s]; 73 } 74 75 /** 76 * Gets the scope. 77 * @return this frame scope 78 */ 79 public Scope getScope() { 80 return scope; 81 } 82 83 /** 84 * Gets this script unbound parameters, i.e. parameters not bound through curry(). 85 * @return the parameter names 86 */ 87 public String[] getUnboundParameters() { 88 return scope.getParameters(curried); 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 * Replace any instance of a closure in this stack by its (fuzzy encoded) offset in it. 102 * <p>This is to avoid the cyclic dependency between the closure and its frame stack that 103 * may point back to it that occur with recursive function definitions.</p> 104 * @param closure the owning closure 105 * @return the cleaned-up stack or the stack itself (most of the time) 106 */ 107 Object[] nocycleStack(final Closure closure) { 108 Object[] ns = stack; 109 for(int i = 0; i < stack.length; ++i) { 110 if (stack[i] == closure) { 111 if (ns == stack) { 112 ns = stack.clone(); 113 } 114 // fuzz it a little 115 ns[i] = Closure.class.hashCode() + i; 116 } 117 } 118 return ns; 119 } 120 121 /** 122 * Sets a value. 123 * @param r the offset in this frame 124 * @param value the value to set in this frame 125 */ 126 void set(final int r, final Object value) { 127 stack[r] = value; 128 } 129 130 }