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