• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.util.Arrays.asList;
20 
21 import android.adservices.common.AdSelectionSignals;
22 import android.os.Trace;
23 
24 import com.android.adservices.service.profiling.Tracing;
25 
26 import com.google.common.collect.ImmutableList;
27 
28 import org.json.JSONArray;
29 import org.json.JSONException;
30 import org.json.JSONObject;
31 
32 import java.util.List;
33 import java.util.Map;
34 import java.util.stream.Collectors;
35 
36 /** Represent an argument to supply to an JS script. */
37 public abstract class JSScriptArgument {
38     protected final String mName;
39 
JSScriptArgument(String name)40     protected JSScriptArgument(String name) {
41         mName = name;
42     }
43 
44     /**
45      * @return an argument with the given {@code name} and the given string value {@code value}
46      */
stringArg(String name, String value)47     public static JSScriptStringArgument stringArg(String name, String value) {
48         return new JSScriptStringArgument(name, value);
49     }
50 
51     /**
52      * @return a JS object with the given {@code name} and value obtained parsing the given {@code
53      *     value}.
54      * @throws JSONException if {@code value} doesn't represent a valid JSON object
55      */
jsonArg(String name, String value)56     public static JSScriptJsonArgument jsonArg(String name, String value) throws JSONException {
57         // Creating the JSONObject just to parse value and cause a JSONException if invalid.
58         new JSONObject(value);
59         return new JSScriptJsonArgument(name, value);
60     }
61 
62     /**
63      * @return a JS array object with the given {@code name} and value obtained parsing the given
64      *     {@code value}.
65      * @throws JSONException if {@code value} doesn't represent a valid JSON array object
66      */
jsonArrayArg(String name, String value)67     public static JSScriptJsonArrayArgument jsonArrayArg(String name, String value)
68             throws JSONException {
69         Trace.beginSection(Tracing.JS_ARRAY_ARG + ":validation");
70         try {
71             new JSONArray(value);
72         } finally {
73             Trace.endSection();
74         }
75 
76         Trace.beginSection(Tracing.JS_ARRAY_ARG + ":conversion");
77         JSScriptJsonArrayArgument result = new JSScriptJsonArrayArgument(name, value);
78         Trace.endSection();
79         return result;
80     }
81 
82     /**
83      * @return a JS array object with the given {@code name} and {@code values} obtained by
84      *     marshaling each value using the {@code valueMarshaller}. It does not validate input.
85      */
jsonArrayArgNoValidation( String name, Iterable<T> values, AccumulatingJsonMarshaller<T> valueMarshaller)86     public static <T> JSScriptJsonArrayArgument jsonArrayArgNoValidation(
87             String name, Iterable<T> values, AccumulatingJsonMarshaller<T> valueMarshaller) {
88         Trace.beginSection(Tracing.JS_ARRAY_ARG_NO_VALIDATION);
89         StringBuilder sb = new StringBuilder();
90         sb.append("[");
91 
92         for (T item : values) {
93             valueMarshaller.serializeEntryToJson(item, sb);
94             sb.append(",");
95         }
96 
97         // Delete the last comma
98         if (sb.length() > 1) {
99             sb.deleteCharAt(sb.length() - 1);
100         }
101 
102         sb.append("]");
103 
104         JSScriptJsonArrayArgument result = new JSScriptJsonArrayArgument(name, sb.toString());
105         Trace.endSection();
106         return result;
107     }
108 
109     /**
110      * @return a JS object with the given {@code name} and value obtained parsing the given {@code
111      *     value}.
112      * @throws JSONException if {@code value} doesn't represent a valid JSON object
113      */
jsonArg(String name, AdSelectionSignals value)114     public static JSScriptJsonArgument jsonArg(String name, AdSelectionSignals value)
115             throws JSONException {
116         // TODO(b/238849930) Merge this validation with AdSelectionSignals validation
117         new JSONObject(value.toString());
118         return new JSScriptJsonArgument(name, value.toString());
119     }
120 
121     /**
122      * @return a JS object with the given {@code name} and value obtained parsing the given map
123      *     {@code value}.
124      * @throws JSONException if {@code value} doesn't represent a valid JSON object
125      */
stringMapToRecordArg(String name, Map<String, String> stringMap)126     public static JSScriptArgument stringMapToRecordArg(String name, Map<String, String> stringMap)
127             throws JSONException {
128         ImmutableList.Builder<JSScriptArgument> mapArg = ImmutableList.builder();
129         for (Map.Entry<String, String> signal : stringMap.entrySet()) {
130             mapArg.add(jsonArg(signal.getKey(), signal.getValue()));
131         }
132         return recordArg(name, mapArg.build());
133     }
134 
135     /**
136      * @return a JS array argument with the given {@code name} initialized with the values specified
137      *     with {@code items}.
138      */
arrayArg( String name, T... items)139     public static <T extends JSScriptArgument> JSScriptArrayArgument<T> arrayArg(
140             String name, T... items) {
141         return new JSScriptArrayArgument<>(name, asList(items));
142     }
143 
144     /**
145      * @return a JS array argument with the given {@code name} initialized with the values specified
146      *     with {@code items}.
147      */
arrayArg( String name, List<T> items)148     public static <T extends JSScriptArgument> JSScriptArrayArgument<T> arrayArg(
149             String name, List<T> items) {
150         return new JSScriptArrayArgument<>(name, items);
151     }
152 
153     /**
154      * @return a JS array argument with the given {@code name} initialized with the values specified
155      *     with {@code items}.
156      */
stringArrayArg( String name, List<String> items)157     public static JSScriptArrayArgument<JSScriptStringArgument> stringArrayArg(
158             String name, List<String> items) {
159         return new JSScriptArrayArgument<>(
160                 name,
161                 items.stream().map(str -> stringArg("ignored", str)).collect(Collectors.toList()));
162     }
163 
164     /**
165      * @return a JS array argument with the given {@code name} initialized with the values specified
166      *     with {@code items}
167      */
168     public static <T extends Number>
numericArrayArg( String name, List<T> items)169             JSScriptArrayArgument<JSScriptNumericArgument<T>> numericArrayArg(
170                     String name, List<T> items) {
171         return new JSScriptArrayArgument<>(
172                 name,
173                 items.stream()
174                         .map(num -> new JSScriptNumericArgument<>("ignored", num))
175                         .collect(Collectors.toList()));
176     }
177 
178     /**
179      * @return a JS object with the given {@code name} and {@code fields} as fields values.
180      */
recordArg(String name, JSScriptArgument... fields)181     public static JSScriptRecordArgument recordArg(String name, JSScriptArgument... fields) {
182         return new JSScriptRecordArgument(name, asList(fields));
183     }
184 
185     /**
186      * @return a JS object with the given {@code name} and {@code fields} as fields values.
187      */
recordArg(String name, List<JSScriptArgument> fields)188     public static JSScriptRecordArgument recordArg(String name, List<JSScriptArgument> fields) {
189         return new JSScriptRecordArgument(name, fields);
190     }
191 
192     /**
193      * @return a numeric variable with the given {@code name} and {@code value}.
194      */
numericArg(String name, T value)195     public static <T extends Number> JSScriptNumericArgument<T> numericArg(String name, T value) {
196         return new JSScriptNumericArgument<>(name, value);
197     }
198 
199     /**
200      * @return the JS code to use to initialize the variable.
201      */
variableDeclaration()202     public String variableDeclaration() {
203         return String.format("const %s = %s;", name(), initializationValue());
204     }
205 
206     /**
207      * @return name of the argument as referred in the call to the auction script function.
208      */
name()209     public String name() {
210         return mName;
211     }
212 
213     /**
214      * @return the JS code to use to initialize the newly declared variable.
215      */
initializationValue()216     abstract String initializationValue();
217 }
218