// ********************************************************************** // // Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include #include using namespace std; FreezeScript::TransformVisitor::TransformVisitor(const DataPtr& src, const TransformInfoPtr& info, const string& context) : _src(src), _info(info), _context(context) { assert(_info); } void FreezeScript::TransformVisitor::visitBoolean(const BooleanDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { BooleanDataPtr b = BooleanDataPtr::dynamicCast(_src); if(b) { dest->setValue(b->getValue()); } else { StringDataPtr s = StringDataPtr::dynamicCast(_src); if(s) { string v = s->getValue(); if(v == "true") { dest->setValue(true); } else if(v == "false") { dest->setValue(false); } else { conversionError(type, _src->getType(), v); } } else { typeMismatchError(type, _src->getType()); } } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitInteger(const IntegerDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { IntegerDataPtr i = IntegerDataPtr::dynamicCast(_src); if(i) { dest->setValue(i->getValue(), false); } else { StringDataPtr s = StringDataPtr::dynamicCast(_src); if(s) { string str = s->getValue(); Ice::Long value; if(IceUtilInternal::stringToInt64(str, value)) { dest->setValue(value, false); } else { conversionError(type, _src->getType(), str); } } else { typeMismatchError(type, _src->getType()); } } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitDouble(const DoubleDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { DoubleDataPtr d = DoubleDataPtr::dynamicCast(_src); if(d) { dest->setValue(d->doubleValue()); } else { StringDataPtr s = StringDataPtr::dynamicCast(_src); if(s) { string str = s->stringValue(); const char* start = str.c_str(); char* end; double v = strtod(start, &end); if(errno == ERANGE) { rangeError(str, type); } else { while(*end) { if(!isspace(static_cast(*end))) { conversionError(type, _src->getType(), str); return; } end++; } if(!*end) { dest->setValue(v); } } } else { typeMismatchError(type, _src->getType()); } } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitString(const StringDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { StringDataPtr s = StringDataPtr::dynamicCast(_src); BooleanDataPtr b = BooleanDataPtr::dynamicCast(_src); IntegerDataPtr i = IntegerDataPtr::dynamicCast(_src); DoubleDataPtr d = DoubleDataPtr::dynamicCast(_src); EnumDataPtr e = EnumDataPtr::dynamicCast(_src); ProxyDataPtr p = ProxyDataPtr::dynamicCast(_src); if(s || b || i || d || e || p) { dest->setValue(_src->toString()); } else { typeMismatchError(type, _src->getType()); } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitProxy(const ProxyDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { ProxyDataPtr p = ProxyDataPtr::dynamicCast(_src); if(p) { dest->setValue(p->getValue()); } else { StringDataPtr s = StringDataPtr::dynamicCast(_src); if(s) { dest->setValue(s->getValue(), false); } else { typeMismatchError(type, _src->getType()); } } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitStruct(const StructDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { // // Support struct->struct and class->struct transforms. // StructDataPtr s = StructDataPtr::dynamicCast(_src); ObjectRefPtr obj = ObjectRefPtr::dynamicCast(_src); if((s || obj) && isCompatible(type, _src->getType())) { // // Transform members with the same name. // DataMemberMap srcMap = s ? s->getMembers() : obj->getValue()->getMembers(); DataMemberMap destMap = dest->getMembers(); string typeName = typeToString(type); for(DataMemberMap::iterator p = destMap.begin(); p != destMap.end(); ++p) { DataMemberMap::iterator q = srcMap.find(p->first); if(q != srcMap.end()) { string context = typeName + " member " + p->first + " value"; TransformVisitor v(q->second, _info, context); p->second->visit(v); } } } else { typeMismatchError(type, _src->getType()); } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitSequence(const SequenceDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { SequenceDataPtr s = SequenceDataPtr::dynamicCast(_src); if(s && isCompatible(type, _src->getType())) { DataList& srcElements = s->getElements(); DataList destElements; Slice::SequencePtr seqType = Slice::SequencePtr::dynamicCast(type); assert(seqType); Slice::TypePtr elemType = seqType->type(); string typeName = typeToString(type); for(DataList::const_iterator p = srcElements.begin(); p != srcElements.end(); ++p) { DataPtr element = _info->getDataFactory()->create(elemType, false); Destroyer elementDestroyer(element); try { TransformVisitor v(*p, _info, typeName + " element"); element->visit(v); destElements.push_back(element); elementDestroyer.release(); } catch(const ClassNotFoundException& ex) { // // If transformation of the sequence element fails because a class // could not be found, then we invoke purgeObjects() to determine // whether we should ignore the situation (and remove the element // from the sequence) or raise the exception again. // if(!_info->purgeObjects()) { throw; } warning("purging element of sequence " + typeToString(type) + " due to missing class type " + ex.id); } } DataList& l = dest->getElements(); l.swap(destElements); } else { typeMismatchError(type, _src->getType()); } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitEnum(const EnumDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { string name; EnumDataPtr e = EnumDataPtr::dynamicCast(_src); if(e && isCompatible(type, _src->getType())) { name = e->toString(); } else { StringDataPtr s = StringDataPtr::dynamicCast(_src); if(s) { name = s->getValue(); } else { typeMismatchError(type, _src->getType()); return; } } if(!dest->setValueAsString(name)) { conversionError(type, _src->getType(), name); } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitDictionary(const DictionaryDataPtr& dest) { Slice::TypePtr type = dest->getType(); if(_info->doDefaultTransform(type)) { DictionaryDataPtr d = DictionaryDataPtr::dynamicCast(_src); if(d && isCompatible(type, _src->getType())) { DataMap& srcMap = d->getElements(); DataMap destMap; Slice::DictionaryPtr dictType = Slice::DictionaryPtr::dynamicCast(type); assert(dictType); Slice::TypePtr keyType = dictType->keyType(); Slice::TypePtr valueType = dictType->valueType(); string typeName = typeToString(type); for(DataMap::const_iterator p = srcMap.begin(); p != srcMap.end(); ++p) { DataPtr key = _info->getDataFactory()->create(keyType, false); Destroyer keyDestroyer(key); DataPtr value = _info->getDataFactory()->create(valueType, false); Destroyer valueDestroyer(value); TransformVisitor keyVisitor(p->first, _info, typeName + " key"); key->visit(keyVisitor); try { TransformVisitor valueVisitor(p->second, _info, typeName + " value"); value->visit(valueVisitor); } catch(const ClassNotFoundException& ex) { // // If transformation of the dictionary value fails because a class // could not be found, then we invoke purgeObjects() to determine // whether we should ignore the situation (and remove the element // from the dictionary) or raise the exception again. // if(!_info->purgeObjects()) { throw; } warning("purging element of dictionary " + typeToString(dictType) + " due to missing class type " + ex.id); continue; } DataMap::const_iterator q = destMap.find(key); if(q != destMap.end()) { warning("duplicate dictionary key in " + typeToString(dictType)); } else { destMap.insert(DataMap::value_type(key, value)); keyDestroyer.release(); valueDestroyer.release(); } } DataMap& m = dest->getElements(); m.swap(destMap); } else { typeMismatchError(type, _src->getType()); } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::visitObject(const ObjectRefPtr& dest) { // // Support struct->class and class->class transforms. // Slice::TypePtr type = dest->getType(); ObjectRefPtr src = ObjectRefPtr::dynamicCast(_src); StructDataPtr s = StructDataPtr::dynamicCast(_src); if(!src && !s) { typeMismatchError(type, _src->getType()); } else if(_info->doDefaultTransform(type)) { if(src) { // // class->class transform // ObjectDataPtr srcValue = src->getValue(); Slice::TypePtr srcType = src->getType(); if(!srcValue) { // // Allow a nil value from type Object. // if(Slice::BuiltinPtr::dynamicCast(srcType) || isCompatible(type, srcType)) { dest->setValue(0); } else { typeMismatchError(type, srcType); } } else { Slice::TypePtr srcValueType = srcValue->getType(); if(isCompatible(type, srcValueType)) { // // If the types are in the same Slice unit, then we can simply // copy the reference. Otherwise, we check the object map to // see if an equivalent object has already been created, and // if not, then we have to create one. // if(type->unit().get() == srcValueType->unit().get()) { dest->setValue(srcValue); } else { ObjectDataMap& objectDataMap = _info->getObjectDataMap(); ObjectDataMap::iterator p = objectDataMap.find(srcValue.get()); if(p != objectDataMap.end() && p->second) { dest->setValue(p->second); } else { // // If the type has been renamed, we need to get its equivalent // in the new Slice definitions. // Slice::TypePtr newType = _info->getRenamedType(srcValueType); if(!newType) { string name = typeToString(srcValueType); Slice::TypeList l = type->unit()->lookupType(name, false); if(l.empty()) { throw ClassNotFoundException(name); } newType = l.front(); } // // Use createObject() so that an initializer is invoked if necessary. // DataPtr newObj = _info->getDataFactory()->createObject(newType, false); ObjectRefPtr newRef = ObjectRefPtr::dynamicCast(newObj); assert(newRef); ObjectDataPtr newValue = newRef->getValue(); try { transformObject(newValue, srcValue); } catch(...) { newObj->destroy(); throw; } dest->setValue(newValue); newObj->destroy(); } } } else { typeMismatchError(type, srcValueType); } } } else { // // struct->class transform // Slice::TypePtr srcType = _src->getType(); if(isCompatible(type, srcType)) { // // If the type has been renamed, we need to get its equivalent // in the new Slice definitions. // Slice::TypePtr newType = _info->getRenamedType(srcType); if(!newType) { string name = typeToString(srcType); Slice::TypeList l = type->unit()->lookupType(name, false); if(l.empty()) { throw ClassNotFoundException(name); } newType = l.front(); } // // Use createObject() so that an initializer is invoked if necessary. // DataPtr newObj = _info->getDataFactory()->createObject(newType, false); ObjectRefPtr newRef = ObjectRefPtr::dynamicCast(newObj); assert(newRef); ObjectDataPtr newValue = newRef->getValue(); try { // // Transform members with the same name. // DataMemberMap srcMap = s->getMembers(); DataMemberMap destMap = newValue->getMembers(); string typeName = typeToString(type); for(DataMemberMap::iterator p = destMap.begin(); p != destMap.end(); ++p) { DataMemberMap::iterator q = srcMap.find(p->first); if(q != srcMap.end()) { string context = typeName + " member " + p->first + " value"; TransformVisitor v(q->second, _info, context); p->second->visit(v); } } } catch(...) { newObj->destroy(); throw; } dest->setValue(newValue); newObj->destroy(); } else { typeMismatchError(type, srcType); } } } _info->executeCustomTransform(dest, _src); } void FreezeScript::TransformVisitor::transformObject(const ObjectDataPtr& dest, const ObjectDataPtr& src) { // // The source object must be present in the object map (we currently don't support // transforming two ObjectData instances from the same Slice unit - this transform // would be handled by-reference at the ObjectRef level). We must update the object // map before transforming members in order to handle cycles. // ObjectDataMap& objectDataMap = _info->getObjectDataMap(); ObjectDataMap::iterator p = objectDataMap.find(src.get()); assert(p != objectDataMap.end()); assert(p->second == 0); objectDataMap.erase(p); objectDataMap.insert(ObjectDataMap::value_type(src.get(), dest)); p = objectDataMap.find(src.get()); try { // // Transform members with the same name. // DataMemberMap srcMap = src->getMembers(); DataMemberMap destMap = dest->getMembers(); string typeName = typeToString(dest->getType()); for(DataMemberMap::iterator p = destMap.begin(); p != destMap.end(); ++p) { DataMemberMap::iterator q = srcMap.find(p->first); if(q != srcMap.end()) { string context = typeName + " member " + p->first + " value"; TransformVisitor v(q->second, _info, context); p->second->visit(v); } } } catch(...) { objectDataMap.erase(p); #if (defined(_MSC_VER) && (_MSC_VER >= 1600)) objectDataMap.insert(ObjectDataMap::value_type(src.get(), static_cast(nullptr))); #else objectDataMap.insert(ObjectDataMap::value_type(src.get(), 0)); #endif throw; } } bool FreezeScript::TransformVisitor::checkRename(const Slice::TypePtr& dest, const Slice::TypePtr& src) { if(dest->unit().get() != src->unit().get()) { Slice::TypePtr t = _info->getRenamedType(src); return t.get() == dest.get(); } return false; } bool FreezeScript::TransformVisitor::isCompatible(const Slice::TypePtr& dest, const Slice::TypePtr& src) { Slice::BuiltinPtr b1 = Slice::BuiltinPtr::dynamicCast(dest); if(b1) { Slice::BuiltinPtr b2 = Slice::BuiltinPtr::dynamicCast(src); switch(b1->kind()) { case Slice::Builtin::KindByte: case Slice::Builtin::KindShort: case Slice::Builtin::KindInt: case Slice::Builtin::KindLong: { if(b2) { switch(b2->kind()) { case Slice::Builtin::KindByte: case Slice::Builtin::KindShort: case Slice::Builtin::KindInt: case Slice::Builtin::KindLong: case Slice::Builtin::KindString: { return true; } case Slice::Builtin::KindBool: case Slice::Builtin::KindFloat: case Slice::Builtin::KindDouble: case Slice::Builtin::KindObject: case Slice::Builtin::KindObjectProxy: case Slice::Builtin::KindLocalObject: { return false; } } } return false; } case Slice::Builtin::KindBool: { if(b2 && (b2->kind() == Slice::Builtin::KindBool || b2->kind() == Slice::Builtin::KindString)) { return true; } return false; } case Slice::Builtin::KindFloat: case Slice::Builtin::KindDouble: { if(b2) { switch(b2->kind()) { case Slice::Builtin::KindFloat: case Slice::Builtin::KindDouble: case Slice::Builtin::KindString: { return true; } case Slice::Builtin::KindByte: case Slice::Builtin::KindShort: case Slice::Builtin::KindInt: case Slice::Builtin::KindLong: case Slice::Builtin::KindBool: case Slice::Builtin::KindObject: case Slice::Builtin::KindObjectProxy: case Slice::Builtin::KindLocalObject: { return false; } } } return false; } case Slice::Builtin::KindString: { if(b2) { switch(b2->kind()) { case Slice::Builtin::KindByte: case Slice::Builtin::KindBool: case Slice::Builtin::KindShort: case Slice::Builtin::KindInt: case Slice::Builtin::KindLong: case Slice::Builtin::KindFloat: case Slice::Builtin::KindDouble: case Slice::Builtin::KindString: case Slice::Builtin::KindObjectProxy: { return true; } case Slice::Builtin::KindObject: case Slice::Builtin::KindLocalObject: { return false; } } return false; } else if(Slice::EnumPtr::dynamicCast(src)) { return true; } else if(Slice::ProxyPtr::dynamicCast(src)) { return true; } return false; } case Slice::Builtin::KindObject: { // // Allow transformation from Object to class. Validation has to // be done during transformation, when the actual type of // an instance can be compared for compatibility with the // new type. // Slice::ClassDeclPtr cl = Slice::ClassDeclPtr::dynamicCast(src); if(cl || (b2 && b2->kind() == Slice::Builtin::KindObject)) { return true; } return false; } case Slice::Builtin::KindObjectProxy: { Slice::ProxyPtr p = Slice::ProxyPtr::dynamicCast(src); if(p || (b2 && b2->kind() == Slice::Builtin::KindObjectProxy) || (b2 && b2->kind() == Slice::Builtin::KindString)) { return true; } return false; } case Slice::Builtin::KindLocalObject: { assert(false); return false; } } assert(false); } Slice::ClassDeclPtr cl1 = Slice::ClassDeclPtr::dynamicCast(dest); if(cl1) { Slice::ClassDeclPtr cl2 = Slice::ClassDeclPtr::dynamicCast(src); if(cl2 && checkClasses(cl1, cl2)) { return true; } Slice::StructPtr s2 = Slice::StructPtr::dynamicCast(src); if(s2) { if(checkRename(dest, src)) { return true; } if(s2 && cl1->scoped() == s2->scoped()) { return true; } } return false; } Slice::StructPtr s1 = Slice::StructPtr::dynamicCast(dest); if(s1) { if(checkRename(dest, src)) { return true; } Slice::StructPtr s2 = Slice::StructPtr::dynamicCast(src); Slice::ClassDeclPtr cl2 = Slice::ClassDeclPtr::dynamicCast(src); if((s2 && s1->scoped() == s2->scoped()) || (cl2 && s1->scoped() == cl2->scoped())) { return true; } return false; } Slice::ProxyPtr p1 = Slice::ProxyPtr::dynamicCast(dest); if(p1) { Slice::BuiltinPtr b2 = Slice::BuiltinPtr::dynamicCast(src); if(b2 && (b2->kind() == Slice::Builtin::KindObjectProxy || b2->kind() == Slice::Builtin::KindString)) { return true; } Slice::ProxyPtr p2 = Slice::ProxyPtr::dynamicCast(src); if(p2 && checkClasses(p1->_class(), p2->_class())) { return true; } return false; } Slice::DictionaryPtr d1 = Slice::DictionaryPtr::dynamicCast(dest); if(d1) { Slice::DictionaryPtr d2 = Slice::DictionaryPtr::dynamicCast(src); if(d2) { return isCompatible(d1->keyType(), d2->keyType()) && isCompatible(d1->valueType(), d2->valueType()); } return false; } Slice::SequencePtr seq1 = Slice::SequencePtr::dynamicCast(dest); if(seq1) { Slice::SequencePtr seq2 = Slice::SequencePtr::dynamicCast(src); if(seq2) { return isCompatible(seq1->type(), seq2->type()); } return false; } Slice::EnumPtr e1 = Slice::EnumPtr::dynamicCast(dest); if(e1) { Slice::BuiltinPtr b2 = Slice::BuiltinPtr::dynamicCast(src); if(b2 && b2->kind() == Slice::Builtin::KindString) { return true; } if(checkRename(dest, src)) { return true; } Slice::EnumPtr e2 = Slice::EnumPtr::dynamicCast(src); if(e2 && e1->scoped() == e2->scoped()) { return true; } return false; } assert(false); return false; } bool FreezeScript::TransformVisitor::checkClasses(const Slice::ClassDeclPtr& dest, const Slice::ClassDeclPtr& src) { // // Here are the rules for verifying class compatibility: // // 1. If the type ids are the same, assume they are compatible. // 2. If the source type has been renamed, then check its equivalent new definition for compatibility. // 3. Otherwise, the types are only compatible if they are defined in the same Slice unit, and if the // destination type is a base type of the source type. // string s1 = dest->scoped(); string s2 = src->scoped(); if(s1 == s2) { return true; } else { Slice::TypePtr t = _info->getRenamedType(src); Slice::ClassDeclPtr s = Slice::ClassDeclPtr::dynamicCast(t); if(s) { return checkClasses(dest, s); } if(dest->unit().get() != src->unit().get()) { Slice::TypeList l = dest->unit()->lookupTypeNoBuiltin(s2, false); if(l.empty()) { _info->getErrorReporter()->error("class " + s2 + " not found in new Slice definitions"); } s = Slice::ClassDeclPtr::dynamicCast(l.front()); } else { s = src; } if(s) { Slice::ClassDefPtr def = s->definition(); if(!def) { _info->getErrorReporter()->error("class " + s2 + " declared but not defined"); } return def->isA(s1); } } return false; } void FreezeScript::TransformVisitor::typeMismatchError(const Slice::TypePtr& dest, const Slice::TypePtr& src) { ostringstream ostr; ostr << "unable to transform"; if(!_context.empty()) { ostr << ' ' << _context; } ostr << " from " << typeToString(src) << " to " << typeToString(dest); warning(ostr.str()); } void FreezeScript::TransformVisitor::conversionError(const Slice::TypePtr& dest, const Slice::TypePtr& src, const string& value) { ostringstream ostr; ostr << "unable to convert"; if(!_context.empty()) { ostr << ' ' << _context; } ostr << " `" << value << "' from " << typeToString(src) << " to " << typeToString(dest); warning(ostr.str()); } void FreezeScript::TransformVisitor::rangeError(const string& value, const Slice::TypePtr& type) { ostringstream ostr; if(!_context.empty()) { ostr << _context << ' '; } ostr << "`" << value << "' is out of range for type " << typeToString(type); warning(ostr.str()); } void FreezeScript::TransformVisitor::warning(const string& msg) { _info->getErrorReporter()->warning(msg); }