1 // Copyright (c) 2012 The Chromium 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 "ppapi/cpp/var.h" 6 7 #include <stdio.h> 8 #include <string.h> 9 10 #include <algorithm> 11 12 #include "ppapi/c/pp_var.h" 13 #include "ppapi/c/ppb_var.h" 14 #include "ppapi/cpp/instance.h" 15 #include "ppapi/cpp/logging.h" 16 #include "ppapi/cpp/module.h" 17 #include "ppapi/cpp/module_impl.h" 18 19 // Define equivalent to snprintf on Windows. 20 #if defined(_MSC_VER) 21 # define snprintf sprintf_s 22 #endif 23 24 namespace pp { 25 26 namespace { 27 28 template <> const char* interface_name<PPB_Var_1_2>() { 29 return PPB_VAR_INTERFACE_1_2; 30 } 31 template <> const char* interface_name<PPB_Var_1_1>() { 32 return PPB_VAR_INTERFACE_1_1; 33 } 34 template <> const char* interface_name<PPB_Var_1_0>() { 35 return PPB_VAR_INTERFACE_1_0; 36 } 37 38 // Technically you can call AddRef and Release on any Var, but it may involve 39 // cross-process calls depending on the plugin. This is an optimization so we 40 // only do refcounting on the necessary objects. 41 inline bool NeedsRefcounting(const PP_Var& var) { 42 return var.type > PP_VARTYPE_DOUBLE; 43 } 44 45 // This helper function uses the latest available version of VarFromUtf8. Note 46 // that version 1.0 of this method has a different API to later versions. 47 PP_Var VarFromUtf8Helper(const char* utf8_str, uint32_t len) { 48 if (has_interface<PPB_Var_1_2>()) { 49 return get_interface<PPB_Var_1_2>()->VarFromUtf8(utf8_str, len); 50 } else if (has_interface<PPB_Var_1_1>()) { 51 return get_interface<PPB_Var_1_1>()->VarFromUtf8(utf8_str, len); 52 } else if (has_interface<PPB_Var_1_0>()) { 53 return get_interface<PPB_Var_1_0>()->VarFromUtf8(Module::Get()->pp_module(), 54 utf8_str, 55 len); 56 } 57 return PP_MakeNull(); 58 } 59 60 // This helper function uses the latest available version of AddRef. 61 // Returns true on success, false if no appropriate interface was available. 62 bool AddRefHelper(const PP_Var& var) { 63 if (has_interface<PPB_Var_1_2>()) { 64 get_interface<PPB_Var_1_2>()->AddRef(var); 65 return true; 66 } else if (has_interface<PPB_Var_1_1>()) { 67 get_interface<PPB_Var_1_1>()->AddRef(var); 68 return true; 69 } else if (has_interface<PPB_Var_1_0>()) { 70 get_interface<PPB_Var_1_0>()->AddRef(var); 71 return true; 72 } 73 return false; 74 } 75 76 // This helper function uses the latest available version of Release. 77 // Returns true on success, false if no appropriate interface was available. 78 bool ReleaseHelper(const PP_Var& var) { 79 if (has_interface<PPB_Var_1_2>()) { 80 get_interface<PPB_Var_1_2>()->Release(var); 81 return true; 82 } else if (has_interface<PPB_Var_1_1>()) { 83 get_interface<PPB_Var_1_1>()->Release(var); 84 return true; 85 } else if (has_interface<PPB_Var_1_0>()) { 86 get_interface<PPB_Var_1_0>()->Release(var); 87 return true; 88 } 89 return false; 90 } 91 92 } // namespace 93 94 Var::Var() { 95 memset(&var_, 0, sizeof(var_)); 96 var_.type = PP_VARTYPE_UNDEFINED; 97 is_managed_ = true; 98 } 99 100 Var::Var(Null) { 101 memset(&var_, 0, sizeof(var_)); 102 var_.type = PP_VARTYPE_NULL; 103 is_managed_ = true; 104 } 105 106 Var::Var(bool b) { 107 var_.type = PP_VARTYPE_BOOL; 108 var_.padding = 0; 109 var_.value.as_bool = PP_FromBool(b); 110 is_managed_ = true; 111 } 112 113 Var::Var(int32_t i) { 114 var_.type = PP_VARTYPE_INT32; 115 var_.padding = 0; 116 var_.value.as_int = i; 117 is_managed_ = true; 118 } 119 120 Var::Var(double d) { 121 var_.type = PP_VARTYPE_DOUBLE; 122 var_.padding = 0; 123 var_.value.as_double = d; 124 is_managed_ = true; 125 } 126 127 Var::Var(const char* utf8_str) { 128 uint32_t len = utf8_str ? static_cast<uint32_t>(strlen(utf8_str)) : 0; 129 var_ = VarFromUtf8Helper(utf8_str, len); 130 is_managed_ = true; 131 } 132 133 Var::Var(const std::string& utf8_str) { 134 var_ = VarFromUtf8Helper(utf8_str.c_str(), 135 static_cast<uint32_t>(utf8_str.size())); 136 is_managed_ = true; 137 } 138 139 Var::Var(const pp::Resource& resource) { 140 if (has_interface<PPB_Var_1_2>()) { 141 var_ = get_interface<PPB_Var_1_2>()->VarFromResource( 142 resource.pp_resource()); 143 } else { 144 PP_NOTREACHED(); 145 return; 146 } 147 // Set |is_managed_| to true, so |var_| will be properly released upon 148 // destruction. 149 is_managed_ = true; 150 } 151 152 153 Var::Var(const PP_Var& var) { 154 var_ = var; 155 is_managed_ = true; 156 if (NeedsRefcounting(var_)) { 157 if (!AddRefHelper(var_)) 158 var_.type = PP_VARTYPE_NULL; 159 } 160 } 161 162 Var::Var(const Var& other) { 163 var_ = other.var_; 164 is_managed_ = true; 165 if (NeedsRefcounting(var_)) { 166 if (!AddRefHelper(var_)) 167 var_.type = PP_VARTYPE_NULL; 168 } 169 } 170 171 Var::~Var() { 172 if (NeedsRefcounting(var_) && is_managed_) 173 ReleaseHelper(var_); 174 } 175 176 Var& Var::operator=(const Var& other) { 177 // Early return for self-assignment. Note however, that two distinct vars 178 // can refer to the same object, so we still need to be careful about the 179 // refcounting below. 180 if (this == &other) 181 return *this; 182 183 // Be careful to keep the ref alive for cases where we're assigning an 184 // object to itself by addrefing the new one before releasing the old one. 185 bool old_is_managed = is_managed_; 186 is_managed_ = true; 187 if (NeedsRefcounting(other.var_)) { 188 AddRefHelper(other.var_); 189 } 190 if (NeedsRefcounting(var_) && old_is_managed) 191 ReleaseHelper(var_); 192 193 var_ = other.var_; 194 return *this; 195 } 196 197 bool Var::operator==(const Var& other) const { 198 if (var_.type != other.var_.type) 199 return false; 200 switch (var_.type) { 201 case PP_VARTYPE_UNDEFINED: 202 case PP_VARTYPE_NULL: 203 return true; 204 case PP_VARTYPE_BOOL: 205 return AsBool() == other.AsBool(); 206 case PP_VARTYPE_INT32: 207 return AsInt() == other.AsInt(); 208 case PP_VARTYPE_DOUBLE: 209 return AsDouble() == other.AsDouble(); 210 case PP_VARTYPE_STRING: 211 if (var_.value.as_id == other.var_.value.as_id) 212 return true; 213 return AsString() == other.AsString(); 214 case PP_VARTYPE_OBJECT: 215 case PP_VARTYPE_ARRAY: 216 case PP_VARTYPE_ARRAY_BUFFER: 217 case PP_VARTYPE_DICTIONARY: 218 case PP_VARTYPE_RESOURCE: 219 default: // Objects, arrays, dictionaries, resources. 220 return var_.value.as_id == other.var_.value.as_id; 221 } 222 } 223 224 bool Var::AsBool() const { 225 if (!is_bool()) { 226 PP_NOTREACHED(); 227 return false; 228 } 229 return PP_ToBool(var_.value.as_bool); 230 } 231 232 int32_t Var::AsInt() const { 233 if (is_int()) 234 return var_.value.as_int; 235 if (is_double()) 236 return static_cast<int>(var_.value.as_double); 237 PP_NOTREACHED(); 238 return 0; 239 } 240 241 double Var::AsDouble() const { 242 if (is_double()) 243 return var_.value.as_double; 244 if (is_int()) 245 return static_cast<double>(var_.value.as_int); 246 PP_NOTREACHED(); 247 return 0.0; 248 } 249 250 std::string Var::AsString() const { 251 if (!is_string()) { 252 PP_NOTREACHED(); 253 return std::string(); 254 } 255 256 uint32_t len; 257 const char* str; 258 if (has_interface<PPB_Var_1_2>()) 259 str = get_interface<PPB_Var_1_2>()->VarToUtf8(var_, &len); 260 else if (has_interface<PPB_Var_1_1>()) 261 str = get_interface<PPB_Var_1_1>()->VarToUtf8(var_, &len); 262 else if (has_interface<PPB_Var_1_0>()) 263 str = get_interface<PPB_Var_1_0>()->VarToUtf8(var_, &len); 264 else 265 return std::string(); 266 return std::string(str, len); 267 } 268 269 pp::Resource Var::AsResource() const { 270 if (!is_resource()) { 271 PP_NOTREACHED(); 272 return pp::Resource(); 273 } 274 275 if (has_interface<PPB_Var_1_2>()) { 276 return pp::Resource(pp::PASS_REF, 277 get_interface<PPB_Var_1_2>()->VarToResource(var_)); 278 } else { 279 return pp::Resource(); 280 } 281 } 282 283 std::string Var::DebugString() const { 284 char buf[256]; 285 if (is_undefined()) { 286 snprintf(buf, sizeof(buf), "Var(UNDEFINED)"); 287 } else if (is_null()) { 288 snprintf(buf, sizeof(buf), "Var(NULL)"); 289 } else if (is_bool()) { 290 snprintf(buf, sizeof(buf), AsBool() ? "Var(true)" : "Var(false)"); 291 } else if (is_int()) { 292 snprintf(buf, sizeof(buf), "Var(%d)", static_cast<int>(AsInt())); 293 } else if (is_double()) { 294 snprintf(buf, sizeof(buf), "Var(%f)", AsDouble()); 295 } else if (is_string()) { 296 char format[] = "Var<'%s'>"; 297 size_t decoration = sizeof(format) - 2; // The %s is removed. 298 size_t available = sizeof(buf) - decoration; 299 std::string str = AsString(); 300 if (str.length() > available) { 301 str.resize(available - 3); // Reserve space for ellipsis. 302 str.append("..."); 303 } 304 snprintf(buf, sizeof(buf), format, str.c_str()); 305 } else if (is_object()) { 306 snprintf(buf, sizeof(buf), "Var(OBJECT)"); 307 } else if (is_array()) { 308 snprintf(buf, sizeof(buf), "Var(ARRAY)"); 309 } else if (is_dictionary()) { 310 snprintf(buf, sizeof(buf), "Var(DICTIONARY)"); 311 } else if (is_array_buffer()) { 312 snprintf(buf, sizeof(buf), "Var(ARRAY_BUFFER)"); 313 } else if (is_resource()) { 314 snprintf(buf, sizeof(buf), "Var(RESOURCE)"); 315 } else { 316 buf[0] = '\0'; 317 } 318 return buf; 319 } 320 321 } // namespace pp 322