Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions basex-core/src/main/java/org/basex/query/func/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public static Expr item(final QNm qnm, final int arity, final boolean runtime,
}

// user-defined function
final StaticFunc sf = qc.functions.get(info.sc(), name, arity);
final StaticFunc sf = qc.functions.get(info.sc(), name, arity, runtime);
if(sf != null) {
final Expr func = item(sf, fb, qc);
if(sf.updating) qc.updating();
Expand Down Expand Up @@ -215,7 +215,7 @@ private static QNm funcName(final QNm name, final int arity, final InputInfo inf
final QueryContext qc) {

final StaticContext sc = info.sc();
if(name.hasURI() || qc.functions.get(sc, name, arity) != null) return name;
if(name.hasURI() || qc.functions.get(sc, name, arity, false) != null) return name;
return new QNm(name.local(), sc.funcNS != null ? sc.funcNS : FN_URI);
}

Expand Down Expand Up @@ -326,7 +326,7 @@ private static StaticFuncCall staticCall(final QNm name, final FuncBuilder fb,
}

final StaticFuncCall call = new StaticFuncCall(name, fb.args(), fb.keywords, fb.info);
qc.functions.setFunc(call, qc);
qc.functions.setFunc(call, fb.runtime, qc);
return call;
}

Expand Down
20 changes: 13 additions & 7 deletions basex-core/src/main/java/org/basex/query/func/StaticFuncs.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public StaticFunc declare(final StaticContext sc, final QNm name, final Params p

final byte[] modUri = Token.eq(name.uri(), FN_URI) ? FN_URI : QNm.uri(sc.module);
final StaticFunc sf = new StaticFunc(name, params, expr, anns, vs, info, doc);
if(get(sc, name, sf.min, sf.arity()) != null) throw DUPLFUNC_X.get(info, name);
if(get(sc, name, sf.min, sf.arity(), false) != null) throw DUPLFUNC_X.get(info, name);
funcsByModule.computeIfAbsent(modUri, QNmMap::new).computeIfAbsent(name, ArrayList::new).
add(sf);
return sf;
Expand All @@ -76,14 +76,16 @@ public Expr newRef(final QuerySupplier<Expr> resolve) {
/**
* Assigns a function to a static function call.
* @param call name function name
* @param useDynamicContext {@code true} if function lookup should include the dynamic context
* @param qc query context
* @throws QueryException query exception
*/
void setFunc(final StaticFuncCall call, final QueryContext qc) throws QueryException {
void setFunc(final StaticFuncCall call, final boolean useDynamicContext, final QueryContext qc)
throws QueryException {
final InputInfo info = call.info();
final QNm name = call.name;
final int arity = call.arity();
final StaticFunc func = get(info.sc(), name, arity);
final StaticFunc func = get(info.sc(), name, arity, useDynamicContext);
if(func != null) {
if(func.expr == null) throw FUNCNOIMPL_X.get(func.info, func.name.prefixString());
call.setFunc(func);
Expand Down Expand Up @@ -128,10 +130,12 @@ public void compileAll(final CompileContext cc) {
* @param sc static context
* @param qname function name
* @param arity function arity
* @param useDynamicContext {@code true} if function lookup should include the dynamic context
* @return function if found, {@code null} otherwise
*/
public StaticFunc get(final StaticContext sc, final QNm qname, final int arity) {
return get(sc, qname, arity, arity);
public StaticFunc get(final StaticContext sc, final QNm qname, final int arity,
final boolean useDynamicContext) {
return get(sc, qname, arity, arity, useDynamicContext);
}

/**
Expand All @@ -140,13 +144,15 @@ public StaticFunc get(final StaticContext sc, final QNm qname, final int arity)
* @param qname function name
* @param min minimum function arity
* @param max maximum function arity
* @param useDynamicContext {@code true} if function lookup should include the dynamic context
* @return function if found, {@code null} otherwise
*/
private StaticFunc get(final StaticContext sc, final QNm qname, final int min, final int max) {
private StaticFunc get(final StaticContext sc, final QNm qname, final int min, final int max,
final boolean useDynamicContext) {
final byte[] funcUri = qname.uri();
final byte[] modUri = Token.eq(funcUri, FN_URI) ? FN_URI : QNm.uri(sc.module);
StaticFunc func = get(modUri, qname, min, max);
if(func == null && sc.imports.contains(funcUri)) {
if(func == null && (useDynamicContext || sc.imports.contains(funcUri))) {
func = get(funcUri, qname, min, max);
if(func != null && func.anns.contains(Annotation.PRIVATE)) func = null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.basex.query.func.inspect;

import org.basex.query.*;
import org.basex.query.ann.*;
import org.basex.query.func.*;
import org.basex.query.value.item.*;
import org.basex.query.value.node.*;
Expand All @@ -22,16 +21,7 @@ public FNode item(final QueryContext qc, final InputInfo ii) throws QueryExcepti
StaticFunc func = null;
if(name != null) {
final int arity = function.arity();
func = qc.functions.get(ii.sc(), name, arity);
if(func == null) {
for(final StaticFunc sf : qc.functions) {
if(!sf.annotations().contains(Annotation.PRIVATE) && sf.funcName().eq(name)
&& sf.minArity() <= arity && sf.arity() >= arity) {
func = sf;
break;
}
}
}
func = qc.functions.get(ii.sc(), name, arity, true);
}
return new PlainDoc(qc, info).function(name, func, function.funcType(), function.annotations());
}
Expand Down
22 changes: 19 additions & 3 deletions basex-core/src/test/java/org/basex/query/ModuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,10 @@ public final class ModuleTest extends SandboxTest {
+ "};\n"
+ "declare variable $c:hello := 'can you see me now';");

// function is not visible to fn:function-lookup (not in dynamically known function definitions)
// function is visible to fn:function-lookup even when not in static context
query("import module namespace b = 'b' at '" + b.path() + "';\n"
+ "fn:function-lookup(#Q{c}hello, 0)", "");
// function is still visible to inspect:functions
+ "fn:function-lookup(#Q{c}hello, 0)()", "can you see me now");
// function is visible to inspect:functions
query("import module namespace b = 'b' at '" + b.path() + "';\n"
+ "inspect:functions()", "Q{c}hello#0");

Expand All @@ -260,4 +260,20 @@ public final class ModuleTest extends SandboxTest {
+ "declare namespace c = 'c';\n"
+ "$c:hello", QueryError.INVISIBLEVAR_X);
}

/** Tests fn:function-lookup from within a library module for a module imported elsewhere. */
@Test public void gh2641() {
final IOFile sandbox = sandbox();
final IOFile a = new IOFile(sandbox, "a.xqm");
final IOFile b = new IOFile(sandbox, "b.xqm");
write(a, "module namespace a = 'A';\n"
+ "declare function a:lookup() {\n"
+ " function-lookup(QName('B', 'test'), 0)\n"
+ "};");
write(b, "module namespace b = 'B';\n"
+ "declare function b:test() {};");
query("import module namespace a = 'A' at '" + a.path() + "';\n"
+ "import module namespace b = 'B' at '" + b.path() + "';\n"
+ "exists(a:lookup())", true);
}
}
Loading