Example #1
0
        public void Link_MultipleDefinedAtomsWithTheSameName_ThrowsException()
        {
            var a = new ObjectFile();

            a.Atoms.Add(new Procedure()
            {
                Name      = "Proc",
                IsDefined = true
            });

            var b = new ObjectFile();

            b.Atoms.Add(new Procedure()
            {
                Name      = "Proc",
                IsDefined = true
            });

            var linker = new AtomLinker();

            var message = "There are multiple atoms with called 'Proc'.";

            Assert.That(
                () => linker.Link(new[] { a, b }),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message));
        }
Example #2
0
        /// <summary>
        /// Writes an object file to a stream.
        /// </summary>
        /// <param name="file">The object file to write.</param>
        /// <param name="destination">The stream the object file is written to.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="file"/> or <paramref name="destination"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="destination"/> is read only.
        /// </exception>
        public void Write(ObjectFile file, Stream destination)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }
            if (!destination.CanWrite)
            {
                throw new ArgumentException("The stream is read only.", nameof(destination));
            }

            using (writer = new BinaryWriter(destination, Encoding.UTF8, true))
            {
                // Write header.
                writer.Write(0x6D6F7461);   // Ascii for "atom" backwards.
                writer.Write((ushort)1);
                writer.Write(file.IsOriginSet);
                writer.Write(file.Origin);

                of = file;
                foreach (dynamic atom in file)
                {
                    Write(atom);
                }
            }
        }
Example #3
0
        public void Link_MultipleMains_ThrowsException()
        {
            var a = new ObjectFile();

            a.Atoms.Add(new Procedure()
            {
                Name   = "Proc1",
                IsMain = true
            });

            var b = new ObjectFile();

            b.Atoms.Add(new Procedure()
            {
                Name   = "Proc2",
                IsMain = true
            });

            var linker = new AtomLinker();

            var message = "Multiple main procedures.";

            Assert.That(
                () => linker.Link(new[] { a, b }),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message));
        }
Example #4
0
        public void Link_UndefinedAtoms_ThrowsException()
        {
            var a = new ObjectFile();

            var b = new ObjectFile();

            b.Atoms.Add(new NullTerminatedString()
            {
                Name = "A"
            });

            var procedure = new Procedure();

            procedure.IsDefined = true;
            procedure.Name      = "Proc";
            procedure.References.Add(new Reference(b.Atoms[0]));
            b.Atoms.Add(procedure);

            var linker = new AtomLinker();

            var message = new StringBuilder();

            message.AppendLine("Undefined atoms:");
            message.AppendLine("\tA");

            Assert.That(
                () => linker.Link(new[] { a, b }, new MemoryStream()),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message.ToString()));
        }
Example #5
0
        /// <summary>
        /// Copies the references from the object <paramref name="files"/>.
        /// </summary>
        /// <param name="files">The object files to copy references from.</param>
        /// <param name="combined">The object file that owns the copied references.</param>
        private static void CopyReferences(IEnumerable <ObjectFile> files, ObjectFile combined)
        {
            foreach (var file in files)
            {
                foreach (var procedure in file.OfType <Procedure>().Where(p => p.IsDefined))
                {
                    foreach (var reference in procedure.References)
                    {
                        var proc       = combined.OfType <Procedure>().First(p => p.Name == procedure.Name);
                        var referenced = combined.First(a => a.Name == reference.Atom.Name);

                        var r = new Reference(referenced);
                        r.Address = reference.Address;
                        r.IsAddressInLittleEndian = reference.IsAddressInLittleEndian;
                        r.SizeOfAddress           = reference.SizeOfAddress;

                        if (!referenced.IsGlobal)
                        {
                            var a = GetDefiningObjectFile(referenced.Name, files);
                            var b = GetDefiningObjectFile(proc.Name, files);

                            if (a != null && b != null && a != b)
                            {
                                throw new InvalidObjectFileException("'" + proc.Name + "' is referencing '" + referenced.Name + "' which is local to another object file.");
                            }
                        }

                        proc.References.Add(r);
                    }
                }
            }
        }
