Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDEV-5094 onmissingfunction #2419

Open
wants to merge 4 commits into
base: 6.2
Choose a base branch
from
Open
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
56 changes: 46 additions & 10 deletions core/src/main/java/lucee/runtime/type/scope/UndefinedImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package lucee.runtime.type.scope;

Expand All @@ -28,6 +28,9 @@
import lucee.runtime.ComponentScope;
import lucee.runtime.PageContext;
import lucee.runtime.PageContextImpl;
import lucee.runtime.ComponentImpl;
import lucee.runtime.type.util.UDFUtil;
import lucee.runtime.type.FunctionArgument;
import lucee.runtime.config.Config;
import lucee.runtime.config.ConfigPro;
import lucee.runtime.config.Constants;
Expand All @@ -39,6 +42,7 @@
import lucee.runtime.exp.PageException;
import lucee.runtime.functions.system.CFFunction;
import lucee.runtime.listener.ApplicationContextSupport;
import lucee.runtime.listener.ModernApplicationContext;
import lucee.runtime.op.Duplicator;
import lucee.runtime.type.BIF;
import lucee.runtime.type.Collection;
Expand Down Expand Up @@ -80,7 +84,7 @@ public final class UndefinedImpl extends StructSupport implements Undefined, Obj

/**
* constructor of the class
*
*
* @param pageContextImpl
* @param type type of the undefined scope
* (ServletConfig.SCOPE_STRICT;ServletConfig.SCOPE_SMALL;ServletConfig.SCOPE_STANDART)
Expand Down Expand Up @@ -315,7 +319,7 @@ public Struct getScope(Collection.Key key) {

/**
* returns the scope that contains a specific key
*
*
* @param key
* @return
*/
Expand Down Expand Up @@ -360,7 +364,7 @@ public Collection getScopeFor(Collection.Key key, Scope defaultValue) {

/**
* return a list of String with the scope names
*
*
* @param key
* @return
*/
Expand Down Expand Up @@ -793,7 +797,7 @@ public Object call(PageContext pc, final Key methodName, Object[] args) throws P
return bif.call(pc, methodName, args, false);
}

throw new ExpressionException("No matching function [" + methodName + "] found");
return onMissingFunction(pc, methodName, args, null);
}

@Override
Expand All @@ -810,7 +814,7 @@ public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) t
if (bif != null) {
return bif.callWithNamedValues(pc, methodName, args, false);
}
throw new ExpressionException("No matching function [" + methodName + "] found");
return onMissingFunction(pc, methodName, null, args);
}

private UDF getUDF(PageContext pc, Key methodName) throws PageException {
Expand Down Expand Up @@ -850,4 +854,36 @@ public boolean accept(Resource dir, String name) {
public short getScopeCascadingType() {
return type;
}

private Object onMissingFunction(PageContext pc, final Key methodName, Object[] _args, Struct _namedArgs) throws PageException {
ApplicationContextSupport ac = (ApplicationContextSupport) pc.getApplicationContext();
if (ac != null) {
if ( ac instanceof ModernApplicationContext ) {
ComponentImpl appcfc = (ComponentImpl)((ModernApplicationContext) ac).getComponent();

if (appcfc.contains(pc, KeyConstants._onmissingfunction)) {
Argument args = new ArgumentImpl();

if (_args != null) {
for (int i = 0; i < _args.length; i++) {
args.setEL(ArgumentIntKey.init(i + 1), _args[i]);
}
} else if (_namedArgs != null) {
UDFUtil.argumentCollection(_namedArgs, new FunctionArgument[] {});

Iterator<Entry<Key, Object>> it = _namedArgs.entryIterator();
Entry<Key, Object> e;
while (it.hasNext()) {
e = it.next();
args.setEL(e.getKey(), e.getValue());
}

}
return appcfc.call(pc, KeyConstants._onmissingfunction, new Object[] { methodName.getString(), args } );
}
}
}

throw new ExpressionException("No matching function [" + methodName + "] found");
}
}
11 changes: 6 additions & 5 deletions core/src/main/java/lucee/runtime/type/util/KeyConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*
**/
package lucee.runtime.type.util;

Expand Down Expand Up @@ -750,6 +750,7 @@ public class KeyConstants {
public static final Key __toArray = KeyImpl._const("_toArray");
public static final Key __toQuery = KeyImpl._const("_toQuery");
public static final Key _onmissingmethod = KeyImpl._const("onmissingmethod");
public static final Key _onmissingfunction = KeyImpl._const("onmissingfunction");
public static final Key _functions = KeyImpl._const("functions");
public static final Key _fullname = KeyImpl._const("fullname");
public static final Key _skeleton = KeyImpl._const("skeleton");
Expand Down
34 changes: 34 additions & 0 deletions test/tickets/LDEV5094.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
component extends="org.lucee.cfml.test.LuceeTestCase"{
function run( testResults , testBox ) {
describe( title="LDEV-5094 onMissingFunction feature", body=function() {
it( title='should trigger onMissingFunction in Application.cfc when a nonexistent function is called with positional arguments',body=function( currentSpec ) {
var uri = createURI("LDEV5094");
local.result = _InternalRequest(
template:"#uri#/testPositionalArgs.cfm"
);
expect(local.result.filecontent.trim()).toBe('Function name: SOMETESTFUNCTION. Arguments: {"1":"arg1","2":true}');
});

it( title='should trigger onMissingFunction in Application.cfc when a nonexistent function is called with named arguments',body=function( currentSpec ) {
var uri = createURI("LDEV5094");
local.result = _InternalRequest(
template:"#uri#/testNamedArgs.cfm"
);
expect(local.result.filecontent.trim()).toBe('Function name: ANOTHERTESTFUNCTION. Arguments: {"test":true}');
});

it( title='should have lucee throw the usual missing function error when Application.cfc does not implement onMissingFunction',body=function( currentSpec ) {
var uri = createURI("LDEV5094/notimplemented");
local.result = _InternalRequest(
template:"#uri#/test.cfm"
);
expect(local.result.filecontent.trim()).toBe('No matching function [myMissingFunction] found');
});
});
}

private string function createURI(string calledName){
var baseURI="/test/#listLast(getDirectoryFromPath(getCurrenttemplatepath()),"\/")#/";
return baseURI&""&calledName;
}
}
11 changes: 11 additions & 0 deletions test/tickets/LDEV5094/Application.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
component {
this.name="LDEV5094";

public function onRequestStart() {
setting requesttimeout=10;
}

public function onMissingFunction( functionName, functionArguments ){
return "Function name: #arguments.functionName#. Arguments: #SerializeJson( arguments.functionArguments )#";
}
}
8 changes: 8 additions & 0 deletions test/tickets/LDEV5094/notimplemented/Application.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
component {
this.name="LDEV5094";

public function onRequestStart() {
setting requesttimeout=10;
}

}
7 changes: 7 additions & 0 deletions test/tickets/LDEV5094/notimplemented/test.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<cfscript>
try {
myMissingFunction( "test" );
} catch( any e ) {
echo( e.message );
}
</cfscript>
1 change: 1 addition & 0 deletions test/tickets/LDEV5094/testNamedArgs.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<cfoutput>#anotherTestFunction( test=true )#</cfoutput>
1 change: 1 addition & 0 deletions test/tickets/LDEV5094/testPositionalArgs.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<cfoutput>#someTestFunction( "arg1", true )#</cfoutput>
Loading