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