Example #6
0
        public void Link_2FilesWith1Atom_LinksObjectFiles()
        {
            var a = new ObjectFile();

            a.Atoms.Add(new NullTerminatedString()
            {
                Content   = "Abc",
                IsDefined = true,
                Name      = "A"
            });

            var b = new ObjectFile();

            b.Atoms.Add(new NullTerminatedString()
            {
                Content   = "Def",
                IsDefined = true,
                Name      = "B"
            });

            var linker = new AtomLinker();
            var c      = linker.Link(new[] { a, b });

            Assert.AreEqual(2, c.Atoms.Count);

            Assert.AreEqual("A", ((NullTerminatedString)c.Atoms[0]).Name);
            Assert.AreEqual("Abc", ((NullTerminatedString)c.Atoms[0]).Content);
            Assert.True(((NullTerminatedString)c.Atoms[0]).IsDefined);

            Assert.AreEqual("B", ((NullTerminatedString)c.Atoms[1]).Name);
            Assert.AreEqual("Def", ((NullTerminatedString)c.Atoms[1]).Content);
            Assert.True(((NullTerminatedString)c.Atoms[1]).IsDefined);
        }
Example #7
0
        public void Link_LocalAtomInFile2ReferencedByAtomInFile1_ThrowsException()
        {
            var a = new ObjectFile();

            a.Atoms.Add(new NullTerminatedString()
            {
                Name = "A"
            });

            var procedure = new Procedure();

            procedure.IsDefined = true;
            procedure.Name      = "Proc";
            procedure.References.Add(new Reference(a.Atoms[0]));
            a.Atoms.Add(procedure);

            var b = new ObjectFile();

            b.Atoms.Add(new NullTerminatedString()
            {
                IsDefined = true,
                Content   = "Abc",
                Name      = "A"
            });

            var linker = new AtomLinker();

            var message = "'Proc' is referencing 'A' which is local to another object file.";

            Assert.That(
                () => linker.Link(new[] { a, b }),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message));
        }
Example #8
0
        /// <summary>
        /// Resolves the references the given <paramref name="file"/> have.
        /// </summary>
        /// <param name="file">The atoms with references.</param>
        /// <exception cref="InvalidObjectFileException">
        /// There are references with overlapping addresses.
        /// </exception>
        private void ResolveReferences(ObjectFile file)
        {
            foreach (var tuple in references)
            {
                if (file.Atoms.Count < tuple.Item2)
                {
                    var message = string.Format("The atom called '{0}' has a reference to atom number {1} which doesn't exist.", tuple.Item1.Name, tuple.Item2);

                    throw new InvalidObjectFileException(message);
                }

                var reference = new Reference(file.Atoms[(int)tuple.Item2]);
                reference.IsAddressInLittleEndian = tuple.Item3;
                reference.SizeOfAddress           = tuple.Item4;
                reference.Address = tuple.Item5;

                foreach (var r in tuple.Item1.References)
                {
                    if (r.IsOverlapping(reference))
                    {
                        var message = string.Format("{0}'s reference to '{1}' has an overlapping address with the reference to '{2}' at {3}.", tuple.Item1.Name, r.Atom.Name, reference.Atom.Name, ToHex(r.Address));

                        throw new InvalidObjectFileException(message);
                    }
                }

                tuple.Item1.References.Add(reference);
            }
        }
Example #9
0
        /// <summary>
        /// Reads the atoms from the <paramref name="source"/>.
        /// </summary>
        /// <param name="source">The source to read atoms from.</param>
        /// <returns>The atoms read from the <paramref name="source"/>.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="source"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// There is no header on the current position in the <paramref name="source"/>.
        /// </exception>
        /// <exception cref="InvalidObjectFileException">
        /// <paramref name="source"/> is an invalid object file.
        /// </exception>
        public ObjectFile Read(Stream source)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (source.Length - source.Position <= 13)
            {
                throw new ArgumentException("There is no header on the current position.", nameof(source));
            }

            var file = new ObjectFile();

            using (reader = new BinaryReader(source))
            {
                // This number is ascii for "atom" backwards.
                if (reader.ReadInt32() != 0x6D6F7461)
                {
                    throw new InvalidObjectFileException("This is not an atom object file.");
                }
                if (reader.ReadUInt16() != 1)
                {
                    throw new InvalidObjectFileException("This object file is not using version one.");
                }

                file.IsOriginSet = reader.ReadBoolean();
                file.Origin      = reader.ReadUInt64();

                while (!EndOfFile)
                {
                    switch (reader.ReadByte())
                    {
                    case 0:
                        file.Atoms.Add(Procedure());
                        break;

                    case 1:
                        file.Atoms.Add(NullTerminatedString());
                        break;

                    case 2:
                        file.Atoms.Add(Data());
                        break;

                    default:
                        throw new InvalidObjectFileException("Invalid atom type at " + ToHex(reader.BaseStream.Position - 1));
                    }
                }
            }

            ResolveReferences(file);

            return(file);
        }
