Files
avmplus/core/ByteArrayGlue.h
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

439 lines
17 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BYTEARRAYGLUE_INCLUDED
#define BYTEARRAYGLUE_INCLUDED
namespace avmplus
{
class ByteArray : public DataInput,
public DataOutput
{
public:
friend class ByteArrayTask;
friend class ByteArrayObject;
friend class ByteArraySetLengthTask;
friend class ByteArraySwapBufferTask;
friend class ByteArrayCompareAndSwapLengthTask;
friend class ByteArrayClearTask;
class Buffer : public FixedHeapRCObject
{
public:
virtual void destroy();
virtual ~Buffer();
uint8_t* array;
uint32_t capacity;
uint32_t length;
};
ByteArray(Toplevel* toplevel);
ByteArray(Toplevel* toplevel, const ByteArray& other);
ByteArray(Toplevel* toplevel, ByteArray::Buffer* source, bool shared);
~ByteArray();
void Clear();
REALLY_INLINE uint8_t operator[](uint32_t index) const { AvmAssert(index < m_buffer->length); return (index < m_buffer->length) ? m_buffer->array[index] : 0; }
uint8_t& operator[](uint32_t index);
REALLY_INLINE uint32_t GetLength() const { return m_buffer->length; }
REALLY_INLINE bool IsShared() const { return m_isShareable && (m_buffer->RefCount() > 1); }
// Ensure that the capacity of the ByteArray is at least 'newLength',
// and set length = max(GetLength(), newLength),
// and set position = min(GetPosition(), newLength)
//
// Note that SetLength(0) empties the (logical) contents of the ByteArray,
// but doesn't reduce the underlying capacity allocated.
// Use Clear() to eliminate existing memory allocations.
void FASTCALL SetLength(uint32_t newLength);
private:
// Ensure that the capacity of the ByteArray is at least 'newLength',
// and set length = max(GetLength(), newLength),
// and set position = min(GetPosition(), newLength)
//
// This is alternative entry point that *only* the length setter
// uses; it serves as a hint from client that newLength is
// expected maximum length for immediate future.
void FASTCALL SetLengthFromAS3(uint32_t newLength);
void SetLengthCommon(uint32_t newLength, bool calledFromSetter);
void UnprotectedSetLengthCommon(uint32_t newLength, bool calledFromSetter);
int32_t UnprotectedAtomicCompareAndSwapLength(int32_t expectedLength, int32_t newLength);
public:
// Set the length to x+y, with overflow check. If x+y overflows a uint32_t then
// throw a MemoryError (same error that the one-argument variety will throw if
// trying to create a buffer larger than the buffer limit, which is less than 2^32-1).
void FASTCALL SetLength(uint32_t x, uint32_t y);
// You can use this call to get a READ_ONLY pointer into the ByteArray.
// The pointer starts at offset zero (regardless of the value of GetPosition())
// and the data is guaranteed to be valid for GetLength() bytes.
//
// *** USE THIS METHOD WITH EXTREME CAUTION, AND ONLY WHEN ABSOLUTELY NECESSARY ***
//
// In particular, you must not use this method and const_cast the result, as
// you may overwrite ByteArrays that are copy-on-write; please use GetWritableBuffer
// instead if the data must be written to.
//
// You normally should use Read() rather than this method,
// which is provided solely for compatibility with existing code paths;
// it's highly recommend you not use this method for new code.
//
REALLY_INLINE const uint8_t* GetReadableBuffer() const { return m_buffer->array; }
// You can use this call to get a WRITABLE pointer into the ByteArray.
// The pointer starts at offset zero (regardless of the value of GetPosition())
// and the data is guaranteed to be valid for GetLength() bytes.
//
// *** USE THIS METHOD WITH EXTREME CAUTION, AND ONLY WHEN ABSOLUTELY NECESSARY ***
//
// You normally should use Write() rather than this method,
// which is provided solely for compatibility with existing code paths;
// it's highly recommend you not use this method for new code.
//
uint8_t* FASTCALL GetWritableBuffer();
// Make this ByteArray a copy-on-write ByteArray. All existing content will be discarded
// and the new data referenced instead. "owner", if non-null, is a GCObject that
// owns the data (a reference will be kept to it to ensure the data isn't collected);
// if owner is null, it's assumed that the data will never become invalid.
void SetCopyOnWriteData(MMgc::GCObject* owner, const uint8_t* data, uint32_t length);
REALLY_INLINE uint32_t GetPosition() { return m_position; }
REALLY_INLINE void SetPosition(uint32_t pos) { m_position = pos; }
// Ensure that the ByteArray has a capacity of (at least) capacity.
// This does not affect GetLength() or GetPosition().
// If an attempted expansion fails, throws AS3 exception.
void EnsureCapacity(uint32_t capacity);
void EnsureCapacityNoInline(uint32_t capacity); // (same, but NO_INLINE)
// Ensure that the ByteArray has a capacity of (at least) capacity
// and return true, or set exn_recv to the thrown exception and
// return false.
bool EnsureCapacityOrFail(uint32_t capacity,
enum CatchAction, Exception **exn_recv);
// overrides from DataInput
/*virtual*/ uint32_t Available() { return (m_position <= m_buffer->length) ? (m_buffer->length - m_position) : 0; }
/*virtual*/ void Read(void* buffer, uint32_t count);
// overrides from DataOutput
/*virtual*/ void Write(const void* buffer, uint32_t count);
bool isShareable () const;
bool setShareable (bool value);
bool addSubscriber(DomainEnv* subscriber);
bool removeSubscriber(DomainEnv* subscriber);
// compression / decompression
enum CompressionAlgorithm
{
k_zlib,
k_deflate,
k_lzma
};
void Compress(CompressionAlgorithm algorithm);
void Uncompress(CompressionAlgorithm algorithm);
// For use by ByteArrayObject for optimized read functions: check that there are
// nbytes available to read at the current position, and throw an EOF exception
// if not. Update the position to point beyond that area. Return a pointer to
// the start of the area.
//
// Precondition: 0 < nbytes < 4096
uint8_t* requestBytesForShortRead(uint32_t nbytes);
// For use by ByteArrayObject for optimized write functions: check that there are
// nbytes available to write at the current position, and extend the array if not.
// Throw MemoryError if the array cannot be extended. Update the position to point
// beyond the write area. Return a pointer to the start of the area.
//
// Precondition: 0 < nbytes < 4096
uint8_t* requestBytesForShortWrite(uint32_t nbytes);
int32_t CAS(uint32_t index, int32_t expected, int32_t next);
bool share();
#ifdef DEBUGGER
public:
// Called by the profiler to determine the number of bytes attributed
// to this object, not including sizeof(*this).
virtual uint64_t bytesUsed() const;
#endif
/*protected: */
virtual Toplevel* toplevel() const { return m_toplevel; }
static void UpdateSubscribers();
private:
void CompressViaZlibVariant(CompressionAlgorithm algorithm);
void UncompressViaZlibVariant(CompressionAlgorithm algorithm);
void CompressViaLzma();
void UncompressViaLzma();
public: // Tasks need it
class Grower
{
friend class ByteArray;
public:
Grower(ByteArray* owner, uint32_t minimumCapacity)
: m_owner(owner)
, m_oldArray(owner->m_buffer->array)
, m_oldLength(owner->m_buffer->length)
, m_oldCapacity(owner->m_buffer->capacity)
, m_minimumCapacity(minimumCapacity)
{
}
void FASTCALL ReallocBackingStore(uint32_t newCapacity);
void FASTCALL EnsureWritableCapacity();
REALLY_INLINE bool RequestWillReallocBackingStore() const;
REALLY_INLINE bool RequestExceedsMemoryAvailable() const;
void SetLengthCommon(uint32_t newLength, bool calledFromLengthSetter);
void run(); // from SafepointTask
virtual ~Grower();
private:
ByteArray* m_owner;
uint8_t* m_oldArray;
uint32_t m_oldLength;
uint32_t m_oldCapacity;
uint32_t m_minimumCapacity;
};
public:
Buffer* getUnderlyingBuffer() { return m_buffer; }
private:
enum { kGrowthIncr = 4096 };
typedef WeakRefList<DomainEnv> WeakSubscriberList;
private:
void UnprotectedClear();
public:// FIXME made public for Aggregate::reloadGlobalMemories()
void NotifySubscribers();
private: // end FIXME
void TellGcNewBufferMemory(const uint8_t* buf, uint32_t numberOfBytes);
void TellGcDeleteBufferMemory(const uint8_t* buf, uint32_t numberOfBytes);
REALLY_INLINE bool IsCopyOnWrite() const { return m_copyOnWriteOwner != NULL; }
void SetCopyOnWriteOwner(MMgc::GCObject* owner);
ByteArray(const ByteArray& lhs); // unimplemented
ByteArray& operator=(const ByteArray&); // unimplemented
public:
void gcTrace(MMgc::GC* gc)
{
// This tracer assumes the following (true as of 2010-12-08):
// - m_array is allocated with FixedMalloc
gc->TraceLocation(&m_toplevel);
gc->TraceLocation(&m_copyOnWriteOwner);
m_subscribers.gcTrace(gc);
DataInput::gcTrace(gc);
DataOutput::gcTrace(gc);
}
#ifdef DEBUG
// Normally manually traced classes would subclass
// GCInlineObject to pick this up but ByteArray is an anomaly.
MMgc::GCTracerCheckResult gcTraceOffsetIsTraced(uint32_t) const
{
return MMgc::kPassDueToManualTracer;
}
#endif
private:
Toplevel* const m_toplevel;
MMgc::GC* const m_gc;
WeakSubscriberList m_subscribers;
//
// If this is NULL, we are not a copy-on-write ByteArray.
//
// If this is non-NULL, we are copy-on-write, with this pointer being used
// to ensure that our data isn't collected out from under us. (Note that this
// currently requires the object to be GCObject or GCFinalizedObject, not RCObject;
// that's fine for all extant use cases.) (Note that the pointer is written with explicit WB calls.)
//
// Note that (under the covers) we use GC::emptyWeakRef as a sentinel to mean
// "I am copy on write but there is no GCObject controlling my lifespan"; this
// can be the case if (e.g.) the data is compile-time-constant data we are just
// wrapping. Using GC::emptyWeakRef here is, admittedly, an ugly hack, but doing so
// avoids using the lower-three-bits-as-flags approach we're trying to eradicate elsewhere,
// and avoids adding a "bool" field which would expand this struct by an average of 7 bytes
// due to MMgc alignment rules.
// FIXME due to factoring out of the Buffer object alignment may be affected.
//
MMgc::GCObject* m_copyOnWriteOwner;
uint32_t m_position;
FixedHeapRef<Buffer> m_buffer;
bool m_isShareable;
public: // FIXME permissions
bool m_isLinkWrapper;
};
//
// ByteArrayClass
//
class GC_AS3_EXACT(ByteArrayClass, ClassClosure)
{
protected:
ByteArrayClass(VTable *vtable);
public:
GCRef<ByteArrayObject> constructByteArray();
uint32_t get_defaultObjectEncoding() const { return get__defaultObjectEncoding(); }
void set_defaultObjectEncoding(uint32_t version) { set__defaultObjectEncoding(version); }
// ------------------------ DATA SECTION BEGIN
GC_NO_DATA(ByteArrayClass)
DECLARE_SLOTS_ByteArrayClass;
// ------------------------ DATA SECTION END
};
class GC_AS3_EXACT(ByteArrayObject, ScriptObject)
{
friend class ByteArrayClass;
friend class MessageChannelObject; // FIXME Still needed?
protected:
ByteArrayObject(VTable* ivtable, ScriptObject* delegate);
ByteArrayObject(VTable* ivtable, ScriptObject* delegate, const ByteArray& source);
public:
ByteArrayObject(VTable* ivtable, ScriptObject* delegate, ByteArray::Buffer* source);
public:
virtual bool hasAtomProperty(Atom name) const;
virtual void setAtomProperty(Atom name, Atom value);
virtual Atom getAtomProperty(Atom name) const;
virtual bool hasUintProperty(uint32_t i) const;
virtual Atom getUintProperty(uint32_t i) const;
virtual void setUintProperty(uint32_t i, Atom value);
Atom getMultinameProperty(const Multiname* name) const;
void setMultinameProperty(const Multiname* name, Atom value);
bool hasMultinameProperty(const Multiname* name) const;
Atom readObject();
void writeObject(Atom value);
void readBytes(ByteArrayObject *bytes, uint32_t offset, uint32_t length);
void writeBytes(ByteArrayObject *bytes, uint32_t offset, uint32_t length);
String* _toString();
void _compress(String* algorithm);
void _uncompress(String* algorithm);
void writeBoolean(bool value);
void writeByte(int value);
void writeShort(int value);
void writeInt(int value);
void writeUnsignedInt(uint32_t value);
void writeFloat(double value);
#ifdef VMCFG_FLOAT
void writeFloat4(const float4_t& value);
#endif
void writeDouble(double value);
void writeMultiByte(String* value, String* charSet);
void writeUTF(String* value);
void writeUTFBytes(String* value);
bool readBoolean();
int readByte();
int readUnsignedByte();
int readShort();
int readUnsignedShort();
int readInt();
uint32_t readUnsignedInt();
double readFloat();
#ifdef VMCFG_FLOAT
void readFloat4(float4_t*);
#endif
double readDouble();
String* readMultiByte(uint32_t length, String* charSet);
String* readUTF();
String* readUTFBytes(uint32_t length);
uint32_t get_bytesAvailable() { return m_byteArray.Available(); }
uint32_t get_position() { return m_byteArray.GetPosition(); }
void set_position(uint32_t offset) { m_byteArray.SetPosition(offset); }
uint32_t get_length() { return m_byteArray.GetLength(); }
void set_length(uint32_t value) { m_byteArray.SetLengthFromAS3(value); }
ByteArray& GetByteArray() { return m_byteArray; }
uint32_t get_objectEncoding();
void set_objectEncoding(uint32_t version);
Stringp get_endian();
void set_endian(Stringp value);
void clear();
int32_t atomicCompareAndSwapIntAt(int32_t byteIndex , int32_t expectedValue, int32_t newValue );
int32_t atomicCompareAndSwapLength(int32_t expectedLength, int32_t newLength);
bool get_shareable();
void set_shareable(bool val);
public:
ChannelItem* makeChannelItem();
#ifdef DEBUGGER
public:
virtual uint64_t bytesUsed() const;
virtual void registerDependentObjects(IMemorySampler* memSampler);
#endif
private:
// REALLY_INLINE helpers for fast reading and writing, without code duplication.
uint32_t read32();
uint32_t read16();
float readFloatHelper(uint8_t* b); // ...Helper because AOT hates overloaded names
void writeFloatHelper(float value, uint8_t* b); // ditto
void write32(uint32_t value);
ByteArray::CompressionAlgorithm algorithmToEnum(String* algorithm);
// ------------------------ DATA SECTION BEGIN
private:
GC_DATA_BEGIN(ByteArrayObject)
MMgc::Cleaner c;
#ifdef DEBUGGER
bool m_flushBeforeConstructionComplete;
#endif
ByteArray GC_STRUCTURE(m_byteArray);
GC_DATA_END(ByteArrayObject)
DECLARE_SLOTS_ByteArrayObject;
// ------------------------ DATA SECTION END
};
}
#endif /* BYTEARRAYGLUE_INCLUDED */