From afaa373ea56e914060a04734b3bbab68fe03a75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 18 Jan 2021 17:40:28 +0100 Subject: [PATCH] Allow omitting trailing null parameters in RPC invocations for convenience. --- .../li/cil/oc2/common/bus/RPCAdapter.java | 87 ++++++++++++++----- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java b/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java index f18eead6..ea0e2192 100644 --- a/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java +++ b/src/main/java/li/cil/oc2/common/bus/RPCAdapter.java @@ -278,8 +278,8 @@ public final class RPCAdapter implements Steppable { // have relatively few methods per object where the overhead of hashing would not // be worth it. So we just do a linear search, which also gives us maximal // flexibility for free (devices may dynamically change their methods). + final List fallbacks = new ArrayList<>(); String error = ERROR_UNKNOWN_METHOD; - outer: for (final RPCMethod method : device.getMethods()) { if (!Objects.equals(method.getName(), methodInvocation.methodName)) { continue; @@ -287,39 +287,84 @@ public final class RPCAdapter implements Steppable { final RPCParameter[] parametersSpec = method.getParameters(); if (methodInvocation.parameters.size() != parametersSpec.length) { + if (canTrailingParametersBeImplicitlyNull(methodInvocation.parameters, parametersSpec)) { + fallbacks.add(method); + } + error = ERROR_INVALID_PARAMETER_SIGNATURE; continue; // There may be an overload with matching parameter count. } - final Object[] parameters = new Object[parametersSpec.length]; - for (int i = 0; i < parametersSpec.length; i++) { - final RPCParameter parameterInfo = parametersSpec[i]; - try { - parameters[i] = gson.fromJson(methodInvocation.parameters.get(i), parameterInfo.getType()); - } catch (final Throwable e) { - error = ERROR_INVALID_PARAMETER_SIGNATURE; - continue outer; // There may be an overload with matching parameter types. - } + final Object[] parameters = getParameters(methodInvocation.parameters, parametersSpec); + if (parameters == null) { + error = ERROR_INVALID_PARAMETER_SIGNATURE; + continue; // There may be an overload with matching parameter types. } - if (method.isSynchronized() && !isMainThread) { - synchronizedInvocation = methodInvocation; - return; - } - - try { - final Object result = method.invoke(parameters); - writeMessage(Message.MESSAGE_TYPE_RESULT, result); - } catch (final Throwable e) { - writeError(e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()); - } + invokeMethod(methodInvocation, isMainThread, method, parameters); return; } + if (fallbacks.size() == 1) { + final RPCMethod method = fallbacks.get(0); + final Object[] parameters = getParameters(methodInvocation.parameters, method.getParameters()); + if (parameters != null) { + invokeMethod(methodInvocation, isMainThread, method, parameters); + return; + } + } + writeError(error); } + private void invokeMethod(final MethodInvocation methodInvocation, final boolean isMainThread, final RPCMethod method, final Object[] parameters) { + if (method.isSynchronized() && !isMainThread) { + synchronizedInvocation = methodInvocation; + return; + } + + try { + final Object result = method.invoke(parameters); + writeMessage(Message.MESSAGE_TYPE_RESULT, result); + } catch (final Throwable e) { + writeError(e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()); + } + } + + @Nullable + private Object[] getParameters(final JsonArray parameters, final RPCParameter[] parametersSpec) { + final Object[] result = new Object[parametersSpec.length]; + for (int i = 0; i < parametersSpec.length; i++) { + final RPCParameter parameterInfo = parametersSpec[i]; + if (parameters.size() > i) { + try { + result[i] = gson.fromJson(parameters.get(i), parameterInfo.getType()); + } catch (final Throwable e) { + return null; + } + } else { + result[i] = null; + } + } + return result; + } + + private boolean canTrailingParametersBeImplicitlyNull(final JsonArray parameters, final RPCParameter[] parametersSpec) { + if (parameters.size() > parametersSpec.length) { + return false; + } + + for (int i = parameters.size(); i < parametersSpec.length; i++) { + final Class type = parametersSpec[i].getType(); + if (type.isPrimitive()) { + return false; + } + } + + return true; + } + private void writeDeviceList() { writeMessage(Message.MESSAGE_TYPE_LIST, devices); }