Example #10
0
        public void Link_NoMainProcedure_ThrowsException()
        {
            var a      = new ObjectFile();
            var linker = new AtomLinker();

            var message = "There is no main procedure.";

            Assert.That(
                () => linker.Link(new[] { a }, new MemoryStream()),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message));
        }
Example #11
0
        public void Write_ProcedureWithoutReferences_ProcedureWritten()
        {
            byte[] bytes =
            {
                0x61, 0x74, 0x6F, 0x6D, // Magic number.
                0x01, 0x00,             // Version 1.
                0x01,                   // The origin is set.
                0x20, 0x00, 0x00, 0x00, // The origin is set to 0x20.
                0x00, 0x00, 0x00, 0x00,

                // Base part of the atom.
                0x00,                         // Procedure type.
                0x01,                         // The procedure is defined.
                0x01,                         // The procedure is global.
                0x50, 0x72, 0x6F, 0x63, 0x00, // The procedure is called 'Proc'.

                // Procedure part of the atom.
                0x01,                   // This is the main procedure.
                0x01, 0x00, 0x00, 0x00, // The size of the code is 1 byte.
                0xAA,                   // The procedure code.
                0x00, 0x00              // Number of references.
            };

            var procedure = new Procedure()
            {
                IsDefined = true,
                IsGlobal  = true,
                Name      = "Proc",
                IsMain    = true
            };

            procedure.Code.Add(0xAA);
            using (var stream = new MemoryStream())
            {
                var writer = new AtomWriter();

                var file = new ObjectFile();
                file.IsOriginSet = true;
                file.Origin      = 0x20;
                file.Atoms.Add(procedure);

                writer.Write(file, stream);

                CollectionAssert.AreEqual(bytes, stream.ToArray());
            }
        }
Example #12
0
        public void Link_ProcedureReferencingData_LinksToBinary()
        {
            var file = new ObjectFile();

            var data = new Data();

            file.Atoms.Add(data);

            data.IsDefined = true;
            data.Name      = "Data";
            data.Content.Add(0xAA);
            data.Content.Add(0x55);

            var procedure = new Procedure();

            file.Atoms.Add(procedure);

            procedure.IsMain    = true;
            procedure.IsDefined = true;
            procedure.Name      = "Proc";
            procedure.Code.Add(0x00);
            procedure.Code.Add(0x00);

            procedure.References.Add(new Reference(data)
            {
                IsAddressInLittleEndian = true,
                SizeOfAddress           = 2
            });

            var binary = new byte[]
            {
                0x02, 0x00, // Procedure code.
                0xAA, 0x55, // The data chunk.
            };

            using (var stream = new MemoryStream())
            {
                var linker = new AtomLinker();
                linker.Link(new[] { file }, stream);

                CollectionAssert.AreEqual(binary, stream.ToArray());
            }
        }
Example #13
0
        public void Link_ProcedureReferencingProcedure_LinksToBinary()
        {
            var file = new ObjectFile();

            var sub = new Procedure();

            file.Atoms.Add(sub);

            sub.IsDefined = true;
            sub.Name      = "Sub";
            sub.Code.Add(0xAA);
            sub.Code.Add(0x55);

            var main = new Procedure();

            file.Atoms.Add(main);

            main.IsMain    = true;
            main.IsDefined = true;
            main.Name      = "Proc";
            main.Code.Add(0x00);
            main.Code.Add(0x00);

            main.References.Add(new Reference(sub)
            {
                IsAddressInLittleEndian = true,
                SizeOfAddress           = 2
            });

            var binary = new byte[]
            {
                0x02, 0x00, // Main procedure.
                0xAA, 0x55, // Sub procedure.
            };

            using (var stream = new MemoryStream())
            {
                var linker = new AtomLinker();
                linker.Link(new[] { file }, stream);

                CollectionAssert.AreEqual(binary, stream.ToArray());
            }
        }
