001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.instruct.InstructionDetails;
004: import net.sf.saxon.instruct.UserFunction;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.trace.InstructionInfo;
007: import net.sf.saxon.trace.InstructionInfoProvider;
008: import net.sf.saxon.trace.Location;
009: import net.sf.saxon.trans.DynamicError;
010: import net.sf.saxon.trans.XPathException;
011: import net.sf.saxon.type.AnyItemType;
012: import net.sf.saxon.type.ItemType;
013: import net.sf.saxon.type.TypeHierarchy;
014: import net.sf.saxon.value.*;
015:
016: import java.io.PrintStream;
017:
018: /**
019: * This class represents a call to a function defined in the stylesheet or query.
020: * It is used for all user-defined functions in XQuery, and for a limited class of
021: * user-defined functions in XSLT: those that can be reduced to the evaluation
022: * of a single expression.
023: */
024:
025: public class UserFunctionCall extends FunctionCall implements
026: InstructionInfoProvider {
027:
028: private SequenceType staticType;
029: private UserFunction function;
030: private boolean tailRecursive = false;
031: private boolean confirmed = false;
032:
033: // A functionCall is confirmed if the function being called has been located. Generally in this
034: // case the value of 'function' will be non-null; but in XSLT it is possible to look-ahead to confirm
035: // a function binding before the relevant function is actually compiled.
036:
037: public UserFunctionCall() {
038: }
039:
040: /**
041: * Set the static type
042: */
043:
044: public void setStaticType(SequenceType type) {
045: staticType = type;
046: }
047:
048: /**
049: * Create the reference to the function to be called, and validate for consistency
050: */
051:
052: public void setFunction(UserFunction compiledFunction,
053: StaticContext env) throws XPathException {
054: function = compiledFunction;
055: confirmed = true;
056: }
057:
058: /**
059: * Check the function call against the declared function signature
060: */
061:
062: public void checkFunctionCall(UserFunction compiledFunction,
063: StaticContext env) throws XPathException {
064: int n = compiledFunction.getNumberOfArguments();
065: for (int i = 0; i < n; i++) {
066: RoleLocator role = new RoleLocator(
067: RoleLocator.FUNCTION,
068: new Integer(compiledFunction.getFunctionNameCode()),
069: i, env.getNamePool());
070: role.setSourceLocator(this );
071: argument[i] = TypeChecker.staticTypeCheck(argument[i],
072: compiledFunction.getArgumentType(i), false, role,
073: env);
074: }
075: }
076:
077: /**
078: * Get the function that is being called by this function call
079: */
080:
081: public UserFunction getFunction() {
082: return function;
083: }
084:
085: /**
086: * Set this function as confirmed (the function being called is known to exist) or not
087: */
088:
089: public void setConfirmed(boolean conf) {
090: confirmed = conf;
091: }
092:
093: /**
094: * Determine whether this function call is confirmed
095: */
096:
097: public boolean isConfirmed() {
098: return confirmed;
099: }
100:
101: /**
102: * Get the arguments (the expressions supplied in the function call)
103: */
104:
105: public Expression[] getArguments() {
106: return argument;
107: }
108:
109: /**
110: * Method called during the type checking phase
111: */
112:
113: public void checkArguments(StaticContext env) throws XPathException {
114: // these checks are now done in setFunction(), at the time when the function
115: // call is bound to an actual function
116: }
117:
118: /**
119: * Pre-evaluate a function at compile time. This version of the method suppresses
120: * early evaluation by doing nothing.
121: */
122:
123: public Expression preEvaluate(StaticContext env) {
124: return this ;
125: }
126:
127: /**
128: * Determine the data type of the expression, if possible
129: * @return Type.ITEM (meaning not known in advance)
130: * @param th
131: */
132:
133: public ItemType getItemType(TypeHierarchy th) {
134: if (staticType == null) {
135: // the actual type is not known yet, so we return an approximation
136: return AnyItemType.getInstance();
137: } else {
138: return staticType.getPrimaryType();
139: }
140: }
141:
142: public int getIntrinsicDependencies() {
143: return StaticProperty.DEPENDS_ON_USER_FUNCTIONS;
144: }
145:
146: /**
147: * Determine the cardinality of the result
148: */
149:
150: public int computeCardinality() {
151: if (staticType == null) {
152: // the actual type is not known yet, so we return an approximation
153: return StaticProperty.ALLOWS_ZERO_OR_MORE;
154: } else {
155: return staticType.getCardinality();
156: }
157: }
158:
159: /**
160: * Simplify the function call
161: */
162:
163: public Expression simplify(StaticContext env) throws XPathException {
164: for (int i = 0; i < argument.length; i++) {
165: argument[i] = argument[i].simplify(env);
166: }
167: return this ;
168: }
169:
170: /**
171: * Mark tail-recursive calls on stylesheet functions. For most expressions, this does nothing.
172: */
173:
174: public boolean markTailFunctionCalls() {
175: tailRecursive = true;
176: return true;
177: }
178:
179: // TODO: attempt to establish whether the function is capable of creating new nodes. This
180: // enables non-creative functions to be moved out of loops. The problem is how to achieve this
181: // without looping in the case of recursive functions. A simple solution might be to go only
182: // one level deep: if the body of a function is known (without analysing function calls) to be
183: // non-creative, then all calls on that function can be marked as non-creative. Note also that
184: // a function is creative if one of its arguments is creative and the result of the function
185: // depends on the identity of that argument.
186:
187: /**
188: * Call the function, returning the value as an item. This method will be used
189: * only when the cardinality is zero or one. If the function is tail recursive,
190: * it returns an Object representing the arguments to the next (recursive) call
191: */
192:
193: public Item evaluateItem(XPathContext c) throws XPathException {
194: ValueRepresentation val = callFunction(c);
195: if (val instanceof Item) {
196: return (Item) val;
197: } else {
198: return Value.getIterator(val).next();
199: }
200: }
201:
202: /**
203: * Call the function, returning an iterator over the results. (But if the function is
204: * tail recursive, it returns an iterator over the arguments of the recursive call)
205: */
206:
207: public SequenceIterator iterate(XPathContext c)
208: throws XPathException {
209: ValueRepresentation result = callFunction(c);
210: if (result instanceof FunctionCallPackage) {
211: return SingletonIterator
212: .makeIterator(((FunctionCallPackage) result));
213: }
214: return Value.getIterator(result);
215: }
216:
217: /**
218: * This is the method that actually does the function call
219: * @param c the dynamic context
220: * @return the result of the function
221: * @throws XPathException if dynamic errors occur
222: */
223: private ValueRepresentation callFunction(XPathContext c)
224: throws XPathException {
225: int numArgs = argument.length;
226:
227: ValueRepresentation[] actualArgs = new ValueRepresentation[numArgs];
228: for (int i = 0; i < numArgs; i++) {
229: // Decide what form of lazy evaluation to use based on the number of references to the argument
230: int refs = function.getParameterDefinitions()[i]
231: .getReferenceCount();
232: if (argument[i] instanceof Value) {
233: actualArgs[i] = (Value) argument[i];
234: } else {
235: if (refs == 0) {
236: // the argument is never referenced, so don't evaluate it
237: actualArgs[i] = EmptySequence.getInstance();
238: } else if ((argument[i].getDependencies() & StaticProperty.DEPENDS_ON_USER_FUNCTIONS) != 0) {
239: // if the argument contains a call to a user-defined function, then it might be a recursive call.
240: // It's better to evaluate it now, rather than waiting until we are on a new stack frame, as
241: // that can blow the stack if done repeatedly.
242: actualArgs[i] = ExpressionTool.eagerEvaluate(
243: argument[i], c);
244: } else {
245: actualArgs[i] = ExpressionTool.lazyEvaluate(
246: argument[i], c, refs);
247: }
248: }
249: // If the argument has come in as a (non-memo) closure but there are multiple references to it,
250: // then we materialize it in memory now. This shouldn't really happen but it does (tour.xq)
251: if (refs > 1 && actualArgs[i] instanceof Closure
252: && !(actualArgs[i] instanceof MemoClosure)) {
253: actualArgs[i] = ((Closure) actualArgs[i]).reduce();
254: }
255: }
256:
257: if (tailRecursive) {
258: return new FunctionCallPackage(function, actualArgs, c);
259: }
260:
261: XPathContextMajor c2 = c.newCleanContext();
262: c2.setOrigin(this );
263: try {
264: return function.call(actualArgs, c2, true);
265: } catch (StackOverflowError err) {
266: throw new DynamicError(
267: "Too many nested function calls. May be due to infinite recursion.",
268: this );
269: } catch (NullPointerException err) {
270: throw new NullPointerException("Unbound function call "
271: + c2.getConfiguration().getNamePool()
272: .getDisplayName(getFunctionNameCode()));
273: }
274: }
275:
276: /**
277: * Call the function dynamically. For this to be possible, the static arguments of the function call
278: * must have been set up as SuppliedParameterReference objects. The actual arguments are placed on the
279: * callee's stack, and the type conversion takes place "in situ".
280: */
281:
282: public ValueRepresentation dynamicCall(
283: ValueRepresentation[] suppliedArguments,
284: XPathContext context) throws XPathException {
285: ValueRepresentation[] convertedArgs = new ValueRepresentation[suppliedArguments.length];
286: XPathContextMajor c2 = context.newCleanContext();
287: c2.setOrigin(this );
288: c2.setCaller(context);
289: c2.openStackFrame(suppliedArguments.length);
290: for (int i = 0; i < suppliedArguments.length; i++) {
291: c2.setLocalVariable(i, suppliedArguments[i]);
292: convertedArgs[i] = ExpressionTool.lazyEvaluate(argument[i],
293: c2, 10);
294: }
295: XPathContextMajor c3 = c2.newCleanContext();
296: c3.setOrigin(this );
297: return function.call(convertedArgs, c3, true);
298: }
299:
300: public void display(int level, NamePool pool, PrintStream out) {
301: out.println(ExpressionTool.indent(level) + "function "
302: + getDisplayName(pool)
303: + (tailRecursive ? " (tail call)" : ""));
304: for (int a = 0; a < argument.length; a++) {
305: argument[a].display(level + 1, pool, out);
306: }
307: }
308:
309: /**
310: * Get diagnostic information about this expression
311: */
312:
313: public InstructionInfo getInstructionInfo() {
314: InstructionDetails details = new InstructionDetails();
315: details.setConstructType(Location.FUNCTION_CALL);
316: details.setLineNumber(getLineNumber());
317: details.setSystemId(getSystemId());
318: details.setObjectNameCode(getFunctionNameCode());
319: details.setProperty("expression", this );
320: details.setProperty("target", function);
321: return details;
322: }
323:
324: /**
325: * Inner class used to wrap up the set of actual arguments to a tail-recursive call of
326: * the containing function. This argument package is passed back to the calling code
327: * in place of a function result; the caller then loops to re-invoke the function
328: * with these arguments, avoiding the creation of an additional stack frame.
329: */
330:
331: public class FunctionCallPackage extends ObjectValue {
332:
333: private UserFunction function;
334: private ValueRepresentation[] actualArgs;
335: private XPathContext evaluationContext;
336:
337: public FunctionCallPackage(UserFunction function,
338: ValueRepresentation[] actualArgs, XPathContext c) {
339: super (function);
340: this .function = function;
341: this .actualArgs = actualArgs;
342: this .evaluationContext = c;
343: }
344:
345: /**
346: * Determine the item type of the expression
347: * @param th
348: */
349:
350: public ItemType getItemType(TypeHierarchy th) {
351: return UserFunctionCall.this .getItemType(th);
352: }
353:
354: public ValueRepresentation call() throws XPathException {
355: XPathContextMajor c2 = evaluationContext.newCleanContext();
356: c2.setOrigin(UserFunctionCall.this );
357: return function.call(actualArgs, c2, false);
358: }
359:
360: public SequenceIterator iterateResults(XPathContext context)
361: throws XPathException {
362: ValueRepresentation result = call();
363: return new MappingIterator(Value.getIterator(result),
364: Flattener.THE_INSTANCE, context);
365: }
366:
367: // public ValueRepresentation appendTo(SequenceReceiver out) throws XPathException {
368: // // This method has been combined with the only method that called it (Instruction.appendItem())
369: // ValueRepresentation v = call();
370: // SequenceIterator fv = Value.getIterator(v);
371: // while (true) {
372: // Item fvit = fv.next();
373: // if (fvit == null) return null;
374: // if (fvit instanceof UserFunctionCall.FunctionCallPackage) {
375: // return (Value)fvit;
376: // } else {
377: // out.append(fvit, locationId, NodeInfo.ALL_NAMESPACES);
378: // }
379: // }
380: // }
381:
382: /**
383: * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value
384: * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing
385: * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression
386: * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue
387: */
388:
389: public Value reduce() throws XPathException {
390: return new SequenceExtent(iterateResults(null)).reduce();
391: }
392:
393: /**
394: * Get the primitive value (the value in the value space). This returns an
395: * AtomicValue of a class that would be used to represent the primitive value.
396: * In effect this means that for built-in types, it returns the value itself,
397: * but for user-defined type, it returns the primitive value minus the type
398: * annotation. Note that getItemType() when applied to the result of this
399: * function does not not necessarily return a primitive type: for example, this
400: * function may return a value of type xdt:dayTimeDuration, which is not a
401: * primitive type as defined by {@link net.sf.saxon.type.Type#isPrimitiveType(int)}
402: */
403:
404: public AtomicValue getPrimitiveValue() {
405: try {
406: return ((AtomicValue) reduce()).getPrimitiveValue();
407: } catch (XPathException e) {
408: throw new RuntimeException(e);
409: }
410: }
411: }
412:
413: /**
414: * Mapping function that converts a sequence possibly containing embedded FunctionCallPackage items into
415: * one in which any such items are fully expanded
416: */
417:
418: private static class Flattener implements MappingFunction {
419:
420: public static final Flattener THE_INSTANCE = new Flattener();
421:
422: private Flattener() {
423: }
424:
425: /**
426: * Map one item to a sequence.
427: *
428: * @param item The item to be mapped.
429: * If context is supplied, this must be the same as context.currentItem().
430: * @param context The processing context. Some mapping functions use this because they require
431: * context information. Some mapping functions modify the context by maintaining the context item
432: * and position. In other cases, the context may be null.
433: * @return either (a) a SequenceIterator over the sequence of items that the supplied input
434: * item maps to, or (b) an Item if it maps to a single item, or (c) null if it maps to an empty
435: * sequence.
436: */
437:
438: public Object map(Item item, XPathContext context)
439: throws XPathException {
440: if (item instanceof FunctionCallPackage) {
441: return (((FunctionCallPackage) item)
442: .iterateResults(context));
443: } else {
444: return item;
445: }
446: }
447:
448: }
449:
450: }
451:
452: //
453: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
454: // you may not use this file except in compliance with the License. You may obtain a copy of the
455: // License at http://www.mozilla.org/MPL/
456: //
457: // Software distributed under the License is distributed on an "AS IS" basis,
458: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
459: // See the License for the specific language governing rights and limitations under the License.
460: //
461: // The Original Code is: all this file.
462: //
463: // The Initial Developer of the Original Code is Michael H. Kay.
464: //
465: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
466: //
467: // Contributor(s): none.
468: //
|