Skip to content
This repository has been archived by the owner on May 31, 2020. It is now read-only.

Commit

Permalink
Merge pull request #864 from patiences/callable-opt
Browse files Browse the repository at this point in the history
Callables should handle null args and kwargs
  • Loading branch information
freakboy3742 authored Jul 26, 2018
2 parents 61d4189 + bed0fe3 commit 47335f6
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 77 deletions.
33 changes: 19 additions & 14 deletions python/common/org/python/java/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,21 +266,24 @@ public static MatchType parameterMatch(java.lang.Class<?> from_type, java.lang.C
*/
public java.lang.reflect.Method selectMethod(org.python.Object[] args, java.util.Map<java.lang.String, org.python.Object> kwargs) {
// org.Python.debug("Method options: ", this.methods);

int n_args = (args == null) ? 0 : args.length;
java.lang.reflect.Method method = null;
java.lang.StringBuilder signature = new java.lang.StringBuilder();
java.lang.Class<?>[] arg_types = new java.lang.Class<?>[args.length];
int n_args = args.length;
for (int i = 0; i < n_args; i++) {
if (args[i] == null) {
arg_types[i] = null;
} else if (args[i].toJava() == null) {
arg_types[i] = null;
} else {
arg_types[i] = args[i].toJava().getClass();
java.lang.Class<?>[] arg_types = null;
if (n_args != 0) {
arg_types = new java.lang.Class<?>[n_args];
for (int i = 0; i < n_args; i++) {
if (args[i] == null) {
arg_types[i] = null;
} else if (args[i].toJava() == null) {
arg_types[i] = null;
} else {
arg_types[i] = args[i].toJava().getClass();
}
signature.append(Function.descriptor(arg_types[i]));
}
signature.append(Function.descriptor(arg_types[i]));
}

// org.Python.debug("Argument signature", signature);
method = this.methods.get(signature.toString());

Expand Down Expand Up @@ -489,16 +492,18 @@ public org.python.Object invoke(org.python.Object instance, org.python.Object[]
// }
// org.Python.debug(" kwargs: ", kwargs);

if (kwargs.size() > 0) {
if (kwargs != null && kwargs.size() > 0) {
// TODO: This doesn't have to be so - we *could* introspect argument names.
throw new org.python.exceptions.RuntimeError("Cannot use kwargs to invoke a native Java method.");
}

java.lang.reflect.Method method = this.selectMethod(args, kwargs);

// org.Python.debug(" Native method: ", method);

java.lang.Object[] adjusted_args = this.adjustArguments(method, args, kwargs);
java.lang.Object[] adjusted_args = null;
if (args != null || kwargs != null) {
adjusted_args = this.adjustArguments(method, args, kwargs);
}

// if (adjusted_args != null) {
// for (java.lang.Object arg: adjusted_args) {
Expand Down
30 changes: 19 additions & 11 deletions python/common/org/python/java/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,26 @@ public Type(org.python.types.Type.Origin origin, java.lang.Class klass) {
*/
public java.lang.reflect.Constructor selectConstructor(org.python.Object[] args, java.util.Map<java.lang.String, org.python.Object> kwargs) {
// org.Python.debug("Constructor options: ", this.constructors);
int n_args = (args == null) ? 0 : args.length;

java.lang.reflect.Constructor constructor = null;
java.lang.StringBuilder signature = new java.lang.StringBuilder();
java.lang.Class<?>[] arg_types = new java.lang.Class<?>[args.length];
int n_args = args.length;
for (int i = 0; i < n_args; i++) {
if (args[i] == null) {
arg_types[i] = null;
} else if (args[i].toJava() == null) {
arg_types[i] = null;
} else {
arg_types[i] = args[i].toJava().getClass();

java.lang.Class<?>[] arg_types = null;
if (n_args != 0) {
arg_types = new java.lang.Class<?>[n_args];
for (int i = 0; i < n_args; i++) {
if (args[i] == null) {
arg_types[i] = null;
} else if (args[i].toJava() == null) {
arg_types[i] = null;
} else {
arg_types[i] = args[i].toJava().getClass();
}
signature.append(Function.descriptor(arg_types[i]));
}
signature.append(Function.descriptor(arg_types[i]));
}

// org.Python.debug("Argument signature", signature);
constructor = this.constructors.get(signature.toString());

Expand Down Expand Up @@ -258,7 +263,10 @@ public org.python.Object invoke(org.python.Object[] args, java.util.Map<java.lan
}
} else {
constructor = this.selectConstructor(args, kwargs);
java.lang.Object[] adjusted_args = this.adjustArguments(constructor, args, kwargs);
java.lang.Object[] adjusted_args = null;
if (args != null || kwargs != null) {
adjusted_args = this.adjustArguments(constructor, args, kwargs);
}

return new org.python.java.Object(constructor.newInstance(adjusted_args));
}
Expand Down
31 changes: 20 additions & 11 deletions python/common/org/python/types/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ private void checkMissingArgs(int requiredArgs, int passedArgs, java.util.Map<ja
// build list of actual missing args, checking if haven't been passed as kwargs
for (int i = first_arg; i < n_missing_pos_args; i++) {
java.lang.String argname = ((String) varnames.get(i + passedArgs).toJava());
if (!kwargs.containsKey(argname)) {
if (kwargs == null || !kwargs.containsKey(argname)) {
missingArgs.add(argname);
}
}
Expand Down Expand Up @@ -237,6 +237,7 @@ java.lang.Object[] adjustArguments(org.python.Object instance, org.python.Object
// System.out.println("argcount = " + argcount);
// System.out.println("kwonlyargcount = " + kwonlyargcount);

int n_provided_args = (args == null) ? 0 : args.length;
int n_args = argcount + kwonlyargcount;
if ((flags & CO_VARARGS) != 0) {
// System.out.println("HAS VARARGS");
Expand All @@ -253,8 +254,8 @@ java.lang.Object[] adjustArguments(org.python.Object instance, org.python.Object
// System.out.println("nargs = " + n_args);
// System.out.println("first default = " + required_args);

if (0 == has_varargs && args != null && args.length > n_args) {
throwUnexpectedPositionalArgumentsError(n_args, args.length);
if (0 == has_varargs && args != null && n_provided_args > n_args) {
throwUnexpectedPositionalArgumentsError(n_args, n_provided_args);
}

// If there are genuinely *no* arguments - not even self - return null;
Expand All @@ -273,12 +274,12 @@ java.lang.Object[] adjustArguments(org.python.Object instance, org.python.Object
// System.out.println(" aARG 0: " + instance);
}

checkMissingArgs(required_args, (args == null ? 0 : args.length), kwargs, varnames, first_arg);
checkMissingArgs(required_args, n_provided_args, kwargs, varnames, first_arg);

// System.out.println("First arg = " + first_arg);
// Populate the positional args.
for (int i = 0; i < argcount - first_arg; i++) {
if (i < args.length) {
if (i < n_provided_args) {
// System.out.println(" b" + (i + first_arg));
adjusted[i + first_arg] = args[i];
// System.out.println(" bARG " + (i + first_arg) + ": " + args[i]);
Expand Down Expand Up @@ -306,11 +307,17 @@ java.lang.Object[] adjustArguments(org.python.Object instance, org.python.Object
}

// Create a tuple for the varargs
org.python.types.Tuple tuple = null;
if ((flags & CO_VARARGS) != 0) {
// System.out.println("Handle varargs");
// Construct Python tuple object
org.python.types.Tuple tuple = new org.python.types.Tuple(
java.util.Arrays.asList(java.util.Arrays.copyOfRange(args, argcount - first_arg, args.length)));

if (args != null) {
tuple = new org.python.types.Tuple(java.util.Arrays.asList(java.util.Arrays.copyOfRange(args, argcount - first_arg, n_provided_args)));
} else {
// No varargs provided
tuple = new org.python.types.Tuple();
}

adjusted[argcount] = tuple;
// System.out.println(" dARG " + argcount + ": " + tuple);
Expand All @@ -320,7 +327,7 @@ java.lang.Object[] adjustArguments(org.python.Object instance, org.python.Object
for (int i = 0; i < kwonlyargcount; i++) {
java.lang.String varname = ((org.python.types.Str) varnames.get(argcount + has_varargs + i)).value;
// System.out.println(" e" + (argcount + has_varargs + i) + " " + varname);
org.python.Object value = kwargs.remove(varname);
org.python.Object value = (kwargs == null) ? null : kwargs.remove(varname);
if (value == null) {
value = this.default_kwargs.get(varname);
}
Expand All @@ -332,9 +339,11 @@ java.lang.Object[] adjustArguments(org.python.Object instance, org.python.Object
if ((flags & CO_VARKEYWORDS) != 0) {
// System.out.println("Handle varkwargs = " + kwargs);
org.python.types.Dict kwargDict = new org.python.types.Dict();
for (java.util.Map.Entry<java.lang.String, org.python.Object> entry : kwargs.entrySet()) {
// System.out.println("Add KWARG" + entry.getKey());
kwargDict.__setitem__(new org.python.types.Str(entry.getKey()), entry.getValue());
if (kwargs != null) {
for (java.util.Map.Entry<java.lang.String, org.python.Object> entry : kwargs.entrySet()) {
// System.out.println("Add KWARG" + entry.getKey());
kwargDict.__setitem__(new org.python.types.Str(entry.getKey()), entry.getValue());
}
}
adjusted[adjusted.length - 1] = kwargDict;
// System.out.println(" fARG " + (adjusted.length - 1) + ": " + kwargDict);
Expand Down
30 changes: 24 additions & 6 deletions python/common/org/python/types/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ public enum Origin {
public java.lang.reflect.Constructor constructor;
public java.lang.Class klass;

private static org.python.Object[] emptyArgs;
private static java.util.Map<java.lang.String, org.python.Object> emptyKwargs;

private static org.python.Object[] getEmptyArgs() {
if (emptyArgs == null) {
emptyArgs = new org.python.Object[0];
}
return emptyArgs;
}

private static java.util.Map<java.lang.String, org.python.Object> getEmptyKwargs() {
if (emptyKwargs == null) {
emptyKwargs = new java.util.HashMap<java.lang.String, org.python.Object>();
}
return emptyKwargs;
}

/**
* Factory method to obtain Python classes from their Java counterparts
*/
Expand Down Expand Up @@ -411,21 +428,22 @@ public org.python.Object invoke(org.python.Object[] args, java.util.Map<java.lan

adjusted_args = new org.python.Object[arg_names.length + default_arg_names.length];

if (args.length < n_args) {
int n_provided_args = (args == null) ? 0 : args.length;
if (n_provided_args < n_args) {
// Take as many positional arguments as have been literally provided
for (a = 0; a < args.length; a++) {
for (a = 0; a < n_provided_args; a++) {
adjusted_args[a] = args[a];
}

// Populate the rest from kwargs
for (a = args.length; a < n_args; a++) {
for (a = n_provided_args; a < n_args; a++) {
if (a < arg_names.length) {
arg_name = arg_names[a];
} else {
arg_name = default_arg_names[a - arg_names.length];
}

arg = kwargs.remove(arg_name);
arg = (kwargs == null) ? null : kwargs.remove(arg_name);
if (arg == null && a < arg_names.length) {
throw new org.python.exceptions.TypeError(
this.PYTHON_TYPE_NAME + " constructor missing positional argument '" +
Expand All @@ -439,7 +457,7 @@ public org.python.Object invoke(org.python.Object[] args, java.util.Map<java.lan
adjusted_args[a] = args[a];
}

for (a = n_args; a < args.length; a++) {
for (a = n_args; a < n_provided_args; a++) {
if (a < arg_names.length) {
arg_name = arg_names[a];
} else {
Expand All @@ -457,7 +475,7 @@ public org.python.Object invoke(org.python.Object[] args, java.util.Map<java.lan
// }
// org.Python.debug(" adj kwargs: ", kwargs);

return (org.python.Object) this.constructor.newInstance(adjusted_args, kwargs);
return (org.python.Object) this.constructor.newInstance((adjusted_args != null) ? adjusted_args : getEmptyArgs(), (kwargs != null) ? kwargs : getEmptyKwargs());
} else {
throw new org.python.exceptions.RuntimeError("No Python-compatible constructor for type " + this.klass);
}
Expand Down
80 changes: 45 additions & 35 deletions voc/python/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -2076,55 +2076,65 @@ def visit_Call(self, node):
# Create and populate the array of arguments to pass to invoke()
num_args = len([arg for arg in node.args if not isinstance(arg, ast.Starred)])

self.context.add_opcodes(
java.Array(num_args),
)

for i, arg in enumerate(node.args):
# This block implements *args in Python 3.5+
if isinstance(arg, ast.Starred):
self.visit(arg)
continue

if len(node.args) == 0 and getattr(node, 'starargs', None) is None:
self.context.add_opcodes(
JavaOpcodes.DUP(),
ICONST_val(i),
JavaOpcodes.ACONST_NULL(),
)
self.visit(arg)
else:
self.context.add_opcodes(
JavaOpcodes.AASTORE(),
java.Array(num_args),
)

# This block implements *args in Python 3.4
if getattr(node, 'starargs', None) is not None:
# Evaluate the starargs
self.visit(node.starargs)
for i, arg in enumerate(node.args):
# This block implements *args in Python 3.5+
if isinstance(arg, ast.Starred):
self.visit(arg)
continue

self.context.add_opcodes(
AddToArgs(),
)
self.context.add_opcodes(
JavaOpcodes.DUP(),
ICONST_val(i),
)
self.visit(arg)
self.context.add_opcodes(
JavaOpcodes.AASTORE(),
)

# Create and populate the map of kwargs to pass to invoke().
self.context.add_opcodes(
java.Map(),
)
# This block implements *args in Python 3.4
if getattr(node, 'starargs', None) is not None:
# Evaluate the starargs
self.visit(node.starargs)

for keyword in node.keywords:
if keyword.arg is None: # Python 3.5 **kwargs
self.add_doublestarred_kwargs(node, keyword.value)
continue
self.context.add_opcodes(
AddToArgs(),
)

if len(node.keywords) == 0 and getattr(node, 'kwargs', None) is None:
self.context.add_opcodes(
JavaOpcodes.DUP(),
JavaOpcodes.LDC_W(keyword.arg),
JavaOpcodes.ACONST_NULL(),
)
self.visit(keyword.value)
else:
# Create and populate the map of kwargs to pass to invoke().
self.context.add_opcodes(
java.Map.put()
java.Map(),
)

if getattr(node, 'kwargs', None) is not None: # Python 3.4 **kwargs
self.add_doublestarred_kwargs(node, node.kwargs)
for keyword in node.keywords:
if keyword.arg is None: # Python 3.5 **kwargs
self.add_doublestarred_kwargs(node, keyword.value)
continue

self.context.add_opcodes(
JavaOpcodes.DUP(),
JavaOpcodes.LDC_W(keyword.arg),
)
self.visit(keyword.value)
self.context.add_opcodes(
java.Map.put()
)

if getattr(node, 'kwargs', None) is not None: # Python 3.4 **kwargs
self.add_doublestarred_kwargs(node, node.kwargs)

# Set up the stack and invoke the callable
self.context.add_opcodes(
Expand Down

0 comments on commit 47335f6

Please sign in to comment.