Example #14
0
        public void Write_Data_DataWritten()
        {
            byte[] bytes =
            {
                0x61, 0x74, 0x6F, 0x6D, // Magic number.
                0x01, 0x00,             // Version 1.
                0x01,                   // The origin is set.
                0x20, 0x00, 0x00, 0x00, // The origin is set to 0x20.
                0x00, 0x00, 0x00, 0x00,

                // Base part of the atom.
                0x02,                         // Data type.
                0x01,                         // The data is defined.
                0x01,                         // The data is global.
                0x44, 0x61, 0x74, 0x61, 0x00, // The data is called 'Data'.

                // Data part of the atom.
                0x01, 0x00, 0x00, 0x00,         // The size of the data block is 1 byte.
                0xAA                            // The data block.
            };

            var data = new Data()
            {
                IsDefined = true,
                IsGlobal  = true,
                Name      = "Data"
            };

            data.Content.Add(0xAA);
            using (var stream = new MemoryStream())
            {
                var writer = new AtomWriter();

                var file = new ObjectFile();
                file.IsOriginSet = true;
                file.Origin      = 0x20;
                file.Atoms.Add(data);

                writer.Write(file, stream);

                CollectionAssert.AreEqual(bytes, stream.ToArray());
            }
        }
Example #15
0
        public void Link_InconsistentOrigin_ThrowsException()
        {
            var a = new ObjectFile();

            a.IsOriginSet = true;
            a.Origin      = 0x10;

            var b = new ObjectFile();

            b.IsOriginSet = true;

            var linker = new AtomLinker();

            var message = "Inconsistent origin.";

            Assert.That(
                () => linker.Link(new[] { a, b }),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message));
        }
Example #16
0
        public void Write_NullTerminatedString_StringWritten()
        {
            byte[] bytes =
            {
                0x61, 0x74, 0x6F, 0x6D, // Magic number.
                0x01, 0x00,             // Version 1.
                0x01,                   // The origin is set.
                0x20, 0x00, 0x00, 0x00, // The origin is set to 0x20.
                0x00, 0x00, 0x00, 0x00,

                // Base part of the atom.
                0x01,                   // String type.
                0x01,                   // The string is defined.
                0x01,                   // The string is global.
                0x54, 0x78, 0x74, 0x00, // The string is called 'Txt'.

                // String part of the atom.
                0x41, 0x62, 0x63, 0x00          // The string is 'Abc'.
            };

            var s = new NullTerminatedString()
            {
                IsDefined = true,
                IsGlobal  = true,
                Name      = "Txt",
                Content   = "Abc"
            };

            using (var stream = new MemoryStream())
            {
                var writer = new AtomWriter();

                var file = new ObjectFile();
                file.IsOriginSet = true;
                file.Origin      = 0x20;
                file.Atoms.Add(s);

                writer.Write(file, stream);

                CollectionAssert.AreEqual(bytes, stream.ToArray());
            }
        }
Example #17
0
        public void Link_ProcedureReferencingNullTerminatedString_LinksToBinary()
        {
            var file = new ObjectFile();

            file.Atoms.Add(new NullTerminatedString()
            {
                IsDefined = true,
                Content   = "Abc",
                Name      = "A"
            });

            var procedure = new Procedure();

            file.Atoms.Add(procedure);

            procedure.IsMain    = true;
            procedure.IsDefined = true;
            procedure.Name      = "Proc";
            procedure.Code.Add(0x00);
            procedure.Code.Add(0x00);

            procedure.References.Add(new Reference(file.Atoms[0])
            {
                IsAddressInLittleEndian = true,
                SizeOfAddress           = 2
            });

            var binary = new byte[]
            {
                0x02, 0x00,             // Procedure code.
                0x41, 0x62, 0x63, 0x00  // The string 'Abc'.
            };

            using (var stream = new MemoryStream())
            {
                var linker = new AtomLinker();
                linker.Link(new[] { file }, stream);

                CollectionAssert.AreEqual(binary, stream.ToArray());
            }
        }
