Chromium Code Reviews| Index: ppapi/shared_impl/var_value_conversions.cc |
| diff --git a/ppapi/shared_impl/var_value_conversions.cc b/ppapi/shared_impl/var_value_conversions.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..605a984b238d5661ebddb0407f99bcefaf610533 |
| --- /dev/null |
| +++ b/ppapi/shared_impl/var_value_conversions.cc |
| @@ -0,0 +1,349 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ppapi/shared_impl/var_value_conversions.h" |
| + |
| +#include <limits> |
| +#include <set> |
| +#include <stack> |
| + |
| +#include "base/logging.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/values.h" |
| +#include "ppapi/c/pp_bool.h" |
| +#include "ppapi/c/pp_stdint.h" |
| +#include "ppapi/shared_impl/dictionary_var.h" |
| +#include "ppapi/shared_impl/ppapi_globals.h" |
| +#include "ppapi/shared_impl/scoped_pp_var.h" |
| +#include "ppapi/shared_impl/var.h" |
| +#include "ppapi/shared_impl/var_tracker.h" |
| + |
| +namespace ppapi { |
| + |
| +namespace { |
| + |
| +// In CreateValueFromVar(), a stack is used to keep track of conversion progress |
| +// of array and dictionary vars. VarNodeBase is the base class of stack |
| +// elements. |
| +class VarNodeBase : public base::RefCounted<VarNodeBase> { |
| + public: |
| + virtual bool IsArrayVar() const { return false; } |
| + virtual bool IsDictionaryVar() const { return false; } |
| + |
| + protected: |
| + friend class base::RefCounted<VarNodeBase>; |
| + |
| + VarNodeBase() {} |
| + virtual ~VarNodeBase() {} |
| +}; |
| + |
| +class DictionaryVarNode : public VarNodeBase { |
| + public: |
| + DictionaryVarNode(int64_t in_var_id, const DictionaryVar* dict_var) |
| + : var_id(in_var_id), |
| + next(dict_var->key_value_map().begin()), |
| + end(dict_var->key_value_map().end()), |
| + dict_value(new base::DictionaryValue()) { |
| + } |
| + |
| + virtual bool IsDictionaryVar() const OVERRIDE { |
| + return true; |
| + } |
| + |
| + int64_t var_id; |
| + DictionaryVar::KeyValueMap::const_iterator next; |
| + DictionaryVar::KeyValueMap::const_iterator end; |
| + std::string current_key; |
| + scoped_ptr<base::DictionaryValue> dict_value; |
| + |
| + private: |
| + virtual ~DictionaryVarNode() {} |
| +}; |
| + |
| +// In CreateVarFromValue(), a stack is used to keep track of conversion progress |
| +// of list and dictionary values. ValueNodeBase is the base class of stack |
| +// elements. |
| +class ValueNodeBase : public base::RefCounted<ValueNodeBase> { |
| + public: |
| + virtual bool IsListValue() const { return false; } |
| + virtual bool IsDictionaryValue() const { return false; } |
| + |
| + protected: |
| + friend class base::RefCounted<ValueNodeBase>; |
| + |
| + ValueNodeBase() {} |
| + virtual ~ValueNodeBase() {} |
| +}; |
| + |
| +class DictionaryValueNode : public ValueNodeBase { |
| + public: |
| + explicit DictionaryValueNode(const base::DictionaryValue& dict_value) |
| + : iter(dict_value), |
| + dict_var(new DictionaryVar()) { |
| + } |
| + |
| + virtual bool IsDictionaryValue() const OVERRIDE { |
| + return true; |
| + } |
| + |
| + base::DictionaryValue::Iterator iter; |
| + std::string current_key; |
| + scoped_refptr<DictionaryVar> dict_var; |
| + |
| + private: |
| + virtual ~DictionaryValueNode() {} |
| +}; |
| + |
| +// Helper function for CreateValueFromVar(). If |var| is neither array nor |
| +// dictionary, the conversion result is stored in |value|; otherwise, |
| +// |parent_ids| and |state| are updated. |
| +// |
| +// Returns false on failure. |
| +bool CreateValueFromVarHelper(const PP_Var& var, |
| + scoped_ptr<base::Value>* value, |
| + std::set<int64_t>* parent_ids, |
|
dmichael (off chromium)
2013/03/15 17:35:48
As I mentioned on IM, I think we probably should d
yzshen1
2013/03/15 22:56:49
Done. (Discussed offline.)
On 2013/03/15 17:35:48,
|
| + std::stack<scoped_refptr<VarNodeBase> >* state) { |
| + switch (var.type) { |
| + case PP_VARTYPE_UNDEFINED: |
| + case PP_VARTYPE_NULL: { |
| + value->reset(base::Value::CreateNullValue()); |
| + return true; |
| + } |
| + case PP_VARTYPE_BOOL: { |
| + value->reset(new base::FundamentalValue(PP_ToBool(var.value.as_bool))); |
| + return true; |
| + } |
| + case PP_VARTYPE_INT32: { |
| + value->reset(new base::FundamentalValue(var.value.as_int)); |
| + return true; |
| + } |
| + case PP_VARTYPE_DOUBLE: { |
| + value->reset(new base::FundamentalValue(var.value.as_double)); |
| + return true; |
| + } |
| + case PP_VARTYPE_STRING: { |
| + StringVar* string_var = StringVar::FromPPVar(var); |
| + if (!string_var) |
| + return false; |
| + |
| + value->reset(new base::StringValue(string_var->value())); |
| + return true; |
| + } |
| + case PP_VARTYPE_OBJECT: { |
| + return false; |
| + } |
| + case PP_VARTYPE_ARRAY: { |
| + // TODO(yzshen): Implement it once array var is supported. |
| + return false; |
| + } |
| + case PP_VARTYPE_DICTIONARY: { |
| + if (parent_ids->find(var.value.as_id) != parent_ids->end()) { |
|
dmichael (off chromium)
2013/03/15 17:35:48
optional suggestion: you can also ContainsKey from
yzshen1
2013/03/15 22:56:49
Done.
|
| + // A circular reference is found. |
| + return false; |
| + } |
| + |
| + DictionaryVar* dict_var = DictionaryVar::FromPPVar(var); |
| + if (!dict_var) |
| + return false; |
| + |
| + parent_ids->insert(var.value.as_id); |
| + state->push(new DictionaryVarNode(var.value.as_id, dict_var)); |
| + return true; |
| + } |
| + case PP_VARTYPE_ARRAY_BUFFER: { |
| + ArrayBufferVar* array_buffer = ArrayBufferVar::FromPPVar(var); |
| + if (!array_buffer) |
| + return false; |
| + |
| + base::BinaryValue* binary_value = |
| + base::BinaryValue::CreateWithCopiedBuffer( |
| + static_cast<const char*>(array_buffer->Map()), |
| + array_buffer->ByteLength()); |
| + array_buffer->Unmap(); |
| + value->reset(binary_value); |
| + return true; |
| + } |
| + } |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +// Helper function for CreateVarFromValue(). If |value| is neither list nor |
| +// dictionary, the conversion result is stored in |var|; otherwise, |state| is |
| +// updated. |
| +// |
| +// Returns false on failure. |
| +bool CreateVarFromValueHelper( |
| + const base::Value& value, |
| + ScopedPPVar* var, |
| + std::stack<scoped_refptr<ValueNodeBase> >* state) { |
| + switch (value.GetType()) { |
| + case base::Value::TYPE_NULL: { |
| + *var = PP_MakeNull(); |
| + return true; |
| + } |
| + case base::Value::TYPE_BOOLEAN: { |
| + bool result = false; |
| + if (value.GetAsBoolean(&result)) { |
| + *var = PP_MakeBool(PP_FromBool(result)); |
| + return true; |
| + } |
| + return false; |
| + } |
| + case base::Value::TYPE_INTEGER: { |
| + int result = 0; |
| + if (value.GetAsInteger(&result)) { |
| + *var = PP_MakeInt32(result); |
| + return true; |
| + } |
| + return false; |
| + } |
| + case base::Value::TYPE_DOUBLE: { |
| + double result = 0; |
| + if (value.GetAsDouble(&result)) { |
| + *var = PP_MakeDouble(result); |
| + return true; |
| + } |
| + return false; |
| + } |
| + case base::Value::TYPE_STRING: { |
| + std::string result; |
| + if (value.GetAsString(&result)) { |
| + *var = ScopedPPVar(ScopedPPVar::PassRef(), |
| + StringVar::StringToPPVar(result)); |
| + return true; |
| + } |
| + return false; |
| + } |
| + case base::Value::TYPE_BINARY: { |
| + const base::BinaryValue& binary_value = |
| + static_cast<const base::BinaryValue&>(value); |
| + |
| + size_t size = binary_value.GetSize(); |
| + if (size > std::numeric_limits<uint32>::max()) |
| + return false; |
| + |
| + ScopedPPVar temp( |
| + ScopedPPVar::PassRef(), |
| + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( |
| + static_cast<uint32>(size), binary_value.GetBuffer())); |
| + if (temp.get().type == PP_VARTYPE_ARRAY_BUFFER) { |
| + *var = temp; |
| + return true; |
| + } |
| + return false; |
| + } |
| + case base::Value::TYPE_DICTIONARY: { |
| + state->push(new DictionaryValueNode( |
| + static_cast<const base::DictionaryValue&>(value))); |
| + return true; |
| + } |
| + case base::Value::TYPE_LIST: { |
| + // TODO(yzshen): Add support once array var is supported. |
| + return false; |
| + } |
| + } |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +} // namespace |
| + |
| +base::Value* CreateValueFromVar(const PP_Var& var) { |
| + // Used to detect circular references. |
| + std::set<int64_t> parent_ids; |
| + std::stack<scoped_refptr<VarNodeBase> > state; |
| + scoped_ptr<base::Value> current_value; |
| + |
| + if (!CreateValueFromVarHelper(var, ¤t_value, &parent_ids, &state)) |
| + return NULL; |
| + |
| + while (!state.empty()) { |
| + PP_Var current_var = PP_MakeUndefined(); |
| + if (state.top()->IsDictionaryVar()) { |
| + scoped_refptr<DictionaryVarNode> top( |
| + static_cast<DictionaryVarNode*>(state.top().get())); |
| + |
| + DCHECK(current_value.get() || top->current_key.empty()); |
| + if (current_value.get()) { |
| + top->dict_value->SetWithoutPathExpansion(top->current_key, |
| + current_value.release()); |
|
dmichael (off chromium)
2013/03/15 17:35:48
As we discussed offline, I had a little bit of tro
yzshen1
2013/03/15 22:56:49
Done. Thanks!
On 2013/03/15 17:35:48, dmichael wr
|
| + } |
| + if (top->next == top->end) { |
| + current_value.reset(top->dict_value.release()); |
| + parent_ids.erase(top->var_id); |
| + state.pop(); |
| + continue; |
| + } else { |
| + top->current_key = top->next->first; |
| + current_var = top->next->second.get(); |
| + ++top->next; |
| + |
| + // Ignore the key-value pair if the value is undefined. |
| + if (current_var.type == PP_VARTYPE_UNDEFINED) { |
| + top->current_key.clear(); |
| + continue; |
| + } |
| + } |
| + } else { |
| + NOTREACHED(); |
| + return NULL; |
| + } |
| + |
| + DCHECK(!current_value.get()); |
| + if (!CreateValueFromVarHelper(current_var, ¤t_value, &parent_ids, |
| + &state)) { |
| + return NULL; |
| + } |
| + } |
| + DCHECK(parent_ids.empty()); |
| + return current_value.release(); |
| +} |
| + |
| +PP_Var CreateVarFromValue(const base::Value& value) { |
| + std::stack<scoped_refptr<ValueNodeBase> > state; |
| + ScopedPPVar current_var; |
| + |
| + if (!CreateVarFromValueHelper(value, ¤t_var, &state)) |
| + return PP_MakeUndefined(); |
| + |
| + while (!state.empty()) { |
| + const base::Value* current_value = NULL; |
| + if (state.top()->IsDictionaryValue()) { |
| + scoped_refptr<DictionaryValueNode> top( |
| + static_cast<DictionaryValueNode*>(state.top().get())); |
| + |
| + DCHECK(current_var.get().type != PP_VARTYPE_UNDEFINED || |
| + top->current_key.empty()); |
| + if (current_var.get().type != PP_VARTYPE_UNDEFINED && |
| + !top->dict_var->SetWithStringKey( |
| + top->current_key, current_var.get())) { |
| + return PP_MakeUndefined(); |
| + } |
| + current_var = PP_MakeUndefined(); |
| + if (top->iter.IsAtEnd()) { |
| + current_var = ScopedPPVar(ScopedPPVar::PassRef(), |
| + top->dict_var->GetPPVar()); |
| + state.pop(); |
| + continue; |
| + } else { |
| + top->current_key = top->iter.key(); |
| + current_value = &top->iter.value(); |
| + top->iter.Advance(); |
| + } |
| + } else { |
| + NOTREACHED(); |
| + return PP_MakeUndefined(); |
| + } |
| + |
| + DCHECK(current_var.get().type == PP_VARTYPE_UNDEFINED); |
| + if (!CreateVarFromValueHelper(*current_value, ¤t_var, &state)) |
| + return PP_MakeUndefined(); |
| + }; |
| + |
| + return current_var.Release(); |
| +} |
| +} // namespace ppapi |
| + |