eda13c7e457fa49aabae3685b7b3e99186f4ec5d
[platform/upstream/nodejs.git] / src / smalloc.cc
1 #include "smalloc.h"
2
3 #include "env.h"
4 #include "env-inl.h"
5 #include "node.h"
6 #include "node_internals.h"
7 #include "v8-profiler.h"
8 #include "v8.h"
9
10 #include <string.h>
11
12 #define ALLOC_ID (0xA10C)
13
14 namespace node {
15 namespace smalloc {
16
17 using v8::Context;
18 using v8::External;
19 using v8::ExternalArrayType;
20 using v8::FunctionCallbackInfo;
21 using v8::Handle;
22 using v8::HandleScope;
23 using v8::HeapProfiler;
24 using v8::Isolate;
25 using v8::Local;
26 using v8::Object;
27 using v8::Persistent;
28 using v8::RetainedObjectInfo;
29 using v8::Uint32;
30 using v8::Value;
31 using v8::WeakCallbackData;
32 using v8::kExternalUint8Array;
33
34
35 class CallbackInfo {
36  public:
37   static inline void Free(char* data, void* hint);
38   static inline CallbackInfo* New(Isolate* isolate,
39                                   Handle<Object> object,
40                                   FreeCallback callback,
41                                   void* hint = 0);
42   inline void Dispose(Isolate* isolate);
43   inline Persistent<Object>* persistent();
44  private:
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,
50                       void* hint);
51   ~CallbackInfo();
52   Persistent<Object> persistent_;
53   FreeCallback const callback_;
54   void* const hint_;
55   DISALLOW_COPY_AND_ASSIGN(CallbackInfo);
56 };
57
58
59 void CallbackInfo::Free(char* data, void*) {
60   ::free(data);
61 }
62
63
64 CallbackInfo* CallbackInfo::New(Isolate* isolate,
65                                 Handle<Object> object,
66                                 FreeCallback callback,
67                                 void* hint) {
68   return new CallbackInfo(isolate, object, callback, hint);
69 }
70
71
72 void CallbackInfo::Dispose(Isolate* isolate) {
73   WeakCallback(isolate, PersistentToLocal(isolate, persistent_));
74 }
75
76
77 Persistent<Object>* CallbackInfo::persistent() {
78   return &persistent_;
79 }
80
81
82 CallbackInfo::CallbackInfo(Isolate* isolate,
83                            Handle<Object> object,
84                            FreeCallback callback,
85                            void* hint)
86     : persistent_(isolate, object),
87       callback_(callback),
88       hint_(hint) {
89   persistent_.SetWeak(this, WeakCallback);
90   persistent_.SetWrapperClassId(ALLOC_ID);
91   persistent_.MarkIndependent();
92 }
93
94
95 CallbackInfo::~CallbackInfo() {
96   persistent_.Reset();
97 }
98
99
100 void CallbackInfo::WeakCallback(
101     const WeakCallbackData<Object, CallbackInfo>& data) {
102   data.GetParameter()->WeakCallback(data.GetIsolate(), data.GetValue());
103 }
104
105
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;
116   }
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_);
121   delete this;
122 }
123
124
125 // return size of external array type, or 0 if unrecognized
126 size_t ExternalArraySize(enum ExternalArrayType type) {
127   switch (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);
146   }
147   return 0;
148 }
149
150
151 // copyOnto(source, source_start, dest, dest_start, copy_length)
152 void CopyOnto(const FunctionCallbackInfo<Value>& args) {
153   Environment* env = Environment::GetCurrent(args);
154
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");
159
160   Local<Object> source = args[0].As<Object>();
161   Local<Object> dest = args[2].As<Object>();
162
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");
167
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());
175
176   size_t source_length = source->GetIndexedPropertiesExternalArrayDataLength();
177   enum ExternalArrayType source_type =
178     source->GetIndexedPropertiesExternalArrayDataType();
179   size_t source_size = ExternalArraySize(source_type);
180
181   size_t dest_length = dest->GetIndexedPropertiesExternalArrayDataLength();
182   enum ExternalArrayType dest_type =
183     dest->GetIndexedPropertiesExternalArrayDataType();
184   size_t dest_size = ExternalArraySize(dest_type);
185
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");
190     if (dest_size == 0)
191       return env->ThrowTypeError("unknown dest external array type");
192
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");
199
200     source_length *= source_size;
201     copy_length *= source_size;
202     dest_length *= dest_size;
203   }
204
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");
214
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");
220
221   memmove(dest_data + dest_start, source_data + source_start, copy_length);
222 }
223
224
225 // dest will always be same type as source
226 // for internal use:
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>();
231
232   CHECK(source->HasIndexedPropertiesInExternalArrayData());
233   CHECK_EQ(false, dest->HasIndexedPropertiesInExternalArrayData());
234
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);
241
242   CHECK_NE(source_size, 0);
243
244   size_t start = args[2]->Uint32Value();
245   size_t end = args[3]->Uint32Value();
246   size_t length = end - start;
247
248   if (source_size > 1) {
249     CHECK_GE(length * source_size, length);
250     length *= source_size;
251   }
252
253   CHECK(source_data != nullptr || length == 0);
254   CHECK_LE(end, source_len);
255   CHECK_LE(start, end);
256
257   dest->SetIndexedPropertiesToExternalArrayData(source_data + start,
258                                                 source_type,
259                                                 length);
260   args.GetReturnValue().Set(source);
261 }
262
263
264 // for internal use:
265 //    alloc(obj, n[, type]);
266 void Alloc(const FunctionCallbackInfo<Value>& args) {
267   Environment* env = Environment::GetCurrent(args);
268
269   Local<Object> obj = args[0].As<Object>();
270
271   // can't perform this check in JS
272   if (obj->HasIndexedPropertiesInExternalArrayData())
273     return env->ThrowTypeError("object already has external array data");
274
275   size_t length = args[1]->Uint32Value();
276   enum ExternalArrayType array_type;
277
278   // it's faster to not pass the default argument then use Uint32Value
279   if (args[2]->IsUndefined()) {
280     array_type = kExternalUint8Array;
281   } else {
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;
286   }
287
288   Alloc(env, obj, length, array_type);
289   args.GetReturnValue().Set(obj);
290 }
291
292
293 void Alloc(Environment* env,
294            Handle<Object> obj,
295            size_t length,
296            enum ExternalArrayType type) {
297   size_t type_size = ExternalArraySize(type);
298
299   CHECK_LE(length, kMaxLength);
300   CHECK_GT(type_size, 0);
301
302   if (length == 0)
303     return Alloc(env, obj, nullptr, length, type);
304
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");
309   }
310
311   Alloc(env, obj, data, length, type);
312 }
313
314
315 void Alloc(Environment* env,
316            Handle<Object> obj,
317            char* data,
318            size_t length,
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);
325 }
326
327
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>());
332 }
333
334
335 void AllocDispose(Environment* env, Handle<Object> obj) {
336   HandleScope handle_scope(env->isolate());
337
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());
344       return;
345     }
346   }
347
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);
353
354   CHECK_GT(array_size, 0);
355   CHECK_GE(length * array_size, length);
356
357   length *= array_size;
358
359   if (data != nullptr) {
360     obj->SetIndexedPropertiesToExternalArrayData(nullptr,
361                                                  kExternalUint8Array,
362                                                  0);
363     free(data);
364   }
365   if (length != 0) {
366     int64_t change_in_bytes = -static_cast<int64_t>(length);
367     env->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
368   }
369 }
370
371
372 void Alloc(Environment* env,
373            Handle<Object> obj,
374            size_t length,
375            FreeCallback fn,
376            void* hint,
377            enum ExternalArrayType type) {
378   CHECK_LE(length, kMaxLength);
379
380   size_t type_size = ExternalArraySize(type);
381
382   CHECK_GT(type_size, 0);
383   CHECK_GE(length * type_size, length);
384
385   length *= type_size;
386
387   char* data = new char[length];
388   Alloc(env, obj, data, length, fn, hint, type);
389 }
390
391
392 void Alloc(Environment* env,
393            Handle<Object> obj,
394            char* data,
395            size_t length,
396            FreeCallback fn,
397            void* hint,
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);
408 }
409
410
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>()));
415 }
416
417
418 bool HasExternalData(Environment* env, Local<Object> obj) {
419   return obj->HasIndexedPropertiesInExternalArrayData();
420 }
421
422 void IsTypedArray(const FunctionCallbackInfo<Value>& args) {
423   args.GetReturnValue().Set(args[0]->IsTypedArray());
424 }
425
426 void AllocTruncate(const FunctionCallbackInfo<Value>& args) {
427   Environment* env = Environment::GetCurrent(args);
428
429   Local<Object> obj = args[0].As<Object>();
430
431   // can't perform this check in JS
432   if (!obj->HasIndexedPropertiesInExternalArrayData())
433     return env->ThrowTypeError("object has no external array data");
434
435   char* data = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
436   enum ExternalArrayType array_type =
437       obj->GetIndexedPropertiesExternalArrayDataType();
438   int length = obj->GetIndexedPropertiesExternalArrayDataLength();
439
440   unsigned int new_len = args[1]->Uint32Value();
441   if (new_len > kMaxLength)
442     return env->ThrowRangeError("truncate length is bigger than kMaxLength");
443
444   if (static_cast<int>(new_len) > length)
445     return env->ThrowRangeError("truncate length is bigger than current one");
446
447   obj->SetIndexedPropertiesToExternalArrayData(data,
448                                                array_type,
449                                                static_cast<int>(new_len));
450 }
451
452
453
454 class RetainedAllocInfo: public RetainedObjectInfo {
455  public:
456   explicit RetainedAllocInfo(Handle<Value> wrapper);
457
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;
463
464  private:
465   static const char label_[];
466   char* data_;
467   int length_;
468 };
469
470
471 const char RetainedAllocInfo::label_[] = "smalloc";
472
473
474 RetainedAllocInfo::RetainedAllocInfo(Handle<Value> wrapper) {
475   Local<Object> obj = wrapper.As<Object>();
476   length_ = obj->GetIndexedPropertiesExternalArrayDataLength();
477   data_ = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
478 }
479
480
481 void RetainedAllocInfo::Dispose() {
482   delete this;
483 }
484
485
486 bool RetainedAllocInfo::IsEquivalent(RetainedObjectInfo* other) {
487   return label_ == other->GetLabel() &&
488          data_ == static_cast<RetainedAllocInfo*>(other)->data_;
489 }
490
491
492 intptr_t RetainedAllocInfo::GetHash() {
493   return reinterpret_cast<intptr_t>(data_);
494 }
495
496
497 const char* RetainedAllocInfo::GetLabel() {
498   return label_;
499 }
500
501
502 intptr_t RetainedAllocInfo::GetSizeInBytes() {
503   return length_;
504 }
505
506
507 RetainedObjectInfo* WrapperInfo(uint16_t class_id, Handle<Value> wrapper) {
508   return new RetainedAllocInfo(wrapper);
509 }
510
511
512 void Initialize(Handle<Object> exports,
513                 Handle<Value> unused,
514                 Handle<Context> context) {
515   Environment* env = Environment::GetCurrent(context);
516
517   env->SetMethod(exports, "copyOnto", CopyOnto);
518   env->SetMethod(exports, "sliceOnto", SliceOnto);
519
520   env->SetMethod(exports, "alloc", Alloc);
521   env->SetMethod(exports, "dispose", AllocDispose);
522   env->SetMethod(exports, "truncate", AllocTruncate);
523
524   env->SetMethod(exports, "hasExternalData", HasExternalData);
525   env->SetMethod(exports, "isTypedArray", IsTypedArray);
526
527   exports->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"),
528                Uint32::NewFromUnsigned(env->isolate(), kMaxLength));
529
530   HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler();
531   heap_profiler->SetWrapperClassInfoProvider(ALLOC_ID, WrapperInfo);
532 }
533
534
535 }  // namespace smalloc
536 }  // namespace node
537
538 NODE_MODULE_CONTEXT_AWARE_BUILTIN(smalloc, node::smalloc::Initialize)