Example #18
0
        public void Link_GlobalAtomInFile1ReferencedByAtomInFile2_LinksObjectFiles()
        {
            var a = new ObjectFile();

            a.Atoms.Add(new NullTerminatedString()
            {
                IsDefined = true,
                IsGlobal  = true,
                Content   = "Abc",
                Name      = "A"
            });

            var b = new ObjectFile();

            b.Atoms.Add(new NullTerminatedString()
            {
                Name = "A"
            });

            var procedure = new Procedure();

            procedure.IsDefined = true;
            procedure.Name      = "Proc";
            procedure.References.Add(new Reference(b.Atoms[0]));
            b.Atoms.Add(procedure);

            var linker = new AtomLinker();
            var c      = linker.Link(new[] { a, b });

            Assert.AreEqual(2, c.Atoms.Count);

            Assert.AreEqual("A", ((NullTerminatedString)c.Atoms[0]).Name);
            Assert.AreEqual("Abc", ((NullTerminatedString)c.Atoms[0]).Content);
            Assert.True(((NullTerminatedString)c.Atoms[0]).IsDefined);

            Assert.AreEqual("Proc", ((Procedure)c.Atoms[1]).Name);
            Assert.AreSame(((Procedure)c.Atoms[1]).References[0].Atom, c.Atoms[0]);
            Assert.True(((Procedure)c.Atoms[1]).IsDefined);
        }
Example #19
0
        public void Write_NoAtoms_HeaderWritten()
        {
            byte[] bytes =
            {
                0x61, 0x74, 0x6F, 0x6D, // Magic number.
                0x01, 0x00,             // Version 1.
                0x01,                   // The origin is set.
                0x20, 0x00, 0x00, 0x00, // The origin is set to 0x20.
                0x00, 0x00, 0x00, 0x00,
            };

            using (var stream = new MemoryStream())
            {
                var writer = new AtomWriter();

                var file = new ObjectFile();
                file.IsOriginSet = true;
                file.Origin      = 0x20;

                writer.Write(file, stream);

                CollectionAssert.AreEqual(bytes, stream.ToArray());
            }
        }
Example #20
0
        public void Link_ObjectFileWithMainAndUnreferencedAtoms_OnlyMainIsLinked()
        {
            var file = new ObjectFile();

            var sub = new Procedure();

            file.Atoms.Add(sub);

            sub.IsDefined = true;
            sub.Name      = "Sub";
            sub.Code.Add(0xAA);
            sub.Code.Add(0x55);

            var main = new Procedure();

            file.Atoms.Add(main);

            main.IsMain    = true;
            main.IsDefined = true;
            main.Name      = "Proc";
            main.Code.Add(0x00);
            main.Code.Add(0x00);

            var binary = new byte[]
            {
                0x00, 0x00, // Main procedure.
            };

            using (var stream = new MemoryStream())
            {
                var linker = new AtomLinker();
                linker.Link(new[] { file }, stream);

                CollectionAssert.AreEqual(binary, stream.ToArray());
            }
        }
Example #21
0
        public void Link_MultipleUndefinedDifferentlyTypedAtomsWithTheSameName_ThrowsException()
        {
            var a = new ObjectFile();

            a.Atoms.Add(new Procedure()
            {
                Name = "Proc"
            });

            var b = new ObjectFile();

            b.Atoms.Add(new Data()
            {
                Name = "Proc"
            });

            var linker = new AtomLinker();

            var message = "'Proc' and 'Proc' is not of the same type.";

            Assert.That(
                () => linker.Link(new[] { a, b }),
                Throws.TypeOf <InvalidObjectFileException>().With.Message.EqualTo(message));
        }
