diff --git a/src/main/java/net/starlark/java/eval/BuiltinFunction.java b/src/main/java/net/starlark/java/eval/BuiltinFunction.java index aab7a8d6ea2f89..80f733c6f9a8a5 100644 --- a/src/main/java/net/starlark/java/eval/BuiltinFunction.java +++ b/src/main/java/net/starlark/java/eval/BuiltinFunction.java @@ -156,6 +156,16 @@ private Object[] getArgumentVector( ParamDescriptor[] parameters = desc.getParameters(); + // Fast case: reuse positional as Java argument vector + // StringModule methods, which are treated specially below, will never match this case since + // their "self" parameter is restricted to String and thus + // getPositionalsCanBeJavaArgumentVector() is false. + if (desc.getPositionalsCanBeJavaArgumentVector() + && positional.length == parameters.length + && named.length == 0) { + return positional; + } + // Allocate argument vector. int n = parameters.length; if (desc.acceptsExtraArgs()) { diff --git a/src/main/java/net/starlark/java/eval/MethodDescriptor.java b/src/main/java/net/starlark/java/eval/MethodDescriptor.java index 53c4f5265dbceb..b89f4c7b4a1d28 100644 --- a/src/main/java/net/starlark/java/eval/MethodDescriptor.java +++ b/src/main/java/net/starlark/java/eval/MethodDescriptor.java @@ -46,6 +46,7 @@ final class MethodDescriptor { private final boolean allowReturnNones; private final boolean useStarlarkThread; private final boolean useStarlarkSemantics; + private final boolean positionalsCanBeJavaArgumentVector; private enum HowToHandleReturn { NULL_TO_NONE, // any Starlark value; null -> None @@ -100,6 +101,20 @@ private MethodDescriptor( } else { howToHandleReturn = HowToHandleReturn.FROM_JAVA; } + + this.positionalsCanBeJavaArgumentVector = + !extraKeywords + && !extraPositionals + && !useStarlarkSemantics + && !useStarlarkThread + && Arrays.stream(parameters) + .allMatch(MethodDescriptor::paramCanBeUsedAsPositionalWithoutChecks); + } + + private static boolean paramCanBeUsedAsPositionalWithoutChecks(ParamDescriptor param) { + return param.isPositional() + && param.disabledByFlag() == null + && param.getAllowedClasses() == null; } /** Returns the StarlarkMethod annotation corresponding to this method. */ @@ -287,4 +302,12 @@ String getDoc() { boolean isSelfCall() { return selfCall; } + + /** + * Returns true if positional arguments can be used as a Java method call argument vector without + * further checks when parameter count matches. + */ + boolean getPositionalsCanBeJavaArgumentVector() { + return positionalsCanBeJavaArgumentVector; + } } diff --git a/src/test/java/net/starlark/java/eval/MethodLibraryTest.java b/src/test/java/net/starlark/java/eval/MethodLibraryTest.java index 0e76b265fe7a48..f2204aee53676e 100644 --- a/src/test/java/net/starlark/java/eval/MethodLibraryTest.java +++ b/src/test/java/net/starlark/java/eval/MethodLibraryTest.java @@ -22,7 +22,9 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import java.util.List; +import net.starlark.java.annot.Param; import net.starlark.java.annot.StarlarkBuiltin; +import net.starlark.java.annot.StarlarkMethod; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -772,6 +774,29 @@ ev.new Scenario() "','.join(elements=['foo', 'bar'])"); } + @StarlarkBuiltin(name = "named_only", doc = "") + static final class NamedOnly implements StarlarkValue { + @StarlarkMethod( + name = "foo", + documented = false, + parameters = { + @Param(name = "a"), + @Param(name = "b", named = true, defaultValue = "None", positional = false), + }) + public Object foo(Object a, Object b) { + return a; + } + } + + @Test + public void testNamedOnlyArgument() throws Exception { + ev.new Scenario() + .update("named_only", new NamedOnly()) + .testIfErrorContains( + "foo() accepts no more than 1 positional argument but got 2", + "named_only.foo([1, 2, 3], int)"); + } + @Test public void testStringJoinRequiresStrings() throws Exception { ev.new Scenario() diff --git a/src/test/java/net/starlark/java/eval/testdata/bench_call.star b/src/test/java/net/starlark/java/eval/testdata/bench_call.star new file mode 100644 index 00000000000000..5c0661947868de --- /dev/null +++ b/src/test/java/net/starlark/java/eval/testdata/bench_call.star @@ -0,0 +1,21 @@ +def bench_call_type(b): + for _ in range(b.n): + type(1) + +def bench_call_list_append(b): + for _ in range(b.n): + [].append("foo") + +def bench_call_dict_get(b): + d = {"foo": "bar"} + for _ in range(b.n): + d.get("baz") + +def bench_call_dict_get_none(b): + d = {"foo": "bar"} + for _ in range(b.n): + d.get("baz", None) + +def bench_call_bool(b): + for _ in range(b.n): + bool()