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    *      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 }