Example #22
0
        public void Write_ProcedureWithReference_ProcedureRead()
        {
            byte[] bytes =
            {
                0x61, 0x74, 0x6F, 0x6D, // Magic number.
                0x01, 0x00,             // Version 1.
                0x01,                   // The origin is set.
                0x20, 0x00, 0x00, 0x00, // The origin is set to 0x20.
                0x00, 0x00, 0x00, 0x00,

                // Base part of the atom.
                0x00,                         // Procedure type.
                0x01,                         // The procedure is defined.
                0x01,                         // The procedure is global.
                0x50, 0x72, 0x6F, 0x63, 0x00, // The procedure is called 'Proc'.

                // Procedure part of the atom.
                0x01,                   // This is the main procedure.
                0x04, 0x00, 0x00, 0x00, // The size of the code is 4 bytes.
                0x00, 0x00, 0x00, 0x00, // The procedure code.
                0x01, 0x00,             // Number of references.

                // Reference.
                0x01, 0x00, 0x00, 0x00, // It is atom number 1 that is being referenced (index based).
                0x01,                   // The address is in little endian.
                0x04,                   // The size of the address is 4 bytes.
                0x00, 0x00, 0x00, 0x00, // The address to relocate.

                // Null terminated string.
                0x01,       // Null terminated string type.
                0x01,       // The string is defined.
                0x00,       // The string is not global.
                0x53, 0x00, // The string is called 'S'.

                // String part
                0x54, 0x78, 0x74, 0x00          // The string is 'Txt'.
            };

            var s = new NullTerminatedString()
            {
                IsDefined = true,
                IsGlobal  = false,
                Name      = "S",
                Content   = "Txt"
            };

            var procedure = new Procedure()
            {
                IsDefined = true,
                IsGlobal  = true,
                Name      = "Proc",
                IsMain    = true
            };

            procedure.Code.Add(0x00);
            procedure.Code.Add(0x00);
            procedure.Code.Add(0x00);
            procedure.Code.Add(0x00);

            procedure.References.Add(new Reference(s)
            {
                IsAddressInLittleEndian = true,
                SizeOfAddress           = 4,
                Address = 0x00
            });

            using (var stream = new MemoryStream())
            {
                var writer = new AtomWriter();

                var file = new ObjectFile();
                file.IsOriginSet = true;
                file.Origin      = 0x20;

                file.Atoms.Add(procedure);
                file.Atoms.Add(s);

                writer.Write(file, stream);

                CollectionAssert.AreEqual(bytes, stream.ToArray());
            }
        }
Example #23
0
        /// <summary>
        /// Combines object files into a single one.
        /// </summary>
        /// <param name="files">The object files to combine.</param>
        /// <returns>The combined object file.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="files"/> is null.
        /// </exception>
        /// <exception cref="InvalidObjectFileException">
        /// The combined object file is invalid.
        /// </exception>
        public ObjectFile Link(IEnumerable <ObjectFile> files)
        {
            if (files == null)
            {
                throw new ArgumentNullException(nameof(files));
            }

            var combined = new ObjectFile();

            foreach (var file in files)
            {
                if (combined.Origin != file.Origin)
                {
                    if (combined.IsOriginSet)
                    {
                        throw new InvalidObjectFileException("Inconsistent origin.");
                    }

                    combined.IsOriginSet = true;
                    combined.Origin      = file.Origin;
                }

                foreach (var atom in file)
                {
                    var procedure = atom as Procedure;
                    if (procedure != null)
                    {
                        if (combined.OfType <Procedure>().Any(p => p.IsMain) && procedure.IsMain)
                        {
                            throw new InvalidObjectFileException("Multiple main procedures.");
                        }
                    }

                    var duplicate = combined.FirstOrDefault(a => a.Name == atom.Name);
                    if (duplicate != null)
                    {
                        if (duplicate.IsDefined && atom.IsDefined)
                        {
                            throw new InvalidObjectFileException("There are multiple atoms with called '" + atom.Name + "'.");
                        }
                        if (duplicate.GetType() != atom.GetType())
                        {
                            throw new InvalidObjectFileException("'" + duplicate.Name + "' and '" + atom.Name + "' is not of the same type.");
                        }
                        if (!duplicate.IsDefined && !atom.IsDefined)
                        {
                            continue;
                        }
                        if (duplicate.IsDefined && !atom.IsDefined)
                        {
                            continue;
                        }

                        combined.Atoms.Remove(duplicate);
                    }

                    combined.Atoms.Add(Copy(atom));
                }
            }

            CopyReferences(files, combined);

            return(combined);
        }