@awayfl/avm2
Version:
Virtual machine for executing AS3 code
173 lines (172 loc) • 6.77 kB
JavaScript
/*
* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { StringUtilities } from '@awayfl/swf-loader';
var textDecoder = null;
if (typeof TextDecoder !== 'undefined') {
textDecoder = new TextDecoder();
}
var AbcStream = /** @class */ (function () {
function AbcStream(bytes) {
this._bytes = bytes;
this._view = new DataView(bytes.buffer, bytes.byteOffset);
this._position = 0;
}
AbcStream._getResultBuffer = function (length) {
if (!AbcStream._resultBuffer || AbcStream._resultBuffer.length < length) {
AbcStream._resultBuffer = new Uint32Array(length * 2);
}
return AbcStream._resultBuffer;
};
Object.defineProperty(AbcStream.prototype, "position", {
get: function () {
return this._position;
},
enumerable: false,
configurable: true
});
AbcStream.prototype.remaining = function () {
return this._bytes.length - this._position;
};
AbcStream.prototype.seek = function (position) {
this._position = position;
};
AbcStream.prototype.advance = function (length) {
this._position += length;
};
AbcStream.prototype.readU8 = function () {
return this._bytes[this._position++];
};
AbcStream.prototype.readU8s = function (count) {
var b = new Uint8Array(count);
b.set(this._bytes.subarray(this._position, this._position + count), 0);
this._position += count;
return b;
};
AbcStream.prototype.viewU8s = function (count) {
var view = this._bytes.subarray(this._position, this._position + count);
this._position += count;
return view;
};
AbcStream.prototype.readS8 = function () {
return this._bytes[this._position++] << 24 >> 24;
};
AbcStream.prototype.readU32 = function () {
return this.readS32() >>> 0;
};
AbcStream.prototype.readU30 = function () {
var result = this.readU32();
if (result & 0xc0000000) {
// TODO: Spec says this is a corrupt ABC file, but it seems that some content
// has this, e.g. 1000-0.abc
// error("Corrupt ABC File");
return result;
}
return result;
};
AbcStream.prototype.readU30Unsafe = function () {
return this.readU32();
};
AbcStream.prototype.readS16 = function () {
return (this.readU30Unsafe() << 16) >> 16;
};
/**
* Read a variable-length encoded 32-bit signed integer. The value may use one to five bytes (little endian),
* each contributing 7 bits. The most significant bit of each byte indicates that the next byte is part of
* the value. The spec indicates that the most significant bit of the last byte to be read is sign extended
* but this turns out not to be the case in the real implementation, for instance 0x7f should technically be
* -1, but instead it's 127. Moreover, what happens to the remaining 4 high bits of the fifth byte that is
* read? Who knows, here we'll just stay true to the Tamarin implementation.
*/
AbcStream.prototype.readS32 = function () {
var result = this.readU8();
if (result & 0x80) {
result = result & 0x7f | this.readU8() << 7;
if (result & 0x4000) {
result = result & 0x3fff | this.readU8() << 14;
if (result & 0x200000) {
result = result & 0x1fffff | this.readU8() << 21;
if (result & 0x10000000) {
result = result & 0x0fffffff | this.readU8() << 28;
result = result & 0xffffffff;
}
}
}
}
return result;
};
AbcStream.prototype.readWord = function () {
var result = this._view.getUint32(this._position, true);
this._position += 4;
return result;
};
AbcStream.prototype.readS24 = function () {
var u = this.readU8() | (this.readU8() << 8) | (this.readU8() << 16);
return (u << 8) >> 8;
};
AbcStream.prototype.readDouble = function () {
var result = this._view.getFloat64(this._position, true);
this._position += 8;
return result;
};
AbcStream.prototype.readUTFString = function (length) {
/**
* Use the TextDecoder API whenever available.
* http://encoding.spec.whatwg.org/#concept-encoding-get
*/
if (textDecoder) {
var position = this._position;
this._position += length;
return textDecoder.decode(this._bytes.subarray(position, position + length));
}
var pos = this._position;
var end = pos + length;
var bytes = this._bytes;
var i = 0;
var result = AbcStream._getResultBuffer(length * 2);
while (pos < end) {
var c = bytes[pos++];
if (c <= 0x7f) {
result[i++] = c;
}
else if (c >= 0xc0) { // multibyte
var code = 0;
if (c < 0xe0) { // 2 bytes
code = ((c & 0x1f) << 6) | (bytes[pos++] & 0x3f);
}
else if (c < 0xf0) { // 3 bytes
code = ((c & 0x0f) << 12) | ((bytes[pos++] & 0x3f) << 6) | (bytes[pos++] & 0x3f);
}
else { // 4 bytes
// turned into two characters in JS as surrogate pair
code = (((c & 0x07) << 18)
| ((bytes[pos++] & 0x3f) << 12)
| ((bytes[pos++] & 0x3f) << 6)
| (bytes[pos++] & 0x3f)) - 0x10000;
// High surrogate
result[i++] = ((code & 0xffc00) >>> 10) + 0xd800;
// Low surrogate
code = (code & 0x3ff) + 0xdc00;
}
result[i++] = code;
} // Otherwise it's an invalid UTF8, skipped.
}
this._position = pos;
return StringUtilities.fromCharCodeArray(result.subarray(0, i));
};
AbcStream._resultBuffer = new Uint32Array(256);
return AbcStream;
}());
export { AbcStream };