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 |