Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:BaseXdb/basex

  • Loading branch information...
commit 87c5f6a07cc889aaa9e6d326d04e1b4adb5c8d63 2 parents a6a6f7e + 964e2bd
@ChristianGruen ChristianGruen authored
View
64 src/main/java/org/basex/query/func/BaseFuncCall.java
@@ -29,38 +29,50 @@ public BaseFuncCall(final InputInfo ii, final QNm nm, final Expr[] arg) {
public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException {
Expr fun = func;
Var[] args = args(ctx);
- do {
- // cache arguments, evaluate function and reset variable scope
- final VarStack cs = addArgs(ctx, args);
- ctx.tailCalls = 0;
- try {
- return fun.item(ctx, ii);
- } catch(final Continuation c) {
- fun = c.getFunc();
- args = c.getArgs();
- } finally {
- ctx.vars.reset(cs);
- }
- } while(true);
+
+ final int calls = ctx.tailCalls;
+ try {
+ do {
+ // cache arguments, evaluate function and reset variable scope
+ final VarStack cs = addArgs(ctx, args);
+ ctx.tailCalls = 0;
+ try {
+ return fun.item(ctx, ii);
+ } catch(final Continuation c) {
+ fun = c.getFunc();
+ args = c.getArgs();
+ } finally {
+ ctx.vars.reset(cs);
+ }
+ } while(true);
+ } finally {
+ ctx.tailCalls = calls;
+ }
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
Expr fun = func;
Var[] args = args(ctx);
- do {
- // cache arguments, evaluate function and reset variable scope
- final VarStack cs = addArgs(ctx, args);
- ctx.tailCalls = 0;
- try {
- return ctx.value(fun);
- } catch(final Continuation c) {
- fun = c.getFunc();
- args = c.getArgs();
- } finally {
- ctx.vars.reset(cs);
- }
- } while(true);
+
+ final int calls = ctx.tailCalls;
+ try {
+ do {
+ // cache arguments, evaluate function and reset variable scope
+ final VarStack cs = addArgs(ctx, args);
+ ctx.tailCalls = 0;
+ try {
+ return ctx.value(fun);
+ } catch(final Continuation c) {
+ fun = c.getFunc();
+ args = c.getArgs();
+ } finally {
+ ctx.vars.reset(cs);
+ }
+ } while(true);
+ } finally {
+ ctx.tailCalls = calls;
+ }
}
@Override
View
30 src/main/java/org/basex/query/func/TailFuncCall.java
@@ -29,24 +29,32 @@
@Override
public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException {
- checkHeight(ctx);
+ final int calls = checkHeight(ctx);
// cache arguments, evaluate function and reset variable scope
final VarStack cs = addArgs(ctx, args(ctx));
- final Item it = func.item(ctx, ii);
- ctx.vars.reset(cs);
- return it;
+ try {
+ final Item it = func.item(ctx, ii);
+ return it;
+ } finally {
+ ctx.vars.reset(cs);
+ ctx.tailCalls = calls;
+ }
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
- checkHeight(ctx);
+ final int calls = checkHeight(ctx);
// cache arguments, evaluate function and reset variable scope
final VarStack cs = addArgs(ctx, args(ctx));
- final Value v = ctx.value(func);
- ctx.vars.reset(cs);
- return v;
+ try {
+ final Value v = ctx.value(func);
+ return v;
+ } finally {
+ ctx.vars.reset(cs);
+ ctx.tailCalls = calls;
+ }
}
@Override
@@ -59,10 +67,12 @@ public Iter iter(final QueryContext ctx) throws QueryException {
* Checks is the maximum number of successive tail calls is reached, and
* triggers a continuation exception if this happens.
* @param ctx query context
+ * @return old number of successive tail calls
* @throws QueryException query exception
*/
- private void checkHeight(final QueryContext ctx) throws QueryException {
- final int max = ctx.maxCalls;
+ private int checkHeight(final QueryContext ctx) throws QueryException {
+ final int max = ctx.maxCalls, old = ctx.tailCalls;
if(max >= 0 && ctx.tailCalls++ > max) throw new Continuation(args(ctx));
+ return old;
}
}
View
31 src/test/java/org/basex/test/query/ast/TCOTest.java
@@ -1,5 +1,6 @@
package org.basex.test.query.ast;
+import org.basex.query.expr.*;
import org.basex.query.func.*;
import org.basex.util.*;
import org.junit.*;
@@ -73,8 +74,36 @@ public void tightLoopTest() {
null,
"exists(//" + Util.name(UserFunc.class) + "/" +
- Util.name(TailFuncCall.class) + ")",
+ Util.name(TailFuncCall.class) + ")",
"exists(//" + Util.name(BaseFuncCall.class) + ")"
);
}
+
+ /** Checks if a function only containing a tail call is properly optimized. */
+ @Test
+ public void selfRecursive() {
+ check("declare function local:f($i) { if($i eq 12345) then $i else local:f($i+1) };" +
+ "local:f(0)",
+
+ "12345",
+
+ "exists(//" + Util.name(If.class) + "/" +
+ Util.name(TailFuncCall.class) + ")"
+ );
+ }
+
+ /** Checks if a function only containing a tail call is properly optimized. */
+ @Test
+ public void mixedSelfRecursive() {
+ check("declare function local:inc($i) { $i + 1 };" +
+ "declare function local:f($i) { if($i eq 12345) then $i " +
+ "else local:f(local:inc($i)) };" +
+ "local:f(0)",
+
+ "12345",
+
+ "exists(//" + Util.name(If.class) + "/" +
+ Util.name(TailFuncCall.class) + ")"
+ );
+ }
}
Please sign in to comment.
Something went wrong with that request. Please try again.