| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <assert.h> | |
| 6 | |
| 7 #include "native_client/src/trusted/plugin/var_utils.h" | |
| 8 | |
| 9 #include "native_client/src/include/nacl_macros.h" | |
| 10 #include "native_client/src/include/nacl_string.h" | |
| 11 #include "native_client/src/include/portability_io.h" | |
| 12 #include "native_client/src/shared/platform/nacl_check.h" | |
| 13 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h" | |
| 14 #include "native_client/src/trusted/plugin/array_ppapi.h" | |
| 15 #include "native_client/src/trusted/plugin/desc_based_handle.h" | |
| 16 #include "native_client/src/trusted/plugin/scriptable_handle.h" | |
| 17 #include "native_client/src/trusted/plugin/utility.h" | |
| 18 | |
| 19 #include "ppapi/cpp/dev/scriptable_object_deprecated.h" | |
| 20 | |
| 21 namespace plugin { | |
| 22 | |
| 23 | |
| 24 // In JavaScript, foo[1] is equivalent to foo["1"], so map both indexed and | |
| 25 // string names to a string. | |
| 26 nacl::string NameAsString(const pp::Var& name) { | |
| 27 if (name.is_string()) | |
| 28 return name.AsString(); | |
| 29 assert(name.is_int()); | |
| 30 nacl::stringstream namestream; | |
| 31 namestream << name.AsInt(); | |
| 32 return namestream.str(); | |
| 33 } | |
| 34 | |
| 35 | |
| 36 //----------------------------------------------------------------------------- | |
| 37 // Translation from pp::Var to NaClSrpcArg | |
| 38 //----------------------------------------------------------------------------- | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 // Sets |array_length| and allocate |array_data| based on the length | |
| 43 // represented by |var|. Sets |exception| on error. | |
| 44 template<typename T> void PPVarToAllocateArray(const pp::Var& var, | |
| 45 nacl_abi_size_t* array_length, | |
| 46 T** array_data, | |
| 47 pp::Var* exception) { | |
| 48 // Initialize result values for error cases. | |
| 49 *array_length = 0; | |
| 50 *array_data = NULL; | |
| 51 | |
| 52 if (!var.is_number()) { | |
| 53 *exception = "incompatible argument: unable to get array length"; | |
| 54 return; | |
| 55 } | |
| 56 // AsInt will work if var is int or double. | |
| 57 size_t length = var.AsInt(); | |
| 58 | |
| 59 // Check for overflow on size multiplication and IMC array size limit. | |
| 60 if ((length > SIZE_T_MAX / sizeof(T)) || | |
| 61 (length * sizeof(T) > NACL_ABI_IMC_USER_BYTES_MAX)) { | |
| 62 *exception = "incompatible argument: array length is too long"; | |
| 63 return; | |
| 64 } | |
| 65 | |
| 66 *array_length = static_cast<nacl_abi_size_t>(length); | |
| 67 *array_data = reinterpret_cast<T*>(malloc(sizeof(T) * length)); | |
| 68 if (array_data == NULL) { | |
| 69 *exception = "incompatible argument: internal error"; | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 | |
| 74 // Translates |var| into |array_length| and |array_data| for type |type|. | |
| 75 // Sets |exception| on error. | |
| 76 template<typename T> void PPVarToArray(const pp::Var& var, | |
| 77 nacl_abi_size_t* array_length, | |
| 78 T** array_data, | |
| 79 pp::Var* exception) { | |
| 80 if (!var.is_object()) { | |
| 81 *exception = "incompatible argument: type is not array"; | |
| 82 return; | |
| 83 } | |
| 84 pp::VarPrivate var_private(var); | |
| 85 | |
| 86 pp::Var length_var = var_private.GetProperty(pp::Var("length"), exception); | |
| 87 PLUGIN_PRINTF((" PPVarToArray (length=%s)\n", | |
| 88 length_var.DebugString().c_str())); | |
| 89 PPVarToAllocateArray(length_var, array_length, array_data, exception); | |
| 90 if (!exception->is_undefined()) { | |
| 91 return; | |
| 92 } | |
| 93 | |
| 94 for (size_t i = 0; i < *array_length; ++i) { | |
| 95 int32_t index = nacl::assert_cast<int32_t>(i); | |
| 96 pp::Var element = var_private.GetProperty(pp::Var(index), exception); | |
| 97 PLUGIN_PRINTF((" PPVarToArray (array[%d]=%s)\n", | |
| 98 index, element.DebugString().c_str())); | |
| 99 if (!exception->is_undefined()) { | |
| 100 break; | |
| 101 } | |
| 102 if (!element.is_number()) { | |
| 103 *exception = "incompatible argument: non-numeric element type"; | |
| 104 break; | |
| 105 } | |
| 106 if (element.is_int()) { | |
| 107 (*array_data)[i] = static_cast<T>(element.AsInt()); | |
| 108 } else if (element.is_double()) { | |
| 109 (*array_data)[i] = static_cast<T>(element.AsDouble()); | |
| 110 } else { | |
| 111 NACL_NOTREACHED(); | |
| 112 } | |
| 113 } | |
| 114 if (!exception->is_undefined()) { | |
| 115 free(array_data); | |
| 116 *array_length = 0; | |
| 117 *array_data = NULL; | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 | |
| 122 // Returns the address of |var| if var is an object. Sets |exception| on error. | |
| 123 // |var| was passed in from SRPC input marshalling. It is guaranteed to be | |
| 124 // alive until the SRPC method is dispatched to. | |
| 125 pp::Var* PPVarToObjectVar(const pp::Var& var, pp::Var* exception) { | |
| 126 if (!var.is_object()) { | |
| 127 *exception = "incompatible argument: type is not object"; | |
| 128 return NULL; | |
| 129 } | |
| 130 return const_cast<pp::Var*>(&var); | |
| 131 } | |
| 132 | |
| 133 | |
| 134 // Returns NaClDesc* corresponding to |var|. Sets |exception| on error. | |
| 135 NaClDesc* PPVarToNaClDesc(const pp::Var& var, pp::Var* exception) { | |
| 136 if (!var.is_object()) { | |
| 137 *exception = "incompatible argument: type is not object"; | |
| 138 return NULL; | |
| 139 } | |
| 140 pp::VarPrivate var_private(var); | |
| 141 pp::deprecated::ScriptableObject* scriptable_object = | |
| 142 var_private.AsScriptableObject(); | |
| 143 if (scriptable_object == NULL) { | |
| 144 *exception = "incompatible argument: type is not scriptable object"; | |
| 145 return NULL; | |
| 146 } | |
| 147 ScriptableHandle* scriptable_handle = | |
| 148 static_cast<ScriptableHandle*>(scriptable_object); | |
| 149 if (!ScriptableHandle::is_valid(scriptable_handle)) { | |
| 150 *exception = "incompatible argument: not a valid scriptable handle"; | |
| 151 return NULL; | |
| 152 } | |
| 153 NaClDesc* nacl_desc = scriptable_handle->desc_handle()->desc(); | |
| 154 if (nacl_desc == NULL) { | |
| 155 *exception = "incompatible argument: not a handle object"; | |
| 156 return NULL; | |
| 157 } | |
| 158 return nacl_desc; | |
| 159 } | |
| 160 | |
| 161 | |
| 162 // Allocates and returns a pointer to string corresponding to |var|. | |
| 163 // Sets |exception| on error. | |
| 164 char* PPVarToString(const pp::Var& var, pp::Var* exception) { | |
| 165 if (!var.is_string()) { | |
| 166 *exception = "incompatible argument: type is not string"; | |
| 167 return NULL; | |
| 168 } | |
| 169 | |
| 170 nacl::string var_as_string = var.AsString(); | |
| 171 size_t size = var_as_string.size() + 1; // + \0 | |
| 172 char* string_as_chars = reinterpret_cast<char*>(malloc(size)); | |
| 173 if (string_as_chars == NULL) { | |
| 174 *exception = "incompatible argument: internal error"; | |
| 175 return NULL; | |
| 176 } | |
| 177 | |
| 178 memcpy(string_as_chars, var_as_string.c_str(), size); | |
| 179 return string_as_chars; | |
| 180 } | |
| 181 | |
| 182 | |
| 183 // Returns a number corresponding to |var|. JavaScript might mix int and | |
| 184 // double types, so use them interchangeably. Sets |exception| on error. | |
| 185 template<typename T> T PPVarToNumber(const pp::Var& var, pp::Var* exception) { | |
| 186 T result = 0; | |
| 187 if (!var.is_number()) | |
| 188 *exception = "incompatible argument: type is not numeric"; | |
| 189 else if (var.is_double()) | |
| 190 result = static_cast<T>(var.AsDouble()); | |
| 191 else if (var.is_int()) | |
| 192 result = static_cast<T>(var.AsInt()); | |
| 193 return result; | |
| 194 } | |
| 195 | |
| 196 } // namespace | |
| 197 | |
| 198 | |
| 199 // Allocates |arg| of the array length represented by |var|. No-op for scalars. | |
| 200 // Sets |exception| and returns false on error. | |
| 201 bool PPVarToAllocateNaClSrpcArg(const pp::Var& var, | |
| 202 NaClSrpcArg* arg, pp::Var* exception) { | |
| 203 PLUGIN_PRINTF((" PPVarToAllocateNaClSrpcArg (var=%s, arg->tag='%c')\n", | |
| 204 var.DebugString().c_str(), arg->tag)); | |
| 205 switch (arg->tag) { | |
| 206 case NACL_SRPC_ARG_TYPE_BOOL: | |
| 207 case NACL_SRPC_ARG_TYPE_DOUBLE: | |
| 208 case NACL_SRPC_ARG_TYPE_INT: | |
| 209 case NACL_SRPC_ARG_TYPE_LONG: | |
| 210 case NACL_SRPC_ARG_TYPE_STRING: | |
| 211 case NACL_SRPC_ARG_TYPE_HANDLE: | |
| 212 case NACL_SRPC_ARG_TYPE_OBJECT: | |
| 213 break; // nothing to do | |
| 214 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY: | |
| 215 PPVarToAllocateArray(var, &arg->u.count, &arg->arrays.carr, exception); | |
| 216 break; | |
| 217 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY: | |
| 218 PPVarToAllocateArray(var, &arg->u.count, &arg->arrays.darr, exception); | |
| 219 break; | |
| 220 case NACL_SRPC_ARG_TYPE_INT_ARRAY: | |
| 221 PPVarToAllocateArray(var, &arg->u.count, &arg->arrays.iarr, exception); | |
| 222 break; | |
| 223 case NACL_SRPC_ARG_TYPE_LONG_ARRAY: | |
| 224 PPVarToAllocateArray(var, &arg->u.count, &arg->arrays.larr, exception); | |
| 225 break; | |
| 226 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY: | |
| 227 case NACL_SRPC_ARG_TYPE_INVALID: | |
| 228 default: | |
| 229 *exception = "variant array and invalid type arguments are not supported"; | |
| 230 } | |
| 231 PLUGIN_PRINTF((" PPVarToAllocateNaClSrpcArg (return exception=%s)\n", | |
| 232 exception->DebugString().c_str())); | |
| 233 return exception->is_undefined(); | |
| 234 } | |
| 235 | |
| 236 | |
| 237 // Translates |var| into |arg|. Returns false and sets exception on error. | |
| 238 bool PPVarToNaClSrpcArg(const pp::Var& var, | |
| 239 NaClSrpcArg* arg, pp::Var* exception) { | |
| 240 PLUGIN_PRINTF((" PPVarToNaClSrpcArg (var=%s, arg->tag='%c')\n", | |
| 241 var.DebugString().c_str(), arg->tag)); | |
| 242 switch (arg->tag) { | |
| 243 case NACL_SRPC_ARG_TYPE_BOOL: | |
| 244 if (!var.is_bool()) | |
| 245 *exception = "incompatible argument: type is not bool"; | |
| 246 else | |
| 247 arg->u.bval = var.AsBool(); | |
| 248 break; | |
| 249 case NACL_SRPC_ARG_TYPE_DOUBLE: | |
| 250 arg->u.dval = PPVarToNumber<double>(var, exception); | |
| 251 break; | |
| 252 case NACL_SRPC_ARG_TYPE_INT: | |
| 253 arg->u.ival = PPVarToNumber<int32_t>(var, exception); | |
| 254 break; | |
| 255 case NACL_SRPC_ARG_TYPE_LONG: | |
| 256 arg->u.lval = PPVarToNumber<int64_t>(var, exception); | |
| 257 break; | |
| 258 case NACL_SRPC_ARG_TYPE_STRING: | |
| 259 arg->arrays.str = PPVarToString(var, exception); | |
| 260 break; | |
| 261 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY: | |
| 262 PPVarToArray(var, &arg->u.count, &arg->arrays.carr, exception); | |
| 263 break; | |
| 264 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY: | |
| 265 PPVarToArray(var, &arg->u.count, &arg->arrays.darr, exception); | |
| 266 break; | |
| 267 case NACL_SRPC_ARG_TYPE_INT_ARRAY: | |
| 268 PPVarToArray(var, &arg->u.count, &arg->arrays.iarr, exception); | |
| 269 break; | |
| 270 case NACL_SRPC_ARG_TYPE_LONG_ARRAY: | |
| 271 PPVarToArray(var, &arg->u.count, &arg->arrays.larr, exception); | |
| 272 break; | |
| 273 case NACL_SRPC_ARG_TYPE_HANDLE: | |
| 274 arg->u.hval = reinterpret_cast<NaClSrpcImcDescType>( | |
| 275 PPVarToNaClDesc(var, exception)); | |
| 276 break; | |
| 277 case NACL_SRPC_ARG_TYPE_OBJECT: | |
| 278 arg->arrays.oval = reinterpret_cast<void*>( | |
| 279 PPVarToObjectVar(var, exception)); | |
| 280 break; | |
| 281 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY: | |
| 282 case NACL_SRPC_ARG_TYPE_INVALID: | |
| 283 default: | |
| 284 *exception = "variant array and invalid type arguments are not supported"; | |
| 285 } | |
| 286 PLUGIN_PRINTF((" PPVarToNaClSrpcArg (return exception=%s)\n", | |
| 287 exception->DebugString().c_str())); | |
| 288 return exception->is_undefined(); | |
| 289 } | |
| 290 | |
| 291 | |
| 292 //----------------------------------------------------------------------------- | |
| 293 // Translation NaClSrpcArg to pp::Var | |
| 294 //----------------------------------------------------------------------------- | |
| 295 | |
| 296 namespace { | |
| 297 | |
| 298 // PPAPI does not have a 64-bit integer Var type. | |
| 299 // To make the array construction below work, we define a set of overloaded | |
| 300 // functions to convert an array element to a pp::Var. | |
| 301 template<typename T> pp::Var ArrayElementToPPVar(T array_element) { | |
| 302 return pp::Var(array_element); | |
| 303 } | |
| 304 | |
| 305 // One overload is specialized, and it truncates, losing precision of course. | |
| 306 pp::Var ArrayElementToPPVar(int64_t array_element) { | |
| 307 return pp::Var(static_cast<int32_t>(array_element)); | |
| 308 } | |
| 309 | |
| 310 // Return a pp::Var constructed from |array_data|. Sets |exception| on error. | |
| 311 template<typename T> pp::Var ArrayToPPVar(T* array_data, | |
| 312 nacl_abi_size_t array_length, | |
| 313 Plugin* plugin, | |
| 314 pp::Var* exception) { | |
| 315 ArrayPpapi* array = new(std::nothrow) ArrayPpapi(plugin); | |
| 316 if (array == NULL) { | |
| 317 *exception = "incompatible argument: internal error"; | |
| 318 return pp::Var(); | |
| 319 } | |
| 320 | |
| 321 for (size_t i = 0; i < array_length; ++i) { | |
| 322 int32_t index = static_cast<int32_t>(i); | |
| 323 array->SetProperty(pp::Var(index), | |
| 324 ArrayElementToPPVar(array_data[i]), | |
| 325 exception); | |
| 326 } | |
| 327 return pp::VarPrivate(plugin, array); | |
| 328 } | |
| 329 | |
| 330 | |
| 331 // Returns a pp::Var corresponding to |desc| or void. Sets |exception| on error. | |
| 332 pp::Var NaClDescToPPVar(NaClDesc* desc, Plugin* plugin, pp::Var* exception) { | |
| 333 nacl::DescWrapper* wrapper = plugin->wrapper_factory()->MakeGeneric(desc); | |
| 334 | |
| 335 DescBasedHandle* desc_handle = DescBasedHandle::New(wrapper); | |
| 336 | |
| 337 pp::deprecated::ScriptableObject* object = | |
| 338 ScriptableHandle::NewDescHandle(desc_handle); | |
| 339 if (object == NULL) { | |
| 340 *exception = "incompatible argument: failed to create handle var"; | |
| 341 return pp::Var(); | |
| 342 } | |
| 343 return pp::VarPrivate(plugin, object); | |
| 344 } | |
| 345 | |
| 346 | |
| 347 // Returns a pp::Var corresponding to |obj|. Only predeclared plugin methods | |
| 348 // can return objects and they only return a ScriptableHandle that is actually | |
| 349 // a ScriptableHandle. | |
| 350 pp::Var ObjectToPPVar(void* obj) { | |
| 351 ScriptableHandle* handle = reinterpret_cast<ScriptableHandle*>(obj); | |
| 352 // This confirms that this this is indeed a valid SriptableHandle that was | |
| 353 // created by us. In theory, a predeclared method could receive and return | |
| 354 // an opaque JavaScript object that is not a ScriptableHandle. But we don't | |
| 355 // have methods like this at the time. If one ever creates such a method, | |
| 356 // this CHECK will fail and remind the author to update this code to handle | |
| 357 // arbitrary objects. | |
| 358 CHECK(ScriptableHandle::is_valid(handle)); | |
| 359 if (handle->var() != NULL) | |
| 360 return *handle->var(); // make a copy | |
| 361 | |
| 362 return pp::VarPrivate(handle->plugin(), handle); | |
| 363 } | |
| 364 | |
| 365 } // namespace | |
| 366 | |
| 367 // Returns a pp::Var corresponding to |arg| or void. Sets |exception| on error. | |
| 368 pp::Var NaClSrpcArgToPPVar(const NaClSrpcArg* arg, Plugin* plugin, | |
| 369 pp::Var* exception) { | |
| 370 PLUGIN_PRINTF((" NaClSrpcArgToPPVar (arg->tag='%c')\n", arg->tag)); | |
| 371 pp::Var var; | |
| 372 switch (arg->tag) { | |
| 373 case NACL_SRPC_ARG_TYPE_BOOL: | |
| 374 var = pp::Var(arg->u.bval != 0); | |
| 375 break; | |
| 376 case NACL_SRPC_ARG_TYPE_DOUBLE: | |
| 377 var = pp::Var(arg->u.dval); | |
| 378 break; | |
| 379 case NACL_SRPC_ARG_TYPE_INT: | |
| 380 var = pp::Var(arg->u.ival); | |
| 381 break; | |
| 382 case NACL_SRPC_ARG_TYPE_LONG: | |
| 383 // PPAPI does not have a 64-bit integral type. Downcast. | |
| 384 var = pp::Var(static_cast<int32_t>(arg->u.lval)); | |
| 385 break; | |
| 386 case NACL_SRPC_ARG_TYPE_STRING: | |
| 387 var = pp::Var(arg->arrays.str); | |
| 388 break; | |
| 389 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY: | |
| 390 var = ArrayToPPVar(arg->arrays.carr, arg->u.count, plugin, exception); | |
| 391 break; | |
| 392 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY: | |
| 393 var = ArrayToPPVar(arg->arrays.darr, arg->u.count, plugin, exception); | |
| 394 break; | |
| 395 case NACL_SRPC_ARG_TYPE_INT_ARRAY: | |
| 396 var = ArrayToPPVar(arg->arrays.iarr, arg->u.count, plugin, exception); | |
| 397 break; | |
| 398 case NACL_SRPC_ARG_TYPE_LONG_ARRAY: | |
| 399 var = ArrayToPPVar(arg->arrays.larr, arg->u.count, plugin, exception); | |
| 400 break; | |
| 401 case NACL_SRPC_ARG_TYPE_HANDLE: | |
| 402 var = NaClDescToPPVar(arg->u.hval, plugin, exception); | |
| 403 break; | |
| 404 case NACL_SRPC_ARG_TYPE_OBJECT: | |
| 405 var = ObjectToPPVar(arg->arrays.oval); | |
| 406 break; | |
| 407 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY: | |
| 408 case NACL_SRPC_ARG_TYPE_INVALID: | |
| 409 default: | |
| 410 *exception = "variant array and invalid argument types are not supproted"; | |
| 411 } | |
| 412 PLUGIN_PRINTF((" NaClSrpcArgToPPVar (return var=%s, exception=%s)\n", | |
| 413 var.DebugString().c_str(), exception->DebugString().c_str())); | |
| 414 return var; | |
| 415 } | |
| 416 | |
| 417 } // namespace plugin | |
| OLD | NEW |