diff --git a/lib/smalloc.js b/lib/smalloc.js index b6eb21fbc3298e..f181cb60cf2d7c 100644 --- a/lib/smalloc.js +++ b/lib/smalloc.js @@ -2,12 +2,14 @@ const smalloc = process.binding('smalloc'); const kMaxLength = smalloc.kMaxLength; +const kMinType = smalloc.kMinType; +const kMaxType = smalloc.kMaxType; const util = require('util'); exports.alloc = alloc; -exports.copyOnto = smalloc.copyOnto; +exports.copyOnto = copyOnto; exports.dispose = dispose; -exports.hasExternalData = smalloc.hasExternalData; +exports.hasExternalData = hasExternalData; // don't allow kMaxLength to accidentally be overwritten. it's a lot less // apparent when a primitive is accidentally changed. @@ -15,24 +17,8 @@ Object.defineProperty(exports, 'kMaxLength', { enumerable: true, value: kMaxLength, writable: false }); -// enumerated values for different external array types -var Types = {}; - -// Must match enum v8::ExternalArrayType. -Object.defineProperties(Types, { - 'Int8': { enumerable: true, value: 1, writable: false }, - 'Uint8': { enumerable: true, value: 2, writable: false }, - 'Int16': { enumerable: true, value: 3, writable: false }, - 'Uint16': { enumerable: true, value: 4, writable: false }, - 'Int32': { enumerable: true, value: 5, writable: false }, - 'Uint32': { enumerable: true, value: 6, writable: false }, - 'Float': { enumerable: true, value: 7, writable: false }, - 'Double': { enumerable: true, value: 8, writable: false }, - 'Uint8Clamped': { enumerable: true, value: 9, writable: false } -}); - Object.defineProperty(exports, 'Types', { - enumerable: true, value: Types, writable: false + enumerable: true, value: Object.freeze(smalloc.types), writable: false }); @@ -50,13 +36,20 @@ function alloc(n, obj, type) { throw new TypeError('obj must be an Object'); } - // 1 == v8::kExternalUint8Array, 9 == v8::kExternalUint8ClampedArray - if (type < 1 || type > 9) - throw new TypeError('unknown external array type: ' + type); if (Array.isArray(obj)) - throw new TypeError('Arrays are not supported'); + throw new TypeError('obj cannot be an array'); + if (obj instanceof Buffer) + throw new TypeError('obj cannot be a Buffer'); + if (smalloc.isTypedArray(obj)) + throw new TypeError('obj cannot be a typed array'); + if (smalloc.hasExternalData(obj)) + throw new TypeError('object already has external array data'); + + if (type < kMinType || type > kMaxType) + throw new TypeError('unknown external array type: ' + type); if (n > kMaxLength) - throw new RangeError('n > kMaxLength'); + throw new RangeError('Attempt to allocate array larger than maximum ' + + 'size: 0x' + kMaxLength.toString(16) + ' elements'); return smalloc.alloc(obj, n, type); } @@ -70,7 +63,29 @@ function dispose(obj) { if (smalloc.isTypedArray(obj)) throw new TypeError('obj cannot be a typed array'); if (!smalloc.hasExternalData(obj)) - throw new Error('obj has no external array data'); + throw new TypeError('obj has no external array data'); smalloc.dispose(obj); } + + +function copyOnto(source, sourceStart, dest, destStart, copyLength) { + if (util.isPrimitive(source)) + throw new TypeError('source must be an Object'); + if (util.isPrimitive(dest)) + throw new TypeError('dest must be an Object'); + if (!smalloc.hasExternalData(source)) + throw new TypeError('source has no external array data'); + if (!smalloc.hasExternalData(dest)) + throw new TypeError('dest has no external array data'); + + return smalloc.copyOnto(source, sourceStart, dest, destStart, copyLength); +} + + +function hasExternalData(obj) { + if (util.isPrimitive(obj)) + return false; + + return smalloc.hasExternalData(obj); +} diff --git a/src/smalloc.cc b/src/smalloc.cc index 89461959dd8912..319c3937605404 100644 --- a/src/smalloc.cc +++ b/src/smalloc.cc @@ -10,6 +10,20 @@ #include #define ALLOC_ID (0xA10C) +#define EXTERNAL_ARRAY_TYPES(V) \ + V(Int8, kExternalInt8Array) \ + V(Uint8, kExternalUint8Array) \ + V(Int16, kExternalInt16Array) \ + V(Uint16, kExternalUint16Array) \ + V(Int32, kExternalInt32Array) \ + V(Uint32, kExternalUint32Array) \ + V(Float, kExternalFloat32Array) \ + V(Double, kExternalFloat64Array) \ + V(Uint8Clamped, kExternalUint8ClampedArray) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + namespace node { namespace smalloc { @@ -152,18 +166,14 @@ size_t ExternalArraySize(enum ExternalArrayType type) { void CopyOnto(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsObject()) - return env->ThrowTypeError("source must be an object"); - if (!args[2]->IsObject()) - return env->ThrowTypeError("dest must be an object"); + ASSERT(args[0]->IsObject()); + ASSERT(args[2]->IsObject()); Local source = args[0].As(); Local dest = args[2].As(); - if (!source->HasIndexedPropertiesInExternalArrayData()) - return env->ThrowError("source has no external array data"); - if (!dest->HasIndexedPropertiesInExternalArrayData()) - return env->ThrowError("dest has no external array data"); + ASSERT(source->HasIndexedPropertiesInExternalArrayData()); + ASSERT(dest->HasIndexedPropertiesInExternalArrayData()); size_t source_start = args[1]->Uint32Value(); size_t dest_start = args[3]->Uint32Value(); @@ -266,11 +276,11 @@ void SliceOnto(const FunctionCallbackInfo& args) { void Alloc(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + ASSERT(args[0]->IsObject()); + Local obj = args[0].As(); - // can't perform this check in JS - if (obj->HasIndexedPropertiesInExternalArrayData()) - return env->ThrowTypeError("object already has external array data"); + ASSERT(!obj->HasIndexedPropertiesInExternalArrayData()); size_t length = args[1]->Uint32Value(); enum ExternalArrayType array_type; @@ -410,8 +420,8 @@ void Alloc(Environment* env, void HasExternalData(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - args.GetReturnValue().Set(args[0]->IsObject() && - HasExternalData(env, args[0].As())); + ASSERT(args[0]->IsObject()); + args.GetReturnValue().Set(HasExternalData(env, args[0].As())); } @@ -563,6 +573,7 @@ void Initialize(Handle exports, Handle unused, Handle context) { Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); env->SetMethod(exports, "copyOnto", CopyOnto); env->SetMethod(exports, "sliceOnto", SliceOnto); @@ -577,6 +588,25 @@ void Initialize(Handle exports, exports->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"), Uint32::NewFromUnsigned(env->isolate(), kMaxLength)); + Local types = Object::New(isolate); + + uint32_t kMinType = ~0; + uint32_t kMaxType = 0; + #define V(name, value) \ + types->Set(FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ + Uint32::NewFromUnsigned(env->isolate(), v8::value)); \ + kMinType = MIN(kMinType, v8::value); \ + kMaxType = MAX(kMinType, v8::value); + + EXTERNAL_ARRAY_TYPES(V) + #undef V + + exports->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "types"), types); + exports->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kMinType"), + Uint32::NewFromUnsigned(env->isolate(), kMinType)); + exports->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxType"), + Uint32::NewFromUnsigned(env->isolate(), kMaxType)); + HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler(); heap_profiler->SetWrapperClassInfoProvider(ALLOC_ID, WrapperInfo); } diff --git a/test/parallel/test-smalloc.js b/test/parallel/test-smalloc.js index 61ffe3b84da88c..ef1aa60bad6838 100644 --- a/test/parallel/test-smalloc.js +++ b/test/parallel/test-smalloc.js @@ -309,3 +309,23 @@ assert.throws(function() { assert.throws(function() { smalloc.dispose({}); }); + + +// Types should be immutable +assert.deepStrictEqual(Object.getOwnPropertyDescriptor(smalloc, 'Types'), { + value: smalloc.Types, + writable: false, + enumerable: true, + configurable: false +}); + +var types = Object.keys(smalloc.Types); +var Types = smalloc.Types; + +for (var i = 0; i < types.length; i++) + assert.deepStrictEqual(Object.getOwnPropertyDescriptor(Types, types[i]), { + value: Types[types[i]], + writable: false, + enumerable: true, + configurable: false + });