File: | platform/mac/avmshell/../../../core/ByteArrayGlue.cpp |
Location: | line 377, column 9 |
Description: | Value stored to 'error' is never read |
1 | /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ |
2 | /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ |
3 | /* ***** BEGIN LICENSE BLOCK ***** |
4 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
5 | * |
6 | * The contents of this file are subject to the Mozilla Public License Version |
7 | * 1.1 (the "License"); you may not use this file except in compliance with |
8 | * the License. You may obtain a copy of the License at |
9 | * http://www.mozilla.org/MPL/ |
10 | * |
11 | * Software distributed under the License is distributed on an "AS IS" basis, |
12 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 | * for the specific language governing rights and limitations under the |
14 | * License. |
15 | * |
16 | * The Original Code is [Open Source Virtual Machine.]. |
17 | * |
18 | * The Initial Developer of the Original Code is |
19 | * Adobe System Incorporated. |
20 | * Portions created by the Initial Developer are Copyright (C) 2004-2006 |
21 | * the Initial Developer. All Rights Reserved. |
22 | * |
23 | * Contributor(s): |
24 | * Adobe AS3 Team |
25 | * |
26 | * Alternatively, the contents of this file may be used under the terms of |
27 | * either the GNU General Public License Version 2 or later (the "GPL"), or |
28 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
29 | * in which case the provisions of the GPL or the LGPL are applicable instead |
30 | * of those above. If you wish to allow use of your version of this file only |
31 | * under the terms of either the GPL or the LGPL, and not to allow others to |
32 | * use your version of this file under the terms of the MPL, indicate your |
33 | * decision by deleting the provisions above and replace them with the notice |
34 | * and other provisions required by the GPL or the LGPL. If you do not delete |
35 | * the provisions above, a recipient may use your version of this file under |
36 | * the terms of any one of the MPL, the GPL or the LGPL. |
37 | * |
38 | * ***** END LICENSE BLOCK ***** */ |
39 | |
40 | |
41 | #include "avmplus.h" |
42 | #include "zlib.h" |
43 | |
44 | namespace avmplus |
45 | { |
46 | // |
47 | // ByteArray |
48 | // |
49 | |
50 | ByteArray::ByteArray(Toplevel* toplevel) |
51 | : DataIOBase() |
52 | , DataInput() |
53 | , DataOutput() |
54 | , m_toplevel(toplevel) |
55 | , m_gc(toplevel->core()->GetGC()) |
56 | , m_subscribers(m_gc, 0) |
57 | , m_copyOnWriteOwner(NULL__null) |
58 | , m_array(NULL__null) |
59 | , m_capacity(0) |
60 | , m_length(0) |
61 | , m_position(0) |
62 | { |
63 | AvmAssert(m_gc != NULL)do { } while (0); |
64 | } |
65 | |
66 | ByteArray::~ByteArray() |
67 | { |
68 | // no: this can reallocate memory, which is bad to do in a dtor |
69 | // m_subscribers.clear(); |
70 | _Clear(); |
71 | } |
72 | |
73 | void ByteArray::Clear() |
74 | { |
75 | if (m_subscribers.length() > 0) |
76 | { |
77 | AvmAssert(false)do { } while (0); // shouldn't get here? |
78 | m_toplevel->throwRangeError(kInvalidRangeError); |
79 | } |
80 | _Clear(); |
81 | } |
82 | |
83 | void ByteArray::_Clear() |
84 | { |
85 | if (m_array && !IsCopyOnWrite()) |
86 | { |
87 | TellGcDeleteBufferMemory(m_array, m_length); |
88 | mmfx_delete_array(m_array)::MMgcDestructTaggedArrayChecked(m_array); |
89 | } |
90 | m_array = NULL__null; |
91 | m_capacity = 0; |
92 | m_length = 0; |
93 | m_copyOnWriteOwner = NULL__null; |
94 | } |
95 | |
96 | void ByteArray::SetCopyOnWriteOwner(MMgc::GCObject* owner) |
97 | { |
98 | if (owner != NULL__null && m_gc->IsPointerToGCPage(this)) |
99 | { |
100 | WB(m_gc, m_gc->FindBeginningFast(this), &m_copyOnWriteOwner, owner)m_gc->privateWriteBarrier(m_gc->FindBeginningFast(this) , &m_copyOnWriteOwner, (const void *) (owner)); |
101 | } |
102 | else |
103 | { |
104 | m_copyOnWriteOwner = owner; |
105 | } |
106 | } |
107 | |
108 | void ByteArray::SetCopyOnWriteData(MMgc::GCObject* owner, const uint8_t* data, uint32_t length) |
109 | { |
110 | Clear(); |
111 | m_array = const_cast<uint8_t*>(data); |
112 | m_capacity = length; |
113 | m_length = length; |
114 | // we must have a non-null value for m_copyOnWriteOwner, as we |
115 | // use it as an implicit boolean as well, so if none is provided, |
116 | // cheat and use m_gc->emptyWeakRef |
117 | if (owner == NULL__null) |
118 | owner = (MMgc::GCObject*)m_gc->emptyWeakRef; |
119 | SetCopyOnWriteOwner(owner); |
120 | } |
121 | |
122 | void FASTCALL__attribute__((fastcall)) ByteArray::Grower::EnsureWritableCapacity(uint32_t minimumCapacity) |
123 | { |
124 | if (minimumCapacity > (MMgc::GCHeap::kMaxObjectSize - MMgc::GCHeap::kBlockSize*2)) |
125 | m_owner->ThrowMemoryError(); |
126 | |
127 | if (minimumCapacity > m_owner->m_capacity || m_owner->IsCopyOnWrite()) |
128 | { |
129 | uint32_t newCapacity = m_owner->m_capacity << 1; |
130 | if (newCapacity < minimumCapacity) |
131 | newCapacity = uint32_t(minimumCapacity); |
132 | if (newCapacity < kGrowthIncr) |
133 | newCapacity = kGrowthIncr; |
134 | |
135 | m_oldArray = m_owner->m_array; |
136 | m_oldLength = m_owner->m_length; |
137 | |
138 | uint8_t* newArray = mmfx_new_array_opt(uint8_t, newCapacity, MMgc::kCanFailAndZero)::MMgcConstructTaggedArray((uint8_t*)__null, newCapacity, MMgc ::kCanFailAndZero); |
139 | if (!newArray) |
140 | m_owner->ThrowMemoryError(); |
141 | |
142 | m_owner->TellGcNewBufferMemory(newArray, newCapacity); |
143 | if (m_oldArray) |
144 | VMPI_memcpy::memcpy(newArray, m_oldArray, m_oldLength); |
145 | |
146 | m_owner->m_array = newArray; |
147 | m_owner->m_capacity = newCapacity; |
148 | if (m_owner->m_copyOnWriteOwner != NULL__null) |
149 | { |
150 | m_owner->m_copyOnWriteOwner = NULL__null; |
151 | // Set this to NULL so we don't attempt to delete it in our dtor. |
152 | m_oldArray = NULL__null; |
153 | } |
154 | } |
155 | } |
156 | |
157 | /* |
158 | Why the "Grower" class? |
159 | |
160 | (1) It provides a clean way to defer discarding the old buffer until the |
161 | end of the calling function; this matters in the case of Write(), |
162 | as it's legal to call Write() on your own buffer, and so if growth |
163 | occurs, you must not discard the old buffer until copying takes place. |
164 | (2) It avoid redundant calls to NotifySubscribers(); previously we'd call |
165 | once when a reallocation occurred, then again when the length field |
166 | changed. |
167 | (3) It streamlines copy-on-write handling; formerly we either did |
168 | redundant CopyOnWrite alloc-and-copy followed by a Grow alloc-and-copy, |
169 | or an if-else clause with redundant alloc-and-copy. |
170 | |
171 | Of course, the dtor will be skipped (and thus notify and deletes skipped) |
172 | if a longjmp over it, but the old code was subject to the same defects, |
173 | so this is not a new liability. |
174 | */ |
175 | ByteArray::Grower::~Grower() |
176 | { |
177 | if (m_oldArray != m_owner->m_array || m_oldLength != m_owner->m_length) |
178 | { |
179 | m_owner->NotifySubscribers(); |
180 | } |
181 | // m_oldArray could be NULL if we grew a copy-on-write ByteArray. |
182 | if (m_oldArray != NULL__null && m_oldArray != m_owner->m_array) |
183 | { |
184 | m_owner->TellGcDeleteBufferMemory(m_oldArray, m_oldLength); |
185 | mmfx_delete_array(m_oldArray)::MMgcDestructTaggedArrayChecked(m_oldArray); |
186 | } |
187 | } |
188 | |
189 | uint8_t* FASTCALL__attribute__((fastcall)) ByteArray::GetWritableBuffer() |
190 | { |
191 | Grower grower(this); |
192 | grower.EnsureWritableCapacity(m_capacity); |
193 | return m_array; |
194 | } |
195 | |
196 | uint8_t& ByteArray::operator[](uint32_t index) |
197 | { |
198 | if (index >= m_length) |
199 | SetLength(index + 1); |
200 | return m_array[index]; |
201 | } |
202 | |
203 | void FASTCALL__attribute__((fastcall)) ByteArray::SetLength(uint32_t newLength) |
204 | { |
205 | if (m_subscribers.length() > 0 && m_length < DomainEnv::GLOBAL_MEMORY_MIN_SIZE) |
206 | m_toplevel->throwRangeError(kInvalidRangeError); |
207 | |
208 | Grower grower(this); |
209 | if (newLength > m_capacity) |
210 | { |
211 | grower.EnsureWritableCapacity(newLength); |
212 | } |
213 | m_length = newLength; |
214 | if (m_position > newLength) |
215 | m_position = newLength; |
216 | } |
217 | |
218 | // When we might be reading or writing to ourself, use this function |
219 | // apparently SunPro compiler doesn't like combining REALLY_INLINE with static functions in CPP files |
220 | /*static*/ |
221 | REALLY_INLINEinline __attribute__((always_inline)) void move_or_copy(void* dst, const void* src, uint32_t count) |
222 | { |
223 | if ((uintptr_t(dst) - uintptr_t(src)) >= uintptr_t(count)) |
224 | { |
225 | VMPI_memcpy::memcpy(dst, src, count); |
226 | } |
227 | else |
228 | { |
229 | VMPI_memmove::memmove(dst, src, count); |
230 | } |
231 | } |
232 | |
233 | void ByteArray::Read(void* buffer, uint32_t count) |
234 | { |
235 | CheckEOF(count); |
236 | move_or_copy(buffer, m_array + m_position, count); |
237 | m_position += count; |
238 | } |
239 | |
240 | void ByteArray::Write(const void* buffer, uint32_t count) |
241 | { |
242 | uint32_t writeEnd = m_position + count; |
243 | |
244 | Grower grower(this); |
245 | grower.EnsureWritableCapacity(writeEnd); |
246 | |
247 | move_or_copy(m_array + m_position, buffer, count); |
248 | m_position += count; |
249 | if (m_length < m_position) |
250 | m_length = m_position; |
251 | } |
252 | |
253 | void ByteArray::EnsureCapacity(uint32_t capacity) |
254 | { |
255 | Grower grower(this); |
256 | grower.EnsureWritableCapacity(capacity); |
257 | } |
258 | |
259 | void ByteArray::NotifySubscribers() |
260 | { |
261 | for (uint32_t i = 0, n = m_subscribers.length(); i < n; ++i) |
262 | { |
263 | AvmAssert(m_length >= DomainEnv::GLOBAL_MEMORY_MIN_SIZE)do { } while (0); |
264 | |
265 | DomainEnv* subscriber = m_subscribers.get(i); |
266 | if (subscriber) |
267 | { |
268 | subscriber->notifyGlobalMemoryChanged(m_array, m_length); |
269 | } |
270 | else |
271 | { |
272 | // Domain went away? remove link |
273 | m_subscribers.removeAt(i); |
274 | --i; |
275 | } |
276 | } |
277 | } |
278 | |
279 | boolbool ByteArray::addSubscriber(DomainEnv* subscriber) |
280 | { |
281 | if (m_length >= DomainEnv::GLOBAL_MEMORY_MIN_SIZE) |
282 | { |
283 | removeSubscriber(subscriber); |
284 | m_subscribers.add(subscriber); |
285 | // notify the new "subscriber" of the current state of the world |
286 | subscriber->notifyGlobalMemoryChanged(m_array, m_length); |
287 | return truetrue; |
288 | } |
289 | return falsefalse; |
290 | } |
291 | |
292 | boolbool ByteArray::removeSubscriber(DomainEnv* subscriber) |
293 | { |
294 | for (uint32_t i = 0, n = m_subscribers.length(); i < n; ++i) |
295 | { |
296 | if (m_subscribers.get(i) == subscriber) |
297 | { |
298 | m_subscribers.removeAt(i); |
299 | return truetrue; |
300 | } |
301 | } |
302 | return falsefalse; |
303 | } |
304 | |
305 | #ifdef DEBUGGER |
306 | uint64_t ByteArray::bytesUsed() const |
307 | { |
308 | // If m_copyOnWrite is set, then we don't own the buffer, so the profiler |
309 | // should not attribute it to us. |
310 | return IsCopyOnWrite() ? 0 : m_capacity; |
311 | } |
312 | #endif |
313 | |
314 | void ByteArray::TellGcNewBufferMemory(const uint8_t* buf, uint32_t numberOfBytes) |
315 | { |
316 | if (buf && numberOfBytes > 0) |
317 | { |
318 | m_gc->SignalDependentAllocation(numberOfBytes); |
319 | } |
320 | } |
321 | |
322 | void ByteArray::TellGcDeleteBufferMemory(const uint8_t* buf, uint32_t numberOfBytes) |
323 | { |
324 | // Note that we can't rely on using m_toplevel->core()->GetGC(); |
325 | // order of destruction is unspecified, and if this is called at destruction |
326 | // time, Toplevel might have been destroyed before us. So keep a separate GC*. |
327 | if (buf && numberOfBytes > 0) |
328 | { |
329 | m_gc->SignalDependentDeallocation(numberOfBytes); |
330 | } |
331 | } |
332 | |
333 | void ByteArray::Compress(CompressionAlgorithm algorithm) |
334 | { |
335 | // Snarf the data and give ourself some empty data |
336 | // (remember, existing data might be copy-on-write so don't dance on it) |
337 | uint8_t* origData = m_array; |
338 | uint32_t origLen = m_length; |
339 | MMgc::GCObject* origCopyOnWriteOwner = m_copyOnWriteOwner; |
340 | if (!origLen) // empty buffer should give empty result |
341 | return; |
342 | |
343 | m_array = NULL__null; |
344 | m_length = 0; |
345 | m_capacity = 0; |
346 | m_position = 0; |
347 | m_copyOnWriteOwner = NULL__null; |
348 | |
349 | int error = Z_OK0; |
350 | |
351 | // Use zlib to compress the data. This next block is essentially the |
352 | // implementation of the compress2() method, but modified to pass a |
353 | // negative window value (-15) to deflateInit2() for k_deflate mode |
354 | // in order to obtain deflate-only compression (no ZLib headers). |
355 | |
356 | const int MAX_WINDOW_RAW_DEFLATE = -15; |
357 | const int DEFAULT_MEMORY_USE = 8; |
358 | |
359 | z_stream stream; |
360 | VMPI_memset::memset(&stream, 0, sizeof(stream)); |
361 | error = deflateInit2(&stream,deflateInit2_((&stream),(9),(8),(algorithm == k_zlib ? 15 : MAX_WINDOW_RAW_DEFLATE),(DEFAULT_MEMORY_USE), (0), "1.2.3" , (int)sizeof(z_stream)) |
362 | Z_BEST_COMPRESSION,deflateInit2_((&stream),(9),(8),(algorithm == k_zlib ? 15 : MAX_WINDOW_RAW_DEFLATE),(DEFAULT_MEMORY_USE), (0), "1.2.3" , (int)sizeof(z_stream)) |
363 | Z_DEFLATED,deflateInit2_((&stream),(9),(8),(algorithm == k_zlib ? 15 : MAX_WINDOW_RAW_DEFLATE),(DEFAULT_MEMORY_USE), (0), "1.2.3" , (int)sizeof(z_stream)) |
364 | algorithm == k_zlib ? MAX_WBITS : MAX_WINDOW_RAW_DEFLATE,deflateInit2_((&stream),(9),(8),(algorithm == k_zlib ? 15 : MAX_WINDOW_RAW_DEFLATE),(DEFAULT_MEMORY_USE), (0), "1.2.3" , (int)sizeof(z_stream)) |
365 | DEFAULT_MEMORY_USE,deflateInit2_((&stream),(9),(8),(algorithm == k_zlib ? 15 : MAX_WINDOW_RAW_DEFLATE),(DEFAULT_MEMORY_USE), (0), "1.2.3" , (int)sizeof(z_stream)) |
366 | Z_DEFAULT_STRATEGY)deflateInit2_((&stream),(9),(8),(algorithm == k_zlib ? 15 : MAX_WINDOW_RAW_DEFLATE),(DEFAULT_MEMORY_USE), (0), "1.2.3" , (int)sizeof(z_stream)); |
367 | AvmAssert(error == Z_OK)do { } while (0); |
368 | |
369 | uint32_t newCap = deflateBound(&stream, origLen); |
370 | EnsureCapacity(newCap); |
371 | |
372 | stream.next_in = origData; |
373 | stream.avail_in = origLen; |
374 | stream.next_out = m_array; |
375 | stream.avail_out = m_capacity; |
376 | |
377 | error = deflate(&stream, Z_FINISH4); |
Value stored to 'error' is never read | |
378 | AvmAssert(error == Z_STREAM_END)do { } while (0); |
379 | |
380 | m_length = stream.total_out; |
381 | AvmAssert(m_length <= m_capacity)do { } while (0); |
382 | |
383 | // Note that Compress() has always ended with position == length, |
384 | // but Uncompress() has always ended with position == 0. |
385 | // Weird, but we must maintain it. |
386 | m_position = m_length; |
387 | |
388 | deflateEnd(&stream); |
389 | |
390 | // Note: the Compress() method has never reported an error for corrupted data, |
391 | // so we won't start now. (Doing so would probably require a version check, |
392 | // to avoid breaking content that relies on misbehavior.) |
393 | if (origData && origData != m_array && origCopyOnWriteOwner == NULL__null) |
394 | { |
395 | TellGcDeleteBufferMemory(origData, origLen); |
396 | mmfx_delete_array(origData)::MMgcDestructTaggedArrayChecked(origData); |
397 | } |
398 | } |
399 | |
400 | void ByteArray::Uncompress(CompressionAlgorithm algorithm) |
401 | { |
402 | // Snarf the data and give ourself some empty data |
403 | // (remember, existing data might be copy-on-write so don't dance on it) |
404 | uint8_t* origData = m_array; |
405 | uint32_t origCap = m_capacity; |
406 | uint32_t origLen = m_length; |
407 | uint32_t origPos = m_position; |
408 | MMgc::GCObject* origCopyOnWriteOwner = m_copyOnWriteOwner; |
409 | if (!origLen) // empty buffer should give empty result |
410 | return; |
411 | |
412 | m_array = NULL__null; |
413 | m_length = 0; |
414 | m_capacity = 0; |
415 | m_position = 0; |
416 | m_copyOnWriteOwner = NULL__null; |
417 | // we know that the uncompressed data will be at least as |
418 | // large as the compressed data, so let's start there, |
419 | // rather than at zero. |
420 | EnsureCapacity(origCap); |
421 | |
422 | const uint32_t kScratchSize = 8192; |
423 | uint8_t* scratch = mmfx_new_array(uint8_t, kScratchSize)::MMgcConstructTaggedArray((uint8_t*)__null, kScratchSize, MMgc ::kNone); |
424 | |
425 | int error = Z_OK0; |
426 | |
427 | z_stream stream; |
428 | VMPI_memset::memset(&stream, 0, sizeof(stream)); |
429 | error = inflateInit2(&stream, algorithm == k_zlib ? 15 : -15)inflateInit2_((&stream), (algorithm == k_zlib ? 15 : -15) , "1.2.3", (int)sizeof(z_stream)); |
430 | AvmAssert(error == Z_OK)do { } while (0); |
431 | |
432 | stream.next_in = origData; |
433 | stream.avail_in = origLen; |
434 | while (error == Z_OK0) |
435 | { |
436 | stream.next_out = scratch; |
437 | stream.avail_out = kScratchSize; |
438 | error = inflate(&stream, Z_NO_FLUSH0); |
439 | Write(scratch, kScratchSize - stream.avail_out); |
440 | } |
441 | |
442 | inflateEnd(&stream); |
443 | |
444 | mmfx_delete_array(scratch)::MMgcDestructTaggedArrayChecked(scratch); |
445 | |
446 | if (error == Z_STREAM_END1) |
447 | { |
448 | // everything is cool |
449 | if (origData && origData != m_array && origCopyOnWriteOwner == NULL__null) |
450 | { |
451 | TellGcDeleteBufferMemory(origData, origLen); |
452 | mmfx_delete_array(origData)::MMgcDestructTaggedArrayChecked(origData); |
453 | } |
454 | |
455 | // Note that Compress() has always ended with position == length, |
456 | // but Uncompress() has always ended with position == 0. |
457 | // Weird, but we must maintain it. |
458 | m_position = 0; |
459 | } |
460 | else |
461 | { |
462 | // When we error, put the original data back |
463 | m_array = origData; |
464 | m_length = origLen; |
465 | m_capacity = origCap; |
466 | m_position = origPos; |
467 | SetCopyOnWriteOwner(origCopyOnWriteOwner); |
468 | toplevel()->throwIOError(kCompressedDataError); |
469 | } |
470 | } |
471 | |
472 | // |
473 | // ByteArrayObject |
474 | // |
475 | |
476 | ByteArrayObject::ByteArrayObject(VTable* ivtable, ScriptObject* delegate) |
477 | : ScriptObject(ivtable, delegate) |
478 | , m_byteArray(toplevel()) |
479 | { |
480 | c.set(&m_byteArray, sizeof(ByteArray)); |
481 | ByteArrayClass* cls = toplevel()->byteArrayClass(); |
482 | m_byteArray.SetObjectEncoding((ObjectEncoding)cls->get_defaultObjectEncoding()); |
483 | toplevel()->byteArrayCreated(this); |
484 | } |
485 | |
486 | Atom ByteArrayObject::getUintProperty(uint32_t i) const |
487 | { |
488 | if (i < m_byteArray.GetLength()) |
489 | { |
490 | intptr_t const b = m_byteArray[i]; |
491 | return atomFromIntptrValue(b); |
492 | } |
493 | else |
494 | { |
495 | return undefinedAtom; |
496 | } |
497 | } |
498 | |
499 | boolbool ByteArrayObject::hasUintProperty(uint32_t i) const |
500 | { |
501 | return i < m_byteArray.GetLength(); |
502 | } |
503 | |
504 | void ByteArrayObject::setUintProperty(uint32_t i, Atom value) |
505 | { |
506 | m_byteArray[i] = uint8_t(AvmCore::integer(value)); |
507 | } |
508 | |
509 | Atom ByteArrayObject::getAtomProperty(Atom name) const |
510 | { |
511 | uint32_t index; |
512 | if (AvmCore::getIndexFromAtom(name, &index)) |
513 | { |
514 | return getUintProperty(index); |
515 | } |
516 | |
517 | return ScriptObject::getAtomProperty(name); |
518 | } |
519 | |
520 | void ByteArrayObject::setAtomProperty(Atom name, Atom value) |
521 | { |
522 | uint32_t index; |
523 | if (AvmCore::getIndexFromAtom(name, &index)) |
524 | { |
525 | setUintProperty(index, value); |
526 | } |
527 | else |
528 | { |
529 | ScriptObject::setAtomProperty(name, value); |
530 | } |
531 | } |
532 | |
533 | boolbool ByteArrayObject::hasAtomProperty(Atom name) const |
534 | { |
535 | if (core()->currentBugCompatibility()->bugzilla558863) |
536 | { |
537 | uint32_t index; |
538 | if (AvmCore::getIndexFromAtom(name, &index)) |
539 | { |
540 | return index < m_byteArray.GetLength(); |
541 | } |
542 | |
543 | return ScriptObject::hasAtomProperty(name); |
544 | } |
545 | else |
546 | { |
547 | return ScriptObject::hasAtomProperty(name) |
548 | || getAtomProperty(name) != undefinedAtom; |
549 | } |
550 | } |
551 | |
552 | Atom ByteArrayObject::getMultinameProperty(const Multiname* name) const |
553 | { |
554 | uint32_t index; |
555 | if (name->getName()->parseIndex(index)) |
556 | { |
557 | return getUintProperty(index); |
558 | } |
559 | |
560 | return ScriptObject::getMultinameProperty(name); |
561 | } |
562 | |
563 | void ByteArrayObject::setMultinameProperty(const Multiname* name, Atom value) |
564 | { |
565 | uint32_t index; |
566 | if (name->getName()->parseIndex(index)) |
567 | { |
568 | setUintProperty(index, value); |
569 | } |
570 | else |
571 | { |
572 | ScriptObject::setMultinameProperty(name, value); |
573 | } |
574 | } |
575 | |
576 | boolbool ByteArrayObject::hasMultinameProperty(const Multiname* name) const |
577 | { |
578 | uint32_t index; |
579 | if (name->getName()->parseIndex(index)) |
580 | { |
581 | return index < m_byteArray.GetLength(); |
582 | } |
583 | |
584 | return ScriptObject::hasMultinameProperty(name); |
585 | } |
586 | |
587 | String* ByteArrayObject::_toString() |
588 | { |
589 | uint32_t len = m_byteArray.GetLength(); |
590 | const uint8_t* c = m_byteArray.GetReadableBuffer(); |
591 | |
592 | Toplevel* toplevel = this->toplevel(); |
593 | AvmCore* core = toplevel->core(); |
594 | |
595 | if (len >= 3) |
596 | { |
597 | // UTF8 BOM |
598 | if ((c[0] == 0xef) && (c[1] == 0xbb) && (c[2] == 0xbf)) |
599 | { |
600 | return core->newStringUTF8((const char*)c + 3, len - 3); |
601 | } |
602 | else if ((c[0] == 0xfe) && (c[1] == 0xff)) |
603 | { |
604 | //UTF-16 big endian |
605 | c += 2; |
606 | len = (len - 2) >> 1; |
607 | return core->newStringEndianUTF16(/*littleEndian*/falsefalse, (const wchar*)c, len); |
608 | } |
609 | else if ((c[0] == 0xff) && (c[1] == 0xfe)) |
610 | { |
611 | //UTF-16 little endian |
612 | c += 2; |
613 | len = (len - 2) >> 1; |
614 | return core->newStringEndianUTF16(/*littleEndian*/truetrue, (const wchar*)c, len); |
615 | } |
616 | } |
617 | |
618 | String* result = toplevel->tryFromSystemCodepage(c); |
619 | if (result != NULL__null) |
620 | return result; |
621 | |
622 | // Use newStringUTF8() with "strict" explicitly set to false to mimick old, |
623 | // buggy behavior, where malformed UTF-8 sequences are stored as single characters. |
624 | return core->newStringUTF8((const char*)c, len, falsefalse); |
625 | } |
626 | |
627 | Atom ByteArrayObject::readObject() |
628 | { |
629 | return m_byteArray.ReadObject(); |
630 | } |
631 | |
632 | void ByteArrayObject::writeObject(Atom value) |
633 | { |
634 | m_byteArray.WriteObject(value); |
635 | } |
636 | |
637 | int ByteArrayObject::readByte() |
638 | { |
639 | return (int8_t)m_byteArray.ReadU8(); |
640 | } |
641 | |
642 | int ByteArrayObject::readUnsignedByte() |
643 | { |
644 | return m_byteArray.ReadU8(); |
645 | } |
646 | |
647 | int ByteArrayObject::readShort() |
648 | { |
649 | return (int16_t)m_byteArray.ReadU16(); |
650 | } |
651 | |
652 | int ByteArrayObject::readUnsignedShort() |
653 | { |
654 | return m_byteArray.ReadU16(); |
655 | } |
656 | |
657 | int ByteArrayObject::readInt() |
658 | { |
659 | return (int32_t)m_byteArray.ReadU32(); |
660 | } |
661 | |
662 | uint32_t ByteArrayObject::readUnsignedInt() |
663 | { |
664 | return m_byteArray.ReadU32(); |
665 | } |
666 | |
667 | double ByteArrayObject::readFloat() |
668 | { |
669 | return m_byteArray.ReadFloat(); |
670 | } |
671 | |
672 | double ByteArrayObject::readDouble() |
673 | { |
674 | return m_byteArray.ReadDouble(); |
675 | } |
676 | |
677 | boolbool ByteArrayObject::readBoolean() |
678 | { |
679 | return m_byteArray.ReadBoolean(); |
680 | } |
681 | |
682 | void ByteArrayObject::writeBoolean(boolbool value) |
683 | { |
684 | m_byteArray.WriteBoolean(value); |
685 | } |
686 | |
687 | void ByteArrayObject::writeByte(int value) |
688 | { |
689 | m_byteArray.WriteU8((uint8_t)value); |
690 | } |
691 | |
692 | void ByteArrayObject::writeShort(int value) |
693 | { |
694 | m_byteArray.WriteU16((uint16_t)value); |
695 | } |
696 | |
697 | void ByteArrayObject::writeInt(int value) |
698 | { |
699 | m_byteArray.WriteU32((uint32_t)value); |
700 | } |
701 | |
702 | void ByteArrayObject::writeUnsignedInt(uint32_t value) |
703 | { |
704 | m_byteArray.WriteU32(value); |
705 | } |
706 | |
707 | void ByteArrayObject::writeFloat(double value) |
708 | { |
709 | m_byteArray.WriteFloat((float)value); |
710 | } |
711 | |
712 | void ByteArrayObject::writeDouble(double value) |
713 | { |
714 | m_byteArray.WriteDouble(value); |
715 | } |
716 | |
717 | ByteArray::CompressionAlgorithm ByteArrayObject::algorithmToEnum(String* algorithm) |
718 | { |
719 | Toplevel* toplevel = this->toplevel(); |
720 | toplevel->checkNull(algorithm, "algorithm"); |
721 | if (algorithm->equalsLatin1("zlib")) |
722 | { |
723 | return ByteArray::k_zlib; |
724 | } |
725 | if (algorithm->equalsLatin1("deflate")) |
726 | { |
727 | return ByteArray::k_deflate; |
728 | } |
729 | else |
730 | { |
731 | // Unknown format |
732 | toplevel->throwIOError(kCompressedDataError); |
733 | return ByteArray::k_zlib; // not reached, pacify compiler |
734 | } |
735 | } |
736 | |
737 | void ByteArrayObject::_compress(String* algorithm) |
738 | { |
739 | m_byteArray.Compress(algorithmToEnum(algorithm)); |
740 | } |
741 | |
742 | void ByteArrayObject::_uncompress(String* algorithm) |
743 | { |
744 | m_byteArray.Uncompress(algorithmToEnum(algorithm)); |
745 | } |
746 | |
747 | void ByteArrayObject::writeBytes(ByteArrayObject *bytes, |
748 | uint32_t offset, |
749 | uint32_t length) |
750 | { |
751 | toplevel()->checkNull(bytes, "bytes"); |
752 | |
753 | if (length == 0) { |
754 | length = bytes->get_length() - offset; |
755 | } |
756 | |
757 | m_byteArray.WriteByteArray(bytes->GetByteArray(), |
758 | offset, |
759 | length); |
760 | } |
761 | |
762 | void ByteArrayObject::readBytes(ByteArrayObject *bytes, |
763 | uint32_t offset, |
764 | uint32_t length) |
765 | { |
766 | toplevel()->checkNull(bytes, "bytes"); |
767 | |
768 | if (length == 0) { |
769 | length = m_byteArray.Available(); |
770 | } |
771 | |
772 | m_byteArray.ReadByteArray(bytes->GetByteArray(), |
773 | offset, |
774 | length); |
775 | } |
776 | |
777 | String* ByteArrayObject::readMultiByte(uint32_t length, String* charSet) |
778 | { |
779 | toplevel()->checkNull(charSet, "charSet"); |
780 | return m_byteArray.ReadMultiByte(length, charSet); |
781 | } |
782 | |
783 | String* ByteArrayObject::readUTF() |
784 | { |
785 | return m_byteArray.ReadUTF(); |
786 | } |
787 | |
788 | String* ByteArrayObject::readUTFBytes(uint32_t length) |
789 | { |
790 | return m_byteArray.ReadUTFBytes(length); |
791 | } |
792 | |
793 | void ByteArrayObject::writeMultiByte(String* value, String* charSet) |
794 | { |
795 | toplevel()->checkNull(value, "value"); |
796 | toplevel()->checkNull(charSet, "charSet"); |
797 | m_byteArray.WriteMultiByte(value, charSet); |
798 | } |
799 | |
800 | void ByteArrayObject::writeUTF(String* value) |
801 | { |
802 | toplevel()->checkNull(value, "value"); |
803 | m_byteArray.WriteUTF(value); |
804 | } |
805 | |
806 | void ByteArrayObject::writeUTFBytes(String* value) |
807 | { |
808 | toplevel()->checkNull(value, "value"); |
809 | m_byteArray.WriteUTFBytes(value); |
810 | } |
811 | |
812 | uint32_t ByteArrayObject::get_objectEncoding() |
813 | { |
814 | return m_byteArray.GetObjectEncoding(); |
815 | } |
816 | |
817 | void ByteArrayObject::set_objectEncoding(uint32_t objectEncoding) |
818 | { |
819 | if ((objectEncoding == kAMF3)||(objectEncoding == kAMF0)) |
820 | { |
821 | m_byteArray.SetObjectEncoding(ObjectEncoding(objectEncoding)); |
822 | } |
823 | else |
824 | { |
825 | toplevel()->throwArgumentError(kInvalidEnumError, "objectEncoding"); |
826 | } |
827 | } |
828 | |
829 | Stringp ByteArrayObject::get_endian() |
830 | { |
831 | return (m_byteArray.GetEndian() == kBigEndian) ? core()->kbigEndian : core()->klittleEndian; |
832 | } |
833 | |
834 | void ByteArrayObject::set_endian(Stringp type) |
835 | { |
836 | Toplevel* toplevel = this->toplevel(); |
837 | AvmCore* core = toplevel->core(); |
838 | |
839 | toplevel->checkNull(type, "endian"); |
840 | |
841 | type = core->internString(type); |
842 | if (type == core->kbigEndian) |
843 | { |
844 | m_byteArray.SetEndian(kBigEndian); |
845 | } |
846 | else if (type == core->klittleEndian) |
847 | { |
848 | m_byteArray.SetEndian(kLittleEndian); |
849 | } |
850 | else |
851 | { |
852 | toplevel->throwArgumentError(kInvalidEnumError, "type"); |
853 | } |
854 | } |
855 | |
856 | void ByteArrayObject::clear() |
857 | { |
858 | m_byteArray.Clear(); |
859 | m_byteArray.SetPosition(0); |
860 | } |
861 | |
862 | #ifdef DEBUGGER |
863 | uint64_t ByteArrayObject::bytesUsed() const |
864 | { |
865 | uint64_t size = ScriptObject::bytesUsed(); |
866 | size += m_byteArray.bytesUsed(); |
867 | return size; |
868 | } |
869 | #endif |
870 | |
871 | // |
872 | // ByteArrayClass |
873 | // |
874 | |
875 | ByteArrayClass::ByteArrayClass(VTable *vtable) |
876 | : ClassClosure(vtable) |
877 | { |
878 | setPrototypePtr(toplevel()->objectClass->construct()); |
879 | set_defaultObjectEncoding(kEncodeDefault); |
880 | } |
881 | |
882 | GCRef<ByteArrayObject> ByteArrayClass::constructByteArray() |
883 | { |
884 | return constructObject(); |
885 | } |
886 | } |
887 |