Skip to content

Commit 438f2cc

Browse files
authored
The default values of method arguments require to use the method owner instance (#339)
1 parent 7e76ef8 commit 438f2cc

File tree

7 files changed

+60
-54
lines changed

7 files changed

+60
-54
lines changed

airframe-codec/.jvm/src/test/scala/wvlet/airframe/codec/ObjectCodecTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class ObjectCodecTest extends CodecSpec {
5050
h.getLastValue shouldBe v
5151
}
5252

53-
"populate the default value when missing" in {
53+
"populate the default value when missing" taggedAs ("default-arg") in {
5454
val packer = MessagePack.newBufferPacker
5555
packer.packMapHeader(1)
5656
packer.packString("i")

airframe-codec/src/main/scala/wvlet/airframe/codec/ObjectCodec.scala

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,56 +19,19 @@ import wvlet.airframe.surface._
1919

2020
import scala.util.{Failure, Success, Try}
2121

22-
object ParamListCodec extends LogSupport {
23-
val defaultEmptyParamBinder = { p: Parameter =>
24-
Zero.zeroOf(p.surface)
25-
}
26-
27-
/**
28-
* Access the default value of a method argument, through the owner object.
29-
*
30-
* {{{
31-
* public class wvlet.airframe.opts.LauncherTest$MyCommand {
32-
* public void hello(int, java.lang.String);
33-
* descriptor: (ILjava/lang/String;)V
34-
* public int hello$default$1();
35-
* descriptor: ()I
36-
* }
37-
* }}}
38-
*
39-
* @param methodOwner
40-
* @return
41-
*/
42-
def resolveMethodArgDefaultFromOwnerObject(methodOwner: Any) = { p: Parameter =>
43-
p match {
44-
case m: MethodParameter =>
45-
try {
46-
val methodName = "%s$default$%d".format(m.method.name, p.index + 1)
47-
val dm = methodOwner.getClass.getMethod(methodName)
48-
dm.invoke(methodOwner)
49-
} catch {
50-
case e: Throwable =>
51-
Zero.zeroOf(p.surface)
52-
}
53-
case ohter =>
54-
Zero.zeroOf(p.surface)
55-
}
56-
}
57-
}
58-
5922
/**
6023
* A generic codec for parameter lists:
6124
* - array form: [v1, v2, ...]
6225
* - map form: {k1:v1, k2:v2, ..}
6326
* @param name
6427
* @param params
6528
* @param paramCodec
66-
* @param emptyParamBinder
29+
* @param methodOwner
6730
*/
6831
class ParamListCodec(name: String,
6932
params: IndexedSeq[Parameter],
7033
paramCodec: Seq[MessageCodec[_]],
71-
emptyParamBinder: Parameter => Any = ParamListCodec.defaultEmptyParamBinder)
34+
methodOwner: Option[Any] = None)
7235
extends MessageCodec[Seq[Any]]
7336
with LogSupport {
7437
private lazy val codecTable =
@@ -105,6 +68,21 @@ class ParamListCodec(name: String,
10568
}
10669
}
10770

71+
private def getParamDefaultValue(p: Parameter): Any = {
72+
p match {
73+
case m: MethodParameter =>
74+
methodOwner
75+
.flatMap { owner =>
76+
// If the method owner instance is provided, we can resolve method arg default values
77+
m.getMethodArgDefaultValue(owner)
78+
}
79+
.orElse(p.getDefaultValue)
80+
.getOrElse(Zero.zeroOf(p.surface))
81+
case other =>
82+
p.getDefaultValue.getOrElse(Zero.zeroOf(p.surface))
83+
}
84+
}
85+
10886
override def unpack(u: Unpacker, v: MessageHolder): Unit = {
10987
val numParams = params.length
11088

@@ -118,7 +96,7 @@ class ParamListCodec(name: String,
11896
paramCodec(index).unpack(u, v)
11997
val arg = if (v.isNull) {
12098
trace(v.getError)
121-
p.getDefaultValue.getOrElse(Zero.zeroOf(p.surface))
99+
getParamDefaultValue(p)
122100
} else {
123101
v.getLastValue
124102
}
@@ -128,7 +106,8 @@ class ParamListCodec(name: String,
128106
// Populate args with the default or zero value
129107
while (index < numParams) {
130108
val p = params(index)
131-
b += p.getDefaultValue.getOrElse(emptyParamBinder(p))
109+
val v = getParamDefaultValue(p)
110+
b += v
132111
index += 1
133112
}
134113
// Ignore additional args
@@ -165,13 +144,11 @@ class ParamListCodec(name: String,
165144
val args = for (i <- 0 until numParams) yield {
166145
val p = params(i)
167146
val paramName = CName.toCanonicalName(p.name)
168-
map.get(paramName) match {
169-
case Some(x) =>
170-
x
171-
case None =>
172-
p.getDefaultValue.getOrElse(emptyParamBinder(p))
173-
}
147+
map
148+
.get(paramName)
149+
.getOrElse(getParamDefaultValue(p))
174150
}
151+
trace(s"map:${map.mkString(",")}, args:${args.mkString(", ")}")
175152
v.setObject(args)
176153
case other =>
177154
u.skipValue

airframe-launcher/src/main/scala/wvlet/airframe/launcher/Launcher.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,14 @@ class CommandLauncher(private[launcher] val launcherInfo: LauncherInfo,
373373
methodSurface.name,
374374
methodSurface.args.toIndexedSeq,
375375
paramCodecs,
376-
// We need to supply default values by using the parent object
377-
ParamListCodec.resolveMethodArgDefaultFromOwnerObject(parentObj)
376+
// We need to supply method owner object to resolve function arg values
377+
methodOwner = Some(parentObj)
378378
)
379379

380380
val msgpack = result.parseTree.toMsgPack
381381
methodArgCodec
382382
.unpackMsgPack(msgpack).map { args =>
383+
trace(s"calling method ${methodSurface} with args: ${args.mkString(", ")}")
383384
val methodResult = methodSurface.call(parentObj, args: _*)
384385
LauncherResult(stack, Some(methodResult))
385386
}

airframe-launcher/src/test/scala/wvlet/airframe/launcher/ArgProcessorTest.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package wvlet.airframe.launcher
1515
import wvlet.airframe.AirframeSpec
1616
import wvlet.airframe.launcher.LauncherTest.capture
1717
import wvlet.log.LogSupport
18+
import wvlet.log.io.IOUtil
1819

1920
object ArgProcessorTest {
2021

@@ -56,6 +57,17 @@ object ArgProcessorTest {
5657
Launcher
5758
.of[Cmd]
5859
.add(subCommandModule, name = "sub", description = "sub command")
60+
61+
class FunctionArg(
62+
@option(prefix = "-e", description = "Environment")
63+
env: String = "default-env")
64+
extends LogSupport {
65+
66+
@command(description = "Start a proxy server")
67+
def proxy(
68+
@option(prefix = "-p,--port", description = "port number")
69+
port: Int = IOUtil.randomPort) = {}
70+
}
5971
}
6072

6173
class ArgProcessorTest extends AirframeSpec {
@@ -119,4 +131,7 @@ class ArgProcessorTest extends AirframeSpec {
119131
c3 should include("nested2")
120132
}
121133

134+
"should support function arg" taggedAs ("farg") in {
135+
Launcher.of[FunctionArg].execute("proxy")
136+
}
122137
}

airframe-surface/jvm/src/main/scala/wvlet/airframe/surface/reflect/ReflectMethodSurface.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ case class ReflectMethodSurface(mod: Int, owner: Surface, name: String, returnTy
3636
override def call(obj: Any, x: Any*): Any = method match {
3737
case Some(m) =>
3838
if (x == null || x.isEmpty) {
39+
trace(s"Calling method ${name}")
3940
m.invoke(obj)
4041
} else {
41-
m.invoke(obj, x.map(_.asInstanceOf[AnyRef]): _*)
42+
val args = x.map(_.asInstanceOf[AnyRef])
43+
trace(s"Calling method ${name} with args: ${args.mkString(", ")}")
44+
m.invoke(obj, args: _*)
4245
}
4346
case None => null
4447
}

airframe-surface/jvm/src/main/scala/wvlet/airframe/surface/reflect/RuntimeMethodParameter.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313
*/
1414
package wvlet.airframe.surface.reflect
1515

16-
import wvlet.airframe.surface.{MethodParameter, MethodRef, Parameter, Surface}
1716
import java.{lang => jl}
1817

18+
import wvlet.airframe.surface.{MethodParameter, MethodRef, Surface}
1919
import wvlet.log.LogSupport
2020

21-
import scala.util.Try
22-
2321
/**
2422
* MethodParameter implementation using reflection for accessing parameter values
2523
*/
@@ -65,4 +63,15 @@ case class RuntimeMethodParameter(
6563
}
6664
}
6765
}
66+
67+
override def getMethodArgDefaultValue(methodOwner: Any): Option[Any] = {
68+
try {
69+
val methodName = "%s$default$%d".format(method.name, index + 1)
70+
val dm = methodOwner.getClass.getMethod(methodName)
71+
Some(dm.invoke(methodOwner))
72+
} catch {
73+
case e: Throwable =>
74+
None
75+
}
76+
}
6877
}

airframe-surface/shared/src/main/scala/wvlet/airframe/surface/Surface.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ case class MethodRef(owner: Class[_], name: String, paramTypes: Seq[Class[_]], i
8181

8282
trait MethodParameter extends Parameter {
8383
def method: MethodRef
84+
def getMethodArgDefaultValue(methodOwner: Any): Option[Any] = getDefaultValue
8485
}
8586

8687
trait MethodSurface extends ParameterBase {

0 commit comments

Comments
 (0)