001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.instruct.TailCall;
004: import net.sf.saxon.instruct.TailCallReturner;
005: import net.sf.saxon.om.Item;
006: import net.sf.saxon.om.NamePool;
007: import net.sf.saxon.om.SequenceIterator;
008: import net.sf.saxon.om.ValueRepresentation;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.ItemType;
011: import net.sf.saxon.type.SchemaType;
012: import net.sf.saxon.type.TypeHierarchy;
013: import net.sf.saxon.value.Value;
014:
015: import java.io.PrintStream;
016:
017: /**
018: * A LetExpression is modelled on the XQuery syntax let $x := expr return expr. This syntax
019: * is not available in the surface XPath language, but it is used internally in an optimized
020: * expression tree.
021: */
022:
023: public class LetExpression extends Assignation implements
024: TailCallReturner {
025:
026: // This integer holds an approximation to the number of times that the declared variable is referenced.
027: // The value 1 means there is only one reference and it is not in a loop, which means that the value will
028: // not be retained in memory. If there are multiple references or references within a loop, the value will
029: // be a small integer > 1. The special value FILTERED indicates that there is a reference within a loop
030: // in the form $x[predicate], which indicates that the value should potentially be indexable.
031: int refCount;
032:
033: public LetExpression() {
034: }
035:
036: /**
037: * Type-check the expression
038: */
039:
040: public Expression typeCheck(StaticContext env,
041: ItemType contextItemType) throws XPathException {
042:
043: if (declaration == null) {
044: // we've already done the type checking, no need to do it again
045: return this ;
046: }
047:
048: // The order of events is critical here. First we ensure that the type of the
049: // sequence expression is established. This is used to establish the type of the variable,
050: // which in turn is required when type-checking the action part.
051:
052: sequence = sequence.typeCheck(env, contextItemType);
053:
054: RoleLocator role = new RoleLocator(RoleLocator.VARIABLE,
055: new Integer(nameCode), 0, env.getNamePool());
056: role.setSourceLocator(this );
057: sequence = TypeChecker.strictTypeCheck(sequence, declaration
058: .getRequiredType(), role, env);
059: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
060: final ItemType actualItemType = sequence.getItemType(th);
061: declaration.refineTypeInformation(actualItemType, sequence
062: .getCardinality(),
063: (sequence instanceof Value ? (Value) sequence : null),
064: sequence.getSpecialProperties(), env);
065:
066: action = action.typeCheck(env, contextItemType);
067: return this ;
068: }
069:
070: /**
071: * Perform optimisation of an expression and its subexpressions.
072: * <p/>
073: * <p>This method is called after all references to functions and variables have been resolved
074: * to the declaration of the function or variable, and after all type checking has been done.</p>
075: *
076: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
077: * different optimization strategies to be used in different circumstances.
078: * @param env the static context of the expression
079: * @param contextItemType the static type of "." at the point where this expression is invoked.
080: * The parameter is set to null if it is known statically that the context item will be undefined.
081: * If the type of the context item is not known statically, the argument is set to
082: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
083: * @return the original expression, rewritten if appropriate to optimize execution
084: * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
085: * (typically a type error)
086: */
087:
088: public Expression optimize(Optimizer opt, StaticContext env,
089: ItemType contextItemType) throws XPathException {
090: if (declaration != null) {
091: refCount = declaration.getReferenceCount(this , env);
092: if (refCount == 0) {
093: // variable is not used - no need to evaluate it
094: return action;
095: }
096: declaration = null; // let the garbage collector take it
097: }
098:
099: int tries = 0;
100: while (tries++ < 5) {
101: Expression seq2 = sequence.optimize(opt, env,
102: contextItemType);
103: if (seq2 == sequence) {
104: break;
105: }
106: sequence = seq2;
107: adoptChildExpression(sequence);
108: resetStaticProperties();
109: }
110:
111: tries = 0;
112: while (tries++ < 5) {
113: Expression act2 = action
114: .optimize(opt, env, contextItemType);
115: if (act2 == action) {
116: break;
117: }
118: action = act2;
119: adoptChildExpression(action);
120: resetStaticProperties();
121: }
122:
123: // Try to promote any WHERE clause appearing within the LET expression
124:
125: Expression p = promoteWhereClause(null);
126: if (p != null) {
127: return p;
128: }
129:
130: return this ;
131: }
132:
133: /**
134: * Check that any elements and attributes constructed or returned by this expression are acceptable
135: * in the content model of a given complex type. It's always OK to say yes, since the check will be
136: * repeated at run-time. The process of checking element and attribute constructors against the content
137: * model of a complex type also registers the type of content expected of those constructors, so the
138: * static validation can continue recursively.
139: */
140:
141: public void checkPermittedContents(SchemaType parentType,
142: StaticContext env, boolean whole) throws XPathException {
143: action.checkPermittedContents(parentType, env, whole);
144: }
145:
146: /**
147: * Iterate over the sequence of values
148: */
149:
150: public SequenceIterator iterate(XPathContext context)
151: throws XPathException {
152: // minimize stack consumption by evaluating nested LET expressions iteratively
153: LetExpression let = this ;
154: while (true) {
155: ValueRepresentation val = let.eval(context);
156: context.setLocalVariable(let.slotNumber, val);
157: if (let.action instanceof LetExpression) {
158: let = (LetExpression) let.action;
159: } else {
160: break;
161: }
162: }
163: return let.action.iterate(context);
164: }
165:
166: /**
167: * Evaluate the variable. (This is overridden in a subclass).
168: */
169:
170: protected ValueRepresentation eval(XPathContext context)
171: throws XPathException {
172: //System.err.println("Let line " + getLineNumber());
173: return ExpressionTool.lazyEvaluate(sequence, context, refCount);
174: }
175:
176: /**
177: * Evaluate the expression as a singleton
178: */
179:
180: public Item evaluateItem(XPathContext context)
181: throws XPathException {
182: // minimize stack consumption by evaluating nested LET expressions iteratively
183: LetExpression let = this ;
184: while (true) {
185: ValueRepresentation val = let.eval(context);
186: context.setLocalVariable(let.slotNumber, val);
187: if (let.action instanceof LetExpression) {
188: let = (LetExpression) let.action;
189: } else {
190: break;
191: }
192: }
193: return let.action.evaluateItem(context);
194: }
195:
196: /**
197: * Process this expression as an instruction, writing results to the current
198: * outputter
199: */
200:
201: public void process(XPathContext context) throws XPathException {
202: // minimize stack consumption by evaluating nested LET expressions iteratively
203: LetExpression let = this ;
204: while (true) {
205: ValueRepresentation val = let.eval(context);
206: context.setLocalVariable(let.slotNumber, val);
207: if (let.action instanceof LetExpression) {
208: let = (LetExpression) let.action;
209: } else {
210: break;
211: }
212: }
213: let.action.process(context);
214: }
215:
216: /**
217: * Determine the data type of the items returned by the expression, if possible
218: * @return one of the values Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
219: * or Type.ITEM (meaning not known in advance)
220: * @param th
221: */
222:
223: public ItemType getItemType(TypeHierarchy th) {
224: return action.getItemType(th);
225: }
226:
227: /**
228: * Determine the static cardinality of the expression
229: */
230:
231: public int computeCardinality() {
232: return action.getCardinality();
233: }
234:
235: /**
236: * Get the static properties of this expression (other than its type). The result is
237: * bit-signficant. These properties are used for optimizations. In general, if
238: * property bit is set, it is true, but if it is unset, the value is unknown.
239: */
240:
241: public int computeSpecialProperties() {
242: return action.getSpecialProperties();
243: }
244:
245: /**
246: * Mark tail function calls
247: */
248:
249: public boolean markTailFunctionCalls() {
250: return ExpressionTool.markTailFunctionCalls(action);
251: }
252:
253: /**
254: * Promote this expression if possible
255: */
256:
257: public Expression promote(PromotionOffer offer)
258: throws XPathException {
259: Expression exp = offer.accept(this );
260: if (exp != null) {
261: return exp;
262: } else {
263: // pass the offer on to the sequence expression
264: sequence = doPromotion(sequence, offer);
265: if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES
266: || offer.action == PromotionOffer.UNORDERED
267: || offer.action == PromotionOffer.REPLACE_CURRENT) {
268: action = doPromotion(action, offer);
269: } else if (offer.action == PromotionOffer.RANGE_INDEPENDENT
270: // || offer.action == PromotionOffer.WHERE_CLAUSE
271: ) {
272: // Pass the offer to the action expression only if the action isn't depending on the
273: // variable bound by this let expression
274: Binding[] savedBindingList = offer.bindingList;
275: Binding[] newBindingList = new Binding[offer.bindingList.length + 1];
276: System.arraycopy(offer.bindingList, 0, newBindingList,
277: 0, offer.bindingList.length);
278: newBindingList[offer.bindingList.length] = this ;
279: offer.bindingList = newBindingList;
280: action = doPromotion(action, offer);
281: offer.bindingList = savedBindingList;
282: }
283: // if this results in the expression (let $x := $y return Z), replace all references to
284: // to $x by references to $y in the Z part, and eliminate this LetExpression by
285: // returning the action part.
286: if (sequence instanceof VariableReference) {
287: replaceVariable(offer.getOptimizer(), sequence);
288: return action;
289: }
290: // similarly, for (let $x := lazy($y) return Z)
291: if (sequence instanceof LazyExpression
292: && ((LazyExpression) sequence).getBaseExpression() instanceof VariableReference) {
293: replaceVariable(offer.getOptimizer(),
294: ((LazyExpression) sequence).getBaseExpression());
295: return action;
296: }
297:
298: return this ;
299: }
300: }
301:
302: private void replaceVariable(Optimizer opt, Expression seq)
303: throws XPathException {
304: PromotionOffer offer2 = new PromotionOffer(opt);
305: offer2.action = PromotionOffer.INLINE_VARIABLE_REFERENCES;
306: Binding[] bindingList = { this };
307: offer2.bindingList = bindingList;
308: offer2.containingExpression = seq;
309: action = doPromotion(action, offer2);
310: if (offer2.accepted) {
311: // there might be further references to the variable
312: offer2.accepted = false;
313: replaceVariable(opt, seq);
314: }
315: }
316:
317: /**
318: * ProcessLeavingTail: called to do the real work of this instruction.
319: * The results of the instruction are written
320: * to the current Receiver, which can be obtained via the Controller.
321: *
322: * @param context The dynamic context of the transformation, giving access to the current node,
323: * the current variables, etc.
324: * @return null if the instruction has completed execution; or a TailCall indicating
325: * a function call or template call that is delegated to the caller, to be made after the stack has
326: * been unwound so as to save stack space.
327: */
328:
329: public TailCall processLeavingTail(XPathContext context)
330: throws XPathException {
331: // minimize stack consumption by evaluating nested LET expressions iteratively
332: LetExpression let = this ;
333: while (true) {
334: ValueRepresentation val = let.eval(context);
335: context.setLocalVariable(let.slotNumber, val);
336: if (let.action instanceof LetExpression) {
337: let = (LetExpression) let.action;
338: } else {
339: break;
340: }
341: }
342: if (let.action instanceof TailCallReturner) {
343: return ((TailCallReturner) let.action)
344: .processLeavingTail(context);
345: } else {
346: let.action.process(context);
347: return null;
348: }
349: }
350:
351: /**
352: * Diagnostic print of expression structure
353: */
354:
355: public void display(int level, NamePool pool, PrintStream out) {
356: out.println(ExpressionTool.indent(level) + "let $"
357: + getVariableName(pool) + "[refCount=" + refCount
358: + "] :=");
359: sequence.display(level + 1, pool, out);
360: out.println(ExpressionTool.indent(level) + "return");
361: action.display(level + 1, pool, out);
362: }
363:
364: }
365:
366: //
367: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
368: // you may not use this file except in compliance with the License. You may obtain a copy of the
369: // License at http://www.mozilla.org/MPL/
370: //
371: // Software distributed under the License is distributed on an "AS IS" basis,
372: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
373: // See the License for the specific language governing rights and limitations under the License.
374: //
375: // The Original Code is: all this file.
376: //
377: // The Initial Developer of the Original Code is Michael H. Kay
378: //
379: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
380: //
381: // Contributor(s): none.
382: //
|