mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
commit
12f8cfbb67
18 changed files with 2311 additions and 6 deletions
|
@ -20,7 +20,7 @@ test = function(name, func) {
|
|||
unitTest.run();
|
||||
print(" Success: " + unitTest.numAssertions + " assertions passed");
|
||||
} catch (error) {
|
||||
print(" Failure: " + error.message);
|
||||
print(" Failure: " + error.name + " " + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,12 @@ AssertionException = function(expected, actual, message) {
|
|||
this.name = 'AssertionException';
|
||||
};
|
||||
|
||||
UnthrownException = function(message) {
|
||||
print("Creating exception");
|
||||
this.message = message + "\n";
|
||||
this.name = 'UnthrownException';
|
||||
};
|
||||
|
||||
UnitTest = function(name, func) {
|
||||
this.numAssertions = 0;
|
||||
this.func = func;
|
||||
|
@ -65,4 +71,27 @@ UnitTest.prototype.assertNull = function(value, message) {
|
|||
if (value !== null) {
|
||||
throw new AssertionException(value, null, message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
UnitTest.prototype.arrayEqual = function(array1, array2, message) {
|
||||
this.numAssertions++;
|
||||
if (array1.length !== array2.length) {
|
||||
throw new AssertionException(array1.length , array2.length , message);
|
||||
}
|
||||
for (var i = 0; i < array1.length; ++i) {
|
||||
if (array1[i] !== array2[i]) {
|
||||
throw new AssertionException(array1[i], array2[i], i + " " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnitTest.prototype.raises = function(func, message) {
|
||||
this.numAssertions++;
|
||||
try {
|
||||
func();
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UnthrownException(message);
|
||||
}
|
708
examples/typedArraysUnitTest.js
Normal file
708
examples/typedArraysUnitTest.js
Normal file
|
@ -0,0 +1,708 @@
|
|||
//
|
||||
// typedArraysunitTest.js
|
||||
// examples
|
||||
//
|
||||
// Created by Clément Brisset on 7/7/14
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("Test.js");
|
||||
|
||||
// e.g. extractbits([0xff, 0x80, 0x00, 0x00], 23, 30); inclusive
|
||||
function extractbits(bytes, lo, hi) {
|
||||
var out = 0;
|
||||
bytes = bytes.slice(); // make a copy
|
||||
var lsb = bytes.pop(), sc = 0, sh = 0;
|
||||
|
||||
for (; lo > 0; lo--, hi--) {
|
||||
lsb >>= 1;
|
||||
if (++sc === 8) { sc = 0; lsb = bytes.pop(); }
|
||||
}
|
||||
|
||||
for (; hi >= 0; hi--) {
|
||||
out = out | (lsb & 0x01) << sh++;
|
||||
lsb >>= 1;
|
||||
if (++sc === 8) { sc = 0; lsb = bytes.pop(); }
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
test('ArrayBuffer', function(finished) {
|
||||
this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length');
|
||||
|
||||
this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation');
|
||||
this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation');
|
||||
this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation');
|
||||
|
||||
this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length');
|
||||
|
||||
this.raises(function () { return new ArrayBuffer(-1); }, 'negative length');
|
||||
this.raises(function () { return new ArrayBuffer(0x80000000); }, 'absurd length');
|
||||
});
|
||||
|
||||
test('DataView constructors', function (finished) {
|
||||
var d = new DataView(new ArrayBuffer(8));
|
||||
|
||||
d.setUint32(0, 0x12345678);
|
||||
this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian');
|
||||
|
||||
d.setUint32(0, 0x12345678, true);
|
||||
this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian');
|
||||
|
||||
d.setUint32(0, 0x12345678, true);
|
||||
this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian');
|
||||
|
||||
d.setUint32(0, 0x12345678);
|
||||
this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian');
|
||||
|
||||
this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument');
|
||||
this.raises(function () { return new DataView("bogus"); }, 'non-ArrayBuffer argument');
|
||||
});
|
||||
|
||||
|
||||
test('ArrayBufferView', function () {
|
||||
var ab = new ArrayBuffer(48);
|
||||
var i32 = new Int32Array(ab, 16);
|
||||
i32.set([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
|
||||
this.assertEquals(i32.buffer, ab, 'ArrayBuffers equal');
|
||||
this.assertEquals(i32.byteOffset, 16, 'byteOffset');
|
||||
this.assertEquals(i32.byteLength, 32, 'byteLength');
|
||||
|
||||
var da = new DataView(i32.buffer, 8);
|
||||
this.assertEquals(da.buffer, ab, 'DataView: ArrayBuffers equal');
|
||||
this.assertEquals(da.byteOffset, 8, 'DataView: byteOffset');
|
||||
this.assertEquals(da.byteLength, 40, 'DataView: byteLength');
|
||||
});
|
||||
|
||||
test('TypedArrays', function () {
|
||||
var a;
|
||||
|
||||
this.assertEquals(Int8Array.BYTES_PER_ELEMENT, 1, 'Int8Array.BYTES_PER_ELEMENT');
|
||||
a = new Int8Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'int8Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'int8Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 8, 'int8Array.byteLength');
|
||||
|
||||
this.assertEquals(Uint8Array.BYTES_PER_ELEMENT, 1, 'Uint8Array.BYTES_PER_ELEMENT');
|
||||
a = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'uint8Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'uint8Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 8, 'uint8Array.byteLength');
|
||||
|
||||
this.assertEquals(Int16Array.BYTES_PER_ELEMENT, 2, 'Int16Array.BYTES_PER_ELEMENT');
|
||||
a = new Int16Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'int16Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'int16Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 16, 'int16Array.byteLength');
|
||||
|
||||
this.assertEquals(Uint16Array.BYTES_PER_ELEMENT, 2, 'Uint16Array.BYTES_PER_ELEMENT');
|
||||
a = new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'uint16Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'uint16Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 16, 'uint16Array.byteLength');
|
||||
|
||||
this.assertEquals(Int32Array.BYTES_PER_ELEMENT, 4, 'Int32Array.BYTES_PER_ELEMENT');
|
||||
a = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'int32Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'int32Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 32, 'int32Array.byteLength');
|
||||
|
||||
this.assertEquals(Uint32Array.BYTES_PER_ELEMENT, 4, 'Uint32Array.BYTES_PER_ELEMENT');
|
||||
a = new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'uint32Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'uint32Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 32, 'uint32Array.byteLength');
|
||||
|
||||
this.assertEquals(Float32Array.BYTES_PER_ELEMENT, 4, 'Float32Array.BYTES_PER_ELEMENT');
|
||||
a = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'float32Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'float32Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 32, 'float32Array.byteLength');
|
||||
|
||||
this.assertEquals(Float64Array.BYTES_PER_ELEMENT, 8, 'Float64Array.BYTES_PER_ELEMENT');
|
||||
a = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 8, 'float64Array.BYTES_PER_ELEMENT');
|
||||
this.assertEquals(a.byteOffset, 0, 'float64Array.byteOffset');
|
||||
this.assertEquals(a.byteLength, 64, 'float64Array.byteLength');
|
||||
});
|
||||
|
||||
|
||||
test('typed array constructors', function () {
|
||||
this.arrayEqual(new Int8Array({ length: 3 }), [0, 0, 0], 'array equal -1');
|
||||
var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer;
|
||||
|
||||
var int8 = new Int8Array();
|
||||
this.assertEquals(int8.length, 0, 'no args 0');
|
||||
this.raises(function () { return new Int8Array(-1); }, 'bogus length');
|
||||
this.raises(function () { return new Int8Array(0x80000000); }, 'bogus length');
|
||||
|
||||
int8 = new Int8Array(4);
|
||||
this.assertEquals(int8.BYTES_PER_ELEMENT, 1);
|
||||
this.assertEquals(int8.length, 4, 'length 1');
|
||||
this.assertEquals(int8.byteLength, 4, 'length 2');
|
||||
this.assertEquals(int8.byteOffset, 0, 'length 3');
|
||||
this.assertEquals(int8.get(-1), undefined, 'length, out of bounds 4');
|
||||
this.assertEquals(int8.get(4), undefined, 'length, out of bounds 5');
|
||||
|
||||
int8 = new Int8Array([1, 2, 3, 4, 5, 6]);
|
||||
this.assertEquals(int8.length, 6, 'array 6');
|
||||
this.assertEquals(int8.byteLength, 6, 'array 7');
|
||||
this.assertEquals(int8.byteOffset, 0, 'array 8');
|
||||
this.assertEquals(int8.get(3), 4, 'array 9');
|
||||
this.assertEquals(int8.get(-1), undefined, 'array, out of bounds 10');
|
||||
this.assertEquals(int8.get(6), undefined, 'array, out of bounds 11');
|
||||
|
||||
int8 = new Int8Array(rawbuf);
|
||||
this.assertEquals(int8.length, 8, 'buffer 12');
|
||||
this.assertEquals(int8.byteLength, 8, 'buffer 13');
|
||||
this.assertEquals(int8.byteOffset, 0, 'buffer 14');
|
||||
this.assertEquals(int8.get(7), 7, 'buffer 15');
|
||||
int8.set([111]);
|
||||
this.assertEquals(int8.get(0), 111, 'buffer 16');
|
||||
this.assertEquals(int8.get(-1), undefined, 'buffer, out of bounds 17');
|
||||
this.assertEquals(int8.get(8), undefined, 'buffer, out of bounds 18');
|
||||
|
||||
int8 = new Int8Array(rawbuf, 2);
|
||||
this.assertEquals(int8.length, 6, 'buffer, byteOffset 19');
|
||||
this.assertEquals(int8.byteLength, 6, 'buffer, byteOffset 20');
|
||||
this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset 21');
|
||||
this.assertEquals(int8.get(5), 7, 'buffer, byteOffset 22');
|
||||
int8.set([112]);
|
||||
this.assertEquals(int8.get(0), 112, 'buffer 23');
|
||||
this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, out of bounds 24');
|
||||
this.assertEquals(int8.get(6), undefined, 'buffer, byteOffset, out of bounds 25');
|
||||
|
||||
int8 = new Int8Array(rawbuf, 8);
|
||||
this.assertEquals(int8.length, 0, 'buffer, byteOffset 26');
|
||||
|
||||
this.raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27');
|
||||
this.raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28');
|
||||
this.raises(function () { return new Int32Array(rawbuf, -1); }, 'invalid byteOffset 29');
|
||||
this.raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30');
|
||||
|
||||
int8 = new Int8Array(rawbuf, 2, 4);
|
||||
this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31');
|
||||
this.assertEquals(int8.byteLength, 4, 'buffer, byteOffset, length 32');
|
||||
this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset, length 33');
|
||||
this.assertEquals(int8.get(3), 5, 'buffer, byteOffset, length 34');
|
||||
int8.set([113]);
|
||||
this.assertEquals(int8.get(0), 113, 'buffer, byteOffset, length 35');
|
||||
this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36');
|
||||
this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37');
|
||||
|
||||
this.raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length');
|
||||
this.raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length');
|
||||
this.raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length');
|
||||
});
|
||||
|
||||
test('TypedArray clone constructor', function () {
|
||||
var src = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
var dst = new Int32Array(src);
|
||||
this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '1');
|
||||
src.set([99]);
|
||||
this.arrayEqual(src, [99, 2, 3, 4, 5, 6, 7, 8], '2');
|
||||
this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '3');
|
||||
});
|
||||
|
||||
|
||||
test('conversions', function () {
|
||||
var uint8 = new Uint8Array([1, 2, 3, 4]),
|
||||
uint16 = new Uint16Array(uint8.buffer),
|
||||
uint32 = new Uint32Array(uint8.buffer);
|
||||
|
||||
// Note: can't probe individual bytes without endianness awareness
|
||||
this.arrayEqual(uint8, [1, 2, 3, 4]);
|
||||
uint16.set([0xffff]);
|
||||
this.arrayEqual(uint8, [0xff, 0xff, 3, 4]);
|
||||
uint16.set([0xeeee], 1);
|
||||
this.arrayEqual(uint8, [0xff, 0xff, 0xee, 0xee]);
|
||||
uint32.set([0x11111111]);
|
||||
this.assertEquals(uint16.get(0), 0x1111);
|
||||
this.assertEquals(uint16.get(1), 0x1111);
|
||||
this.arrayEqual(uint8, [0x11, 0x11, 0x11, 0x11]);
|
||||
});
|
||||
|
||||
|
||||
test('signed/unsigned conversions', function () {
|
||||
|
||||
var int8 = new Int8Array(1), uint8 = new Uint8Array(int8.buffer);
|
||||
uint8.set([123]);
|
||||
this.assertEquals(int8.get(0), 123, 'int8/uint8');
|
||||
uint8.set([161]);
|
||||
this.assertEquals(int8.get(0), -95, 'int8/uint8');
|
||||
int8.set([-120]);
|
||||
this.assertEquals(uint8.get(0), 136, 'uint8/int8');
|
||||
int8.set([-1]);
|
||||
this.assertEquals(uint8.get(0), 0xff, 'uint8/int8');
|
||||
|
||||
var int16 = new Int16Array(1), uint16 = new Uint16Array(int16.buffer);
|
||||
uint16.set([3210]);
|
||||
this.assertEquals(int16.get(0), 3210, 'int16/uint16');
|
||||
uint16.set([49232]);
|
||||
this.assertEquals(int16.get(0), -16304, 'int16/uint16');
|
||||
int16.set([-16384]);
|
||||
this.assertEquals(uint16.get(0), 49152, 'uint16/int16');
|
||||
int16.set([-1]);
|
||||
this.assertEquals(uint16.get(0), 0xffff, 'uint16/int16');
|
||||
|
||||
var int32 = new Int32Array(1), uint32 = new Uint32Array(int32.buffer);
|
||||
uint32.set([0x80706050]);
|
||||
this.assertEquals(int32.get(0), -2140118960, 'int32/uint32');
|
||||
int32.set([-2023406815]);
|
||||
this.assertEquals(uint32.get(0), 0x87654321, 'uint32/int32');
|
||||
int32.set([-1]);
|
||||
this.assertEquals(uint32.get(0), 0xffffffff, 'uint32/int32');
|
||||
});
|
||||
|
||||
|
||||
test('IEEE754 single precision unpacking', function () {
|
||||
function fromBytes(bytes) {
|
||||
var uint8 = new Uint8Array(bytes),
|
||||
dv = new DataView(uint8.buffer);
|
||||
return dv.getFloat32(0);
|
||||
}
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x01])), true, 'Q-NaN');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x00])), true, 'Indeterminate');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xbf, 0xff, 0xff])), true, 'S-NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0x80, 0x00, 0x01])), true, 'S-NaN');
|
||||
|
||||
this.assertEquals(fromBytes([0xff, 0x80, 0x00, 0x00]), -Infinity, '-Infinity');
|
||||
|
||||
this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized');
|
||||
this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized');
|
||||
this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized');
|
||||
this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized');
|
||||
|
||||
// TODO: Denormalized values fail on Safari on iOS/ARM
|
||||
this.assertEquals(fromBytes([0x80, 0x7f, 0xff, 0xff]), -1.1754942106924411E-38, '-Denormalized');
|
||||
this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x01]), -1.4012984643248170E-45, '-Denormalized');
|
||||
|
||||
this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00]), -0, '-0');
|
||||
this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00]), +0, '+0');
|
||||
|
||||
// TODO: Denormalized values fail on Safari on iOS/ARM
|
||||
this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x01]), 1.4012984643248170E-45, '+Denormalized');
|
||||
this.assertEquals(fromBytes([0x00, 0x7f, 0xff, 0xff]), 1.1754942106924411E-38, '+Denormalized');
|
||||
|
||||
this.assertEquals(fromBytes([0x00, 0x80, 0x00, 0x00]), 1.1754943508222875E-38, '+Normalized');
|
||||
this.assertEquals(fromBytes([0x7f, 0x7f, 0xff, 0xff]), 3.4028234663852886E+38, '+Normalized');
|
||||
|
||||
this.assertEquals(fromBytes([0x7f, 0x80, 0x00, 0x00]), +Infinity, '+Infinity');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0x80, 0x00, 0x01])), true, 'S+NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xbf, 0xff, 0xff])), true, 'S+NaN');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xc0, 0x00, 0x00])), true, 'Q+NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff])), true, 'Q+NaN');
|
||||
});
|
||||
|
||||
test('IEEE754 single precision packing', function () {
|
||||
|
||||
function toBytes(v) {
|
||||
var uint8 = new Uint8Array(4), dv = new DataView(uint8.buffer);
|
||||
dv.setFloat32(0, v);
|
||||
var bytes = [];
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
bytes.push(uint8.get(i));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
this.arrayEqual(toBytes(-Infinity), [0xff, 0x80, 0x00, 0x00], '-Infinity');
|
||||
|
||||
this.arrayEqual(toBytes(-3.4028235677973366e+38), [0xff, 0x80, 0x00, 0x00], '-Overflow');
|
||||
this.arrayEqual(toBytes(-3.402824E+38), [0xff, 0x80, 0x00, 0x00], '-Overflow');
|
||||
|
||||
this.arrayEqual(toBytes(-3.4028234663852886E+38), [0xff, 0x7f, 0xff, 0xff], '-Normalized');
|
||||
this.arrayEqual(toBytes(-1.1754943508222875E-38), [0x80, 0x80, 0x00, 0x00], '-Normalized');
|
||||
|
||||
// TODO: Denormalized values fail on Safari iOS/ARM
|
||||
this.arrayEqual(toBytes(-1.1754942106924411E-38), [0x80, 0x7f, 0xff, 0xff], '-Denormalized');
|
||||
this.arrayEqual(toBytes(-1.4012984643248170E-45), [0x80, 0x00, 0x00, 0x01], '-Denormalized');
|
||||
|
||||
this.arrayEqual(toBytes(-7.006492321624085e-46), [0x80, 0x00, 0x00, 0x00], '-Underflow');
|
||||
|
||||
this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00], '-0');
|
||||
this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00], '+0');
|
||||
|
||||
this.arrayEqual(toBytes(7.006492321624085e-46), [0x00, 0x00, 0x00, 0x00], '+Underflow');
|
||||
|
||||
// TODO: Denormalized values fail on Safari iOS/ARM
|
||||
this.arrayEqual(toBytes(1.4012984643248170E-45), [0x00, 0x00, 0x00, 0x01], '+Denormalized');
|
||||
this.arrayEqual(toBytes(1.1754942106924411E-38), [0x00, 0x7f, 0xff, 0xff], '+Denormalized');
|
||||
|
||||
this.arrayEqual(toBytes(1.1754943508222875E-38), [0x00, 0x80, 0x00, 0x00], '+Normalized');
|
||||
this.arrayEqual(toBytes(3.4028234663852886E+38), [0x7f, 0x7f, 0xff, 0xff], '+Normalized');
|
||||
|
||||
this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow');
|
||||
this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow');
|
||||
this.arrayEqual(toBytes(+Infinity), [0x7f, 0x80, 0x00, 0x00], '+Infinity');
|
||||
|
||||
// Allow any NaN pattern (exponent all 1's, fraction non-zero)
|
||||
var nanbytes = toBytes(NaN),
|
||||
sign = extractbits(nanbytes, 31, 31),
|
||||
exponent = extractbits(nanbytes, 23, 30),
|
||||
fraction = extractbits(nanbytes, 0, 22);
|
||||
this.assertEquals(exponent === 255 && fraction !== 0, true, 'NaN');
|
||||
});
|
||||
|
||||
|
||||
test('IEEE754 double precision unpacking', function () {
|
||||
|
||||
function fromBytes(bytes) {
|
||||
var uint8 = new Uint8Array(bytes),
|
||||
dv = new DataView(uint8.buffer);
|
||||
return dv.getFloat64(0);
|
||||
}
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'Q-NaN');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Indeterminate');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S-NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S-NaN');
|
||||
|
||||
this.assertEquals(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -Infinity, '-Infinity');
|
||||
|
||||
this.assertEquals(fromBytes([0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -1.7976931348623157E+308, '-Normalized');
|
||||
this.assertEquals(fromBytes([0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -2.2250738585072014E-308, '-Normalized');
|
||||
|
||||
// TODO: Denormalized values fail on Safari iOS/ARM
|
||||
this.assertEquals(fromBytes([0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -2.2250738585072010E-308, '-Denormalized');
|
||||
this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), -4.9406564584124654E-324, '-Denormalized');
|
||||
|
||||
this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -0, '-0');
|
||||
this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +0, '+0');
|
||||
|
||||
// TODO: Denormalized values fail on Safari iOS/ARM
|
||||
this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), 4.9406564584124654E-324, '+Denormalized');
|
||||
this.assertEquals(fromBytes([0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 2.2250738585072010E-308, '+Denormalized');
|
||||
|
||||
this.assertEquals(fromBytes([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), 2.2250738585072014E-308, '+Normalized');
|
||||
this.assertEquals(fromBytes([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 1.7976931348623157E+308, '+Normalized');
|
||||
|
||||
this.assertEquals(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +Infinity, '+Infinity');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S+NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S+NaN');
|
||||
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Q+NaN');
|
||||
this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q+NaN');
|
||||
});
|
||||
|
||||
|
||||
test('IEEE754 double precision packing', function () {
|
||||
|
||||
function toBytes(v) {
|
||||
var uint8 = new Uint8Array(8),
|
||||
dv = new DataView(uint8.buffer);
|
||||
dv.setFloat64(0, v);
|
||||
var bytes = [];
|
||||
for (var i = 0; i < 8; i += 1) {
|
||||
bytes.push(uint8.get(i));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
this.arrayEqual(toBytes(-Infinity), [0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Infinity');
|
||||
|
||||
this.arrayEqual(toBytes(-1.7976931348623157E+308), [0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Normalized');
|
||||
this.arrayEqual(toBytes(-2.2250738585072014E-308), [0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Normalized');
|
||||
|
||||
// TODO: Denormalized values fail on Safari iOS/ARM
|
||||
this.arrayEqual(toBytes(-2.2250738585072010E-308), [0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Denormalized');
|
||||
this.arrayEqual(toBytes(-4.9406564584124654E-324), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '-Denormalized');
|
||||
|
||||
this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-0');
|
||||
this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+0');
|
||||
|
||||
// TODO: Denormalized values fail on Safari iOS/ARM
|
||||
this.arrayEqual(toBytes(4.9406564584124654E-324), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '+Denormalized');
|
||||
this.arrayEqual(toBytes(2.2250738585072010E-308), [0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Denormalized');
|
||||
|
||||
this.arrayEqual(toBytes(2.2250738585072014E-308), [0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Normalized');
|
||||
this.arrayEqual(toBytes(1.7976931348623157E+308), [0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Normalized');
|
||||
|
||||
this.arrayEqual(toBytes(+Infinity), [0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Infinity');
|
||||
|
||||
// Allow any NaN pattern (exponent all 1's, fraction non-zero)
|
||||
var nanbytes = toBytes(NaN),
|
||||
sign = extractbits(nanbytes, 63, 63),
|
||||
exponent = extractbits(nanbytes, 52, 62),
|
||||
fraction = extractbits(nanbytes, 0, 51);
|
||||
this.assertEquals(exponent === 2047 && fraction !== 0, true, 'NaN');
|
||||
});
|
||||
|
||||
test('Int32Array round trips', function () {
|
||||
var i32 = new Int32Array([0]);
|
||||
var data = [
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
123,
|
||||
-456,
|
||||
0x80000000 >> 0,
|
||||
0x7fffffff >> 0,
|
||||
0x12345678 >> 0,
|
||||
0x87654321 >> 0
|
||||
];
|
||||
|
||||
for (var i = 0; i < data.length; i += 1) {
|
||||
var datum = data[i];
|
||||
i32.set([datum]);
|
||||
this.assertEquals(datum, i32.get(0), String(datum));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('Int16Array round trips', function () {
|
||||
var i16 = new Int16Array([0]);
|
||||
var data = [
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
123,
|
||||
-456,
|
||||
0xffff8000 >> 0,
|
||||
0x00007fff >> 0,
|
||||
0x00001234 >> 0,
|
||||
0xffff8765 >> 0
|
||||
];
|
||||
|
||||
for (var i = 0; i < data.length; i += 1) {
|
||||
var datum = data[i];
|
||||
i16.set([datum]);
|
||||
this.assertEquals(datum, i16.get(0), String(datum));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('Int8Array round trips', function () {
|
||||
var i8 = new Int8Array([0]);
|
||||
var data = [
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
123,
|
||||
-45,
|
||||
0xffffff80 >> 0,
|
||||
0x0000007f >> 0,
|
||||
0x00000012 >> 0,
|
||||
0xffffff87 >> 0
|
||||
];
|
||||
|
||||
for (var i = 0; i < data.length; i += 1) {
|
||||
var datum = data[i];
|
||||
i8.set([datum]);
|
||||
this.assertEquals(datum, i8.get(0), String(datum));
|
||||
}
|
||||
});
|
||||
|
||||
test('TypedArray setting', function () {
|
||||
|
||||
var a = new Int32Array([1, 2, 3, 4, 5]);
|
||||
var b = new Int32Array(5);
|
||||
b.set(a);
|
||||
this.arrayEqual(b, [1, 2, 3, 4, 5], '1');
|
||||
this.raises(function () { b.set(a, 1); });
|
||||
|
||||
b.set(new Int32Array([99, 98]), 2);
|
||||
this.arrayEqual(b, [1, 2, 99, 98, 5], '2');
|
||||
|
||||
b.set(new Int32Array([99, 98, 97]), 2);
|
||||
this.arrayEqual(b, [1, 2, 99, 98, 97], '3');
|
||||
|
||||
this.raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); });
|
||||
this.raises(function () { b.set([101, 102, 103, 104], 4); });
|
||||
|
||||
// ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ]
|
||||
// a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ]
|
||||
// a2 = [ ^, ^, ^, ^ ]
|
||||
var ab = new ArrayBuffer(8);
|
||||
var a1 = new Uint8Array(ab);
|
||||
for (var i = 0; i < a1.length; i += 1) { a1.set([i], i); }
|
||||
var a2 = new Uint8Array(ab, 4);
|
||||
a1.set(a2, 2);
|
||||
this.arrayEqual(a1, [0, 1, 4, 5, 6, 7, 6, 7]);
|
||||
this.arrayEqual(a2, [6, 7, 6, 7]);
|
||||
});
|
||||
|
||||
|
||||
test('TypedArray.subarray', function () {
|
||||
|
||||
var a = new Int32Array([1, 2, 3, 4, 5]);
|
||||
this.arrayEqual(a.subarray(3), [4, 5]);
|
||||
this.arrayEqual(a.subarray(1, 3), [2, 3]);
|
||||
this.arrayEqual(a.subarray(-3), [3, 4, 5]);
|
||||
this.arrayEqual(a.subarray(-3, -1), [3, 4]);
|
||||
this.arrayEqual(a.subarray(3, 2), []);
|
||||
this.arrayEqual(a.subarray(-2, -3), []);
|
||||
this.arrayEqual(a.subarray(4, 1), []);
|
||||
this.arrayEqual(a.subarray(-1, -4), []);
|
||||
this.arrayEqual(a.subarray(1).subarray(1), [3, 4, 5]);
|
||||
this.arrayEqual(a.subarray(1, 4).subarray(1, 2), [3]);
|
||||
});
|
||||
|
||||
|
||||
test('DataView constructors', function () {
|
||||
|
||||
var d = new DataView(new ArrayBuffer(8));
|
||||
|
||||
d.setUint32(0, 0x12345678);
|
||||
this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian');
|
||||
|
||||
d.setUint32(0, 0x12345678, true);
|
||||
this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian');
|
||||
|
||||
d.setUint32(0, 0x12345678, true);
|
||||
this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian');
|
||||
|
||||
d.setUint32(0, 0x12345678);
|
||||
this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian');
|
||||
|
||||
// Chrome allows no arguments, throws if non-ArrayBuffer
|
||||
//stricterEqual(new DataView().buffer.byteLength, 0, 'no arguments');
|
||||
|
||||
// Safari (iOS 5) does not
|
||||
//raises(function () { return new DataView(); }, TypeError, 'no arguments');
|
||||
|
||||
// Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR)
|
||||
this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument');
|
||||
|
||||
this.raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument');
|
||||
});
|
||||
|
||||
|
||||
|
||||
test('DataView accessors', function () {
|
||||
var u = new Uint8Array(8), d = new DataView(u.buffer);
|
||||
this.arrayEqual(u, [0, 0, 0, 0, 0, 0, 0, 0], '1');
|
||||
|
||||
d.setUint8(0, 255);
|
||||
this.arrayEqual(u, [0xff, 0, 0, 0, 0, 0, 0, 0], '2');
|
||||
|
||||
d.setInt8(1, -1);
|
||||
this.arrayEqual(u, [0xff, 0xff, 0, 0, 0, 0, 0, 0], '3');
|
||||
|
||||
d.setUint16(2, 0x1234);
|
||||
this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0, 0, 0, 0]), '4';
|
||||
|
||||
d.setInt16(4, -1);
|
||||
this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0xff, 0xff, 0, 0], '5');
|
||||
|
||||
d.setUint32(1, 0x12345678);
|
||||
this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x78, 0xff, 0, 0], '6');
|
||||
|
||||
d.setInt32(4, -2023406815);
|
||||
this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x87, 0x65, 0x43, 0x21], '7');
|
||||
|
||||
d.setFloat32(2, 1.2E+38);
|
||||
this.arrayEqual(u, [0xff, 0x12, 0x7e, 0xb4, 0x8e, 0x52, 0x43, 0x21], '8');
|
||||
|
||||
d.setFloat64(0, -1.2345678E+301);
|
||||
this.arrayEqual(u, [0xfe, 0x72, 0x6f, 0x51, 0x5f, 0x61, 0x77, 0xe5], '9');
|
||||
|
||||
u.set([0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87]);
|
||||
this.assertEquals(d.getUint8(0), 128, '10');
|
||||
this.assertEquals(d.getInt8(1), -127, '11');
|
||||
this.assertEquals(d.getUint16(2), 33411, '12');
|
||||
this.assertEquals(d.getInt16(3), -31868, '13');
|
||||
this.assertEquals(d.getUint32(4), 2223343239, '14');
|
||||
this.assertEquals(d.getInt32(2), -2105310075, '15');
|
||||
this.assertEquals(d.getFloat32(2), -1.932478247535851e-37, '16');
|
||||
this.assertEquals(d.getFloat64(0), -3.116851295377095e-306, '17');
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('DataView endian', function () {
|
||||
var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer;
|
||||
var d;
|
||||
|
||||
d = new DataView(rawbuf);
|
||||
this.assertEquals(d.byteLength, 8, 'buffer');
|
||||
this.assertEquals(d.byteOffset, 0, 'buffer');
|
||||
this.raises(function () { d.getUint8(-2); }); // Chrome bug for index -, DOMException, 'bounds for buffer'?
|
||||
this.raises(function () { d.getUint8(8); }, 'bounds for buffer');
|
||||
this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer');
|
||||
this.raises(function () { d.setUint8(8, 0); }, 'bounds for buffer');
|
||||
|
||||
d = new DataView(rawbuf, 2);
|
||||
this.assertEquals(d.byteLength, 6, 'buffer, byteOffset');
|
||||
this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset');
|
||||
this.assertEquals(d.getUint8(5), 7, 'buffer, byteOffset');
|
||||
this.raises(function () { d.getUint8(-2); }, 'bounds for buffer, byteOffset');
|
||||
this.raises(function () { d.getUint8(6); }, 'bounds for buffer, byteOffset');
|
||||
this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset');
|
||||
this.raises(function () { d.setUint8(6, 0); }, 'bounds for buffer, byteOffset');
|
||||
|
||||
d = new DataView(rawbuf, 8);
|
||||
this.assertEquals(d.byteLength, 0, 'buffer, byteOffset');
|
||||
|
||||
this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset');
|
||||
this.raises(function () { return new DataView(rawbuf, 9); }, 'invalid byteOffset');
|
||||
this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset');
|
||||
|
||||
d = new DataView(rawbuf, 2, 4);
|
||||
this.assertEquals(d.byteLength, 4, 'buffer, byteOffset, length');
|
||||
this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset, length');
|
||||
this.assertEquals(d.getUint8(3), 5, 'buffer, byteOffset, length');
|
||||
this.raises(function () { return d.getUint8(-2); }, 'bounds for buffer, byteOffset, length');
|
||||
this.raises(function () { d.getUint8(4); }, 'bounds for buffer, byteOffset, length');
|
||||
this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset, length');
|
||||
this.raises(function () { d.setUint8(4, 0); }, 'bounds for buffer, byteOffset, length');
|
||||
|
||||
this.raises(function () { return new DataView(rawbuf, 0, 9); }, 'invalid byteOffset+length');
|
||||
this.raises(function () { return new DataView(rawbuf, 8, 1); }, 'invalid byteOffset+length');
|
||||
this.raises(function () { return new DataView(rawbuf, 9, -1); }, 'invalid byteOffset+length');
|
||||
});
|
||||
|
||||
|
||||
test('Typed Array getters/setters', function () {
|
||||
// Only supported if Object.defineProperty() is fully supported on non-DOM objects.
|
||||
try {
|
||||
var o = {};
|
||||
Object.defineProperty(o, 'x', { get: function() { return 1; } });
|
||||
if (o.x !== 1) throw Error();
|
||||
} catch (_) {
|
||||
ok(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var bytes = new Uint8Array([1, 2, 3, 4]),
|
||||
uint32s = new Uint32Array(bytes.buffer);
|
||||
|
||||
this.assertEquals(bytes[1], 2);
|
||||
uint32s[0] = 0xffffffff;
|
||||
this.assertEquals(bytes[1], 0xff);
|
||||
});
|
||||
|
||||
|
||||
test('Uint8ClampedArray', function () {
|
||||
this.assertEquals(Uint8ClampedArray.BYTES_PER_ELEMENT, 1, 'Uint8ClampedArray.BYTES_PER_ELEMENT');
|
||||
var a = new Uint8ClampedArray([-Infinity, -Number.MAX_VALUE, -1, -Number.MIN_VALUE, -0,
|
||||
0, Number.MIN_VALUE, 1, 1.1, 1.9, 255, 255.1, 255.9, 256, Number.MAX_VALUE, Infinity,
|
||||
NaN]);
|
||||
this.assertEquals(a.BYTES_PER_ELEMENT, 1);
|
||||
this.assertEquals(a.byteOffset, 0);
|
||||
this.assertEquals(a.byteLength, 17);
|
||||
this.arrayEqual(a, [0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0], "array test");
|
||||
});
|
||||
|
||||
test('Regression Tests', function() {
|
||||
// Bug: https://github.com/inexorabletash/polyfill/issues/16
|
||||
var minFloat32 = 1.401298464324817e-45;
|
||||
var truncated = new Float32Array([-minFloat32 / 2 - Math.pow(2, -202)]).get(0);
|
||||
this.assertEquals(truncated, -minFloat32, 'smallest 32 bit float should not truncate to zero');
|
||||
});
|
||||
|
155
libraries/script-engine/src/ArrayBufferClass.cpp
Normal file
155
libraries/script-engine/src/ArrayBufferClass.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// ArrayBufferClass.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/3/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "ArrayBufferPrototype.h"
|
||||
#include "DataViewClass.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "TypedArrays.h"
|
||||
|
||||
#include "ArrayBufferClass.h"
|
||||
|
||||
static const QString CLASS_NAME = "ArrayBuffer";
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) :
|
||||
QObject(scriptEngine->getEngine()),
|
||||
QScriptClass(scriptEngine->getEngine()),
|
||||
_scriptEngine(scriptEngine) {
|
||||
qScriptRegisterMetaType<QByteArray>(engine(), toScriptValue, fromScriptValue);
|
||||
QScriptValue global = engine()->globalObject();
|
||||
|
||||
// Save string handles for quick lookup
|
||||
_name = engine()->toStringHandle(CLASS_NAME.toLatin1());
|
||||
_byteLength = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1());
|
||||
|
||||
// build prototype
|
||||
_proto = engine()->newQObject(new ArrayBufferPrototype(this),
|
||||
QScriptEngine::QtOwnership,
|
||||
QScriptEngine::SkipMethodsInEnumeration |
|
||||
QScriptEngine::ExcludeSuperClassMethods |
|
||||
QScriptEngine::ExcludeSuperClassProperties);
|
||||
_proto.setPrototype(global.property("Object").property("prototype"));
|
||||
|
||||
// Register constructor
|
||||
_ctor = engine()->newFunction(construct, _proto);
|
||||
_ctor.setData(engine()->toScriptValue(this));
|
||||
|
||||
engine()->globalObject().setProperty(name(), _ctor);
|
||||
|
||||
// Registering other array types
|
||||
// The script engine is there parent so it'll delete them with itself
|
||||
new DataViewClass(scriptEngine);
|
||||
new Int8ArrayClass(scriptEngine);
|
||||
new Uint8ArrayClass(scriptEngine);
|
||||
new Uint8ClampedArrayClass(scriptEngine);
|
||||
new Int16ArrayClass(scriptEngine);
|
||||
new Uint16ArrayClass(scriptEngine);
|
||||
new Int32ArrayClass(scriptEngine);
|
||||
new Uint32ArrayClass(scriptEngine);
|
||||
new Float32ArrayClass(scriptEngine);
|
||||
new Float64ArrayClass(scriptEngine);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::newInstance(qint32 size) {
|
||||
const qint32 MAX_LENGTH = 100000000;
|
||||
if (size < 0) {
|
||||
engine()->evaluate("throw \"ArgumentError: negative length\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (size > MAX_LENGTH) {
|
||||
engine()->evaluate("throw \"ArgumentError: absurd length\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
engine()->reportAdditionalMemoryCost(size);
|
||||
QScriptEngine* eng = engine();
|
||||
QVariant variant = QVariant::fromValue(QByteArray(size, 0));
|
||||
QScriptValue data = eng->newVariant(variant);
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) {
|
||||
QScriptValue data = engine()->newVariant(QVariant::fromValue(ba));
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
ArrayBufferClass* cls = qscriptvalue_cast<ArrayBufferClass*>(context->callee().data());
|
||||
if (!cls) {
|
||||
// return if callee (function called) is not of type ArrayBuffer
|
||||
return QScriptValue();
|
||||
}
|
||||
QScriptValue arg = context->argument(0);
|
||||
if (!arg.isValid() || !arg.isNumber()) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
quint32 size = arg.toInt32();
|
||||
QScriptValue newObject = cls->newInstance(size);
|
||||
|
||||
if (context->isCalledAsConstructor()) {
|
||||
// if called with keyword new, replace this object.
|
||||
context->setThisObject(newObject);
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data());
|
||||
if (ba && name == _byteLength) {
|
||||
// if the property queried is byteLength, only handle read access
|
||||
return flags &= HandlesReadAccess;
|
||||
}
|
||||
return 0; // No access
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data());
|
||||
if (ba && name == _byteLength) {
|
||||
return ba->length();
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
}
|
||||
|
||||
QString ArrayBufferClass::name() const {
|
||||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::prototype() const {
|
||||
return _proto;
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) {
|
||||
QScriptValue ctor = engine->globalObject().property(CLASS_NAME);
|
||||
ArrayBufferClass* cls = qscriptvalue_cast<ArrayBufferClass*>(ctor.data());
|
||||
if (!cls) {
|
||||
return engine->newVariant(QVariant::fromValue(ba));
|
||||
}
|
||||
return cls->newInstance(ba);
|
||||
}
|
||||
|
||||
void ArrayBufferClass::fromScriptValue(const QScriptValue& obj, QByteArray& ba) {
|
||||
ba = qvariant_cast<QByteArray>(obj.data().toVariant());
|
||||
}
|
||||
|
61
libraries/script-engine/src/ArrayBufferClass.h
Normal file
61
libraries/script-engine/src/ArrayBufferClass.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// ArrayBufferClass.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/3/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ArrayBufferClass_h
|
||||
#define hifi_ArrayBufferClass_h
|
||||
|
||||
#include <QScriptClass>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptString>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
class ScriptEngine;
|
||||
|
||||
class ArrayBufferClass : public QObject, public QScriptClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferClass(ScriptEngine* scriptEngine);
|
||||
QScriptValue newInstance(qint32 size);
|
||||
QScriptValue newInstance(const QByteArray& ba);
|
||||
|
||||
QueryFlags queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id);
|
||||
QScriptValue property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id);
|
||||
QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id);
|
||||
|
||||
QString name() const;
|
||||
QScriptValue prototype() const;
|
||||
|
||||
ScriptEngine* getEngine() { return _scriptEngine; }
|
||||
|
||||
private:
|
||||
static QScriptValue construct(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba);
|
||||
static void fromScriptValue(const QScriptValue& obj, QByteArray& ba);
|
||||
|
||||
QScriptValue _proto;
|
||||
QScriptValue _ctor;
|
||||
|
||||
// JS Object attributes
|
||||
QScriptString _name;
|
||||
QScriptString _byteLength;
|
||||
|
||||
ScriptEngine* _scriptEngine;
|
||||
};
|
||||
|
||||
#endif // hifi_ArrayBufferClass_h
|
48
libraries/script-engine/src/ArrayBufferPrototype.cpp
Normal file
48
libraries/script-engine/src/ArrayBufferPrototype.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ArrayBufferPrototype.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/3/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "ArrayBufferClass.h"
|
||||
#include "ArrayBufferPrototype.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QByteArray ArrayBufferPrototype::slice(qint32 begin, qint32 end) const {
|
||||
QByteArray* ba = thisArrayBuffer();
|
||||
// if indices < 0 then they start from the end of the array
|
||||
begin = (begin < 0) ? ba->size() + begin : begin;
|
||||
end = (end < 0) ? ba->size() + end : end;
|
||||
|
||||
// here we clamp the indices to fit the array
|
||||
begin = glm::clamp(begin, 0, (ba->size() - 1));
|
||||
end = glm::clamp(end, 0, (ba->size() - 1));
|
||||
|
||||
return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray();
|
||||
}
|
||||
|
||||
QByteArray ArrayBufferPrototype::slice(qint32 begin) const {
|
||||
QByteArray* ba = thisArrayBuffer();
|
||||
// if indices < 0 then they start from the end of the array
|
||||
begin = (begin < 0) ? ba->size() + begin : begin;
|
||||
|
||||
// here we clamp the indices to fit the array
|
||||
begin = glm::clamp(begin, 0, (ba->size() - 1));
|
||||
|
||||
return ba->mid(begin, -1);
|
||||
}
|
||||
|
||||
QByteArray* ArrayBufferPrototype::thisArrayBuffer() const {
|
||||
return qscriptvalue_cast<QByteArray*>(thisObject().data());
|
||||
}
|
31
libraries/script-engine/src/ArrayBufferPrototype.h
Normal file
31
libraries/script-engine/src/ArrayBufferPrototype.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// ArrayBufferPrototype.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/3/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ArrayBufferPrototype_h
|
||||
#define hifi_ArrayBufferPrototype_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
class ArrayBufferPrototype : public QObject, public QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferPrototype(QObject* parent = NULL);
|
||||
|
||||
public slots:
|
||||
QByteArray slice(qint32 begin, qint32 end) const;
|
||||
QByteArray slice(qint32 begin) const;
|
||||
|
||||
private:
|
||||
QByteArray* thisArrayBuffer() const;
|
||||
};
|
||||
|
||||
#endif // hifi_ArrayBufferPrototype_h
|
52
libraries/script-engine/src/ArrayBufferViewClass.cpp
Normal file
52
libraries/script-engine/src/ArrayBufferViewClass.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ArrayBufferViewClass.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) :
|
||||
QObject(scriptEngine->getEngine()),
|
||||
QScriptClass(scriptEngine->getEngine()),
|
||||
_scriptEngine(scriptEngine) {
|
||||
// Save string handles for quick lookup
|
||||
_bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1());
|
||||
_byteOffsetName = engine()->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1());
|
||||
_byteLengthName = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1());
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) {
|
||||
if (name == _bufferName || name == _byteOffsetName || name == _byteLengthName) {
|
||||
return flags &= HandlesReadAccess; // Only keep read access flags
|
||||
}
|
||||
return 0; // No access
|
||||
}
|
||||
|
||||
QScriptValue ArrayBufferViewClass::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
if (name == _bufferName) {
|
||||
return object.data().property(_bufferName);
|
||||
}
|
||||
if (name == _byteOffsetName) {
|
||||
return object.data().property(_byteOffsetName);
|
||||
}
|
||||
if (name == _byteLengthName) {
|
||||
return object.data().property(_byteLengthName);
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
}
|
52
libraries/script-engine/src/ArrayBufferViewClass.h
Normal file
52
libraries/script-engine/src/ArrayBufferViewClass.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ArrayBufferViewClass.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ArrayBufferViewClass_h
|
||||
#define hifi_ArrayBufferViewClass_h
|
||||
|
||||
#include <QScriptClass>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptClass>
|
||||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptString>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
static const QString BUFFER_PROPERTY_NAME = "buffer";
|
||||
static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset";
|
||||
static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength";
|
||||
|
||||
class ArrayBufferViewClass : public QObject, public QScriptClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ArrayBufferViewClass(ScriptEngine* scriptEngine);
|
||||
|
||||
ScriptEngine* getScriptEngine() { return _scriptEngine; }
|
||||
|
||||
virtual QueryFlags queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id);
|
||||
virtual QScriptValue property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id);
|
||||
virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id);
|
||||
protected:
|
||||
// JS Object attributes
|
||||
QScriptString _bufferName;
|
||||
QScriptString _byteOffsetName;
|
||||
QScriptString _byteLengthName;
|
||||
|
||||
ScriptEngine* _scriptEngine;
|
||||
};
|
||||
|
||||
#endif // hifi_ArrayBufferViewClass_h
|
94
libraries/script-engine/src/DataViewClass.cpp
Normal file
94
libraries/script-engine/src/DataViewClass.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// DataViewClass.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DataViewPrototype.h"
|
||||
|
||||
#include "DataViewClass.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
static const QString DATA_VIEW_NAME = "DataView";
|
||||
|
||||
DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass(scriptEngine) {
|
||||
QScriptValue global = engine()->globalObject();
|
||||
|
||||
// Save string handles for quick lookup
|
||||
_name = engine()->toStringHandle(DATA_VIEW_NAME.toLatin1());
|
||||
|
||||
// build prototype
|
||||
_proto = engine()->newQObject(new DataViewPrototype(this),
|
||||
QScriptEngine::QtOwnership,
|
||||
QScriptEngine::SkipMethodsInEnumeration |
|
||||
QScriptEngine::ExcludeSuperClassMethods |
|
||||
QScriptEngine::ExcludeSuperClassProperties);
|
||||
_proto.setPrototype(global.property("Object").property("prototype"));
|
||||
|
||||
// Register constructor
|
||||
_ctor = engine()->newFunction(construct, _proto);
|
||||
_ctor.setData(engine()->toScriptValue(this));
|
||||
engine()->globalObject().setProperty(name(), _ctor);
|
||||
}
|
||||
|
||||
QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) {
|
||||
QScriptValue data = engine()->newObject();
|
||||
data.setProperty(_bufferName, buffer);
|
||||
data.setProperty(_byteOffsetName, byteOffset);
|
||||
data.setProperty(_byteLengthName, byteLentgh);
|
||||
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue DataViewClass::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
DataViewClass* cls = qscriptvalue_cast<DataViewClass*>(context->callee().data());
|
||||
if (!cls || context->argumentCount() < 1) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue bufferArg = context->argument(0);
|
||||
QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue();
|
||||
QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue();
|
||||
|
||||
QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast<QByteArray*>(bufferArg.data()) :NULL;
|
||||
if (!arrayBuffer) {
|
||||
engine->evaluate("throw \"TypeError: 1st argument not a ArrayBuffer\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (byteOffsetArg.isNumber() &&
|
||||
(byteOffsetArg.toInt32() < 0 ||
|
||||
byteOffsetArg.toInt32() > arrayBuffer->size())) {
|
||||
engine->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (byteLengthArg.isNumber() &&
|
||||
(byteLengthArg.toInt32() < 0 ||
|
||||
byteOffsetArg.toInt32() + byteLengthArg.toInt32() > arrayBuffer->size())) {
|
||||
engine->evaluate("throw \"RangeError: byteLength out of range\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
quint32 byteOffset = (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() : 0;
|
||||
quint32 byteLength = (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() : arrayBuffer->size() - byteOffset;
|
||||
QScriptValue newObject = cls->newInstance(bufferArg, byteOffset, byteLength);
|
||||
|
||||
if (context->isCalledAsConstructor()) {
|
||||
context->setThisObject(newObject);
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
QString DataViewClass::name() const {
|
||||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue DataViewClass::prototype() const {
|
||||
return _proto;
|
||||
}
|
36
libraries/script-engine/src/DataViewClass.h
Normal file
36
libraries/script-engine/src/DataViewClass.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// DataViewClass.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DataViewClass_h
|
||||
#define hifi_DataViewClass_h
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
class DataViewClass : public ArrayBufferViewClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataViewClass(ScriptEngine* scriptEngine);
|
||||
QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength);
|
||||
|
||||
QString name() const;
|
||||
QScriptValue prototype() const;
|
||||
|
||||
private:
|
||||
static QScriptValue construct(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
QScriptValue _proto;
|
||||
QScriptValue _ctor;
|
||||
|
||||
QScriptString _name;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_DataViewClass_h
|
255
libraries/script-engine/src/DataViewPrototype.cpp
Normal file
255
libraries/script-engine/src/DataViewPrototype.cpp
Normal file
|
@ -0,0 +1,255 @@
|
|||
//
|
||||
// DataViewPrototype.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include <QDebug>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "DataViewClass.h"
|
||||
|
||||
#include "DataViewPrototype.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
DataViewPrototype::DataViewPrototype(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QByteArray* DataViewPrototype::thisArrayBuffer() const {
|
||||
QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME);
|
||||
return qscriptvalue_cast<QByteArray*>(bufferObject.data());
|
||||
}
|
||||
|
||||
bool DataViewPrototype::realOffset(qint32& offset, size_t size) const {
|
||||
if (offset < 0) {
|
||||
return false;
|
||||
}
|
||||
quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32();
|
||||
quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32();
|
||||
offset += viewOffset;
|
||||
return (offset + size) <= viewOffset + viewLength;
|
||||
}
|
||||
|
||||
qint32 DataViewPrototype::getInt8(qint32 byteOffset) {
|
||||
if (realOffset(byteOffset, sizeof(qint8))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
|
||||
qint8 result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
quint32 DataViewPrototype::getUint8(qint32 byteOffset) {
|
||||
if (realOffset(byteOffset, sizeof(quint8))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
|
||||
quint8 result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint32 DataViewPrototype::getInt16(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(qint16))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
qint16 result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
quint32 DataViewPrototype::getUint16(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(quint16))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
quint16 result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint32 DataViewPrototype::getInt32(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(qint32))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
qint32 result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(quint32))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
quint32 result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(float))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||
|
||||
float result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
return QScriptValue(result);
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(double))) {
|
||||
QDataStream stream(*thisArrayBuffer());
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
|
||||
|
||||
double result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) {
|
||||
if (realOffset(byteOffset, sizeof(qint8))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
|
||||
stream << (qint8)value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) {
|
||||
if (realOffset(byteOffset, sizeof(quint8))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
|
||||
stream << (quint8)value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(qint16))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
stream << (qint16)value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(quint16))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
stream << (quint16)value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(qint32))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
stream << (qint32)value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(quint32))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
|
||||
stream << (quint32)value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(float))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||
|
||||
stream << value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleEndian) {
|
||||
if (realOffset(byteOffset, sizeof(double))) {
|
||||
QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite);
|
||||
stream.skipRawData(byteOffset);
|
||||
stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian);
|
||||
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
|
||||
|
||||
stream << value;
|
||||
} else {
|
||||
thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
68
libraries/script-engine/src/DataViewPrototype.h
Normal file
68
libraries/script-engine/src/DataViewPrototype.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// DataViewPrototype.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/8/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DataViewPrototype_h
|
||||
#define hifi_DataViewPrototype_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
class DataViewPrototype : public QObject, public QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataViewPrototype(QObject* parent = NULL);
|
||||
|
||||
public slots:
|
||||
// Gets the value of the given type at the specified byte offset
|
||||
// from the start of the view. There is no alignment constraint;
|
||||
// multi-byte values may be fetched from any offset.
|
||||
//
|
||||
// For multi-byte values, the optional littleEndian argument
|
||||
// indicates whether a big-endian or little-endian value should be
|
||||
// read. If false or undefined, a big-endian value is read.
|
||||
//
|
||||
// These methods raise an exception if they would read
|
||||
// beyond the end of the view.
|
||||
qint32 getInt8(qint32 byteOffset);
|
||||
quint32 getUint8(qint32 byteOffset);
|
||||
qint32 getInt16(qint32 byteOffset, bool littleEndian = false);
|
||||
quint32 getUint16(qint32 byteOffset, bool littleEndian = false);
|
||||
qint32 getInt32(qint32 byteOffset, bool littleEndian = false);
|
||||
quint32 getUint32(qint32 byteOffset, bool littleEndian = false);
|
||||
QScriptValue getFloat32(qint32 byteOffset, bool littleEndian = false);
|
||||
QScriptValue getFloat64(qint32 byteOffset, bool littleEndian = false);
|
||||
|
||||
// Stores a value of the given type at the specified byte offset
|
||||
// from the start of the view. There is no alignment constraint;
|
||||
// multi-byte values may be stored at any offset.
|
||||
//
|
||||
// For multi-byte values, the optional littleEndian argument
|
||||
// indicates whether the value should be stored in big-endian or
|
||||
// little-endian byte order. If false or undefined, the value is
|
||||
// stored in big-endian byte order.
|
||||
//
|
||||
// These methods raise an exception if they would write
|
||||
// beyond the end of the view.
|
||||
void setInt8(qint32 byteOffset, qint32 value);
|
||||
void setUint8(qint32 byteOffset, quint32 value);
|
||||
void setInt16(qint32 byteOffset, qint32 value, bool littleEndian = false);
|
||||
void setUint16(qint32 byteOffset, quint32 value, bool littleEndian = false);
|
||||
void setInt32(qint32 byteOffset, qint32 value, bool littleEndian = false);
|
||||
void setUint32(qint32 byteOffset, quint32 value, bool littleEndian = false);
|
||||
void setFloat32(qint32 byteOffset, float value, bool littleEndian = false);
|
||||
void setFloat64(qint32 byteOffset, double value, bool littleEndian = false);
|
||||
|
||||
private:
|
||||
QByteArray* thisArrayBuffer() const;
|
||||
bool realOffset(qint32& offset, size_t size) const;
|
||||
};
|
||||
|
||||
#endif // hifi_DataViewPrototype_h
|
|
@ -32,10 +32,13 @@
|
|||
#include <VoxelDetail.h>
|
||||
|
||||
#include "AnimationObject.h"
|
||||
#include "ArrayBufferViewClass.h"
|
||||
#include "DataViewClass.h"
|
||||
#include "MenuItemProperties.h"
|
||||
#include "MIDIEvent.h"
|
||||
#include "LocalVoxels.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "TypedArrays.h"
|
||||
#include "XMLHttpRequestClass.h"
|
||||
|
||||
VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface;
|
||||
|
@ -90,7 +93,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
|||
_quatLibrary(),
|
||||
_vec3Library(),
|
||||
_uuidLibrary(),
|
||||
_animationCache(this)
|
||||
_animationCache(this),
|
||||
_arrayBufferClass(new ArrayBufferClass(this))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -115,13 +119,14 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL,
|
|||
_quatLibrary(),
|
||||
_vec3Library(),
|
||||
_uuidLibrary(),
|
||||
_animationCache(this)
|
||||
_animationCache(this),
|
||||
_arrayBufferClass(new ArrayBufferClass(this))
|
||||
{
|
||||
QString scriptURLString = scriptURL.toString();
|
||||
_fileNameString = scriptURLString;
|
||||
|
||||
QUrl url(scriptURL);
|
||||
|
||||
|
||||
// if the scheme length is one or lower, maybe they typed in a file, let's try
|
||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||
|
@ -210,7 +215,7 @@ void ScriptEngine::init() {
|
|||
if (_isInitialized) {
|
||||
return; // only initialize once
|
||||
}
|
||||
|
||||
|
||||
_isInitialized = true;
|
||||
|
||||
_voxelsScriptingInterface.init();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <VoxelsScriptingInterface.h>
|
||||
|
||||
#include "AbstractControllerScriptingInterface.h"
|
||||
#include "ArrayBufferClass.h"
|
||||
#include "Quat.h"
|
||||
#include "ScriptUUID.h"
|
||||
#include "Vec3.h"
|
||||
|
@ -56,6 +57,9 @@ public:
|
|||
/// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||
static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; }
|
||||
|
||||
QScriptEngine* getEngine() { return &_engine; }
|
||||
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
|
||||
|
||||
/// sets the script contents, will return false if failed, will fail if script is already running
|
||||
bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString(""));
|
||||
|
||||
|
@ -147,6 +151,8 @@ private:
|
|||
Vec3 _vec3Library;
|
||||
ScriptUUID _uuidLibrary;
|
||||
AnimationCache _animationCache;
|
||||
|
||||
ArrayBufferClass* _arrayBufferClass;
|
||||
|
||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||
};
|
||||
|
|
106
libraries/script-engine/src/TypedArrayPrototype.cpp
Normal file
106
libraries/script-engine/src/TypedArrayPrototype.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// TypedArrayPrototype.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/14/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TypedArrays.h"
|
||||
|
||||
#include "TypedArrayPrototype.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QByteArray* TypedArrayPrototype::thisArrayBuffer() const {
|
||||
QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME);
|
||||
return qscriptvalue_cast<QByteArray*>(bufferObject.data());
|
||||
}
|
||||
|
||||
void TypedArrayPrototype::set(QScriptValue array, qint32 offset) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
if (array.isArray() || typedArray) {
|
||||
if (offset < 0) {
|
||||
engine()->evaluate("throw \"ArgumentError: negative offset\"");
|
||||
}
|
||||
quint32 length = array.property("length").toInt32();
|
||||
if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) {
|
||||
engine()->evaluate("throw \"ArgumentError: array does not fit\"");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < length; ++i) {
|
||||
thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i)));
|
||||
}
|
||||
} else {
|
||||
engine()->evaluate("throw \"ArgumentError: not an array\"");
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue TypedArrayPrototype::subarray(qint32 begin) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
|
||||
qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32();
|
||||
qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32();
|
||||
qint32 bytesPerElement = typedArray->_bytesPerElement;
|
||||
|
||||
// if indices < 0 then they start from the end of the array
|
||||
begin = (begin < 0) ? length + begin : begin;
|
||||
|
||||
// here we clamp the indices to fit the array
|
||||
begin = glm::clamp(begin, 0, (length - 1));
|
||||
|
||||
byteOffset += begin * bytesPerElement;
|
||||
return typedArray->newInstance(arrayBuffer, byteOffset, length - begin);
|
||||
}
|
||||
|
||||
QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName);
|
||||
qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32();
|
||||
qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32();
|
||||
qint32 bytesPerElement = typedArray->_bytesPerElement;
|
||||
|
||||
// if indices < 0 then they start from the end of the array
|
||||
begin = (begin < 0) ? length + begin : begin;
|
||||
end = (end < 0) ? length + end : end;
|
||||
|
||||
// here we clamp the indices to fit the array
|
||||
begin = glm::clamp(begin, 0, (length - 1));
|
||||
end = glm::clamp(end, 0, (length - 1));
|
||||
|
||||
byteOffset += begin * bytesPerElement;
|
||||
length = (end - begin > 0) ? end - begin : 0;
|
||||
return typedArray->newInstance(arrayBuffer, byteOffset, length);
|
||||
}
|
||||
|
||||
QScriptValue TypedArrayPrototype::get(quint32 index) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptString name = engine()->toStringHandle(QString::number(index));
|
||||
uint id;
|
||||
QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(),
|
||||
name,
|
||||
QScriptClass::HandlesReadAccess, &id);
|
||||
if (QScriptClass::HandlesReadAccess & flags) {
|
||||
return typedArray->property(thisObject(), name, id);
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
void TypedArrayPrototype::set(quint32 index, QScriptValue& value) {
|
||||
TypedArray* typedArray = static_cast<TypedArray*>(parent());
|
||||
QScriptValue object = thisObject();
|
||||
QScriptString name = engine()->toStringHandle(QString::number(index));
|
||||
uint id;
|
||||
QScriptClass::QueryFlags flags = typedArray->queryProperty(object,
|
||||
name,
|
||||
QScriptClass::HandlesWriteAccess, &id);
|
||||
if (QScriptClass::HandlesWriteAccess & flags) {
|
||||
typedArray->setProperty(object, name, id, value);
|
||||
}
|
||||
}
|
33
libraries/script-engine/src/TypedArrayPrototype.h
Normal file
33
libraries/script-engine/src/TypedArrayPrototype.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// TypedArrayPrototype.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/14/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_TypedArrayPrototype_h
|
||||
#define hifi_TypedArrayPrototype_h
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
class TypedArrayPrototype : public QObject, public QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TypedArrayPrototype(QObject* parent = NULL);
|
||||
|
||||
public slots:
|
||||
void set(QScriptValue array, qint32 offset = 0);
|
||||
QScriptValue subarray(qint32 begin);
|
||||
QScriptValue subarray(qint32 begin, qint32 end);
|
||||
|
||||
QScriptValue get(quint32 index);
|
||||
void set(quint32 index, QScriptValue& value);
|
||||
private:
|
||||
QByteArray* thisArrayBuffer() const;
|
||||
};
|
||||
|
||||
#endif // hifi_TypedArrayPrototype_h
|
418
libraries/script-engine/src/TypedArrays.cpp
Normal file
418
libraries/script-engine/src/TypedArrays.cpp
Normal file
|
@ -0,0 +1,418 @@
|
|||
//
|
||||
// TypedArrays.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/9/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "TypedArrayPrototype.h"
|
||||
|
||||
#include "TypedArrays.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QByteArray*)
|
||||
|
||||
TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) {
|
||||
_bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1());
|
||||
_lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1());
|
||||
_name = engine()->toStringHandle(name.toLatin1());
|
||||
|
||||
QScriptValue global = engine()->globalObject();
|
||||
|
||||
// build prototype
|
||||
_proto = engine()->newQObject(new TypedArrayPrototype(this),
|
||||
QScriptEngine::QtOwnership,
|
||||
QScriptEngine::SkipMethodsInEnumeration |
|
||||
QScriptEngine::ExcludeSuperClassMethods |
|
||||
QScriptEngine::ExcludeSuperClassProperties);
|
||||
_proto.setPrototype(global.property("Object").property("prototype"));
|
||||
|
||||
// Register constructor
|
||||
_ctor = engine()->newFunction(construct, _proto);
|
||||
_ctor.setData(engine()->toScriptValue(this));
|
||||
engine()->globalObject().setProperty(_name, _ctor);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::newInstance(quint32 length) {
|
||||
ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass();
|
||||
QScriptValue buffer = array->newInstance(length * _bytesPerElement);
|
||||
return newInstance(buffer, 0, length);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::newInstance(QScriptValue array) {
|
||||
const QString ARRAY_LENGTH_HANDLE = "length";
|
||||
if (array.property(ARRAY_LENGTH_HANDLE).isValid()) {
|
||||
quint32 length = array.property(ARRAY_LENGTH_HANDLE).toInt32();
|
||||
QScriptValue newArray = newInstance(length);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
QScriptValue value = array.property(QString::number(i));
|
||||
setProperty(newArray, engine()->toStringHandle(QString::number(i)),
|
||||
i * _bytesPerElement, (value.isNumber()) ? value : QScriptValue(0));
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
engine()->evaluate("throw \"ArgumentError: not an array\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) {
|
||||
QScriptValue data = engine()->newObject();
|
||||
data.setProperty(_bufferName, buffer);
|
||||
data.setProperty(_byteOffsetName, byteOffset);
|
||||
data.setProperty(_byteLengthName, length * _bytesPerElement);
|
||||
data.setProperty(_lengthName, length);
|
||||
|
||||
return engine()->newObject(this, data);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) {
|
||||
TypedArray* cls = qscriptvalue_cast<TypedArray*>(context->callee().data());
|
||||
if (!cls) {
|
||||
return QScriptValue();
|
||||
}
|
||||
if (context->argumentCount() == 0) {
|
||||
return cls->newInstance(0);
|
||||
}
|
||||
|
||||
QScriptValue newObject;
|
||||
QScriptValue bufferArg = context->argument(0);
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(bufferArg.data());
|
||||
|
||||
// parse arguments
|
||||
if (arrayBuffer) {
|
||||
if (context->argumentCount() == 1) {
|
||||
// Case for entire ArrayBuffer
|
||||
newObject = cls->newInstance(bufferArg, 0, arrayBuffer->size());
|
||||
} else {
|
||||
QScriptValue byteOffsetArg = context->argument(1);
|
||||
if (!byteOffsetArg.isNumber()) {
|
||||
engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) {
|
||||
engine->evaluate("throw \"RangeError: byteOffset out of range\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) {
|
||||
engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\"");
|
||||
}
|
||||
quint32 byteOffset = byteOffsetArg.toInt32();
|
||||
|
||||
if (context->argumentCount() == 2) {
|
||||
// case for end of ArrayBuffer
|
||||
if ((arrayBuffer->size() - byteOffset) % cls->_bytesPerElement != 0) {
|
||||
engine->evaluate("throw \"RangeError: byteLength - byteOffset not a multiple of BYTES_PER_ELEMENT\"");
|
||||
}
|
||||
quint32 length = (arrayBuffer->size() - byteOffset) / cls->_bytesPerElement;
|
||||
newObject = cls->newInstance(bufferArg, byteOffset, length);
|
||||
} else {
|
||||
|
||||
QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue();
|
||||
if (!lengthArg.isNumber()) {
|
||||
engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
if (lengthArg.toInt32() < 0 ||
|
||||
byteOffsetArg.toInt32() + lengthArg.toInt32() * cls->_bytesPerElement > arrayBuffer->size()) {
|
||||
engine->evaluate("throw \"RangeError: byteLength out of range\"");
|
||||
return QScriptValue();
|
||||
}
|
||||
quint32 length = lengthArg.toInt32();
|
||||
|
||||
// case for well-defined range
|
||||
newObject = cls->newInstance(bufferArg, byteOffset, length);
|
||||
}
|
||||
}
|
||||
} else if (context->argument(0).isNumber()) {
|
||||
// case for new ArrayBuffer
|
||||
newObject = cls->newInstance(context->argument(0).toInt32());
|
||||
} else {
|
||||
newObject = cls->newInstance(bufferArg);
|
||||
}
|
||||
|
||||
if (context->isCalledAsConstructor()) {
|
||||
// if called with the new keyword, replace this object
|
||||
context->setThisObject(newObject);
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id) {
|
||||
if (name == _bytesPerElementName || name == _lengthName) {
|
||||
return flags &= HandlesReadAccess; // Only keep read access flags
|
||||
}
|
||||
|
||||
quint32 byteOffset = object.data().property(_byteOffsetName).toInt32();
|
||||
quint32 length = object.data().property(_lengthName).toInt32();
|
||||
bool ok = false;
|
||||
int pos = name.toArrayIndex(&ok);
|
||||
|
||||
// Check that name is a valid index and arrayBuffer exists
|
||||
if (ok && pos >= 0 && pos < length) {
|
||||
*id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation
|
||||
return HandlesReadAccess | HandlesWriteAccess; // Read/Write access
|
||||
}
|
||||
|
||||
return ArrayBufferViewClass::queryProperty(object, name, flags, id);
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
if (name == _bytesPerElementName) {
|
||||
return QScriptValue(_bytesPerElement);
|
||||
}
|
||||
if (name == _lengthName) {
|
||||
return object.data().property(_lengthName);
|
||||
}
|
||||
return ArrayBufferViewClass::property(object, name, id);
|
||||
}
|
||||
|
||||
QScriptValue::PropertyFlags TypedArray::propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id) {
|
||||
return QScriptValue::Undeletable;
|
||||
}
|
||||
|
||||
QString TypedArray::name() const {
|
||||
return _name.toString();
|
||||
}
|
||||
|
||||
QScriptValue TypedArray::prototype() const {
|
||||
return _proto;
|
||||
}
|
||||
|
||||
void TypedArray::setBytesPerElement(quint32 bytesPerElement) {
|
||||
_bytesPerElement = bytesPerElement;
|
||||
_ctor.setProperty(_bytesPerElementName, _bytesPerElement);
|
||||
}
|
||||
|
||||
// templated helper functions
|
||||
// don't work for floats as they require single precision settings
|
||||
template<class T>
|
||||
QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString& name, uint id) {
|
||||
bool ok = false;
|
||||
name.toArrayIndex(&ok);
|
||||
|
||||
if (ok && arrayBuffer) {
|
||||
QDataStream stream(*arrayBuffer);
|
||||
stream.skipRawData(id);
|
||||
|
||||
T result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint id, const QScriptValue& value) {
|
||||
if (arrayBuffer && value.isNumber()) {
|
||||
QDataStream stream(arrayBuffer, QIODevice::ReadWrite);
|
||||
stream.skipRawData(id);
|
||||
|
||||
stream << (T)value.toNumber();
|
||||
}
|
||||
}
|
||||
|
||||
Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(qint8));
|
||||
}
|
||||
|
||||
QScriptValue Int8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<qint8>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<qint8>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint8));
|
||||
}
|
||||
|
||||
QScriptValue Uint8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint8>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint8ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<quint8>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint8));
|
||||
}
|
||||
|
||||
QScriptValue Uint8ClampedArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint8>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
if (ba && value.isNumber()) {
|
||||
QDataStream stream(ba, QIODevice::ReadWrite);
|
||||
stream.skipRawData(id);
|
||||
if (value.toNumber() > 255) {
|
||||
stream << (quint8)255;
|
||||
} else if (value.toNumber() < 0) {
|
||||
stream << (quint8)0;
|
||||
} else {
|
||||
stream << (quint8)glm::clamp(qRound(value.toNumber()), 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(qint16));
|
||||
}
|
||||
|
||||
QScriptValue Int16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<qint16>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Int16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<qint16>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint16));
|
||||
}
|
||||
|
||||
QScriptValue Uint16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint16>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<quint16>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(qint32));
|
||||
}
|
||||
|
||||
QScriptValue Int32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<qint32>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Int32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<qint32>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(quint32));
|
||||
}
|
||||
|
||||
QScriptValue Uint32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
QScriptValue result = propertyHelper<quint32>(arrayBuffer, name, id);
|
||||
return (result.isValid()) ? result : TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Uint32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
setPropertyHelper<quint32>(ba, name, id, value);
|
||||
}
|
||||
|
||||
Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(float));
|
||||
}
|
||||
|
||||
QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());bool ok = false;
|
||||
name.toArrayIndex(&ok);
|
||||
|
||||
if (ok && arrayBuffer) {
|
||||
QDataStream stream(*arrayBuffer);
|
||||
stream.skipRawData(id);
|
||||
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||
|
||||
float result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
if (ba && value.isNumber()) {
|
||||
QDataStream stream(ba, QIODevice::ReadWrite);
|
||||
stream.skipRawData(id);
|
||||
stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||
|
||||
stream << (float)value.toNumber();
|
||||
}
|
||||
}
|
||||
|
||||
Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) {
|
||||
setBytesPerElement(sizeof(double));
|
||||
}
|
||||
|
||||
QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) {
|
||||
QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());bool ok = false;
|
||||
name.toArrayIndex(&ok);
|
||||
|
||||
if (ok && arrayBuffer) {
|
||||
QDataStream stream(*arrayBuffer);
|
||||
stream.skipRawData(id);
|
||||
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
|
||||
|
||||
double result;
|
||||
stream >> result;
|
||||
if (isNaN(result)) {
|
||||
return QScriptValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return TypedArray::property(object, name, id);
|
||||
}
|
||||
|
||||
void Float64ArrayClass::setProperty(QScriptValue& object, const QScriptString& name,
|
||||
uint id, const QScriptValue& value) {
|
||||
QByteArray* ba = qscriptvalue_cast<QByteArray*>(object.data().property(_bufferName).data());
|
||||
if (ba && value.isNumber()) {
|
||||
QDataStream stream(ba, QIODevice::ReadWrite);
|
||||
stream.skipRawData(id);
|
||||
stream.setFloatingPointPrecision(QDataStream::DoublePrecision);
|
||||
|
||||
stream << (double)value.toNumber();
|
||||
}
|
||||
}
|
||||
|
148
libraries/script-engine/src/TypedArrays.h
Normal file
148
libraries/script-engine/src/TypedArrays.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
// TypedArrays.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/9/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_TypedArrays_h
|
||||
#define hifi_TypedArrays_h
|
||||
|
||||
#include "ArrayBufferViewClass.h"
|
||||
|
||||
static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT";
|
||||
static const QString LENGTH_PROPERTY_NAME = "length";
|
||||
|
||||
static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array";
|
||||
static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array";
|
||||
static const QString UINT_8_CLAMPED_ARRAY_CLASS_NAME = "Uint8ClampedArray";
|
||||
static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array";
|
||||
static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array";
|
||||
static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array";
|
||||
static const QString UINT_32_ARRAY_CLASS_NAME = "Uint32Array";
|
||||
static const QString FLOAT_32_ARRAY_CLASS_NAME = "Float32Array";
|
||||
static const QString FLOAT_64_ARRAY_CLASS_NAME = "Float64Array";
|
||||
|
||||
class TypedArray : public ArrayBufferViewClass {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TypedArray(ScriptEngine* scriptEngine, QString name);
|
||||
virtual QScriptValue newInstance(quint32 length);
|
||||
virtual QScriptValue newInstance(QScriptValue array);
|
||||
virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length);
|
||||
|
||||
virtual QueryFlags queryProperty(const QScriptValue& object,
|
||||
const QScriptString& name,
|
||||
QueryFlags flags, uint* id);
|
||||
virtual QScriptValue property(const QScriptValue& object,
|
||||
const QScriptString& name, uint id);
|
||||
virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) = 0;
|
||||
virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object,
|
||||
const QScriptString& name, uint id);
|
||||
|
||||
QString name() const;
|
||||
QScriptValue prototype() const;
|
||||
|
||||
protected:
|
||||
static QScriptValue construct(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
void setBytesPerElement(quint32 bytesPerElement);
|
||||
|
||||
QScriptValue _proto;
|
||||
QScriptValue _ctor;
|
||||
|
||||
QScriptString _name;
|
||||
QScriptString _bytesPerElementName;
|
||||
QScriptString _lengthName;
|
||||
|
||||
quint32 _bytesPerElement;
|
||||
|
||||
friend class TypedArrayPrototype;
|
||||
};
|
||||
|
||||
class Int8ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int8ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Uint8ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint8ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Uint8ClampedArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint8ClampedArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Int16ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int16ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Uint16ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint16ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Int32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Int32ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Uint32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Uint32ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Float32ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Float32ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
class Float64ArrayClass : public TypedArray {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Float64ArrayClass(ScriptEngine* scriptEngine);
|
||||
|
||||
QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id);
|
||||
void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value);
|
||||
};
|
||||
|
||||
#endif // hifi_TypedArrays_h
|
Loading…
Reference in a new issue