6 #include "node_internals.h"
7 #include "v8-profiler.h"
12 #define ALLOC_ID (0xA10C)
19 using v8::ExternalArrayType;
20 using v8::FunctionCallbackInfo;
22 using v8::HandleScope;
23 using v8::HeapProfiler;
28 using v8::RetainedObjectInfo;
31 using v8::WeakCallbackData;
32 using v8::kExternalUint8Array;
37 static inline void Free(char* data, void* hint);
38 static inline CallbackInfo* New(Isolate* isolate,
39 Handle<Object> object,
40 FreeCallback callback,
42 inline void Dispose(Isolate* isolate);
43 inline Persistent<Object>* persistent();
45 static void WeakCallback(const WeakCallbackData<Object, CallbackInfo>&);
46 inline void WeakCallback(Isolate* isolate, Local<Object> object);
47 inline CallbackInfo(Isolate* isolate,
48 Handle<Object> object,
49 FreeCallback callback,
52 Persistent<Object> persistent_;
53 FreeCallback const callback_;
55 DISALLOW_COPY_AND_ASSIGN(CallbackInfo);
59 void CallbackInfo::Free(char* data, void*) {
64 CallbackInfo* CallbackInfo::New(Isolate* isolate,
65 Handle<Object> object,
66 FreeCallback callback,
68 return new CallbackInfo(isolate, object, callback, hint);
72 void CallbackInfo::Dispose(Isolate* isolate) {
73 WeakCallback(isolate, PersistentToLocal(isolate, persistent_));
77 Persistent<Object>* CallbackInfo::persistent() {
82 CallbackInfo::CallbackInfo(Isolate* isolate,
83 Handle<Object> object,
84 FreeCallback callback,
86 : persistent_(isolate, object),
89 persistent_.SetWeak(this, WeakCallback);
90 persistent_.SetWrapperClassId(ALLOC_ID);
91 persistent_.MarkIndependent();
95 CallbackInfo::~CallbackInfo() {
100 void CallbackInfo::WeakCallback(
101 const WeakCallbackData<Object, CallbackInfo>& data) {
102 data.GetParameter()->WeakCallback(data.GetIsolate(), data.GetValue());
106 void CallbackInfo::WeakCallback(Isolate* isolate, Local<Object> object) {
107 void* array_data = object->GetIndexedPropertiesExternalArrayData();
108 size_t array_length = object->GetIndexedPropertiesExternalArrayDataLength();
109 enum ExternalArrayType array_type =
110 object->GetIndexedPropertiesExternalArrayDataType();
111 size_t array_size = ExternalArraySize(array_type);
112 CHECK_GT(array_size, 0);
113 if (array_size > 1 && array_data != NULL) {
114 CHECK_GT(array_length * array_size, array_length); // Overflow check.
115 array_length *= array_size;
117 object->SetIndexedPropertiesToExternalArrayData(nullptr, array_type, 0);
118 int64_t change_in_bytes = -static_cast<int64_t>(array_length + sizeof(*this));
119 isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
120 callback_(static_cast<char*>(array_data), hint_);
125 // return size of external array type, or 0 if unrecognized
126 size_t ExternalArraySize(enum ExternalArrayType type) {
128 case v8::kExternalUint8Array:
129 return sizeof(uint8_t);
130 case v8::kExternalInt8Array:
131 return sizeof(int8_t);
132 case v8::kExternalInt16Array:
133 return sizeof(int16_t);
134 case v8::kExternalUint16Array:
135 return sizeof(uint16_t);
136 case v8::kExternalInt32Array:
137 return sizeof(int32_t);
138 case v8::kExternalUint32Array:
139 return sizeof(uint32_t);
140 case v8::kExternalFloat32Array:
141 return sizeof(float); // NOLINT(runtime/sizeof)
142 case v8::kExternalFloat64Array:
143 return sizeof(double); // NOLINT(runtime/sizeof)
144 case v8::kExternalUint8ClampedArray:
145 return sizeof(uint8_t);
151 // copyOnto(source, source_start, dest, dest_start, copy_length)
152 void CopyOnto(const FunctionCallbackInfo<Value>& args) {
153 Environment* env = Environment::GetCurrent(args);
155 if (!args[0]->IsObject())
156 return env->ThrowTypeError("source must be an object");
157 if (!args[2]->IsObject())
158 return env->ThrowTypeError("dest must be an object");
160 Local<Object> source = args[0].As<Object>();
161 Local<Object> dest = args[2].As<Object>();
163 if (!source->HasIndexedPropertiesInExternalArrayData())
164 return env->ThrowError("source has no external array data");
165 if (!dest->HasIndexedPropertiesInExternalArrayData())
166 return env->ThrowError("dest has no external array data");
168 size_t source_start = args[1]->Uint32Value();
169 size_t dest_start = args[3]->Uint32Value();
170 size_t copy_length = args[4]->Uint32Value();
171 char* source_data = static_cast<char*>(
172 source->GetIndexedPropertiesExternalArrayData());
173 char* dest_data = static_cast<char*>(
174 dest->GetIndexedPropertiesExternalArrayData());
176 size_t source_length = source->GetIndexedPropertiesExternalArrayDataLength();
177 enum ExternalArrayType source_type =
178 source->GetIndexedPropertiesExternalArrayDataType();
179 size_t source_size = ExternalArraySize(source_type);
181 size_t dest_length = dest->GetIndexedPropertiesExternalArrayDataLength();
182 enum ExternalArrayType dest_type =
183 dest->GetIndexedPropertiesExternalArrayDataType();
184 size_t dest_size = ExternalArraySize(dest_type);
186 // optimization for Uint8 arrays (i.e. Buffers)
187 if (source_size != 1 || dest_size != 1) {
188 if (source_size == 0)
189 return env->ThrowTypeError("unknown source external array type");
191 return env->ThrowTypeError("unknown dest external array type");
193 if (source_length * source_size < source_length)
194 return env->ThrowRangeError("source_length * source_size overflow");
195 if (copy_length * source_size < copy_length)
196 return env->ThrowRangeError("copy_length * source_size overflow");
197 if (dest_length * dest_size < dest_length)
198 return env->ThrowRangeError("dest_length * dest_size overflow");
200 source_length *= source_size;
201 copy_length *= source_size;
202 dest_length *= dest_size;
205 // necessary to check in case (source|dest)_start _and_ copy_length overflow
206 if (copy_length > source_length)
207 return env->ThrowRangeError("copy_length > source_length");
208 if (copy_length > dest_length)
209 return env->ThrowRangeError("copy_length > dest_length");
210 if (source_start > source_length)
211 return env->ThrowRangeError("source_start > source_length");
212 if (dest_start > dest_length)
213 return env->ThrowRangeError("dest_start > dest_length");
215 // now we can guarantee these will catch oob access and *_start overflow
216 if (source_start + copy_length > source_length)
217 return env->ThrowRangeError("source_start + copy_length > source_length");
218 if (dest_start + copy_length > dest_length)
219 return env->ThrowRangeError("dest_start + copy_length > dest_length");
221 memmove(dest_data + dest_start, source_data + source_start, copy_length);
225 // dest will always be same type as source
227 // dest._data = sliceOnto(source, dest, start, end);
228 void SliceOnto(const FunctionCallbackInfo<Value>& args) {
229 Local<Object> source = args[0].As<Object>();
230 Local<Object> dest = args[1].As<Object>();
232 CHECK(source->HasIndexedPropertiesInExternalArrayData());
233 CHECK_EQ(false, dest->HasIndexedPropertiesInExternalArrayData());
235 char* source_data = static_cast<char*>(
236 source->GetIndexedPropertiesExternalArrayData());
237 size_t source_len = source->GetIndexedPropertiesExternalArrayDataLength();
238 enum ExternalArrayType source_type =
239 source->GetIndexedPropertiesExternalArrayDataType();
240 size_t source_size = ExternalArraySize(source_type);
242 CHECK_NE(source_size, 0);
244 size_t start = args[2]->Uint32Value();
245 size_t end = args[3]->Uint32Value();
246 size_t length = end - start;
248 if (source_size > 1) {
249 CHECK_GE(length * source_size, length);
250 length *= source_size;
253 CHECK(source_data != nullptr || length == 0);
254 CHECK_LE(end, source_len);
255 CHECK_LE(start, end);
257 dest->SetIndexedPropertiesToExternalArrayData(source_data + start,
260 args.GetReturnValue().Set(source);
265 // alloc(obj, n[, type]);
266 void Alloc(const FunctionCallbackInfo<Value>& args) {
267 Environment* env = Environment::GetCurrent(args);
269 Local<Object> obj = args[0].As<Object>();
271 // can't perform this check in JS
272 if (obj->HasIndexedPropertiesInExternalArrayData())
273 return env->ThrowTypeError("object already has external array data");
275 size_t length = args[1]->Uint32Value();
276 enum ExternalArrayType array_type;
278 // it's faster to not pass the default argument then use Uint32Value
279 if (args[2]->IsUndefined()) {
280 array_type = kExternalUint8Array;
282 array_type = static_cast<ExternalArrayType>(args[2]->Uint32Value());
283 size_t type_length = ExternalArraySize(array_type);
284 CHECK_GE(type_length * length, length);
285 length *= type_length;
288 Alloc(env, obj, length, array_type);
289 args.GetReturnValue().Set(obj);
293 void Alloc(Environment* env,
296 enum ExternalArrayType type) {
297 size_t type_size = ExternalArraySize(type);
299 CHECK_LE(length, kMaxLength);
300 CHECK_GT(type_size, 0);
303 return Alloc(env, obj, nullptr, length, type);
305 char* data = static_cast<char*>(malloc(length));
306 if (data == nullptr) {
307 FatalError("node::smalloc::Alloc(v8::Handle<v8::Object>, size_t,"
308 " v8::ExternalArrayType)", "Out Of Memory");
311 Alloc(env, obj, data, length, type);
315 void Alloc(Environment* env,
319 enum ExternalArrayType type) {
320 CHECK_EQ(false, obj->HasIndexedPropertiesInExternalArrayData());
321 env->isolate()->AdjustAmountOfExternalAllocatedMemory(length);
322 size_t size = length / ExternalArraySize(type);
323 obj->SetIndexedPropertiesToExternalArrayData(data, type, size);
324 CallbackInfo::New(env->isolate(), obj, CallbackInfo::Free);
328 // for internal use: dispose(obj);
329 void AllocDispose(const FunctionCallbackInfo<Value>& args) {
330 Environment* env = Environment::GetCurrent(args);
331 AllocDispose(env, args[0].As<Object>());
335 void AllocDispose(Environment* env, Handle<Object> obj) {
336 HandleScope handle_scope(env->isolate());
338 if (env->using_smalloc_alloc_cb()) {
339 Local<Value> ext_v = obj->GetHiddenValue(env->smalloc_p_string());
340 if (ext_v->IsExternal()) {
341 Local<External> ext = ext_v.As<External>();
342 CallbackInfo* info = static_cast<CallbackInfo*>(ext->Value());
343 info->Dispose(env->isolate());
348 char* data = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
349 size_t length = obj->GetIndexedPropertiesExternalArrayDataLength();
350 enum ExternalArrayType array_type =
351 obj->GetIndexedPropertiesExternalArrayDataType();
352 size_t array_size = ExternalArraySize(array_type);
354 CHECK_GT(array_size, 0);
355 CHECK_GE(length * array_size, length);
357 length *= array_size;
359 if (data != nullptr) {
360 obj->SetIndexedPropertiesToExternalArrayData(nullptr,
366 int64_t change_in_bytes = -static_cast<int64_t>(length);
367 env->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
372 void Alloc(Environment* env,
377 enum ExternalArrayType type) {
378 CHECK_LE(length, kMaxLength);
380 size_t type_size = ExternalArraySize(type);
382 CHECK_GT(type_size, 0);
383 CHECK_GE(length * type_size, length);
387 char* data = new char[length];
388 Alloc(env, obj, data, length, fn, hint, type);
392 void Alloc(Environment* env,
398 enum ExternalArrayType type) {
399 CHECK_EQ(false, obj->HasIndexedPropertiesInExternalArrayData());
400 Isolate* isolate = env->isolate();
401 HandleScope handle_scope(isolate);
402 env->set_using_smalloc_alloc_cb(true);
403 CallbackInfo* info = CallbackInfo::New(isolate, obj, fn, hint);
404 obj->SetHiddenValue(env->smalloc_p_string(), External::New(isolate, info));
405 isolate->AdjustAmountOfExternalAllocatedMemory(length + sizeof(*info));
406 size_t size = length / ExternalArraySize(type);
407 obj->SetIndexedPropertiesToExternalArrayData(data, type, size);
411 void HasExternalData(const FunctionCallbackInfo<Value>& args) {
412 Environment* env = Environment::GetCurrent(args);
413 args.GetReturnValue().Set(args[0]->IsObject() &&
414 HasExternalData(env, args[0].As<Object>()));
418 bool HasExternalData(Environment* env, Local<Object> obj) {
419 return obj->HasIndexedPropertiesInExternalArrayData();
422 void IsTypedArray(const FunctionCallbackInfo<Value>& args) {
423 args.GetReturnValue().Set(args[0]->IsTypedArray());
426 void AllocTruncate(const FunctionCallbackInfo<Value>& args) {
427 Environment* env = Environment::GetCurrent(args);
429 Local<Object> obj = args[0].As<Object>();
431 // can't perform this check in JS
432 if (!obj->HasIndexedPropertiesInExternalArrayData())
433 return env->ThrowTypeError("object has no external array data");
435 char* data = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
436 enum ExternalArrayType array_type =
437 obj->GetIndexedPropertiesExternalArrayDataType();
438 int length = obj->GetIndexedPropertiesExternalArrayDataLength();
440 unsigned int new_len = args[1]->Uint32Value();
441 if (new_len > kMaxLength)
442 return env->ThrowRangeError("truncate length is bigger than kMaxLength");
444 if (static_cast<int>(new_len) > length)
445 return env->ThrowRangeError("truncate length is bigger than current one");
447 obj->SetIndexedPropertiesToExternalArrayData(data,
449 static_cast<int>(new_len));
454 class RetainedAllocInfo: public RetainedObjectInfo {
456 explicit RetainedAllocInfo(Handle<Value> wrapper);
458 virtual void Dispose() override;
459 virtual bool IsEquivalent(RetainedObjectInfo* other) override;
460 virtual intptr_t GetHash() override;
461 virtual const char* GetLabel() override;
462 virtual intptr_t GetSizeInBytes() override;
465 static const char label_[];
471 const char RetainedAllocInfo::label_[] = "smalloc";
474 RetainedAllocInfo::RetainedAllocInfo(Handle<Value> wrapper) {
475 Local<Object> obj = wrapper.As<Object>();
476 length_ = obj->GetIndexedPropertiesExternalArrayDataLength();
477 data_ = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
481 void RetainedAllocInfo::Dispose() {
486 bool RetainedAllocInfo::IsEquivalent(RetainedObjectInfo* other) {
487 return label_ == other->GetLabel() &&
488 data_ == static_cast<RetainedAllocInfo*>(other)->data_;
492 intptr_t RetainedAllocInfo::GetHash() {
493 return reinterpret_cast<intptr_t>(data_);
497 const char* RetainedAllocInfo::GetLabel() {
502 intptr_t RetainedAllocInfo::GetSizeInBytes() {
507 RetainedObjectInfo* WrapperInfo(uint16_t class_id, Handle<Value> wrapper) {
508 return new RetainedAllocInfo(wrapper);
512 void Initialize(Handle<Object> exports,
513 Handle<Value> unused,
514 Handle<Context> context) {
515 Environment* env = Environment::GetCurrent(context);
517 env->SetMethod(exports, "copyOnto", CopyOnto);
518 env->SetMethod(exports, "sliceOnto", SliceOnto);
520 env->SetMethod(exports, "alloc", Alloc);
521 env->SetMethod(exports, "dispose", AllocDispose);
522 env->SetMethod(exports, "truncate", AllocTruncate);
524 env->SetMethod(exports, "hasExternalData", HasExternalData);
525 env->SetMethod(exports, "isTypedArray", IsTypedArray);
527 exports->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"),
528 Uint32::NewFromUnsigned(env->isolate(), kMaxLength));
530 HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler();
531 heap_profiler->SetWrapperClassInfoProvider(ALLOC_ID, WrapperInfo);
535 } // namespace smalloc
538 NODE_MODULE_CONTEXT_AWARE_BUILTIN(smalloc, node::smalloc::Initialize)