1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.adservices.service.js;
18 
19 import static com.android.adservices.service.js.JSScriptEngineCommonConstants.WASM_MODULE_ARG_NAME;
20 import static com.android.adservices.service.js.JSScriptEngineCommonConstants.WASM_MODULE_BYTES_ID;
21 
22 import android.annotation.NonNull;
23 
24 import java.util.List;
25 import java.util.stream.Collectors;
26 
27 /** Helper method for generating code. */
28 public final class JSScriptEngineCommonCodeGenerator {
29 
30     /**
31      * @return The JS code for the definition an anonymous function containing the declaration of
32      *     the value of {@code args} and the invocation of the given {@code entryFunctionName}. If
33      *     the {@code addWasmBinary} parameter is true, the target function is expected to accept an
34      *     extra final parameter 'wasmModule' of type {@code WebAssembly.Module} and the method will
35      *     return a promise.
36      */
37     @NonNull
generateEntryPointCallingCode( @onNull List<JSScriptArgument> args, @NonNull String entryFunctionName, boolean addWasmBinary)38     public static String generateEntryPointCallingCode(
39             @NonNull List<JSScriptArgument> args,
40             @NonNull String entryFunctionName,
41             boolean addWasmBinary) {
42         StringBuilder resultBuilder = new StringBuilder("(function() {\n");
43         // Declare args as constant inside this function closure to avoid any direct access by
44         // the functions in the script we are calling.
45         for (JSScriptArgument arg : args) {
46             // Avoiding to use addJavaScriptInterface because too expensive, just
47             // declaring the string parameter as part of the script.
48             resultBuilder.append(arg.variableDeclaration());
49             resultBuilder.append("\n");
50         }
51 
52         String argumentPassing =
53                 args.stream().map(JSScriptArgument::name).collect(Collectors.joining(","));
54         if (addWasmBinary) {
55             argumentPassing += "," + WASM_MODULE_ARG_NAME;
56             resultBuilder.append(
57                     String.format(
58                             "return android.consumeNamedDataAsArrayBuffer(\"%s\")"
59                                     + ".then((__value) => {\n"
60                                     + " return WebAssembly.compile(__value).then((%s) => {\n",
61                             WASM_MODULE_BYTES_ID, WASM_MODULE_ARG_NAME));
62         }
63 
64         // Call entryFunctionName with the constants just declared as parameters
65         resultBuilder.append(
66                 String.format(
67                         "return JSON.stringify(%s(%s));\n", entryFunctionName, argumentPassing));
68 
69         if (addWasmBinary) {
70             resultBuilder.append("})});\n");
71         }
72         resultBuilder.append("})();\n");
73 
74         return resultBuilder.toString();
75     }
76 }
77