Kotlin: Is local function passed to inlined functions as parameter inlined? - kotlin

When passing a lambda or anonymous function to inlined functions as a parameter, it's quite simple, the code is pasted to the calling position, but when passing a local function as a parameter, the result seems different(shown as below). I wonder if it's inlined? Why or why not?
For example:
inline fun foo(arg: () -> Int): Int {
return arg()
}
fun bar(): Int {
return 0
}
fun main(args: Array<String>) {
foo(::bar)
}
And decompiled Java code:
public final class InlinedFuncKt {
public static final int foo(#NotNull Function0 arg) {
Intrinsics.checkParameterIsNotNull(arg, "arg");
return ((Number)arg.invoke()).intValue();
}
public static final int bar() {
return 0;
}
public static final void main(#NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
bar();
}
}

bar() is not declared to be inlined. So why would you expect it to be inlined?!
In other words: it would be simply wrong that the signature of method A affects (implicitly) the signature of another method B.
Your idea would (somehow) affect "semantics" of bar() - just because you used bar() as argument to another method call.

As you can in the decompiled code, kotlin does not inline bar in your case, but it does inline it if it is declared as inline fun bar().
The rule is that lambdas are inlined when they are passed to an inlined function. In all other cases like passing a function reference or a lambda object, no inlining is done.

Related

Distinguish function from extension

I tried to write a funtion, which can be inserted in any expresion, in order to log the value:
val x = (2.debug() + 3.debug()).debug("2+3")
But instead I wrote the following endless loop:
fun debug (message: String) {
Log.d (R.string.app_name.toString(), message) }
fun <T> T.debug (tag: String = "value"): T {
debug ("$tag: $this")
return this
}
My aim was to write a "normal" function (1st) and an extension function (2nd) and the extension function should call the normal function.
The problem in my code is: the extension function calls itself instead of the normal function. I do not understand this, because I did not specify an instance receiver in the extension function.
How to fix this?
Given you have different param names in each function, you could change the second function to call the first one with named arguments:
fun <T> T.debug (tag: String = "value"): T {
debug (message = "$tag: $this")
return this
}
I couldn't find a way to strip this out of extension method, so the best way out would be to create differently named wrapper:
fun <T> T.debug (tag: String = "value"): T {
debugWrapper( "$tag: $this")
return this
}
fun debug (message: String) {
Log.d ("tag", message)
}
private fun debugWrapper (message: String) {
debug(message)
}
I think you were looking to keep existing calls to debug and this delivers.
I've tried to look into decompiled code (minus the default parameter, for clarity) in hope to differentiate them by namespace:
public final class TestclassKt {
public static final Object debug(Object $this$debug, #NotNull String tag) {
Intrinsics.checkNotNullParameter(tag, "tag");
debug($this$debug, tag + ": " + $this$debug);
return $this$debug;
}
public static final void debug(#NotNull String message) {
Intrinsics.checkNotNullParameter(message, "message");
Log.d("tag", message);
}
}
But the 2 methods are in same namespace and the only way to differentiate them is by arguments.
I think you've broken Kotlin.

JUnit 5 and Arguments.of() with functions

Writing a JUnit 5 parameterized test and need to pass functions to the test using Arguments.of(), but there are 2 compile errors that I don't know how to fix. Any help would be appreciated.
The method of(Object...) in the type Arguments is not applicable for the arguments (boolean, String::length)
The target type of this expression must be a functional interface
public static Stream<Arguments> some() {
return Stream.of(Arguments.of(true, String::length));
}
#ParameterizedTest
#MethodSource
public <T> void some(final T input, final Function<String, Integer> length) {
}
The following works as expected.
public void sample() {
some(true, String::length);
}
Wrap the arguments in a helper method
Similar to the answer "wrap it in a class", but possibly less intrusive, is to use a helper method to pass the functional interface as a java.lang.Object.
For example, the first raw method reference, Math::ciel, in this parameterized test:
#ParameterizedTest
#MethodSource("testCases")
void shouldExerciseMethod(Function<Double, Double> method, Double expected) {
assertEquals(expected, method.apply(1.5d), 1.0E-8d);
}
static Stream<Arguments> testCases() {
return Stream.of(Arguments.of(Math::ceil, 2.0d),
Arguments.of(Math::floor, 1.0d));
}
causes this compilation error:
java: method of in interface org.junit.jupiter.params.provider.Arguments cannot be applied to given types;
required: java.lang.Object[]
found: Math::ceil,double
reason: varargs mismatch; java.lang.Object is not a functional interface
which you can get around by passing the arguments through a helper method:
static <T, U> Arguments args(Function<T, U> method, U expected) {
return Arguments.of(method, expected);
}
so:
static Stream<Arguments> testCases() {
return Stream.of(args(Math::ceil, 2.0d),
args(Math::floor, 1.0d));
}
My attempts to make the idiom more general using varargs failed with variations on the same error, so I have ended up overloading it whenever I need another signature.
The function needs to be wrapped in a class.
public static class P {
private final Function<String, Integer> mFunction;
public P(final Function<String, Integer> function) {
mFunction = function;
}
public Function<String, Integer> function() {
return mFunction;
}
}
public static Stream<Arguments> some() {
return Stream.of(Arguments.of(3, "abc", new P(String::length)));
}
#ParameterizedTest
#MethodSource
public <T> void some(final int expect, final String input, final P p) {
assertEquals(expect, p.function().apply(input));
}
I liked #adrian-redgers solution, but I think overloading a method for each signature needed is a bit overkill.
You only really need to convert the functional interface to an object. So the solution I implemented was:
/**
* Helps to use {#link org.junit.jupiter.params.provider.Arguments#of(Object...)}, as functional
* interfaces cannot be converted into an object directly.
*/
public class ArgumentsWrapper {
private ArgumentsWrapper() {
throw new IllegalStateException(
ArgumentsWrapper.class + " util class cannot be instantiated");
}
public static <T, U> Function<T, U> wrap(Function<T, U> function) {
return function;
}
}
Then, it can be used as:
public static Stream<Arguments> testMapAlarmTypeConfigWithLanguage() {
return Stream.of(
// Statically imported ArgumentsWrapper#wrap
Arguments.of(null, wrap(AlarmTypeConfig::getNameInEnglish)),
Arguments.of("en-us", wrap(AlarmTypeConfig::getNameInEnglish)),
Arguments.of("es-es", wrap(AlarmTypeConfig::getNameInSpanish)));
}

