001: // Copyright (c) 1999, 2001, 2004, 2005 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.mapping.*;
007: import gnu.mapping.Location; // As opposed to gnu.bytecode.Location
008: import gnu.bytecode.*;
009:
010: /** An Expression to set (bind) or define a new value to a named variable.
011: * @author Per Bothner
012: */
013:
014: public class SetExp extends AccessExp {
015: /** The new value to assign to the variable. */
016: Expression new_value;
017:
018: public SetExp(Object symbol, Expression val) {
019: this .symbol = symbol;
020: new_value = val;
021: }
022:
023: public SetExp(Declaration decl, Expression val) {
024: this .binding = decl;
025: symbol = decl.getSymbol();
026: new_value = val;
027: }
028:
029: public static SetExp makeDefinition(Object symbol, Expression val) {
030: SetExp sexp = new SetExp(symbol, val);
031: sexp.setDefining(true);
032: return sexp;
033: }
034:
035: public static SetExp makeDefinition(Declaration decl, Expression val) {
036: SetExp sexp = new SetExp(decl, val);
037: sexp.setDefining(true);
038: return sexp;
039: }
040:
041: /** Get the Expression for calculating the new ("right-hand") value. */
042: public final Expression getNewValue() {
043: return new_value;
044: }
045:
046: public static final int DEFINING_FLAG = NEXT_AVAIL_FLAG;
047: public static final int GLOBAL_FLAG = NEXT_AVAIL_FLAG << 1;
048: public static final int PREFER_BINDING2 = NEXT_AVAIL_FLAG << 2;
049: public static final int PROCEDURE = NEXT_AVAIL_FLAG << 3;
050: public static final int SET_IF_UNBOUND = NEXT_AVAIL_FLAG << 4;
051: public static final int HAS_VALUE = NEXT_AVAIL_FLAG << 5;
052:
053: public final boolean isDefining() {
054: return (flags & DEFINING_FLAG) != 0;
055: }
056:
057: public final void setDefining(boolean value) {
058: if (value)
059: flags |= DEFINING_FLAG;
060: else
061: flags &= ~DEFINING_FLAG;
062: }
063:
064: /** True if evaluating the SetExp yields the value of the RHS. */
065: public final boolean getHasValue() {
066: return (flags & HAS_VALUE) != 0;
067: }
068:
069: public final void setHasValue(boolean value) {
070: if (value)
071: flags |= HAS_VALUE;
072: else
073: flags &= ~HAS_VALUE;
074: }
075:
076: /** True if this is a functon definition ("defun"). */
077: public final boolean isFuncDef() {
078: return (flags & PROCEDURE) != 0;
079: }
080:
081: public final void setFuncDef(boolean value) {
082: if (value)
083: flags |= PROCEDURE;
084: else
085: flags &= ~PROCEDURE;
086: }
087:
088: public final boolean isSetIfUnbound() {
089: return (flags & SET_IF_UNBOUND) != 0;
090: }
091:
092: public final void setSetIfUnbound(boolean value) {
093: if (value)
094: flags |= SET_IF_UNBOUND;
095: else
096: flags &= ~SET_IF_UNBOUND;
097: }
098:
099: protected boolean mustCompile() {
100: return false;
101: }
102:
103: public void apply(CallContext ctx) throws Throwable {
104: Environment env = ctx.getEnvironment();
105: Symbol sym = symbol instanceof Symbol ? (Symbol) symbol : env
106: .getSymbol(symbol.toString());
107: Object property = null;
108: Language language = Language.getDefaultLanguage();
109: if (isFuncDef() && language.hasSeparateFunctionNamespace())
110: property = EnvironmentKey.FUNCTION;
111: if (isSetIfUnbound()) {
112: Location loc = env.getLocation(sym, property);
113: if (!loc.isBound())
114: loc.set(new_value.eval(env));
115: if (getHasValue())
116: ctx.writeValue(loc);
117: return;
118: }
119:
120: Object new_val = new_value.eval(env);
121: if (binding != null && !(binding.context instanceof ModuleExp)) {
122: Object[] evalFrame = ctx.evalFrames[ScopeExp
123: .nesting(binding.context)];
124: if (binding.isIndirectBinding()) {
125: Location loc;
126: if (isDefining())
127: evalFrame[binding.evalIndex] = Location.make(sym);
128: loc = (Location) evalFrame[binding.evalIndex];
129: loc.set(new_value);
130: } else
131: evalFrame[binding.evalIndex] = new_val;
132: } else if (isDefining()) {
133: /*
134: if (binding != null && binding.isAlias())
135: env.addLocation(sym, null, (gnu.mapping.Location) new_val);
136: else
137: */
138: env.define(sym, property, new_val);
139: } else {
140: env.put(sym, property, new_val);
141: }
142: if (getHasValue())
143: ctx.writeValue(new_val);
144: }
145:
146: public void compile(Compilation comp, Target target) {
147: if (new_value instanceof LambdaExp
148: && target instanceof IgnoreTarget
149: && ((LambdaExp) new_value).getInlineOnly())
150: return;
151: Type type;
152: gnu.bytecode.CodeAttr code = comp.getCode();
153: // FIXME - handle isSetIfUnbound
154: boolean needValue = getHasValue()
155: && !(target instanceof IgnoreTarget);
156:
157: // set the following to true if the value has been pushed.
158: // this is used to detect not implemented cases.
159: // when all cases are implemented, remove this.
160: boolean valuePushed = false;
161:
162: // This code is kind of kludgy, because it handles a number of
163: // different cases: assignments and definitions to both local and
164: // global variables. Some of the complication is because we want
165: // to generate fields for module-level definitions; this is how
166: // bindings are exported from modules.
167:
168: Declaration decl = binding;
169: Expression declValue = decl.getValue();
170: if (decl.getFlag(Declaration.EARLY_INIT) && isDefining()
171: && !decl.ignorable()) {
172: BindingInitializer.create(decl, new_value, comp);
173: } else if (declValue instanceof LambdaExp
174: && decl.context instanceof ModuleExp
175: && (!decl.isPrivate() || declValue instanceof ClassExp)
176: && ((LambdaExp) declValue).getName() != null // FIXME
177: && declValue == new_value) {
178: ((LambdaExp) new_value).compileSetField(comp);
179: } else if (decl.context instanceof ModuleExp
180: && (decl.getFlag(Declaration.IS_CONSTANT) || decl
181: .isAlias()) && isDefining()
182: && declValue != null) { // This is handled in ModuleExp's allocFields method. But:
183: if (needValue) {
184: decl.load(this , 0, comp, Target.pushObject);
185: valuePushed = true;
186: }
187: } else {
188: AccessExp access = this ;
189: Declaration owner = contextDecl();
190: if (!isDefining()) {
191: while (decl != null && decl.isAlias()) {
192: declValue = decl.getValue();
193: if (!(declValue instanceof ReferenceExp))
194: break;
195: ReferenceExp rexp = (ReferenceExp) declValue;
196: Declaration orig = rexp.binding;
197: if (orig == null)
198: break;
199: if (owner != null && orig.needsContext())
200: break;
201: owner = rexp.contextDecl();
202: access = rexp;
203: decl = orig;
204: }
205: }
206: if (decl.ignorable())
207: new_value.compile(comp, Target.Ignore);
208: else if (decl.isAlias() && isDefining()) {
209: decl.load(this , ReferenceExp.DONT_DEREFERENCE, comp,
210: Target.pushObject);
211: ClassType locType = ClassType
212: .make("gnu.mapping.IndirectableLocation");
213: code.emitCheckcast(locType);
214: new_value.compile(comp, Target.pushObject);
215: Method meth = locType.getDeclaredMethod("setAlias", 1);
216: code.emitInvokeVirtual(meth);
217: } else if (decl.isIndirectBinding()) {
218: decl.load(access, ReferenceExp.DONT_DEREFERENCE, comp,
219: Target.pushObject);
220: if (isSetIfUnbound()) {
221: if (needValue) {
222: code.emitDup();
223: valuePushed = true;
224: }
225: code.pushScope();
226: code.emitDup();
227: Variable symLoc = code
228: .addLocal(Compilation.typeLocation);
229: code.emitStore(symLoc);
230: code.emitInvokeVirtual(Compilation.typeLocation
231: .getDeclaredMethod("isBound", 0));
232: code.emitIfIntEqZero();
233: code.emitLoad(symLoc);
234: }
235: new_value.compile(comp, Target.pushObject);
236: if (needValue && !isSetIfUnbound()) {
237: code.emitDupX();
238: valuePushed = true;
239: }
240: String setterName = "set";
241: code.emitInvokeVirtual(Compilation.typeLocation
242: .getDeclaredMethod(setterName, 1));
243: if (isSetIfUnbound()) {
244: code.emitFi();
245: code.popScope();
246: }
247: } else if (decl.isSimple()) {
248: type = decl.getType();
249: new_value.compile(comp, decl);
250: if (needValue) {
251: code.emitDup(type); // dup or dup2
252: valuePushed = true;
253: }
254: Variable var = decl.getVariable();
255: if (var == null)
256: var = decl.allocateVariable(code);
257: code.emitStore(var);
258: } else if (decl.context instanceof ClassExp
259: && decl.field == null && !getFlag(PROCEDURE)
260: && ((ClassExp) decl.context).isMakingClassPair()) {
261: String setName = ClassExp.slotToMethodName("set", decl
262: .getName());
263: ClassExp cl = (ClassExp) decl.context;
264: Method setter = cl.type.getDeclaredMethod(setName, 1);
265: cl.loadHeapFrame(comp);
266: new_value.compile(comp, decl);
267: if (needValue) {
268: code.emitDupX();
269: valuePushed = true;
270: }
271: code.emitInvoke(setter);
272: } else {
273: Field field = decl.field;
274: if (!field.getStaticFlag())
275: decl.loadOwningObject(owner, comp);
276: type = field.getType();
277: new_value.compile(comp, decl);
278: if (field.getStaticFlag()) {
279: if (needValue) {
280: code.emitDup(type);
281: valuePushed = true;
282: }
283: code.emitPutStatic(field);
284: } else {
285: if (needValue) {
286: code.emitDupX();
287: valuePushed = true;
288: }
289: code.emitPutField(field);
290: }
291: }
292: }
293:
294: if (needValue && !valuePushed)
295: throw new Error(
296: "SetExp.compile: not implemented - return value");
297:
298: if (needValue)
299: target.compileFromStack(comp, getType());
300: else
301: comp.compileConstant(Values.empty, target);
302: }
303:
304: public final gnu.bytecode.Type getType() {
305: return !getHasValue() ? Type.void_type
306: : binding == null ? Type.pointer_type : binding
307: .getType();
308: }
309:
310: protected Expression walk(ExpWalker walker) {
311: return walker.walkSetExp(this );
312: }
313:
314: protected void walkChildren(ExpWalker walker) {
315: new_value = (Expression) walker.walk(new_value);
316: }
317:
318: public void print(OutPort out) {
319: out
320: .startLogicalBlock(isDefining() ? "(Define" : "(Set",
321: ")", 2);
322: out.writeSpaceFill();
323: printLineColumn(out);
324: if (binding == null || symbol.toString() != binding.getName()) {
325: out.print('/');
326: out.print(symbol);
327: }
328: if (binding != null) {
329: out.print('/');
330: out.print(binding);
331: }
332: out.writeSpaceLinear();
333: new_value.print(out);
334: out.endLogicalBlock(")");
335: }
336:
337: public String toString() {
338: return "SetExp[" + symbol + ":=" + new_value + ']';
339: }
340: }
|