View Javadoc
1   package io.jawk.backend;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2026 MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import java.util.ArrayDeque;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.Deque;
30  import java.util.LinkedHashMap;
31  import java.util.List;
32  import java.util.Map;
33  import io.jawk.intermediate.UninitializedObject;
34  
35  /**
36   * Runtime stack used by the AVM interpreter.
37   */
38  class RuntimeStack {
39  
40  	static final UninitializedObject BLANK = new UninitializedObject();
41  	private static final Object[] NULL_LOCALS_SENTINEL = new Object[0];
42  
43  	private Object[] globals = null;
44  	private List<String> globalNamesByOffset = Collections.emptyList();
45  	private Map<String, Integer> globalOffsetsByName = Collections.emptyMap();
46  	private Object[] locals = null;
47  	private Deque<Object[]> localsStack = new ArrayDeque<Object[]>();
48  	private Deque<Integer> returnIndexes = new ArrayDeque<Integer>();
49  
50  	@SuppressWarnings("unused")
51  	public void dump() {
52  		System.out.println("globals = " + Arrays.toString(globals));
53  		System.out.println("globalNamesByOffset = " + globalNamesByOffset);
54  		System.out.println("locals = " + Arrays.toString(locals));
55  		System.out.println("localsStack = " + localsStack);
56  		System.out.println("returnIndexes = " + returnIndexes);
57  	}
58  
59  	Object[] getNumGlobals() {
60  		return globals;
61  	}
62  
63  	void reset() {
64  		clearGlobals();
65  		resetTransientState();
66  	}
67  
68  	void clearGlobals() {
69  		globals = null;
70  		globalNamesByOffset = Collections.emptyList();
71  		globalOffsetsByName = Collections.emptyMap();
72  	}
73  
74  	void resetTransientState() {
75  		locals = null;
76  		localsStack.clear();
77  		returnIndexes.clear();
78  		returnValue = null;
79  	}
80  
81  	/** Must be one of the first methods executed. */
82  	void setNumGlobals(long l, Map<String, Integer> offsetsByName) {
83  		globals = new Object[(int) l];
84  		for (int i = 0; i < l; i++) {
85  			globals[i] = null;
86  		}
87  		setGlobalLayoutMetadata((int) l, offsetsByName);
88  		// must accept multiple executions
89  		// expandFrameIfNecessary(num_globals);
90  	}
91  
92  	/*
93  	 * // this assumes globals = Object[0] upon initialization
94  	 * private void expandFrameIfNecessary(int num_globals) {
95  	 * if (num_globals == globals.length)
96  	 * // no need for expansion;
97  	 * // do nothing
98  	 * return;
99  	 * Object[] new_frame = new Object[num_globals];
100 	 * for (int i=0;i<globals.length;++i)
101 	 * new_frame[i] = globals[i];
102 	 * globals = new_frame;
103 	 * }
104 	 */
105 
106 	void rebindGlobals(List<String> namesByOffset) {
107 		globals = new Object[namesByOffset.size()];
108 		for (int i = 0; i < globals.length; i++) {
109 			globals[i] = null;
110 		}
111 		setGlobalLayoutMetadata(namesByOffset);
112 	}
113 
114 	Map<String, Object> snapshotGlobalVariables() {
115 		Map<String, Object> snapshot = new LinkedHashMap<String, Object>();
116 		if (globals == null) {
117 			return snapshot;
118 		}
119 		for (int i = 0; i < globals.length && i < globalNamesByOffset.size(); i++) {
120 			String name = globalNamesByOffset.get(i);
121 			if (name != null) {
122 				snapshot.put(name, globals[i]);
123 			}
124 		}
125 		return snapshot;
126 	}
127 
128 	boolean hasGlobalVariable(String name) {
129 		return globalOffsetsByName.containsKey(name);
130 	}
131 
132 	Object getGlobalVariable(String name) {
133 		Integer offset = globalOffsetsByName.get(name);
134 		return offset == null ? null : globals[offset.intValue()];
135 	}
136 
137 	void setGlobalVariable(String name, Object value) {
138 		Integer offset = globalOffsetsByName.get(name);
139 		if (offset != null) {
140 			globals[offset.intValue()] = value;
141 		}
142 	}
143 
144 	String getGlobalName(int offset) {
145 		return offset >= 0 && offset < globalNamesByOffset.size() ? globalNamesByOffset.get(offset) : null;
146 	}
147 
148 	Object getVariable(long offset, boolean isGlobal) {
149 		if (isGlobal) {
150 			return globals[(int) offset];
151 		} else {
152 			return locals[(int) offset];
153 		}
154 	}
155 
156 	Object setVariable(long offset, Object val, boolean isGlobal) {
157 		if (isGlobal) {
158 			globals[(int) offset] = val;
159 			return val;
160 		} else {
161 			locals[(int) offset] = val;
162 			return val;
163 		}
164 	}
165 
166 	// for _DELETE_ARRAY_
167 	void removeVariable(long offset, boolean isGlobal) {
168 		if (isGlobal) {
169 			globals[(int) offset] = null;
170 		} else {
171 			locals[(int) offset] = null;
172 		}
173 	}
174 
175 	void setFilelistVariable(int offset, Object value) {
176 		globals[offset] = value;
177 	}
178 
179 	void pushFrame(long numFormalParams, int positionIdx) {
180 		localsStack.push(locals == null ? NULL_LOCALS_SENTINEL : locals);
181 		locals = new Object[(int) numFormalParams];
182 		returnIndexes.push(positionIdx);
183 	}
184 
185 	/** returns the position index */
186 	int popFrame() {
187 		Object[] restoredLocals = localsStack.pop();
188 		locals = restoredLocals == NULL_LOCALS_SENTINEL ? null : restoredLocals;
189 		return returnIndexes.pop();
190 	}
191 
192 	void popAllFrames() {
193 		for (int i = localsStack.size(); i > 0; i--) {
194 			Object[] restoredLocals = localsStack.pop();
195 			locals = restoredLocals == NULL_LOCALS_SENTINEL ? null : restoredLocals;
196 			returnIndexes.pop();
197 		}
198 	}
199 
200 	private Object returnValue;
201 
202 	void setReturnValue(Object obj) {
203 		returnValue = obj;
204 	}
205 
206 	Object getReturnValue() {
207 		Object retval;
208 		if (returnValue == null) {
209 			retval = BLANK;
210 		} else {
211 			retval = returnValue;
212 		}
213 		returnValue = null;
214 		return retval;
215 	}
216 
217 	private void setGlobalLayoutMetadata(int numGlobals, Map<String, Integer> offsetsByName) {
218 		List<String> namesByOffset = new ArrayList<String>(Collections.nCopies(numGlobals, (String) null));
219 		if (offsetsByName != null) {
220 			for (Map.Entry<String, Integer> entry : offsetsByName.entrySet()) {
221 				int offset = entry.getValue().intValue();
222 				if (offset >= 0 && offset < numGlobals) {
223 					namesByOffset.set(offset, entry.getKey());
224 				}
225 			}
226 		}
227 		setGlobalLayoutMetadata(namesByOffset);
228 	}
229 
230 	private void setGlobalLayoutMetadata(List<String> namesByOffset) {
231 		globalNamesByOffset = new ArrayList<String>(namesByOffset);
232 		Map<String, Integer> offsetsByName = new LinkedHashMap<String, Integer>();
233 		for (int i = 0; i < namesByOffset.size(); i++) {
234 			String name = namesByOffset.get(i);
235 			if (name != null) {
236 				offsetsByName.put(name, Integer.valueOf(i));
237 			}
238 		}
239 		globalOffsetsByName = offsetsByName;
240 	}
241 }