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