Extension functions issue

Run into some difficulties while using extension functions with existing java api. Here some pseudocode
public class Test {
public Test call() {
return this;
}
public Test call(Object param) {
return this;
}
public void configure1() {
}
public void configure2(boolean value) {
}
}
Kotlin test
fun Test.call(toApply: Test.() -> Unit): Test {
return call()
.apply(toApply)
}
fun Test.call(param: Any, toApply: Test.() -> Unit): Test {
return call(param)
.apply(toApply)
}
fun main(args: Array<String>) {
val test = Test()
//refers to java method; Unresolved reference: configure1;Unresolved reference: configure2
test.call {
configure1()
configure2(true)
}
//refers to my extension function and works fine
test.call(test) {
configure1()
configure2(true)
}
}
Why only function with param works fine ? what’s the difference ?
Kotlin will always give precedence to the classes member functions. Since Test:call(Object) is a possible match, Kotlin selects that method rather than your extension function.
The extension function with the added parameter is resolved the way you expect because the Test class does not have any member functions that would take precedent (no matching signature), so your extension method is selected.
Here is a link to the Kotlin documentation as to how extension functions are resolved: https://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically

Can I access the "enclosing" object for an inlined function call in Kotlin?

Idea: Make an inlined convenience function to synchronize a function call on the "inlining" object's intrinsic lock:
public inline fun <T> intrinsicSync(block: () -> T): T {
return synchronized(intrinsicLockOfTheObjectUsingTheFunction) {
block()
}
}
fun someFunction(parameter: SomeClass) {
intrinsicSync(sharedResource.operation(parameter))
}
Is this possible? If not, is there a different but equally convenient way? I concede that
fun someFunction(parameter: SomeClass) {
synchronized(this) {
sharedResource.operation(parameter)
}
}
isn't the biggest hassle in the world, but I'd still like to know if it's possible to do something similar to what I imagined.
The most you can do is give the function a receiver (i.e. make it an extension function):
public inline fun <T> Any.intrinsicSync(crossinline block: () -> T): T {
// ^ within this function, `this` refers to the receiver
return synchronized(this, block)
}
and then in a member function, you can do
this.intrinsicSync { sharedResource.operation() }
and since this is inferred you should be able to shorten this to
intrinsicSync { sharedResource.operation() }
Keep in mind that extension functions cannot shadow member functions, so if this object has another function with the same signature, it won't choose the extension function.
In your case, though, this seems unlikely.

Call Kotlin inline function from Java

Exceptions.kt:
#Suppress("NOTHING_TO_INLINE")
inline fun generateStyleNotCorrectException(key: String, value: String) =
AOPException(key + " = " + value)
In kotlin:
fun inKotlin(key: String, value: String) {
throw generateStyleNotCorrectException(key, value) }
It works in kotlin and the function is inlined.
But when used in Java code, It just cannot be inlined,
and still a normal static method call (seen from the decompiled contents).
Something like this:
public static final void inJava(String key, String value) throws AOPException {
throw ExceptionsKt.generateStyleNotCorrectException(key, value);
// when decompiled, it has the same contents as before , not the inlined contents.
}
The inlining that's done by the Kotlin compiler is not supported for Java files, since the Java compiler is unaware of this transformation (see this answer about why reified generics do not work from Java at all).
As for other use cases of inlining (most commonly when passing in a lambda as a parameter), as you've already discovered, the bytecode includes a public static method so that the inline function can be still called from Java. In this case, however, no inlining occurs.
Yes, u can do it
In Kotlin file:
Builder.sendEvent { event ->
YandexMetrica.reportEvent(event)
}
.build();
In Java file:
Builder.sendEvent(new Function1<String, Unit>() {
#Override
public Unit invoke(String event) {
Log.i("TEST", event);
return null;
}
})
.build();