Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
439 lines
17 KiB
C++
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 */
|