31
31
import score .impl .TypeConverter ;
32
32
33
33
import java .lang .reflect .Constructor ;
34
+ import java .lang .reflect .Executable ;
34
35
import java .lang .reflect .InvocationTargetException ;
35
36
import java .lang .reflect .Method ;
36
37
import java .math .BigInteger ;
@@ -149,7 +150,12 @@ private Score deploy(Account caller, Score score, Class<?> mainClass, Object[] p
149
150
// User SCORE should only have one public constructor
150
151
throw new AssertionError ("multiple public constructors found" );
151
152
}
152
- score .setInstance (ctor [0 ].newInstance (params ));
153
+ var readonly = checkAnnotationsAndReadOnly (mainClass , ctor [0 ], params .length , false , false , false );
154
+ if (readonly ) {
155
+ throw new IllegalStateException ("ExternalReadOnlyConstructor(class=" +mainClass +")" );
156
+ }
157
+ var params2 = convertParameters (ctor [0 ], params );
158
+ score .setInstance (ctor [0 ].newInstance (params2 ));
153
159
applyFrame ();
154
160
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e ) {
155
161
throw e ;
@@ -310,55 +316,8 @@ private Object handleCall(Account from, BigInteger value, boolean transfer, bool
310
316
throw new IllegalArgumentException (
311
317
"NoValidMethod(score=" +score .getAddress ()+",method=" +method +")" );
312
318
}
313
- if (scoreClass .isAnnotationPresent (TScore .class )) {
314
- // reject calling internal method
315
- var external = scoreMethod .getAnnotation (TExternal .class );
316
- if (external ==null ) {
317
- throw new IllegalArgumentException (
318
- "NotExternal(score=" +score .getAddress ()+",method=" +method +")"
319
- );
320
- }
321
-
322
- // reject calling writable in readonly context
323
- if (isReadonly () && !external .readonly ()) {
324
- throw new IllegalArgumentException (
325
- "PermissionDenied(score=" +score .getAddress ()+",method=" +method +")"
326
- );
327
- }
328
- readonly |= external .readonly ();
329
-
330
- // check payable
331
- if (!external .payable () && value .signum ()>0 ) {
332
- throw new IllegalArgumentException (
333
- "NotPayable(score=" +score .getAddress ()+",method=" +method +")"
334
- );
335
- }
336
-
337
- // optional parameter check.
338
- var parameterAnnotations = scoreMethod .getParameterAnnotations ();
339
- int minParams = parameterAnnotations .length ;
340
- for (int i =0 ; i <parameterAnnotations .length ; i ++) {
341
- if (Arrays .stream (parameterAnnotations [i ])
342
- .anyMatch ((a )->(a .annotationType ().equals (TOptional .class )))) {
343
- if (i < minParams ) {
344
- minParams = i ;
345
- } else {
346
- throw new IllegalArgumentException (
347
- "InvalidOptionalTag(score=" + score .getAddress () + ",method=" + method + ")"
348
- );
349
- }
350
- }
351
- }
352
- if (params .length < minParams ) {
353
- throw new IllegalArgumentException (
354
- "NotEnoughParams(score=" + score .getAddress ()
355
- + ",method=" + method
356
- + ",min=" + minParams
357
- + ",given=" + params .length
358
- + ")"
359
- );
360
- }
361
- }
319
+ readonly = checkAnnotationsAndReadOnly (scoreClass , scoreMethod , params .length ,
320
+ true , isReadonly ()|readonly , value .signum ()>0 );
362
321
Account to = score .getAccount ();
363
322
pushFrame (from , to , readonly , method , value );
364
323
try {
@@ -592,11 +551,55 @@ public static ServiceManager getServiceManager() {
592
551
return getServiceManagerImpl ();
593
552
}
594
553
595
- private static Object [] convertParameters (Method method , Object [] params ) {
554
+ private static Object defaultValueFor (Class <?> clz ) {
555
+ if (clz ==boolean .class ) {
556
+ return false ;
557
+ } else if (clz ==byte .class || clz ==Byte .class ) {
558
+ return (byte ) 0x00 ;
559
+ } else if (clz ==char .class || clz ==Character .class ) {
560
+ return (char ) 0x00 ;
561
+ } else if (clz ==short .class || clz ==Short .class ) {
562
+ return (short ) 0x00 ;
563
+ } else if (clz ==int .class || clz ==Integer .class ) {
564
+ return 0 ;
565
+ } else if (clz ==long .class || clz ==Long .class ) {
566
+ return 0L ;
567
+ } else if (clz ==BigInteger .class ) {
568
+ return BigInteger .ZERO ;
569
+ }
570
+ return null ;
571
+ }
572
+
573
+ private static Object [] convertParameters (Executable method , Object [] params ) {
596
574
Class <?>[] parameterTypes = method .getParameterTypes ();
597
575
int numberOfParams = parameterTypes .length ;
598
576
Object [] parsedParams = new Object [numberOfParams ];
599
577
578
+ var scoreClz = method .getDeclaringClass ();
579
+ if (scoreClz .isAnnotationPresent (TScore .class )) {
580
+ var annotations = method .getParameterAnnotations ();
581
+ int minParams = annotations .length ;
582
+ for (int i =0 ; i <annotations .length ; i ++) {
583
+ if (Arrays .stream (annotations [i ])
584
+ .anyMatch ((a )->(a .annotationType ().equals (TOptional .class )))) {
585
+ if (i < minParams ) {
586
+ minParams = i ;
587
+ } else {
588
+ throw new IllegalArgumentException (
589
+ "InvalidOptionalTag(class=" + method .getDeclaringClass ().getName () + ",method=" + method + ")"
590
+ );
591
+ }
592
+ }
593
+ }
594
+ if (params .length < minParams ) {
595
+ throw new IllegalArgumentException (
596
+ String .format ("NotEnoughParameter(given=%d,min=%d)" ,
597
+ params .length , minParams
598
+ )
599
+ );
600
+ }
601
+ }
602
+
600
603
int i = 0 ;
601
604
for (Class <?> parameterClass : parameterTypes ) {
602
605
if (parameterClass == Map .class || parameterClass == List .class ) {
@@ -605,7 +608,7 @@ private static Object[] convertParameters(Method method, Object[] params) {
605
608
i , parameterClass .getName ()));
606
609
}
607
610
if (i >=params .length ) {
608
- parsedParams [i ] = null ;
611
+ parsedParams [i ] = defaultValueFor ( parameterClass ) ;
609
612
} else {
610
613
try {
611
614
parsedParams [i ] = TypeConverter .cast (params [i ], parameterClass );
@@ -620,6 +623,59 @@ private static Object[] convertParameters(Method method, Object[] params) {
620
623
return parsedParams ;
621
624
}
622
625
626
+
627
+ private boolean checkAnnotationsAndReadOnly (Class <?> scoreClz , Executable method , int params , boolean external , boolean readonly , boolean payable ) {
628
+ if (!scoreClz .isAnnotationPresent (TScore .class )) {
629
+ return false ;
630
+ }
631
+ var externalAnnotation = method .getAnnotation (TExternal .class );
632
+ if (externalAnnotation ==null ) {
633
+ if (external ) {
634
+ throw new IllegalArgumentException (
635
+ "NotExternal(score=" +scoreClz .getName ()+",method=" +method +")"
636
+ );
637
+ }
638
+ } else {
639
+ if (readonly && !externalAnnotation .readonly ()) {
640
+ throw new IllegalArgumentException (
641
+ "PermissionDenied(score=" +
642
+ scoreClz .getName ()+",method=" +method +")"
643
+ );
644
+ }
645
+ readonly |= externalAnnotation .readonly ();
646
+ if (payable && !externalAnnotation .payable ()) {
647
+ throw new IllegalArgumentException (
648
+ "NotPayable(score=" +
649
+ scoreClz .getName ()+",method=" +method +")"
650
+ );
651
+ }
652
+ }
653
+ var annotations = method .getParameterAnnotations ();
654
+ int minParams = annotations .length ;
655
+ for (int i =0 ; i <annotations .length ; i ++) {
656
+ if (Arrays .stream (annotations [i ])
657
+ .anyMatch ((a )->(a .annotationType ().equals (TOptional .class )))) {
658
+ if (i < minParams ) {
659
+ minParams = i ;
660
+ } else {
661
+ throw new IllegalArgumentException (
662
+ "InvalidOptionalTag(class=" + scoreClz .getName () + ",method=" + method + ")"
663
+ );
664
+ }
665
+ }
666
+ }
667
+ if (params < minParams ) {
668
+ throw new IllegalArgumentException (
669
+ String .format ("NotEnoughParameter(score=%s,method=%s,given=%d,min=%d)" ,
670
+ scoreClz .getName (),
671
+ method .toString (),
672
+ params , minParams
673
+ )
674
+ );
675
+ }
676
+ return readonly ;
677
+ }
678
+
623
679
private Object invokeMethod (Score score , String methodName , Object [] params ) {
624
680
try {
625
681
var scoreObj = score .getInstance ();
0 commit comments