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 * https://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 import java.util.concurrent.atomic.AtomicReference;
21
22 /**
23 * A call frame, created from a scope, stores the arguments and local variables in a "stack frame" (sic).
24 * @since 3.0
25 */
26 public class Frame {
27 /** The scope. */
28 private final Scope scope;
29 /** The actual stack frame. */
30 protected final Object[] stack;
31 /** Number of curried parameters. */
32 private final int curried;
33
34 /**
35 * Creates a new frame.
36 * @param s the scope
37 * @param r the stack frame
38 * @param c the number of curried parameters
39 */
40 protected Frame(final Scope s, final Object[] r, final int c) {
41 scope = s;
42 stack = r;
43 curried = c;
44 final String[] symbols = scope.getSymbols();
45 if (symbols.length != r.length) {
46 throw new IllegalArgumentException("Scope and stack frame size mismatch: "
47 + symbols.length + " != " + r.length);
48 }
49 }
50
51 /**
52 * Assign values to this frame.
53 * @param values the values
54 * @return this frame
55 */
56 Frame assign(final Object... values) {
57 if (stack != null) {
58 final int nparm = scope.getArgCount();
59 final Object[] copy = stack.clone();
60 int ncopy = 0;
61 if (values != null && values.length > 0) {
62 ncopy = Math.min(nparm - curried, Math.min(nparm, values.length));
63 System.arraycopy(values, 0, copy, curried, ncopy);
64 }
65 // unbound parameters are defined as null
66 Arrays.fill(copy, curried + ncopy, nparm, null);
67 return newFrame(scope, copy, curried + ncopy);
68 }
69 return this;
70 }
71
72 /**
73 * Creates a new from of this frame"s class.
74 * @param s the scope
75 * @param r the arguments
76 * @param c the number of curried parameters
77 * @return a new instance of frame
78 */
79 Frame newFrame(final Scope s, final Object[] r, final int c) {
80 return new Frame(s, r, c);
81 }
82
83 /**
84 * Captures a value.
85 * @param s the offset in this frame
86 * @param lexical true if this captured symbol is redefined locally
87 * @return the stacked value
88 */
89 Object capture(final int s, final boolean lexical) {
90 return stack[s];
91 }
92
93 /**
94 * Gets a value.
95 * @param s the offset in this frame
96 * @return the stacked value
97 */
98 Object get(final int s) {
99 return stack[s];
100 }
101
102 /**
103 * Sets a value.
104 * @param r the offset in this frame
105 * @param value the value to set in this frame
106 */
107 void set(final int r, final Object value) {
108 stack[r] = value;
109 }
110
111 /**
112 * Gets the scope.
113 * @return this frame scope
114 */
115 public Scope getScope() {
116 return scope;
117 }
118
119 /**
120 * Gets this script unbound parameters, i.e. parameters not bound through curry().
121 * @return the parameter names
122 */
123 public String[] getUnboundParameters() {
124 return scope.getParameters(curried);
125 }
126
127 /**
128 * Tests whether this frame defines a symbol, ie declared it and assigned it a value.
129 * @param s the offset in this frame
130 * @return true if this symbol has been assigned a value, false otherwise
131 */
132 boolean has(final int s) {
133 return s >= 0 && s < stack.length && stack[s] != Scope.UNDECLARED;
134 }
135
136 /**
137 * Replace any instance of a closure in this stack by its (fuzzy encoded) offset in it.
138 * <p>This is to avoid the cyclic dependency between the closure and its frame stack that
139 * may point back to it that occur with recursive function definitions.</p>
140 * @param closure the owning closure
141 * @return the cleaned-up stack or the stack itself (most of the time)
142 */
143 Object[] nocycleStack(final Closure closure) {
144 Object[] ns = stack;
145 for(int i = 0; i < stack.length; ++i) {
146 if (stack[i] == closure) {
147 if (ns == stack) {
148 ns = stack.clone();
149 }
150 // fuzz it a little
151 ns[i] = Closure.class.hashCode() + i;
152 }
153 }
154 return ns;
155 }
156 }
157
158 /**
159 * Pass-by-reference frame.
160 */
161 class ReferenceFrame extends Frame {
162 ReferenceFrame(final Scope s, final Object[] r, final int c) {
163 super(s, r, c);
164 }
165
166 @Override
167 Frame newFrame(final Scope s, final Object[] r, final int c) {
168 return new ReferenceFrame(s, r, c);
169 }
170
171 @Override
172 Object capture(final int s, final boolean lexical) {
173 final Object o = stack[s];
174 if (o instanceof CaptureReference) {
175 final CaptureReference ref = (CaptureReference) o;
176 // if the captured symbol is lexical, it is redefined locally; a new reference is needed to share it with callees
177 // otherwise share the reference
178 return lexical ? (stack[s] = new CaptureReference(ref.get())) : ref;
179 }
180 // capture the register, wrap the value in a reference to share it with callees
181 return stack[s] = new CaptureReference(o);
182 }
183
184 @Override
185 Object get(final int s) {
186 final Object o = stack[s];
187 return o instanceof CaptureReference ? ((CaptureReference) o).get() : o;
188 }
189
190 @Override
191 void set(final int r, final Object value) {
192 final Object o = stack[r];
193 if (o instanceof CaptureReference) {
194 if (value != Scope.UNDEFINED && value != Scope.UNDECLARED) {
195 ((CaptureReference) o).set(value);
196 }
197 } else {
198 stack[r] = value;
199 }
200 }
201 }
202
203 /**
204 * Captured variable reference.
205 */
206 class CaptureReference extends AtomicReference<Object> {
207 CaptureReference(final Object o) {
208 super(o);
209 }
210 }