From d30d765f9e7e3791af94bc89ea3dda432e0bc81b Mon Sep 17 00:00:00 2001 From: Christian Gruen Date: Thu, 3 Mar 2016 14:25:30 +0100 Subject: [PATCH] [FIX] XQuery, inspect:functions: compile closures. #1194 --- .../java/org/basex/http/restxq/RestXqModule.java | 6 +++--- .../src/main/java/org/basex/query/func/Closure.java | 12 ++++++------ .../main/java/org/basex/query/func/Function.java | 8 +++++--- .../main/java/org/basex/query/func/StaticFuncs.java | 13 +++++++++++-- .../org/basex/query/func/fn/FnFunctionLookup.java | 2 +- .../basex/query/func/inspect/InspectFunctions.java | 7 ++----- .../org/basex/query/func/InspectModuleTest.java | 13 ++++++++++--- basex-core/src/test/resources/hello.xqm | 5 +++++ 8 files changed, 43 insertions(+), 23 deletions(-) diff --git a/basex-api/src/main/java/org/basex/http/restxq/RestXqModule.java b/basex-api/src/main/java/org/basex/http/restxq/RestXqModule.java index bf558345c7..5a86f03921 100644 --- a/basex-api/src/main/java/org/basex/http/restxq/RestXqModule.java +++ b/basex-api/src/main/java/org/basex/http/restxq/RestXqModule.java @@ -49,10 +49,10 @@ boolean parse(final HTTPContext http) throws Exception { try(final QueryContext qc = qc(ctx)) { // loop through all functions final String name = file.name(); - for(final StaticFunc uf : qc.funcs.funcs()) { + for(final StaticFunc sf : qc.funcs.funcs()) { // only add functions that are defined in the same module (file) - if(name.equals(new IOFile(uf.info.path()).name())) { - final RestXqFunction rxf = new RestXqFunction(uf, qc, this); + if(name.equals(new IOFile(sf.info.path()).name())) { + final RestXqFunction rxf = new RestXqFunction(sf, qc, this); if(rxf.parse(ctx)) functions.add(rxf); } } diff --git a/basex-core/src/main/java/org/basex/query/func/Closure.java b/basex-core/src/main/java/org/basex/query/func/Closure.java index e3c79b843c..d8bb0297d7 100644 --- a/basex-core/src/main/java/org/basex/query/func/Closure.java +++ b/basex-core/src/main/java/org/basex/query/func/Closure.java @@ -157,8 +157,7 @@ public Expr compile(final QueryContext qc, final VarScope scp) throws QueryExcep @Override public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException { - final SeqType r = expr.seqType(); - final SeqType rt = type == null || r.instanceOf(type) ? r : type; + final SeqType r = expr.seqType(), rt = type == null || r.instanceOf(type) ? r : type; seqType = FuncType.get(anns, rt, args).seqType(); size = 1; @@ -211,16 +210,17 @@ public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryExce @Override public VarUsage count(final Var var) { VarUsage all = VarUsage.NEVER; - for(final Entry e : global.entrySet()) + for(final Entry e : global.entrySet()) { if((all = all.plus(e.getValue().count(var))) == VarUsage.MORE_THAN_ONCE) break; + } return all; } @Override - public Expr inline(final QueryContext qc, final VarScope scp, - final Var var, final Expr ex) throws QueryException { - boolean change = false; + public Expr inline(final QueryContext qc, final VarScope scp, final Var var, final Expr ex) + throws QueryException { + boolean change = false; for(final Entry entry : global.entrySet()) { final Expr e = entry.getValue().inline(qc, scp, var, ex); if(e != null) { diff --git a/basex-core/src/main/java/org/basex/query/func/Function.java b/basex-core/src/main/java/org/basex/query/func/Function.java index 0244405077..4addb24f69 100644 --- a/basex-core/src/main/java/org/basex/query/func/Function.java +++ b/basex-core/src/main/java/org/basex/query/func/Function.java @@ -208,7 +208,7 @@ ITEM_ZM, flag(HOF)), FUNCTION_ARITY(FnFunctionArity.class, "function-arity(function)", arg(FUN_O), ITR), /** XQuery function. */ FUNCTION_LOOKUP(FnFunctionLookup.class, "function-lookup(name,arity)", - arg(QNM, ITR), FUN_OZ, flag(CTX, Flag.POS, NDT)), + arg(QNM, ITR), FUN_OZ, flag(CTX, Flag.POS, NDT, HOF)), /** XQuery function. */ FUNCTION_NAME(FnFunctionName.class, "function-name(function)", arg(FUN_O), QNM_ZO), /** XQuery function. */ @@ -1061,13 +1061,15 @@ ITEM_ZM, flag(HOF)), /* Inspection Module. */ /** XQuery function. */ - _INSPECT_FUNCTION(InspectFunction.class, "function(function)", arg(STR), ELM, INSPECT_URI), + _INSPECT_FUNCTION(InspectFunction.class, "function(function)", + arg(STR), ELM, flag(HOF), INSPECT_URI), /** XQuery function. */ _INSPECT_MODULE(InspectModule.class, "module(uri)", arg(STR), ELM, INSPECT_URI), /** XQuery function. */ _INSPECT_CONTEXT(InspectContext.class, "context()", arg(), ELM, INSPECT_URI), /** XQuery function. */ - _INSPECT_FUNCTIONS(InspectFunctions.class, "functions([uri])", arg(STR), FUN_ZM, INSPECT_URI), + _INSPECT_FUNCTIONS(InspectFunctions.class, "functions([uri])", + arg(STR), FUN_ZM, flag(HOF), INSPECT_URI), /** XQuery function. */ _INSPECT_XQDOC(InspectXqdoc.class, "xqdoc(uri)", arg(STR), ELM, INSPECT_URI), diff --git a/basex-core/src/main/java/org/basex/query/func/StaticFuncs.java b/basex-core/src/main/java/org/basex/query/func/StaticFuncs.java index 52d0ccadb3..acea6ff25f 100644 --- a/basex-core/src/main/java/org/basex/query/func/StaticFuncs.java +++ b/basex-core/src/main/java/org/basex/query/func/StaticFuncs.java @@ -183,13 +183,22 @@ public void checkUp() throws QueryException { } /** - * Compiles the functions. + * Compiles the used functions. * @param qc query context */ public void compile(final QueryContext qc) { + compile(qc, false); + } + + /** + * Compiles all functions. + * @param qc query context + * @param all compile all functions (not only used ones) + */ + public void compile(final QueryContext qc, final boolean all) { // only compile those functions that are used for(final FuncCache fc : funcs.values()) { - if(!fc.calls.isEmpty()) fc.func.compile(qc); + if(all || !fc.calls.isEmpty()) fc.func.compile(qc); } } diff --git a/basex-core/src/main/java/org/basex/query/func/fn/FnFunctionLookup.java b/basex-core/src/main/java/org/basex/query/func/fn/FnFunctionLookup.java index 9376aee8b6..d738827230 100644 --- a/basex-core/src/main/java/org/basex/query/func/fn/FnFunctionLookup.java +++ b/basex-core/src/main/java/org/basex/query/func/fn/FnFunctionLookup.java @@ -30,7 +30,7 @@ public Item item(final QueryContext qc, final InputInfo ii) throws QueryExceptio @Override protected Expr opt(final QueryContext qc, final VarScope scp) { - for(final StaticFunc sf : qc.funcs.funcs()) sf.compile(qc); + qc.funcs.compile(qc, true); return this; } } diff --git a/basex-core/src/main/java/org/basex/query/func/inspect/InspectFunctions.java b/basex-core/src/main/java/org/basex/query/func/inspect/InspectFunctions.java index 245819951d..c85373d2e6 100644 --- a/basex-core/src/main/java/org/basex/query/func/inspect/InspectFunctions.java +++ b/basex-core/src/main/java/org/basex/query/func/inspect/InspectFunctions.java @@ -39,7 +39,7 @@ public Value value(final QueryContext qc) throws QueryException { try { final IO io = checkPath(exprs[0], qc); qc.parse(Token.string(io.read()), io.path(), sc); - qc.compile(); + qc.funcs.compile(qc, true); } catch(final IOException ex) { throw IOERR_X.get(info, ex); } @@ -56,10 +56,7 @@ public Value value(final QueryContext qc) throws QueryException { @Override protected Expr opt(final QueryContext qc, final VarScope scp) throws QueryException { - if(exprs.length == 0) { - for(final StaticFunc sf : qc.funcs.funcs()) sf.compile(qc); - return iter(qc).value(); - } + if(exprs.length == 0) qc.funcs.compile(qc, true); return this; } diff --git a/basex-core/src/test/java/org/basex/query/func/InspectModuleTest.java b/basex-core/src/test/java/org/basex/query/func/InspectModuleTest.java index 2e4373dffb..1f36dd2379 100644 --- a/basex-core/src/test/java/org/basex/query/func/InspectModuleTest.java +++ b/basex-core/src/test/java/org/basex/query/func/InspectModuleTest.java @@ -106,13 +106,20 @@ public void context() { /** Test method. */ @Test public void functions() { + final String url = "src/test/resources/hello.xqm"; query("declare function local:x() { 1 }; " + COUNT.args(_INSPECT_FUNCTIONS.args()), "1"); query("declare function local:x() { 2 }; " + _INSPECT_FUNCTIONS.args() + "()", "2"); - query("import module namespace hello='world' at 'src/test/resources/hello.xqm';" + - "inspect:functions()[last()] instance of function(*)", "true"); + query("import module namespace hello='world' at '" + url + "';" + + _INSPECT_FUNCTIONS.args() + "[last()] instance of function(*)", "true"); - query("for $f in " + _INSPECT_FUNCTIONS.args("src/test/resources/hello.xqm") + query("for $f in " + _INSPECT_FUNCTIONS.args(url) + "where local-name-from-QName(function-name($f)) = 'world' " + "return $f()", "hello world"); + + // ensure that closures will be compiled (GH-1194) + query(_INSPECT_FUNCTIONS.args(url) + + "[function-name(.) = QName('world','closure')]()", "1"); + query("import module namespace hello='world' at '" + url + "';" + + _INSPECT_FUNCTIONS.args() + "[function-name(.) = xs:QName('hello:closure')]()", "1"); } } diff --git a/basex-core/src/test/resources/hello.xqm b/basex-core/src/test/resources/hello.xqm index f59d056c1a..4a8a5ac20d 100644 --- a/basex-core/src/test/resources/hello.xqm +++ b/basex-core/src/test/resources/hello.xqm @@ -31,3 +31,8 @@ declare %public function hello:world() as xs:string { declare %private %Q{ns}ignored function hello:internal() as xs:string { "hello world" }; + +(:~ Closure. :) +declare function hello:closure() { + count#1(1) +};