1111
1212import com .fasterxml .jackson .core .JsonGenerator ;
1313
14+ import com .facebook .infer .annotation .Assertions ;
1415import com .facebook .systrace .Systrace ;
1516
1617import javax .annotation .Nullable ;
4647 * with the same name.
4748 */
4849public abstract class BaseJavaModule implements NativeModule {
49- private interface ArgumentExtractor {
50- @ Nullable Object extractArgument (
50+ // taken from Libraries/Utilities/MessageQueue.js
51+ static final public String METHOD_TYPE_REMOTE = "remote" ;
52+ static final public String METHOD_TYPE_REMOTE_ASYNC = "remoteAsync" ;
53+
54+ private static abstract class ArgumentExtractor {
55+ public int getJSArgumentsNeeded () {
56+ return 1 ;
57+ }
58+
59+ public abstract @ Nullable Object extractArgument (
5160 CatalystInstance catalystInstance , ReadableNativeArray jsArguments , int atIndex );
5261 }
5362
@@ -129,67 +138,110 @@ public Object extractArgument(
129138 }
130139 };
131140
132- private static ArgumentExtractor [] buildArgumentExtractors (Class [] parameterTypes ) {
133- ArgumentExtractor [] argumentExtractors = new ArgumentExtractor [parameterTypes .length ];
134- for (int i = 0 ; i < parameterTypes .length ; i ++) {
135- Class argumentClass = parameterTypes [i ];
136- if (argumentClass == Boolean .class || argumentClass == boolean .class ) {
137- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_BOOLEAN ;
138- } else if (argumentClass == Integer .class || argumentClass == int .class ) {
139- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_INTEGER ;
140- } else if (argumentClass == Double .class || argumentClass == double .class ) {
141- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_DOUBLE ;
142- } else if (argumentClass == Float .class || argumentClass == float .class ) {
143- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_FLOAT ;
144- } else if (argumentClass == String .class ) {
145- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_STRING ;
146- } else if (argumentClass == Callback .class ) {
147- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_CALLBACK ;
148- } else if (argumentClass == ReadableMap .class ) {
149- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_MAP ;
150- } else if (argumentClass == ReadableArray .class ) {
151- argumentExtractors [i ] = ARGUMENT_EXTRACTOR_ARRAY ;
152- } else {
153- throw new RuntimeException (
154- "Got unknown argument class: " + argumentClass .getSimpleName ());
155- }
141+ static final private ArgumentExtractor ARGUMENT_EXTRACTOR_PROMISE = new ArgumentExtractor () {
142+ @ Override
143+ public int getJSArgumentsNeeded () {
144+ return 2 ;
156145 }
157- return argumentExtractors ;
158- }
146+
147+ @ Override
148+ public Promise extractArgument (
149+ CatalystInstance catalystInstance , ReadableNativeArray jsArguments , int atIndex ) {
150+ Callback resolve = (Callback ) ARGUMENT_EXTRACTOR_CALLBACK
151+ .extractArgument (catalystInstance , jsArguments , atIndex );
152+ Callback reject = (Callback ) ARGUMENT_EXTRACTOR_CALLBACK
153+ .extractArgument (catalystInstance , jsArguments , atIndex + 1 );
154+ return new PromiseImpl (resolve , reject );
155+ }
156+ };
159157
160158 private class JavaMethod implements NativeMethod {
159+
161160 private Method mMethod ;
162- private ArgumentExtractor [] mArgumentExtractors ;
163- private Object [] mArguments ;
161+ private final ArgumentExtractor [] mArgumentExtractors ;
162+ private final Object [] mArguments ;
163+ private String mType = METHOD_TYPE_REMOTE ;
164+ private final int mJSArgumentsNeeded ;
164165
165166 public JavaMethod (Method method ) {
166167 mMethod = method ;
167168 Class [] parameterTypes = method .getParameterTypes ();
168169 mArgumentExtractors = buildArgumentExtractors (parameterTypes );
169170 // Since native methods are invoked from a message queue executed on a single thread, it is
170171 // save to allocate only one arguments object per method that can be reused across calls
171- mArguments = new Object [mArgumentExtractors .length ];
172+ mArguments = new Object [parameterTypes .length ];
173+ mJSArgumentsNeeded = calculateJSArgumentsNeeded ();
174+ }
175+
176+ private ArgumentExtractor [] buildArgumentExtractors (Class [] paramTypes ) {
177+ ArgumentExtractor [] argumentExtractors = new ArgumentExtractor [paramTypes .length ];
178+ for (int i = 0 ; i < paramTypes .length ; i += argumentExtractors [i ].getJSArgumentsNeeded ()) {
179+ Class argumentClass = paramTypes [i ];
180+ if (argumentClass == Boolean .class || argumentClass == boolean .class ) {
181+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_BOOLEAN ;
182+ } else if (argumentClass == Integer .class || argumentClass == int .class ) {
183+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_INTEGER ;
184+ } else if (argumentClass == Double .class || argumentClass == double .class ) {
185+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_DOUBLE ;
186+ } else if (argumentClass == Float .class || argumentClass == float .class ) {
187+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_FLOAT ;
188+ } else if (argumentClass == String .class ) {
189+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_STRING ;
190+ } else if (argumentClass == Callback .class ) {
191+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_CALLBACK ;
192+ } else if (argumentClass == Promise .class ) {
193+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_PROMISE ;
194+ Assertions .assertCondition (
195+ i == paramTypes .length - 1 , "Promise must be used as last parameter only" );
196+ mType = METHOD_TYPE_REMOTE_ASYNC ;
197+ } else if (argumentClass == ReadableMap .class ) {
198+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_MAP ;
199+ } else if (argumentClass == ReadableArray .class ) {
200+ argumentExtractors [i ] = ARGUMENT_EXTRACTOR_ARRAY ;
201+ } else {
202+ throw new RuntimeException (
203+ "Got unknown argument class: " + argumentClass .getSimpleName ());
204+ }
205+ }
206+ return argumentExtractors ;
207+ }
208+
209+ private int calculateJSArgumentsNeeded () {
210+ int n = 0 ;
211+ for (ArgumentExtractor extractor : mArgumentExtractors ) {
212+ n += extractor .getJSArgumentsNeeded ();
213+ }
214+ return n ;
215+ }
216+
217+ private String getAffectedRange (int startIndex , int jsArgumentsNeeded ) {
218+ return jsArgumentsNeeded > 1 ?
219+ "" + startIndex + "-" + (startIndex + jsArgumentsNeeded - 1 ) : "" + startIndex ;
172220 }
173221
174222 @ Override
175223 public void invoke (CatalystInstance catalystInstance , ReadableNativeArray parameters ) {
176224 Systrace .beginSection (Systrace .TRACE_TAG_REACT_JAVA_BRIDGE , "callJavaModuleMethod" );
177225 try {
178- if (mArgumentExtractors . length != parameters .size ()) {
226+ if (mJSArgumentsNeeded != parameters .size ()) {
179227 throw new NativeArgumentsParseException (
180228 BaseJavaModule .this .getName () + "." + mMethod .getName () + " got " +
181- parameters .size () + " arguments, expected " + mArgumentExtractors . length );
229+ parameters .size () + " arguments, expected " + mJSArgumentsNeeded );
182230 }
183231
184- int i = 0 ;
232+ int i = 0 , jsArgumentsConsumed = 0 ;
185233 try {
186234 for (; i < mArgumentExtractors .length ; i ++) {
187- mArguments [i ] = mArgumentExtractors [i ].extractArgument (catalystInstance , parameters , i );
235+ mArguments [i ] = mArgumentExtractors [i ].extractArgument (
236+ catalystInstance , parameters , jsArgumentsConsumed );
237+ jsArgumentsConsumed += mArgumentExtractors [i ].getJSArgumentsNeeded ();
188238 }
189239 } catch (UnexpectedNativeTypeException e ) {
190240 throw new NativeArgumentsParseException (
191241 e .getMessage () + " (constructing arguments for " + BaseJavaModule .this .getName () +
192- "." + mMethod .getName () + " at argument index " + i + ")" ,
242+ "." + mMethod .getName () + " at argument index " +
243+ getAffectedRange (jsArgumentsConsumed , mArgumentExtractors [i ].getJSArgumentsNeeded ()) +
244+ ")" ,
193245 e );
194246 }
195247
@@ -214,6 +266,16 @@ public void invoke(CatalystInstance catalystInstance, ReadableNativeArray parame
214266 Systrace .endSection (Systrace .TRACE_TAG_REACT_JAVA_BRIDGE );
215267 }
216268 }
269+
270+ /**
271+ * Determines how the method is exported in JavaScript:
272+ * METHOD_TYPE_REMOTE for regular methods
273+ * METHOD_TYPE_REMOTE_ASYNC for methods that return a promise object to the caller.
274+ */
275+ @ Override
276+ public String getType () {
277+ return mType ;
278+ }
217279 }
218280
219281 @ Override
0 commit comments