1 /*
2  * The Doomsday Engine Project -- libcore
3  *
4  * Copyright © 2009-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5  *
6  * @par License
7  * LGPL: http://www.gnu.org/licenses/lgpl.html
8  *
9  * <small>This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15  * General Public License for more details. You should have received a copy of
16  * the GNU Lesser General Public License along with this program; if not, see:
17  * http://www.gnu.org/licenses</small>
18  */
19 
20 #include "de/RecordValue"
21 #include "de/TextValue"
22 #include "de/RefValue"
23 #include "de/NoneValue"
24 #include "de/FunctionValue"
25 #include "de/Process"
26 #include "de/Context"
27 #include "de/Evaluator"
28 #include "de/Variable"
29 #include "de/Writer"
30 #include "de/Reader"
31 #include "de/ScopeStatement"
32 #include "de/math.h"
33 
34 namespace de {
35 
DENG2_PIMPL(RecordValue)36 DENG2_PIMPL(RecordValue)
37 , DENG2_OBSERVES(Record, Deletion)
38 {
39     Record *record = nullptr;
40     OwnershipFlags ownership;
41     OwnershipFlags oldOwnership; // prior to serialization
42 
43     Impl(Public *i) : Base(i) {}
44 
45     // Observes Record deletion.
46     void recordBeingDeleted(Record &DENG2_DEBUG_ONLY(deleted))
47     {
48         if (!record) return; // Not associated with a record any more.
49 
50         DENG2_ASSERT(record == &deleted);
51         DENG2_ASSERT(!ownership.testFlag(OwnsRecord));
52         record = nullptr;
53         self().setAccessedRecord(nullptr);
54     }
55 };
56 
RecordValue(Record * record,OwnershipFlags o)57 RecordValue::RecordValue(Record *record, OwnershipFlags o)
58     : RecordAccessor(record)
59     , d(new Impl(this))
60 {
61     d->record = record;
62     d->ownership = o;
63     d->oldOwnership = o;
64 
65     DENG2_ASSERT(d->record != nullptr);
66 
67     if (!d->ownership.testFlag(OwnsRecord) &&
68         !d->record->flags().testFlag(Record::WontBeDeleted))
69     {
70         // If we don't own it, someone may delete the record.
71         d->record->audienceForDeletion() += d;
72     }
73 }
74 
RecordValue(Record const & record)75 RecordValue::RecordValue(Record const &record)
76     : RecordAccessor(record)
77     , d(new Impl(this))
78 {
79     d->record = const_cast<Record *>(&record);
80 
81     if (!record.flags().testFlag(Record::WontBeDeleted))
82     {
83         // Someone may delete the record.
84         d->record->audienceForDeletion() += d;
85     }
86 }
87 
RecordValue(IObject const & object)88 RecordValue::RecordValue(IObject const &object)
89     : RecordAccessor(object.objectNamespace())
90     , d(new Impl(this))
91 {
92     d->record = const_cast<Record *>(&object.objectNamespace());
93 
94     if (!d->record->flags().testFlag(Record::WontBeDeleted))
95     {
96         // Someone may delete the record.
97         d->record->audienceForDeletion() += d;
98     }
99 }
100 
~RecordValue()101 RecordValue::~RecordValue()
102 {
103     setRecord(nullptr);
104 }
105 
hasOwnership() const106 bool RecordValue::hasOwnership() const
107 {
108     return d->ownership.testFlag(OwnsRecord);
109 }
110 
usedToHaveOwnership() const111 bool RecordValue::usedToHaveOwnership() const
112 {
113     return d->oldOwnership.testFlag(OwnsRecord);
114 }
115 
record() const116 Record *RecordValue::record() const
117 {
118     return d->record;
119 }
120 
setRecord(Record * record,OwnershipFlags ownership)121 void RecordValue::setRecord(Record *record, OwnershipFlags ownership)
122 {
123     if (record == d->record) return; // Got it already.
124 
125     if (hasOwnership())
126     {
127         delete d->record;
128     }
129     else if (d->record && !d->record->flags().testFlag(Record::WontBeDeleted))
130     {
131         d->record->audienceForDeletion() -= d;
132     }
133 
134     d->record = record;
135     d->ownership = ownership;
136     setAccessedRecord(d->record);
137 
138     if (d->record && !d->ownership.testFlag(OwnsRecord) &&
139         !d->record->flags().testFlag(Record::WontBeDeleted))
140     {
141         // Since we don't own it, someone may delete the record.
142         d->record->audienceForDeletion() += d;
143     }
144 }
145 
takeRecord()146 Record *RecordValue::takeRecord()
147 {
148     verify();
149     if (!hasOwnership())
150     {
151         /// @throw OwnershipError Cannot give away ownership of a record that is not owned.
152         throw OwnershipError("RecordValue::takeRecord", "Value does not own the record");
153     }
154     Record *rec = d->record;
155     DENG2_ASSERT(!d->record->audienceForDeletion().contains(d));
156     d->record = nullptr;
157     d->ownership = RecordNotOwned;
158     setAccessedRecord(nullptr);
159     return rec;
160 }
161 
verify() const162 void RecordValue::verify() const
163 {
164     if (!d->record)
165     {
166         /// @throw NullError The value no longer points to a record.
167         throw NullError("RecordValue::verify", "Value no longer references a record");
168     }
169 }
170 
dereference()171 Record &RecordValue::dereference()
172 {
173     verify();
174     return *d->record;
175 }
176 
dereference() const177 Record const &RecordValue::dereference() const
178 {
179     verify();
180     return *d->record;
181 }
182 
typeId() const183 Value::Text RecordValue::typeId() const
184 {
185     return QStringLiteral("Record");
186 }
187 
duplicate() const188 Value *RecordValue::duplicate() const
189 {
190     verify();
191     if (hasOwnership())
192     {
193         // Make a complete duplicate using a new record.
194         return new RecordValue(new Record(*d->record), OwnsRecord);
195     }
196     return new RecordValue(d->record);
197 }
198 
duplicateAsReference() const199 Value *RecordValue::duplicateAsReference() const
200 {
201     verify();
202     return new RecordValue(d->record);
203 }
204 
asText() const205 Value::Text RecordValue::asText() const
206 {
207     if (d->record)
208     {
209         return d->record->asText();
210     }
211     return "(null)";
212 }
213 
memberScope() const214 Record *RecordValue::memberScope() const
215 {
216     verify();
217     return d->record;
218 }
219 
size() const220 dsize RecordValue::size() const
221 {
222     return dereference().members().size();
223 }
224 
setElement(Value const & index,Value * elementValue)225 void RecordValue::setElement(Value const &index, Value *elementValue)
226 {
227     // We're expecting text.
228     TextValue const *text = dynamic_cast<TextValue const *>(&index);
229     if (!text)
230     {
231         throw IllegalIndexError("RecordValue::setElement",
232                                 "Records must be indexed with text values");
233     }
234     dereference().add(new Variable(text->asText(), elementValue));
235 }
236 
duplicateElement(Value const & value) const237 Value *RecordValue::duplicateElement(Value const &value) const
238 {
239     // We're expecting text.
240     TextValue const *text = dynamic_cast<TextValue const *>(&value);
241     if (!text)
242     {
243         throw IllegalIndexError("RecordValue::duplicateElement",
244                                 "Records must be indexed with text values");
245     }
246     if (dereference().hasMember(*text))
247     {
248         return dereference()[*text].value().duplicateAsReference();
249     }
250     throw NotFoundError("RecordValue::duplicateElement",
251                         "'" + text->asText() + "' does not exist in the record");
252 }
253 
contains(Value const & value) const254 bool RecordValue::contains(Value const &value) const
255 {
256     // We're expecting text.
257     TextValue const *text = dynamic_cast<TextValue const *>(&value);
258     if (!text)
259     {
260         throw IllegalIndexError("RecordValue::contains",
261                                 "Records must be indexed with text values");
262     }
263     return dereference().has(*text);
264 }
265 
isTrue() const266 bool RecordValue::isTrue() const
267 {
268     return size() > 0;
269 }
270 
compare(Value const & value) const271 dint RecordValue::compare(Value const &value) const
272 {
273     RecordValue const *recValue = dynamic_cast<RecordValue const *>(&value);
274     if (!recValue)
275     {
276         // Can't be the same.
277         return cmp(reinterpret_cast<void const *>(this),
278                    reinterpret_cast<void const *>(&value));
279     }
280     return cmp(recValue->d->record, d->record);
281 }
282 
call(Process & process,Value const & arguments,Value *) const283 void RecordValue::call(Process &process, Value const &arguments, Value *) const
284 {
285     verify();
286 
287     // Calling a record causes it to be treated as a class and a new record is
288     // initialized as a member of the class.
289     QScopedPointer<RecordValue> instance(new RecordValue(new Record, RecordValue::OwnsRecord));
290 
291     instance->record()->addSuperRecord(*d->record);
292 
293     // If there is an initializer method, call it now.
294     if (dereference().hasMember(Record::VAR_INIT))
295     {
296         process.call(dereference().function(Record::VAR_INIT), arguments.as<ArrayValue>(),
297                      instance->duplicateAsReference());
298 
299         // Discard the return value from the init function.
300         delete process.context().evaluator().popResult();
301     }
302 
303     process.context().evaluator().pushResult(instance.take());
304 }
305 
306 // Flags for serialization:
307 static duint8 const OWNS_RECORD = 0x1;
308 
operator >>(Writer & to) const309 void RecordValue::operator >> (Writer &to) const
310 {
311     duint8 flags = 0;
312     if (hasOwnership()) flags |= OWNS_RECORD;
313     to << SerialId(RECORD) << flags << dereference();
314 }
315 
operator <<(Reader & from)316 void RecordValue::operator << (Reader &from)
317 {
318     SerialId id;
319     from >> id;
320     if (id != RECORD)
321     {
322         /// @throw DeserializationError The identifier that species the type of the
323         /// serialized value was invalid.
324         throw DeserializationError("RecordValue::operator <<", "Invalid ID");
325     }
326 
327     // Old flags.
328     duint8 flags = 0;
329     from >> flags;
330     d->oldOwnership = OwnershipFlags(flags & OWNS_RECORD? OwnsRecord : 0);
331 
332     from >> dereference();
333 }
334 
takeRecord(Record * record)335 RecordValue *RecordValue::takeRecord(Record *record)
336 {
337     return new RecordValue(record, OwnsRecord);
338 }
339 
takeRecord(Record && record)340 RecordValue *RecordValue::takeRecord(Record &&record)
341 {
342     return new RecordValue(new Record(record), OwnsRecord);
343 }
344 
345 } // namespace de
346