View Javadoc
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 }